1/* ReactiveZero mobile navigation. 2 Injects a hamburger button, turns the navbar menu into a slide-down 3 panel, and makes the Solutions/Industries/Resources dropdowns work as 4 tap-to-expand accordions on phones & tablets. Desktop is untouched. */ 5(function () { 6 'use strict'; 7 8 var DESKTOP = 1280; /* matches the CSS â¤1279px mobile breakpoint */ 9 10 var MENU_ICON = 11 '<svg class="rz-ic-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">' + 12 '<path d="M3.5 6.5h17M3.5 12h17M3.5 17.5h17"/></svg>'; 13 var CLOSE_ICON = 14 '<svg class="rz-ic-close" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">' + 15 '<path d="M5.5 5.5l13 13M18.5 5.5l-13 13"/></svg>'; 16 17 function init() { 18 var comp = document.querySelector('.navbar2_component'); 19 var container = document.querySelector('.navbar2_container'); 20 var menu = document.querySelector('.navbar2_menu'); 21 if (!comp || !container || !menu || document.querySelector('.rz-nav-toggle')) return; 22 23 var btn = document.createElement('button'); 24 btn.type = 'button'; 25 btn.className = 'rz-nav-toggle'; 26 btn.setAttribute('aria-label', 'Open menu'); 27 btn.setAttribute('aria-expanded', 'false'); 28 btn.innerHTML = MENU_ICON + CLOSE_ICON; 29 container.appendChild(btn); 30 31 function isMobile() { return window.innerWidth < DESKTOP; } 32 33 function closeAccordions() { 34 menu.querySelectorAll('.navbar2_menu-dropdown.rz-open').forEach(function (d) { 35 d.classList.remove('rz-open'); 36 }); 37 } 38 39 function open() { 40 comp.classList.add('rz-nav-open'); 41 btn.setAttribute('aria-expanded', 'true'); 42 btn.setAttribute('aria-label', 'Close menu'); 43 document.body.classList.add('rz-nav-locked'); 44 } 45 function close() { 46 comp.classList.remove('rz-nav-open'); 47 btn.setAttribute('aria-expanded', 'false'); 48 btn.setAttribute('aria-label', 'Open menu'); 49 document.body.classList.remove('rz-nav-locked'); 50 closeAccordions(); 51 } 52 function toggle() { comp.classList.contains('rz-nav-open') ? close() : open(); } 53 54 btn.addEventListener('click', function (e) { e.stopPropagation(); toggle(); }); 55 56 /* Dropdown toggles â accordion on mobile (desktop keeps hover) */ 57 menu.querySelectorAll('.navbar2_dropdwn-toggle, .w-dropdown-toggle').forEach(function (tg) { 58 tg.addEventListener('click', function (e) { 59 if (!isMobile()) return; 60 e.preventDefault(); 61 e.stopPropagation(); 62 var dd = tg.closest('.navbar2_menu-dropdown') || tg.closest('.w-dropdown'); 63 if (!dd) return; 64 var willOpen = !dd.classList.contains('rz-open'); 65 menu.querySelectorAll('.navbar2_menu-dropdown.rz-open').forEach(function (o) { 66 if (o !== dd) o.classList.remove('rz-open'); 67 }); 68 dd.classList.toggle('rz-open', willOpen); 69 }, true); 70 }); 71 72 /* Tapping a real navigation link closes the panel */ 73 menu.querySelectorAll('a.navbar2_link, a.navbar2_dropdown-link').forEach(function (a) { 74 a.addEventListener('click', function () { if (isMobile()) close(); }); 75 }); 76 77 /* Desktop pointer hygiene. The desktop CSS keeps a dropdown open while it is 78 :hover OR :focus-within (the latter so keyboard users can open it). A mouse 79 click on the toggle, or on a link that opens a new tab (the R0 link), would 80 otherwise leave that element focused and pin the menu open after the pointer 81 leaves. So on desktop: mouse clicks never focus these elements, and on 82 mouseleave any non-keyboard focus inside the dropdown is dropped. Keyboard 83 (:focus-visible) focus is left alone. */ 84 menu.querySelectorAll('.navbar2_dropdwn-toggle, .w-dropdown-toggle, a.navbar2_dropdown-link').forEach(function (el) { 85 el.addEventListener('mousedown', function (e) { if (!isMobile()) e.preventDefault(); }); 86 }); 87 menu.querySelectorAll('.navbar2_menu-dropdown, .w-dropdown').forEach(function (dd) { 88 dd.addEventListener('mouseleave', function () { 89 if (isMobile()) return; 90 var ae = document.activeElement; 91 if (!ae || !dd.contains(ae)) return; 92 var kb = false; try { kb = ae.matches(':focus-visible'); } catch (x) {} 93 if (!kb) ae.blur(); 94 }); 95 }); 96 97 /* Dismiss on outside click / Escape */ 98 document.addEventListener('click', function (e) { 99 if (comp.classList.contains('rz-nav-open') && !comp.contains(e.target)) close(); 100 }); 101 document.addEventListener('keydown', function (e) { 102 if (e.key === 'Escape' && comp.classList.contains('rz-nav-open')) { close(); btn.focus(); } 103 }); 104 105 /* Reset when growing back to desktop */ 106 var raf; 107 window.addEventListener('resize', function () { 108 cancelAnimationFrame(raf); 109 raf = requestAnimationFrame(function () { if (!isMobile()) close(); }); 110 }); 111 } 112 113 if (document.readyState === 'loading') { 114 document.addEventListener('DOMContentLoaded', init); 115 } else { 116 init(); 117 } 118})();
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.