/*! instant.page v2.0.0 - (C) 2019 Alexandre Dieulot - https://instant.page/license */ let urlToPreload let mouseoverTimer let lastTouchTimestamp const prefetcher = document.createElement('link') const isSupported = prefetcher.relList && prefetcher.relList.supports && prefetcher.relList.supports('prefetch') const isDataSaverEnabled = navigator.connection && navigator.connection.saveData const allowQueryString = 'instantAllowQueryString' in document.body.dataset const allowExternalLinks = 'instantAllowExternalLinks' in document.body.dataset const useWhitelist = 'instantWhitelist' in document.body.dataset let delayOnHover = 65 let useMousedown = false let useMousedownOnly = false if ('instantIntensity' in document.body.dataset) { if (document.body.dataset.instantIntensity.substr(0, 'mousedown'.length) == 'mousedown') { useMousedown = true if (document.body.dataset.instantIntensity == 'mousedown-only') { useMousedownOnly = true } } else { const milliseconds = parseInt(document.body.dataset.instantIntensity) if (!isNaN(milliseconds)) { delayOnHover = milliseconds } } } if (isSupported && !isDataSaverEnabled) { prefetcher.rel = 'prefetch' document.head.appendChild(prefetcher) const eventListenersOptions = { capture: true, passive: true, } if (!useMousedownOnly) { document.addEventListener('touchstart', touchstartListener, eventListenersOptions) } if (!useMousedown) { document.addEventListener('mouseover', mouseoverListener, eventListenersOptions) } else { document.addEventListener('mousedown', mousedownListener, eventListenersOptions) } } 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 } linkElement.addEventListener('touchcancel', touchendAndTouchcancelListener, { passive: true }) linkElement.addEventListener('touchend', touchendAndTouchcancelListener, { passive: true }) urlToPreload = linkElement.href preload(linkElement.href) } function touchendAndTouchcancelListener() { urlToPreload = undefined stopPreloading() } function mouseoverListener(event) { if (performance.now() - lastTouchTimestamp < 1100) { return } const linkElement = event.target.closest('a') if (!isPreloadable(linkElement)) { return } linkElement.addEventListener('mouseout', mouseoutListener, { passive: true }) urlToPreload = linkElement.href mouseoverTimer = setTimeout(() => { preload(linkElement.href) mouseoverTimer = undefined }, delayOnHover) } function mousedownListener(event) { const linkElement = event.target.closest('a') if (!isPreloadable(linkElement)) { return } linkElement.addEventListener('mouseout', mouseoutListener, { passive: true }) urlToPreload = linkElement.href preload(linkElement.href) } function mouseoutListener(event) { if (event.relatedTarget && event.target.closest('a') == event.relatedTarget.closest('a')) { return } if (mouseoverTimer) { clearTimeout(mouseoverTimer) mouseoverTimer = undefined } urlToPreload = undefined stopPreloading() } function isPreloadable(linkElement) { if (!linkElement || !linkElement.href) { return } if (urlToPreload == linkElement.href) { return } if (useWhitelist && !('instant' in linkElement.dataset)) { return } 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) { prefetcher.href = url } function stopPreloading() { prefetcher.removeAttribute('href') }