(function () { "use strict"; /* ------------------------------------------------------ small helpers */ var root = document.documentElement; function remember(key, value) { try { localStorage.setItem(key, value); } catch (error) { /* private mode: never mind */ } } /* ------------------------------------------------------- the language */ var description = document.querySelector('meta[name="description"]'); var TITLE_EN = document.title; var TITLE_RU = "CursorLang — раскладка клавиатуры у курсора"; var DESCRIPTION_EN = description.getAttribute("content"); var DESCRIPTION_RU = description.getAttribute("data-content-ru"); function applyLanguage(lang) { if (lang !== "ru") { lang = "en"; } root.setAttribute("data-lang", lang); root.setAttribute("lang", lang); document.title = lang === "ru" ? TITLE_RU : TITLE_EN; description.setAttribute("content", lang === "ru" ? DESCRIPTION_RU : DESCRIPTION_EN); var labelled = document.querySelectorAll("[data-label-en]"); for (var i = 0; i < labelled.length; i++) { labelled[i].setAttribute("aria-label", labelled[i].getAttribute("data-label-" + lang)); } var buttons = document.querySelectorAll("[data-set-lang]"); for (var j = 0; j < buttons.length; j++) { buttons[j].setAttribute("aria-pressed", buttons[j].getAttribute("data-set-lang") === lang ? "true" : "false"); } applyLook(); } document.addEventListener("click", function (event) { var button = event.target.closest("[data-set-lang]"); if (!button) { return; } var lang = button.getAttribute("data-set-lang"); applyLanguage(lang); remember("cursorlang.lang", lang); }); /* ---------------------------------------------------------- the theme */ document.getElementById("themeBtn").addEventListener("click", function () { var pinned = root.getAttribute("data-theme"); var dark = pinned ? pinned === "dark" : window.matchMedia("(prefers-color-scheme: dark)").matches; var next = dark ? "light" : "dark"; root.setAttribute("data-theme", next); remember("cursorlang.theme", next); }); /* ----------------------------------------------------- the menu button */ var header = document.querySelector("header"); var menuBtn = document.getElementById("menuBtn"); function setMenu(open) { header.setAttribute("data-menu", open ? "open" : "closed"); menuBtn.setAttribute("aria-expanded", open ? "true" : "false"); } menuBtn.addEventListener("click", function () { setMenu(header.getAttribute("data-menu") !== "open"); }); document.addEventListener("click", function (event) { if (header.getAttribute("data-menu") !== "open") { return; } if (event.target.closest("nav.links a") || !event.target.closest("header")) { setMenu(false); } }); document.addEventListener("keydown", function (event) { if (event.key !== "Escape" || header.getAttribute("data-menu") !== "open") { return; } setMenu(false); menuBtn.focus(); }); window.matchMedia("(min-width: 901px)").addEventListener("change", function (event) { if (event.matches) { setMenu(false); } }); // Back to top, without leaving a fragment behind in the address bar document.addEventListener("click", function (event) { var back = event.target.closest('a[href="#top"]'); if (!back) { return; } event.preventDefault(); window.scrollTo({ top: 0 }); history.replaceState(null, "", location.pathname + location.search); }); /* ------------------------------------------- the border under the bar */ var onScroll = function () { header.classList.toggle("scrolled", window.scrollY > 4); }; window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); /* ------------------------------------------------------- the demo above */ var demoText = document.getElementById("demoText"); var demoChip = document.getElementById("demoChip"); var indicator = document.getElementById("demoIndicator"); // A sentence typed in two layouts, the way one is var SCRIPT = [ { layout: "EN", indicator: "ENG", text: "Deploy is green, " }, { layout: "RU", indicator: "РУС", text: "можно мержить" } ]; var stillMotion = window.matchMedia("(prefers-reduced-motion: reduce)"); var timers = []; var typing = false; var onScreen = true; function later(fn, delay) { timers.push(setTimeout(fn, delay)); } function stopTyping() { for (var i = 0; i < timers.length; i++) { clearTimeout(timers[i]); } timers = []; typing = false; } function showStill() { demoText.textContent = SCRIPT[0].text + SCRIPT[1].text; demoChip.textContent = SCRIPT[1].layout; demoChip.classList.remove("out"); demoChip.hidden = false; indicator.textContent = SCRIPT[1].indicator; } function showChip(text) { demoChip.textContent = text; demoChip.classList.remove("out"); demoChip.hidden = false; later(function () { demoChip.classList.add("out"); }, 1100); later(function () { demoChip.hidden = true; }, 1260); } function typeSegment(index, done) { if (index >= SCRIPT.length) { done(); return; } var segment = SCRIPT[index]; indicator.textContent = segment.indicator; showChip(segment.layout); var position = 0; var step = function () { if (position >= segment.text.length) { later(function () { typeSegment(index + 1, done); }, 420); return; } demoText.textContent += segment.text.charAt(position); position++; later(step, 52 + Math.random() * 58); }; later(step, 620); } function run() { demoText.textContent = ""; demoChip.hidden = true; typeSegment(0, function () { later(run, 2600); }); } function renderDemo() { if (stillMotion.matches) { stopTyping(); showStill(); return; } if (document.hidden || !onScreen) { stopTyping(); return; } if (typing) { return; } typing = true; run(); } stillMotion.addEventListener("change", renderDemo); document.addEventListener("visibilitychange", renderDemo); new IntersectionObserver(function (entries) { onScreen = entries[0].isIntersecting; renderDemo(); }).observe(document.querySelector(".stage")); renderDemo(); /* ------------------------------------------------ the preview below */ var chip = document.getElementById("previewChip"); var offset = document.getElementById("offset"); var fontSize = document.getElementById("fontSize"); var opacity = document.getElementById("opacity"); var bgColor = document.getElementById("bgColor"); var fgColor = document.getElementById("fgColor"); var duration = document.getElementById("duration"); var offsetVal = document.getElementById("offsetVal"); var fontSizeVal = document.getElementById("fontSizeVal"); var opacityVal = document.getElementById("opacityVal"); var durationVal = document.getElementById("durationVal"); function applyLook() { var onTheLeft = chip.classList.contains("left"); chip.style.fontSize = fontSize.value + "px"; chip.style.opacity = (opacity.value / 100).toFixed(2); chip.style.background = bgColor.value; chip.style.color = fgColor.value; chip.style.marginLeft = onTheLeft ? "0px" : offset.value + "px"; chip.style.marginRight = onTheLeft ? offset.value + "px" : "0px"; offsetVal.textContent = offset.value; fontSizeVal.textContent = fontSize.value; opacityVal.textContent = opacity.value + "%"; durationVal.textContent = duration.value + (root.getAttribute("data-lang") === "ru" ? " мс" : " ms"); } var inputs = [offset, fontSize, opacity, bgColor, fgColor, duration]; for (var k = 0; k < inputs.length; k++) { inputs[k].addEventListener("input", applyLook); } document.getElementById("lookForm").addEventListener("submit", function (event) { event.preventDefault(); }); document.getElementById("side").addEventListener("click", function (event) { var button = event.target.closest("[data-side]"); if (!button) { return; } var buttons = this.querySelectorAll("[data-side]"); for (var i = 0; i < buttons.length; i++) { buttons[i].setAttribute("aria-pressed", buttons[i] === button ? "true" : "false"); } var left = button.getAttribute("data-side") === "left"; chip.classList.toggle("left", left); chip.classList.toggle("right", !left); applyLook(); }); var previewTimers = []; document.getElementById("previewBtn").addEventListener("click", function () { for (var i = 0; i < previewTimers.length; i++) { clearTimeout(previewTimers[i]); } previewTimers = []; chip.classList.add("out"); previewTimers.push(setTimeout(function () { chip.classList.remove("out"); previewTimers.push(setTimeout(function () { chip.classList.add("out"); previewTimers.push(setTimeout(function () { chip.classList.remove("out"); }, 900)); }, Number(duration.value))); }, 420)); }); /* ------------------------------------------------------------ the start */ applyLanguage(root.getAttribute("data-lang")); }());