/* SITE GALLERY MANAGER Handles two main functions: 1. Responsive Images - Automatically loads appropriate image sizes based on screen width - Listens for resize events and updates images accordingly - Uses data-small, data-medium, and data-full attributes on images 2. Gallery Management - HTML Structure:
- Main image display (.images with multiple
s) - Navigation bar on bottom (nav.thumbnails) with: - Previous/Next buttons - Thumbnail list (toggles with .open class) - Expand button (toggles .expanded class) - Visibility toggle (toggles .hide-info class) - Close button
Key Features: - Opens in modal dialog with keyboard navigation - Manages thumbnail navigation state - Handles responsive image loading - Uses class-based state management (open, expanded, hide-info) - All event handling through delegation - Built on UIHandler base class for state management State Management: - Dialog: open/closed - Thumbnails: collapsed/open/expanded - Info panel: visible/hidden - Active image: tracked with .focused class */ class Media { constructor() { this.currentWidth = window.innerWidth; this.initElements(); if (this.images.length === 0) { return; } this.store = new window.jvbStore({ name: 'images', TTL: 604800 }); this.touch = { x: null, y: null } this.a11y = window.jvbA11y; this.debouncer = window.debouncer; this.isTouching = false; this.initListeners(); document.addEventListener('beforeunload', this.cleanup); } initElements(){ this.images = document.querySelectorAll('.wp-site-blocks img'); this.gallery = document.querySelector('dialog.gallery'); this.modal = new window.jvbModal(this.gallery, { openMessage: 'Opened Gallery', closeMessage: 'Closed Gallery', open: '.open-gallery', close: '.close' }); this.modal.subscribe((event, data) => { if (event === 'modal-open') { this.openGallery(); } else if (event === 'modal-close') { this.closeGallery(); } }); } initListeners() { this.resizeHandler = this.handleResize.bind(this); this.clickHandler = this.handleClick.bind(this); this.keysHandler = this.handleKeys.bind(this); this.touchStartHandler = this.handleTouchStart.bind(this); this.touchEndHandler = this.handleTouchEnd.bind(this); document.addEventListener('click', this.clickHandler); window.addEventListener('resize', this.resizeHandler); console.log('window hash: ',window.location.hash); let target = document.querySelector(window.location.hash); if (target && target.tagName === 'LI') { let trigger = target.querySelector('.open-gallery'); if (trigger) { this.openGallery(trigger); } } this.observer = new IntersectionObserver((entries) =>{ entries.forEach(entry => { if (entry.isIntersecting) { let img = entry.target; if (!img.closest('dialog')) { this.loadAppropriateImage(img); this.observer.unobserve(img); } } }) }, { root: null, rootMargin: '50px', threshold: .1 }); this.images.forEach(img => { if (!img.closest('dialog')) { this.observer.observe(img); } }) } initTouchHandling() { this.isTouching = true; this.touch.x = 0; this.touch.y = 0; this.gallery.addEventListener('touchstart', this.touchStartHandler); this.gallery.addEventListener('touchend', this.touchEndHandler); } cancelTouchHandling() { this.isTouching = false; this.gallery.removeEventListener('touchstart', this.touchStartHandler); this.gallery.removeEventListener('touchend', this.touchEndHandler); } handleTouchStart(e) { this.touch.x = e.touches[0].clientX; this.touch.y = e.touches[0].clientY; } handleTouchEnd(e) { const diffX = e.changedTouches[0].clientX - this.touch.x; const diffY = e.changedTouches[0].clientY - this.touch.y; if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 50) { this.navigateImages({target: this.gallery}, diffX < 0 ? 'next' : 'previous'); } } handleResize(e) { window.debouncer.schedule( 'resize', ()=> { const currentWidth = window.innerWidth; if (Math.abs(currentWidth - this.currentWidth) > 100) { this.currentWidth = currentWidth; this.handleImageResize(); } }, 150 ); } handleClick(e) { } handleKeys(e) { //Escape handled by Modal.js if (e.key === 'Tab') { if (e.shiftKey) { } else { } } } openGallery(target = null) { document.addEventListener('keydown', this.clickHandler); const id = target.dataset.opens; this.initTouchHandling(); let focus; if (!target) { focus = this.images[0]; } else { focus = target.dataset.focus; } if (focus) { this.updateImage(this.gallery.querySelector(`#${focus}`)); } } closeGallery() { this.cancelTouchHandling(); document.removeEventListener('keydown', this.keysHandler); //Restore focus if (this.lastFocusedElement) { this.lastFocusedElement.focus(); } } cleanup() { this.observer.disconnect(); window.removeEventListener('resize', this.resizeHandler) if (this.isTouching) { this.cancelTouchHandling(); document.removeEventListener('keydown', this.keysHandler); } document.removeEventListener('click', this.clickHandler); } }