1(function () { 2 'use strict'; 3 4 // ----------------------------- 5 // Theme toggle 6 // ----------------------------- 7 var root = document.documentElement; 8 var themeBtn = document.getElementById('theme-toggle'); 9 if (themeBtn) { 10 themeBtn.addEventListener('click', function () { 11 var current = root.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'; 12 var next = current === 'dark' ? 'light' : 'dark'; 13 root.setAttribute('data-theme', next); 14 try { localStorage.setItem('theme', next); } catch (e) {} 15 }); 16 } 17 18 // ----------------------------- 19 // Mobile menu 20 // ----------------------------- 21 var menuBtn = document.getElementById('menu-toggle'); 22 var mobileMenu = document.getElementById('mobile-menu'); 23 if (menuBtn && mobileMenu) { 24 menuBtn.addEventListener('click', function () { 25 var open = mobileMenu.classList.toggle('open'); 26 mobileMenu.hidden = !open; 27 menuBtn.setAttribute('aria-expanded', open ? 'true' : 'false'); 28 }); 29 } 30 31 // ----------------------------- 32 // Search panel 33 // ----------------------------- 34 var searchBtn = document.getElementById('search-toggle'); 35 var searchPanel = document.getElementById('search-panel'); 36 var searchClose = document.getElementById('search-close'); 37 var searchInput = document.getElementById('lunr-input'); 38 39 function openSearch() { 40 if (!searchPanel) return; 41 searchPanel.hidden = false; 42 if (searchInput) setTimeout(function () { searchInput.focus(); }, 50); 43 } 44 function closeSearch() { 45 if (!searchPanel) return; 46 searchPanel.hidden = true; 47 } 48 if (searchBtn) searchBtn.addEventListener('click', openSearch); 49 if (searchClose) searchClose.addEventListener('click', closeSearch); 50 document.addEventListener('keydown', function (e) { 51 if (e.key === '/' && document.activeElement && document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') { 52 e.preventDefault(); 53 openSearch(); 54 } else if (e.key === 'Escape' && searchPanel && !searchPanel.hidden) { 55 closeSearch(); 56 } 57 }); 58 59 // ----------------------------- 60 // Reading progress on post pages 61 // ----------------------------- 62 var progressEl = document.getElementById('reading-progress'); 63 if (progressEl) { 64 var ticking = false; 65 var update = function () { 66 var doc = document.documentElement; 67 var body = document.body; 68 var scrollTop = window.pageYOffset || doc.scrollTop || body.scrollTop || 0; 69 var docHeight = Math.max( 70 body.scrollHeight, doc.scrollHeight, 71 body.offsetHeight, doc.offsetHeight 72 ); 73 var winHeight = window.innerHeight || doc.clientHeight; 74 var trackable = docHeight - winHeight; 75 var pct = trackable > 0 ? (scrollTop / trackable) * 100 : 0; 76 if (pct < 0) pct = 0; 77 if (pct > 100) pct = 100; 78 progressEl.style.width = pct + '%'; 79 ticking = false; 80 }; 81 var onScroll = function () { 82 if (!ticking) { 83 window.requestAnimationFrame(update); 84 ticking = true; 85 } 86 }; 87 window.addEventListener('scroll', onScroll, { passive: true }); 88 window.addEventListener('resize', onScroll); 89 update(); 90 } 91 92 // ----------------------------- 93 // Disqus lazy load on click 94 // ----------------------------- 95 document.querySelectorAll('.show-comments').forEach(function (btn) { 96 btn.addEventListener('click', function () { 97 var c = document.getElementById('comments'); 98 if (c) c.style.display = 'block'; 99 btn.style.display = 'none'; 100 }); 101 }); 102 103 // ----------------------------- 104 // Smooth scroll for in-page anchors 105 // ----------------------------- 106 document.querySelectorAll('a[href^="#"]').forEach(function (link) { 107 link.addEventListener('click', function (e) { 108 var href = link.getAttribute('href'); 109 if (href.length <= 1) return; 110 var target = document.querySelector(href); 111 if (!target) return; 112 e.preventDefault(); 113 target.scrollIntoView({ behavior: 'smooth', block: 'start' }); 114 }); 115 }); 116})();
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.