javascript

exercises

exercises.js
/**
 * 21.5 Capstone Projects - Exercises
 *
 * Final project exercises combining all learned concepts
 */

/**
 * Final Project: Build Your Own Application
 *
 * Choose ONE of the following projects to implement.
 * Each project should demonstrate:
 * - Clean code architecture
 * - Security best practices
 * - Performance optimizations
 * - Appropriate design patterns
 * - Error handling
 * - Testing
 */

// ============================================================
// PROJECT OPTION 1: Note-Taking App with Sync
// ============================================================

/**
 * Requirements:
 *
 * 1. Core Features:
 *    - Create, edit, delete notes
 *    - Rich text formatting
 *    - Tags and categories
 *    - Search functionality
 *
 * 2. Data Management:
 *    - Local storage with IndexedDB
 *    - Sync with mock API
 *    - Conflict resolution
 *    - Offline support
 *
 * 3. Security:
 *    - Input sanitization
 *    - XSS prevention
 *    - Secure storage
 *    - Content validation
 *
 * 4. Performance:
 *    - Debounced auto-save
 *    - Lazy loading
 *    - Virtual scrolling for note list
 *    - Memoized search results
 */

class NoteApp {
  constructor() {
    // TODO: Initialize the note-taking application
    // - Set up storage
    // - Initialize event system
    // - Load existing notes
  }

  // Note CRUD operations
  async createNote(data) {
    // TODO: Create a new note with validation
  }

  async updateNote(id, data) {
    // TODO: Update note with conflict detection
  }

  async deleteNote(id) {
    // TODO: Soft delete with recovery option
  }

  // Search and filtering
  searchNotes(query) {
    // TODO: Implement fuzzy search with memoization
  }

  filterByTag(tag) {
    // TODO: Filter notes by tag
  }

  // Sync management
  async syncWithServer() {
    // TODO: Implement sync with conflict resolution
  }

  // Export functionality
  exportNotes(format) {
    // TODO: Export notes as JSON, Markdown, or HTML
  }
}

// ============================================================
// PROJECT OPTION 2: Budget Tracker
// ============================================================

/**
 * Requirements:
 *
 * 1. Core Features:
 *    - Track income and expenses
 *    - Categories and budgets
 *    - Recurring transactions
 *    - Reports and charts
 *
 * 2. Data Management:
 *    - Transaction history
 *    - Monthly summaries
 *    - Budget tracking
 *    - Data export/import
 *
 * 3. Security:
 *    - Sensitive data encryption
 *    - Input validation
 *    - Secure calculations
 *    - Data integrity checks
 *
 * 4. Performance:
 *    - Efficient aggregations
 *    - Cached reports
 *    - Virtualized transaction list
 *    - Optimized chart rendering
 */

class BudgetTracker {
  constructor() {
    // TODO: Initialize the budget tracker
    // - Set up categories
    // - Load transaction history
    // - Calculate running balances
  }

  // Transaction management
  addTransaction(data) {
    // TODO: Add income or expense with validation
  }

  editTransaction(id, data) {
    // TODO: Edit transaction and recalculate
  }

  deleteTransaction(id) {
    // TODO: Delete and update balances
  }

  // Budget management
  setBudget(category, amount, period) {
    // TODO: Set budget for category
  }

  getBudgetStatus() {
    // TODO: Get current budget vs spending
  }

  // Reports
  getMonthlyReport(month, year) {
    // TODO: Generate monthly summary
  }

  getCategoryBreakdown(startDate, endDate) {
    // TODO: Get spending by category
  }

  // Data export
  exportToCSV() {
    // TODO: Export transactions to CSV
  }
}

// ============================================================
// PROJECT OPTION 3: Quiz/Flashcard App
// ============================================================

/**
 * Requirements:
 *
 * 1. Core Features:
 *    - Create flashcard decks
 *    - Multiple question types
 *    - Spaced repetition
 *    - Progress tracking
 *
 * 2. Data Management:
 *    - Deck organization
 *    - Learning statistics
 *    - Progress persistence
 *    - Import/export decks
 *
 * 3. Security:
 *    - Content sanitization
 *    - Safe rendering
 *    - Score validation
 *    - Cheat prevention
 *
 * 4. Performance:
 *    - Efficient card selection
 *    - Cached deck data
 *    - Smooth animations
 *    - Optimized statistics
 */

class FlashcardApp {
  constructor() {
    // TODO: Initialize flashcard application
    // - Load decks
    // - Initialize spaced repetition algorithm
    // - Set up statistics tracking
  }

  // Deck management
  createDeck(name, description) {
    // TODO: Create new deck
  }

  addCard(deckId, front, back, hints) {
    // TODO: Add card to deck
  }

  editCard(cardId, data) {
    // TODO: Edit existing card
  }

  // Study session
  startSession(deckId, options) {
    // TODO: Start study session
    // Use spaced repetition to select cards
  }

  recordAnswer(cardId, correct, timeSpent) {
    // TODO: Record answer and update intervals
  }

  // Progress tracking
  getDeckProgress(deckId) {
    // TODO: Get mastery progress for deck
  }

