1(function () { 2 "use strict"; 3 4 // ============================================================================ 5 // CONSTANTS 6 // ============================================================================ 7 const SELECTORS = { 8 menuContainer: ".header-menu-2025", 9 menuList: ".header-menu-2025__list", 10 menuItem: "li", 11 menuTabsWrapper: ".menu-tabs-wrapper", 12 titleWrapper: ".title-wrapper-depth-0", 13 tab: 'button[role="tab"], a[role="tab"]', 14 tabPanel: '[role="tabpanel"]', 15 tabList: ".menu-tablist", 16 tabsContainer: ".menu-tabs-container", 17 tabHeader: ".menu-tab-header", 18 appsMenu: ".header__apps-menu", 19 appsMenuToggle: ".header__apps-menu-toggle", 20 menuToggle: ".header__main-right-menu-toggle", 21 menuClose: ".header__main-right-menu-close", 22 header: ".header__container", 23 helloBar: "[data-hello-bar]", 24 langSwitcher: ".language-switcher-2025__wrap", 25 mobileButtons: ".header-menu-2025__mobile-buttons" 26 }; 27 28 const BREAKPOINTS = { 29 mobile: 767, 30 tablet: { min: 768, max: 1279 }, 31 desktop: 1279, 32 helloBarDesktop: 1024 33 }; 34 35 const SCROLL_THRESHOLD = 10; 36 const SCROLL_HIDE_THRESHOLD = 50; 37 const RESIZE_DEBOUNCE = 150; 38 const HOVER_CLOSE_DELAY = 100; 39 40 // ============================================================================ 41 // INITIALIZATION 42 // ============================================================================ 43 const menuContainer = document.querySelector(SELECTORS.menuContainer); 44 if (!menuContainer) return; 45 46 let currentOpenMenu = null; 47 let hoverCloseTimer = null; 48 let wasMobileForMegaMenuTabs = false; 49 50 // ============================================================================ 51 // VIEWPORT UTILITIES 52 // ============================================================================ 53 function getViewportWidth() { 54 return window.innerWidth; 55 } 56 57 function isMobileViewport() { 58 return getViewportWidth() <= BREAKPOINTS.mobile; 59 } 60 61 function isTabletViewport() { 62 const width = getViewportWidth(); 63 return width >= BREAKPOINTS.tablet.min && width <= BREAKPOINTS.tablet.max; 64 } 65 66 function isDesktopViewport() { 67 return getViewportWidth() > BREAKPOINTS.desktop; 68 } 69 70 function isHoverDevice() { 71 return window.matchMedia('(hover: hover) and (pointer: fine)').matches; 72 } 73 74 // ============================================================================ 75 // HELPER FUNCTIONS 76 // ============================================================================ 77 function hasRealHref(element) { 78 if (element.tagName !== "A") return false; 79 const href = element.getAttribute("href"); 80 return href && href !== "#" && href.trim() !== ""; 81 } 82 83 function isHelloBarDismissed(helloBar) { 84 if (!helloBar) return false; 85 return helloBar.classList.contains("hello-bar--dismissed") || 86 sessionStorage.getItem("hello-bar-dismissed") === "true"; 87 } 88 89 function resetTabState(tabs) {
90 tabs.forEach((tab) => { 91 tab.classList.remove("selected"); 92 tab.setAttribute("aria-selected", "false"); 93 tab.setAttribute("tabindex", "-1"); 94 }); 95 } 96 97 function setTabActive(tab) { 98 tab.classList.add("selected"); 99 tab.setAttribute("aria-selected", "true"); 100 tab.setAttribute("tabindex", "0"); 101 } 102 103 function hideAllPanels(panels) { 104 panels.forEach((panel) => { 105 panel.classList.remove("selected"); 106 panel.classList.add("hidden"); 107 panel.setAttribute("hidden", ""); 108 }); 109 } 110 111 function showPanel(panel) { 112 if (!panel) return; 113 panel.classList.add("selected"); 114 panel.classList.remove("hidden"); 115 panel.removeAttribute("hidden"); 116 } 117 118 function switchTab(tab, menuWrapper) { 119 const panelId = tab.getAttribute("aria-controls"); 120 if (!panelId) return; 121 122 const allTabs = menuWrapper.querySelectorAll(SELECTORS.tab); 123 const allPanels = menuWrapper.querySelectorAll(SELECTORS.tabPanel); 124 125 resetTabState(allTabs); 126 setTabActive(tab); 127 hideAllPanels(allPanels); 128 129 const targetPanel = menuWrapper.querySelector(`[role="tabpanel"][id="${panelId}"]`); 130 showPanel(targetPanel); 131 132 return { allTabs, allPanels, tablist: menuWrapper.querySelector(SELECTORS.tabList) }; 133 } 134 135 // ============================================================================ 136 // MENU OPERATIONS 137 // ============================================================================ 138 function closeAllMenus() { 139 const allMenus = menuContainer.querySelectorAll(SELECTORS.menuTabsWrapper); 140 allMenus.forEach((menu) => menu.classList.add("hidden")); 141 142 const allTitleWrappers = menuContainer.querySelectorAll(SELECTORS.titleWrapper); 143 allTitleWrappers.forEach((wrapper) => wrapper.classList.remove("active")); 144 145 if (isMobileViewport()) { 146 const allMenuItems = menuContainer.querySelectorAll(`${SELECTORS.menuList} > ${SELECTORS.menuItem}`); 147 allMenuItems.forEach((item) => item.classList.remove("menu-item-hidden-mobile")); 148 } 149 150 currentOpenMenu = null; 151 updateMobileButtonsVisibility(); 152 } 153 154 function closeOtherMenus() { 155 const appsMenuWrapper = document.querySelector(SELECTORS.appsMenu); 156 if (appsMenuWrapper) { 157 appsMenuWrapper.classList.remove("active"); 158 } 159 160 const langSwitcherWraps = document.querySelectorAll(SELECTORS.langSwitcher); 161 langSwitcherWraps.forEach((wrap) => wrap.classList.remove("active")); 162 } 163 164 function closeAllOtherMenus() { 165 closeOtherMenus(); 166 menuContainer.classList.remove("mobile-menu-open", "right-fixation"); 167 document.body.classList.remove("mobile-menu-open"); 168 closeAllMenus(); 169 toggleMenuButtons(false); 170 } 171 172 function openMenu(menuWrapper) { 173 closeOtherMenus(); 174 closeAllMenus(); 175 menuWrapper.classList.remove("hidden"); 176 currentOpenMenu = menuWrapper; 177 178 const menuItem = menuWrapper.closest(SELECTORS.menuItem); 179 if (!menuItem) return; 180 181 const titleWrapper = menuItem.querySelector(SELECTORS.titleWrapper); 182 if (titleWrapper) { 183 titleWrapper.classList.add("active"); 184 } 185 186 if (isMobileViewport()) { 187 const allMenuItems = menuContainer.querySelectorAll(`${SELECTORS.menuList} > ${SELECTORS.menuItem}`); 188 allMenuItems.forEach((item) => { 189 item.classList.toggle("menu-item-hidden-mobile", item !== menuItem); 190 }); 191 resetAriaSelectedOnMobile(); 192 hideAllTabpanelsOnMobile(); 193 } else { 194 selectFirstTab(menuWrapper); 195 } 196 updateMobileButtonsVisibility(); 197 } 198 199 function toggleMenuButtons(isOpen) { 200 const menuToggle = document.querySelector(SELECTORS.menuToggle); 201 const menuClose = document.querySelector(SELECTORS.menuClose); 202 203 if (menuToggle && menuClose) { 204 menuToggle.classList.toggle("hidden", isOpen); 205 menuClose.classList.toggle("active", isOpen); 206 } 207 } 208 209 function updateMobileButtonsVisibility() { 210 if (!isMobileViewport()) return; 211 212 const mobileButtons = menuContainer.querySelector(SELECTORS.mobileButtons); 213 if (!mobileButtons) return; 214 215 const isMainMenuOpen = menuContainer.classList.contains("mobile-menu-open"); 216 const hasActiveCategory = menuContainer.querySelector(`${SELECTORS.titleWrapper}.active`); 217 218 if (isMainMenuOpen && !hasActiveCategory) { 219 mobileButtons.style.display = "flex"; 220 } else { 221 mobileButtons.style.display = "none"; 222 } 223 } 224 225 function showMenuOnDesktop() { 226 if (isDesktopViewport()) { 227 menuContainer.classList.remove("right-fixation", "mobile-menu-open"); 228 document.body.classList.remove("mobile-menu-open"); 229 menuContainer.style.display = ""; 230 menuContainer.style.visibility = ""; 231 } 232 } 233 234 // ============================================================================ 235 // TAB OPERATIONS 236 // ============================================================================ 237 function selectFirstTab(menuWrapper) { 238 if (isMobileViewport()) return; 239 240 const allTabs = menuWrapper.querySelectorAll(SELECTORS.tab); 241 if (allTabs.length === 0) return; 242 243 switchTab(allTabs[0], menuWrapper); 244 } 245 246 function resetAllMegaMenusToFirstTab() { 247 menuContainer.querySelectorAll(SELECTORS.menuTabsWrapper).forEach((wrapper) => { 248 selectFirstTab(wrapper); 249 }); 250 } 251 252 function resetAriaSelectedOnMobile() { 253 if (!isMobileViewport()) return; 254 const allTabs = menuContainer.querySelectorAll(SELECTORS.tab);
255 allTabs.forEach((tab) => tab.setAttribute("aria-selected", "false")); 256 } 257 258 function hideAllTabpanelsOnMobile() { 259 if (!isMobileViewport()) return; 260 261 const allPanels = menuContainer.querySelectorAll(SELECTORS.tabPanel); 262 hideAllPanels(allPanels); 263 264 const allTabHeaders = menuContainer.querySelectorAll(SELECTORS.tabHeader); 265 allTabHeaders.forEach((header) => header.classList.add("hidden")); 266 267 const allTablists = menuContainer.querySelectorAll(SELECTORS.tabList); 268 allTablists.forEach((tablist) => tablist.classList.remove("hidden")); 269 270 const allTitleWrappers = menuContainer.querySelectorAll(`${SELECTORS.titleWrapper}.menu-tab-header-hidden`); 271 allTitleWrappers.forEach((wrapper) => wrapper.classList.remove("menu-tab-header-hidden")); 272 } 273 274 function createMobileTabHeader(tab, menuWrapper, tabsContainer, tablist) { 275 const menuItem = menuWrapper.closest(SELECTORS.menuItem); 276 if (menuItem) { 277 const titleWrapper = menuItem.querySelector(SELECTORS.titleWrapper); 278 if (titleWrapper) { 279 titleWrapper.classList.add("menu-tab-header-hidden"); 280 } 281 } 282 283 const tabClone = tab.cloneNode(true); 284 tabClone.querySelectorAll("svg").forEach((svg) => svg.remove()); 285 const tabText = tabClone.textContent.trim(); 286 287 let tabHeader = menuWrapper.querySelector(SELECTORS.tabHeader); 288 if (!tabHeader) { 289 tabHeader = document.createElement("div"); 290 tabHeader.className = "menu-tab-header"; 291 tabsContainer.insertBefore(tabHeader, tablist); 292 } 293 294 const titleSpan = document.createElement("span"); 295 titleSpan.className = "menu-tab-header-title"; 296 titleSpan.textContent = tabText; 297 298 const chevronSpan = document.createElement("span"); 299 chevronSpan.className = "menu-tab-header-chevron"; 300 301 const backDiv = document.createElement("div"); 302 backDiv.className = "menu-tab-header-back"; 303 backDiv.appendChild(chevronSpan); 304 backDiv.appendChild(titleSpan); 305 306 tabHeader.innerHTML = ""; 307 tabHeader.appendChild(backDiv); 308 tabHeader.classList.remove("hidden"); 309 310 return { tabHeader, menuItem }; 311 } 312 313 function setupMobileTabBackButton(tabHeader, menuItem, tablist, allPanels, allTabs) { 314 const backButton = tabHeader.querySelector(".menu-tab-header-back"); 315 if (!backButton) return; 316 317 const newBackButton = backButton.cloneNode(true); 318 backButton.parentNode.replaceChild(newBackButton, backButton); 319 320 newBackButton.addEventListener("click", (e) => { 321 e.preventDefault(); 322 e.stopPropagation(); 323 324 tabHeader.classList.add("hidden"); 325 tablist.classList.remove("hidden"); 326 327 if (menuItem) { 328 const titleWrapper = menuItem.querySelector(SELECTORS.titleWrapper); 329 if (titleWrapper) { 330 titleWrapper.classList.remove("menu-tab-header-hidden"); 331 } 332 } 333 334 hideAllPanels(allPanels); 335 resetTabState(allTabs); 336 updateMobileButtonsVisibility(); 337 }); 338 } 339 340 function handleTabClick(e) { 341 const tab = e.target.closest(SELECTORS.tab); 342 if (!tab || hasRealHref(tab)) return; 343 344 if (isHoverDevice() && !isMobileViewport() && e.detail > 0) { 345 e.preventDefault(); 346 e.stopPropagation(); 347 return; 348 } 349 350 e.preventDefault(); 351 e.stopPropagation(); 352 353 const menuWrapper = tab.closest(SELECTORS.menuTabsWrapper); 354 if (!menuWrapper) return; 355 356 const { allTabs, allPanels, tablist } = switchTab(tab, menuWrapper); 357 358 if (isMobileViewport() && tablist) { 359 const tabsContainer = menuWrapper.querySelector(SELECTORS.tabsContainer); 360 if (tabsContainer) { 361 tablist.classList.add("hidden"); 362 const { tabHeader, menuItem } = createMobileTabHeader(tab, menuWrapper, tabsContainer, tablist); 363 setupMobileTabBackButton(tabHeader, menuItem, tablist, allPanels, allTabs); 364 } 365 updateMobileButtonsVisibility(); 366 } 367 } 368 369 function handleTabMouseEnter(e) { 370 if (!isHoverDevice() || isMobileViewport()) return; 371 372 const tab = e.currentTarget; 373 if (!tab || hasRealHref(tab)) return; 374 375 const menuWrapper = tab.closest(SELECTORS.menuTabsWrapper); 376 if (!menuWrapper) return; 377 378 switchTab(tab, menuWrapper); 379 } 380 381 function handleCategoryClick(e) {
382 if (isHoverDevice()) return; 383 384 const menuItem = e.currentTarget.closest(SELECTORS.menuItem); 385 if (!menuItem) return; 386 387 const menuWrapper = menuItem.querySelector(SELECTORS.menuTabsWrapper); 388 if (!menuWrapper) return; 389 390 const clickedLink = e.target.closest("a[href]"); 391 if (clickedLink && hasRealHref(clickedLink)) return; 392 393 e.preventDefault(); 394 e.stopPropagation(); 395 396 if (currentOpenMenu === menuWrapper && !menuWrapper.classList.contains("hidden")) { 397 if (isTabletViewport()) return; 398 closeAllMenus(); 399 } else { 400 openMenu(menuWrapper); 401 } 402 } 403 404 function handleCategoryMouseEnter(e) { 405 if (!isHoverDevice()) return; 406 407 const menuWrapper = e.currentTarget.querySelector(SELECTORS.menuTabsWrapper); 408 if (!menuWrapper) return; 409 410 if (hoverCloseTimer) { 411 clearTimeout(hoverCloseTimer); 412 hoverCloseTimer = null; 413 } 414 415 if (currentOpenMenu !== menuWrapper) { 416 openMenu(menuWrapper); 417 } 418 } 419 420 function handleCategoryMouseLeave() { 421 if (!isHoverDevice()) return; 422 if (menuContainer.classList.contains("right-fixation")) return; 423 424 hoverCloseTimer = setTimeout(() => { 425 closeAllMenus(); 426 hoverCloseTimer = null; 427 }, HOVER_CLOSE_DELAY); 428 } 429 430 function handleOutsideClick(e) { 431 if (!currentOpenMenu) return; 432 433 if (isTabletViewport() && menuContainer.contains(e.target)) return; 434 if (e.target.closest(SELECTORS.tab)) return; 435 if (currentOpenMenu.contains(e.target) || e.target.closest(".menu-item-has-children")) return; 436 437 closeAllMenus(); 438 } 439 440 // ============================================================================ 441 // HEADER SCROLL & HELLO-BAR MANAGEMENT 442 // ============================================================================ 443 function initHeaderHeightObserver() { 444 const header = document.querySelector(SELECTORS.header); 445 const helloBar = document.querySelector(SELECTORS.helloBar); 446 if (!header) return; 447 448 function updateHeaderHeight() { 449 const height = header.offsetHeight; 450 let observableHeight = height; 451 452 if (helloBar && helloBar.classList.contains("hello-bar--hidden")) { 453 observableHeight -= helloBar.offsetHeight + 12; 454 } 455 456 document.documentElement.style.setProperty("--header-2025-height", `${height}px`); 457 document.documentElement.style.setProperty("--header-2025-observable-height", `${observableHeight}px`); 458 } 459 460 updateHeaderHeight(); 461 462 const resizeObserver = new ResizeObserver(updateHeaderHeight); 463 resizeObserver.observe(header); 464 465 window.addEventListener("scroll", updateHeaderHeight, { passive: true }); 466 } 467 468 function initHelloBarHeightObserver() { 469 const helloBar = document.querySelector(SELECTORS.helloBar); 470 471 function updateHelloBarHeight() { 472 const height = helloBar ? helloBar.offsetHeight : 0; 473 document.documentElement.style.setProperty("--hello-bar-height", `${height}px`); 474 } 475 476 updateHelloBarHeight(); 477 if (!helloBar) return; 478 479 const resizeObserver = new ResizeObserver(updateHelloBarHeight); 480 resizeObserver.observe(helloBar); 481 } 482 483 function initHeaderScroll() { 484 const header = document.querySelector(SELECTORS.header); 485 const helloBar = document.querySelector(SELECTORS.helloBar); 486 if (!header) return; 487 488 let lastScrollY = window.scrollY; 489 let scrollTimeout; 490 let isScrollingDown = false; 491 492 function updateBodyPadding() { 493 if (header.classList.contains("header--fixed")) { 494 document.body.style.paddingTop = `${header.offsetHeight}px`; 495 } else { 496 document.body.style.paddingTop = "0"; 497 } 498 } 499 500 function updateHelloBarVisibility(currentScrollY) { 501 if (!helloBar || isHelloBarDismissed(helloBar)) return; 502 503 const width = window.innerWidth; 504 if (width <= BREAKPOINTS.helloBarDesktop - 1) { 505 helloBar.classList.toggle("hello-bar--hidden", isScrollingDown && currentScrollY > SCROLL_HIDE_THRESHOLD); 506 } else { 507 helloBar.classList.remove("hello-bar--hidden"); 508 } 509 } 510 511 function handleScroll() { 512 const currentScrollY = window.scrollY; 513 514 if (Math.abs(currentScrollY - lastScrollY) > SCROLL_THRESHOLD) { 515 isScrollingDown = currentScrollY > lastScrollY; 516 } 517 518 const wasFixed = header.classList.contains("header--fixed"); 519 header.classList.toggle("header--fixed", currentScrollY > 0); 520 521 if (wasFixed !== header.classList.contains("header--fixed")) { 522 updateBodyPadding(); 523 } 524 525 updateHelloBarVisibility(currentScrollY); 526 lastScrollY = currentScrollY; 527 } 528 529 window.addEventListener("scroll", () => { 530 if (scrollTimeout) cancelAnimationFrame(scrollTimeout); 531 scrollTimeout = requestAnimationFrame(handleScroll); 532 }, { passive: true }); 533 534 let resizeTimeout; 535 window.addEventListener("resize", () => { 536 clearTimeout(resizeTimeout); 537 resizeTimeout = setTimeout(() => { 538 updateBodyPadding(); 539 if (helloBar && !isHelloBarDismissed(helloBar)) { 540 if (window.innerWidth >= BREAKPOINTS.helloBarDesktop) { 541 helloBar.classList.remove("hello-bar--hidden"); 542 } else { 543 handleScroll(); 544 } 545 } 546 }, RESIZE_DEBOUNCE); 547 }); 548 549 if (window.scrollY > 0) { 550 header.classList.add("header--fixed"); 551 updateBodyPadding(); 552 } 553 554 if (helloBar) { 555 if (isHelloBarDismissed(helloBar)) { 556 helloBar.classList.add("hello-bar--hidden"); 557 } else { 558 const width = window.innerWidth; 559 if (width >= BREAKPOINTS.helloBarDesktop) { 560 helloBar.classList.remove("hello-bar--hidden"); 561 } else { 562 helloBar.classList.toggle("hello-bar--hidden", window.scrollY > SCROLL_HIDE_THRESHOLD); 563 } 564 } 565 } 566 567 const resizeObserver = new ResizeObserver(() => { 568 if (header.classList.contains("header--fixed")) { 569 updateBodyPadding(); 570 } 571 }); 572 resizeObserver.observe(header); 573 } 574 575 // ============================================================================ 576 // MENU TOGGLE HANDLERS 577 // ============================================================================ 578 function initMobileMenuToggle() { 579 const menuToggle = document.querySelector(SELECTORS.menuToggle); 580 const menuClose = document.querySelector(SELECTORS.menuClose); 581 if (!menuToggle || !menuContainer) return; 582 583 function closeMobileMenu() { 584 menuContainer.classList.remove("mobile-menu-open"); 585 document.body.classList.remove("mobile-menu-open"); 586 closeAllMenus(); 587 toggleMenuButtons(false); 588 updateMobileButtonsVisibility(); 589 } 590 591 function openMobileMenu() { 592 closeOtherMenus(); 593 menuContainer.classList.add("mobile-menu-open"); 594 document.body.classList.add("mobile-menu-open"); 595 toggleMenuButtons(true); 596 updateMobileButtonsVisibility(); 597 } 598 599 menuToggle.addEventListener("click", (e) => { 600 e.preventDefault(); 601 e.stopPropagation(); 602 if (!isMobileViewport()) return; 603 604 const isOpen = menuContainer.classList.contains("mobile-menu-open"); 605 if (isOpen) { 606 closeMobileMenu(); 607 } else { 608 openMobileMenu(); 609 } 610 }); 611 612 if (menuClose) { 613 menuClose.addEventListener("click", (e) => { 614 if (!isMobileViewport()) return; 615 616 const langSwitcherWraps = document.querySelectorAll(SELECTORS.langSwitcher); 617 const isLangSwitcherOpen = Array.from(langSwitcherWraps).some((wrap) => 618 wrap.classList.contains("active") 619 ); 620 621 if (isLangSwitcherOpen) return; 622 623 e.preventDefault(); 624 e.stopPropagation(); 625 closeMobileMenu(); 626 }); 627 } 628 629 document.addEventListener("click", (e) => { 630 if (!isMobileViewport() || !menuContainer.classList.contains("mobile-menu-open")) return; 631 if (menuContainer.contains(e.target) || menuToggle.contains(e.target)) return; 632 closeMobileMenu(); 633 }); 634 635 let resizeTimeout; 636 window.addEventListener("resize", () => { 637 clearTimeout(resizeTimeout); 638 resizeTimeout = setTimeout(() => { 639 if (!isMobileViewport() && menuContainer.classList.contains("mobile-menu-open")) { 640 closeMobileMenu(); 641 } 642 }, RESIZE_DEBOUNCE); 643 }); 644 } 645 646 function initTabletMenuToggle() { 647 const menuToggle = document.querySelector(SELECTORS.menuToggle); 648 const menuClose = document.querySelector(SELECTORS.menuClose); 649 if (!menuToggle || !menuContainer) return; 650 651 function closeTabletMenu() { 652 menuContainer.classList.remove("right-fixation"); 653 closeAllMenus(); 654 toggleMenuButtons(false); 655 } 656
657 function openTabletMenu() { 658 closeOtherMenus(); 659 menuContainer.classList.add("right-fixation"); 660 toggleMenuButtons(true); 661 662 const firstMenuItem = menuContainer.querySelector(".menu-item-has-children"); 663 const menuTabsWrapper = firstMenuItem?.querySelector(SELECTORS.menuTabsWrapper); 664 if (menuTabsWrapper) { 665 openMenu(menuTabsWrapper); 666 } 667 } 668 669 menuToggle.addEventListener("click", (e) => { 670 e.preventDefault(); 671 e.stopPropagation(); 672 if (!isTabletViewport()) return; 673 674 const isOpen = menuContainer.classList.contains("right-fixation"); 675 if (isOpen) { 676 closeTabletMenu(); 677 } else { 678 openTabletMenu(); 679 } 680 }); 681 682 if (menuClose) { 683 menuClose.addEventListener("click", (e) => { 684 e.preventDefault(); 685 e.stopPropagation(); 686 if (!isTabletViewport()) return; 687 closeTabletMenu(); 688 }); 689 } 690 691 document.addEventListener("click", (e) => { 692 if (!isTabletViewport() || !menuContainer.classList.contains("right-fixation")) return; 693 if (menuContainer.contains(e.target) || menuToggle.contains(e.target)) return; 694 closeTabletMenu(); 695 }); 696 697 let resizeTimeout; 698 window.addEventListener("resize", () => { 699 clearTimeout(resizeTimeout); 700 resizeTimeout = setTimeout(() => { 701 if (isDesktopViewport()) { 702 if (menuContainer.classList.contains("right-fixation")) { 703 closeTabletMenu(); 704 } 705 menuContainer.style.display = ""; 706 menuContainer.style.visibility = ""; 707 } else if (!isTabletViewport() && menuContainer.classList.contains("right-fixation")) { 708 closeTabletMenu(); 709 } 710 }, RESIZE_DEBOUNCE); 711 }); 712 } 713 714 function initAppsMenuToggle() { 715 const appsMenuToggle = document.querySelector(SELECTORS.appsMenuToggle); 716 const appsMenuWrapper = document.querySelector(SELECTORS.appsMenu); 717 if (!appsMenuToggle || !appsMenuWrapper) return; 718 719 appsMenuToggle.addEventListener("click", (e) => { 720 e.preventDefault(); 721 e.stopPropagation(); 722 const isOpen = appsMenuWrapper.classList.contains("active"); 723 if (isOpen) { 724 appsMenuWrapper.classList.remove("active"); 725 } else { 726 closeAllOtherMenus(); 727 appsMenuWrapper.classList.add("active"); 728 } 729 }); 730 731 document.addEventListener("click", (e) => { 732 if (!appsMenuWrapper.contains(e.target)) { 733 appsMenuWrapper.classList.remove("active"); 734 } 735 }); 736 } 737 738 // ============================================================================ 739 // MENU CURRENT CLASSES UPDATE 740 // ============================================================================ 741 function updateMenuCurrentClasses() { 742 const currentPath = window.location.pathname.replace(/\/$/, '') || '/'; 743 const menuLinks = menuContainer.querySelectorAll('a[href]'); 744 745 // First, remove all old classes 746 menuContainer.querySelectorAll('.current-menu-item, .current-menu-link, .menu-tab.selected').forEach(el => { 747 el.classList.remove('current-menu-item', 'current-menu-link', 'selected'); 748 }); 749 menuContainer.querySelectorAll('.menu-tab[aria-selected="true"]').forEach(tab => { 750 tab.setAttribute('aria-selected', 'false'); 751 }); 752 753 menuLinks.forEach(link => { 754 const href = link.getAttribute('href'); 755 if (!href || href === '#' || href.startsWith('#')) return; 756 757 // Normalize link path 758 let hrefPath; 759 let hrefOrigin; 760 try { 761 // If href is relative, create full URL 762 const url = new URL(href, window.location.origin); 763 hrefOrigin = url.origin; 764 hrefPath = url.pathname.replace(/\/$/, '') || '/'; 765 } catch (e) { 766 // If failed to parse as URL, use as is 767 hrefPath = href.split('?')[0].split('#')[0].replace(/\/$/, '') || '/'; 768 hrefOrigin = window.location.origin; // Assume same origin for relative links 769 } 770 771 // Only compare paths if the link is on the same domain 772 if (hrefOrigin === window.location.origin && hrefPath === currentPath) { 773 // Add class to link 774 link.classList.add('current-menu-link'); 775 776 // Find parent <li> 777 let li = link.closest('li'); 778 if (li) { 779 li.classList.add('current-menu-item'); 780 } 781 } 782 }); 783 } 784 785 // ============================================================================ 786 // INITIALIZATION 787 // ============================================================================ 788 function initMenuHandlers() { 789 const menuList = menuContainer.querySelector(SELECTORS.menuList); 790 if (!menuList) return; 791 792 const categories = menuList.querySelectorAll(SELECTORS.menuItem);
793 categories.forEach((category) => { 794 const titleWrapper = category.querySelector(SELECTORS.titleWrapper); 795 if (titleWrapper && !titleWrapper.querySelector("a[href]")) { 796 category.addEventListener("click", handleCategoryClick); 797 category.addEventListener("mouseenter", handleCategoryMouseEnter); 798 category.addEventListener("mouseleave", handleCategoryMouseLeave); 799 } 800 }); 801 } 802 803 function initTabHandlers() { 804 const tabs = menuContainer.querySelectorAll(SELECTORS.tab); 805 tabs.forEach((tab) => { 806 tab.addEventListener("click", handleTabClick); 807 tab.addEventListener("mouseenter", handleTabMouseEnter); 808 }); 809 } 810 811 function init() { 812 initMenuHandlers(); 813 initTabHandlers(); 814 initAppsMenuToggle(); 815 initTabletMenuToggle(); 816 initMobileMenuToggle(); 817 initHeaderHeightObserver(); 818 initHelloBarHeightObserver(); 819 initHeaderScroll(); 820 document.addEventListener("click", handleOutsideClick); 821 822 updateMenuCurrentClasses(); 823 resetAllMegaMenusToFirstTab(); 824 wasMobileForMegaMenuTabs = isMobileViewport(); 825 showMenuOnDesktop(); 826 resetAriaSelectedOnMobile(); 827 hideAllTabpanelsOnMobile(); 828 updateMobileButtonsVisibility(); 829 830 window.addEventListener("resize", () => { 831 const nowMobile = isMobileViewport(); 832 if (nowMobile) { 833 resetAriaSelectedOnMobile(); 834 hideAllTabpanelsOnMobile(); 835 } else if (wasMobileForMegaMenuTabs) { 836 resetAllMegaMenusToFirstTab(); 837 } 838 wasMobileForMegaMenuTabs = nowMobile; 839 showMenuOnDesktop(); 840 updateMobileButtonsVisibility(); 841 }); 842 } 843 844 if (document.readyState === "loading") { 845 document.addEventListener("DOMContentLoaded", init); 846 } else { 847 init(); 848 } 849})();
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.