// const originalAddEventListener = EventTarget.prototype.addEventListener; // // // Override addEventListener to wrap handlers with logging // EventTarget.prototype.addEventListener = function(type, handler, options) { // if (!['mousemove', 'load','mousedown', 'DOMContentLoaded'].includes(type)) { // Only log 'change' events, or remove this condition for all events // const wrappedHandler = function(event) { // console.log(`🎯 ${type} event triggered on:`, event.target); // console.log(`📋 Handler function:`, handler); // console.log(`📊 Event details:`, event); // console.trace('Call stack:'); // Shows where the event was triggered from // // // Call the original handler // return handler.apply(this, arguments); // }; // // // Call the original addEventListener with our wrapped handler // return originalAddEventListener.call(this, type, wrappedHandler, options); // } else { // // For non-change events, use original behavior // return originalAddEventListener.call(this, type, handler, options); // } // }; // A11yHelper - Centralized accessibility functionality class A11yHelper { constructor() { this.liveRegion = document.querySelector('.screen-reader-text.live-region'); if(!this.liveRegion){ console.log('No accessibility region.'); } } announce(message, priority = 'polite') { if (!this.liveRegion) return; // Set priority - use 'assertive' for critical updates this.liveRegion.setAttribute('aria-live', priority); // Clear and set with small delay to ensure announcement this.liveRegion.textContent = ''; setTimeout(() => { this.liveRegion.textContent = message; }, 50); } announceItems(count, isAppending = false, hasMore = true) { const action = isAppending ? 'Added' : 'Loaded'; const more = hasMore ? '. More available to load.' : '. No more available to load.'; this.announce(`${action} ${count} new items${more}`); } announceEmpty(isFavourites = false) { const message = isFavourites ? "No favourites found. Try adding some items to your collection." : "No items found matching your current filters."; this.announce(message); } announceNavigation(current, total, isFirst = false, isLast = false) { if (isFirst) { this.announce('At first image'); } else if (isLast) { this.announce('At last image'); } else { this.announce(`Image ${current} of ${total}`); } } trapFocus(element, returnFocus = null) { if (!element) return () => {}; const focusableElements = element.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); if (!focusableElements.length) return () => {}; const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; // Store previously focused element const previousFocus = returnFocus || document.activeElement; // Focus first element firstElement.focus(); // Handle keyboard navigation const handler = (e) => { if (e.key !== 'Tab') return; if (e.shiftKey && document.activeElement === firstElement) { lastElement.focus(); e.preventDefault(); } else if (!e.shiftKey && document.activeElement === lastElement) { firstElement.focus(); e.preventDefault(); } }; element.addEventListener('keydown', handler); // Return cleanup function return () => { element.removeEventListener('keydown', handler); if (previousFocus && typeof previousFocus.focus === 'function') { previousFocus.focus(); } }; } makeNavigable(elements, activationCallback) { if (!elements || !elements.length) return; Array.from(elements).forEach(element => { if (element.getAttribute('data-keyboard-nav')) return; element.setAttribute('data-keyboard-nav', 'true'); element.setAttribute('tabindex', '0'); element.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (activationCallback) { activationCallback(element); } else { const link = element.querySelector('a'); if (link) link.click(); } } }); }); } } document.addEventListener('DOMContentLoaded', function() { window.jvbA11y = new A11yHelper(); });