  getStudyStreak() {
    // TODO: Calculate study streak
  }

  // Import/Export
  exportDeck(deckId, format) {
    // TODO: Export deck as JSON or Anki format
  }

  importDeck(data) {
    // TODO: Import deck from file
  }
}

// ============================================================
// PROJECT OPTION 4: Kanban Board
// ============================================================

/**
 * Requirements:
 *
 * 1. Core Features:
 *    - Customizable columns
 *    - Drag and drop cards
 *    - Card details and comments
 *    - Labels and priorities
 *
 * 2. Data Management:
 *    - Board state persistence
 *    - Card ordering
 *    - Activity history
 *    - Multiple boards
 *
 * 3. Security:
 *    - Content validation
 *    - Safe drag-drop
 *    - XSS prevention
 *    - Data sanitization
 *
 * 4. Performance:
 *    - Efficient DOM updates
 *    - Throttled drag events
 *    - Optimistic updates
 *    - Memoized renders
 */

class KanbanBoard {
  constructor(boardId) {
    // TODO: Initialize kanban board
    // - Load board data
    // - Set up drag-drop handlers
    // - Initialize column management
  }

  // Column management
  addColumn(name, position) {
    // TODO: Add new column
  }

  moveColumn(columnId, newPosition) {
    // TODO: Reorder column
  }

  deleteColumn(columnId) {
    // TODO: Delete column (with cards)
  }

  // Card management
  addCard(columnId, data) {
    // TODO: Add card to column
  }

  moveCard(cardId, targetColumnId, position) {
    // TODO: Move card between columns
  }

  updateCard(cardId, data) {
    // TODO: Update card details
  }

  // Filtering and search
  filterCards(criteria) {
    // TODO: Filter visible cards
  }

  searchCards(query) {
    // TODO: Search cards
  }

  // Activity tracking
  getCardActivity(cardId) {
    // TODO: Get activity history
  }
}

// ============================================================
// EVALUATION RUBRIC
// ============================================================

/**
 * Your project will be evaluated on:
 *
 * 1. ARCHITECTURE (25 points)
 *    - Clean separation of concerns
 *    - Appropriate use of patterns
 *    - Modular, reusable code
 *    - Clear file organization
 *
 * 2. SECURITY (25 points)
 *    - Input validation everywhere
 *    - Output encoding
 *    - Secure data handling
 *    - Error message safety
 *
 * 3. PERFORMANCE (20 points)
 *    - Efficient algorithms
 *    - Proper caching
 *    - Lazy loading where appropriate
 *    - Memory management
 *
 * 4. CODE QUALITY (15 points)
 *    - Readable, self-documenting code
 *    - Consistent style
 *    - Meaningful names
 *    - Appropriate comments
 *
 * 5. ERROR HANDLING (15 points)
 *    - Graceful degradation
 *    - User-friendly messages
 *    - Recovery mechanisms
 *    - Logging
 */

// ============================================================
// STARTER TEMPLATE
// ============================================================

/**
 * Use this template as a starting point for your project
 */

class ApplicationCore {
  constructor(config = {}) {
    this.config = config;
    this.eventBus = new EventBusFinal();
    this.store = new StoreFinal(config.storeName || 'app');
    this.initialized = false;
  }

  async init() {
    if (this.initialized) return;

    try {
      await this.store.init();
      await this.loadInitialData();
      this.setupEventHandlers();
      this.initialized = true;
      this.eventBus.emit('app:initialized');
    } catch (error) {
      console.error('Failed to initialize app:', error);
      throw error;
    }
  }

  async loadInitialData() {
    // Override in subclass
  }

  setupEventHandlers() {
    // Override in subclass
  }

  destroy() {
    this.eventBus.clear();
    this.initialized = false;
  }
}

class EventBusFinal {
  constructor() {
    this.events = new Map();
  }

  on(event, handler) {
    if (!this.events.has(event)) {
      this.events.set(event, new Set());
    }
    this.events.get(event).add(handler);
    return () => this.off(event, handler);
  }

  off(event, handler) {
    this.events.get(event)?.delete(handler);
  }

  emit(event, data) {
    this.events.get(event)?.forEach((handler) => {
      try {
        handler(data);
      } catch (error) {
        console.error(`Event handler error for ${event}:`, error);
      }
    });
  }

  clear() {
    this.events.clear();
  }
}

class StoreFinal {
  constructor(name) {
    this.name = name;
    this.data = {};
  }

  async init() {
    const saved = localStorage.getItem(this.name);
    if (saved) {
      try {
        this.data = JSON.parse(saved);
      } catch {
        this.data = {};
      }
    }
  }

  get(key) {
    return this.data[key];
  }

  set(key, value) {
    this.data[key] = value;
    this.persist();
  }

  delete(key) {
    delete this.data[key];
    this.persist();
  }

  persist() {
    localStorage.setItem(this.name, JSON.stringify(this.data));
  }

  clear() {
    this.data = {};
    localStorage.removeItem(this.name);
  }
}

