/*
|
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:
|
<a class="open-gallery" data-opens="gallery-id" data-focus="image-to-show">
|
<figure><img...></figure>
|
</a>
|
|
<dialog class="gallery" id="gallery-id">
|
- Main image display (.images with multiple <figure>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
|
</dialog>
|
|
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);
|
}
|
}
|