UNPKG

mcp-quiz-server

Version:

๐Ÿง  AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

136 lines (121 loc) โ€ข 4.52 kB
/** * @moduleName: EventManagement * @version: 2.0.0 * @since: 2025-07-26 * @lastUpdated: 2025-07-26 * @projectSummary: Enhanced MCP Quiz Server - Event Management System * @techStack: TypeScript, DOM API, Event Delegation * @dependency: None (core utility) * @interModuleDependency: Used by QuizContent, AnswerHandler, NavigationController * @requirementsTraceability: * {@link Requirements.REQ_UI_003} (Event Handling) * {@link Requirements.REQ_BUG_021} (Infinite Recursion Fix) * @briefDescription: Centralized event management system preventing memory leaks and infinite recursion * @methods: addListener, removeListener, cleanup, delegate * @contributors: GitHub Copilot * @examples: * - EventManagement.addListener(button, 'click', handler, 'continue-button') * - EventManagement.cleanup(['continue-button', 'complete-button']) * @vulnerabilitiesAssessment: Memory leak prevention, proper event cleanup, no sensitive data */ export class EventManagement { private static eventListeners = new Map< string, { element: Element; event: string; handler: EventListener; } >(); /** * Add an event listener with automatic cleanup of existing listeners * @param element - The DOM element to attach the listener to * @param event - The event type (e.g., 'click', 'keydown') * @param handler - The event handler function * @param key - Unique key for this listener (enables cleanup) */ static addListener(element: Element, event: string, handler: EventListener, key: string): void { // Remove existing listener if present to prevent duplicates if (this.eventListeners.has(key)) { const existing = this.eventListeners.get(key)!; existing.element.removeEventListener(existing.event, existing.handler); } // Add new listener element.addEventListener(event, handler); this.eventListeners.set(key, { element, event, handler, }); console.log(`๐Ÿ”— Event listener added: ${key} (${event})`); } /** * Remove a specific event listener * @param key - The unique key of the listener to remove */ static removeListener(key: string): void { if (this.eventListeners.has(key)) { const listener = this.eventListeners.get(key)!; listener.element.removeEventListener(listener.event, listener.handler); this.eventListeners.delete(key); console.log(`๐Ÿ—‘๏ธ Event listener removed: ${key}`); } } /** * Clean up multiple listeners by their keys * @param keys - Array of listener keys to clean up, or undefined to clean all */ static cleanup(keys?: string[]): void { const keysToClean = keys || Array.from(this.eventListeners.keys()); keysToClean.forEach(key => { this.removeListener(key); }); console.log(`๐Ÿงน Event cleanup completed: ${keysToClean.length} listeners removed`); } /** * Get the count of active listeners (for debugging) */ static getActiveListenerCount(): number { return this.eventListeners.size; } /** * Get all active listener keys (for debugging) */ static getActiveListenerKeys(): string[] { return Array.from(this.eventListeners.keys()); } /** * Event delegation helper for dynamic content * @param container - The container element to delegate from * @param selector - CSS selector for target elements * @param event - Event type to listen for * @param handler - Event handler function * @param key - Unique key for cleanup */ static delegate( container: Element, selector: string, event: string, handler: (event: Event, target: Element) => void, key: string ): void { const delegatedHandler = (e: Event) => { const target = (e.target as Element).closest(selector); if (target) { handler(e, target); } }; this.addListener(container, event, delegatedHandler, key); } /** * Safe cleanup for component destruction * Call this when a component is being destroyed to prevent memory leaks */ static componentCleanup(componentPrefix: string): void { const componentKeys = Array.from(this.eventListeners.keys()).filter(key => key.startsWith(componentPrefix) ); this.cleanup(componentKeys); console.log(`๐Ÿ—๏ธ Component cleanup: ${componentPrefix} (${componentKeys.length} listeners)`); } }