MediaWiki:AutoLoad.js
From Psalms: Layer by Layer
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
// =========================== // Begin placeholder auto-loading // =========================== var loadQueue = []; var isQueueRunning = false; var observer; var manualScrollInProgress = false; var tocClicked; var backgroundQueue = []; var isBackgroundLoading = false; function processBackgroundQueue() { if (manualScrollInProgress || isBackgroundLoading) return; if (backgroundQueue.length === 0) return; var target = backgroundQueue.shift(); if (!target || target.dataset.loaded === 'true') { setTimeout(processBackgroundQueue, 3000); // next in 3s return; } isBackgroundLoading = true; loadChunk(target, function () { setTimeout(function () { attachToggleListeners(); initializeLazyLoadedEnhancements(target); window.renderMermaidIn(target); isBackgroundLoading = false; processBackgroundQueue(); // load next }, 100); }, false); // not a manual load } function loadChunk(placeholder, onDone, isManualLoad) { try { if (!placeholder || !placeholder.id) { console.warn("Missing or invalid placeholder element:", placeholder); if (onDone) onDone(); return; } var id = placeholder.id; // ✅ Check if the *next element* contains an element with the tocClicked ID if (isManualLoad && typeof tocClicked === 'string') { var sibling = placeholder.nextElementSibling; while (sibling) { if (sibling.querySelector && sibling.querySelector('#' + CSS.escape(tocClicked))) { console.log("[LazyLoad] Skipping placeholder", id, "in favor of tocClicked found in later sibling:", tocClicked); id = tocClicked; tocClicked = null; break; } sibling = sibling.nextElementSibling; } } var basePage = mw && mw.config ? mw.config.get('wgPageName') : null; if (!basePage) { console.error("Failed to retrieve page name from mw.config."); placeholder.innerHTML = '<div class="load-error">Cannot determine base page name.</div>'; if (onDone) onDone(); return; } var sourcePage = placeholder.getAttribute("data-src"); var nextPage = null; if (sourcePage){ nextPage = sourcePage; } else{ basePage = basePage.replace(/Test/g, ""); var cleanedId = id.replace(/^(vv|v|verse)[\.\_\-\s]*/i, ''); nextPage = basePage + "/" + cleanedId; } console.log('Seeking to load ' + nextPage + ' (from original ' + id + ')'); var xhr = new XMLHttpRequest(); xhr.open('GET', '/w/' + nextPage + '?action=render', true); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { placeholder.innerHTML = xhr.responseText; placeholder.dataset.loaded = 'true'; // ensure collapsibles collapse as needed mw.loader.using('jquery.makeCollapsible', function () { $('.mw-collapsible').makeCollapsible(); }); } else if (xhr.status === 404) { placeholder.innerHTML = '<div class="missing-chunk">This section is not yet available.</div>'; placeholder.dataset.loaded = 'true'; } else { placeholder.innerHTML = '<div class="load-error">Error loading section (status ' + xhr.status + ').</div>'; placeholder.dataset.loaded = 'true'; } if (onDone) onDone(); } }; xhr.onerror = function () { console.error("Network error while loading:", nextPage); placeholder.innerHTML = '<div class="load-error">Network error loading section.</div>'; placeholder.dataset.loaded = 'true'; if (onDone) onDone(); }; xhr.send(); } catch (err) { console.error("Unexpected error in loadChunk:", err); if (placeholder) { placeholder.innerHTML = '<div class="load-error">Unexpected error loading section.</div>'; placeholder.dataset.loaded = 'true'; } if (onDone) onDone(); } } function renderMermaidIn(container) { if (!container) { console.warn("⚠️ No container provided to renderMermaidIn"); return; } function tryRender() { if (typeof window.mermaid === 'undefined' || typeof window.mermaid.init !== 'function') { console.log("⏳ Mermaid not ready yet, retrying..."); setTimeout(tryRender, 250); return; } var blocks = container.querySelectorAll('pre.mermaid'); if (blocks.length === 0) { console.log("ℹ️ No Mermaid blocks found in container:", container.id || container); return; } console.log("✅ Rendering", blocks.length, "Mermaid blocks in container:", container.id || container); try { window.mermaid.init(undefined, blocks); } catch (e) { console.error("❌ Error rendering Mermaid:", e); } } tryRender(); } function whenMermaidReady(callback, retries) { retries = retries || 20; if (typeof mermaid !== 'undefined' && typeof mermaid.init === 'function') { callback(); } else if (retries > 0) { setTimeout(function () { whenMermaidReady(callback, retries - 1); }, 100); } else { console.warn("Mermaid is not ready after retries"); } } function processQueueSequentially() { if (manualScrollInProgress) { return; // skip auto loading while user is navigating } if (loadQueue.length === 0) { isQueueRunning = false; return; } var target = loadQueue.shift(); // ⏹ Skip if already loaded if (target.dataset.loaded === 'true') { processQueueSequentially(); return; } // 📏 Check if still within 200px of viewport var rect = target.getBoundingClientRect(); var threshold = 200; // px margin above and below viewport var isVisible = ( rect.top < (window.innerHeight + threshold) && rect.bottom > -threshold ); if (!isVisible) { // Not close enough — skip this one and move on processQueueSequentially(); return; } loadChunk(target, function () { setTimeout(function () { observer.disconnect(); observePlaceholders(); attachToggleListeners(); initializeLazyLoadedEnhancements(target); setTimeout(function () { window.renderMermaidIn(target); }, 200); processQueueSequentially(); }, 50); }, false); } function startQueueProcessor() { if (isQueueRunning) return; isQueueRunning = true; processQueueSequentially(); } // Start observing placeholders function observePlaceholders() { document.querySelectorAll('.lazy-chunk').forEach(function (el) { if (!el.dataset.loaded) { observer.observe(el); } }); } // =========================== // End placeholder auto-loading // =========================== $(document).ready(function () { // Initialize observer observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { var target = entry.target; if (!target.dataset.loaded && !loadQueue.includes(target)) { loadQueue.push(target); } } }); startQueueProcessor(); }, { rootMargin: '100px' }); // Start observing on page load observePlaceholders(); // background loading document.querySelectorAll('.lazy-chunk').forEach(function (el) { if (!el.dataset.loaded && !backgroundQueue.includes(el)) { backgroundQueue.push(el); } }); // Lazy-load tabs on click document.querySelectorAll('[data-toggle="tab"]').forEach(function (tabBtn) { tabBtn.addEventListener("click", function () { var tabId = tabBtn.getAttribute("aria-controls"); var target = document.getElementById(tabId); // Only lazy-load if the tab pane has class 'lazy-chunk' and isn't already loaded if (!target || !target.classList.contains("lazy-chunk") || target.dataset.loaded === 'true') return; console.log("[TabLazyLoad] Triggered for tab:", tabId); loadChunk(target, function () { attachToggleListeners(); initializeLazyLoadedEnhancements(target); //renderMermaidIn(target); }, true); // manual load }); }); // Start slow background loading after a short delay setTimeout(processBackgroundQueue, 3000); // wait a few seconds to let user interact }); $(document).ready(function () { $('.toc-link[href^="#"]').on('click', function (e) { var hash = this.getAttribute('href'); if (!hash || hash.length < 2) return; var targetId = decodeURIComponent(hash.substring(1)); console.log("🔎 Clicked TOC link to:", targetId); tocClicked = targetId; }); });