1(function () { 2 'use strict'; 3 4 /* ---- Sticky-header background on scroll ---- */ 5 var header = document.getElementById('ows-header'); 6 function onScroll() { 7 if (!header) return; 8 header.classList.toggle('is-scrolled', window.scrollY > 8); 9 } 10 window.addEventListener('scroll', onScroll, { passive: true }); 11 onScroll(); 12 13 /* ---- Mobile burger toggle ---- */ 14 var burger = document.getElementById('ows-burger'); 15 var mobile = document.getElementById('ows-mobile'); 16 if (burger && mobile) { 17 burger.addEventListener('click', function () { 18 var open = mobile.classList.toggle('is-open'); 19 burger.setAttribute('aria-expanded', open ? 'true' : 'false'); 20 }); 21 // Close when a leaf link (no sub-menu sibling) is followed. 22 mobile.addEventListener('click', function (e) { 23 var link = e.target.closest('a'); 24 if (!link) return; 25 var parentLi = link.parentElement; 26 var hasSub = parentLi && parentLi.querySelector(':scope > .sub-menu'); 27 if (!hasSub) { 28 mobile.classList.remove('is-open'); 29 burger.setAttribute('aria-expanded', 'false'); 30 } 31 }); 32 } 33 34 /* ---- Mobile sub-menu accordion ---- */ 35 // Target <li class="menu-item-has-children"> inside .ows-mobile nav. 36 var mobileNav = mobile ? mobile.querySelector('nav') : null; 37 if (mobileNav) { 38 var parents = mobileNav.querySelectorAll('.menu-item-has-children'); 39 parents.forEach(function (li) { 40 var trigger = li.querySelector(':scope > a'); 41 var sub = li.querySelector(':scope > .sub-menu'); 42 if (!trigger || !sub) return; 43 44 // Prevent navigation when toggling a parent that only has hash href. 45 trigger.addEventListener('click', function (e) { 46 var href = trigger.getAttribute('href'); 47 if (!href || href === '#') { 48 e.preventDefault(); 49 } 50 // Close siblings. 51 parents.forEach(function (sibling) { 52 if (sibling !== li && sibling.parentElement === li.parentElement) { 53 sibling.classList.remove('is-open'); 54 } 55 }); 56 li.classList.toggle('is-open'); 57 }); 58 }); 59 } 60 61 /* ---- Desktop dropdown: close on Escape ---- */ 62 document.addEventListener('keydown', function (e) { 63 if (e.key === 'Escape') { 64 var focused = document.activeElement; 65 if (focused) focused.blur(); 66 } 67 }); 68 69 /* ---- Dark / Light theme toggle ---- */ 70 var themeToggle = document.getElementById('ows-theme-toggle'); 71 var themeMeta = document.getElementById('ows-theme-meta'); 72 73 function applyTheme(theme) { 74 document.documentElement.setAttribute('data-theme', theme); 75 localStorage.setItem('ows-theme', theme); 76 if (themeMeta) { 77 themeMeta.content = theme === 'light' ? '#f8fafc' : '#0a0e1a'; 78 } 79 if (themeToggle) { 80 themeToggle.setAttribute( 81 'aria-label', 82 theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode' 83 ); 84 } 85 } 86 87 if (themeToggle) { 88 // Set initial aria-label 89 var initialTheme = document.documentElement.getAttribute('data-theme') || 'dark'; 90 themeToggle.setAttribute( 91 'aria-label', 92 initialTheme === 'light' ? 'Switch to dark mode' : 'Switch to light mode' 93 ); 94 95 themeToggle.addEventListener('click', function () { 96 var current = document.documentElement.getAttribute('data-theme') || 'dark'; 97 applyTheme(current === 'light' ? 'dark' : 'light'); 98 }); 99 } 100 101 // React to system preference changes (when no manual override has been saved). 102 window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', function (e) { 103 if (!localStorage.getItem('ows-theme')) { 104 applyTheme(e.matches ? 'light' : 'dark'); 105 } 106 }); 107 108})();
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.