// js/core/EventBus.js /** * Central event dispatcher for calendar using DOM CustomEvents * Provides logging and debugging capabilities */ export class EventBus { constructor() { this.eventLog = []; this.debug = false; // Set to true for console logging this.listeners = new Set(); // Track listeners for cleanup } /** * Subscribe to an event via DOM addEventListener * @param {string} eventType * @param {Function} handler * @param {Object} options * @returns {Function} Unsubscribe function */ on(eventType, handler, options = {}) { document.addEventListener(eventType, handler, options); // Track for cleanup this.listeners.add({ eventType, handler, options }); // Return unsubscribe function return () => this.off(eventType, handler); } /** * Subscribe to an event once * @param {string} eventType * @param {Function} handler */ once(eventType, handler) { return this.on(eventType, handler, { once: true }); } /** * Unsubscribe from an event * @param {string} eventType * @param {Function} handler */ off(eventType, handler) { document.removeEventListener(eventType, handler); // Remove from tracking for (const listener of this.listeners) { if (listener.eventType === eventType && listener.handler === handler) { this.listeners.delete(listener); break; } } } /** * Emit an event via DOM CustomEvent * @param {string} eventType * @param {*} detail * @returns {boolean} Whether event was cancelled */ emit(eventType, detail = {}) { const event = new CustomEvent(eventType, { detail, bubbles: true, cancelable: true }); this.eventLog.push({ type: eventType, detail, timestamp: Date.now() }); // Emit on document (only DOM events now) return !document.dispatchEvent(event); } /** * Get event history * @param {string} eventType Optional filter by type * @returns {Array} */ getEventLog(eventType = null) { if (eventType) { return this.eventLog.filter(e => e.type === eventType); } return this.eventLog; } /** * Enable/disable debug mode * @param {boolean} enabled */ setDebug(enabled) { this.debug = enabled; } /** * Clean up all tracked listeners */ destroy() { for (const listener of this.listeners) { document.removeEventListener(listener.eventType, listener.handler); } this.listeners.clear(); this.eventLog = []; } } // Create singleton instance export const eventBus = new EventBus();