1/** 2 * Amelia Main Header Widget JavaScript 3 * Handles mobile menu functionality and dropdown interactions 4 */ 5 6(function() { 7 'use strict'; 8 9 // Initialize when DOM is ready 10 document.addEventListener('DOMContentLoaded', function() { 11 initMainHeaderFunctionality(); 12 }); 13 14 // Also initialize immediately if DOM is already loaded 15 if (document.readyState === 'loading') { 16 document.addEventListener('DOMContentLoaded', initMainHeaderFunctionality); 17 } else { 18 initMainHeaderFunctionality(); 19 } 20 21 // Also initialize for Elementor preview mode 22 if (typeof elementorFrontend !== 'undefined') { 23 document.addEventListener('elementor/frontend/init', function() { 24 elementorFrontend.hooks.addAction('frontend/element_ready/amelia-main-header.default', function($scope) { 25 initMainHeaderFunctionality($scope); 26 }); 27 }); 28 } 29 30 function initMainHeaderFunctionality(scope) { 31 let container; 32 33 if (scope) { 34 // Handle jQuery objects from Elementor 35 if (scope.length !== undefined && scope[0]) { 36 container = scope[0]; 37 } else if (scope.nodeType) { 38 // Handle DOM elements 39 container = scope; 40 } else { 41 container = document; 42 } 43 } else { 44 container = document; 45 } 46 47 if (!container || !container.querySelectorAll) { 48 container = document; 49 } 50 51 const headers = container.querySelectorAll('.amelia-main-header'); 52 53 headers.forEach(function(header) { 54 initMobileMenu(header); 55 initDesktopDropdowns(header); 56 }); 57 } 58 59 function initMobileMenu(header) { 60 const mobileToggle = header.querySelector('.mobile-menu-toggle'); 61 // Look for mobile overlay globally since it's outside the header 62 const mobileOverlay = document.querySelector('.amelia-mobile-menu-overlay'); 63 const mobileClose = mobileOverlay ? mobileOverlay.querySelector('.mobile-menu-close') : null; 64 const dropdownItems = mobileOverlay ? mobileOverlay.querySelectorAll('.mobile-menu-item.has-dropdown') : []; 65 66 // Check if already initialized 67 if (mobileToggle && mobileToggle.hasAttribute('data-initialized')) { 68 return; 69 } 70 71 if (!mobileToggle || !mobileOverlay) { 72 return; 73 } 74 75 // Mark as initialized 76 mobileToggle.setAttribute('data-initialized', 'true'); 77 78 // Toggle mobile menu 79 mobileToggle.addEventListener('click', function(e) { 80 e.preventDefault(); 81 toggleMobileMenu(); 82 }); 83 84 function toggleMobileMenu() { 85 const isActive = mobileToggle.classList.contains('active'); 86 87 if (isActive) { 88 closeMobileMenu(); 89 } else { 90 mobileToggle.classList.add('active'); 91 mobileOverlay.classList.add('active'); 92 document.body.classList.add('mobile-menu-open'); 93 } 94 } 95 96 // Close mobile menu 97 if (mobileClose) { 98 mobileClose.addEventListener('click', closeMobileMenu); 99 } 100 101 // Close on overlay click (outside content) 102 mobileOverlay.addEventListener('click', function(e) { 103 if (e.target === mobileOverlay) { 104 closeMobileMenu(); 105 } 106 }); 107 108 // Close on escape key 109 document.addEventListener('keydown', function(e) { 110 if (e.key === 'Escape' && mobileOverlay.classList.contains('active')) { 111 closeMobileMenu(); 112 } 113 }); 114 115 function closeMobileMenu() { 116 mobileToggle.classList.remove('active'); 117 mobileOverlay.classList.remove('active'); 118 document.body.classList.remove('mobile-menu-open'); 119 } 120 121 // Close mobile menu on window resize to larger screens 122 let resizeTimeout; 123 window.addEventListener('resize', function() { 124 clearTimeout(resizeTimeout); 125 resizeTimeout = setTimeout(function() { 126 // Check if screen is now larger than mobile breakpoint (1024px) 127 if (window.innerWidth > 1024 && mobileOverlay.classList.contains('active')) { 128 closeMobileMenu(); 129 } 130 }, 150); // Debounce resize events 131 }); 132 133 // Handle mobile dropdown toggles
134 dropdownItems.forEach(function(item) { 135 const toggleItem = item.querySelector('.mobile-menu-toggle-item'); 136 137 if (toggleItem) { 138 toggleItem.addEventListener('click', function() { 139 // Close other dropdowns 140 dropdownItems.forEach(function(otherItem) { 141 if (otherItem !== item) { 142 otherItem.classList.remove('expanded'); 143 } 144 }); 145 146 // Toggle current dropdown 147 item.classList.toggle('expanded'); 148 }); 149 } 150 }); 151 152 // Handle mobile footer dropdown toggles 153 const mobileFooterItems = mobileOverlay ? mobileOverlay.querySelectorAll('.mobile-footer-item.has-dropdown') : []; 154 mobileFooterItems.forEach(function(footerItem) { 155 const toggleItem = footerItem.querySelector('.mobile-footer-toggle-item'); 156 157 if (toggleItem) { 158 toggleItem.addEventListener('click', function(e) { 159 e.preventDefault(); 160 161 // Close other footer dropdowns 162 mobileFooterItems.forEach(function(otherItem) { 163 if (otherItem !== footerItem) { 164 otherItem.classList.remove('expanded'); 165 } 166 }); 167 168 // Toggle current footer dropdown 169 footerItem.classList.toggle('expanded'); 170 }); 171 } 172 }); 173 174 // Close mobile menu when clicking on menu links 175 const mobileLinks = header.querySelectorAll('.mobile-menu-link, .mobile-dropdown-items a'); 176 mobileLinks.forEach(function(link) { 177 link.addEventListener('click', function() { 178 // Only close if it's not a hash link or if it's going to a different page 179 if (!link.getAttribute('href').startsWith('#') || link.getAttribute('href') === '#') { 180 setTimeout(closeMobileMenu, 300); 181 } 182 }); 183 }); 184 } 185 186 function initDesktopDropdowns(header) { 187 const dropdownItems = header.querySelectorAll('.desktop-nav .has-dropdown'); 188 189 dropdownItems.forEach(function(item) { 190 let hideTimeout; 191 192 // Show dropdown on mouse enter 193 item.addEventListener('mouseenter', function() { 194 clearTimeout(hideTimeout); 195 196 // Hide other dropdowns 197 dropdownItems.forEach(function(otherItem) { 198 if (otherItem !== item) { 199 otherItem.classList.remove('dropdown-open'); 200 } 201 }); 202 203 item.classList.add('dropdown-open'); 204 }); 205 206 // Hide dropdown on mouse leave 207 item.addEventListener('mouseleave', function() { 208 hideTimeout = setTimeout(function() { 209 item.classList.remove('dropdown-open'); 210 }, 100); 211 }); 212 213 // Handle keyboard navigation 214 const link = item.querySelector('.main-menu-link'); 215 if (link) { 216 link.addEventListener('keydown', function(e) { 217 if (e.key === 'Enter' || e.key === ' ') { 218 e.preventDefault(); 219 item.classList.toggle('dropdown-open'); 220 } 221 if (e.key === 'Escape') { 222 item.classList.remove('dropdown-open'); 223 } 224 }); 225 } 226 }); 227 228 // Close dropdowns when clicking outside 229 document.addEventListener('click', function(e) { 230 if (!header.contains(e.target)) { 231 dropdownItems.forEach(function(item) { 232 item.classList.remove('dropdown-open'); 233 }); 234 } 235 }); 236 } 237 238 // Prevent body scroll when mobile menu is open 239 const style = document.createElement('style'); 240 style.textContent = ` 241 body.mobile-menu-open { 242 overflow: hidden; 243 position: fixed; 244 width: 100%; 245 } 246 `; 247 document.head.appendChild(style); 248 249})();
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.