113 lines
3.1 KiB
JavaScript
113 lines
3.1 KiB
JavaScript
/* 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
|
|
};
|
|
})();
|
|
})();
|