PageSourceSearch

http://mpl.ai/newwebsite/js/layout.js

js mpl.ai collected 2026-10-05 15:39:50 UTC 4,426 bytes, 108 lines download raw bytes

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.