MediaWiki:AutoLoad.js

From Psalms: Layer by Layer
Jump to: navigation, search

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;
  });
});