1/* Universal site script: injects shared header/footer partials, then wires up 2 nav, FAQ accordion, scroll reveals, and the footer year â on every page. */ 3 (function () { 4 "use strict"; 5 6 function injectPartials() { 7 var slots = Array.prototype.slice.call(document.querySelectorAll("[data-include]")); 8 9 return Promise.all( 10 slots.map(function (el) { 11 return fetch(el.getAttribute("data-include")) 12 .then(function (r) { return r.text(); }) 13 .then(function (html) { 14 el.innerHTML = html; 15 }) 16 .catch(function (err) { 17 console.error("Partial failed to load:", err); 18 }); 19 }) 20 ); 21 } 22 23 function initNav() { 24 var header = document.querySelector(".site-header"); 25 26 // FIX: header may not exist (injected partial not ready yet or missing page) 27 if (header) { 28 var onScroll = function () { 29 header.classList.toggle("is-scrolled", window.scrollY > 24); 30 }; 31 32 window.addEventListener("scroll", onScroll, { passive: true }); 33 onScroll(); 34 } 35 36 var toggle = document.querySelector(".nav__toggle"); 37 var menu = document.getElementById("nav-menu"); 38 39 if (toggle && menu) { 40 var setMenu = function (open) { 41 toggle.setAttribute("aria-expanded", String(open)); 42 toggle.setAttribute("aria-label", open ? "Close menu" : "Open menu"); 43 menu.classList.toggle("is-open", open); 44 document.body.classList.toggle("menu-open", open); 45 }; 46 47 toggle.addEventListener("click", function () { 48 setMenu(toggle.getAttribute("aria-expanded") !== "true"); 49 }); 50 51 menu.addEventListener("click", function (e) { 52 if (e.target.closest("a")) setMenu(false); 53 }); 54 55 document.addEventListener("keydown", function (e) { 56 if (e.key === "Escape" && menu.classList.contains("is-open")) { 57 setMenu(false); 58 toggle.focus(); 59 } 60 }); 61 } 62 63 var year = document.getElementById("year"); 64 if (year) year.textContent = String(new Date().getFullYear()); 65 } 66 67 function initFAQ() { 68 var items = document.querySelectorAll(".faq__item"); 69 70 items.forEach(function (item) { 71 item.addEventListener("toggle", function () { 72 if (!item.open) return; 73 74 items.forEach(function (other) { 75 if (other !== item) other.open = false; 76 }); 77 }); 78 }); 79 } 80 81 function initReveals() { 82 var reveals = document.querySelectorAll(".reveal"); 83 84 if (!("IntersectionObserver" in window)) { 85 reveals.forEach(function (el) { 86 el.classList.add("is-visible"); 87 }); 88 return; 89 } 90 91 var io = new IntersectionObserver(function (entries) { 92 entries.forEach(function (entry) { 93 if (entry.isIntersecting) { 94 entry.target.classList.add("is-visible"); 95 io.unobserve(entry.target); 96 } 97 }); 98 }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" }); 99 100 reveals.forEach(function (el) { 101 io.observe(el); 102 }); 103 } 104 105 function initMarquee() { 106 var marquees = document.querySelectorAll(".marquee"); 107 if (!marquees.length) return; 108 109 marquees.forEach(function (marquee) { 110 var tracks = marquee.querySelectorAll(".marquee__track"); 111 112 tracks.forEach(function (track) { 113 var list = track.querySelector(".marquee__list"); 114 if (!list) return; 115 116 // Match perceived speed across rows regardless of list length. 117 var pxPerSec = 36; 118 var duration = Math.max(30, list.scrollWidth / pxPerSec); 119 track.style.setProperty("--speed", duration.toFixed(1) + "s"); 120 121 // Duplicate the list so the loop is seamless. 122 var clone = list.cloneNode(true); 123 clone.setAttribute("aria-hidden", "true"); 124 track.appendChild(clone); 125 }); 126 127 marquee.classList.add("is-ready"); 128 }); 129 } 130 131 // Run page features that exist immediately 132 initFAQ(); 133 initReveals(); 134 initMarquee(); 135 136 // Inject header/footer, then init nav AFTER DOM exists 137 injectPartials().then(function () { 138 requestAnimationFrame(function () { 139 setTimeout(function () { 140 initNav(); 141 }, 0); 142 }); 143 }); 144 145 })();
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.