/* global window, document */ (function () { 'use strict'; var Luxtools = window.Luxtools || (window.Luxtools = {}); // ============================================================ // Gallery Thumbnails Module // ============================================================ // Uses fetch() with AbortController to load thumbnails. // This allows true HTTP request cancellation on navigation, // unlike native loading="lazy" where queued requests block. Luxtools.GalleryThumbnails = (function () { var controller = null; var maxConcurrent = 4; var activeCount = 0; var queue = []; function abortAll() { if (controller) { controller.abort(); controller = null; } queue = []; activeCount = 0; } function processQueue() { if (!controller) return; while (activeCount < maxConcurrent && queue.length > 0) { var img = queue.shift(); loadThumb(img); } } function loadThumb(img) { if (!controller) return; var src = img.getAttribute('data-src'); if (!src) { processQueue(); return; } activeCount++; fetch(src, { signal: controller.signal }) .then(function (response) { if (!response.ok) throw new Error('HTTP ' + response.status); return response.blob(); }) .then(function (blob) { img.src = URL.createObjectURL(blob); img.removeAttribute('data-src'); }) .catch(function (err) { // Aborted or failed - ignore if (err.name !== 'AbortError') { // Keep data-src for potential retry, just log } }) .finally(function () { activeCount--; processQueue(); }); } function queueThumb(img) { if (!controller) return; if (!img.getAttribute('data-src')) return; if (img.getAttribute('data-queued') === '1') return; img.setAttribute('data-queued', '1'); queue.push(img); processQueue(); } function init() { var imgs = document.querySelectorAll( 'div.luxtools-gallery img.luxtools-thumb[data-src], div.luxtools-imagebox img[data-src]' ); if (!imgs || !imgs.length) return; // Create abort controller for all requests controller = new AbortController(); // Abort all pending requests on navigation window.addEventListener('beforeunload', abortAll); window.addEventListener('pagehide', abortAll); // Use IntersectionObserver to trigger loading if ('IntersectionObserver' in window) { var io = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; queueThumb(entry.target); io.unobserve(entry.target); }); }, { rootMargin: '200px' }); imgs.forEach(function (img) { io.observe(img); }); } else { // Fallback: queue all imgs.forEach(queueThumb); } } return { init: init }; })(); })();