1/** 2 * Header nav and burger interactions for new home 3 */ 4(function() { 5 var headerBurger = document.querySelector('.header-links-wrap .burger'); 6 var menuBurger = document.querySelector('header nav .mobile-menu-header .burger'); 7 var nav = document.querySelector('header nav'); 8 var body = document.body; 9 10 if (!nav) return; 11 12 function toggleMenu() { 13 nav.classList.toggle('active'); 14 body.classList.toggle('menu-open'); 15 16 if (headerBurger) { 17 headerBurger.classList.toggle('active'); 18 } 19 20 body.style.overflow = nav.classList.contains('active') ? 'hidden' : ''; 21 22 if (nav.classList.contains('active') && window.innerWidth <= 768) { 23 setTimeout(function() { 24 updateVisualViewportHeight(); 25 }, 100); 26 } 27 } 28 29 if (headerBurger) { 30 headerBurger.addEventListener('click', function(e) { 31 e.preventDefault(); 32 e.stopPropagation(); 33 toggleMenu(); 34 }); 35 } 36 37 if (menuBurger) { 38 menuBurger.addEventListener('click', function(e) { 39 e.preventDefault(); 40 e.stopPropagation(); 41 toggleMenu(); 42 }); 43 } 44 45 function isTouchDevice() { 46 return ('ontouchstart' in window) || 47 (navigator.maxTouchPoints > 0) || 48 (navigator.msMaxTouchPoints > 0); 49 } 50 51 function handleSubmenuClick(e) { 52 var link = e.target.closest('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li > a'); 53 if (!link) { 54 link = e.target.closest('header nav > ul > li > a'); 55 } 56 if (!link) return; 57 58 var parentLi = link.parentElement; 59 var submenu = parentLi.querySelector('ul'); 60 61 if (!submenu) return; 62 63 if (window.innerWidth >= 1024 && !isTouchDevice()) { 64 e.preventDefault(); 65 e.stopPropagation(); 66 67 var isOpen = parentLi.classList.contains('sub-menu-active'); 68 69 document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) { 70 if (item !== parentLi) { 71 item.classList.remove('sub-menu-active'); 72 } 73 }); 74 75 if (isOpen) { 76 parentLi.classList.remove('sub-menu-active'); 77 } else { 78 parentLi.classList.add('sub-menu-active'); 79 } 80 return; 81 } 82 83 e.preventDefault(); 84 e.stopPropagation(); 85 86 var isOpen = parentLi.classList.contains('sub-menu-active'); 87 88 document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) { 89 if (item !== parentLi) { 90 item.classList.remove('sub-menu-active'); 91 } 92 }); 93 94 if (isOpen) { 95 parentLi.classList.remove('sub-menu-active'); 96 } else { 97 parentLi.classList.add('sub-menu-active'); 98 } 99 } 100 101 nav.addEventListener('click', handleSubmenuClick); 102 103 document.addEventListener('click', function(e) { 104 if (!e.target.closest('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li') && 105 !e.target.closest('header nav > ul > li') && 106 !e.target.closest('header nav')) { 107 document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) { 108 item.classList.remove('sub-menu-active'); 109 }); 110 } 111 }); 112 113 nav.addEventListener('click', function(e) { 114 if (e.target === nav) { 115 toggleMenu(); 116 } 117 }); 118 119 var previousWidth = window.innerWidth; 120 window.addEventListener('resize', function() { 121 var currentWidth = window.innerWidth; 122 123 if (previousWidth < 1024 && currentWidth >= 1024) { 124 if (nav.classList.contains('active')) { 125 toggleMenu(); 126 } 127 document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul >
127 li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) { 128 item.classList.remove('sub-menu-active'); 129 }); 130 } 131 132 previousWidth = currentWidth; 133 }); 134 135 var header = document.querySelector('header'); 136 if (header) { 137 function updateHeaderOnScroll() { 138 var scrollY = window.scrollY || window.pageYOffset; 139 140 if (scrollY === 0) { 141 header.classList.remove('scrolling'); 142 } else { 143 header.classList.add('scrolling'); 144 } 145 } 146 147 updateHeaderOnScroll(); 148 149 window.addEventListener('scroll', updateHeaderOnScroll, { passive: true }); 150 } 151 152 // VisualViewport-based height calculation for mobile menu (normal behavior) 153 // Ensures menu and button are always within visible viewport 154 function isMobileDevice() { 155 return window.innerWidth <= 768; 156 } 157 158 function updateVisualViewportHeight() { 159 if (!nav || !isMobileDevice() || !nav.classList.contains('active')) { 160 return; 161 } 162 163 var visualViewport = window.visualViewport; 164 var innerHeight = window.innerHeight; 165 166 if (visualViewport) { 167 // Set nav height to visible viewport height (normal behavior) 168 nav.style.setProperty('--vvh', visualViewport.height + 'px'); 169 } else { 170 // Fallback: use window innerHeight 171 nav.style.setProperty('--vvh', innerHeight + 'px'); 172 } 173 } 174 175 // Keyboard detection - prevents layout shift when keyboard opens 176 var KEYBOARD_THRESHOLD = 120; // Keyboard is considered open if viewport shrinks by more than this 177 178 function detectKeyboardState() { 179 if (!window.visualViewport || !nav || !nav.classList.contains('active') || !isMobileDevice()) { 180 return; 181 } 182 183 var innerHeight = window.innerHeight; 184 var visualHeight = window.visualViewport.height; 185 var difference = innerHeight - visualHeight; 186 var keyboardOpen = difference > KEYBOARD_THRESHOLD; 187 188 // Add/remove .kb-open class on html element 189 if (keyboardOpen) { 190 document.documentElement.classList.add('kb-open'); 191 } else { 192 document.documentElement.classList.remove('kb-open'); 193 } 194 } 195 196 // Initialize and set up listeners for VisualViewport changes (normal behavior) 197 if (nav) { 198 // Listen for visual viewport changes (preferred) 199 if (window.visualViewport) { 200 window.visualViewport.addEventListener('resize', function() { 201 if (nav.classList.contains('active') && isMobileDevice()) { 202 updateVisualViewportHeight(); // Normal behavior 203 detectKeyboardState(); // Also detect keyboard state 204 } 205 }); 206 window.visualViewport.addEventListener('scroll', function() { 207 if (nav.classList.contains('active') && isMobileDevice()) { 208 updateVisualViewportHeight(); 209 } 210 }); 211 } 212 213 // Fallback: listen for window resize (less accurate but works on older iOS) 214 var resizeTimeout; 215 window.addEventListener('resize', function() { 216 if (nav.classList.contains('active') && isMobileDevice()) { 217 clearTimeout(resizeTimeout); 218 resizeTimeout = setTimeout(function() { 219 updateVisualViewportHeight(); 220 detectKeyboardState(); 221 }, 100); 222 } 223 }, { passive: true }); 224 225 // Also adjust on orientation change 226 window.addEventListener('orientationchange', function() { 227 if (nav.classList.contains('active') && isMobileDevice()) { 228 setTimeout(function() { 229 updateVisualViewportHeight(); 230 detectKeyboardState(); 231 }, 200); 232 } 233 }); 234 } 235 236 // Language Dropdown 237 var languageDropdownWrapper = document.querySelector('.language-dropdown-wrapper'); 238 var languageToggle = document.querySelector('.language-toggle'); 239 240 if (languageToggle && languageDropdownWrapper) { 241 languageToggle.addEventListener('click', function(e) { 242 e.preventDefault(); 243 e.stopPropagation(); 244 languageDropdownWrapper.classList.toggle('active'); 245 }); 246 247 // Close dropdown when clicking outside 248 document.addEventListener('click', function(e) { 249 if (!e.target.closest('.language-dropdown-wrapper')) { 250 languageDropdownWrapper.classList.remove('active'); 251 } 252 }); 253 254 // Handle language option clicks 255 var languageOptions = document.querySelectorAll('.language-option'); 256 languageOptions.forEach(function(option) { 257 option.addEventListener('click', function(e) { 258 e.preventDefault(); 259 var locale = this.getAttribute('data-lang'); 260 if (locale) { 261 var url = new URL(window.location.href); 262 url.searchParams.set('locale', locale); 263 window.location.href = url.toString(); 264 } 265 }); 266 }); 267 } 268})(); 269
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.