1/*! instant.page v2.0.0 - (C) 2019 Alexandre Dieulot - https://instant.page/license */ 2 3let urlToPreload 4let mouseoverTimer 5let lastTouchTimestamp 6 7const prefetcher = document.createElement('link') 8const isSupported = prefetcher.relList && prefetcher.relList.supports && prefetcher.relList.supports('prefetch') 9const isDataSaverEnabled = navigator.connection && navigator.connection.saveData 10const allowQueryString = 'instantAllowQueryString' in document.body.dataset 11const allowExternalLinks = 'instantAllowExternalLinks' in document.body.dataset 12const useWhitelist = 'instantWhitelist' in document.body.dataset 13 14let delayOnHover = 65 15let useMousedown = false 16let useMousedownOnly = false 17if ('instantIntensity' in document.body.dataset) { 18 if (document.body.dataset.instantIntensity.substr(0, 'mousedown'.length) == 'mousedown') { 19 useMousedown = true 20 if (document.body.dataset.instantIntensity == 'mousedown-only') { 21 useMousedownOnly = true 22 } 23 } else { 24 const milliseconds = parseInt(document.body.dataset.instantIntensity) 25 if (!isNaN(milliseconds)) { 26 delayOnHover = milliseconds 27 } 28 } 29} 30 31if (isSupported && !isDataSaverEnabled) { 32 prefetcher.rel = 'prefetch' 33 document.head.appendChild(prefetcher) 34 35 const eventListenersOptions = { 36 capture: true, 37 passive: true, 38 } 39 40 if (!useMousedownOnly) { 41 document.addEventListener('touchstart', touchstartListener, eventListenersOptions) 42 } 43 44 if (!useMousedown) { 45 document.addEventListener('mouseover', mouseoverListener, eventListenersOptions) 46 } else { 47 document.addEventListener('mousedown', mousedownListener, eventListenersOptions) 48 } 49} 50 51function touchstartListener(event) { 52 /* Chrome on Android calls mouseover before touchcancel so `lastTouchTimestamp` 53 * must be assigned on touchstart to be measured on mouseover. */ 54 lastTouchTimestamp = performance.now() 55 56 const linkElement = event.target.closest('a') 57 58 if (!isPreloadable(linkElement)) { 59 return 60 } 61 62 linkElement.addEventListener('touchcancel', touchendAndTouchcancelListener, { 63 passive: true 64 }) 65 linkElement.addEventListener('touchend', touchendAndTouchcancelListener, { 66 passive: true 67 }) 68 69 urlToPreload = linkElement.href 70 preload(linkElement.href) 71} 72 73function touchendAndTouchcancelListener() { 74 urlToPreload = undefined 75 stopPreloading() 76} 77 78function mouseoverListener(event) { 79 if (performance.now() - lastTouchTimestamp < 1100) { 80 return 81 } 82 83 const linkElement = event.target.closest('a') 84 85 if (!isPreloadable(linkElement)) { 86 return 87 } 88 89 linkElement.addEventListener('mouseout', mouseoutListener, { 90 passive: true 91 }) 92 93 urlToPreload = linkElement.href 94 95 mouseoverTimer = setTimeout(() => { 96 preload(linkElement.href) 97 mouseoverTimer = undefined 98 }, delayOnHover) 99} 100 101function mousedownListener(event) { 102 const linkElement = event.target.closest('a') 103 104 if (!isPreloadable(linkElement)) { 105 return 106 } 107 108 linkElement.addEventListener('mouseout', mouseoutListener, { 109 passive: true 110 }) 111 112 urlToPreload = linkElement.href 113 114 preload(linkElement.href) 115} 116 117function mouseoutListener(event) { 118 if (event.relatedTarget && event.target.closest('a') == event.relatedTarget.closest('a')) { 119 return 120 } 121 122 if (mouseoverTimer) { 123 clearTimeout(mouseoverTimer) 124 mouseoverTimer = undefined 125 } 126 127 urlToPreload = undefined 128 129 stopPreloading() 130} 131 132function isPreloadable(linkElement) { 133 if (!linkElement || !linkElement.href) { 134 return 135 } 136 137 if (urlToPreload == linkElement.href) { 138 return 139 } 140 141 if (useWhitelist && !('instant' in linkElement.dataset)) { 142 return 143 } 144 145 if (!allowExternalLinks && linkElement.origin != location.origin && !('instant' in linkElement.dataset)) { 146 return 147 } 148 149 if (!['http:', 'https:'].includes(linkElement.protocol)) { 150 return 151 } 152 153 if (linkElement.protocol == 'http:' && location.protocol == 'https:') { 154 return 155 } 156 157 if (!allowQueryString && linkElement.search && !('instant' in linkElement.dataset)) { 158 return 159 } 160 161 if (linkElement.hash && linkElement.pathname + linkElement.search == location.pathname + location.search) { 162 return 163 } 164 165 if ('noInstant' in linkElement.dataset) { 166 return 167 } 168 169 return true 170} 171 172function preload(url) { 173 prefetcher.href = url 174} 175 176function stopPreloading() { 177 prefetcher.removeAttribute('href') 178}
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.