1document.addEventListener('DOMContentLoaded', function () { 2 const langToggleBtn = document.getElementById('langToggleBtn'); 3 const langMenu = document.getElementById('langMenu'); 4 const hamburgerBtn = document.getElementById('HamburgerBtn'); 5 const navFirstLayer = document.getElementById('nav-first-layer'); 6 const navMenu = document.getElementById('mobile-menu'); 7 const body = document.body; 8 9 // Store original scroll position before locking 10 let scrollPosition = 0; 11 12 // Function to lock body scroll but allow scrolling within the mobile menu 13 function lockBodyScroll() { 14 // Store current scroll position 15 scrollPosition = window.pageYOffset; 16 17 // Add styles to lock body scroll 18 body.style.overflow = 'hidden'; 19 body.style.position = 'fixed'; 20 body.style.top = `0`; 21 body.style.width = '100%'; 22 23 // Ensure the mobile menu itself is scrollable 24 navMenu.style.overflow = 'auto'; 25 26 // Make all second-layer menus scrollable as well 27 document.querySelectorAll('.second-layer').forEach((layer) => { 28 layer.style.overflow = 'auto'; 29 }); 30 } 31 32 // Function to unlock body scroll 33 function unlockBodyScroll() { 34 body.style.removeProperty('overflow'); 35 body.style.removeProperty('position'); 36 body.style.removeProperty('top'); 37 body.style.removeProperty('width'); 38 window.scrollTo(0, scrollPosition); 39 } 40 41 // 1) Language toggle 42 langToggleBtn.addEventListener('click', () => { 43 langMenu.classList.toggle('hidden'); 44 const isOpen = !langMenu.classList.contains('hidden'); 45 langToggleBtn.setAttribute('aria-expanded', isOpen.toString()); 46 47 // Add/remove active state class using BEM modifier 48 if (isOpen) { 49 langToggleBtn.classList.add('language-toggle--active'); 50 } else { 51 langToggleBtn.classList.remove('language-toggle--active'); 52 } 53 }); 54 55 // Close language dropdown if clicking outside 56 document.addEventListener('click', (event) => { 57 const isInside = langMenu.contains(event.target); 58 const isToggle = langToggleBtn.contains(event.target); 59 if (!isInside && !isToggle && !langMenu.classList.contains('hidden')) { 60 langMenu.classList.add('hidden'); 61 langToggleBtn.setAttribute('aria-expanded', 'false'); 62 // Remove active state class when closing 63 langToggleBtn.classList.remove('language-toggle--active'); 64 } 65 }); 66 67 // 2) Hamburger icons 68 // -- The default "hamburger" icon: 69 const hamburgerIconHTML = ` 70 <span class="mobile-menu__icon"> 71 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"> 72 <path d="M17.5 10C17.5 10.1658 17.4342 10.3247 17.3169 10.4419C17.1997 10.5592 17.0408 10.625 16.875 10.625H3.125C2.95924 10.625 2.80027 10.5592 2.68306 10.4419C2.56585 10.3247 2.5 10.1658 2.5 10C2.5 9.83424 2.56585 9.67527 2.68306 9.55806C2.80027 9.44085 2.95924 9.375 3.125 9.375H16.875C17.0408 9.375 17.1997 9.44085 17.3169 9.55806C17.4342 9.67527 17.5 9.83424 17.5 10ZM3.125 5.625H16.875C17.0408 5.625 17.1997 5.55915 17.3169 5.44194C17.4342 5.32473 17.5 5.16576 17.5 5C17.5 4.83424 17.4342 4.67527 17.3169 4.55806C17.1997 4.44085 17.0408 4.375 16.875 4.375H3.125C2.95924 4.375 2.80027 4.44085 2.68306 4.55806C2.56585 4.67527 2.5 4.83424 2.5 5C2.5 5.16576 2.56585 5.32473 2.68306 5.44194C2.80027 5.55915 2.95924 5.625 3.125 5.625ZM16.875 14.375H3.125C2.95924 14.375 2.80027 14.4408 2.68306 14.5581C2.56585 14.6753 2.5 14.8342 2.5 15C2.5 15.1658 2.56585 15.3247 2.68306 15.4419C2.80027 15.5592 2.95924 15.625 3.125 15.625H16.875C17.0408 15.625 17.1997 15.5592 17.3169 15.4419C17.4342 15.3247 17.5 15.1658 17.5 15C17.5 14.8342 17.4342 14.6753 17.3169 14.5581C17.1997 14.4408 17.0408 14.375 16.875 14.375Z" fill="currentColor"/> 73 </svg> 74 </span> 75 `; 76 77 // -- The "close" icon: 78 const closeIconHTML = ` 79 <span class="mobile-menu__icon"> 80 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"> 81 <path d="M16.067 15.1828C16.1251 15.2409 16.1712 15.3098 16.2026 15.3857C16.234 15.4616 16.2502 15.5429 16.2502 15.625C16.2502 15.7071 16.234 15.7885 16.2026 15.8643C16.1712 15.9402 16.1251 16.0091 16.067 16.0672C16.009 16.1253 15.94 16.1713 15.8642 16.2028C15.7883 16.2342 15.707 16.2504 15.6249 16.2504C15.5427 16.2504 15.4614 16.2342 15.3855 16.2028C15.3097 16.1713 15.2407 16.1253 15.1827 16.0672L9.99986 10.8836L4.81705 16.0672C4.69977 16.1845 4.54071 16.2504 4.37486 16.2504C4.20901 16.2504 4.04995 16.1845 3.93267 16.0672C3.8154 15.9499 3.74951 15.7909 3.74951 15.625C3.74951 15.4592 3.8154 15.3001 3.93267 15.1828L9.11627 10L3.93267 4.8172C3.8154 4.69992 3.74951 4.54086 3.74951 4.37501C3.74951 4.20916 3.8154 4.0501 3.93267 3.93282C4.04995 3.81555 4.20901 3.74966 4.37486 3.74966C4.54071 3.74966 4.69977 3.81555 4.81705 3.93282L9.99986 9.11642L15.1827 3.93282C15.2999 3.81555 15.459 3.74966 15.6249 3.74966C15.7907 3.74966 15.9498 3.81555 16.067 3.93282C16.1843 4.0501 16.2502 4.20916 16.2502 4.37501C16.2502 4.54086 16.1843 4.69992 16.067 4.8172L10.8835 10L16.067 15.1828Z" fill="currentColor"/> 82 </svg> 83 </span> 84 `; 85 86 // Initially show the hamburger icon 87 hamburgerBtn.innerHTML = hamburgerIconHTML; 88 89 // Get references to utility elements 90 const utilityButtonsContainer = document.getElementById('utility-buttons'); 91 const langToggleContainer = document.querySelector('.language-selector'); 92 const accessibilityButton = document.getElementById('enableRecite'); 93 const searchButton = document.getElementById('search-button'); 94 95 // On click, toggle menu visibility + replace icon 96 hamburgerBtn.addEventListener('click', () => { 97 navMenu.classList.toggle('hidden'); 98 hamburgerBtn.classList.toggle('animate-fade'); 99 100 // If navMenu is NOT hidden => menu is open 101 if (!navMenu.classList.contains('hidden')) { 102 hamburgerBtn.innerHTML = closeIconHTML; 103 lockBodyScroll(); // Lock body scroll when menu opens 104 105 // Hide specific utility elements individually 106 if (langToggleContainer) langToggleContainer.classList.add('hidden!'); 107 if (accessibilityButton) accessibilityButton.classList.add('hidden!'); 108 if (searchButton) searchButton.classList.add('hidden!'); 109 110 // Reset to first layer if menu was previously closed in second layer 111 navFirstLayer.classList.remove('hidden'); 112 document.querySelectorAll('.second-layer').forEach((secondLayer) => { 113 secondLayer.classList.add('hidden'); 114 }); 115 if (document.getElementById('BackButtonContainer')) { 116 document.getElementById('BackButtonContainer').classList.add('hidden!'); 117 } 118 } else { 119 // If navMenu is hidden => menu is closed 120 hamburgerBtn.innerHTML = hamburgerIconHTML; 121 unlockBodyScroll(); // Unlock body scroll when menu closes 122 123 // Show all utility elements again 124 if (langToggleContainer) langToggleContainer.classList.remove('hidden!'); 125 if (accessibilityButton) accessibilityButton.classList.remove('hidden!'); 126 if (searchButton) searchButton.classList.remove('hidden!'); 127 } 128 }); 129 130 // Search functionality 131 const searchBtn = document.getElementById('search-button'); 132 const searchForm = document.querySelector('#header form'); 133 const searchBar = document.getElementById('search-bar'); 134 const desktopNav = document.getElementById('desktop-nav'); 135 const searchCancelButton = document.getElementById('search-cancel-button'); 136 const searchSubmitButton = document.getElementById('search-submit-button'); 137 const logoContainer = document.querySelector('#header > div > a'); // Logo container 138 const clearSearchButton = document.querySelector( 139 'button[aria-label="Clear search"]' 140 ); 141 const mobileMenu = document.getElementById('mobile-menu'); 142 143 // Initially hide the search form completely 144 if (searchForm) { 145 searchForm.style.display = 'none'; 146 } 147 148 // Check if we're on mobile 149 function isMobile() { 150 return window.innerWidth < 768; // Standard md breakpoint 151 } 152 153 // Get the flight info message element from the DOM 154 const flightInfoMessage = document.getElementById('flight-info-message'); 155 156 // Append flight info to mobile menu if it doesn't already exist 157 if ( 158 mobileMenu && 159 flightInfoMessage && 160 !mobileMenu.contains(flightInfoMessage) 161 ) { 162 mobileMenu.appendChild(flightInfoMessage); 163 } 164 165 // Function to show search form 166 function showSearchForm() { 167 if (isMobile()) { 168 // On mobile: show the mobile menu container with flight info only 169 if (mobileMenu) { 170 // Show mobile menu container 171 mobileMenu.classList.remove('hidden'); 172 // Add search header gradient class 173 mobileMenu.classList.add('search-header-gradient'); 174 175 // Hide regular nav content 176 if (navFirstLayer) navFirstLayer.classList.add('hidden'); 177 if (document.getElementById('BackButtonContainer')) { 178 document 179 .getElementById('BackButtonContainer') 180 .classList.add('hidden!'); 181 } 182 183 // Hide all second layers 184 document.querySelectorAll('.second-layer').forEach((layer) => { 185 layer.classList.add('hidden'); 186 }); 187 188 // Show flight info message 189 if (flightInfoMessage) flightInfoMessage.classList.remove('hidden'); 190 191 // Change hamburger to close icon 192 if (hamburgerBtn) hamburgerBtn.innerHTML = closeIconHTML; 193 194 // Lock body scroll 195 lockBodyScroll(); 196 } 197 198 // Hide the logo and utility buttons on mobile 199 if (logoContainer) logoContainer.classList.add('hidden'); 200 if (utilityButtonsContainer) 201 utilityButtonsContainer.classList.add('hidden'); 202 203 // Show search form and components 204 if (searchForm) searchForm.style.display = 'flex'; 205 if (searchBar) searchBar.classList.remove('hidden'); 206 if (searchCancelButton) searchCancelButton.classList.remove('hidden'); 207 if (searchSubmitButton) searchSubmitButton.classList.remove('hidden'); 208 } else { 209 // On desktop: normal search behavior 210 // Hide desktop nav and utility buttons 211 if (desktopNav) desktopNav.classList.add('hidden!'); 212 if (utilityButtonsContainer) 213 utilityButtonsContainer.classList.add('hidden'); 214 215 // Show search form and components 216 if (searchForm) searchForm.style.display = 'flex'; 217 if (searchBar) searchBar.classList.remove('hidden'); 218 if (searchCancelButton) searchCancelButton.classList.remove('hidden'); 219 if (searchSubmitButton) searchSubmitButton.classList.remove('hidden'); 220 } 221 222 // Focus on the search input 223 const searchInput = document.querySelector('input[name="searchTerm"]'); 224 if (searchInput) searchInput.focus(); 225 } 226 227 // Function to hide search form 228 function hideSearchForm() { 229 if (isMobile()) { 230 // On mobile: hide the mobile menu 231 if (mobileMenu) { 232 mobileMenu.classList.add('hidden'); 233 // Remove search header gradient class 234 mobileMenu.classList.remove('search-header-gradient'); 235 236 // Hide flight info message 237 if (flightInfoMessage) flightInfoMessage.classList.add('hidden'); 238 239 // Change back to hamburger icon 240 if (hamburgerBtn) hamburgerBtn.innerHTML = hamburgerIconHTML; 241 242 // Unlock body scroll 243 unlockBodyScroll(); 244 } 245 246 // Show the logo and utility buttons again on mobile 247 if (logoContainer) logoContainer.classList.remove('hidden'); 248 if (utilityButtonsContainer) 249 utilityButtonsContainer.classList.remove('hidden'); 250 } else { 251 // On desktop: normal hide behavior 252 // Show desktop nav and utility buttons 253 if (desktopNav) desktopNav.classList.remove('hidden!'); 254 if (utilityButtonsContainer) 255 utilityButtonsContainer.classList.remove('hidden'); 256 } 257 258 // Hide search form and components for both mobile and desktop 259 if (searchForm) searchForm.style.display = 'none'; 260 if (searchBar) searchBar.classList.add('hidden'); 261 if (searchCancelButton) searchCancelButton.classList.add('hidden'); 262 if (searchSubmitButton) searchSubmitButton.classList.add('hidden'); 263 } 264 265 // Handle search button click 266 if (searchBtn) { 267 searchBtn.addEventListener('click', showSearchForm); 268 } 269 270 // Handle cancel button click 271 if (searchCancelButton) { 272 searchCancelButton.addEventListener('click', hideSearchForm); 273 } 274 275 // Handle Escape key to close search 276 document.addEventListener('keydown', (e) => { 277 if (e.key === 'Escape') { 278 // Check if search is currently visible 279 if (searchBar && !searchBar.classList.contains('hidden')) { 280 e.preventDefault(); 281 hideSearchForm(); 282 } 283 } 284 }); 285 286 // Enhance the clear button functionality to also close search on mobile 287 if (clearSearchButton) { 288 const originalOnClick = clearSearchButton.getAttribute('onclick'); 289 clearSearchButton.removeAttribute('onclick'); 290 291 clearSearchButton.addEventListener('click', function () { 292 // Clear the input (original functionality) 293 const searchInput = document.querySelector('input[name="searchTerm"]'); 294 if (searchInput) searchInput.value = ''; 295 296 // On mobile, also close the search 297 if (isMobile()) { 298 hideSearchForm(); 299 } 300 301 // Focus back on input if we're still showing the search on desktop 302 if ( 303 !isMobile() && 304 searchInput && 305 !searchBar.classList.contains('hidden') 306 ) { 307 searchInput.focus(); 308 } 309 }); 310 } 311 312 // Handle clicks outside the search area to cancel 313 document.addEventListener('click', (event) => { 314 const isSearchButtonClick = searchBtn && searchBtn.contains(event.target); 315 const isSearchFormClick = searchForm && searchForm.contains(event.target); 316 const isSearchCancelClick = 317 searchCancelButton && searchCancelButton.contains(event.target); 318 319 // If search is visible and click is outside search area 320 if ( 321 searchBar && 322 !searchBar.classList.contains('hidden') && 323 !isSearchButtonClick && 324 !isSearchFormClick && 325 !isSearchCancelClick 326 ) { 327 hideSearchForm(); 328 } 329 }); 330 331 const backBtn = document.getElementById('BackBtn'); 332 333 // Only add back-button behavior if it exists 334 if (backBtn) { 335 backBtn.addEventListener('click', () => { 336 // Show the first layer menu: 337 navFirstLayer.classList.remove('hidden'); 338 navFirstLayer.classList.add('animate-slide-in-left'); 339 // Hide ALL second-layer menus: 340 document.querySelectorAll('.second-layer').forEach((secondLayer) => { 341 secondLayer.classList.add('hidden'); 342 }); 343 document.getElementById('BackButtonContainer').classList.add('hidden!'); 344 }); 345 } 346 347 // Handle clicks on the first-layer items 348 const firstLayerItems = document.querySelectorAll( 349 '#nav-first-layer li[data-second-layer]' 350 );
351 firstLayerItems.forEach((li) => { 352 li.addEventListener('click', () => { 353 // 1) Get the ID from data attribute 354 const secondLayerId = li.getAttribute('data-second-layer'); 355 if (!secondLayerId) return; 356 357 const secondLayerEl = document.getElementById(secondLayerId); 358 if (!secondLayerEl) return; 359 360 // 2) Show that second-layer while hiding the first 361 secondLayerEl.classList.add('animate-slide-in-right'); 362 secondLayerEl.classList.remove('hidden'); 363 navFirstLayer.classList.add('hidden'); 364 365 // 3) Reveal the back button 366 if (document.getElementById('BackButtonContainer')) { 367 document 368 .getElementById('BackButtonContainer') 369 .classList.remove('hidden!'); 370 } 371 }); 372 }); 373});
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.