From 0afb2c0046b55c123eafb4ab9ee77efa68d12463 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sat, 06 Jun 2026 17:15:31 +0000
Subject: [PATCH] =Starting the Favourites.js setup, converting previous Northeh stuff to new Registrar, fixing up Square.php integration to match

---
 assets/js/Gallery.js |  352 +++++++++++++++++++++++++---------------------------------
 1 files changed, 154 insertions(+), 198 deletions(-)

diff --git a/assets/js/Gallery.js b/assets/js/Gallery.js
index e952b26..9c596c4 100644
--- a/assets/js/Gallery.js
+++ b/assets/js/Gallery.js
@@ -1,272 +1,228 @@
 class Gallery {
-	constructor(modal, config) {
-		this.imageWrapper = config.imageWrapper??null;
-		this.container = config.container ? document.querySelector(config.container) : document.querySelector('main');
-		this.modal = new window.jvbModal(
-			modal,
-			{
-				onOpen: false,
-				onSave: false,
-				onClose: () => this.closeGallery(false)
-			}
-		);
-		this.items = this.getGalleryItems() || [];
-		this.loadMore = config.loadMore??false;
+	constructor(modal, config = {}) {
+		this.container = config.container
+			? document.querySelector(config.container)
+			: document.querySelector('main');
 
-		this.modalElement = (typeof modal === 'string') ? document.querySelector(modal)??false : modal;
-		if (!this.modal) {
-			return;
-		}
+		this.gallerySelector = config.gallerySelector || 'img[data-small]';
+
+		this.modal = new window.jvbModal(modal, {
+			onOpen: false,
+			onSave: false,
+			onClose: () => this.closeGallery(false)
+		});
+
+		this.modalElement = (typeof modal === 'string')
+			? document.querySelector(modal)
+			: modal;
+
+		if (!this.modalElement) return;
 
 		this.a11y = window.jvbA11y;
-		this.openWhenReady = false;
-
-
-		this.initElements();
-		this.imageWrapper = (typeof this.imageWrapper === 'string') ? this.imageWrapper : '.'+this.imageWrapper.classList.join('.');
-
 		this.index = 0;
+		this.items = [];
+
 		this.swipe = {
 			touchStart: null,
 			touchEnd: null,
 			minSwipe: 50,
 		};
-		this.isLoading = false;
 
+		this.initElements();
+		this.initListeners();
 	}
 
 	initElements() {
 		this.prevBtn = this.modalElement.querySelector('.prev');
 		this.nextBtn = this.modalElement.querySelector('.next');
-		this.favourite = this.modalElement.querySelector('.favourite');
 		this.image = this.modalElement.querySelector('.image');
 		this.counter = this.modalElement.querySelector('.counter');
-		this.extra = this.modalElement.querySelector('.item-info');
-
-		//If we don't have the wrapper set up, we can remove these elements
-		if (!this.imageWrapper) {
-			this.favourite.remove();
-			this.extra.remove();
-		}
-	}
-
-	getGalleryItems() {
-		let search = (this.imageWrapper) ? this.imageWrapper : 'img';
-
-		return Array.from(this.container.querySelectorAll(search))
-			.map(item => {
-				const img = (this.imageWrapper) ? item.querySelector('img') : item;
-				if (!img) return null;
-
-				return {
-					id: (this.imageWrapper) ? item.querySelector('button.favourite').dataset.id : '',
-					small: img.dataset.small || img.src,
-					large: img.dataset.large || img.src,
-					full: img.dataset.full || img.src,
-					alt: img.alt || '',
-					fav: (this.imageWrapper) ? item.querySelector('button.favourite')?.cloneNode(true) : '',
-					info: (this.imageWrapper) ? item.querySelector('.item-info')?.cloneNode(true) : ''
-				};
-			}).filter(Boolean);
 	}
 
 	initListeners() {
+		// Delegate click handling to container
 		this.container.addEventListener('click', this.handleClick.bind(this));
-		document.addEventListener('keydown', this.handleKeys.bind(this));
-		document.addEventListener('touchstart', this.handleTouchStart.bind(this));
-		document.addEventListener('touchend', this.handleTouchEnd.bind(this));
-		document.addEventListener('touchmove', this.handleTouchMove.bind(this));
+
+		// Modal-specific listeners added only when open
+		this.boundKeyHandler = this.handleKeys.bind(this);
+		this.boundTouchStart = this.handleTouchStart.bind(this);
+		this.boundTouchMove = this.handleTouchMove.bind(this);
+		this.boundTouchEnd = this.handleTouchEnd.bind(this);
 	}
-	destroyListeners() {
-		this.container.removeEventListener('click', this.handleClick.bind(this));
-		document.removeEventListener('keydown', this.handleKeys.bind(this));
-		document.removeEventListener('touchstart', this.handleTouchStart.bind(this));
-		document.removeEventListener('touchend', this.handleTouchEnd.bind(this));
-		document.removeEventListener('touchmove', this.handleTouchMove.bind(this));
+
+	handleClick(e) {
+		// Open gallery when clicking images
+		const img = e.target.closest(this.gallerySelector);
+		if (img && !this.modal.isOpen) {
+			e.preventDefault();
+			this.openGallery(img);
+			return;
+		}
+
+		// Navigation within gallery
+		if (this.modal.isOpen) {
+			if (e.target.closest('.next')) {
+				this.navigate(1);
+			} else if (e.target.closest('.prev')) {
+				this.navigate(-1);
+			}
+		}
 	}
 
 	handleKeys(e) {
+		if (!this.modal.isOpen) return;
+
 		switch (e.key) {
 			case 'ArrowLeft':
+				e.preventDefault();
 				this.navigate(-1);
 				break;
 			case 'ArrowRight':
+				e.preventDefault();
 				this.navigate(1);
 				break;
 		}
 	}
 
 	handleTouchStart(e) {
+		if (!this.modal.isOpen) return;
 		this.swipe.touchStart = e.touches[0].clientX;
 	}
 
 	handleTouchMove(e) {
+		if (!this.modal.isOpen) return;
 		this.swipe.touchEnd = e.touches[0].clientX;
 	}
 
 	handleTouchEnd(e) {
-		if (!this.swipe.touchStart || !this.swipe.touchEnd) return;
-		const distance = this.swipe.touchStart - this.swipe.touchEnd;
-		const isLeftSwipe = distance > this.swipe.minSwipe;
-		const isRightSwipe = distance < -this.swipe.minSwipe;
+		if (!this.modal.isOpen || !this.swipe.touchStart || !this.swipe.touchEnd) return;
 
-		if (isLeftSwipe) {
-			this.navigate(1);
-		} else if (isRightSwipe) {
-			this.navigate(-1);
+		const distance = this.swipe.touchStart - this.swipe.touchEnd;
+
+		if (Math.abs(distance) > this.swipe.minSwipe) {
+			this.navigate(distance > 0 ? 1 : -1);
 		}
+
 		this.swipe.touchStart = null;
 		this.swipe.touchEnd = null;
 	}
 
-	handleClick(e) {
-		//test if it's a link click, and the click has an image
-		if(window.targetCheck(e, '.feed-images')){
-			this.handleGalleryOpen(e);
-		}
+	buildGalleryItems() {
+		return Array.from(this.container.querySelectorAll(this.gallerySelector))
+			.map((img, index) => ({
+				id: img.dataset.id || index,
+				small: img.dataset.small || img.src,
+				medium: img.dataset.medium || img.src,
+				full: img.dataset.full || img.src,
+				alt: img.alt || '',
+				element: img
+			}));
+	}
 
-		if(window.targetCheck(e, '.nav')) {
-			if (window.targetCheck(e, '.next')) {
-				this.navigate(1);
-			} else {
-				this.navigate(-1);
-			}
-		}
-		if (window.targetCheck(e, 'button.cancel')) {
-			this.closeGallery();
+	openGallery(clickedImg) {
+		// Build fresh gallery items
+		this.items = this.buildGalleryItems();
+
+		// Find clicked image index
+		this.index = this.items.findIndex(item =>
+			item.element === clickedImg
+		);
+
+		if (this.index === -1) this.index = 0;
+
+		// Attach modal-specific listeners
+		document.addEventListener('keydown', this.boundKeyHandler);
+		document.addEventListener('touchstart', this.boundTouchStart, { passive: true });
+		document.addEventListener('touchmove', this.boundTouchMove, { passive: true });
+		document.addEventListener('touchend', this.boundTouchEnd, { passive: true });
+
+		this.modal.handleOpen();
+		this.updateDisplay();
+		this.preloadAdjacent();
+
+		this.a11y.announce(
+			`Gallery opened. Image ${this.index + 1} of ${this.items.length}. Use arrow keys to navigate.`
+		);
+	}
+
+	closeGallery(useModal = true) {
+		// Remove modal-specific listeners
+		document.removeEventListener('keydown', this.boundKeyHandler);
+		document.removeEventListener('touchstart', this.boundTouchStart);
+		document.removeEventListener('touchmove', this.boundTouchMove);
+		document.removeEventListener('touchend', this.boundTouchEnd);
+
+		if (useModal) {
+			this.modal.handleClose();
 		}
 	}
 
-	async navigate(direction) {
+	navigate(direction) {
 		let newIndex = this.index + direction;
 
-		//Check if out of bounds
-		if (newIndex <0 || newIndex >= this.items.length) {
-			this.a11y.announceNavigation(newIndex, this.items.length, direction < 0, direction > 0);
-			if (newIndex <0) {
-				newIndex = this.items.length - 1;
-			} else {
-				newIndex = 0;
-			}
+		// Wrap around
+		if (newIndex < 0) {
+			newIndex = this.items.length - 1;
+		} else if (newIndex >= this.items.length) {
+			newIndex = 0;
+		} else if (this.items.length - newIndex === 3) {
+			this.notify('load-more');
 		}
 
-		//update index
 		this.index = newIndex;
-
 		this.updateDisplay();
-		this.preloadImages();
+		this.preloadAdjacent();
 
-		this.a11y.announceNavigation(this.index, this.items.length);
-
-		if (typeof this.loadMore === 'function' &&
-			direction > 0 &&
-			newIndex >= (this.items.length - 3)) {
-			if (window.feedBlock.hasMore) {
-				await this.loadMore();
-				this.updateGalleryItems(this.getGalleryItems());
-			}
-		}
-	}
-
-	updateGalleryItems(newItems) {
-		const currentItem = this.items[this.index];
-
-		this.items = newItems;
-		if (currentItem) {
-			const newIndex = this.items.findIndex(item =>
-				item.full === currentItem.full ||
-				item.large === currentItem.large
-			);
-
-			if (newIndex !== -1) {
-				this.index = newIndex;
-			}
-		}
-
-		this.updateNavigationButtons();
-		if(this.items.length > 0 && this.openWhenReady) {
-			let index = this.findIndex('id', this.openWhenReady);
-			this.openGallery(index);
-		}
-	}
-	preloadImages() {
-		[-1,0,1].forEach(offset => {
-			const index = this.index + offset;
-			if (index >=0 && index < this.items.length) {
-				const img = new Image();
-				const item = this.items[index];
-				if (window.innerWidth < 1000) {
-					img.src = item.large || item.src;
-				} else {
-					img.src = item.full || item.src;
-				}
-			}
-		});
+		this.a11y.announce(`Image ${this.index + 1} of ${this.items.length}`);
 	}
 
 	updateDisplay() {
 		const item = this.items[this.index];
 		if (!item) return;
 
-		[
-			this.image.src,
-			this.image.alt,
-			this.counter.textContent
-		] = [
-			(window.innerWidth < 1000) ?
-				(item.large || item.src) :
-				(item.full || item.src),
-			item.alt || '',
-			`${this.index + 1} / ${this.items.length}`
-		];
-		if (this.imageWrapper) {
-			if (item.fav) {
-				window.removeChildren(this.favourite);
-				this.favourite.appendChild(item.fav.cloneNode(true));
+		// Use medium/full based on viewport
+		const src = window.innerWidth < 1000
+			? (item.medium || item.src)
+			: (item.full || item.src);
+
+		this.image.src = src;
+		this.image.alt = item.alt;
+		this.counter.textContent = `${this.index + 1} / ${this.items.length}`;
+
+		// Update button states
+		this.prevBtn.classList.toggle('disabled', this.items.length <= 1);
+		this.nextBtn.classList.toggle('disabled', this.items.length <= 1);
+	}
+
+	preloadAdjacent() {
+		[-1, 1].forEach(offset => {
+			const index = this.index + offset;
+			if (index >= 0 && index < this.items.length) {
+				const item = this.items[index];
+				const img = new Image();
+				img.src = window.innerWidth < 1000
+					? (item.medium || item.src)
+					: (item.full || item.src);
 			}
-			if (item.info) {
-				window.removeChildren(this.extra);
-				this.extra.appendChild(item.info.cloneNode(true));
-			}
-		}
-
-		this.updateNavigationButtons();
+		});
 	}
 
-	updateNavigationButtons() {
-		this.prevBtn.classList.toggle('end', this.index > 0 ? '' : 'none');
-		this.nextBtn.classList.toggle('end', this.index < this.items.length -1 ? '' : 'none');
-	}
-
-	handleGalleryOpen(e) {
-		let item = (this.imageWrapper) ? e.target.closest(this.imageWrapper) : e.target.closest('img');
-		let key = (this.imageWrapper) ? 'id' : 'small';
-		let value = item.dataset[key];
-
-		let index = this.findIndex(key, value);
-		this.openGallery(index);
-	}
-
-	findIndex(property, value) {
-		return this.items.findIndex(obj => obj[property] === value);
-	}
-
-	openGallery(index) {
-		this.initListeners();
-		this.index = index;
-		this.modal.handleOpen();
-		this.updateDisplay();
-		this.preloadImages();
-
-		this.a11y.announce(`Image ${this.index + 1} of ${this.items.length}. Use arrow keys to navigate.`);
-	}
-
-	closeGallery(useModal = true) {
-		this.destroyListeners();
-		if (useModal) {
-			this.modal.handleClose();
-		}
+	cleanup() {
+		this.container.removeEventListener('click', this.handleClick);
+		this.closeGallery(false);
 	}
 }
 
-window.jvbGallery = Gallery;
+
+document.addEventListener('DOMContentLoaded', function() {
+	let galleries = document.querySelectorAll('dialog.gallery');
+	if (galleries.length > 0) {
+		window.galleries = new Map();
+
+		galleries.forEach(gallery => {
+			let id = gallery.id;
+			window.galleries.set(id, new Gallery(gallery));
+		});
+	}
+});
+

--
Gitblit v1.10.0