// ============================================================
// UTILITY FUNCTIONS
// ============================================================

// Input sanitization
function sanitizeHTML(str) {
  const temp = document.createElement('div');
  temp.textContent = str;
  return temp.innerHTML;
}

// Debounce
function debounce(fn, ms) {
  let timeout;
  return function (...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn.apply(this, args), ms);
  };
}

// Throttle
function throttle(fn, ms) {
  let lastCall = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastCall >= ms) {
      lastCall = now;
      return fn.apply(this, args);
    }
  };
}

// Memoization
function memoize(fn) {
  const cache = new Map();
  return function (...args) {
    const key = JSON.stringify(args);
    if (!cache.has(key)) {
      cache.set(key, fn.apply(this, args));
    }
    return cache.get(key);
  };
}

// Generate unique ID
function generateId() {
  return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}

// Deep clone
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (Array.isArray(obj)) return obj.map(deepClone);
  return Object.fromEntries(
    Object.entries(obj).map(([key, value]) => [key, deepClone(value)])
  );
}

// ============================================================
// EXAMPLE SOLUTION: Minimal Note App
// ============================================================

/*
// SOLUTION: Note-Taking App Implementation

class NoteAppSolution extends ApplicationCore {
    constructor() {
        super({ storeName: 'notes-app' });
        this.notes = [];
    }
    
    async loadInitialData() {
        this.notes = this.store.get('notes') || [];
    }
    
    setupEventHandlers() {
        this.eventBus.on('note:change', () => {
            this.store.set('notes', this.notes);
        });
    }
    
    createNote(data) {
        const note = {
            id: generateId(),
            title: sanitizeHTML(data.title || 'Untitled'),
            content: sanitizeHTML(data.content || ''),
            tags: (data.tags || []).map(t => sanitizeHTML(t).slice(0, 50)),
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString(),
            deleted: false
        };
        
        this.notes.push(note);
        this.eventBus.emit('note:change');
        this.eventBus.emit('note:created', note);
        
        return note;
    }
    
    updateNote(id, data) {
        const note = this.notes.find(n => n.id === id);
        if (!note || note.deleted) return null;
        
        if (data.title !== undefined) note.title = sanitizeHTML(data.title);
        if (data.content !== undefined) note.content = sanitizeHTML(data.content);
        if (data.tags !== undefined) note.tags = data.tags.map(t => sanitizeHTML(t).slice(0, 50));
        note.updatedAt = new Date().toISOString();
        
        this.eventBus.emit('note:change');
        this.eventBus.emit('note:updated', note);
        
        return note;
    }
    
    deleteNote(id) {
        const note = this.notes.find(n => n.id === id);
        if (!note) return false;
        
        note.deleted = true;
        note.deletedAt = new Date().toISOString();
        
        this.eventBus.emit('note:change');
        this.eventBus.emit('note:deleted', note);
        
        return true;
    }
    
    restoreNote(id) {
        const note = this.notes.find(n => n.id === id);
        if (!note || !note.deleted) return false;
        
        note.deleted = false;
        delete note.deletedAt;
        
        this.eventBus.emit('note:change');
        this.eventBus.emit('note:restored', note);
        
        return true;
    }
    
    searchNotes = memoize((query) => {
        const q = query.toLowerCase();
        return this.notes.filter(n => 
            !n.deleted && (
                n.title.toLowerCase().includes(q) ||
                n.content.toLowerCase().includes(q) ||
                n.tags.some(t => t.toLowerCase().includes(q))
            )
        );
    });
    
    filterByTag(tag) {
        return this.notes.filter(n => !n.deleted && n.tags.includes(tag));
    }
    
    getAllNotes() {
        return this.notes.filter(n => !n.deleted);
    }
    
    getDeletedNotes() {
        return this.notes.filter(n => n.deleted);
    }
    
    exportNotes(format = 'json') {
        const notes = this.getAllNotes();
        
        switch (format) {
            case 'json':
                return JSON.stringify(notes, null, 2);
            
            case 'markdown':
                return notes.map(n => 
                    `# ${n.title}\n\n${n.content}\n\nTags: ${n.tags.join(', ')}\n\n---\n`
                ).join('\n');
            
            default:
                throw new Error(`Unsupported format: ${format}`);
        }
    }
}

// Usage:
// const app = new NoteAppSolution();
// await app.init();
// app.createNote({ title: 'My First Note', content: 'Hello World!' });
// console.log(app.getAllNotes());
*/

console.log('=== Capstone Project Exercises ===');
console.log('Choose one project option and implement it fully.');
console.log('Use the starter template and utility functions provided.');
console.log('Refer to the evaluation rubric for grading criteria.');

// Export for use
if (typeof module !== 'undefined' && module.exports) {
  module.exports = {
    NoteApp,
    BudgetTracker,
    FlashcardApp,
    KanbanBoard,
    ApplicationCore,
    EventBusFinal,
    StoreFinal,
    sanitizeHTML,
    debounce,
    throttle,
    memoize,
    generateId,
    deepClone,
  };
}
Exercises - JavaScript Tutorial | DeepML