diff options
Diffstat (limited to 'static/js/instantpage.ts')
-rw-r--r-- | static/js/instantpage.ts | 330 |
1 files changed, 60 insertions, 270 deletions
diff --git a/static/js/instantpage.ts b/static/js/instantpage.ts index 7c78a6f..1b5ed36 100644 --- a/static/js/instantpage.ts +++ b/static/js/instantpage.ts @@ -1,292 +1,82 @@ -(function () { - self.addEventListener("DOMContentLoaded", function () { - /*! instant.page v5.1.0 - (C) 2019-2020 Alexandre Dieulot - https://instant.page/license */ - - let mouseoverTimer; - let lastTouchTimestamp; - const prefetches = new Set(); - const prefetchElement = document.createElement("link"); - const isSupported = prefetchElement.relList && - prefetchElement.relList.supports && - prefetchElement.relList.supports("prefetch") && - window.IntersectionObserver && - "isIntersecting" in IntersectionObserverEntry.prototype; - const allowQueryString = "instantAllowQueryString" in document.body.dataset; - const allowExternalLinks = "instantAllowExternalLinks" in - document.body.dataset; - const useWhitelist = "instantWhitelist" in document.body.dataset; - const mousedownShortcut = "instantMousedownShortcut" in - document.body.dataset; - const DELAY_TO_NOT_BE_CONSIDERED_A_TOUCH_INITIATED_ACTION = 1111; +/* + * Instant Page Copyright (C) 2019-2023 Alexandre Dieulot + * https://github.com/instantpage/instant.page/blob/master/LICENSE + * Licence: MIT | https://mit-license.org/ +*/ - let delayOnHover = 65; - let useMousedown = false; - let useMousedownOnly = false; - let useViewport = false; - - if ("instantIntensity" in document.body.dataset) { - const intensity = document.body.dataset.instantIntensity; +(function () { - if (intensity.substr(0, "mousedown".length) == "mousedown") { - useMousedown = true; - if (intensity == "mousedown-only") { - useMousedownOnly = true; - } - } else if (intensity.substr(0, "viewport".length) == "viewport") { - if ( - !(navigator.connection && - (navigator.connection.saveData || - (navigator.connection.effectiveType && - navigator.connection.effectiveType.includes("2g")))) - ) { - if (intensity == "viewport") { - /* Biggest iPhone resolution (which we want): 414 × 896 = 370944 - * Small 7" tablet resolution (which we don’t want): 600 × 1024 = 614400 - * Note that the viewport (which we check here) is smaller than the resolution due to the UI’s chrome */ - if ( - document.documentElement.clientWidth * - document.documentElement.clientHeight < 450000 - ) { - useViewport = true; - } - } else if (intensity == "viewport-all") { - useViewport = true; - } - } - } else { - const milliseconds = parseInt(intensity); - if (!isNaN(milliseconds)) { - delayOnHover = milliseconds; - } - } + /*/ + * Element.closest() polyfill + * https://developer.mozilla.org/en-US/docs/Web/API/Element/closest + * https://github.com/idmadj/element-closest-polyfill#readme + * Copyright (C) Abdelmadjid Hammou + * Licence: ISC | https://www.isc.org/licenses/ + /*/ + + if (typeof Element !== "undefined") { + if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.msMatchesSelector || + Element.prototype.webkitMatchesSelector; } - - if (isSupported) { - const eventListenersOptions = { - capture: true, - passive: true, + if (!Element.prototype.closest) { + Element.prototype.closest = function (s) { + var el = this; + do { + if (el.matches(s)) return el; + el = el.parentElement || el.parentNode; + } while (el !== null && el.nodeType === 1); + return null; }; - - if (!useMousedownOnly) { - document.addEventListener( - "touchstart", - touchstartListener, - eventListenersOptions, - ); - } - - if (!useMousedown) { - document.addEventListener( - "mouseover", - mouseoverListener, - eventListenersOptions, - ); - } else if (!mousedownShortcut) { - document.addEventListener( - "mousedown", - mousedownListener, - eventListenersOptions, - ); - } - - if (mousedownShortcut) { - document.addEventListener( - "mousedown", - mousedownShortcutListener, - eventListenersOptions, - ); - } - - if (useViewport) { - let triggeringFunction; - if (window.requestIdleCallback) { - triggeringFunction = function (callback) { - requestIdleCallback(callback, { - timeout: 1500, - }); - }; - } else { - triggeringFunction = function (callback) { - callback(); - }; - } - - triggeringFunction(function () { - const intersectionObserver = new IntersectionObserver( - function (entries) { - entries.forEach(function (entry) { - if (entry.isIntersecting) { - const linkElement = entry.target; - intersectionObserver.unobserve(linkElement); - preload(linkElement.href); - } - }); - }, - ); - - document.querySelectorAll("a").forEach(function (linkElement) { - if (isPreloadable(linkElement)) { - intersectionObserver.observe(linkElement); - } - }); - }); - } } + } - function touchstartListener(event) { - /* Chrome on Android calls mouseover before touchcancel so `lastTouchTimestamp` - * must be assigned on touchstart to be measured on mouseover. */ - lastTouchTimestamp = performance.now(); + /*/ + * ----------------------------------------------------------------- + /*/ - const linkElement = event.target.closest("a"); - - if (!isPreloadable(linkElement)) { - return; - } - - preload(linkElement.href); - } - - function mouseoverListener(event) { - if ( - performance.now() - lastTouchTimestamp < - DELAY_TO_NOT_BE_CONSIDERED_A_TOUCH_INITIATED_ACTION - ) { - return; + function fetch(url, method, callback) { + const http = new XMLHttpRequest(); + http.onreadystatechange = function () { + if (http.readyState === 4 && http.status === 200) { + if (callback) callback(http); } + }; + http.open(method, url); + http.send(); + return http; + } - const linkElement = event.target.closest("a"); - - if (!isPreloadable(linkElement)) { - return; - } - - linkElement.addEventListener("mouseout", mouseoutListener, { - passive: true, - }); - - mouseoverTimer = setTimeout(function () { - preload(linkElement.href); - mouseoverTimer = undefined; - }, delayOnHover); - } - - function mousedownListener(event) { - const linkElement = event.target.closest("a"); - - if (!isPreloadable(linkElement)) { - return; - } - - preload(linkElement.href); - } - - function mouseoutListener(event) { - if ( - event.relatedTarget && - event.target.closest("a") == event.relatedTarget.closest("a") - ) { - return; - } - - if (mouseoverTimer) { - clearTimeout(mouseoverTimer); - mouseoverTimer = undefined; - } - } - - function mousedownShortcutListener(event) { - if ( - performance.now() - lastTouchTimestamp < - DELAY_TO_NOT_BE_CONSIDERED_A_TOUCH_INITIATED_ACTION - ) { - return; - } - - const linkElement = event.target.closest("a"); - - if (event.which > 1 || event.metaKey || event.ctrlKey) { - return; - } - - if (!linkElement) { - return; - } - - linkElement.addEventListener("click", function (event) { - if (event.detail == 1337) { - return; - } - - event.preventDefault(); - }, { capture: true, passive: false, once: true }); + self.addEventListener("DOMContentLoaded", function () { - const customEvent = new MouseEvent("click", { - view: window, - bubbles: true, - cancelable: false, - detail: 1337, + ["mouseout", "mousedown", "touchstart"].forEach(function (event) { + self.addEventListener(event, function (event) { + const url = event.target.closest("a"); + if (preloadable(url) === undefined) return; + preload(url.href); }); - linkElement.dispatchEvent(customEvent); - } - - function isPreloadable(linkElement) { - if (!linkElement || !linkElement.href) { - return; - } + }); - if (useWhitelist && !("instant" in linkElement.dataset)) { - return; + function preloadable(url) { + switch (true) { + case (url === null || url.href === null): return; + case (url.origin !== location.origin): return; + case (["http:", "https:"].includes(url.protocol) === null): return; + case (url.protocol === "http:" && location.protocol === "https:"): return; + case (url.hash && url.pathname + url.search == location.pathname + location.search): return; + default: return true; } - - if ( - !allowExternalLinks && linkElement.origin != location.origin && - !("instant" in linkElement.dataset) - ) { - return; - } - - if (!["http:", "https:"].includes(linkElement.protocol)) { - return; - } - - if (linkElement.protocol == "http:" && location.protocol == "https:") { - return; - } - - if ( - !allowQueryString && linkElement.search && - !("instant" in linkElement.dataset) - ) { - return; - } - - if ( - linkElement.hash && - linkElement.pathname + linkElement.search == - location.pathname + location.search - ) { - return; - } - - if ("noInstant" in linkElement.dataset) { - return; - } - - return true; } function preload(url) { - if (prefetches.has(url)) { - return; - } - const prefetcher = document.createElement("link"); - prefetcher.rel = "prefetch"; + prefetcher.rel = "custom-prefetch"; prefetcher.href = url; + const selector = 'link[rel="'.concat(prefetcher.rel, '"][href="').concat(prefetcher.href, '"]'); + const prefetched = document.head.contains(document.querySelector(selector)); + if (prefetched) { return; } document.head.appendChild(prefetcher); - - prefetches.add(url); + fetch(url, "GET", function () {}); } }); })(); |