1(() => { 2 "use strict"; 3 4 const page = document.querySelector(".wht-about-page"); 5 if (!page) return; 6 7 const header = page.querySelector("[data-wht-about-header]"); 8 const menuToggle = header?.querySelector("[data-wht-about-menu-toggle]"); 9 const menuPanel = header?.querySelector("[data-wht-about-mobile-panel]"); 10 const menuClose = header?.querySelector("[data-wht-about-menu-close]"); 11 let lastMenuTrigger = null; 12 13 if (header?.dataset.whtAboutHeaderBound === "true") return; 14 if (header) header.dataset.whtAboutHeaderBound = "true"; 15 16 function updateMobilePanelTop() { 17 if (!header) return; 18 const rect = header.getBoundingClientRect(); 19 document.documentElement.style.setProperty( 20 "--wht-vineyard-mobile-panel-top", 21 `${Math.max(0, Math.round(rect.bottom))}px`, 22 ); 23 } 24 25 function positionSubmenus() { 26 if (!header) return; 27 const padding = 16; 28 29 header 30 .querySelectorAll(".wht-vineyard-header__submenu") 31 .forEach((submenu) => { 32 submenu.style.setProperty("--wht-submenu-shift", "0px"); 33 const rect = submenu.getBoundingClientRect(); 34 let shift = 0; 35 36 if (rect.left < padding) shift += padding - rect.left; 37 if (rect.right + shift > window.innerWidth - padding) { 38 shift -= rect.right + shift - (window.innerWidth - padding); 39 } 40 41 submenu.style.setProperty( 42 "--wht-submenu-shift", 43 `${Math.round(shift)}px`, 44 ); 45 }); 46 } 47 48 function setMobileMenu(open, restoreFocus = false) { 49 if (!menuToggle || !menuPanel) return; 50 51 if (open && window.innerWidth > 1199) open = false; 52 53 if (open) { 54 lastMenuTrigger = document.activeElement; 55 updateMobilePanelTop(); 56 menuPanel.hidden = false; 57 menuPanel.setAttribute("aria-hidden", "false"); 58 menuToggle.setAttribute("aria-expanded", "true"); 59 menuToggle.setAttribute("aria-label", "Close menu"); 60 document.body.classList.add("wht-vineyard-mobile-menu-open"); 61 window.requestAnimationFrame(() => 62 menuClose?.focus({ preventScroll: true }), 63 ); 64 return; 65 } 66 67 menuPanel.hidden = true; 68 menuPanel.setAttribute("aria-hidden", "true"); 69 menuToggle.setAttribute("aria-expanded", "false"); 70 menuToggle.setAttribute("aria-label", "Open menu"); 71 document.body.classList.remove("wht-vineyard-mobile-menu-open"); 72 73 if (restoreFocus && lastMenuTrigger instanceof HTMLElement) { 74 lastMenuTrigger.focus({ preventScroll: true }); 75 } 76 } 77 78 if (header) { 79 const menuGroups = Array.from( 80 header.querySelectorAll(".wht-vineyard-header__menu-group"), 81 ); 82 const finePointer = window.matchMedia( 83 "(hover: hover) and (pointer: fine)", 84 ); 85 let activeGroup = null; 86 87 updateMobilePanelTop(); 88 positionSubmenus(); 89 90 const getGroupButton = (group) => 91 group?.querySelector(".wht-vineyard-header__menu-button") || null; 92 93 const setExpanded = (group, expanded, forceClosed = false) => { 94 const button = getGroupButton(group); 95 if (!group || !button) return; 96 97 group.classList.toggle("is-open", expanded); 98 group.classList.toggle( 99 "is-force-closed", 100 !expanded && forceClosed === true, 101 ); 102 button.setAttribute("aria-expanded", expanded ? "true" : "false"); 103 104 if (expanded) activeGroup = group; 105 else if (activeGroup === group) activeGroup = null; 106 }; 107 108 const closeOtherGroups = (except) => { 109 menuGroups.forEach((group) => { 110 if (group !== except) setExpanded(group, false, true); 111 }); 112 }; 113 114 const openGroup = (group) => { 115 closeOtherGroups(group); 116 group.classList.remove("is-force-closed"); 117 setExpanded(group, true); 118 }; 119 120 const closeGroup = (group, restoreFocus = false) => { 121 const button = getGroupButton(group); 122 setExpanded(group, false, true); 123 if (restoreFocus) button?.focus({ preventScroll: true }); 124 }; 125 126 menuGroups.forEach((group) => { 127 const button = getGroupButton(group); 128 const firstLink = group.querySelector( 129 ".wht-vineyard-header__submenu a", 130 ); 131 let suppressNextKeyboardClick = false;
132 if (!button) return; 133 134 group.addEventListener("pointerenter", () => { 135 if (finePointer.matches) openGroup(group); 136 }); 137 138 button.addEventListener("click", (event) => { 139 event.preventDefault(); 140 if (suppressNextKeyboardClick && event.detail === 0) { 141 suppressNextKeyboardClick = false; 142 return; 143 } 144 suppressNextKeyboardClick = false; 145 if (button.getAttribute("aria-expanded") === "true") { 146 closeGroup(group); 147 } else { 148 openGroup(group); 149 } 150 }); 151 152 button.addEventListener("keydown", (event) => { 153 if (event.key === "Enter" || event.key === " ") { 154 event.preventDefault(); 155 suppressNextKeyboardClick = true; 156 if (button.getAttribute("aria-expanded") === "true") { 157 closeGroup(group); 158 } else { 159 openGroup(group); 160 } 161 return; 162 } 163 164 if (event.key === "ArrowDown") { 165 event.preventDefault(); 166 openGroup(group); 167 firstLink?.focus({ preventScroll: true }); 168 } 169 }); 170 171 group.addEventListener("focusout", (event) => { 172 if (!event.relatedTarget || !group.contains(event.relatedTarget)) { 173 closeGroup(group); 174 } 175 }); 176 177 group 178 .querySelectorAll(".wht-vineyard-header__submenu a") 179 .forEach((link) => 180 link.addEventListener("click", () => closeGroup(group)), 181 ); 182 }); 183 184 document.addEventListener("pointerdown", (event) => { 185 if (activeGroup && !activeGroup.contains(event.target)) { 186 closeGroup(activeGroup); 187 } 188 if ( 189 menuToggle?.getAttribute("aria-expanded") === "true" && 190 !header.contains(event.target) && 191 (!menuPanel || !menuPanel.contains(event.target)) 192 ) { 193 setMobileMenu(false); 194 } 195 }); 196 197 document.addEventListener("keydown", (event) => { 198 if (event.key === "Escape" && activeGroup) { 199 event.preventDefault(); 200 event.stopPropagation(); 201 closeGroup(activeGroup, true); 202 } 203 }); 204 } 205 206 menuToggle?.addEventListener("click", () => { 207 setMobileMenu(menuToggle.getAttribute("aria-expanded") !== "true"); 208 }); 209 210 menuClose?.addEventListener("click", () => setMobileMenu(false, true)); 211 212 menuPanel?.addEventListener("click", (event) => { 213 if (event.target.closest("a")) setMobileMenu(false); 214 }); 215 216 menuPanel?.addEventListener("keydown", (event) => { 217 if (event.key === "Escape") { 218 event.preventDefault(); 219 setMobileMenu(false, true); 220 return; 221 } 222 223 if (event.key !== "Tab" || menuPanel.hidden) return; 224 225 const focusable = Array.from( 226 menuPanel.querySelectorAll( 227 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])', 228 ), 229 ).filter( 230 (element) => 231 !element.hidden && element.getAttribute("aria-hidden") !== "true", 232 ); 233 234 if (!focusable.length) { 235 event.preventDefault(); 236 return; 237 } 238 239 const first = focusable[0]; 240 const last = focusable[focusable.length - 1]; 241 242 if (event.shiftKey && document.activeElement === first) { 243 event.preventDefault(); 244 last.focus({ preventScroll: true }); 245 } else if (!event.shiftKey && document.activeElement === last) { 246 event.preventDefault(); 247 first.focus({ preventScroll: true }); 248 } 249 }); 250 251 window.addEventListener( 252 "resize", 253 () => { 254 updateMobilePanelTop(); 255 positionSubmenus(); 256 if (window.innerWidth > 1199) setMobileMenu(false); 257 }, 258 { passive: true }, 259 ); 260 261})();
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.