1+ /*! instant.page v1.2.2 - (C) 2019 Alexandre Dieulot - https://instant.page/license */
2+
3+ let urlToPreload
4+ let mouseoverTimer
5+ let lastTouchTimestamp
6+
7+ const prefetcher = document . createElement ( 'link' )
8+ const isSupported = prefetcher . relList && prefetcher . relList . supports && prefetcher . relList . supports ( 'prefetch' )
9+ const isDataSaverEnabled = navigator . connection && navigator . connection . saveData
10+ const allowQueryString = 'instantAllowQueryString' in document . body . dataset
11+ const allowExternalLinks = 'instantAllowExternalLinks' in document . body . dataset
12+
13+ if ( isSupported && ! isDataSaverEnabled ) {
14+ prefetcher . rel = 'prefetch'
15+ document . head . appendChild ( prefetcher )
16+
17+ const eventListenersOptions = {
18+ capture : true ,
19+ passive : true ,
20+ }
21+ document . addEventListener ( 'touchstart' , touchstartListener , eventListenersOptions )
22+ document . addEventListener ( 'mouseover' , mouseoverListener , eventListenersOptions )
23+ }
24+
25+ function touchstartListener ( event ) {
26+ /* Chrome on Android calls mouseover before touchcancel so `lastTouchTimestamp`
27+ * must be assigned on touchstart to be measured on mouseover. */
28+ lastTouchTimestamp = performance . now ( )
29+
30+ const linkElement = event . target . closest ( 'a' )
31+
32+ if ( ! isPreloadable ( linkElement ) ) {
33+ return
34+ }
35+
36+ linkElement . addEventListener ( 'touchcancel' , touchendAndTouchcancelListener , { passive : true } )
37+ linkElement . addEventListener ( 'touchend' , touchendAndTouchcancelListener , { passive : true } )
38+
39+ urlToPreload = linkElement . href
40+ preload ( linkElement . href )
41+ }
42+
43+ function touchendAndTouchcancelListener ( ) {
44+ urlToPreload = undefined
45+ stopPreloading ( )
46+ }
47+
48+ function mouseoverListener ( event ) {
49+ if ( performance . now ( ) - lastTouchTimestamp < 1100 ) {
50+ return
51+ }
52+
53+ const linkElement = event . target . closest ( 'a' )
54+
55+ if ( ! isPreloadable ( linkElement ) ) {
56+ return
57+ }
58+
59+ linkElement . addEventListener ( 'mouseout' , mouseoutListener , { passive : true } )
60+
61+ urlToPreload = linkElement . href
62+
63+ mouseoverTimer = setTimeout ( ( ) => {
64+ preload ( linkElement . href )
65+ mouseoverTimer = undefined
66+ } , 65 )
67+ }
68+
69+ function mouseoutListener ( event ) {
70+ if ( event . relatedTarget && event . target . closest ( 'a' ) == event . relatedTarget . closest ( 'a' ) ) {
71+ return
72+ }
73+
74+ if ( mouseoverTimer ) {
75+ clearTimeout ( mouseoverTimer )
76+ mouseoverTimer = undefined
77+ }
78+ else {
79+ urlToPreload = undefined
80+ stopPreloading ( )
81+ }
82+ }
83+
84+ function isPreloadable ( linkElement ) {
85+ if ( ! linkElement || ! linkElement . href ) {
86+ return
87+ }
88+
89+ if ( urlToPreload == linkElement . href ) {
90+ return
91+ }
92+
93+ const preloadLocation = new URL ( linkElement . href )
94+
95+ if ( ! allowExternalLinks && preloadLocation . origin != location . origin && ! ( 'instant' in linkElement . dataset ) ) {
96+ return
97+ }
98+
99+ if ( ! [ 'http:' , 'https:' ] . includes ( preloadLocation . protocol ) ) {
100+ return
101+ }
102+
103+ if ( preloadLocation . protocol == 'http:' && location . protocol == 'https:' ) {
104+ return
105+ }
106+
107+ if ( ! allowQueryString && preloadLocation . search && ! ( 'instant' in linkElement . dataset ) ) {
108+ return
109+ }
110+
111+ if ( preloadLocation . hash && preloadLocation . pathname + preloadLocation . search == location . pathname + location . search ) {
112+ return
113+ }
114+
115+ if ( 'noInstant' in linkElement . dataset ) {
116+ return
117+ }
118+
119+ return true
120+ }
121+
122+ function preload ( url ) {
123+ prefetcher . href = url
124+ }
125+
126+ function stopPreloading ( ) {
127+ prefetcher . removeAttribute ( 'href' )
128+ }
0 commit comments