1 2"use strict"; 3document.documentElement.classList.add("js"); 4 5const state = { 6 language: localStorage.getItem("profiLanguage") || "ru", 7 content: null 8}; 9 10const $ = (s, root=document) => root.querySelector(s); 11const $$ = (s, root=document) => [...root.querySelectorAll(s)]; 12 13async function loadContent(){ 14 const r = await fetch("data/site-content.json", {cache:"no-store"}); 15 if(!r.ok) throw new Error("site-content.json"); 16 state.content = await r.json(); 17 applyLanguage(); 18} 19 20function applyLanguage(){ 21 if(!state.content) return; 22 const t = state.content[state.language]; 23 document.documentElement.lang = state.language === "kk" ? "kk" : "ru"; 24 25 $$("[data-i18n]").forEach(el => { 26 const key = el.dataset.i18n; 27 if(Object.prototype.hasOwnProperty.call(t,key)) el.textContent = t[key]; 28 }); 29 30 $$("[data-lang-btn]").forEach(btn => { 31 const active = btn.dataset.langBtn === state.language; 32 btn.classList.toggle("active", active); 33 btn.setAttribute("aria-pressed", active ? "true":"false"); 34 }); 35 36 const year = $("#currentYear"); 37 if(year) year.textContent = new Date().getFullYear(); 38 39 window.dispatchEvent(new CustomEvent("profi-language-change", {detail:{language:state.language, content:t}})); 40} 41 42$$("[data-lang-btn]").forEach(btn => { 43 btn.addEventListener("click", () => { 44 state.language = btn.dataset.langBtn; 45 localStorage.setItem("profiLanguage", state.language); 46 applyLanguage(); 47 }); 48}); 49 50const menuBtn = $("#menuBtn"); 51const mobileNav = $("#mobileNav"); 52 53function closeMenu(){ 54 if(!menuBtn || !mobileNav) return; 55 menuBtn.classList.remove("active"); 56 mobileNav.classList.remove("open"); 57 menuBtn.setAttribute("aria-expanded","false"); 58 document.body.classList.remove("menu-open"); 59} 60if(menuBtn && mobileNav){ 61 menuBtn.addEventListener("click", () => { 62 const open = mobileNav.classList.toggle("open"); 63 menuBtn.classList.toggle("active", open); 64 menuBtn.setAttribute("aria-expanded", open ? "true":"false"); 65 document.body.classList.toggle("menu-open", open); 66 }); 67 $$("#mobileNav a").forEach(a => a.addEventListener("click", closeMenu)); 68 window.addEventListener("resize", () => { if(innerWidth > 1020) closeMenu(); }); 69} 70 71function initReveal(){ 72 const items = $$(".reveal"); 73 if(!("IntersectionObserver" in window)){ 74 items.forEach(i => i.classList.add("visible")); 75 return; 76 } 77 const io = new IntersectionObserver(entries => { 78 entries.forEach(e => { 79 if(e.isIntersecting){ 80 e.target.classList.add("visible"); 81 io.unobserve(e.target); 82 } 83 }); 84 }, {threshold:.12}); 85 items.forEach(i => io.observe(i)); 86} 87document.addEventListener("DOMContentLoaded", initReveal); 88 89loadContent().catch(console.error);
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.