diff options
author | tdro <tdro@noreply.example.com> | 2024-02-12 20:15:17 -0500 |
---|---|---|
committer | tdro <tdro@noreply.example.com> | 2024-02-12 20:15:17 -0500 |
commit | d23d5bc6dae8035357f1dea088392ab3d2546f8e (patch) | |
tree | b050a87ebf4f073aa2bc8c5b08c2735039994dc0 /assets | |
parent | b541593793efc19639835200f185d273620770e6 (diff) | |
download | canory-d23d5bc6dae8035357f1dea088392ab3d2546f8e.tar.gz canory-d23d5bc6dae8035357f1dea088392ab3d2546f8e.tar.bz2 canory-d23d5bc6dae8035357f1dea088392ab3d2546f8e.zip |
static/js: Add custom DOM filter
Fallback to native navigation if transition in 300ms is not guaranteed
Diffstat (limited to 'assets')
-rw-r--r-- | assets/js/index.js | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/assets/js/index.js b/assets/js/index.js index 91d3e61..15f720a 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -752,6 +752,108 @@ }); })(); (function() { + const timeout = 300; + const state = "on"; + const key = "config.speed.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])) {} 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) { + filter(url.href, http); + 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 i1 = 0; i1 < links.length; i1++){ + const active = links[i1].contains(event.target); + const change = self.location.href !== links[i1].href; + const view = links[i1].attributes.hasOwnProperty("download") === false; + const local = self.location.origin === links[i1].origin && links[i1].target !== "_self"; + const hashed = self.location.pathname === links[i1].pathname && links[i1].href.includes("#"); + if (active && local && change && view && hashed === false) { + event.preventDefault(); + const url = links[i1].href; + links[i1].style.cursor = "wait"; + fetch(url, "GET", function(http) { + links[i1].removeAttribute("style"); + if (filter(url, http) === false) return self.location.href = url; + history.pushState({}, "", links[i1].href); + persist(); + self.document.dispatchEvent(new CustomEvent("URLChangedCustomEvent", { + bubbles: true + })); + }); + } + } + }); +})(); +(function() { const relative = new Intl.RelativeTimeFormat("en", { localeMatcher: "best fit", numeric: "always", |