// retoor export class ScrollMemory { static KEY = "molodetz-scroll"; static MAX_ENTRIES = 50; static MAX_AGE_MS = 60 * 60 * 1000; constructor() { history.scrollRestoration = "manual"; this.key = location.pathname + location.search; window.addEventListener("pagehide", () => this.save()); const navigation = performance.getEntriesByType("navigation")[0]; const type = navigation ? navigation.type : "navigate"; if (type === "back_forward" || type === "reload" || sessionStorage.getItem("molodetz-restore") === this.key) { sessionStorage.removeItem("molodetz-restore"); this.restore(); } document.addEventListener("click", (event) => { const link = event.target.closest(".breadcrumbs a, a[data-back]"); if (link) { sessionStorage.setItem("molodetz-restore", new URL(link.href).pathname); } }); } read() { try { return JSON.parse(sessionStorage.getItem(ScrollMemory.KEY) || "{}"); } catch { return {}; } } save() { const entries = this.read(); entries[this.key] = { y: window.scrollY, at: Date.now() }; const now = Date.now(); const kept = Object.entries(entries) .filter(([, value]) => now - value.at < ScrollMemory.MAX_AGE_MS) .sort((a, b) => b[1].at - a[1].at) .slice(0, ScrollMemory.MAX_ENTRIES); sessionStorage.setItem(ScrollMemory.KEY, JSON.stringify(Object.fromEntries(kept))); } restore() { const entry = this.read()[this.key]; if (!entry) { return; } const deadline = performance.now() + 1500; let aborted = false; const abort = () => { aborted = true; }; ["wheel", "touchstart", "keydown"].forEach((name) => window.addEventListener(name, abort, { once: true, passive: true })); const apply = () => { if (aborted) { return; } window.scrollTo(0, entry.y); if (Math.abs(window.scrollY - entry.y) > 2 && performance.now() < deadline) { requestAnimationFrame(apply); } }; requestAnimationFrame(apply); } }