1/*! instant.page v3.0.0 - (C) 2019-2020 Alexandre Dieulot - https://instant.page/license */ 2 3let mouseoverTimer 4let lastTouchTimestamp 5const prefetches = new Set() 6const prefetchElement = document.createElement('link') 7const isSupported = prefetchElement.relList && prefetchElement.relList.supports && prefetchElement.relList.supports('prefetch') && window.IntersectionObserver && 'isIntersecting' in IntersectionObserverEntry.prototype 8const allowQueryString = 'instantAllowQueryString' in document.body.dataset 9const allowExternalLinks = 'instantAllowExternalLinks' in document.body.dataset 10const useWhitelist = 'instantWhitelist' in document.body.dataset 11 12let delayOnHover = 65 13let useMousedown = false 14let useMousedownOnly = false 15let useViewport = false 16if ('instantIntensity' in document.body.dataset) { 17 const intensity = document.body.dataset.instantIntensity 18 19 if (intensity.substr(0, 'mousedown'.length) == 'mousedown') { 20 useMousedown = true 21 if (intensity == 'mousedown-only') { 22 useMousedownOnly = true 23 } 24 } 25 else if (intensity.substr(0, 'viewport'.length) == 'viewport') { 26 if (!(navigator.connection && (navigator.connection.saveData || (navigator.connection.effectiveType && navigator.connection.effectiveType.includes('2g'))))) { 27 if (intensity == "viewport") { 28 /* Biggest iPhone resolution (which we want): 414 Ã 896 = 370944 29 * Small 7" tablet resolution (which we donât want): 600 Ã 1024 = 614400 30 * Note that the viewport (which we check here) is smaller than the resolution due to the UIâs chrome */ 31 if (document.documentElement.clientWidth * document.documentElement.clientHeight < 450000) { 32 useViewport = true 33 } 34 } 35 else if (intensity == "viewport-all") { 36 useViewport = true 37 } 38 } 39 } 40 else { 41 const milliseconds = parseInt(intensity) 42 if (!isNaN(milliseconds)) { 43 delayOnHover = milliseconds 44 } 45 } 46} 47 48if (isSupported) { 49 const eventListenersOptions = { 50 capture: true, 51 passive: true, 52 } 53 54 if (!useMousedownOnly) { 55 document.addEventListener('touchstart', touchstartListener, eventListenersOptions) 56 } 57 58 if (!useMousedown) { 59 document.addEventListener('mouseover', mouseoverListener, eventListenersOptions) 60 } 61 else { 62 document.addEventListener('mousedown', mousedownListener, eventListenersOptions) 63 } 64 65 if (useViewport) { 66 let triggeringFunction 67 if (window.requestIdleCallback) { 68 triggeringFunction = (callback) => { 69 requestIdleCallback(callback, { 70 timeout: 1500, 71 }) 72 } 73 } 74 else { 75 triggeringFunction = (callback) => { 76 callback() 77 } 78 } 79 80 triggeringFunction(() => { 81 const intersectionObserver = new IntersectionObserver((entries) => { 82 entries.forEach((entry) => { 83 if (entry.isIntersecting) { 84 const linkElement = entry.target 85 intersectionObserver.unobserve(linkElement) 86 preload(linkElement.href) 87 } 88 }) 89 }) 90 91 document.querySelectorAll('a').forEach((linkElement) => { 92 if (isPreloadable(linkElement)) { 93 intersectionObserver.observe(linkElement) 94 } 95 }) 96 }) 97 } 98} 99 100function touchstartListener(event) { 101 /* Chrome on Android calls mouseover before touchcancel so `lastTouchTimestamp` 102 * must be assigned on touchstart to be measured on mouseover. */ 103 lastTouchTimestamp = performance.now() 104 105 const linkElement = event.target.closest('a') 106 107 if (!isPreloadable(linkElement)) { 108 return 109 } 110 111 preload(linkElement.href) 112} 113 114function mouseoverListener(event) { 115 if (performance.now() - lastTouchTimestamp < 1100) { 116 return 117 } 118 119 const linkElement = event.target.closest('a') 120 121 if (!isPreloadable(linkElement)) { 122 return 123 } 124 125 linkElement.addEventListener('mouseout', mouseoutListener, {passive: true}) 126 127 mouseoverTimer = setTimeout(() => { 128 preload(linkElement.href) 129 mouseoverTimer = undefined 130 }, delayOnHover) 131} 132 133function mousedownListener(event) { 134 const linkElement = event.target.closest('a') 135 136 if (!isPreloadable(linkElement)) { 137 return 138 } 139 140 preload(linkElement.href) 141} 142 143function mouseoutListener(event) { 144 if (event.relatedTarget && event.target.closest('a') == event.relatedTarget.closest('a')) { 145 return 146 } 147 148 if (mouseoverTimer) { 149 clearTimeout(mouseoverTimer) 150 mouseoverTimer = undefined 151 } 152} 153 154function isPreloadable(linkElement) { 155 if (!linkElement || !linkElement.href) { 156 return 157 } 158
159 if (useWhitelist && !('instant' in linkElement.dataset)) { 160 return 161 } 162 163 if (!allowExternalLinks && linkElement.origin != location.origin && !('instant' in linkElement.dataset)) { 164 return 165 } 166 167 if (!['http:', 'https:'].includes(linkElement.protocol)) { 168 return 169 } 170 171 if (linkElement.protocol == 'http:' && location.protocol == 'https:') { 172 return 173 } 174 175 if (!allowQueryString && linkElement.search && !('instant' in linkElement.dataset)) { 176 return 177 } 178 179 if (linkElement.hash && linkElement.pathname + linkElement.search == location.pathname + location.search) { 180 return 181 } 182 183 if ('noInstant' in linkElement.dataset) { 184 return 185 } 186 187 return true 188} 189 190function preload(url) { 191 if (prefetches.has(url)) { 192 return 193 } 194 195 const prefetcher = document.createElement('link') 196 prefetcher.rel = 'prefetch' 197 prefetcher.href = url 198 document.head.appendChild(prefetcher) 199 200 prefetches.add(url) 201}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.