From af5c542b3ff6cbbdeec6da1b1a03e58b007fd941 Mon Sep 17 00:00:00 2001 From: tdro Date: Sat, 16 Mar 2024 15:22:06 -0400 Subject: static/js/instantpage: Add custom prefetch --- static/js/instantpage.ts | 248 +++++++++++++++-------------------------------- 1 file changed, 78 insertions(+), 170 deletions(-) (limited to 'static') diff --git a/static/js/instantpage.ts b/static/js/instantpage.ts index aa290cd..b76cef8 100644 --- a/static/js/instantpage.ts +++ b/static/js/instantpage.ts @@ -5,24 +5,38 @@ */ (function () { + + 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; + } + self.addEventListener("DOMContentLoaded", function () { - 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 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; + let mouseoverTimer; + let lastTouchTimestamp; let delayOnHover = 65; let useMousedown = false; let useMousedownOnly = false; @@ -33,86 +47,48 @@ if (intensity.substr(0, "mousedown".length) == "mousedown") { useMousedown = true; - if (intensity == "mousedown-only") { - useMousedownOnly = 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 (!(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; - } + /* + 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; - } + if (!isNaN(milliseconds)) { delayOnHover = milliseconds; } } } if (isSupported) { - const eventListenersOptions = { - capture: true, - passive: true, - }; - - if (!useMousedownOnly) { - document.addEventListener( - "touchstart", - touchstartListener, - eventListenersOptions, - ); - } + const eventListenersOptions = { capture: true, passive: true }; + + if (!useMousedownOnly) { document.addEventListener( "touchstart", touchstartListener, eventListenersOptions,); } if (!useMousedown) { - document.addEventListener( - "mouseover", - mouseoverListener, - eventListenersOptions, - ); + document.addEventListener( "mouseover", mouseoverListener, eventListenersOptions,); } else if (!mousedownShortcut) { - document.addEventListener( - "mousedown", - mousedownListener, - eventListenersOptions, - ); + document.addEventListener( "mousedown", mousedownListener, eventListenersOptions,); } - if (mousedownShortcut) { - document.addEventListener( - "mousedown", - mousedownShortcutListener, - eventListenersOptions, - ); - } + if (mousedownShortcut) { document.addEventListener( "mousedown", mousedownShortcutListener, eventListenersOptions,); } if (useViewport) { let triggeringFunction; if (window.requestIdleCallback) { - triggeringFunction = function (callback) { - requestIdleCallback(callback, { - timeout: 1500, - }); - }; + triggeringFunction = function (callback) { requestIdleCallback(callback, { timeout: 1500 }); } } else { - triggeringFunction = function (callback) { - callback(); - }; + triggeringFunction = function (callback) { callback(); } } triggeringFunction(function () { @@ -129,45 +105,29 @@ ); document.querySelectorAll("a").forEach(function (linkElement) { - if (isPreloadable(linkElement)) { - intersectionObserver.observe(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. */ + /* + 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; - } - + if (!isPreloadable(linkElement)) { return; } preload(linkElement.href); } function mouseoverListener(event) { - if ( - performance.now() - lastTouchTimestamp < - DELAY_TO_NOT_BE_CONSIDERED_A_TOUCH_INITIATED_ACTION - ) { - return; - } + if (performance.now() - lastTouchTimestamp < DELAY_TO_NOT_BE_CONSIDERED_A_TOUCH_INITIATED_ACTION) { return; } const linkElement = event.target.closest("a"); - - if (!isPreloadable(linkElement)) { - return; - } - - linkElement.addEventListener("mouseout", mouseoutListener, { - passive: true, - }); + if (!isPreloadable(linkElement)) { return; } + linkElement.addEventListener("mouseout", mouseoutListener, { passive: true }); mouseoverTimer = setTimeout(function () { preload(linkElement.href); @@ -177,51 +137,26 @@ function mousedownListener(event) { const linkElement = event.target.closest("a"); - - if (!isPreloadable(linkElement)) { - return; - } - + 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; - } + 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; - } + 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 (event.which > 1 || event.metaKey || event.ctrlKey) { return; } - if (!linkElement) { - return; - } + if (!linkElement) { return; } linkElement.addEventListener("click", function (event) { - if (event.detail == 1337) { - return; - } - + if (event.detail == 1337) { return; } event.preventDefault(); }, { capture: true, passive: false, once: true }); @@ -231,66 +166,39 @@ cancelable: false, detail: 1337, }); + linkElement.dispatchEvent(customEvent); } function isPreloadable(linkElement) { - if (!linkElement || !linkElement.href) { - return; - } + if (!linkElement || !linkElement.href) { return; } - if (useWhitelist && !("instant" in linkElement.dataset)) { - return; - } + if (useWhitelist && !("instant" in linkElement.dataset)) { return; } - if ( - !allowExternalLinks && linkElement.origin != location.origin && - !("instant" in linkElement.dataset) - ) { - return; - } + if (!allowExternalLinks && linkElement.origin != location.origin && !("instant" in linkElement.dataset)) { return; } - if (!["http:", "https:"].includes(linkElement.protocol)) { - return; - } + if (!["http:", "https:"].includes(linkElement.protocol)) { return; } - if (linkElement.protocol == "http:" && location.protocol == "https:") { - return; - } + if (linkElement.protocol == "http:" && location.protocol == "https:") { return; } - if ( - !allowQueryString && linkElement.search && - !("instant" in linkElement.dataset) - ) { - return; - } + if (!allowQueryString && linkElement.search && !("instant" in linkElement.dataset)) { return; } - if ( - linkElement.hash && - linkElement.pathname + linkElement.search == - location.pathname + location.search - ) { - return; - } + if (linkElement.hash && linkElement.pathname + linkElement.search == location.pathname + location.search) { return; } - if ("noInstant" in linkElement.dataset) { - return; - } + if ("noInstant" in linkElement.dataset) { return; } return true; } function preload(url) { - if (prefetches.has(url)) { - return; - } - + if (prefetches.has(url)) { return; } const prefetcher = document.createElement("link"); - prefetcher.rel = "prefetch"; + prefetcher.rel = "custom-prefetch"; prefetcher.href = url; - document.head.appendChild(prefetcher); - - prefetches.add(url); + fetch(url, "GET", function () { + document.head.appendChild(prefetcher); + prefetches.add(url); + }); } }); })(); -- cgit v1.2.3