@prefetchru/prefetch 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/prefetch.esm.min.js +2 -2
- package/dist/prefetch.min.js +2 -2
- package/package.json +1 -1
- package/prefetch.esm.js +86 -78
- package/prefetch.js +86 -78
- package/src/core.js +83 -76
package/README.md
CHANGED
|
@@ -139,7 +139,7 @@ ESM версия автоматически определяет `nonce` чер
|
|
|
139
139
|
// (window.Prefetch также доступен, если не занят другой библиотекой)
|
|
140
140
|
|
|
141
141
|
// Версия библиотеки
|
|
142
|
-
console.log(PrefetchRu.version) // "1.1.
|
|
142
|
+
console.log(PrefetchRu.version) // "1.1.3"
|
|
143
143
|
|
|
144
144
|
// Программная предзагрузка URL
|
|
145
145
|
// ВАЖНО: URL проходит те же проверки, что и автоматические ссылки
|
package/dist/prefetch.esm.min.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* prefetch.ru v1.1.
|
|
2
|
+
* prefetch.ru v1.1.3 (ESM) - Мгновенная загрузка страниц
|
|
3
3
|
* © 2026 Сергей Макаров | MIT License
|
|
4
4
|
* https://prefetch.ru | https://github.com/prefetch-ru
|
|
5
5
|
*/
|
|
6
|
-
var e="undefined"!=typeof window&&window.PrefetchRu&&window.PrefetchRu.__prefetchRu?window.PrefetchRu:"undefined"!=typeof window&&window.Prefetch&&window.Prefetch.__prefetchRu?window.Prefetch:function(e){var t=e&&e.getNonce;if(!(!e||e.isBrowser)||"undefined"==typeof window||"undefined"==typeof document)return{__prefetchRu:!0,version:"1.1.1",preload:function(){},destroy:function(){},refresh:function(){}};var n=new Set,r=new WeakMap,o=!1,i=0,a=[],c=new Set,s={prefetch:[],prerender:[],crossOrigin:[]},u=0,l="",d=0,f=0,h=null,p=!1,m=!1,v=null,g=!1,w=null,y=null,b=!1,E=65,L=80,x=50,T=!1,k=!1,O=!1,P=!1,A="none",S=!1,N=!1,C=!1,q=!1,I=!1,R=/(^|\/)(login|logout|auth|register|cart|basket|add|delete|remove)(\/|$|\.)/i,M=/\.(pdf|doc|docx|xls|xlsx|zip|rar|exe)($|\?)/i;function _(){if(!o){var e=document.body;if(e){l=location.origin+location.pathname+location.search,v=void 0!==window.BX?"bitrix":void 0!==window.B24||void 0!==window.BX24?"bitrix24":document.querySelector(".t-records")||void 0!==window.Tilda?"tilda":null;var t=e.dataset;if(T="prefetchAllowQueryString"in t||"instantAllowQueryString"in t,k="prefetchAllowExternalLinks"in t||"instantAllowExternalLinks"in t,O="prefetchWhitelist"in t||"instantWhitelist"in t,t.prefetchNonce&&(y=t.prefetchNonce),!y&&t.instantNonce&&(y=t.instantNonce),!m&&("prefetchSpecrules"in t||"instantSpecrules"in t)&&HTMLScriptElement.supports&&HTMLScriptElement.supports("speculationrules")){var n=t.prefetchSpecrules||t.instantSpecrules;"prerender"===n?(A="prerender",P=!0):"no"!==n&&(A="prefetch",P=!0)}S="prefetchSpecrulesFallback"in t||"instantSpecrulesFallback"in t,N="prefetchPrerenderAll"in t||"instantPrerenderAll"in t;var r=t.prefetchIntensity||t.instantIntensity;if("mousedown"===r)C=!0;else if("viewport"===r||"viewport-all"===r)("viewport-all"===r||p&&D())&&(q=!0);else if(r){var i=parseInt(r,10);!isNaN(i)&&i>=0&&(E=i)}p&&(L=Math.max(60,Math.min(E||0,150))),(I="prefetchObserveDom"in t||"instantObserveDom"in t)||"bitrix"!==v&&"tilda"!==v||(I=!0),window.addEventListener("popstate",U),window.addEventListener("hashchange",U),window.addEventListener("pageshow",W),"tilda"===v&&E<100&&(E=100);var a={capture:!0,passive:!0};if(document.addEventListener("touchstart",z,a),C?document.addEventListener("mousedown",F,a):document.addEventListener("mouseover",K,a),q&&"undefined"==typeof IntersectionObserver&&(q=!1),q)(window.requestIdleCallback||function(e){setTimeout(e,1)})(Z,{timeout:1500});I&&q&&"undefined"!=typeof MutationObserver&&function(){if(o)return;if(te)return;(te=new MutationObserver(function(e){var t=!1;e:for(var n=0;n<e.length;n++)for(var r=e[n].addedNodes,o=0;o<r.length;o++){var i=r[o];if(1===i.nodeType&&("A"===i.tagName||i.querySelector&&i.querySelector("a"))){t=!0;break e}}t&&Y&&(clearTimeout(ne),ne=setTimeout(ee,100))})).observe(document.body,{childList:!0,subtree:!0})}()}}}function D(){return!g&&("slow-2g"!==w&&"2g"!==w&&"3g"!==w)}function U(){l=location.origin+location.pathname+location.search}function W(e){e&&e.persisted&&U()}function B(e){return e?(e.nodeType&&1!==e.nodeType&&(e=e.parentElement),e&&"function"==typeof e.closest?e.closest("a"):null):null}function z(e){if(!(o||e&&!1===e.isTrusted)){d=Date.now();var t=B(e.target);if(G(t)){f&&(clearTimeout(f),f=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null);var n=!1;h=function(){n=!0,f&&(clearTimeout(f),f=0),document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null},document.addEventListener("touchmove",h,{capture:!0,passive:!0,once:!0}),document.addEventListener("scroll",h,{capture:!0,passive:!0,once:!0}),f=setTimeout(function(){h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),f=0,n||H(t.href,t)},L)}}}function K(e){if(!o&&!(e&&!1===e.isTrusted||d&&Date.now()-d<2500)){var t=B(e.target);if(t&&!r.has(t)&&G(t)){t.addEventListener("mouseleave",j,{passive:!0,once:!0});var n=setTimeout(function(){H(t.href,t),r.delete(t)},E);r.set(t,n)}}}function j(e){var t=e.currentTarget;if(t){var n=r.get(t);n&&(clearTimeout(n),r.delete(t))}}function F(e){if(!o&&!(e&&!1===e.isTrusted||"number"==typeof e.button&&(1===e.button||2===e.button)||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||d&&Date.now()-d<2500)){var t=B(e.target);G(t)&&H(t.href,t)}}function G(e){if(!e)return!1;var t=e.getAttribute("href");if(null===t||""===t.trim())return!1;if(!e.href)return!1;if(e.target&&"_self"!==e.target)return!1;if(e.hasAttribute("download"))return!1;if("noPrefetch"in e.dataset||"prefetchNo"in e.dataset)return!1;if(O&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if("http:"!==e.protocol&&"https:"!==e.protocol)return!1;if("http:"===e.protocol&&"https:"===location.protocol)return!1;if(e.origin!==location.origin&&!k&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.search&&!T&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.hash&&e.pathname+e.search===location.pathname+location.search)return!1;var r=e.origin+e.pathname+e.search;return r!==l&&(!n.has(r)&&(!!function(e){var t=e.href,n=e.pathname||"",r=e.hash||"";if("bitrix"===v||"bitrix24"===v){if(-1!==t.indexOf("/bitrix/")||-1!==t.indexOf("sessid="))return!1;if(e.classList.contains("bx-ajax"))return!1}if("tilda"===v&&(-1!==r.indexOf("#popup:")||-1!==r.indexOf("#rec")))return!1;return!R.test(n)&&!M.test(n)}(e)&&!!function(e){var t=e.className||"",n=e.hostname||"";return-1===t.indexOf("ym-")&&("mc.yandex.ru"!==n&&"metrika.yandex.ru"!==n&&(-1===t.indexOf("ga-")&&-1===t.indexOf("gtm-")&&("google-analytics.com"!==n&&!n.endsWith(".google-analytics.com")&&("googletagmanager.com"!==n&&!n.endsWith(".googletagmanager.com")&&(-1===t.indexOf("piwik")&&-1===t.indexOf("matomo")&&("matomo.org"!==n&&!n.endsWith(".matomo.org")&&"piwik.org"!==n&&!n.endsWith(".piwik.org")))))))}(e)))}function H(e,t){if(!o&&D()){var r=function(e){try{var t=new URL(e,location.href),n=t.origin+t.pathname+t.search;return t.hash="",{requestUrl:t.href,key:n}}catch(t){return{requestUrl:e,key:e}}}(e),c=r.requestUrl,s=r.key;if(!n.has(s)){n.size>=x&&n.delete(n.values().next().value),n.add(s);var u=function(e){return P&&"none"!==A?"prerender"!==A?A:N||O||e&&e.dataset&&("prefetchPrerender"in e.dataset||"instantPrerender"in e.dataset)?"prerender":"prefetch":"none"}(t);if(i>=4)return a.length>=50?void n.delete(s):void a.push({url:c,key:s,mode:u});Q(c,s,u)}}}function Q(e,t,n){if("none"===n)m||!b?V(e,t):J(e,t);else{var r=!1;try{!function(e,t){var n=!1;try{n=new URL(e,location.href).origin!==location.origin}catch(e){}n&&"prerender"===t&&(t="prefetch");n?s.crossOrigin.push(e):"prerender"===t?s.prerender.push(e):s.prefetch.push(e);if(!u){var r=window.requestIdleCallback||function(e){setTimeout(e,1)};u=r($,{timeout:50})}}(e,n),r=!0}catch(e){}!S&&r||(m||!b?V(e,t):J(e,t))}}function X(){for(;a.length>0&&i<4;){var e=a.shift();Q(e.url,e.key,e.mode)}}function $(){if(u=0,!o){var e=document.head;if(e){var t={};if(s.prefetch.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.prefetch.slice()}),s.prefetch.length=0),s.prerender.length>0&&(t.prerender=t.prerender||[],t.prerender.push({source:"list",urls:s.prerender.slice()}),s.prerender.length=0),s.crossOrigin.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.crossOrigin.slice(),referrer_policy:"no-referrer",requires:["anonymous-client-ip-when-cross-origin"]}),s.crossOrigin.length=0),t.prefetch||t.prerender){var n=document.createElement("script");n.type="speculationrules",y&&(n.nonce=y),n.textContent=JSON.stringify(t),e.appendChild(n),e.removeChild(n)}}}}function J(e,t){var r=document.head;if(r){i++;var o=document.createElement("link");o.rel="prefetch",o.href=e,o.as="document";try{o.fetchPriority="low"}catch(e){}try{new URL(e,location.href).origin!==location.origin&&(o.referrerPolicy="no-referrer",o.crossOrigin="anonymous")}catch(e){}var a=setTimeout(function(){a=0,n.delete(t),c()},3e4);o.onload=c,o.onerror=function(){n.delete(t),c()},r.appendChild(o)}else n.delete(t);function c(){a&&(clearTimeout(a),a=0),o.onload=o.onerror=null,o.parentNode&&o.parentNode.removeChild(o),i--,X()}}function V(e,t){if("function"==typeof fetch){i++;var r=!1,o=null,a=0;"undefined"!=typeof AbortController&&(o=new AbortController,c.add(o),a=setTimeout(function(){try{o.abort()}catch(e){}l(!1)},5e3));var s=!1;try{s=new URL(e,location.href).origin!==location.origin}catch(e){}var u={method:"GET",cache:"force-cache",credentials:s?"omit":"same-origin",mode:s?"no-cors":"cors"};s||(u.headers={Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",Purpose:"prefetch"}),s&&(u.referrerPolicy="no-referrer"),o&&(u.signal=o.signal);try{fetch(e,u).then(function(e){l(e&&(e.ok||304===e.status||"opaque"===e.type))}).catch(function(){l(!1)})}catch(e){l(!1)}}else n.delete(t);function l(e){r||(r=!0,a&&clearTimeout(a),o&&c.delete(o),e||n.delete(t),i--,X())}}!function(){if(t)try{y=t()}catch(e){}try{var e=document.createElement("link");e.relList&&"function"==typeof e.relList.supports&&(b=e.relList.supports("prefetch"))}catch(e){}var n=navigator.userAgent,r=navigator.userAgentData;if(r){m=!1;var o="Android"===r.platform;p=r.mobile||!1;for(var i=r.brands||[],a=0;a<i.length;a++){var c=i[a];if("Chromium"===c.brand||"Google Chrome"===c.brand){parseInt(c.version,10);break}}}else{m=/iPad|iPhone/.test(n)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1;o=/Android/.test(n);p=(m||o)&&Math.min(screen.width,screen.height)<768;var s=n.match(/Chrome\/(\d+)/);s&&parseInt(s[1],10)}p&&(x=20);var u=navigator.connection;u&&(w=u.effectiveType,g=u.saveData||!1),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",_):_()}();var Y=null;function Z(){o||Y||(Y=new IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&(Y.unobserve(e.target),G(e.target)&&H(e.target.href,e.target))})},{rootMargin:p?"100px":"200px"}),ee())}function ee(){Y&&document.querySelectorAll("a").forEach(function(e){G(e)&&Y.observe(e)})}var te=null,ne=null;return{__prefetchRu:!0,version:"1.1.1",preload:function(e){if(function(e){return!(!e||"string"!=typeof e||!(e=e.trim())||/^\/\//.test(e)||/^(javascript|data|vbscript|file):/i.test(e)||!(/^https?:\/\//i.test(e)||/^\//.test(e)||/^\./.test(e))&&/^[a-z][a-z0-9+.-]*:/i.test(e))}(e)){var t=document.createElement("a");t.setAttribute("href",e.trim()),G(t)&&H(t.href,t)}},destroy:function(){o=!0,a.length=0,f&&(clearTimeout(f),f=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),c.forEach(function(e){try{e.abort()}catch(e){}}),c.clear(),u&&((window.cancelIdleCallback||clearTimeout)(u),u=0),s.prefetch.length=s.prerender.length=s.crossOrigin.length=0;var e={capture:!0,passive:!0};document.removeEventListener("touchstart",z,e),document.removeEventListener("mouseover",K,e),document.removeEventListener("mousedown",F,e),window.removeEventListener("popstate",U),window.removeEventListener("hashchange",U),window.removeEventListener("pageshow",W),Y&&(Y.disconnect(),Y=null),te&&(te.disconnect(),te=null)},refresh:U}}({isBrowser:"undefined"!=typeof window,getNonce:function(){var e=function(e){try{if(!e)return null;for(var t=document.getElementsByTagName("script"),n=0;n<t.length;n++){var r=t[n];if(r&&r.src&&r.src===e){var o=r.nonce||r.getAttribute("nonce")||null;if(o)return o;break}}}catch(e){}return null}(import.meta.url);if(e)return e;try{var t=document.currentScript;if(t&&t.nonce)return t.nonce}catch(e){}return null}});"undefined"!=typeof window&&(window.PrefetchRu=e,window.Prefetch||(window.Prefetch=e));export{e as Prefetch,e as default};
|
|
6
|
+
var e="undefined"!=typeof window&&window.PrefetchRu&&window.PrefetchRu.__prefetchRu?window.PrefetchRu:"undefined"!=typeof window&&window.Prefetch&&window.Prefetch.__prefetchRu?window.Prefetch:function(e){var t=e&&e.getNonce;if(!(!e||e.isBrowser)||"undefined"==typeof window||"undefined"==typeof document)return{__prefetchRu:!0,version:"1.1.3",preload:function(){},destroy:function(){},refresh:function(){}};var n=new Set,r=new WeakMap,i=!1,o=0,a=[],c=new Set,s={prefetch:[],prerender:[],crossOrigin:[]},u=0,l="",f=0,d=0,h=null,p=!1,v=!1,m=null,g=!1,w=null,y=null,b=null,E=!1,L=65,x=80,T=50,O=!1,k=!1,P=!1,A=!1,S="none",N=!1,q=!1,C=!1,I=!1,M=!1,R=/(^|\/)(login|logout|auth|register|cart|basket|add|delete|remove)(\/|$|\.)/i,_=/\.(pdf|doc|docx|xls|xlsx|zip|rar|exe)($|\?)/i;function D(){if(!i){var e=document.body;if(e){l=location.origin+location.pathname+location.search,m=void 0!==window.BX?"bitrix":void 0!==window.B24||void 0!==window.BX24?"bitrix24":document.querySelector(".t-records")||void 0!==window.Tilda?"tilda":null;var t=e.dataset;if(O="prefetchAllowQueryString"in t||"instantAllowQueryString"in t,k="prefetchAllowExternalLinks"in t||"instantAllowExternalLinks"in t,P="prefetchWhitelist"in t||"instantWhitelist"in t,t.prefetchNonce&&(b=t.prefetchNonce),!b&&t.instantNonce&&(b=t.instantNonce),!v&&("prefetchSpecrules"in t||"instantSpecrules"in t)&&HTMLScriptElement.supports&&HTMLScriptElement.supports("speculationrules")){var n=t.prefetchSpecrules||t.instantSpecrules;"prerender"===n?(S="prerender",A=!0):"no"!==n&&(S="prefetch",A=!0)}N="prefetchSpecrulesFallback"in t||"instantSpecrulesFallback"in t,q="prefetchPrerenderAll"in t||"instantPrerenderAll"in t;var r=t.prefetchIntensity||t.instantIntensity;if("mousedown"===r)C=!0;else if("viewport"===r||"viewport-all"===r)("viewport-all"===r||p&&W())&&(I=!0);else if(r){var o=parseInt(r,10);!isNaN(o)&&o>=0&&(L=o)}p&&(x=Math.max(60,Math.min(L||0,150))),(M="prefetchObserveDom"in t||"instantObserveDom"in t)||"bitrix"!==m&&"tilda"!==m||(M=!0),window.addEventListener("popstate",U),window.addEventListener("hashchange",U),window.addEventListener("pageshow",z),"tilda"===m&&L<100&&(L=100);var a={capture:!0,passive:!0};if(document.addEventListener("touchstart",j,a),C?document.addEventListener("mousedown",Q,a):document.addEventListener("mouseover",F,a),I&&"undefined"==typeof IntersectionObserver&&(I=!1),I)(window.requestIdleCallback||function(e){setTimeout(e,1)})(te,{timeout:1500});M&&I&&"undefined"!=typeof MutationObserver&&function(){if(i)return;if(ne)return;ne=new MutationObserver(function(e){if(ee){for(var t=[],n=0;n<e.length;n++)for(var r=e[n].addedNodes,i=0;i<r.length;i++){var o=r[i];if(1===o.nodeType)if("A"===o.tagName)t.push(o);else if(o.querySelectorAll)for(var a=o.querySelectorAll("a"),c=0;c<a.length;c++)t.push(a[c])}if(t.length>0)(window.requestIdleCallback||function(e){setTimeout(e,1)})(function(){for(var e=0;e<t.length;e++)ee&&X(t[e])&&ee.observe(t[e])})}}),ne.observe(document.body,{childList:!0,subtree:!0})}()}}}function W(){return!g&&("slow-2g"!==w&&"2g"!==w&&"3g"!==w)}function B(){y&&(w=y.effectiveType,g=y.saveData||!1)}function U(){l=location.origin+location.pathname+location.search}function z(e){e&&e.persisted&&U()}function K(e){return e?(e.nodeType&&1!==e.nodeType&&(e=e.parentElement),e&&"function"==typeof e.closest?e.closest("a"):null):null}function j(e){if(!(i||e&&!1===e.isTrusted)){f=Date.now();var t=K(e.target);if(X(t)){d&&(clearTimeout(d),d=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null);var n=!1;h=function(){n=!0,d&&(clearTimeout(d),d=0),document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null},document.addEventListener("touchmove",h,{capture:!0,passive:!0,once:!0}),document.addEventListener("scroll",h,{capture:!0,passive:!0,once:!0}),d=setTimeout(function(){h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),d=0,n||$(t.href,t)},x)}}}function F(e){if(!i&&!(e&&!1===e.isTrusted||f&&Date.now()-f<2500)){var t=K(e.target);if(t&&!r.has(t)&&X(t)){t.addEventListener("mouseleave",H,{passive:!0,once:!0});var n=setTimeout(function(){$(t.href,t),r.delete(t)},L);r.set(t,n)}}}function H(e){var t=e.currentTarget;if(t){var n=r.get(t);n&&(clearTimeout(n),r.delete(t))}}function Q(e){if(!i&&!(e&&!1===e.isTrusted||"number"==typeof e.button&&(1===e.button||2===e.button)||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||f&&Date.now()-f<2500)){var t=K(e.target);X(t)&&$(t.href,t)}}function X(e){if(!e)return!1;var t=e.getAttribute("href");if(null===t||""===t.trim())return!1;if(!e.href)return!1;if(e.target&&"_self"!==e.target)return!1;if(e.hasAttribute("download"))return!1;if("noPrefetch"in e.dataset||"prefetchNo"in e.dataset)return!1;if(P&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if("http:"!==e.protocol&&"https:"!==e.protocol)return!1;if("http:"===e.protocol&&"https:"===location.protocol)return!1;if(e.origin!==location.origin&&!k&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.search&&!O&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.hash&&e.pathname+e.search===location.pathname+location.search)return!1;var r=e.origin+e.pathname+e.search;return r!==l&&(!n.has(r)&&(!!function(e){var t=e.pathname||"",n=e.hash||"";if("bitrix"===m||"bitrix24"===m){if(-1!==t.indexOf("/bitrix/")||e.search&&-1!==e.search.indexOf("sessid="))return!1;if(e.classList.contains("bx-ajax"))return!1}if("tilda"===m&&(-1!==n.indexOf("#popup:")||-1!==n.indexOf("#rec")))return!1;return!R.test(t)&&!_.test(t)}(e)&&!!function(e){var t=e.className||"",n=e.hostname||"";return-1===t.indexOf("ym-")&&("mc.yandex.ru"!==n&&"metrika.yandex.ru"!==n&&(-1===t.indexOf("ga-")&&-1===t.indexOf("gtm-")&&("google-analytics.com"!==n&&!n.endsWith(".google-analytics.com")&&("googletagmanager.com"!==n&&!n.endsWith(".googletagmanager.com")&&(-1===t.indexOf("piwik")&&-1===t.indexOf("matomo")&&("matomo.org"!==n&&!n.endsWith(".matomo.org")&&"piwik.org"!==n&&!n.endsWith(".piwik.org")))))))}(e)))}function $(e,t){if(!i&&W()){var r,c,s;if(t&&t.origin)c=t.origin+t.pathname+t.search,r=t.href.split("#")[0],s=t.origin!==location.origin;else{var u=function(e){try{var t=new URL(e,location.href),n=t.origin+t.pathname+t.search;return t.hash="",{requestUrl:t.href,key:n}}catch(t){return{requestUrl:e,key:e}}}(e);r=u.requestUrl,c=u.key,s=!1;try{s=new URL(r,location.href).origin!==location.origin}catch(e){}}if(!n.has(c)){n.size>=T&&n.delete(n.values().next().value),n.add(c);var l=function(e){return A&&"none"!==S?"prerender"!==S?S:q||P||e&&e.dataset&&("prefetchPrerender"in e.dataset||"instantPrerender"in e.dataset)?"prerender":"prefetch":"none"}(t);if(o>=4)return a.length>=50?void n.delete(c):void a.push({url:r,key:c,mode:l,crossOrigin:s});G(r,c,l,s)}}}function G(e,t,n,r){if("none"===n)v||!E||r?Z(e,t,r):Y(e,t,r);else{var i=!1;try{!function(e,t,n){n&&"prerender"===t&&(t="prefetch");n?s.crossOrigin.push(e):"prerender"===t?s.prerender.push(e):s.prefetch.push(e);if(!u){var r=window.requestIdleCallback||function(e){setTimeout(e,1)};u=r(V,{timeout:50})}}(e,n,r),i=!0}catch(e){}!N&&i||(v||!E||r?Z(e,t,r):Y(e,t,r))}}function J(){for(;a.length>0&&o<4;){var e=a.shift();G(e.url,e.key,e.mode,e.crossOrigin)}}function V(){if(u=0,!i){var e=document.head;if(e){var t={};if(s.prefetch.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.prefetch}),s.prefetch=[]),s.prerender.length>0&&(t.prerender=t.prerender||[],t.prerender.push({source:"list",urls:s.prerender}),s.prerender=[]),s.crossOrigin.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.crossOrigin,referrer_policy:"no-referrer",requires:["anonymous-client-ip-when-cross-origin"]}),s.crossOrigin=[]),t.prefetch||t.prerender){var n=document.createElement("script");n.type="speculationrules",b&&(n.nonce=b),n.textContent=JSON.stringify(t),e.appendChild(n),e.removeChild(n)}}}}function Y(e,t,r){var i=document.head;if(i){o++;var a=document.createElement("link");a.rel="prefetch",a.href=e,a.as="document";try{a.fetchPriority="low"}catch(e){}r&&(a.referrerPolicy="no-referrer",a.crossOrigin="anonymous");var c=setTimeout(function(){c=0,n.delete(t),s()},3e4);a.onload=s,a.onerror=function(){n.delete(t),s()},i.appendChild(a)}else n.delete(t);function s(){c&&(clearTimeout(c),c=0),a.onload=a.onerror=null,a.parentNode&&a.parentNode.removeChild(a),o--,J()}}function Z(e,t,r){if("function"==typeof fetch){o++;var i=!1,a=null,s=0;"undefined"!=typeof AbortController&&(a=new AbortController,c.add(a),s=setTimeout(function(){try{a.abort()}catch(e){}l(!1)},5e3));var u={method:"GET",cache:"force-cache",credentials:r?"omit":"same-origin",mode:r?"no-cors":"cors"};r||(u.headers={Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",Purpose:"prefetch"}),r&&(u.referrerPolicy="no-referrer"),a&&(u.signal=a.signal);try{fetch(e,u).then(function(e){l(e&&(e.ok||304===e.status||"opaque"===e.type))}).catch(function(){l(!1)})}catch(e){l(!1)}}else n.delete(t);function l(e){i||(i=!0,s&&clearTimeout(s),a&&c.delete(a),e||n.delete(t),o--,J())}}!function(){if(t)try{b=t()}catch(e){}try{var e=document.createElement("link");e.relList&&"function"==typeof e.relList.supports&&(E=e.relList.supports("prefetch"))}catch(e){}var n=navigator.userAgent,r=navigator.userAgentData;r?(v=!1,p=r.mobile||!1):(v=/iPad|iPhone/.test(n)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1,p=(v||/Android/.test(n))&&Math.min(screen.width,screen.height)<768),p&&(T=20),(y=navigator.connection)&&(w=y.effectiveType,g=y.saveData||!1,"function"==typeof y.addEventListener&&y.addEventListener("change",B)),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",D):D()}();var ee=null;function te(){i||ee||(ee=new IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&(ee.unobserve(e.target),X(e.target)&&$(e.target.href,e.target))})},{rootMargin:p?"100px":"200px"}),function(){if(!ee)return;document.querySelectorAll("a").forEach(function(e){X(e)&&ee.observe(e)})}())}var ne=null;return{__prefetchRu:!0,version:"1.1.3",preload:function(e){if(function(e){return!(!e||"string"!=typeof e||!(e=e.trim())||/^\/\//.test(e)||/^(javascript|data|vbscript|file):/i.test(e)||!(/^https?:\/\//i.test(e)||/^\//.test(e)||/^\./.test(e))&&/^[a-z][a-z0-9+.-]*:/i.test(e))}(e)){var t=document.createElement("a");t.setAttribute("href",e.trim()),X(t)&&$(t.href,t)}},destroy:function(){i=!0,a.length=0,d&&(clearTimeout(d),d=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),c.forEach(function(e){try{e.abort()}catch(e){}}),c.clear(),u&&((window.cancelIdleCallback||clearTimeout)(u),u=0),s.prefetch.length=s.prerender.length=s.crossOrigin.length=0;var e={capture:!0,passive:!0};document.removeEventListener("touchstart",j,e),document.removeEventListener("mouseover",F,e),document.removeEventListener("mousedown",Q,e),y&&"function"==typeof y.removeEventListener&&y.removeEventListener("change",B),window.removeEventListener("popstate",U),window.removeEventListener("hashchange",U),window.removeEventListener("pageshow",z),ee&&(ee.disconnect(),ee=null),ne&&(ne.disconnect(),ne=null)},refresh:U}}({isBrowser:"undefined"!=typeof window,getNonce:function(){var e=function(e){try{if(!e)return null;for(var t=document.getElementsByTagName("script"),n=0;n<t.length;n++){var r=t[n];if(r&&r.src&&r.src===e){var i=r.nonce||r.getAttribute("nonce")||null;if(i)return i;break}}}catch(e){}return null}(import.meta.url);if(e)return e;try{var t=document.currentScript;if(t&&t.nonce)return t.nonce}catch(e){}return null}});"undefined"!=typeof window&&(window.PrefetchRu=e,window.Prefetch||(window.Prefetch=e));export{e as Prefetch,e as default};
|
package/dist/prefetch.min.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* prefetch.ru v1.1.
|
|
2
|
+
* prefetch.ru v1.1.3 - Мгновенная загрузка страниц
|
|
3
3
|
* © 2026 Сергей Макаров | MIT License
|
|
4
4
|
* https://prefetch.ru | https://github.com/prefetch-ru
|
|
5
5
|
*/
|
|
6
|
-
!function(){"use strict";if(!(window.PrefetchRu&&window.PrefetchRu.__prefetchRu||window.Prefetch&&window.Prefetch.__prefetchRu)){var e=function(e){var t=e&&e.getNonce;if(e&&!e.isBrowser||"undefined"==typeof window||"undefined"==typeof document)return{__prefetchRu:!0,version:"1.1.1",preload:function(){},destroy:function(){},refresh:function(){}};var n=new Set,r=new WeakMap,o=!1,i=0,a=[],c=new Set,s={prefetch:[],prerender:[],crossOrigin:[]},u=0,l="",d=0,f=0,h=null,p=!1,m=!1,v=null,g=!1,w=null,y=null,b=!1,L=65,E=80,x=50,T=!1,O=!1,k=!1,P=!1,S="none",A=!1,N=!1,C=!1,q=!1,I=!1,M=/(^|\/)(login|logout|auth|register|cart|basket|add|delete|remove)(\/|$|\.)/i,R=/\.(pdf|doc|docx|xls|xlsx|zip|rar|exe)($|\?)/i;function _(){if(!o){var e=document.body;if(e){l=location.origin+location.pathname+location.search,v=void 0!==window.BX?"bitrix":void 0!==window.B24||void 0!==window.BX24?"bitrix24":document.querySelector(".t-records")||void 0!==window.Tilda?"tilda":null;var t=e.dataset;if(T="prefetchAllowQueryString"in t||"instantAllowQueryString"in t,O="prefetchAllowExternalLinks"in t||"instantAllowExternalLinks"in t,k="prefetchWhitelist"in t||"instantWhitelist"in t,t.prefetchNonce&&(y=t.prefetchNonce),!y&&t.instantNonce&&(y=t.instantNonce),!m&&("prefetchSpecrules"in t||"instantSpecrules"in t)&&HTMLScriptElement.supports&&HTMLScriptElement.supports("speculationrules")){var n=t.prefetchSpecrules||t.instantSpecrules;"prerender"===n?(S="prerender",P=!0):"no"!==n&&(S="prefetch",P=!0)}A="prefetchSpecrulesFallback"in t||"instantSpecrulesFallback"in t,N="prefetchPrerenderAll"in t||"instantPrerenderAll"in t;var r=t.prefetchIntensity||t.instantIntensity;if("mousedown"===r)C=!0;else if("viewport"===r||"viewport-all"===r)("viewport-all"===r||p&&D())&&(q=!0);else if(r){var i=parseInt(r,10);!isNaN(i)&&i>=0&&(L=i)}p&&(E=Math.max(60,Math.min(L||0,150))),(I="prefetchObserveDom"in t||"instantObserveDom"in t)||"bitrix"!==v&&"tilda"!==v||(I=!0),window.addEventListener("popstate",U),window.addEventListener("hashchange",U),window.addEventListener("pageshow",W),"tilda"===v&&L<100&&(L=100);var a={capture:!0,passive:!0};document.addEventListener("touchstart",z,a),C?document.addEventListener("mousedown",F,a):document.addEventListener("mouseover",K,a),q&&"undefined"==typeof IntersectionObserver&&(q=!1),q&&(window.requestIdleCallback||function(e){setTimeout(e,1)})(Z,{timeout:1500}),I&&q&&"undefined"!=typeof MutationObserver&&(o||te||(te=new MutationObserver(function(e){var t=!1;e:for(var n=0;n<e.length;n++)for(var r=e[n].addedNodes,o=0;o<r.length;o++){var i=r[o];if(1===i.nodeType&&("A"===i.tagName||i.querySelector&&i.querySelector("a"))){t=!0;break e}}t&&Y&&(clearTimeout(ne),ne=setTimeout(ee,100))})).observe(document.body,{childList:!0,subtree:!0}))}}}function D(){return!g&&"slow-2g"!==w&&"2g"!==w&&"3g"!==w}function U(){l=location.origin+location.pathname+location.search}function W(e){e&&e.persisted&&U()}function B(e){return e?(e.nodeType&&1!==e.nodeType&&(e=e.parentElement),e&&"function"==typeof e.closest?e.closest("a"):null):null}function z(e){if(!(o||e&&!1===e.isTrusted)){d=Date.now();var t=B(e.target);if(G(t)){f&&(clearTimeout(f),f=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null);var n=!1;h=function(){n=!0,f&&(clearTimeout(f),f=0),document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null},document.addEventListener("touchmove",h,{capture:!0,passive:!0,once:!0}),document.addEventListener("scroll",h,{capture:!0,passive:!0,once:!0}),f=setTimeout(function(){h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),f=0,n||H(t.href,t)},E)}}}function K(e){if(!o&&!(e&&!1===e.isTrusted||d&&Date.now()-d<2500)){var t=B(e.target);if(t&&!r.has(t)&&G(t)){t.addEventListener("mouseleave",j,{passive:!0,once:!0});var n=setTimeout(function(){H(t.href,t),r.delete(t)},L);r.set(t,n)}}}function j(e){var t=e.currentTarget;if(t){var n=r.get(t);n&&(clearTimeout(n),r.delete(t))}}function F(e){if(!o&&!(e&&!1===e.isTrusted||"number"==typeof e.button&&(1===e.button||2===e.button)||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||d&&Date.now()-d<2500)){var t=B(e.target);G(t)&&H(t.href,t)}}function G(e){if(!e)return!1;var t=e.getAttribute("href");if(null===t||""===t.trim())return!1;if(!e.href)return!1;if(e.target&&"_self"!==e.target)return!1;if(e.hasAttribute("download"))return!1;if("noPrefetch"in e.dataset||"prefetchNo"in e.dataset)return!1;if(k&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if("http:"!==e.protocol&&"https:"!==e.protocol)return!1;if("http:"===e.protocol&&"https:"===location.protocol)return!1;if(e.origin!==location.origin&&!O&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.search&&!T&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.hash&&e.pathname+e.search===location.pathname+location.search)return!1;var r=e.origin+e.pathname+e.search;return r!==l&&!n.has(r)&&!!function(e){var t=e.href,n=e.pathname||"",r=e.hash||"";if("bitrix"===v||"bitrix24"===v){if(-1!==t.indexOf("/bitrix/")||-1!==t.indexOf("sessid="))return!1;if(e.classList.contains("bx-ajax"))return!1}return("tilda"!==v||-1===r.indexOf("#popup:")&&-1===r.indexOf("#rec"))&&(!M.test(n)&&!R.test(n))}(e)&&!!function(e){var t=e.className||"",n=e.hostname||"";return!(-1!==t.indexOf("ym-")||"mc.yandex.ru"===n||"metrika.yandex.ru"===n||-1!==t.indexOf("ga-")||-1!==t.indexOf("gtm-")||"google-analytics.com"===n||n.endsWith(".google-analytics.com")||"googletagmanager.com"===n||n.endsWith(".googletagmanager.com")||-1!==t.indexOf("piwik")||-1!==t.indexOf("matomo")||"matomo.org"===n||n.endsWith(".matomo.org")||"piwik.org"===n||n.endsWith(".piwik.org"))}(e)}function H(e,t){if(!o&&D()){var r=function(e){try{var t=new URL(e,location.href),n=t.origin+t.pathname+t.search;return t.hash="",{requestUrl:t.href,key:n}}catch(t){return{requestUrl:e,key:e}}}(e),c=r.requestUrl,s=r.key;if(!n.has(s)){n.size>=x&&n.delete(n.values().next().value),n.add(s);var u=function(e){return P&&"none"!==S?"prerender"!==S?S:N||k||e&&e.dataset&&("prefetchPrerender"in e.dataset||"instantPrerender"in e.dataset)?"prerender":"prefetch":"none"}(t);if(i>=4)return a.length>=50?void n.delete(s):void a.push({url:c,key:s,mode:u});Q(c,s,u)}}}function Q(e,t,n){if("none"===n)m||!b?V(e,t):J(e,t);else{var r=!1;try{!function(e,t){var n=!1;try{n=new URL(e,location.href).origin!==location.origin}catch(e){}if(n&&"prerender"===t&&(t="prefetch"),n?s.crossOrigin.push(e):"prerender"===t?s.prerender.push(e):s.prefetch.push(e),!u){var r=window.requestIdleCallback||function(e){setTimeout(e,1)};u=r($,{timeout:50})}}(e,n),r=!0}catch(e){}!A&&r||(m||!b?V(e,t):J(e,t))}}function X(){for(;a.length>0&&i<4;){var e=a.shift();Q(e.url,e.key,e.mode)}}function $(){if(u=0,!o){var e=document.head;if(e){var t={};if(s.prefetch.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.prefetch.slice()}),s.prefetch.length=0),s.prerender.length>0&&(t.prerender=t.prerender||[],t.prerender.push({source:"list",urls:s.prerender.slice()}),s.prerender.length=0),s.crossOrigin.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.crossOrigin.slice(),referrer_policy:"no-referrer",requires:["anonymous-client-ip-when-cross-origin"]}),s.crossOrigin.length=0),t.prefetch||t.prerender){var n=document.createElement("script");n.type="speculationrules",y&&(n.nonce=y),n.textContent=JSON.stringify(t),e.appendChild(n),e.removeChild(n)}}}}function J(e,t){var r=document.head;if(r){i++;var o=document.createElement("link");o.rel="prefetch",o.href=e,o.as="document";try{o.fetchPriority="low"}catch(e){}try{new URL(e,location.href).origin!==location.origin&&(o.referrerPolicy="no-referrer",o.crossOrigin="anonymous")}catch(e){}var a=setTimeout(function(){a=0,n.delete(t),c()},3e4);o.onload=c,o.onerror=function(){n.delete(t),c()},r.appendChild(o)}else n.delete(t);function c(){a&&(clearTimeout(a),a=0),o.onload=o.onerror=null,o.parentNode&&o.parentNode.removeChild(o),i--,X()}}function V(e,t){if("function"==typeof fetch){i++;var r=!1,o=null,a=0;"undefined"!=typeof AbortController&&(o=new AbortController,c.add(o),a=setTimeout(function(){try{o.abort()}catch(e){}l(!1)},5e3));var s=!1;try{s=new URL(e,location.href).origin!==location.origin}catch(e){}var u={method:"GET",cache:"force-cache",credentials:s?"omit":"same-origin",mode:s?"no-cors":"cors"};s||(u.headers={Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",Purpose:"prefetch"}),s&&(u.referrerPolicy="no-referrer"),o&&(u.signal=o.signal);try{fetch(e,u).then(function(e){l(e&&(e.ok||304===e.status||"opaque"===e.type))}).catch(function(){l(!1)})}catch(e){l(!1)}}else n.delete(t);function l(e){r||(r=!0,a&&clearTimeout(a),o&&c.delete(o),e||n.delete(t),i--,X())}}!function(){if(t)try{y=t()}catch(e){}try{var e=document.createElement("link");e.relList&&"function"==typeof e.relList.supports&&(b=e.relList.supports("prefetch"))}catch(e){}var n=navigator.userAgent,r=navigator.userAgentData;if(r){m=!1;var o="Android"===r.platform;p=r.mobile||!1;for(var i=r.brands||[],a=0;a<i.length;a++){var c=i[a];if("Chromium"===c.brand||"Google Chrome"===c.brand){parseInt(c.version,10);break}}}else{m=/iPad|iPhone/.test(n)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1,o=/Android/.test(n),p=(m||o)&&Math.min(screen.width,screen.height)<768;var s=n.match(/Chrome\/(\d+)/);s&&parseInt(s[1],10)}p&&(x=20);var u=navigator.connection;u&&(w=u.effectiveType,g=u.saveData||!1),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",_):_()}();var Y=null;function Z(){o||Y||(Y=new IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&(Y.unobserve(e.target),G(e.target)&&H(e.target.href,e.target))})},{rootMargin:p?"100px":"200px"}),ee())}function ee(){Y&&document.querySelectorAll("a").forEach(function(e){G(e)&&Y.observe(e)})}var te=null,ne=null;return{__prefetchRu:!0,version:"1.1.1",preload:function(e){if(function(e){return!(!e||"string"!=typeof e||!(e=e.trim())||/^\/\//.test(e)||/^(javascript|data|vbscript|file):/i.test(e)||!(/^https?:\/\//i.test(e)||/^\//.test(e)||/^\./.test(e))&&/^[a-z][a-z0-9+.-]*:/i.test(e))}(e)){var t=document.createElement("a");t.setAttribute("href",e.trim()),G(t)&&H(t.href,t)}},destroy:function(){o=!0,a.length=0,f&&(clearTimeout(f),f=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),c.forEach(function(e){try{e.abort()}catch(e){}}),c.clear(),u&&((window.cancelIdleCallback||clearTimeout)(u),u=0),s.prefetch.length=s.prerender.length=s.crossOrigin.length=0;var e={capture:!0,passive:!0};document.removeEventListener("touchstart",z,e),document.removeEventListener("mouseover",K,e),document.removeEventListener("mousedown",F,e),window.removeEventListener("popstate",U),window.removeEventListener("hashchange",U),window.removeEventListener("pageshow",W),Y&&(Y.disconnect(),Y=null),te&&(te.disconnect(),te=null)},refresh:U}}({isBrowser:!0,getNonce:function(){try{var e=document.currentScript;if(e&&e.nonce)return e.nonce}catch(e){}return null}});window.PrefetchRu=e,window.Prefetch||(window.Prefetch=e)}}();
|
|
6
|
+
!function(){"use strict";if(!(window.PrefetchRu&&window.PrefetchRu.__prefetchRu||window.Prefetch&&window.Prefetch.__prefetchRu)){var e=function(e){var t=e&&e.getNonce;if(e&&!e.isBrowser||"undefined"==typeof window||"undefined"==typeof document)return{__prefetchRu:!0,version:"1.1.3",preload:function(){},destroy:function(){},refresh:function(){}};var n=new Set,r=new WeakMap,i=!1,o=0,a=[],c=new Set,s={prefetch:[],prerender:[],crossOrigin:[]},u=0,l="",d=0,f=0,h=null,p=!1,v=!1,m=null,g=!1,w=null,y=null,E=null,b=!1,L=65,x=80,T=50,O=!1,k=!1,A=!1,P=!1,S="none",N=!1,q=!1,C=!1,I=!1,M=!1,_=/(^|\/)(login|logout|auth|register|cart|basket|add|delete|remove)(\/|$|\.)/i,D=/\.(pdf|doc|docx|xls|xlsx|zip|rar|exe)($|\?)/i;function R(){if(!i){var e=document.body;if(e){l=location.origin+location.pathname+location.search,m=void 0!==window.BX?"bitrix":void 0!==window.B24||void 0!==window.BX24?"bitrix24":document.querySelector(".t-records")||void 0!==window.Tilda?"tilda":null;var t=e.dataset;if(O="prefetchAllowQueryString"in t||"instantAllowQueryString"in t,k="prefetchAllowExternalLinks"in t||"instantAllowExternalLinks"in t,A="prefetchWhitelist"in t||"instantWhitelist"in t,t.prefetchNonce&&(E=t.prefetchNonce),!E&&t.instantNonce&&(E=t.instantNonce),!v&&("prefetchSpecrules"in t||"instantSpecrules"in t)&&HTMLScriptElement.supports&&HTMLScriptElement.supports("speculationrules")){var n=t.prefetchSpecrules||t.instantSpecrules;"prerender"===n?(S="prerender",P=!0):"no"!==n&&(S="prefetch",P=!0)}N="prefetchSpecrulesFallback"in t||"instantSpecrulesFallback"in t,q="prefetchPrerenderAll"in t||"instantPrerenderAll"in t;var r=t.prefetchIntensity||t.instantIntensity;if("mousedown"===r)C=!0;else if("viewport"===r||"viewport-all"===r)("viewport-all"===r||p&&W())&&(I=!0);else if(r){var o=parseInt(r,10);!isNaN(o)&&o>=0&&(L=o)}p&&(x=Math.max(60,Math.min(L||0,150))),(M="prefetchObserveDom"in t||"instantObserveDom"in t)||"bitrix"!==m&&"tilda"!==m||(M=!0),window.addEventListener("popstate",U),window.addEventListener("hashchange",U),window.addEventListener("pageshow",z),"tilda"===m&&L<100&&(L=100);var a={capture:!0,passive:!0};document.addEventListener("touchstart",j,a),C?document.addEventListener("mousedown",Q,a):document.addEventListener("mouseover",F,a),I&&"undefined"==typeof IntersectionObserver&&(I=!1),I&&(window.requestIdleCallback||function(e){setTimeout(e,1)})(te,{timeout:1500}),M&&I&&"undefined"!=typeof MutationObserver&&(i||ne||(ne=new MutationObserver(function(e){if(ee){for(var t=[],n=0;n<e.length;n++)for(var r=e[n].addedNodes,i=0;i<r.length;i++){var o=r[i];if(1===o.nodeType)if("A"===o.tagName)t.push(o);else if(o.querySelectorAll)for(var a=o.querySelectorAll("a"),c=0;c<a.length;c++)t.push(a[c])}t.length>0&&(window.requestIdleCallback||function(e){setTimeout(e,1)})(function(){for(var e=0;e<t.length;e++)ee&&X(t[e])&&ee.observe(t[e])})}})).observe(document.body,{childList:!0,subtree:!0}))}}}function W(){return!g&&"slow-2g"!==w&&"2g"!==w&&"3g"!==w}function B(){y&&(w=y.effectiveType,g=y.saveData||!1)}function U(){l=location.origin+location.pathname+location.search}function z(e){e&&e.persisted&&U()}function K(e){return e?(e.nodeType&&1!==e.nodeType&&(e=e.parentElement),e&&"function"==typeof e.closest?e.closest("a"):null):null}function j(e){if(!(i||e&&!1===e.isTrusted)){d=Date.now();var t=K(e.target);if(X(t)){f&&(clearTimeout(f),f=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null);var n=!1;h=function(){n=!0,f&&(clearTimeout(f),f=0),document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null},document.addEventListener("touchmove",h,{capture:!0,passive:!0,once:!0}),document.addEventListener("scroll",h,{capture:!0,passive:!0,once:!0}),f=setTimeout(function(){h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),f=0,n||$(t.href,t)},x)}}}function F(e){if(!i&&!(e&&!1===e.isTrusted||d&&Date.now()-d<2500)){var t=K(e.target);if(t&&!r.has(t)&&X(t)){t.addEventListener("mouseleave",H,{passive:!0,once:!0});var n=setTimeout(function(){$(t.href,t),r.delete(t)},L);r.set(t,n)}}}function H(e){var t=e.currentTarget;if(t){var n=r.get(t);n&&(clearTimeout(n),r.delete(t))}}function Q(e){if(!i&&!(e&&!1===e.isTrusted||"number"==typeof e.button&&(1===e.button||2===e.button)||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||d&&Date.now()-d<2500)){var t=K(e.target);X(t)&&$(t.href,t)}}function X(e){if(!e)return!1;var t=e.getAttribute("href");if(null===t||""===t.trim())return!1;if(!e.href)return!1;if(e.target&&"_self"!==e.target)return!1;if(e.hasAttribute("download"))return!1;if("noPrefetch"in e.dataset||"prefetchNo"in e.dataset)return!1;if(A&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if("http:"!==e.protocol&&"https:"!==e.protocol)return!1;if("http:"===e.protocol&&"https:"===location.protocol)return!1;if(e.origin!==location.origin&&!k&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.search&&!O&&!("prefetch"in e.dataset)&&!("instant"in e.dataset))return!1;if(e.hash&&e.pathname+e.search===location.pathname+location.search)return!1;var r=e.origin+e.pathname+e.search;return r!==l&&!n.has(r)&&!!function(e){var t=e.pathname||"",n=e.hash||"";if("bitrix"===m||"bitrix24"===m){if(-1!==t.indexOf("/bitrix/")||e.search&&-1!==e.search.indexOf("sessid="))return!1;if(e.classList.contains("bx-ajax"))return!1}return("tilda"!==m||-1===n.indexOf("#popup:")&&-1===n.indexOf("#rec"))&&(!_.test(t)&&!D.test(t))}(e)&&!!function(e){var t=e.className||"",n=e.hostname||"";return!(-1!==t.indexOf("ym-")||"mc.yandex.ru"===n||"metrika.yandex.ru"===n||-1!==t.indexOf("ga-")||-1!==t.indexOf("gtm-")||"google-analytics.com"===n||n.endsWith(".google-analytics.com")||"googletagmanager.com"===n||n.endsWith(".googletagmanager.com")||-1!==t.indexOf("piwik")||-1!==t.indexOf("matomo")||"matomo.org"===n||n.endsWith(".matomo.org")||"piwik.org"===n||n.endsWith(".piwik.org"))}(e)}function $(e,t){if(!i&&W()){var r,c,s;if(t&&t.origin)c=t.origin+t.pathname+t.search,r=t.href.split("#")[0],s=t.origin!==location.origin;else{var u=function(e){try{var t=new URL(e,location.href),n=t.origin+t.pathname+t.search;return t.hash="",{requestUrl:t.href,key:n}}catch(t){return{requestUrl:e,key:e}}}(e);r=u.requestUrl,c=u.key,s=!1;try{s=new URL(r,location.href).origin!==location.origin}catch(e){}}if(!n.has(c)){n.size>=T&&n.delete(n.values().next().value),n.add(c);var l=function(e){return P&&"none"!==S?"prerender"!==S?S:q||A||e&&e.dataset&&("prefetchPrerender"in e.dataset||"instantPrerender"in e.dataset)?"prerender":"prefetch":"none"}(t);if(o>=4)return a.length>=50?void n.delete(c):void a.push({url:r,key:c,mode:l,crossOrigin:s});G(r,c,l,s)}}}function G(e,t,n,r){if("none"===n)v||!b||r?Z(e,t,r):Y(e,t,r);else{var i=!1;try{!function(e,t,n){if(n&&"prerender"===t&&(t="prefetch"),n?s.crossOrigin.push(e):"prerender"===t?s.prerender.push(e):s.prefetch.push(e),!u){var r=window.requestIdleCallback||function(e){setTimeout(e,1)};u=r(V,{timeout:50})}}(e,n,r),i=!0}catch(e){}!N&&i||(v||!b||r?Z(e,t,r):Y(e,t,r))}}function J(){for(;a.length>0&&o<4;){var e=a.shift();G(e.url,e.key,e.mode,e.crossOrigin)}}function V(){if(u=0,!i){var e=document.head;if(e){var t={};if(s.prefetch.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.prefetch}),s.prefetch=[]),s.prerender.length>0&&(t.prerender=t.prerender||[],t.prerender.push({source:"list",urls:s.prerender}),s.prerender=[]),s.crossOrigin.length>0&&(t.prefetch=t.prefetch||[],t.prefetch.push({source:"list",urls:s.crossOrigin,referrer_policy:"no-referrer",requires:["anonymous-client-ip-when-cross-origin"]}),s.crossOrigin=[]),t.prefetch||t.prerender){var n=document.createElement("script");n.type="speculationrules",E&&(n.nonce=E),n.textContent=JSON.stringify(t),e.appendChild(n),e.removeChild(n)}}}}function Y(e,t,r){var i=document.head;if(i){o++;var a=document.createElement("link");a.rel="prefetch",a.href=e,a.as="document";try{a.fetchPriority="low"}catch(e){}r&&(a.referrerPolicy="no-referrer",a.crossOrigin="anonymous");var c=setTimeout(function(){c=0,n.delete(t),s()},3e4);a.onload=s,a.onerror=function(){n.delete(t),s()},i.appendChild(a)}else n.delete(t);function s(){c&&(clearTimeout(c),c=0),a.onload=a.onerror=null,a.parentNode&&a.parentNode.removeChild(a),o--,J()}}function Z(e,t,r){if("function"==typeof fetch){o++;var i=!1,a=null,s=0;"undefined"!=typeof AbortController&&(a=new AbortController,c.add(a),s=setTimeout(function(){try{a.abort()}catch(e){}l(!1)},5e3));var u={method:"GET",cache:"force-cache",credentials:r?"omit":"same-origin",mode:r?"no-cors":"cors"};r||(u.headers={Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",Purpose:"prefetch"}),r&&(u.referrerPolicy="no-referrer"),a&&(u.signal=a.signal);try{fetch(e,u).then(function(e){l(e&&(e.ok||304===e.status||"opaque"===e.type))}).catch(function(){l(!1)})}catch(e){l(!1)}}else n.delete(t);function l(e){i||(i=!0,s&&clearTimeout(s),a&&c.delete(a),e||n.delete(t),o--,J())}}!function(){if(t)try{E=t()}catch(e){}try{var e=document.createElement("link");e.relList&&"function"==typeof e.relList.supports&&(b=e.relList.supports("prefetch"))}catch(e){}var n=navigator.userAgent,r=navigator.userAgentData;r?(v=!1,p=r.mobile||!1):(v=/iPad|iPhone/.test(n)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1,p=(v||/Android/.test(n))&&Math.min(screen.width,screen.height)<768),p&&(T=20),(y=navigator.connection)&&(w=y.effectiveType,g=y.saveData||!1,"function"==typeof y.addEventListener&&y.addEventListener("change",B)),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",R):R()}();var ee=null;function te(){i||ee||(ee=new IntersectionObserver(function(e){e.forEach(function(e){e.isIntersecting&&(ee.unobserve(e.target),X(e.target)&&$(e.target.href,e.target))})},{rootMargin:p?"100px":"200px"}))&&document.querySelectorAll("a").forEach(function(e){X(e)&&ee.observe(e)})}var ne=null;return{__prefetchRu:!0,version:"1.1.3",preload:function(e){if(function(e){return!(!e||"string"!=typeof e||!(e=e.trim())||/^\/\//.test(e)||/^(javascript|data|vbscript|file):/i.test(e)||!(/^https?:\/\//i.test(e)||/^\//.test(e)||/^\./.test(e))&&/^[a-z][a-z0-9+.-]*:/i.test(e))}(e)){var t=document.createElement("a");t.setAttribute("href",e.trim()),X(t)&&$(t.href,t)}},destroy:function(){i=!0,a.length=0,f&&(clearTimeout(f),f=0),h&&(document.removeEventListener("touchmove",h,!0),document.removeEventListener("scroll",h,!0),h=null),c.forEach(function(e){try{e.abort()}catch(e){}}),c.clear(),u&&((window.cancelIdleCallback||clearTimeout)(u),u=0),s.prefetch.length=s.prerender.length=s.crossOrigin.length=0;var e={capture:!0,passive:!0};document.removeEventListener("touchstart",j,e),document.removeEventListener("mouseover",F,e),document.removeEventListener("mousedown",Q,e),y&&"function"==typeof y.removeEventListener&&y.removeEventListener("change",B),window.removeEventListener("popstate",U),window.removeEventListener("hashchange",U),window.removeEventListener("pageshow",z),ee&&(ee.disconnect(),ee=null),ne&&(ne.disconnect(),ne=null)},refresh:U}}({isBrowser:!0,getNonce:function(){try{var e=document.currentScript;if(e&&e.nonce)return e.nonce}catch(e){}return null}});window.PrefetchRu=e,window.Prefetch||(window.Prefetch=e)}}();
|
package/package.json
CHANGED
package/prefetch.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* prefetch.ru v1.1.
|
|
2
|
+
* prefetch.ru v1.1.3 (ESM) - Мгновенная загрузка страниц
|
|
3
3
|
* © 2026 Сергей Макаров | MIT License
|
|
4
4
|
* https://prefetch.ru | https://github.com/prefetch-ru
|
|
5
5
|
*/
|
|
@@ -19,7 +19,7 @@ function createPrefetchCore(options) {
|
|
|
19
19
|
if (!isBrowser || typeof window === 'undefined' || typeof document === 'undefined') {
|
|
20
20
|
return {
|
|
21
21
|
__prefetchRu: true,
|
|
22
|
-
version: '1.1.
|
|
22
|
+
version: '1.1.3',
|
|
23
23
|
preload: function () {},
|
|
24
24
|
destroy: function () {},
|
|
25
25
|
refresh: function () {}
|
|
@@ -57,6 +57,9 @@ function createPrefetchCore(options) {
|
|
|
57
57
|
var saveData = false;
|
|
58
58
|
var connType = null;
|
|
59
59
|
|
|
60
|
+
// v1.1.3: ссылка на navigator.connection для снятия listener в destroy()
|
|
61
|
+
var conn = null;
|
|
62
|
+
|
|
60
63
|
// CSP / поддержка
|
|
61
64
|
var scriptNonce = null;
|
|
62
65
|
var supportsLinkPrefetch = false;
|
|
@@ -102,40 +105,29 @@ function createPrefetchCore(options) {
|
|
|
102
105
|
var ua = navigator.userAgent;
|
|
103
106
|
var uaData = navigator.userAgentData; // v1.0.12: UA-CH API (более надёжно в долгосрочной перспективе)
|
|
104
107
|
|
|
105
|
-
//
|
|
106
|
-
// v1.0.12: используем userAgentData если доступен, иначе fallback на UA
|
|
108
|
+
// v1.1.3: UA-CH API если доступен, иначе fallback на UA
|
|
107
109
|
if (uaData) {
|
|
108
110
|
// UA-CH API: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData
|
|
109
111
|
isIOS = false; // UA-CH пока не поддерживается на iOS
|
|
110
|
-
var isAndroid = uaData.platform === 'Android';
|
|
111
112
|
isMobile = uaData.mobile || false;
|
|
112
|
-
// Chromium версия через brands
|
|
113
|
-
var brands = uaData.brands || [];
|
|
114
|
-
for (var i = 0; i < brands.length; i++) {
|
|
115
|
-
var b = brands[i];
|
|
116
|
-
if (b.brand === 'Chromium' || b.brand === 'Google Chrome') {
|
|
117
|
-
parseInt(b.version, 10);
|
|
118
|
-
break
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
113
|
} else {
|
|
122
114
|
// Fallback на традиционный UA sniffing
|
|
123
115
|
isIOS =
|
|
124
116
|
/iPad|iPhone/.test(ua) ||
|
|
125
117
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
|
126
|
-
|
|
127
|
-
isMobile = (isIOS || isAndroid) && Math.min(screen.width, screen.height) < 768;
|
|
128
|
-
// Chromium версия из UA
|
|
129
|
-
var cm = ua.match(/Chrome\/(\d+)/);
|
|
130
|
-
if (cm) parseInt(cm[1], 10);
|
|
118
|
+
isMobile = (isIOS || /Android/.test(ua)) && Math.min(screen.width, screen.height) < 768;
|
|
131
119
|
}
|
|
132
120
|
if (isMobile) maxPreloads = 20;
|
|
133
121
|
|
|
134
122
|
// Сеть
|
|
135
|
-
|
|
123
|
+
conn = navigator.connection;
|
|
136
124
|
if (conn) {
|
|
137
125
|
connType = conn.effectiveType;
|
|
138
126
|
saveData = conn.saveData || false;
|
|
127
|
+
// v1.1.3: реактивное обновление при смене типа соединения
|
|
128
|
+
if (typeof conn.addEventListener === 'function') {
|
|
129
|
+
conn.addEventListener('change', onConnectionChange);
|
|
130
|
+
}
|
|
139
131
|
}
|
|
140
132
|
|
|
141
133
|
// Ждём DOM
|
|
@@ -268,6 +260,14 @@ function createPrefetchCore(options) {
|
|
|
268
260
|
return true
|
|
269
261
|
}
|
|
270
262
|
|
|
263
|
+
// v1.1.3: реактивное обновление состояния сети
|
|
264
|
+
function onConnectionChange() {
|
|
265
|
+
if (conn) {
|
|
266
|
+
connType = conn.effectiveType;
|
|
267
|
+
saveData = conn.saveData || false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
271
|
// v1.0.11: обновление currentKey при навигации (SPA-гибриды, pushState)
|
|
272
272
|
function updateCurrentKey() {
|
|
273
273
|
currentKey = location.origin + location.pathname + location.search;
|
|
@@ -438,14 +438,13 @@ function createPrefetchCore(options) {
|
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
function checkPlatform(a) {
|
|
441
|
-
var href = a.href;
|
|
442
|
-
|
|
443
441
|
// v1.0.11: используем свойства <a> напрямую вместо new URL() (perf)
|
|
444
442
|
var pathname = a.pathname || '';
|
|
445
443
|
var hash = a.hash || '';
|
|
446
444
|
|
|
447
445
|
if (platform === 'bitrix' || platform === 'bitrix24') {
|
|
448
|
-
|
|
446
|
+
// v1.1.3: проверяем pathname и search вместо href (точнее, без ложных срабатываний на домене)
|
|
447
|
+
if (pathname.indexOf('/bitrix/') !== -1 || (a.search && a.search.indexOf('sessid=') !== -1)) return false
|
|
449
448
|
if (a.classList.contains('bx-ajax')) return false
|
|
450
449
|
}
|
|
451
450
|
|
|
@@ -519,10 +518,20 @@ function createPrefetchCore(options) {
|
|
|
519
518
|
if (disabled) return
|
|
520
519
|
if (!isNetworkOk()) return
|
|
521
520
|
|
|
522
|
-
// v1.
|
|
523
|
-
|
|
524
|
-
var requestUrl
|
|
525
|
-
|
|
521
|
+
// v1.1.3: если a — реальный DOM-элемент, берём key и crossOrigin из его свойств
|
|
522
|
+
// без лишнего new URL() (parseUrl нужен только для публичного API с произвольным url)
|
|
523
|
+
var requestUrl, key, isCrossOrigin;
|
|
524
|
+
if (a && a.origin) {
|
|
525
|
+
key = a.origin + a.pathname + a.search;
|
|
526
|
+
requestUrl = a.href.split('#')[0]; // убираем hash для запроса
|
|
527
|
+
isCrossOrigin = a.origin !== location.origin;
|
|
528
|
+
} else {
|
|
529
|
+
var parsed = parseUrl(url);
|
|
530
|
+
requestUrl = parsed.requestUrl;
|
|
531
|
+
key = parsed.key;
|
|
532
|
+
isCrossOrigin = false;
|
|
533
|
+
try { isCrossOrigin = new URL(requestUrl, location.href).origin !== location.origin; } catch (e) {}
|
|
534
|
+
}
|
|
526
535
|
|
|
527
536
|
if (preloaded.has(key)) return
|
|
528
537
|
if (preloaded.size >= maxPreloads) {
|
|
@@ -537,19 +546,20 @@ function createPrefetchCore(options) {
|
|
|
537
546
|
// v1.0.11: лимит очереди — отбрасываем новые при переполнении
|
|
538
547
|
// v1.0.11: при drop удаляем ключ (иначе URL "навсегда" считается прогретым)
|
|
539
548
|
if (queue.length >= maxQueue) { preloaded.delete(key); return }
|
|
540
|
-
queue.push({ url: requestUrl, key: key, mode: mode });
|
|
549
|
+
queue.push({ url: requestUrl, key: key, mode: mode, crossOrigin: isCrossOrigin });
|
|
541
550
|
return
|
|
542
551
|
}
|
|
543
552
|
|
|
544
|
-
doPreload(requestUrl, key, mode);
|
|
553
|
+
doPreload(requestUrl, key, mode, isCrossOrigin);
|
|
545
554
|
}
|
|
546
555
|
|
|
547
|
-
|
|
556
|
+
// v1.1.3: isCrossOrigin передаётся через цепочку вызовов (без повторного new URL())
|
|
557
|
+
function doPreload(requestUrl, key, mode, isCrossOrigin) {
|
|
548
558
|
if (mode !== 'none') {
|
|
549
559
|
// v1.0.11: try/catch для preloadSpec — при строгом CSP/Trusted Types может выбросить исключение
|
|
550
560
|
var specOk = false;
|
|
551
561
|
try {
|
|
552
|
-
preloadSpec(requestUrl, mode);
|
|
562
|
+
preloadSpec(requestUrl, mode, isCrossOrigin);
|
|
553
563
|
specOk = true;
|
|
554
564
|
} catch (e) {
|
|
555
565
|
// Ошибка в Speculation Rules — fallback обязателен
|
|
@@ -558,31 +568,26 @@ function createPrefetchCore(options) {
|
|
|
558
568
|
// v1.0.11: fallback только если явно включён ИЛИ если SpecRules не удался
|
|
559
569
|
// По умолчанию fallback отключён для избежания двойного трафика
|
|
560
570
|
if (specRulesFallback || !specOk) {
|
|
561
|
-
|
|
562
|
-
|
|
571
|
+
// v1.1.1: cross-origin всегда через fetch (no-cors), <link> требует CORS headers
|
|
572
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
573
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
563
574
|
}
|
|
564
575
|
return
|
|
565
576
|
}
|
|
566
577
|
|
|
567
|
-
|
|
568
|
-
|
|
578
|
+
// v1.1.1: cross-origin всегда через fetch (no-cors), <link crossorigin=anonymous> требует CORS
|
|
579
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
580
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
569
581
|
}
|
|
570
582
|
|
|
571
583
|
function processQueue() {
|
|
572
584
|
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
573
585
|
var item = queue.shift();
|
|
574
|
-
doPreload(item.url, item.key, item.mode);
|
|
586
|
+
doPreload(item.url, item.key, item.mode, item.crossOrigin);
|
|
575
587
|
}
|
|
576
588
|
}
|
|
577
589
|
|
|
578
|
-
function preloadSpec(url, mode) {
|
|
579
|
-
// v1.0.13: для cross-origin проверяем и модифицируем правила
|
|
580
|
-
var isCrossOrigin = false;
|
|
581
|
-
try {
|
|
582
|
-
var u = new URL(url, location.href);
|
|
583
|
-
isCrossOrigin = u.origin !== location.origin;
|
|
584
|
-
} catch (e) {}
|
|
585
|
-
|
|
590
|
+
function preloadSpec(url, mode, isCrossOrigin) {
|
|
586
591
|
// v1.0.13: для cross-origin никогда не делаем prerender (только prefetch)
|
|
587
592
|
if (isCrossOrigin && mode === 'prerender') {
|
|
588
593
|
mode = 'prefetch';
|
|
@@ -616,18 +621,19 @@ function createPrefetchCore(options) {
|
|
|
616
621
|
|
|
617
622
|
var rules = {};
|
|
618
623
|
|
|
624
|
+
// v1.1.3: передаём массив напрямую и создаём новый (без лишнего .slice())
|
|
619
625
|
// Same-origin prefetch
|
|
620
626
|
if (specBuffer.prefetch.length > 0) {
|
|
621
627
|
rules.prefetch = rules.prefetch || [];
|
|
622
|
-
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch
|
|
623
|
-
specBuffer.prefetch
|
|
628
|
+
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch });
|
|
629
|
+
specBuffer.prefetch = [];
|
|
624
630
|
}
|
|
625
631
|
|
|
626
632
|
// Same-origin prerender
|
|
627
633
|
if (specBuffer.prerender.length > 0) {
|
|
628
634
|
rules.prerender = rules.prerender || [];
|
|
629
|
-
rules.prerender.push({ source: 'list', urls: specBuffer.prerender
|
|
630
|
-
specBuffer.prerender
|
|
635
|
+
rules.prerender.push({ source: 'list', urls: specBuffer.prerender });
|
|
636
|
+
specBuffer.prerender = [];
|
|
631
637
|
}
|
|
632
638
|
|
|
633
639
|
// Cross-origin (только prefetch, с privacy requirements)
|
|
@@ -635,11 +641,11 @@ function createPrefetchCore(options) {
|
|
|
635
641
|
rules.prefetch = rules.prefetch || [];
|
|
636
642
|
rules.prefetch.push({
|
|
637
643
|
source: 'list',
|
|
638
|
-
urls: specBuffer.crossOrigin
|
|
644
|
+
urls: specBuffer.crossOrigin,
|
|
639
645
|
referrer_policy: 'no-referrer',
|
|
640
646
|
requires: ['anonymous-client-ip-when-cross-origin']
|
|
641
647
|
});
|
|
642
|
-
specBuffer.crossOrigin
|
|
648
|
+
specBuffer.crossOrigin = [];
|
|
643
649
|
}
|
|
644
650
|
|
|
645
651
|
// Если нет правил — выходим
|
|
@@ -654,7 +660,7 @@ function createPrefetchCore(options) {
|
|
|
654
660
|
head.removeChild(s);
|
|
655
661
|
}
|
|
656
662
|
|
|
657
|
-
function preloadLink(url, key) {
|
|
663
|
+
function preloadLink(url, key, isCrossOrigin) {
|
|
658
664
|
var head = document.head;
|
|
659
665
|
// v1.0.10: если head недоступен, откатываем ключ
|
|
660
666
|
if (!head) { preloaded.delete(key); return }
|
|
@@ -668,13 +674,10 @@ function createPrefetchCore(options) {
|
|
|
668
674
|
try { l.fetchPriority = 'low'; } catch (e) {}
|
|
669
675
|
|
|
670
676
|
// v1.0.11: для cross-origin: referrerPolicy + crossOrigin
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
676
|
-
}
|
|
677
|
-
} catch (e) {}
|
|
677
|
+
if (isCrossOrigin) {
|
|
678
|
+
l.referrerPolicy = 'no-referrer';
|
|
679
|
+
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
680
|
+
}
|
|
678
681
|
|
|
679
682
|
// v1.0.13: safety timeout — предохранитель если onload/onerror не сработают
|
|
680
683
|
// (экзотические браузеры, сетевые ошибки без событий)
|
|
@@ -698,7 +701,7 @@ function createPrefetchCore(options) {
|
|
|
698
701
|
head.appendChild(l);
|
|
699
702
|
}
|
|
700
703
|
|
|
701
|
-
function preloadFetch(url, key) {
|
|
704
|
+
function preloadFetch(url, key, isCrossOrigin) {
|
|
702
705
|
// v1.0.10: если fetch недоступен, откатываем ключ
|
|
703
706
|
if (typeof fetch !== 'function') { preloaded.delete(key); return }
|
|
704
707
|
|
|
@@ -731,12 +734,6 @@ function createPrefetchCore(options) {
|
|
|
731
734
|
}, 5000);
|
|
732
735
|
}
|
|
733
736
|
|
|
734
|
-
// v1.0.13: определяем cross-origin для корректных настроек fetch
|
|
735
|
-
var isCrossOrigin = false;
|
|
736
|
-
try {
|
|
737
|
-
isCrossOrigin = new URL(url, location.href).origin !== location.origin;
|
|
738
|
-
} catch (e) {}
|
|
739
|
-
|
|
740
737
|
var opts = {
|
|
741
738
|
method: 'GET',
|
|
742
739
|
cache: 'force-cache',
|
|
@@ -806,30 +803,36 @@ function createPrefetchCore(options) {
|
|
|
806
803
|
|
|
807
804
|
// Mutation Observer
|
|
808
805
|
var mutObserver = null;
|
|
809
|
-
var mutTimer = null;
|
|
810
806
|
|
|
811
807
|
function startMutationObserver() {
|
|
812
808
|
// v1.0.11: защита от вызова после destroy()
|
|
813
809
|
if (disabled) return
|
|
814
810
|
if (mutObserver) return
|
|
815
811
|
mutObserver = new MutationObserver(function (muts) {
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
812
|
+
if (!vpObserver) return
|
|
813
|
+
// v1.1.3: собираем только новые ссылки из addedNodes (не пересканируем весь DOM)
|
|
814
|
+
var pending = [];
|
|
815
|
+
for (var i = 0; i < muts.length; i++) {
|
|
819
816
|
var nodes = muts[i].addedNodes;
|
|
820
817
|
for (var j = 0; j < nodes.length; j++) {
|
|
821
818
|
var n = nodes[j];
|
|
822
|
-
if (n.nodeType
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
819
|
+
if (n.nodeType !== 1) continue
|
|
820
|
+
if (n.tagName === 'A') {
|
|
821
|
+
pending.push(n);
|
|
822
|
+
} else if (n.querySelectorAll) {
|
|
823
|
+
var links = n.querySelectorAll('a');
|
|
824
|
+
for (var k = 0; k < links.length; k++) pending.push(links[k]);
|
|
827
825
|
}
|
|
828
826
|
}
|
|
829
827
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
828
|
+
// v1.1.3: обрабатываем через rIC чтобы не блокировать UI при массовых вставках DOM
|
|
829
|
+
if (pending.length > 0) {
|
|
830
|
+
var rIC = window.requestIdleCallback || function (cb) { setTimeout(cb, 1); };
|
|
831
|
+
rIC(function () {
|
|
832
|
+
for (var i = 0; i < pending.length; i++) {
|
|
833
|
+
if (vpObserver && canPreload(pending[i])) vpObserver.observe(pending[i]);
|
|
834
|
+
}
|
|
835
|
+
});
|
|
833
836
|
}
|
|
834
837
|
});
|
|
835
838
|
mutObserver.observe(document.body, { childList: true, subtree: true });
|
|
@@ -890,6 +893,11 @@ function createPrefetchCore(options) {
|
|
|
890
893
|
document.removeEventListener('mouseover', onMouseOver, opts);
|
|
891
894
|
document.removeEventListener('mousedown', onMouseDown, opts);
|
|
892
895
|
|
|
896
|
+
// v1.1.3: снимаем listener сети
|
|
897
|
+
if (conn && typeof conn.removeEventListener === 'function') {
|
|
898
|
+
conn.removeEventListener('change', onConnectionChange);
|
|
899
|
+
}
|
|
900
|
+
|
|
893
901
|
// v1.0.11: снимаем слушатели навигации
|
|
894
902
|
window.removeEventListener('popstate', updateCurrentKey);
|
|
895
903
|
window.removeEventListener('hashchange', updateCurrentKey);
|
|
@@ -909,7 +917,7 @@ function createPrefetchCore(options) {
|
|
|
909
917
|
// Публичный API
|
|
910
918
|
var api = {
|
|
911
919
|
__prefetchRu: true,
|
|
912
|
-
version: '1.1.
|
|
920
|
+
version: '1.1.3',
|
|
913
921
|
preload: function (url) {
|
|
914
922
|
// v1.0.11: валидация URL + прогон через canPreload() (консистентность с авто-режимом)
|
|
915
923
|
if (!isValidPrefetchUrl(url)) return
|
package/prefetch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* prefetch.ru v1.1.
|
|
2
|
+
* prefetch.ru v1.1.3 - Мгновенная загрузка страниц
|
|
3
3
|
* © 2026 Сергей Макаров | MIT License
|
|
4
4
|
* https://prefetch.ru | https://github.com/prefetch-ru
|
|
5
5
|
*/
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
if (!isBrowser || typeof window === 'undefined' || typeof document === 'undefined') {
|
|
23
23
|
return {
|
|
24
24
|
__prefetchRu: true,
|
|
25
|
-
version: '1.1.
|
|
25
|
+
version: '1.1.3',
|
|
26
26
|
preload: function () {},
|
|
27
27
|
destroy: function () {},
|
|
28
28
|
refresh: function () {}
|
|
@@ -60,6 +60,9 @@
|
|
|
60
60
|
var saveData = false;
|
|
61
61
|
var connType = null;
|
|
62
62
|
|
|
63
|
+
// v1.1.3: ссылка на navigator.connection для снятия listener в destroy()
|
|
64
|
+
var conn = null;
|
|
65
|
+
|
|
63
66
|
// CSP / поддержка
|
|
64
67
|
var scriptNonce = null;
|
|
65
68
|
var supportsLinkPrefetch = false;
|
|
@@ -105,40 +108,29 @@
|
|
|
105
108
|
var ua = navigator.userAgent;
|
|
106
109
|
var uaData = navigator.userAgentData; // v1.0.12: UA-CH API (более надёжно в долгосрочной перспективе)
|
|
107
110
|
|
|
108
|
-
//
|
|
109
|
-
// v1.0.12: используем userAgentData если доступен, иначе fallback на UA
|
|
111
|
+
// v1.1.3: UA-CH API если доступен, иначе fallback на UA
|
|
110
112
|
if (uaData) {
|
|
111
113
|
// UA-CH API: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData
|
|
112
114
|
isIOS = false; // UA-CH пока не поддерживается на iOS
|
|
113
|
-
var isAndroid = uaData.platform === 'Android';
|
|
114
115
|
isMobile = uaData.mobile || false;
|
|
115
|
-
// Chromium версия через brands
|
|
116
|
-
var brands = uaData.brands || [];
|
|
117
|
-
for (var i = 0; i < brands.length; i++) {
|
|
118
|
-
var b = brands[i];
|
|
119
|
-
if (b.brand === 'Chromium' || b.brand === 'Google Chrome') {
|
|
120
|
-
parseInt(b.version, 10);
|
|
121
|
-
break
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
116
|
} else {
|
|
125
117
|
// Fallback на традиционный UA sniffing
|
|
126
118
|
isIOS =
|
|
127
119
|
/iPad|iPhone/.test(ua) ||
|
|
128
120
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
|
129
|
-
|
|
130
|
-
isMobile = (isIOS || isAndroid) && Math.min(screen.width, screen.height) < 768;
|
|
131
|
-
// Chromium версия из UA
|
|
132
|
-
var cm = ua.match(/Chrome\/(\d+)/);
|
|
133
|
-
if (cm) parseInt(cm[1], 10);
|
|
121
|
+
isMobile = (isIOS || /Android/.test(ua)) && Math.min(screen.width, screen.height) < 768;
|
|
134
122
|
}
|
|
135
123
|
if (isMobile) maxPreloads = 20;
|
|
136
124
|
|
|
137
125
|
// Сеть
|
|
138
|
-
|
|
126
|
+
conn = navigator.connection;
|
|
139
127
|
if (conn) {
|
|
140
128
|
connType = conn.effectiveType;
|
|
141
129
|
saveData = conn.saveData || false;
|
|
130
|
+
// v1.1.3: реактивное обновление при смене типа соединения
|
|
131
|
+
if (typeof conn.addEventListener === 'function') {
|
|
132
|
+
conn.addEventListener('change', onConnectionChange);
|
|
133
|
+
}
|
|
142
134
|
}
|
|
143
135
|
|
|
144
136
|
// Ждём DOM
|
|
@@ -271,6 +263,14 @@
|
|
|
271
263
|
return true
|
|
272
264
|
}
|
|
273
265
|
|
|
266
|
+
// v1.1.3: реактивное обновление состояния сети
|
|
267
|
+
function onConnectionChange() {
|
|
268
|
+
if (conn) {
|
|
269
|
+
connType = conn.effectiveType;
|
|
270
|
+
saveData = conn.saveData || false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
274
|
// v1.0.11: обновление currentKey при навигации (SPA-гибриды, pushState)
|
|
275
275
|
function updateCurrentKey() {
|
|
276
276
|
currentKey = location.origin + location.pathname + location.search;
|
|
@@ -441,14 +441,13 @@
|
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
function checkPlatform(a) {
|
|
444
|
-
var href = a.href;
|
|
445
|
-
|
|
446
444
|
// v1.0.11: используем свойства <a> напрямую вместо new URL() (perf)
|
|
447
445
|
var pathname = a.pathname || '';
|
|
448
446
|
var hash = a.hash || '';
|
|
449
447
|
|
|
450
448
|
if (platform === 'bitrix' || platform === 'bitrix24') {
|
|
451
|
-
|
|
449
|
+
// v1.1.3: проверяем pathname и search вместо href (точнее, без ложных срабатываний на домене)
|
|
450
|
+
if (pathname.indexOf('/bitrix/') !== -1 || (a.search && a.search.indexOf('sessid=') !== -1)) return false
|
|
452
451
|
if (a.classList.contains('bx-ajax')) return false
|
|
453
452
|
}
|
|
454
453
|
|
|
@@ -522,10 +521,20 @@
|
|
|
522
521
|
if (disabled) return
|
|
523
522
|
if (!isNetworkOk()) return
|
|
524
523
|
|
|
525
|
-
// v1.
|
|
526
|
-
|
|
527
|
-
var requestUrl
|
|
528
|
-
|
|
524
|
+
// v1.1.3: если a — реальный DOM-элемент, берём key и crossOrigin из его свойств
|
|
525
|
+
// без лишнего new URL() (parseUrl нужен только для публичного API с произвольным url)
|
|
526
|
+
var requestUrl, key, isCrossOrigin;
|
|
527
|
+
if (a && a.origin) {
|
|
528
|
+
key = a.origin + a.pathname + a.search;
|
|
529
|
+
requestUrl = a.href.split('#')[0]; // убираем hash для запроса
|
|
530
|
+
isCrossOrigin = a.origin !== location.origin;
|
|
531
|
+
} else {
|
|
532
|
+
var parsed = parseUrl(url);
|
|
533
|
+
requestUrl = parsed.requestUrl;
|
|
534
|
+
key = parsed.key;
|
|
535
|
+
isCrossOrigin = false;
|
|
536
|
+
try { isCrossOrigin = new URL(requestUrl, location.href).origin !== location.origin; } catch (e) {}
|
|
537
|
+
}
|
|
529
538
|
|
|
530
539
|
if (preloaded.has(key)) return
|
|
531
540
|
if (preloaded.size >= maxPreloads) {
|
|
@@ -540,19 +549,20 @@
|
|
|
540
549
|
// v1.0.11: лимит очереди — отбрасываем новые при переполнении
|
|
541
550
|
// v1.0.11: при drop удаляем ключ (иначе URL "навсегда" считается прогретым)
|
|
542
551
|
if (queue.length >= maxQueue) { preloaded.delete(key); return }
|
|
543
|
-
queue.push({ url: requestUrl, key: key, mode: mode });
|
|
552
|
+
queue.push({ url: requestUrl, key: key, mode: mode, crossOrigin: isCrossOrigin });
|
|
544
553
|
return
|
|
545
554
|
}
|
|
546
555
|
|
|
547
|
-
doPreload(requestUrl, key, mode);
|
|
556
|
+
doPreload(requestUrl, key, mode, isCrossOrigin);
|
|
548
557
|
}
|
|
549
558
|
|
|
550
|
-
|
|
559
|
+
// v1.1.3: isCrossOrigin передаётся через цепочку вызовов (без повторного new URL())
|
|
560
|
+
function doPreload(requestUrl, key, mode, isCrossOrigin) {
|
|
551
561
|
if (mode !== 'none') {
|
|
552
562
|
// v1.0.11: try/catch для preloadSpec — при строгом CSP/Trusted Types может выбросить исключение
|
|
553
563
|
var specOk = false;
|
|
554
564
|
try {
|
|
555
|
-
preloadSpec(requestUrl, mode);
|
|
565
|
+
preloadSpec(requestUrl, mode, isCrossOrigin);
|
|
556
566
|
specOk = true;
|
|
557
567
|
} catch (e) {
|
|
558
568
|
// Ошибка в Speculation Rules — fallback обязателен
|
|
@@ -561,31 +571,26 @@
|
|
|
561
571
|
// v1.0.11: fallback только если явно включён ИЛИ если SpecRules не удался
|
|
562
572
|
// По умолчанию fallback отключён для избежания двойного трафика
|
|
563
573
|
if (specRulesFallback || !specOk) {
|
|
564
|
-
|
|
565
|
-
|
|
574
|
+
// v1.1.1: cross-origin всегда через fetch (no-cors), <link> требует CORS headers
|
|
575
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
576
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
566
577
|
}
|
|
567
578
|
return
|
|
568
579
|
}
|
|
569
580
|
|
|
570
|
-
|
|
571
|
-
|
|
581
|
+
// v1.1.1: cross-origin всегда через fetch (no-cors), <link crossorigin=anonymous> требует CORS
|
|
582
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
583
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
572
584
|
}
|
|
573
585
|
|
|
574
586
|
function processQueue() {
|
|
575
587
|
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
576
588
|
var item = queue.shift();
|
|
577
|
-
doPreload(item.url, item.key, item.mode);
|
|
589
|
+
doPreload(item.url, item.key, item.mode, item.crossOrigin);
|
|
578
590
|
}
|
|
579
591
|
}
|
|
580
592
|
|
|
581
|
-
function preloadSpec(url, mode) {
|
|
582
|
-
// v1.0.13: для cross-origin проверяем и модифицируем правила
|
|
583
|
-
var isCrossOrigin = false;
|
|
584
|
-
try {
|
|
585
|
-
var u = new URL(url, location.href);
|
|
586
|
-
isCrossOrigin = u.origin !== location.origin;
|
|
587
|
-
} catch (e) {}
|
|
588
|
-
|
|
593
|
+
function preloadSpec(url, mode, isCrossOrigin) {
|
|
589
594
|
// v1.0.13: для cross-origin никогда не делаем prerender (только prefetch)
|
|
590
595
|
if (isCrossOrigin && mode === 'prerender') {
|
|
591
596
|
mode = 'prefetch';
|
|
@@ -619,18 +624,19 @@
|
|
|
619
624
|
|
|
620
625
|
var rules = {};
|
|
621
626
|
|
|
627
|
+
// v1.1.3: передаём массив напрямую и создаём новый (без лишнего .slice())
|
|
622
628
|
// Same-origin prefetch
|
|
623
629
|
if (specBuffer.prefetch.length > 0) {
|
|
624
630
|
rules.prefetch = rules.prefetch || [];
|
|
625
|
-
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch
|
|
626
|
-
specBuffer.prefetch
|
|
631
|
+
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch });
|
|
632
|
+
specBuffer.prefetch = [];
|
|
627
633
|
}
|
|
628
634
|
|
|
629
635
|
// Same-origin prerender
|
|
630
636
|
if (specBuffer.prerender.length > 0) {
|
|
631
637
|
rules.prerender = rules.prerender || [];
|
|
632
|
-
rules.prerender.push({ source: 'list', urls: specBuffer.prerender
|
|
633
|
-
specBuffer.prerender
|
|
638
|
+
rules.prerender.push({ source: 'list', urls: specBuffer.prerender });
|
|
639
|
+
specBuffer.prerender = [];
|
|
634
640
|
}
|
|
635
641
|
|
|
636
642
|
// Cross-origin (только prefetch, с privacy requirements)
|
|
@@ -638,11 +644,11 @@
|
|
|
638
644
|
rules.prefetch = rules.prefetch || [];
|
|
639
645
|
rules.prefetch.push({
|
|
640
646
|
source: 'list',
|
|
641
|
-
urls: specBuffer.crossOrigin
|
|
647
|
+
urls: specBuffer.crossOrigin,
|
|
642
648
|
referrer_policy: 'no-referrer',
|
|
643
649
|
requires: ['anonymous-client-ip-when-cross-origin']
|
|
644
650
|
});
|
|
645
|
-
specBuffer.crossOrigin
|
|
651
|
+
specBuffer.crossOrigin = [];
|
|
646
652
|
}
|
|
647
653
|
|
|
648
654
|
// Если нет правил — выходим
|
|
@@ -657,7 +663,7 @@
|
|
|
657
663
|
head.removeChild(s);
|
|
658
664
|
}
|
|
659
665
|
|
|
660
|
-
function preloadLink(url, key) {
|
|
666
|
+
function preloadLink(url, key, isCrossOrigin) {
|
|
661
667
|
var head = document.head;
|
|
662
668
|
// v1.0.10: если head недоступен, откатываем ключ
|
|
663
669
|
if (!head) { preloaded.delete(key); return }
|
|
@@ -671,13 +677,10 @@
|
|
|
671
677
|
try { l.fetchPriority = 'low'; } catch (e) {}
|
|
672
678
|
|
|
673
679
|
// v1.0.11: для cross-origin: referrerPolicy + crossOrigin
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
679
|
-
}
|
|
680
|
-
} catch (e) {}
|
|
680
|
+
if (isCrossOrigin) {
|
|
681
|
+
l.referrerPolicy = 'no-referrer';
|
|
682
|
+
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
683
|
+
}
|
|
681
684
|
|
|
682
685
|
// v1.0.13: safety timeout — предохранитель если onload/onerror не сработают
|
|
683
686
|
// (экзотические браузеры, сетевые ошибки без событий)
|
|
@@ -701,7 +704,7 @@
|
|
|
701
704
|
head.appendChild(l);
|
|
702
705
|
}
|
|
703
706
|
|
|
704
|
-
function preloadFetch(url, key) {
|
|
707
|
+
function preloadFetch(url, key, isCrossOrigin) {
|
|
705
708
|
// v1.0.10: если fetch недоступен, откатываем ключ
|
|
706
709
|
if (typeof fetch !== 'function') { preloaded.delete(key); return }
|
|
707
710
|
|
|
@@ -734,12 +737,6 @@
|
|
|
734
737
|
}, 5000);
|
|
735
738
|
}
|
|
736
739
|
|
|
737
|
-
// v1.0.13: определяем cross-origin для корректных настроек fetch
|
|
738
|
-
var isCrossOrigin = false;
|
|
739
|
-
try {
|
|
740
|
-
isCrossOrigin = new URL(url, location.href).origin !== location.origin;
|
|
741
|
-
} catch (e) {}
|
|
742
|
-
|
|
743
740
|
var opts = {
|
|
744
741
|
method: 'GET',
|
|
745
742
|
cache: 'force-cache',
|
|
@@ -809,30 +806,36 @@
|
|
|
809
806
|
|
|
810
807
|
// Mutation Observer
|
|
811
808
|
var mutObserver = null;
|
|
812
|
-
var mutTimer = null;
|
|
813
809
|
|
|
814
810
|
function startMutationObserver() {
|
|
815
811
|
// v1.0.11: защита от вызова после destroy()
|
|
816
812
|
if (disabled) return
|
|
817
813
|
if (mutObserver) return
|
|
818
814
|
mutObserver = new MutationObserver(function (muts) {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
815
|
+
if (!vpObserver) return
|
|
816
|
+
// v1.1.3: собираем только новые ссылки из addedNodes (не пересканируем весь DOM)
|
|
817
|
+
var pending = [];
|
|
818
|
+
for (var i = 0; i < muts.length; i++) {
|
|
822
819
|
var nodes = muts[i].addedNodes;
|
|
823
820
|
for (var j = 0; j < nodes.length; j++) {
|
|
824
821
|
var n = nodes[j];
|
|
825
|
-
if (n.nodeType
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
822
|
+
if (n.nodeType !== 1) continue
|
|
823
|
+
if (n.tagName === 'A') {
|
|
824
|
+
pending.push(n);
|
|
825
|
+
} else if (n.querySelectorAll) {
|
|
826
|
+
var links = n.querySelectorAll('a');
|
|
827
|
+
for (var k = 0; k < links.length; k++) pending.push(links[k]);
|
|
830
828
|
}
|
|
831
829
|
}
|
|
832
830
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
831
|
+
// v1.1.3: обрабатываем через rIC чтобы не блокировать UI при массовых вставках DOM
|
|
832
|
+
if (pending.length > 0) {
|
|
833
|
+
var rIC = window.requestIdleCallback || function (cb) { setTimeout(cb, 1); };
|
|
834
|
+
rIC(function () {
|
|
835
|
+
for (var i = 0; i < pending.length; i++) {
|
|
836
|
+
if (vpObserver && canPreload(pending[i])) vpObserver.observe(pending[i]);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
836
839
|
}
|
|
837
840
|
});
|
|
838
841
|
mutObserver.observe(document.body, { childList: true, subtree: true });
|
|
@@ -893,6 +896,11 @@
|
|
|
893
896
|
document.removeEventListener('mouseover', onMouseOver, opts);
|
|
894
897
|
document.removeEventListener('mousedown', onMouseDown, opts);
|
|
895
898
|
|
|
899
|
+
// v1.1.3: снимаем listener сети
|
|
900
|
+
if (conn && typeof conn.removeEventListener === 'function') {
|
|
901
|
+
conn.removeEventListener('change', onConnectionChange);
|
|
902
|
+
}
|
|
903
|
+
|
|
896
904
|
// v1.0.11: снимаем слушатели навигации
|
|
897
905
|
window.removeEventListener('popstate', updateCurrentKey);
|
|
898
906
|
window.removeEventListener('hashchange', updateCurrentKey);
|
|
@@ -912,7 +920,7 @@
|
|
|
912
920
|
// Публичный API
|
|
913
921
|
var api = {
|
|
914
922
|
__prefetchRu: true,
|
|
915
|
-
version: '1.1.
|
|
923
|
+
version: '1.1.3',
|
|
916
924
|
preload: function (url) {
|
|
917
925
|
// v1.0.11: валидация URL + прогон через canPreload() (консистентность с авто-режимом)
|
|
918
926
|
if (!isValidPrefetchUrl(url)) return
|
package/src/core.js
CHANGED
|
@@ -49,11 +49,13 @@ export function createPrefetchCore(options) {
|
|
|
49
49
|
|
|
50
50
|
var isMobile = false
|
|
51
51
|
var isIOS = false
|
|
52
|
-
var chromiumVer = null
|
|
53
52
|
var platform = null
|
|
54
53
|
var saveData = false
|
|
55
54
|
var connType = null
|
|
56
55
|
|
|
56
|
+
// v1.1.3: ссылка на navigator.connection для снятия listener в destroy()
|
|
57
|
+
var conn = null
|
|
58
|
+
|
|
57
59
|
// CSP / поддержка
|
|
58
60
|
var scriptNonce = null
|
|
59
61
|
var supportsLinkPrefetch = false
|
|
@@ -99,40 +101,29 @@ export function createPrefetchCore(options) {
|
|
|
99
101
|
var ua = navigator.userAgent
|
|
100
102
|
var uaData = navigator.userAgentData // v1.0.12: UA-CH API (более надёжно в долгосрочной перспективе)
|
|
101
103
|
|
|
102
|
-
//
|
|
103
|
-
// v1.0.12: используем userAgentData если доступен, иначе fallback на UA
|
|
104
|
+
// v1.1.3: UA-CH API если доступен, иначе fallback на UA
|
|
104
105
|
if (uaData) {
|
|
105
106
|
// UA-CH API: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData
|
|
106
107
|
isIOS = false // UA-CH пока не поддерживается на iOS
|
|
107
|
-
var isAndroid = uaData.platform === 'Android'
|
|
108
108
|
isMobile = uaData.mobile || false
|
|
109
|
-
// Chromium версия через brands
|
|
110
|
-
var brands = uaData.brands || []
|
|
111
|
-
for (var i = 0; i < brands.length; i++) {
|
|
112
|
-
var b = brands[i]
|
|
113
|
-
if (b.brand === 'Chromium' || b.brand === 'Google Chrome') {
|
|
114
|
-
chromiumVer = parseInt(b.version, 10)
|
|
115
|
-
break
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
109
|
} else {
|
|
119
110
|
// Fallback на традиционный UA sniffing
|
|
120
111
|
isIOS =
|
|
121
112
|
/iPad|iPhone/.test(ua) ||
|
|
122
113
|
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
|
|
123
|
-
|
|
124
|
-
isMobile = (isIOS || isAndroid) && Math.min(screen.width, screen.height) < 768
|
|
125
|
-
// Chromium версия из UA
|
|
126
|
-
var cm = ua.match(/Chrome\/(\d+)/)
|
|
127
|
-
if (cm) chromiumVer = parseInt(cm[1], 10)
|
|
114
|
+
isMobile = (isIOS || /Android/.test(ua)) && Math.min(screen.width, screen.height) < 768
|
|
128
115
|
}
|
|
129
116
|
if (isMobile) maxPreloads = 20
|
|
130
117
|
|
|
131
118
|
// Сеть
|
|
132
|
-
|
|
119
|
+
conn = navigator.connection
|
|
133
120
|
if (conn) {
|
|
134
121
|
connType = conn.effectiveType
|
|
135
122
|
saveData = conn.saveData || false
|
|
123
|
+
// v1.1.3: реактивное обновление при смене типа соединения
|
|
124
|
+
if (typeof conn.addEventListener === 'function') {
|
|
125
|
+
conn.addEventListener('change', onConnectionChange)
|
|
126
|
+
}
|
|
136
127
|
}
|
|
137
128
|
|
|
138
129
|
// Ждём DOM
|
|
@@ -265,6 +256,14 @@ export function createPrefetchCore(options) {
|
|
|
265
256
|
return true
|
|
266
257
|
}
|
|
267
258
|
|
|
259
|
+
// v1.1.3: реактивное обновление состояния сети
|
|
260
|
+
function onConnectionChange() {
|
|
261
|
+
if (conn) {
|
|
262
|
+
connType = conn.effectiveType
|
|
263
|
+
saveData = conn.saveData || false
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
268
267
|
// v1.0.11: обновление currentKey при навигации (SPA-гибриды, pushState)
|
|
269
268
|
function updateCurrentKey() {
|
|
270
269
|
currentKey = location.origin + location.pathname + location.search
|
|
@@ -435,14 +434,13 @@ export function createPrefetchCore(options) {
|
|
|
435
434
|
}
|
|
436
435
|
|
|
437
436
|
function checkPlatform(a) {
|
|
438
|
-
var href = a.href
|
|
439
|
-
|
|
440
437
|
// v1.0.11: используем свойства <a> напрямую вместо new URL() (perf)
|
|
441
438
|
var pathname = a.pathname || ''
|
|
442
439
|
var hash = a.hash || ''
|
|
443
440
|
|
|
444
441
|
if (platform === 'bitrix' || platform === 'bitrix24') {
|
|
445
|
-
|
|
442
|
+
// v1.1.3: проверяем pathname и search вместо href (точнее, без ложных срабатываний на домене)
|
|
443
|
+
if (pathname.indexOf('/bitrix/') !== -1 || (a.search && a.search.indexOf('sessid=') !== -1)) return false
|
|
446
444
|
if (a.classList.contains('bx-ajax')) return false
|
|
447
445
|
}
|
|
448
446
|
|
|
@@ -516,10 +514,20 @@ export function createPrefetchCore(options) {
|
|
|
516
514
|
if (disabled) return
|
|
517
515
|
if (!isNetworkOk()) return
|
|
518
516
|
|
|
519
|
-
// v1.
|
|
520
|
-
|
|
521
|
-
var requestUrl
|
|
522
|
-
|
|
517
|
+
// v1.1.3: если a — реальный DOM-элемент, берём key и crossOrigin из его свойств
|
|
518
|
+
// без лишнего new URL() (parseUrl нужен только для публичного API с произвольным url)
|
|
519
|
+
var requestUrl, key, isCrossOrigin
|
|
520
|
+
if (a && a.origin) {
|
|
521
|
+
key = a.origin + a.pathname + a.search
|
|
522
|
+
requestUrl = a.href.split('#')[0] // убираем hash для запроса
|
|
523
|
+
isCrossOrigin = a.origin !== location.origin
|
|
524
|
+
} else {
|
|
525
|
+
var parsed = parseUrl(url)
|
|
526
|
+
requestUrl = parsed.requestUrl
|
|
527
|
+
key = parsed.key
|
|
528
|
+
isCrossOrigin = false
|
|
529
|
+
try { isCrossOrigin = new URL(requestUrl, location.href).origin !== location.origin } catch (e) {}
|
|
530
|
+
}
|
|
523
531
|
|
|
524
532
|
if (preloaded.has(key)) return
|
|
525
533
|
if (preloaded.size >= maxPreloads) {
|
|
@@ -534,19 +542,20 @@ export function createPrefetchCore(options) {
|
|
|
534
542
|
// v1.0.11: лимит очереди — отбрасываем новые при переполнении
|
|
535
543
|
// v1.0.11: при drop удаляем ключ (иначе URL "навсегда" считается прогретым)
|
|
536
544
|
if (queue.length >= maxQueue) { preloaded.delete(key); return }
|
|
537
|
-
queue.push({ url: requestUrl, key: key, mode: mode })
|
|
545
|
+
queue.push({ url: requestUrl, key: key, mode: mode, crossOrigin: isCrossOrigin })
|
|
538
546
|
return
|
|
539
547
|
}
|
|
540
548
|
|
|
541
|
-
doPreload(requestUrl, key, mode)
|
|
549
|
+
doPreload(requestUrl, key, mode, isCrossOrigin)
|
|
542
550
|
}
|
|
543
551
|
|
|
544
|
-
|
|
552
|
+
// v1.1.3: isCrossOrigin передаётся через цепочку вызовов (без повторного new URL())
|
|
553
|
+
function doPreload(requestUrl, key, mode, isCrossOrigin) {
|
|
545
554
|
if (mode !== 'none') {
|
|
546
555
|
// v1.0.11: try/catch для preloadSpec — при строгом CSP/Trusted Types может выбросить исключение
|
|
547
556
|
var specOk = false
|
|
548
557
|
try {
|
|
549
|
-
preloadSpec(requestUrl, mode)
|
|
558
|
+
preloadSpec(requestUrl, mode, isCrossOrigin)
|
|
550
559
|
specOk = true
|
|
551
560
|
} catch (e) {
|
|
552
561
|
// Ошибка в Speculation Rules — fallback обязателен
|
|
@@ -555,31 +564,26 @@ export function createPrefetchCore(options) {
|
|
|
555
564
|
// v1.0.11: fallback только если явно включён ИЛИ если SpecRules не удался
|
|
556
565
|
// По умолчанию fallback отключён для избежания двойного трафика
|
|
557
566
|
if (specRulesFallback || !specOk) {
|
|
558
|
-
|
|
559
|
-
|
|
567
|
+
// v1.1.1: cross-origin всегда через fetch (no-cors), <link> требует CORS headers
|
|
568
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin)
|
|
569
|
+
else preloadLink(requestUrl, key, isCrossOrigin)
|
|
560
570
|
}
|
|
561
571
|
return
|
|
562
572
|
}
|
|
563
573
|
|
|
564
|
-
|
|
565
|
-
|
|
574
|
+
// v1.1.1: cross-origin всегда через fetch (no-cors), <link crossorigin=anonymous> требует CORS
|
|
575
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin)
|
|
576
|
+
else preloadLink(requestUrl, key, isCrossOrigin)
|
|
566
577
|
}
|
|
567
578
|
|
|
568
579
|
function processQueue() {
|
|
569
580
|
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
570
581
|
var item = queue.shift()
|
|
571
|
-
doPreload(item.url, item.key, item.mode)
|
|
582
|
+
doPreload(item.url, item.key, item.mode, item.crossOrigin)
|
|
572
583
|
}
|
|
573
584
|
}
|
|
574
585
|
|
|
575
|
-
function preloadSpec(url, mode) {
|
|
576
|
-
// v1.0.13: для cross-origin проверяем и модифицируем правила
|
|
577
|
-
var isCrossOrigin = false
|
|
578
|
-
try {
|
|
579
|
-
var u = new URL(url, location.href)
|
|
580
|
-
isCrossOrigin = u.origin !== location.origin
|
|
581
|
-
} catch (e) {}
|
|
582
|
-
|
|
586
|
+
function preloadSpec(url, mode, isCrossOrigin) {
|
|
583
587
|
// v1.0.13: для cross-origin никогда не делаем prerender (только prefetch)
|
|
584
588
|
if (isCrossOrigin && mode === 'prerender') {
|
|
585
589
|
mode = 'prefetch'
|
|
@@ -613,18 +617,19 @@ export function createPrefetchCore(options) {
|
|
|
613
617
|
|
|
614
618
|
var rules = {}
|
|
615
619
|
|
|
620
|
+
// v1.1.3: передаём массив напрямую и создаём новый (без лишнего .slice())
|
|
616
621
|
// Same-origin prefetch
|
|
617
622
|
if (specBuffer.prefetch.length > 0) {
|
|
618
623
|
rules.prefetch = rules.prefetch || []
|
|
619
|
-
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch
|
|
620
|
-
specBuffer.prefetch
|
|
624
|
+
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch })
|
|
625
|
+
specBuffer.prefetch = []
|
|
621
626
|
}
|
|
622
627
|
|
|
623
628
|
// Same-origin prerender
|
|
624
629
|
if (specBuffer.prerender.length > 0) {
|
|
625
630
|
rules.prerender = rules.prerender || []
|
|
626
|
-
rules.prerender.push({ source: 'list', urls: specBuffer.prerender
|
|
627
|
-
specBuffer.prerender
|
|
631
|
+
rules.prerender.push({ source: 'list', urls: specBuffer.prerender })
|
|
632
|
+
specBuffer.prerender = []
|
|
628
633
|
}
|
|
629
634
|
|
|
630
635
|
// Cross-origin (только prefetch, с privacy requirements)
|
|
@@ -632,11 +637,11 @@ export function createPrefetchCore(options) {
|
|
|
632
637
|
rules.prefetch = rules.prefetch || []
|
|
633
638
|
rules.prefetch.push({
|
|
634
639
|
source: 'list',
|
|
635
|
-
urls: specBuffer.crossOrigin
|
|
640
|
+
urls: specBuffer.crossOrigin,
|
|
636
641
|
referrer_policy: 'no-referrer',
|
|
637
642
|
requires: ['anonymous-client-ip-when-cross-origin']
|
|
638
643
|
})
|
|
639
|
-
specBuffer.crossOrigin
|
|
644
|
+
specBuffer.crossOrigin = []
|
|
640
645
|
}
|
|
641
646
|
|
|
642
647
|
// Если нет правил — выходим
|
|
@@ -651,7 +656,7 @@ export function createPrefetchCore(options) {
|
|
|
651
656
|
head.removeChild(s)
|
|
652
657
|
}
|
|
653
658
|
|
|
654
|
-
function preloadLink(url, key) {
|
|
659
|
+
function preloadLink(url, key, isCrossOrigin) {
|
|
655
660
|
var head = document.head
|
|
656
661
|
// v1.0.10: если head недоступен, откатываем ключ
|
|
657
662
|
if (!head) { preloaded.delete(key); return }
|
|
@@ -665,13 +670,10 @@ export function createPrefetchCore(options) {
|
|
|
665
670
|
try { l.fetchPriority = 'low' } catch (e) {}
|
|
666
671
|
|
|
667
672
|
// v1.0.11: для cross-origin: referrerPolicy + crossOrigin
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
l.crossOrigin = 'anonymous' // не отправлять cookies на внешние домены
|
|
673
|
-
}
|
|
674
|
-
} catch (e) {}
|
|
673
|
+
if (isCrossOrigin) {
|
|
674
|
+
l.referrerPolicy = 'no-referrer'
|
|
675
|
+
l.crossOrigin = 'anonymous' // не отправлять cookies на внешние домены
|
|
676
|
+
}
|
|
675
677
|
|
|
676
678
|
// v1.0.13: safety timeout — предохранитель если onload/onerror не сработают
|
|
677
679
|
// (экзотические браузеры, сетевые ошибки без событий)
|
|
@@ -695,7 +697,7 @@ export function createPrefetchCore(options) {
|
|
|
695
697
|
head.appendChild(l)
|
|
696
698
|
}
|
|
697
699
|
|
|
698
|
-
function preloadFetch(url, key) {
|
|
700
|
+
function preloadFetch(url, key, isCrossOrigin) {
|
|
699
701
|
// v1.0.10: если fetch недоступен, откатываем ключ
|
|
700
702
|
if (typeof fetch !== 'function') { preloaded.delete(key); return }
|
|
701
703
|
|
|
@@ -728,12 +730,6 @@ export function createPrefetchCore(options) {
|
|
|
728
730
|
}, 5000)
|
|
729
731
|
}
|
|
730
732
|
|
|
731
|
-
// v1.0.13: определяем cross-origin для корректных настроек fetch
|
|
732
|
-
var isCrossOrigin = false
|
|
733
|
-
try {
|
|
734
|
-
isCrossOrigin = new URL(url, location.href).origin !== location.origin
|
|
735
|
-
} catch (e) {}
|
|
736
|
-
|
|
737
733
|
var opts = {
|
|
738
734
|
method: 'GET',
|
|
739
735
|
cache: 'force-cache',
|
|
@@ -803,30 +799,36 @@ export function createPrefetchCore(options) {
|
|
|
803
799
|
|
|
804
800
|
// Mutation Observer
|
|
805
801
|
var mutObserver = null
|
|
806
|
-
var mutTimer = null
|
|
807
802
|
|
|
808
803
|
function startMutationObserver() {
|
|
809
804
|
// v1.0.11: защита от вызова после destroy()
|
|
810
805
|
if (disabled) return
|
|
811
806
|
if (mutObserver) return
|
|
812
807
|
mutObserver = new MutationObserver(function (muts) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
808
|
+
if (!vpObserver) return
|
|
809
|
+
// v1.1.3: собираем только новые ссылки из addedNodes (не пересканируем весь DOM)
|
|
810
|
+
var pending = []
|
|
811
|
+
for (var i = 0; i < muts.length; i++) {
|
|
816
812
|
var nodes = muts[i].addedNodes
|
|
817
813
|
for (var j = 0; j < nodes.length; j++) {
|
|
818
814
|
var n = nodes[j]
|
|
819
|
-
if (n.nodeType
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
815
|
+
if (n.nodeType !== 1) continue
|
|
816
|
+
if (n.tagName === 'A') {
|
|
817
|
+
pending.push(n)
|
|
818
|
+
} else if (n.querySelectorAll) {
|
|
819
|
+
var links = n.querySelectorAll('a')
|
|
820
|
+
for (var k = 0; k < links.length; k++) pending.push(links[k])
|
|
824
821
|
}
|
|
825
822
|
}
|
|
826
823
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
824
|
+
// v1.1.3: обрабатываем через rIC чтобы не блокировать UI при массовых вставках DOM
|
|
825
|
+
if (pending.length > 0) {
|
|
826
|
+
var rIC = window.requestIdleCallback || function (cb) { setTimeout(cb, 1) }
|
|
827
|
+
rIC(function () {
|
|
828
|
+
for (var i = 0; i < pending.length; i++) {
|
|
829
|
+
if (vpObserver && canPreload(pending[i])) vpObserver.observe(pending[i])
|
|
830
|
+
}
|
|
831
|
+
})
|
|
830
832
|
}
|
|
831
833
|
})
|
|
832
834
|
mutObserver.observe(document.body, { childList: true, subtree: true })
|
|
@@ -887,6 +889,11 @@ export function createPrefetchCore(options) {
|
|
|
887
889
|
document.removeEventListener('mouseover', onMouseOver, opts)
|
|
888
890
|
document.removeEventListener('mousedown', onMouseDown, opts)
|
|
889
891
|
|
|
892
|
+
// v1.1.3: снимаем listener сети
|
|
893
|
+
if (conn && typeof conn.removeEventListener === 'function') {
|
|
894
|
+
conn.removeEventListener('change', onConnectionChange)
|
|
895
|
+
}
|
|
896
|
+
|
|
890
897
|
// v1.0.11: снимаем слушатели навигации
|
|
891
898
|
window.removeEventListener('popstate', updateCurrentKey)
|
|
892
899
|
window.removeEventListener('hashchange', updateCurrentKey)
|