1/** IntersectionObserver-based reveal */ 2function setupScrollReveal() { 3 const elements = document.querySelectorAll(".reveal"); 4 const observer = new IntersectionObserver( 5 (entries) => { 6 entries.forEach((entry) => { 7 if (entry.isIntersecting) { 8 const delay = parseInt(entry.target.dataset.delay || "0", 10); 9 setTimeout(() => entry.target.classList.add("is-visible"), delay); 10 observer.unobserve(entry.target); 11 } 12 }); 13 }, 14 { threshold: 0.12 } 15 ); 16 elements.forEach((el) => observer.observe(el)); 17} 18 19/** Smooth scroll with offset handled by CSS scroll-padding; add scrollspy */ 20function setupScrollSpy() { 21 const navLinks = Array.from( 22 document.querySelectorAll('#desktop-nav-links a, #mobile-nav-links a') 23 ); 24 const sections = navLinks 25 .map((a) => document.querySelector(a.getAttribute("href"))) 26 .filter(Boolean); 27 28 function onScroll() { 29 const pos = window.scrollY + 100; 30 let activeIndex = sections.findIndex((s, i) => { 31 const next = sections[i + 1]; 32 return pos >= s.offsetTop && (!next || pos < next.offsetTop); 33 }); 34 35 // Fallback for when no section is "active" (e.g., at the very top) 36 // No, let's highlight the first one if we're above its offset 37 if (pos < sections[0].offsetTop) { 38 activeIndex = 0; 39 } else if (activeIndex === -1 && sections.length > 0) { 40 // If at the bottom, keep the last one active 41 if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 10) { 42 activeIndex = sections.length - 1; 43 } else { 44 activeIndex = 0; // Default to first 45 } 46 } 47 48 49 navLinks.forEach((a, i) => { 50 // UPDATED LOGIC: 51 // Remove active classes from all, then add to the active one. 52 // This is safer than toggling. 53 a.classList.remove("text-light-primary", "dark:text-dark-primary"); 54 a.classList.add("text-light-text", "dark:text-dark-text"); 55 56 if (i === activeIndex) { 57 a.classList.remove("text-light-text", "dark:text-dark-text"); 58 a.classList.add("text-light-primary", "dark:text-dark-primary"); 59 } 60 }); 61 } 62 document.addEventListener("scroll", onScroll, { passive: true }); 63 onScroll(); // Run on load 64} 65 66/** Back-to-top button */ 67function setupBackToTop() { 68 const btn = document.getElementById("back-to-top"); 69 function toggle() { 70 if (window.scrollY > 600) { 71 btn.classList.add("show"); 72 btn.classList.remove("hidden"); 73 } else { 74 btn.classList.add("hidden"); 75 btn.classList.remove("show"); 76 } 77 } 78 window.addEventListener("scroll", toggle, { passive: true }); 79 btn.addEventListener("click", () => window.scrollTo({ top: 0, behavior: "smooth" })); 80 toggle(); 81}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.