1(() => { 2 // Get common utilities and validate they exist 3 const common = window?.brickSite?.common; 4 if (!common) return; 5 6 const { getBricksContext } = common; 7 8 // Get the top-level mobile menu section 9 const bricks = getBricksContext('.js-brick-mobile-menu'); 10 if (!bricks || bricks.length === 0) return; 11 12 /** 13 * Helper function to get DOM elements needed for mobile menu 14 * @returns {Object} Object containing menu elements 15 */ 16 const getMenuElements = () => ({ 17 sideMenu: common.document.getElementById('sideMenu'), 18 burgerIcon: common.document.getElementById('burgerIcon'), 19 body: common.document.body, 20 headerCta: common.document.getElementById('headerCta'), 21 mobileHeader: document.querySelector('.mobile-header-container'), 22 }); 23 24 /** 25 * Helper to find the CTA button in the side menu 26 * @returns {HTMLElement|null} The CTA button element or null if not found 27 */ 28 const findCtaButton = () => { 29 const { sideMenu } = getMenuElements(); 30 if (!sideMenu) return null; 31 32 // Find CTA by class 33 const ctaButton = sideMenu.querySelector('.cta-button'); 34 if (ctaButton) return ctaButton; 35 36 // Fallback: find by button classes 37 const selectors = [ 38 'a[class*="b-primary"]', 39 'a[class*="b-secondary"]', 40 'button[class*="b-primary"]', 41 'button[class*="b-secondary"]', 42 ]; 43 44 for (const selector of selectors) { 45 const cta = sideMenu.querySelector(selector); 46 if (cta) return cta; 47 } 48 49 return null; 50 }; 51 52 /** 53 * Check if there are nav items in the side menu 54 * @returns {boolean} True if nav items exist 55 */ 56 const hasNavItems = () => { 57 const { sideMenu } = getMenuElements(); 58 if (!sideMenu) return false; 59 60 // Check for nav element first 61 const navElement = sideMenu.querySelector('nav'); 62 if (!navElement) return false; 63 64 // Check if nav has any links inside 65 const links = navElement.querySelectorAll('a'); 66 return links.length > 0; 67 }; 68 69 /** 70 * Handle the CTA display case for when there are no nav items 71 */ 72 const handleCtaDisplay = () => { 73 const { sideMenu, mobileHeader, headerCta } = getMenuElements(); 74 if (!sideMenu || !mobileHeader) return; 75 76 const ctaButton = findCtaButton(); 77 const hasNav = hasNavItems(); 78 79 // If we have a CTA but no nav items, we need special handling 80 if (!hasNav && ctaButton) { 81 // Hide the side menu 82 sideMenu.style.display = 'none'; 83 84 // Create header CTA if it doesn't exist 85 if (!headerCta && mobileHeader) { 86 // Create a new CTA by cloning the original to preserve all styling and classes 87 const newHeaderCta = ctaButton.cloneNode(true); 88 89 // Set ID while keeping original brick class 90 newHeaderCta.id = 'headerCta'; 91 92 // Ensure it has the cta-button class for styling 93 if (!newHeaderCta.classList.contains('cta-button')) { 94 newHeaderCta.classList.add('cta-button'); 95 } 96 97 // Add to the mobile header 98 mobileHeader.appendChild(newHeaderCta); 99 100 // Add click event to handle navigation 101 newHeaderCta.addEventListener('click', (e) => { 102 const href = newHeaderCta.getAttribute('href'); 103 if (!href || href === '#' || href.includes('undefined')) { 104 e.preventDefault(); 105 return; // Don't navigate to invalid URLs 106 } 107 }); 108 } 109 } else { 110 // Normal case - show side menu, hide header CTA 111 sideMenu.style.display = ''; 112 113 // Remove header CTA if it exists 114 if (headerCta) { 115 headerCta.remove(); 116 } 117 } 118 }; 119 120 /** 121 * Restores the scroll position after menu closes 122 * @param {HTMLElement} body - The document body element 123 */ 124 const restoreScrollPosition = (body) => { 125 const scrollY = body.style.top; 126 body.style.top = ''; 127 window.scrollTo(0, Number.parseInt(scrollY || '0', 10) * -1); 128 }; 129 130 /** 131 * Handles closing the mobile menu and restoring page scroll 132 * @returns {Promise} Resolves when menu is closed and animation completes 133 */ 134 const closeMobileMenu = () => { 135 return new Promise((resolve) => { 136 const { sideMenu, burgerIcon, body } = getMenuElements(); 137 138 if (sideMenu?.classList.contains('open')) { 139 // Remove menu-related classes 140 sideMenu.classList.remove('open'); 141 burgerIcon?.classList.remove('open'); 142 body.classList.remove('no-scroll'); 143 144 // Restore scroll position 145 restoreScrollPosition(body); 146 147 // Wait for menu close animation 148 setTimeout(resolve, 300); 149 } else { 150 resolve(); 151 } 152 }); 153 }; 154 155 /** 156 * Handles opening the mobile menu and storing scroll position 157 */ 158 const openMobileMenu = () => { 159 const { sideMenu, burgerIcon, body } = getMenuElements(); 160 161 sideMenu.classList.add('open'); 162 if (burgerIcon) burgerIcon.classList.add('open'); 163 body.classList.add('no-scroll'); 164 body.style.top = `-${window.scrollY}px`;
165 }; 166 167 /** 168 * Handles burger icon click to toggle menu state 169 * @param {Event} event - Click event 170 */ 171 const onBurgerClick = (event) => { 172 event.preventDefault(); 173 const { sideMenu } = getMenuElements(); 174 175 if (sideMenu.classList.contains('open')) { 176 closeMobileMenu(); 177 } else { 178 openMobileMenu(); 179 } 180 }; 181 182 /** 183 * Handles clicks on menu item arrows for submenus 184 * @param {Event} event - Click event 185 */ 186 const onMenuItemArrowClick = (event) => { 187 const arrowButton = event.currentTarget; 188 const menuItem = arrowButton.parentNode; 189 const menuBlock = menuItem.parentNode; 190 const menuSubpagesContainer = menuItem.nextSibling; 191 const arrowImg = arrowButton.querySelector('svg'); 192 const isOpen = menuBlock.classList.contains('brick-nav-content-item-opened'); 193 194 // Toggle submenu height 195 menuSubpagesContainer.style.height = isOpen ? '0px' : `${menuSubpagesContainer.scrollHeight}px`; 196 menuBlock.classList.toggle('brick-nav-content-item-opened'); 197 arrowImg.style.transform = isOpen ? 'rotate(0deg)' : 'rotate(180deg)'; 198 }; 199 200 /** 201 * Handles clicks on navigation links 202 * @param {Event} event - Click event 203 */ 204 const onNavLinkClick = async (event) => { 205 try { 206 const href = event.currentTarget.getAttribute('href'); 207 208 // Handle same-page anchor links 209 if (href?.includes('#')) { 210 const [pathname, hash] = href.split('#'); 211 const isSamePage = pathname === '' || pathname === window.location.pathname; 212 213 if (isSamePage && hash) { 214 event.preventDefault(); 215 216 // Close menu first 217 await closeMobileMenu(); 218 219 // Scroll to anchor 220 const targetElement = document.getElementById(hash); 221 if (targetElement) { 222 targetElement.scrollIntoView({ behavior: 'smooth' }); 223 } 224 } 225 } 226 227 // For other links (different pages), just close the menu 228 // and let default navigation happen 229 await closeMobileMenu(); 230 } catch (error) { 231 console.error('Error handling navigation link click:', error); 232 await closeMobileMenu(); 233 } 234 }; 235 236 // Initialize mobile menu functionality 237 [...bricks].forEach((brickScope) => { 238 // Add click handlers for burger menu buttons 239 const burgerButtons = brickScope.querySelectorAll('#burgerIcon'); 240 burgerButtons.forEach((button) => { 241 // Remove any existing listeners before adding new ones 242 const newButton = button.cloneNode(true); 243 button.parentNode.replaceChild(newButton, button); 244 newButton.addEventListener('click', onBurgerClick); 245 }); 246 247 // Add click handlers for submenu arrows 248 [...brickScope.querySelectorAll('.brick-nav-mobile-dropdown-button')].forEach((button) => { 249 button.addEventListener('click', onMenuItemArrowClick); 250 }); 251 252 // Add click handlers for all navigation links 253 [...brickScope.getElementsByTagName('a')].forEach((link) => { 254 link.addEventListener('click', onNavLinkClick); 255 }); 256 257 // Run this on a slight delay to ensure DOM is fully rendered 258 setTimeout(() => { 259 // Handle special case for CTA-only menus 260 handleCtaDisplay(); 261 }, 100); 262 263 // Recheck CTA display on window resize 264 window.addEventListener('resize', handleCtaDisplay); 265 }); 266})();
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.