UNPKG

quicklink

Version:

Faster subsequent page-loads by prefetching in-viewport links during idle time

2 lines (1 loc) 4.2 kB
function e(e,t,n,r){const o={headers:{accept:"*/*"}};return t||(o.mode="no-cors"),n&&(o.credentials="include"),o.priority=r?"high":"low",window.fetch?fetch(e,o):function(e,t){return new Promise((n,r,o)=>{(o=new XMLHttpRequest).open("GET",e,o.withCredentials=t),o.setRequestHeader("Accept","*/*"),o.onload=()=>{200===o.status?n():r()},o.send()})}(e,n)}const t=(n=document.createElement("link")).relList&&n.relList.supports&&n.relList.supports("prefetch")?function(e,t){return new Promise((n,r,o)=>{(o=document.createElement("link")).rel="prefetch",o.href=e,t&&o.setAttribute("crossorigin","anonymous"),o.onload=n,o.onerror=r,document.head.append(o)})}:e;var n;const r=window.requestIdleCallback||(e=>{const t=Date.now();return setTimeout(()=>{e({didTimeout:!1,timeRemaining:()=>Math.max(0,50-(Date.now()-t))})},1)});function o(e,t){const n=e.get(t);try{n.remove(),e.delete(t)}catch(e){return e}return e}const s=new Set,i=new Set;let c=!1;function a(e,t){return Array.isArray(t)?t.some(t=>a(e,t)):(t.test||t).call(t,e.href,e)}function u(e){return!e||(e.saveData?new Error("Save-Data is enabled"):!/2g/.test(e.effectiveType)||new Error("network conditions are poor"))}function l(e={}){if(!window.IntersectionObserver||!("isIntersecting"in IntersectionObserverEntry.prototype))return()=>{};const[t,n]=function(e){e=e||1;var t=[],n=0;function r(){n<e&&t.length>0&&(t.shift()(),n++)}return[function(e){t.push(e)>1||r()},function(){n--,r()}]}(e.throttle||1/0),u=e.limit||1/0,l=e.threshold||0,h=e.origins||[location.hostname],m=e.ignores||[],p=e.delay||0,g=new Map,w=new Map,v=e.timeoutFn||r,E="function"==typeof e.hrefFn&&e.hrefFn,y=e.prerender||!1;c=e.prerenderAndPrefetch||!1;const A=(e,t)=>{t?setTimeout(e,t):e()},b=new IntersectionObserver(r=>{for(let a of r){const r=g.get(a.target.href)||new Set;g.set(a.target.href,r),a.isIntersecting?(a=a.target,r.add(a),A(()=>{r&&r.size&&(y||c||b.unobserve(a),(c||y)&&i.size<u&&!w.has(a.href)?d(E?E(a):a.href,e.eagerness).then(e=>{for(const[t,n]of e)w.set(t,n)}).catch(t=>{if(!e.onError)throw t;e.onError(t)}):s.size<u&&!y&&t(()=>{f(E?E(a):a.href,e.priority,e.checkAccessControlAllowOrigin,e.checkAccessControlAllowCredentials,e.onlyOnMouseover).then(n).catch(t=>{n(),e.onError&&e.onError(t)})}))},p)):(a=a.target,r.delete(a),w.has(a.href)&&w.set(o(w,a.href)))}},{threshold:l});return v(()=>{const t=e.el&&e.el.length>0&&"A"===e.el[0].nodeName?e.el:(e.el||document).querySelectorAll("a");for(const e of t)h.length&&!h.includes(e.hostname)||a(e,m)||b.observe(e)},{timeout:e.timeout||2e3}),()=>{s.clear(),b.disconnect()}}function f(n,r,o,a,l){const f=u(navigator.connection);return f instanceof Error?Promise.reject(new Error(`Cannot prefetch, ${f.message}`)):(i.size>0&&!c&&console.warn("[Warning] You are using both prefetching and prerendering on the same document"),Promise.all([n].flat().map(n=>s.has(n)?[]:(s.add(n),function(e,t,n,...r){if(!n)return e(t,...r);const o=document.querySelectorAll(`a[href="${t}"]`),s=new Map;for(const n of o){const o=()=>{const c=setTimeout(()=>(n.removeEventListener("mouseenter",o),n.removeEventListener("mouseleave",i),e(t,...r)),200);s.set(n,c)},i=()=>{const e=s.get(n);e&&(clearTimeout(e),s.delete(n))};n.addEventListener("mouseenter",o),n.addEventListener("mouseleave",i)}}(r?e:t,new URL(n,location.href).toString(),l,o,a,r)))))}function d(e,t="immediate"){e=[e].flat();const n=u(navigator.connection);if(n instanceof Error)return Promise.reject(new Error(`Cannot prerender, ${n.message}`));if(!HTMLScriptElement.supports("speculationrules"))return f(e,!0,!1,!1,"moderate"===t||"conservative"===t),Promise.reject(new Error("This browser does not support the speculation rules API. Falling back to prefetch."));for(const t of e)i.add(t);s.size>0&&!c&&console.warn("[Warning] You are using both prefetching and prerendering on the same document");const r=function(e,t){const n=new Map;try{for(const r of e){const e=document.createElement("script");e.type="speculationrules",e.text=JSON.stringify({prerender:[{source:"list",urls:[r],eagerness:t}]}),document.head.append(e),n.set(r,e)}}catch(e){return e}return n}(e,t);return r.size>0?Promise.resolve(r):Promise.reject(r)}export{l as listen,f as prefetch,d as prerender};