UNPKG

@uplink-protocol/calendar-controller

Version:

Flexible calendar API supporting both calendar and date-picker integrations for any JavaScript framework or library

359 lines (300 loc) 15.2 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Event Listener Duplicate Prevention Test</title> <link rel="stylesheet" href="css/advanced-floating-calendar.css"> <style> body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; } .test-controls { background: white; padding: 20px; border-radius: 10px; margin-bottom: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .test-button { background: #007acc; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin: 5px; font-size: 14px; } .test-button:hover { background: #005a9e; } .status { margin-top: 15px; padding: 10px; background: #f0f8ff; border-left: 4px solid #007acc; border-radius: 4px; font-family: monospace; font-size: 12px; } .warning { background: #fff3cd; border-left-color: #ffc107; } .success { background: #d4edda; border-left-color: #28a745; } .error { background: #f8d7da; border-left-color: #dc3545; } </style> </head> <body> <div class="test-controls"> <h3>🔍 Event Listener Duplicate Prevention Test</h3> <p><strong>This test validates that event listeners are not duplicated on re-render/re-initialization.</strong></p> <button class="test-button" onclick="testEventListenerDuplication()">Test Event Listener Duplication</button> <button class="test-button" onclick="forceReInitialize()">Force Re-Initialize</button> <button class="test-button" onclick="testResizeListeners()">Test Resize Listeners</button> <button class="test-button" onclick="testDragListeners()">Test Drag Listeners</button> <button class="test-button" onclick="simulateMultipleRenders()">Simulate Multiple Renders</button> <button class="test-button" onclick="countEventListeners()">Count Active Listeners</button> <button class="test-button" onclick="testCleanup()">Test Cleanup</button> <div class="status" id="status"> Ready for testing. Click buttons above to run tests. </div> </div> <!-- Floating Calendar Widget --> <div id="floating-calendar-widget" class="floating-calendar-widget"> <div class="calendar-header"> <div class="calendar-controls"> <button class="minimize-btn"></button> <button class="close-btn">×</button> </div> <h3>Calendar Widget</h3> <div class="quick-actions"> <button class="quick-action" data-action="today">Today</button> <button class="quick-action" data-action="tomorrow">Tomorrow</button> <button class="quick-action" data-action="next-week">Next Week</button> </div> </div> <div class="calendar-content"> <div class="calendar-navigation"> <button class="nav-btn prev-btn"></button> <span class="current-month">December 2024</span> <button class="nav-btn next-btn"></button> </div> <div class="calendar-grid"> <!-- Calendar will be generated here --> </div> </div> </div> <!-- Include the advanced floating calendar script --> <script src="js/calendar-controller.js"></script> <script src="js/advanced-floating-calendar.js"></script> <script> let testCounter = 0; let initialListenerCounts = {}; function updateStatus(message, type = 'info') { const statusEl = document.getElementById('status'); statusEl.className = `status ${type}`; statusEl.innerHTML = `<strong>[Test ${++testCounter}]</strong> ${message}`; } // Track event listener additions by monkey-patching addEventListener function setupEventListenerTracking() { const listenerCounts = { document: { keydown: 0, mousemove: 0, mouseup: 0 }, window: { resize: 0 }, total: 0 }; const originalAddEventListener = EventTarget.prototype.addEventListener; const originalRemoveEventListener = EventTarget.prototype.removeEventListener; EventTarget.prototype.addEventListener = function(type, listener, options) { if (this === document) { if (listenerCounts.document[type] !== undefined) { listenerCounts.document[type]++; listenerCounts.total++; } } else if (this === window) { if (listenerCounts.window[type] !== undefined) { listenerCounts.window[type]++; listenerCounts.total++; } } return originalAddEventListener.call(this, type, listener, options); }; EventTarget.prototype.removeEventListener = function(type, listener, options) { if (this === document) { if (listenerCounts.document[type] !== undefined && listenerCounts.document[type] > 0) { listenerCounts.document[type]--; listenerCounts.total--; } } else if (this === window) { if (listenerCounts.window[type] !== undefined && listenerCounts.window[type] > 0) { listenerCounts.window[type]--; listenerCounts.total--; } } return originalRemoveEventListener.call(this, type, listener, options); }; return listenerCounts; } function testEventListenerDuplication() { updateStatus('Testing event listener duplication prevention...', 'info'); const counts = setupEventListenerTracking(); initialListenerCounts = { ...counts }; // Re-initialize the calendar multiple times for (let i = 0; i < 5; i++) { if (window.advancedCalendar) { window.advancedCalendar.setupEventListeners(); } } const duplicates = []; Object.keys(counts.document).forEach(type => { if (counts.document[type] > 1) { duplicates.push(`document.${type}: ${counts.document[type]}`); } }); Object.keys(counts.window).forEach(type => { if (counts.window[type] > 1) { duplicates.push(`window.${type}: ${counts.window[type]}`); } }); if (duplicates.length > 0) { updateStatus(`❌ Found duplicate listeners: ${duplicates.join(', ')}`, 'error'); } else { updateStatus('✅ No duplicate event listeners detected!', 'success'); } } function forceReInitialize() { updateStatus('Force re-initializing calendar...', 'info'); if (window.advancedCalendar) { // Simulate re-initialization window.advancedCalendar.setupEventListeners(); window.advancedCalendar.setupDragFunctionality(); updateStatus('✅ Calendar re-initialized. Check for duplicates with other tests.', 'success'); } else { updateStatus('❌ Calendar not found', 'error'); } } function testResizeListeners() { updateStatus('Testing resize event listener handling...', 'info'); let resizeCallCount = 0; const originalHandleResize = window.advancedCalendar?.handleResize; if (originalHandleResize) { window.advancedCalendar.handleResize = function() { resizeCallCount++; return originalHandleResize.call(this); }; // Trigger resize event window.dispatchEvent(new Event('resize')); setTimeout(() => { if (resizeCallCount === 1) { updateStatus('✅ Resize handler called exactly once', 'success'); } else { updateStatus(`❌ Resize handler called ${resizeCallCount} times`, 'error'); } // Restore original method window.advancedCalendar.handleResize = originalHandleResize; }, 200); } else { updateStatus('❌ Calendar resize handler not found', 'error'); } } function testDragListeners() { updateStatus('Testing drag event listener handling...', 'info'); let mouseMoveCallCount = 0; let mouseUpCallCount = 0; const originalHandleMouseMove = window.advancedCalendar?.handleMouseMove; const originalHandleMouseUp = window.advancedCalendar?.handleMouseUp; if (originalHandleMouseMove && originalHandleMouseUp) { window.advancedCalendar.handleMouseMove = function(e) { mouseMoveCallCount++; return originalHandleMouseMove.call(this, e); }; window.advancedCalendar.handleMouseUp = function(e) { mouseUpCallCount++; return originalHandleMouseUp.call(this, e); }; // Trigger drag events document.dispatchEvent(new MouseEvent('mousemove', { clientX: 100, clientY: 100 })); document.dispatchEvent(new MouseEvent('mouseup')); setTimeout(() => { const results = []; if (mouseMoveCallCount <= 1) results.push('✅ MouseMove: OK'); else results.push(`❌ MouseMove called ${mouseMoveCallCount} times`); if (mouseUpCallCount <= 1) results.push('✅ MouseUp: OK'); else results.push(`❌ MouseUp called ${mouseUpCallCount} times`); updateStatus(`Drag listeners test: ${results.join(', ')}`, mouseMoveCallCount <= 1 && mouseUpCallCount <= 1 ? 'success' : 'error'); // Restore original methods window.advancedCalendar.handleMouseMove = originalHandleMouseMove; window.advancedCalendar.handleMouseUp = originalHandleMouseUp; }, 100); } else { updateStatus('❌ Calendar drag handlers not found', 'error'); } } function simulateMultipleRenders() { updateStatus('Simulating multiple calendar renders...', 'info'); if (window.advancedCalendar) { // Render multiple times and check for listener accumulation for (let i = 0; i < 10; i++) { window.advancedCalendar.render(); } updateStatus('✅ Multiple renders completed. Use other tests to check for issues.', 'success'); } else { updateStatus('❌ Calendar not found', 'error'); } } function countEventListeners() { updateStatus('Counting active event listeners...', 'info'); if (window.advancedCalendar) { const calendar = window.advancedCalendar; const counts = { documentListeners: calendar.documentListeners ? calendar.documentListeners.size : 'N/A', windowListeners: calendar.windowListeners ? calendar.windowListeners.size : 'N/A', dateCells: calendar.widget.querySelectorAll('[data-date]').length }; updateStatus(`Active listeners - Document: ${counts.documentListeners}, Window: ${counts.windowListeners}, Date cells: ${counts.dateCells}`, 'info'); } else { updateStatus('❌ Calendar not found', 'error'); } } function testCleanup() { updateStatus('Testing event listener cleanup...', 'info'); if (window.advancedCalendar) { const calendar = window.advancedCalendar; // Check initial state const initialDocListeners = calendar.documentListeners.size; const initialWinListeners = calendar.windowListeners.size; // Test cleanup calendar.removeEventListeners(); const finalDocListeners = calendar.documentListeners.size; const finalWinListeners = calendar.windowListeners.size; if (finalDocListeners === 0 && finalWinListeners === 0) { updateStatus(`✅ Cleanup successful! Removed ${initialDocListeners} document and ${initialWinListeners} window listeners`, 'success'); // Re-setup listeners for continued testing calendar.setupEventListeners(); } else { updateStatus(`❌ Cleanup failed! Document: ${finalDocListeners}, Window: ${finalWinListeners}`, 'error'); } } else { updateStatus('❌ Calendar not found', 'error'); } } // Initialize test when page loads document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { updateStatus('Event listener test page loaded. Calendar should be initialized.', 'success'); }, 500); }); </script> </body> </html>