/* * DOM Filter Copyright (C) 2024 Thedro Neely * Licence: AGPL | https://www.gnu.org/licenses/agpl-3.0.txt */ (function () { type millisecond = number; const timeout: millisecond = 300; const state = "on"; const key = "config.navigation.fast"; function fetch(url, method, callback) { const http = new XMLHttpRequest(); http.onreadystatechange = function () { if (callback && http.readyState === 4) { if (http.status === 200) callback(http); else { console.error("ERROR: Unable to navigate", http); self.location.href = url; } } }; http.open(method, url); http.timeout = timeout; http.send(); return http; } function styles(node) { return (node.nodeName === "LINK") && node.rel && node.rel.includes("stylesheet"); } function scripts(node) { return (node.nodeName === "SCRIPT") && node.hasAttribute("src"); } function filter(url, http) { let live = document; let node = live.head.childNodes.length; let next = (new DOMParser()).parseFromString(http.responseText, "text/html"); if (next.doctype === null || !http.getResponseHeader("content-type").includes("text/html")) return false; while (node--) { if (styles(live.head.childNodes[node]) || scripts(live.head.childNodes[node])) { // console.log("INFO: Persist:", live.head.childNodes[node]); } else { live.head.removeChild(live.head.childNodes[node]); } } while (next.head.firstChild) { if (styles(next.head.firstChild) || scripts(next.head.firstChild)) { next.head.removeChild(next.head.firstChild); } else { live.head.appendChild(next.head.firstChild); } } while (live.documentElement.attributes.length > 0) { live.documentElement.removeAttribute( live.documentElement.attributes[live.documentElement.attributes.length - 1].name, ); } while (next.documentElement.attributes.length > 0) { live.documentElement.setAttribute( next.documentElement.attributes[next.documentElement.attributes.length - 1].name, next.documentElement.attributes[next.documentElement.attributes.length - 1].value, ); next.documentElement.removeAttribute( next.documentElement.attributes[next.documentElement.attributes.length - 1].name, ); } live.body.parentElement.replaceChild(next.body, live.body); } function persist() { let persist = document.createElement("link"); persist.rel = "location"; persist.target = JSON.stringify(self.location); document.head.appendChild(persist); } self.addEventListener("DOMContentLoaded", function () { if (localStorage[key] !== state) return; persist(); }); self.addEventListener("popstate", function (event) { if (localStorage[key] !== state) return; const link = JSON.parse(document.querySelector('link[rel="location"]').target); const url = event.target.location; const hashed = link.pathname === url.pathname; if (hashed) return; fetch(url, "GET", function (http) { if (filter(url, http) === false) return self.location.href = url; persist(); self.document.dispatchEvent(new CustomEvent("URLChangedCustomEvent", { bubbles: true })); }); }); self.addEventListener("click", function (event) { if (localStorage[key] !== state) return; const links = document.querySelectorAll("a"); for (let i = 0; i < links.length; i++) { const active = links[i].contains(event.target); const change = self.location.href !== links[i].href; const view = links[i].attributes.hasOwnProperty("download") === false; const local = self.location.origin === links[i].origin && links[i].target !== "_self"; const hashed = self.location.pathname === links[i].pathname && links[i].href.includes("#"); if (active && local && change && view && (hashed === false)) { event.preventDefault(); const url = links[i].href; links[i].style.cursor = "wait"; fetch(url, "GET", function (http) { links[i].removeAttribute("style") if (filter(url, http) === false) return self.location.href = url; history.pushState({}, "", links[i].href); persist(); self.document.dispatchEvent(new CustomEvent("URLChangedCustomEvent", { bubbles: true })); }); } } }); })(); /* * Copyright (C) 2024 Thedro Neely * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */