1/* ================================================================ 2 layout.js â BEHAVIOR ONLY (no HTML generation) 3 All nav/footer HTML lives statically in each page. 4 Language switching: plain <a> links to /culture/?culture=... 5 Handles: theme toggle, logo swap, mobile menu, scroll shadow. 6================================================================ */ 7 8/* ââ Anti-flash: apply saved theme before paint âââââââââââââââ */ 9(function () { 10 var saved = localStorage.getItem('mpl-theme'); 11 var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; 12 document.documentElement.classList.toggle('dark', (saved || (prefersDark ? 'dark' : 'light')) === 'dark'); 13})(); 14 15window.applyTheme = function (theme) { 16 document.documentElement.classList.toggle('dark', theme === 'dark'); 17 localStorage.setItem('mpl-theme', theme); 18 _updateThemeUI(theme); 19}; 20 21function _updateThemeUI(theme) { 22 var isDark = theme === 'dark'; 23 document.querySelectorAll('.logo-img-light').forEach(function (el) { el.style.display = isDark ? 'none' : 'block'; }); 24 document.querySelectorAll('.logo-img-dark').forEach(function (el) { el.style.display = isDark ? 'block' : 'none'; }); 25 document.querySelectorAll('.icon-sun').forEach(function (el) { el.style.display = isDark ? 'none' : 'block'; }); 26 document.querySelectorAll('.icon-moon').forEach(function (el) { el.style.display = isDark ? 'block' : 'none'; }); 27 document.querySelectorAll('.theme-label-light').forEach(function (el) { el.style.display = isDark ? 'none' : 'inline'; }); 28 document.querySelectorAll('.theme-label-dark').forEach(function (el) { el.style.display = isDark ? 'inline' : 'none'; }); 29} 30 31document.addEventListener('DOMContentLoaded', function () { 32 33 /* Init theme UI */ 34 var currentTheme = localStorage.getItem('mpl-theme') || 35 (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); 36 _updateThemeUI(currentTheme); 37 38 /* Theme toggle buttons */ 39 document.querySelectorAll('#theme-toggle-btn, #theme-toggle-mobile').forEach(function (btn) { 40 btn.addEventListener('click', function () { 41 window.applyTheme(document.documentElement.classList.contains('dark') ? 'light' : 'dark'); 42 }); 43 }); 44 45 /* Sticky nav scroll shadow */ 46 var nav = document.querySelector('.site-nav'); 47 if (nav) { 48 var onScroll = function () { nav.classList.toggle('scrolled', window.scrollY > 10); }; 49 window.addEventListener('scroll', onScroll, { passive: true }); 50 onScroll(); 51 } 52 53 /* Mobile hamburger */ 54 var hamburger = document.querySelector('.nav-hamburger'); 55 var mobileMenu = document.querySelector('.nav-mobile'); 56 if (hamburger && mobileMenu) { 57 var mobileViewport = window.matchMedia('(max-width: 768px)'); 58 var setMobileMenuState = function (open) { 59 mobileMenu.classList.toggle('open', open); 60 hamburger.setAttribute('aria-expanded', String(open)); 61 document.documentElement.classList.toggle('nav-menu-open', open); 62 document.body.classList.toggle('nav-menu-open', open); 63 }; 64 65 var closeMobileMenu = function () { 66 setMobileMenuState(false); 67 }; 68 69 hamburger.addEventListener('click', function () { 70 setMobileMenuState(!mobileMenu.classList.contains('open')); 71 if (typeof lucide !== 'undefined') lucide.createIcons(); 72 }); 73 74 mobileMenu.querySelectorAll('a').forEach(function (link) { 75 link.addEventListener('click', function () { 76 closeMobileMenu(); 77 }); 78 }); 79 80 document.addEventListener('click', function (event) { 81 if (!mobileMenu.classList.contains('open') || !mobileViewport.matches) return; 82 if (mobileMenu.contains(event.target) || hamburger.contains(event.target)) return; 83 closeMobileMenu(); 84 }); 85 86 document.addEventListener('keydown', function (event) { 87 if (event.key === 'Escape' && mobileMenu.classList.contains('open')) { 88 closeMobileMenu(); 89 hamburger.focus(); 90 } 91 }); 92 93 mobileViewport.addEventListener('change', function (event) { 94 if (!event.matches) { 95 closeMobileMenu(); 96 } 97 }); 98 } 99 100 /* Lucide icons */ 101 if (typeof lucide !== 'undefined') lucide.createIcons(); 102 103 /* OS theme preference change */ 104 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function (e) { 105 if (!localStorage.getItem('mpl-theme')) window.applyTheme(e.matches ? 'dark' : 'light'); 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.