const MAX_HISTORY_LENGTH = 32; const PIP_WINDOW_SIZE = { width: 280, height: 140 }; class Counter { constructor(elementId, storageKey) { this.el = document.getElementById(elementId); this.key = storageKey; this.count = parseInt(localStorage.getItem(this.key)) || 0; this.history = []; this.render(); } render = () => { this.el.textContent = this.count; } set = (val) => { this.history.push(this.count); if (this.history.length > MAX_HISTORY_LENGTH) this.history.shift(); this.count = val; localStorage.setItem(this.key, this.count); this.render(); } increase = () => { this.set(this.count + 1); } undo = () => { if (this.history.length > 0) { this.count = this.history.pop(); localStorage.setItem(this.key, this.count); this.render(); } } } const stitches = new Counter("stitches-counter", "stitches"); const rows = new Counter("rows-counter", "rows"); const inputAuto = document.getElementById("input-autoincrease"); const inputStitchMax = document.getElementById("input-stitches"); inputAuto.checked = localStorage.getItem("input_autoincrease") === "true"; inputStitchMax.value = localStorage.getItem("input_stitches") || "10"; inputAuto.addEventListener("change", () => { localStorage.setItem("input_autoincrease", inputAuto.checked); }); inputStitchMax.addEventListener("change", () => { localStorage.setItem("input_stitches", inputStitchMax.value); }); document.getElementById("button-stitch").addEventListener("click", () => { const max = parseInt(inputStitchMax.value); if (inputAuto.checked && stitches.count + 1 >= max) { rows.increase(); stitches.set(0); } else { stitches.increase(); } }); document.getElementById("button-row").addEventListener("click", () => { rows.increase(); stitches.set(0); }); document.getElementById("button-undo").addEventListener("click", () => { stitches.undo(); rows.undo(); }); document.getElementById("button-reset").addEventListener("click", () => { stitches.set(0); rows.set(0); }); class PipManager { constructor(pipElement, pipContainer, pipButton) { this.el = pipElement; this.container = pipContainer; this.button = pipButton; this.pipWindow = null; this.button.addEventListener("click", this.start); } start = async () => { if (this.pipWindow && !this.pipWindow.closed) return; this.pipWindow = await documentPictureInPicture.requestWindow(PIP_WINDOW_SIZE); for (const styleSheet of document.styleSheets) { try { const style = document.createElement("style"); const rules = Array.from(styleSheet.cssRules).map(r => r.cssText).join(""); style.textContent = rules; this.pipWindow.document.head.appendChild(style); } catch { if (styleSheet.href) { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = styleSheet.href; this.pipWindow.document.head.appendChild(link); } } } this.pipWindow.document.body.appendChild(this.el); // Add keydown listener inside PiP window this.pipWindow.addEventListener("keydown", (event) => { // Ignore if focused on input or textarea in PiP window const active = this.pipWindow.document.activeElement; if (active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA")) return; if (event.code === "Space") { event.preventDefault(); const max = parseInt(inputStitchMax.value); if (inputAuto.checked && stitches.count + 1 >= max) { rows.increase(); stitches.set(0); } else { stitches.increase(); } } }); this.pipWindow.addEventListener("pagehide", () => this.stop()); } stop = () => { this.container.appendChild(this.el); this.pipWindow = null; } } new PipManager( document.getElementById("div-pip-element"), document.getElementById("div-pip-container"), document.getElementById("button-start-pip") ); const notesField = document.getElementById("notes"); notesField.value = localStorage.getItem("notes") || ""; notesField.addEventListener("input", () => { localStorage.setItem("notes", notesField.value); }); window.addEventListener("keydown", (event) => { // Ignore if focused on input or textarea, so typing space doesn't increment stitches accidentally const active = document.activeElement; if (active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA")) return; if (event.code === "Space") { event.preventDefault(); // prevent page scroll on space key const max = parseInt(inputStitchMax.value); if (inputAuto.checked && stitches.count + 1 >= max) { rows.increase(); stitches.set(0); } else { stitches.increase(); } } });