1/* 2 * Gapfy mobile nav â slide-in overlay menu on viewports under 3 * 1024px. The hamburger button in SiteNavbar.razor flips 4 * .has-mobile-open on the <header>, which the CSS uses to slide 5 * .site-nav-links in from the right (and overlay the page). 6 * 7 * Closes on: 8 * - hamburger click again (toggle) 9 * - ESC key 10 * - click on backdrop 11 * - click on any nav link (so navigating closes the drawer) 12 * 13 * Body scroll is locked while the drawer is open so scrolling the 14 * overlay does not bleed into the page underneath. 15 * 16 * Why event delegation instead of per-element listeners: 17 * This is a Blazor Web App with enhanced navigation. When the user 18 * follows a link, Blazor fetches the new page and MERGES it into the 19 * live DOM rather than reloading. During that merge the <header> can be 20 * preserved, recreated, or have JS-injected nodes (like the backdrop) 21 * stripped â none of which is under our control. Per-element listeners 22 * wired once and a "bound" flag get silently lost or skipped after such 23 * a swap, which is exactly how the mobile menu "stops working" after the 24 * first in-app navigation. By delegating every click off document (which 25 * Blazor never replaces) and creating the backdrop on demand when the 26 * drawer opens, the menu keeps working through any number of enhanced 27 * navigations with no re-scan and no dependency on the enhancedload event. 28 */ 29(function () { 30 if (typeof window === 'undefined' || typeof document === 'undefined') return; 31 32 var OPEN_CLASS = 'has-mobile-open'; 33 var BODY_LOCK_CLASS = 'has-mobile-nav-open'; 34 35 // The backdrop is not in the server-rendered markup (keeps the Razor 36 // clean). Create it lazily the first time a header opens, or re-create 37 // it if an enhanced-navigation merge stripped the previously injected 38 // one. Either way the open header always ends up with a backdrop. 39 function ensureBackdrop(header) { 40 var backdrop = header.querySelector('.site-nav-mobile-backdrop'); 41 if (!backdrop) { 42 backdrop = document.createElement('div'); 43 backdrop.className = 'site-nav-mobile-backdrop'; 44 backdrop.setAttribute('aria-hidden', 'true'); 45 header.appendChild(backdrop); 46 } 47 return backdrop; 48 } 49 50 function open(header) { 51 ensureBackdrop(header); 52 header.classList.add(OPEN_CLASS); 53 document.body.classList.add(BODY_LOCK_CLASS); 54 var toggle = header.querySelector('.site-nav-mobile-toggle'); 55 if (toggle) toggle.setAttribute('aria-expanded', 'true'); 56 } 57 58 function close(header) { 59 header.classList.remove(OPEN_CLASS); 60 document.body.classList.remove(BODY_LOCK_CLASS); 61 var toggle = header.querySelector('.site-nav-mobile-toggle'); 62 if (toggle) toggle.setAttribute('aria-expanded', 'false'); 63 } 64 65 function closeAll() { 66 var openHeaders = document.querySelectorAll('.site-navbar.' + OPEN_CLASS); 67 for (var i = 0; i < openHeaders.length; i++) close(openHeaders[i]); 68 } 69 70 // Single delegated click handler. Lives on document, so it survives 71 // every DOM swap Blazor performs during enhanced navigation. 72 document.addEventListener('click', function (e) { 73 var target = e.target; 74 // e.target can be a text node / SVG in some browsers; guard closest. 75 if (!target || typeof target.closest !== 'function') return; 76 77 // Hamburger (or the icon inside it) â toggle the drawer. 78 var toggle = target.closest('.site-nav-mobile-toggle'); 79 if (toggle) { 80 var header = toggle.closest('.site-navbar'); 81 if (!header) return; 82 e.preventDefault(); 83 e.stopPropagation(); 84 if (header.classList.contains(OPEN_CLASS)) close(header); else open(header); 85 return; 86 } 87 88 // Backdrop â close the header it belongs to. 89 var backdrop = target.closest('.site-nav-mobile-backdrop'); 90 if (backdrop) { 91 var h = backdrop.closest('.site-navbar'); 92 if (h) close(h); 93 return; 94 } 95 96 // Link inside the drawer â close so navigation feels natural. 97 // Dropdown triggers are <button>, not <a>, so they keep acting as 98 // in-drawer accordions and do not match here. 99 var link = target.closest('.site-nav-links a'); 100 if (link) { 101 var hd = link.closest('.site-navbar'); 102 if (hd && hd.classList.contains(OPEN_CLASS)) close(hd); 103 } 104 }); 105 106 // ESC closes any open drawer. 107 document.addEventListener('keydown', function (e) { 108 if (e.key === 'Escape' || e.keyCode === 27) closeAll(); 109 }); 110 111 // Auto-close if the viewport grows past the mobile breakpoint while the 112 // drawer is open â otherwise the desktop layout shows both the drawer 113 // and the inline nav at once. 114 var mql = window.matchMedia('(min-width: 1024px)'); 115 function onMql() { if (mql.matches) closeAll(); } 116 if (mql.addEventListener) mql.addEventListener('change', onMql); 117 else if (mql.addListener) mql.addListener(onMql); 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.