diff --git a/file.php b/file.php index e253416..b85c0ea 100644 --- a/file.php +++ b/file.php @@ -10,6 +10,15 @@ if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../'); if (!defined('DOKU_DISABLE_GZIP_OUTPUT')) define('DOKU_DISABLE_GZIP_OUTPUT', 1); // we gzip ourself here require_once(DOKU_INC . 'inc/init.php'); +// Close the session early to prevent blocking concurrent requests. +// PHP sessions are locked by default - if we hold the lock during thumbnail +// generation, all other requests from this user (including page navigation) +// will be blocked until we finish. Since we only need session data for ACL +// checks (which happen before this point via init.php), we can safely close it. +if (function_exists('session_status') && session_status() === PHP_SESSION_ACTIVE) { + session_write_close(); +} + global $INPUT; $syntax = plugin_load('syntax', 'luxtools'); diff --git a/images/placeholder.svg b/images/placeholder.svg new file mode 100644 index 0000000..8c12f1e --- /dev/null +++ b/images/placeholder.svg @@ -0,0 +1,5 @@ + diff --git a/js/gallery-thumbnails.js b/js/gallery-thumbnails.js index 1c9d1a1..e532266 100644 --- a/js/gallery-thumbnails.js +++ b/js/gallery-thumbnails.js @@ -8,46 +8,15 @@ // ============================================================ // Gallery Thumbnails Module // ============================================================ + // Thumbnail loading now relies on native loading="lazy" attribute. + // The browser handles deferred loading, connection limits, and + // automatic cancellation on navigation. + // + // This module is kept as a stub for potential future enhancements. Luxtools.GalleryThumbnails = (function () { - function loadThumb(img) { - var src = img.getAttribute('data-thumb-src') || ''; - if (!src) return; - if (img.getAttribute('data-thumb-loading') === '1') return; - img.setAttribute('data-thumb-loading', '1'); - - var pre = new window.Image(); - pre.onload = function () { - img.src = src; - img.removeAttribute('data-thumb-src'); - img.removeAttribute('data-thumb-loading'); - }; - pre.onerror = function () { - img.removeAttribute('data-thumb-loading'); - }; - pre.src = src; - } - function init() { - // Handle both gallery and imagebox thumbnails - var imgs = document.querySelectorAll('div.luxtools-gallery img[data-thumb-src], div.luxtools-imagebox img[data-thumb-src]'); - if (!imgs || !imgs.length) return; - - if ('IntersectionObserver' in window) { - var io = new window.IntersectionObserver(function (entries) { - entries.forEach(function (entry) { - if (!entry.isIntersecting) return; - loadThumb(entry.target); - io.unobserve(entry.target); - }); - }, { rootMargin: '200px' }); - - imgs.forEach(function (img) { io.observe(img); }); - } else { - // Fallback: load soon after initial render - window.setTimeout(function () { - imgs.forEach(loadThumb); - }, 0); - } + // Native lazy loading handles everything. + // No JavaScript intervention needed. } return { diff --git a/src/Output.php b/src/Output.php index e4d786e..d96e61a 100644 --- a/src/Output.php +++ b/src/Output.php @@ -67,17 +67,17 @@ class Output $genThumbW = (int)max(1, (int)round($thumbW * $thumbScale)); $genThumbH = (int)max(1, (int)round($thumbH * $thumbScale)); + // Build placeholder URL from config (DokuWiki media ID) + $placeholderStyle = ''; $placeholderId = $syntax ? trim((string)$syntax->getConf('thumb_placeholder')) : ''; + if ($placeholderId !== '' && function_exists('ml')) { + $placeholderUrl = ml($placeholderId, ['w' => $thumbW, 'h' => $thumbH], true, '&'); + $placeholderStyle = ' style="--luxtools-placeholder: url(' . hsc($placeholderUrl) . ')"'; + } /** @var \Doku_Renderer_xhtml $renderer */ $renderer = $this->renderer; - $renderer->doc .= '