/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2024 metamuffin */ /// import { e } from "./jshelper/src/element.ts"; const DURATION = 200 globalThis.addEventListener("DOMContentLoaded", () => { patch_page() }) globalThis.addEventListener("popstate", async (_e) => { await transition_to(window.location.href, true) }) let disable_transition = false globalThis.addEventListener("navigationrequiresreload", () => { disable_transition = true }) function patch_page() { document.querySelectorAll("a").forEach(el => { if (!el.href.startsWith("http")) return el.addEventListener("click", async ev => { ev.preventDefault() await transition_to(el.href) }) }) } async function transition_to(href: string, back?: boolean) { stop() // nice! if (disable_transition) return window.location.href = href const trigger_load = prepare_load(href, back) await fade(false) trigger_load() disable_transition = false; } function show_message(mesg: string, mode: "error" | "success" = "error") { clear_spinner() disable_transition = true document.body.append(e("span", { class: ["jst-message", mode] }, mesg)) } function prepare_load(href: string, back?: boolean) { const r_promise = fetch(href, { headers: { accept: "text/html" }, redirect: "manual" }) return async () => { let rt = "" try { const r = await r_promise if (r.type == "opaqueredirect") { window.location.href = href show_message("Native Player Started.", "success") setTimeout(() => window.location.reload(), 500) return } if (!r.ok) return show_message("Error response. Try again.") rt = await r.text() } catch (e) { if (e instanceof TypeError) return show_message("Navigation failed. Check your connection.") return show_message("unknown error when fetching page") } const [head, body] = rt.split("")[1].split("") if (!back) window.history.pushState({}, "", href) clear_spinner() document.head.innerHTML = head document.body.outerHTML = body globalThis.dispatchEvent(new Event("DOMContentLoaded")) globalThis.scrollTo({ top: 0 }); fade(true) } } let spinner_timeout: number | undefined, spinner_element: HTMLElement | undefined; function clear_spinner() { if (spinner_timeout) clearTimeout(spinner_timeout) if (spinner_element) spinner_element.remove() spinner_element = spinner_timeout = undefined; } function fade(dir: boolean) { const overlay = document.createElement("div") overlay.classList.add("jst-fade") overlay.style.backgroundColor = dir ? "black" : "transparent" overlay.style.animationName = dir ? "jst-fadeout" : "jst-fadein" overlay.style.animationFillMode = "forwards" overlay.style.animationDuration = `${DURATION}ms` document.body.appendChild(overlay) return new Promise(res => { setTimeout(() => { if (dir) document.body.removeChild(overlay) spinner_timeout = setTimeout(() => { overlay.append(spinner_element = e("div", { class: "jst-spinner" }, "This is a spinner.")) }, 500) res() }, DURATION) }) }