(() => { let links = []; let linkById = new Map(); let io = null; // Maps ->
function indexLinksAndObserve() { links = Array.from(document.querySelectorAll('a.doc-group-link[href*="#"]')); linkById.clear(); links.forEach(a => { // Supports absolute/relative hrefs and with path const raw = a.getAttribute('href'); const url = new URL(raw, location.origin); const id = url.hash.replace(/^#/, ''); if (!id) return; a.dataset.targetId = id; linkById.set(id, a); }); // Create/renew an IntersectionObserver for the target sections if (io) io.disconnect(); io = new IntersectionObserver(onIntersect, { // Treat the section as "active" when it enters the central band of the viewport root: null, rootMargin: "-35% 0px -55% 0px", threshold: [0, 1] }); linkById.forEach((_a, id) => { const el = document.getElementById(id); if (el) io.observe(el); }); // If there's already a hash in the URL, mark it as active on load if (location.hash) setActive(location.hash.slice(1)); } // Mark a single link as active function setActive(id) { links.forEach(a => a.classList.toggle('w--current', a.dataset.targetId === id)); } // Choose the most "visible" section at the moment function onIntersect(entries) { const visible = entries .filter(e => e.isIntersecting) .sort((a, b) => b.intersectionRatio - a.intersectionRatio); if (visible[0]) setActive(visible[0].target.id); } // Smooth scroll and immediate feedback when clicking a menu link (same page) document.addEventListener('click', (ev) => { const a = ev.target.closest('a.doc-group-link[href*="#"]'); if (!a) return; const raw = a.getAttribute('href'); const url = new URL(raw, location.origin); // If the link points to another page, let the browser navigate if (url.pathname !== location.pathname) return; const id = url.hash.replace(/^#/, ''); const target = document.getElementById(id); if (!target) return; ev.preventDefault(); target.scrollIntoView({ behavior: 'smooth', block: 'start' }); setActive(id); // instant feedback history.pushState(null, '', '#' + id); // update the URL }, true); // If the hash changes for other reasons (e.g., back/forward) window.addEventListener('hashchange', () => { const id = location.hash.slice(1); if (id) setActive(id); }); // Since the menu is injected later, watch the DOM and index when it appears const mo = new MutationObserver((mutations) => { for (const m of mutations) { if ([...m.addedNodes].some(n => n.nodeType === 1 && n.querySelector?.('a.doc-group-link'))) { indexLinksAndObserve(); break; } } }); mo.observe(document.body, { childList: true, subtree: true }); // If it happens to already be in the DOM, index now if (document.querySelector('a.doc-group-link')) indexLinksAndObserve(); })();