1document.addEventListener('DOMContentLoaded', function () { 2 3 /* ââ Scroll shadow âââââââââââââââââââââââââââ */ 4 const header = document.getElementById('site-header'); 5 if (header) { 6 window.addEventListener('scroll', () => { 7 header.classList.toggle('scrolled', window.scrollY > 20); 8 }, { passive: true }); 9 } 10 11 /* ââ Hamburger âââââââââââââââââââââââââââââââ */ 12 const hamburger = document.getElementById('hamburger'); 13 const nav = document.getElementById('hdr-nav'); 14 if (hamburger && nav) { 15 hamburger.addEventListener('click', () => { 16 const open = nav.classList.toggle('is-open'); 17 hamburger.classList.toggle('is-open', open); 18 hamburger.setAttribute('aria-expanded', open); 19 document.body.style.overflow = open ? 'hidden' : ''; 20 }); 21 document.addEventListener('keydown', e => { 22 if (e.key === 'Escape' && nav.classList.contains('is-open')) { 23 nav.classList.remove('is-open'); 24 hamburger.classList.remove('is-open'); 25 document.body.style.overflow = ''; 26 } 27 }); 28 } 29 30 /* ââ Mobile dropdowns ââââââââââââââââââââââââ */ 31 document.querySelectorAll('.nav-link--drop').forEach(link => { 32 link.addEventListener('click', () => { 33 if (window.innerWidth > 900) return; 34 const item = link.closest('.nav-item'); 35 const wasOpen = item.classList.contains('is-open'); 36 document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('is-open')); 37 if (!wasOpen) item.classList.add('is-open'); 38 }); 39 }); 40 41 window.addEventListener('resize', () => { 42 if (window.innerWidth > 900 && nav) { 43 nav.classList.remove('is-open'); 44 hamburger && hamburger.classList.remove('is-open'); 45 document.body.style.overflow = ''; 46 document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('is-open')); 47 } 48 }); 49 50 /* ââ Active nav ââââââââââââââââââââââââââââââ */ 51 const path = window.location.pathname; 52 document.querySelectorAll('.nav-link').forEach(link => { 53 const href = link.getAttribute('href'); 54 if (href && href.length > 4 && path.includes(href)) { 55 link.closest('.nav-item')?.classList.add('active'); 56 } 57 }); 58 59 /* ââ Language modal ââââââââââââââââââââââââââ */ 60 const backdrop = document.getElementById('lang-modal-backdrop'); 61 if (backdrop) { 62 // Set session flag then navigate 63 backdrop.querySelectorAll('.lang-option').forEach(el => { 64 el.addEventListener('click', function(e) { 65 e.preventDefault(); 66 const href = this.getAttribute('href'); 67 // Tell server to set session so modal doesn't show again this session 68 fetch('/set-lang-session.php').finally(() => { 69 window.location.href = href; 70 }); 71 }); 72 }); 73 74 // Dismiss on backdrop click (without setting session â will show again on reload) 75 backdrop.addEventListener('click', e => { 76 if (e.target === backdrop) fadeOut(backdrop); 77 }); 78 79 // Dismiss on Escape 80 document.addEventListener('keydown', e => { 81 if (e.key === 'Escape') fadeOut(backdrop); 82 }); 83 } 84 85 /* ââ Lang toggle â smooth page transition âââ */ 86 document.querySelectorAll('.lang-toggle').forEach(toggle => { 87 toggle.addEventListener('click', function(e) { 88 e.preventDefault(); 89 const href = this.getAttribute('href'); 90 // Fade page out then navigate 91 document.body.style.transition = 'opacity 0.3s ease'; 92 document.body.style.opacity = '0'; 93 setTimeout(() => { window.location.href = href; }, 300); 94 }); 95 }); 96 97 /* ââ Helpers âââââââââââââââââââââââââââââââââ */ 98 function fadeOut(el) { 99 el.style.transition = 'opacity 0.35s ease'; 100 el.style.opacity = '0'; 101 el.style.pointerEvents = 'none'; 102 setTimeout(() => { el.style.display = 'none'; }, 360); 103 } 104 105 function setCookie(name, value, days) { 106 const expires = new Date(Date.now() + days * 864e5).toUTCString(); 107 document.cookie = name + '=' + value + '; expires=' + expires + '; path=/'; 108 } 109 110 function getCookie(name) { 111 return document.cookie.split('; ').reduce((r, v) => { 112 const parts = v.split('='); 113 return parts[0] === name ? parts[1] : r; 114 }, null); 115 } 116 117});
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.