1(function () { 2 "use strict"; 3 4 const ready = (fn) => { 5 if (document.readyState === "loading") { 6 document.addEventListener("DOMContentLoaded", fn, { once: true }); 7 } else { 8 fn(); 9 } 10 }; 11 12 ready(() => { 13 const body = document.body; 14 const navBtn = document.getElementById("nav-btn"); 15 const navBtnText = navBtn ? navBtn.querySelector(".nav-btn__text") : null; 16 const globalNav = document.getElementById("global-nav"); 17 18 // Create overlay if the template hasn't been updated yet (prevents hard-fail). 19 let navOverlay = document.getElementById("nav-overlay"); 20 if (!navOverlay) { 21 navOverlay = document.createElement("div"); 22 navOverlay.id = "nav-overlay"; 23 navOverlay.className = "nav-overlay"; 24 navOverlay.setAttribute("hidden", ""); 25 document.body.appendChild(navOverlay); 26 } 27 28 // Optional: search modal (may not exist on some templates) 29 const searchBtn = document.getElementById("search-btn"); 30 const searchPanel = document.getElementById("search-panel"); 31 32 if (!body || !navBtn || !globalNav) return; 33 const MOBILE_BREAKPOINT = 948; 34 const mqMobile = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`); 35 const isMobile = () => mqMobile.matches; 36 37 const setHidden = (el, hidden) => { 38 if (!el) return; 39 if (hidden) el.setAttribute("hidden", ""); 40 else el.removeAttribute("hidden"); 41 }; 42 43 const setNavExpanded = (expanded) => { 44 navBtn.setAttribute("aria-expanded", expanded ? "true" : "false"); 45 navBtn.classList.toggle("show", expanded); // keeps existing sprite swap working 46 navBtn.setAttribute( 47 "aria-label", 48 expanded ? "ã¡ãã¥ã¼ãéãã" : "ã¡ãã¥ã¼" 49 ); 50 51 // Visible label swap (Menu <-> Close) 52 if (navBtnText) { 53 navBtnText.textContent = expanded ? "" : ""; 54 } 55 }; 56 57 const openOverlay = () => setHidden(navOverlay, false); 58 59 const closeOverlayIfNothingOpen = () => { 60 const navOpen = body.classList.contains("is-nav-open"); 61 const searchOpen = body.classList.contains("is-search-open"); 62 if (!navOpen && !searchOpen) setHidden(navOverlay, true); 63 }; 64 65 const closeNav = () => { 66 body.classList.remove("is-nav-open"); 67 globalNav.classList.remove("open-menu"); 68 setNavExpanded(false); 69 closeOverlayIfNothingOpen(); 70 71 // Clean up any leftover inline styles from legacy toggles 72 const ul = globalNav.querySelector("ul"); 73 if (ul && ul.getAttribute("style")) ul.removeAttribute("style"); 74 }; 75 76 const openNav = () => { 77 body.classList.add("is-nav-open"); 78 globalNav.classList.add("open-menu"); 79 setNavExpanded(true); 80 openOverlay(); 81 }; 82 83 const closeSearch = () => { 84 if (!searchPanel) return; 85 body.classList.remove("is-search-open"); 86 if (searchBtn) searchBtn.setAttribute("aria-expanded", "false"); 87 setHidden(searchPanel, true); 88 closeOverlayIfNothingOpen(); 89 }; 90 91 const openSearch = () => { 92 if (!searchPanel) return; 93 closeAll(); 94 body.classList.add("is-search-open"); 95 if (searchBtn) searchBtn.setAttribute("aria-expanded", "true"); 96 setHidden(searchPanel, false); 97 openOverlay(); 98 99 const input = searchPanel.querySelector( 100 'input[type="search"], input[type="text"]' 101 ); 102 if (input) window.setTimeout(() => input.focus(), 0); 103 }; 104 105 const closeAll = () => { 106 closeNav(); 107 closeSearch(); 108 setHidden(navOverlay, true); 109 }; 110 111 // Menu button 112 navBtn.addEventListener("click", (e) => { 113 e.preventDefault(); 114 115 // Mobile: drawer toggle 116 if (isMobile()) { 117 const willOpen = !body.classList.contains("is-nav-open"); 118 closeAll(); 119 if (willOpen) openNav(); 120 return; 121 } 122 123 // Desktop/tablet: do nothing (nav is visible by default) 124 }); 125 126 // Overlay click closes everything 127 navOverlay.addEventListener("click", closeAll); 128 129 // Close mobile nav when clicking a nav link 130 globalNav.addEventListener("click", (e) => { 131 if (!isMobile()) return; 132 const target = e.target; 133 if (!(target instanceof Element)) return; 134 const link = target.closest("a"); 135 if (!link) return; 136 closeNav(); 137 }); 138 139 // Search button (optional) 140 if (searchBtn && searchPanel) { 141 searchBtn.addEventListener("click", (e) => { 142 e.preventDefault(); 143 const willOpen = searchPanel.hasAttribute("hidden"); 144 closeAll(); 145 if (willOpen) openSearch(); 146 }); 147 } 148 149 // ESC closes 150 document.addEventListener("keydown", (e) => { 151 if (e.key === "Escape") closeAll(); 152 }); 153
154 // Breakpoint changes: reset state 155 // MediaQueryList change listener: addEventListener('change') is modern; addListener is legacy Safari. 156 if (typeof mqMobile.addEventListener === "function") { 157 mqMobile.addEventListener("change", closeAll); 158 } else if (typeof mqMobile.addListener === "function") { 159 mqMobile.addListener(closeAll); 160 } 161 162 // Front page: header band appears after scroll 163 const applyScrolledClass = () => { 164 if (!body.classList.contains("home")) return; 165 body.classList.toggle("is-scrolled", window.scrollY > 20); 166 }; 167 168 window.addEventListener("scroll", applyScrolledClass, { passive: true }); 169 applyScrolledClass(); 170 171 // Initial state 172 setHidden(navOverlay, true); 173 setNavExpanded(false); 174 }); 175})(); 176 177(() => { 178 const map = { 179 "#purchase": "sw2-tab-1", 180 "#support": "sw2-tab-2" 181 }; 182 183 const applyHash = () => { 184 const id = map[location.hash]; 185 if (!id) return; 186 const radio = document.getElementById(id); 187 if (radio) radio.checked = true; 188 }; 189 190 window.addEventListener("hashchange", applyHash, { passive: true }); 191 applyHash(); 192})();
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.