@prefetchru/prefetch 1.1.2 → 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 +84 -85
- package/prefetch.js +84 -85
- package/src/core.js +81 -83
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.2",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,k=!1,O=!1,P=!1,A="none",S=!1,N=!1,C=!1,q=!1,R=!1,I=/(^|\/)(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&&(L=i)}p&&(E=Math.max(60,Math.min(L||0,150))),(R="prefetchObserveDom"in t||"instantObserveDom"in t)||"bitrix"!==v&&"tilda"!==v||(R=!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};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});R&&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)},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(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!I.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){var r=!1;try{r=new URL(e,location.href).origin!==location.origin}catch(e){}if("none"===n)m||!b||r?V(e,t):J(e,t);else{var o=!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),o=!0}catch(e){}!S&&o||(m||!b||r?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.2",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.2",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:[]},l=0,u="",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,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){u=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(){u=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!==u&&!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"))&&(!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 l=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:l});Q(c,s,l)}}}function Q(e,t,n){var r=!1;try{r=new URL(e,location.href).origin!==location.origin}catch(e){}if("none"===n)m||!b||r?V(e,t):J(e,t);else{var o=!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),!l){var r=window.requestIdleCallback||function(e){setTimeout(e,1)};l=r($,{timeout:50})}}(e,n),o=!0}catch(e){}!A&&o||(m||!b||r?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(l=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){}u(!1)},5e3));var s=!1;try{s=new URL(e,location.href).origin!==location.origin}catch(e){}var l={method:"GET",cache:"force-cache",credentials:s?"omit":"same-origin",mode:s?"no-cors":"cors"};s||(l.headers={Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",Purpose:"prefetch"}),s&&(l.referrerPolicy="no-referrer"),o&&(l.signal=o.signal);try{fetch(e,l).then(function(e){u(e&&(e.ok||304===e.status||"opaque"===e.type))}).catch(function(){u(!1)})}catch(e){u(!1)}}else n.delete(t);function u(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 l=navigator.connection;l&&(w=l.effectiveType,g=l.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.2",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(),l&&((window.cancelIdleCallback||clearTimeout)(l),l=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,26 +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
|
-
|
|
548
|
-
|
|
549
|
-
var isCrossOrigin = false;
|
|
550
|
-
try {
|
|
551
|
-
var u = new URL(requestUrl, location.href);
|
|
552
|
-
isCrossOrigin = u.origin !== location.origin;
|
|
553
|
-
} catch (e) {}
|
|
554
|
-
|
|
556
|
+
// v1.1.3: isCrossOrigin передаётся через цепочку вызовов (без повторного new URL())
|
|
557
|
+
function doPreload(requestUrl, key, mode, isCrossOrigin) {
|
|
555
558
|
if (mode !== 'none') {
|
|
556
559
|
// v1.0.11: try/catch для preloadSpec — при строгом CSP/Trusted Types может выбросить исключение
|
|
557
560
|
var specOk = false;
|
|
558
561
|
try {
|
|
559
|
-
preloadSpec(requestUrl, mode);
|
|
562
|
+
preloadSpec(requestUrl, mode, isCrossOrigin);
|
|
560
563
|
specOk = true;
|
|
561
564
|
} catch (e) {
|
|
562
565
|
// Ошибка в Speculation Rules — fallback обязателен
|
|
@@ -566,32 +569,25 @@ function createPrefetchCore(options) {
|
|
|
566
569
|
// По умолчанию fallback отключён для избежания двойного трафика
|
|
567
570
|
if (specRulesFallback || !specOk) {
|
|
568
571
|
// v1.1.1: cross-origin всегда через fetch (no-cors), <link> требует CORS headers
|
|
569
|
-
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key);
|
|
570
|
-
else preloadLink(requestUrl, key);
|
|
572
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
573
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
571
574
|
}
|
|
572
575
|
return
|
|
573
576
|
}
|
|
574
577
|
|
|
575
578
|
// v1.1.1: cross-origin всегда через fetch (no-cors), <link crossorigin=anonymous> требует CORS
|
|
576
|
-
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key);
|
|
577
|
-
else preloadLink(requestUrl, key);
|
|
579
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
580
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
578
581
|
}
|
|
579
582
|
|
|
580
583
|
function processQueue() {
|
|
581
584
|
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
582
585
|
var item = queue.shift();
|
|
583
|
-
doPreload(item.url, item.key, item.mode);
|
|
586
|
+
doPreload(item.url, item.key, item.mode, item.crossOrigin);
|
|
584
587
|
}
|
|
585
588
|
}
|
|
586
589
|
|
|
587
|
-
function preloadSpec(url, mode) {
|
|
588
|
-
// v1.0.13: для cross-origin проверяем и модифицируем правила
|
|
589
|
-
var isCrossOrigin = false;
|
|
590
|
-
try {
|
|
591
|
-
var u = new URL(url, location.href);
|
|
592
|
-
isCrossOrigin = u.origin !== location.origin;
|
|
593
|
-
} catch (e) {}
|
|
594
|
-
|
|
590
|
+
function preloadSpec(url, mode, isCrossOrigin) {
|
|
595
591
|
// v1.0.13: для cross-origin никогда не делаем prerender (только prefetch)
|
|
596
592
|
if (isCrossOrigin && mode === 'prerender') {
|
|
597
593
|
mode = 'prefetch';
|
|
@@ -625,18 +621,19 @@ function createPrefetchCore(options) {
|
|
|
625
621
|
|
|
626
622
|
var rules = {};
|
|
627
623
|
|
|
624
|
+
// v1.1.3: передаём массив напрямую и создаём новый (без лишнего .slice())
|
|
628
625
|
// Same-origin prefetch
|
|
629
626
|
if (specBuffer.prefetch.length > 0) {
|
|
630
627
|
rules.prefetch = rules.prefetch || [];
|
|
631
|
-
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch
|
|
632
|
-
specBuffer.prefetch
|
|
628
|
+
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch });
|
|
629
|
+
specBuffer.prefetch = [];
|
|
633
630
|
}
|
|
634
631
|
|
|
635
632
|
// Same-origin prerender
|
|
636
633
|
if (specBuffer.prerender.length > 0) {
|
|
637
634
|
rules.prerender = rules.prerender || [];
|
|
638
|
-
rules.prerender.push({ source: 'list', urls: specBuffer.prerender
|
|
639
|
-
specBuffer.prerender
|
|
635
|
+
rules.prerender.push({ source: 'list', urls: specBuffer.prerender });
|
|
636
|
+
specBuffer.prerender = [];
|
|
640
637
|
}
|
|
641
638
|
|
|
642
639
|
// Cross-origin (только prefetch, с privacy requirements)
|
|
@@ -644,11 +641,11 @@ function createPrefetchCore(options) {
|
|
|
644
641
|
rules.prefetch = rules.prefetch || [];
|
|
645
642
|
rules.prefetch.push({
|
|
646
643
|
source: 'list',
|
|
647
|
-
urls: specBuffer.crossOrigin
|
|
644
|
+
urls: specBuffer.crossOrigin,
|
|
648
645
|
referrer_policy: 'no-referrer',
|
|
649
646
|
requires: ['anonymous-client-ip-when-cross-origin']
|
|
650
647
|
});
|
|
651
|
-
specBuffer.crossOrigin
|
|
648
|
+
specBuffer.crossOrigin = [];
|
|
652
649
|
}
|
|
653
650
|
|
|
654
651
|
// Если нет правил — выходим
|
|
@@ -663,7 +660,7 @@ function createPrefetchCore(options) {
|
|
|
663
660
|
head.removeChild(s);
|
|
664
661
|
}
|
|
665
662
|
|
|
666
|
-
function preloadLink(url, key) {
|
|
663
|
+
function preloadLink(url, key, isCrossOrigin) {
|
|
667
664
|
var head = document.head;
|
|
668
665
|
// v1.0.10: если head недоступен, откатываем ключ
|
|
669
666
|
if (!head) { preloaded.delete(key); return }
|
|
@@ -677,13 +674,10 @@ function createPrefetchCore(options) {
|
|
|
677
674
|
try { l.fetchPriority = 'low'; } catch (e) {}
|
|
678
675
|
|
|
679
676
|
// v1.0.11: для cross-origin: referrerPolicy + crossOrigin
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
685
|
-
}
|
|
686
|
-
} catch (e) {}
|
|
677
|
+
if (isCrossOrigin) {
|
|
678
|
+
l.referrerPolicy = 'no-referrer';
|
|
679
|
+
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
680
|
+
}
|
|
687
681
|
|
|
688
682
|
// v1.0.13: safety timeout — предохранитель если onload/onerror не сработают
|
|
689
683
|
// (экзотические браузеры, сетевые ошибки без событий)
|
|
@@ -707,7 +701,7 @@ function createPrefetchCore(options) {
|
|
|
707
701
|
head.appendChild(l);
|
|
708
702
|
}
|
|
709
703
|
|
|
710
|
-
function preloadFetch(url, key) {
|
|
704
|
+
function preloadFetch(url, key, isCrossOrigin) {
|
|
711
705
|
// v1.0.10: если fetch недоступен, откатываем ключ
|
|
712
706
|
if (typeof fetch !== 'function') { preloaded.delete(key); return }
|
|
713
707
|
|
|
@@ -740,12 +734,6 @@ function createPrefetchCore(options) {
|
|
|
740
734
|
}, 5000);
|
|
741
735
|
}
|
|
742
736
|
|
|
743
|
-
// v1.0.13: определяем cross-origin для корректных настроек fetch
|
|
744
|
-
var isCrossOrigin = false;
|
|
745
|
-
try {
|
|
746
|
-
isCrossOrigin = new URL(url, location.href).origin !== location.origin;
|
|
747
|
-
} catch (e) {}
|
|
748
|
-
|
|
749
737
|
var opts = {
|
|
750
738
|
method: 'GET',
|
|
751
739
|
cache: 'force-cache',
|
|
@@ -815,30 +803,36 @@ function createPrefetchCore(options) {
|
|
|
815
803
|
|
|
816
804
|
// Mutation Observer
|
|
817
805
|
var mutObserver = null;
|
|
818
|
-
var mutTimer = null;
|
|
819
806
|
|
|
820
807
|
function startMutationObserver() {
|
|
821
808
|
// v1.0.11: защита от вызова после destroy()
|
|
822
809
|
if (disabled) return
|
|
823
810
|
if (mutObserver) return
|
|
824
811
|
mutObserver = new MutationObserver(function (muts) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
812
|
+
if (!vpObserver) return
|
|
813
|
+
// v1.1.3: собираем только новые ссылки из addedNodes (не пересканируем весь DOM)
|
|
814
|
+
var pending = [];
|
|
815
|
+
for (var i = 0; i < muts.length; i++) {
|
|
828
816
|
var nodes = muts[i].addedNodes;
|
|
829
817
|
for (var j = 0; j < nodes.length; j++) {
|
|
830
818
|
var n = nodes[j];
|
|
831
|
-
if (n.nodeType
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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]);
|
|
836
825
|
}
|
|
837
826
|
}
|
|
838
827
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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
|
+
});
|
|
842
836
|
}
|
|
843
837
|
});
|
|
844
838
|
mutObserver.observe(document.body, { childList: true, subtree: true });
|
|
@@ -899,6 +893,11 @@ function createPrefetchCore(options) {
|
|
|
899
893
|
document.removeEventListener('mouseover', onMouseOver, opts);
|
|
900
894
|
document.removeEventListener('mousedown', onMouseDown, opts);
|
|
901
895
|
|
|
896
|
+
// v1.1.3: снимаем listener сети
|
|
897
|
+
if (conn && typeof conn.removeEventListener === 'function') {
|
|
898
|
+
conn.removeEventListener('change', onConnectionChange);
|
|
899
|
+
}
|
|
900
|
+
|
|
902
901
|
// v1.0.11: снимаем слушатели навигации
|
|
903
902
|
window.removeEventListener('popstate', updateCurrentKey);
|
|
904
903
|
window.removeEventListener('hashchange', updateCurrentKey);
|
|
@@ -918,7 +917,7 @@ function createPrefetchCore(options) {
|
|
|
918
917
|
// Публичный API
|
|
919
918
|
var api = {
|
|
920
919
|
__prefetchRu: true,
|
|
921
|
-
version: '1.1.
|
|
920
|
+
version: '1.1.3',
|
|
922
921
|
preload: function (url) {
|
|
923
922
|
// v1.0.11: валидация URL + прогон через canPreload() (консистентность с авто-режимом)
|
|
924
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,26 +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
|
-
|
|
551
|
-
|
|
552
|
-
var isCrossOrigin = false;
|
|
553
|
-
try {
|
|
554
|
-
var u = new URL(requestUrl, location.href);
|
|
555
|
-
isCrossOrigin = u.origin !== location.origin;
|
|
556
|
-
} catch (e) {}
|
|
557
|
-
|
|
559
|
+
// v1.1.3: isCrossOrigin передаётся через цепочку вызовов (без повторного new URL())
|
|
560
|
+
function doPreload(requestUrl, key, mode, isCrossOrigin) {
|
|
558
561
|
if (mode !== 'none') {
|
|
559
562
|
// v1.0.11: try/catch для preloadSpec — при строгом CSP/Trusted Types может выбросить исключение
|
|
560
563
|
var specOk = false;
|
|
561
564
|
try {
|
|
562
|
-
preloadSpec(requestUrl, mode);
|
|
565
|
+
preloadSpec(requestUrl, mode, isCrossOrigin);
|
|
563
566
|
specOk = true;
|
|
564
567
|
} catch (e) {
|
|
565
568
|
// Ошибка в Speculation Rules — fallback обязателен
|
|
@@ -569,32 +572,25 @@
|
|
|
569
572
|
// По умолчанию fallback отключён для избежания двойного трафика
|
|
570
573
|
if (specRulesFallback || !specOk) {
|
|
571
574
|
// v1.1.1: cross-origin всегда через fetch (no-cors), <link> требует CORS headers
|
|
572
|
-
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key);
|
|
573
|
-
else preloadLink(requestUrl, key);
|
|
575
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
576
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
574
577
|
}
|
|
575
578
|
return
|
|
576
579
|
}
|
|
577
580
|
|
|
578
581
|
// v1.1.1: cross-origin всегда через fetch (no-cors), <link crossorigin=anonymous> требует CORS
|
|
579
|
-
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key);
|
|
580
|
-
else preloadLink(requestUrl, key);
|
|
582
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin);
|
|
583
|
+
else preloadLink(requestUrl, key, isCrossOrigin);
|
|
581
584
|
}
|
|
582
585
|
|
|
583
586
|
function processQueue() {
|
|
584
587
|
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
585
588
|
var item = queue.shift();
|
|
586
|
-
doPreload(item.url, item.key, item.mode);
|
|
589
|
+
doPreload(item.url, item.key, item.mode, item.crossOrigin);
|
|
587
590
|
}
|
|
588
591
|
}
|
|
589
592
|
|
|
590
|
-
function preloadSpec(url, mode) {
|
|
591
|
-
// v1.0.13: для cross-origin проверяем и модифицируем правила
|
|
592
|
-
var isCrossOrigin = false;
|
|
593
|
-
try {
|
|
594
|
-
var u = new URL(url, location.href);
|
|
595
|
-
isCrossOrigin = u.origin !== location.origin;
|
|
596
|
-
} catch (e) {}
|
|
597
|
-
|
|
593
|
+
function preloadSpec(url, mode, isCrossOrigin) {
|
|
598
594
|
// v1.0.13: для cross-origin никогда не делаем prerender (только prefetch)
|
|
599
595
|
if (isCrossOrigin && mode === 'prerender') {
|
|
600
596
|
mode = 'prefetch';
|
|
@@ -628,18 +624,19 @@
|
|
|
628
624
|
|
|
629
625
|
var rules = {};
|
|
630
626
|
|
|
627
|
+
// v1.1.3: передаём массив напрямую и создаём новый (без лишнего .slice())
|
|
631
628
|
// Same-origin prefetch
|
|
632
629
|
if (specBuffer.prefetch.length > 0) {
|
|
633
630
|
rules.prefetch = rules.prefetch || [];
|
|
634
|
-
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch
|
|
635
|
-
specBuffer.prefetch
|
|
631
|
+
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch });
|
|
632
|
+
specBuffer.prefetch = [];
|
|
636
633
|
}
|
|
637
634
|
|
|
638
635
|
// Same-origin prerender
|
|
639
636
|
if (specBuffer.prerender.length > 0) {
|
|
640
637
|
rules.prerender = rules.prerender || [];
|
|
641
|
-
rules.prerender.push({ source: 'list', urls: specBuffer.prerender
|
|
642
|
-
specBuffer.prerender
|
|
638
|
+
rules.prerender.push({ source: 'list', urls: specBuffer.prerender });
|
|
639
|
+
specBuffer.prerender = [];
|
|
643
640
|
}
|
|
644
641
|
|
|
645
642
|
// Cross-origin (только prefetch, с privacy requirements)
|
|
@@ -647,11 +644,11 @@
|
|
|
647
644
|
rules.prefetch = rules.prefetch || [];
|
|
648
645
|
rules.prefetch.push({
|
|
649
646
|
source: 'list',
|
|
650
|
-
urls: specBuffer.crossOrigin
|
|
647
|
+
urls: specBuffer.crossOrigin,
|
|
651
648
|
referrer_policy: 'no-referrer',
|
|
652
649
|
requires: ['anonymous-client-ip-when-cross-origin']
|
|
653
650
|
});
|
|
654
|
-
specBuffer.crossOrigin
|
|
651
|
+
specBuffer.crossOrigin = [];
|
|
655
652
|
}
|
|
656
653
|
|
|
657
654
|
// Если нет правил — выходим
|
|
@@ -666,7 +663,7 @@
|
|
|
666
663
|
head.removeChild(s);
|
|
667
664
|
}
|
|
668
665
|
|
|
669
|
-
function preloadLink(url, key) {
|
|
666
|
+
function preloadLink(url, key, isCrossOrigin) {
|
|
670
667
|
var head = document.head;
|
|
671
668
|
// v1.0.10: если head недоступен, откатываем ключ
|
|
672
669
|
if (!head) { preloaded.delete(key); return }
|
|
@@ -680,13 +677,10 @@
|
|
|
680
677
|
try { l.fetchPriority = 'low'; } catch (e) {}
|
|
681
678
|
|
|
682
679
|
// v1.0.11: для cross-origin: referrerPolicy + crossOrigin
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
688
|
-
}
|
|
689
|
-
} catch (e) {}
|
|
680
|
+
if (isCrossOrigin) {
|
|
681
|
+
l.referrerPolicy = 'no-referrer';
|
|
682
|
+
l.crossOrigin = 'anonymous'; // не отправлять cookies на внешние домены
|
|
683
|
+
}
|
|
690
684
|
|
|
691
685
|
// v1.0.13: safety timeout — предохранитель если onload/onerror не сработают
|
|
692
686
|
// (экзотические браузеры, сетевые ошибки без событий)
|
|
@@ -710,7 +704,7 @@
|
|
|
710
704
|
head.appendChild(l);
|
|
711
705
|
}
|
|
712
706
|
|
|
713
|
-
function preloadFetch(url, key) {
|
|
707
|
+
function preloadFetch(url, key, isCrossOrigin) {
|
|
714
708
|
// v1.0.10: если fetch недоступен, откатываем ключ
|
|
715
709
|
if (typeof fetch !== 'function') { preloaded.delete(key); return }
|
|
716
710
|
|
|
@@ -743,12 +737,6 @@
|
|
|
743
737
|
}, 5000);
|
|
744
738
|
}
|
|
745
739
|
|
|
746
|
-
// v1.0.13: определяем cross-origin для корректных настроек fetch
|
|
747
|
-
var isCrossOrigin = false;
|
|
748
|
-
try {
|
|
749
|
-
isCrossOrigin = new URL(url, location.href).origin !== location.origin;
|
|
750
|
-
} catch (e) {}
|
|
751
|
-
|
|
752
740
|
var opts = {
|
|
753
741
|
method: 'GET',
|
|
754
742
|
cache: 'force-cache',
|
|
@@ -818,30 +806,36 @@
|
|
|
818
806
|
|
|
819
807
|
// Mutation Observer
|
|
820
808
|
var mutObserver = null;
|
|
821
|
-
var mutTimer = null;
|
|
822
809
|
|
|
823
810
|
function startMutationObserver() {
|
|
824
811
|
// v1.0.11: защита от вызова после destroy()
|
|
825
812
|
if (disabled) return
|
|
826
813
|
if (mutObserver) return
|
|
827
814
|
mutObserver = new MutationObserver(function (muts) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
815
|
+
if (!vpObserver) return
|
|
816
|
+
// v1.1.3: собираем только новые ссылки из addedNodes (не пересканируем весь DOM)
|
|
817
|
+
var pending = [];
|
|
818
|
+
for (var i = 0; i < muts.length; i++) {
|
|
831
819
|
var nodes = muts[i].addedNodes;
|
|
832
820
|
for (var j = 0; j < nodes.length; j++) {
|
|
833
821
|
var n = nodes[j];
|
|
834
|
-
if (n.nodeType
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
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]);
|
|
839
828
|
}
|
|
840
829
|
}
|
|
841
830
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
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
|
+
});
|
|
845
839
|
}
|
|
846
840
|
});
|
|
847
841
|
mutObserver.observe(document.body, { childList: true, subtree: true });
|
|
@@ -902,6 +896,11 @@
|
|
|
902
896
|
document.removeEventListener('mouseover', onMouseOver, opts);
|
|
903
897
|
document.removeEventListener('mousedown', onMouseDown, opts);
|
|
904
898
|
|
|
899
|
+
// v1.1.3: снимаем listener сети
|
|
900
|
+
if (conn && typeof conn.removeEventListener === 'function') {
|
|
901
|
+
conn.removeEventListener('change', onConnectionChange);
|
|
902
|
+
}
|
|
903
|
+
|
|
905
904
|
// v1.0.11: снимаем слушатели навигации
|
|
906
905
|
window.removeEventListener('popstate', updateCurrentKey);
|
|
907
906
|
window.removeEventListener('hashchange', updateCurrentKey);
|
|
@@ -921,7 +920,7 @@
|
|
|
921
920
|
// Публичный API
|
|
922
921
|
var api = {
|
|
923
922
|
__prefetchRu: true,
|
|
924
|
-
version: '1.1.
|
|
923
|
+
version: '1.1.3',
|
|
925
924
|
preload: function (url) {
|
|
926
925
|
// v1.0.11: валидация URL + прогон через canPreload() (консистентность с авто-режимом)
|
|
927
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,26 +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
|
-
|
|
545
|
-
|
|
546
|
-
var isCrossOrigin = false
|
|
547
|
-
try {
|
|
548
|
-
var u = new URL(requestUrl, location.href)
|
|
549
|
-
isCrossOrigin = u.origin !== location.origin
|
|
550
|
-
} catch (e) {}
|
|
551
|
-
|
|
552
|
+
// v1.1.3: isCrossOrigin передаётся через цепочку вызовов (без повторного new URL())
|
|
553
|
+
function doPreload(requestUrl, key, mode, isCrossOrigin) {
|
|
552
554
|
if (mode !== 'none') {
|
|
553
555
|
// v1.0.11: try/catch для preloadSpec — при строгом CSP/Trusted Types может выбросить исключение
|
|
554
556
|
var specOk = false
|
|
555
557
|
try {
|
|
556
|
-
preloadSpec(requestUrl, mode)
|
|
558
|
+
preloadSpec(requestUrl, mode, isCrossOrigin)
|
|
557
559
|
specOk = true
|
|
558
560
|
} catch (e) {
|
|
559
561
|
// Ошибка в Speculation Rules — fallback обязателен
|
|
@@ -563,32 +565,25 @@ export function createPrefetchCore(options) {
|
|
|
563
565
|
// По умолчанию fallback отключён для избежания двойного трафика
|
|
564
566
|
if (specRulesFallback || !specOk) {
|
|
565
567
|
// v1.1.1: cross-origin всегда через fetch (no-cors), <link> требует CORS headers
|
|
566
|
-
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key)
|
|
567
|
-
else preloadLink(requestUrl, key)
|
|
568
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin)
|
|
569
|
+
else preloadLink(requestUrl, key, isCrossOrigin)
|
|
568
570
|
}
|
|
569
571
|
return
|
|
570
572
|
}
|
|
571
573
|
|
|
572
574
|
// v1.1.1: cross-origin всегда через fetch (no-cors), <link crossorigin=anonymous> требует CORS
|
|
573
|
-
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key)
|
|
574
|
-
else preloadLink(requestUrl, key)
|
|
575
|
+
if (isIOS || !supportsLinkPrefetch || isCrossOrigin) preloadFetch(requestUrl, key, isCrossOrigin)
|
|
576
|
+
else preloadLink(requestUrl, key, isCrossOrigin)
|
|
575
577
|
}
|
|
576
578
|
|
|
577
579
|
function processQueue() {
|
|
578
580
|
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
579
581
|
var item = queue.shift()
|
|
580
|
-
doPreload(item.url, item.key, item.mode)
|
|
582
|
+
doPreload(item.url, item.key, item.mode, item.crossOrigin)
|
|
581
583
|
}
|
|
582
584
|
}
|
|
583
585
|
|
|
584
|
-
function preloadSpec(url, mode) {
|
|
585
|
-
// v1.0.13: для cross-origin проверяем и модифицируем правила
|
|
586
|
-
var isCrossOrigin = false
|
|
587
|
-
try {
|
|
588
|
-
var u = new URL(url, location.href)
|
|
589
|
-
isCrossOrigin = u.origin !== location.origin
|
|
590
|
-
} catch (e) {}
|
|
591
|
-
|
|
586
|
+
function preloadSpec(url, mode, isCrossOrigin) {
|
|
592
587
|
// v1.0.13: для cross-origin никогда не делаем prerender (только prefetch)
|
|
593
588
|
if (isCrossOrigin && mode === 'prerender') {
|
|
594
589
|
mode = 'prefetch'
|
|
@@ -622,18 +617,19 @@ export function createPrefetchCore(options) {
|
|
|
622
617
|
|
|
623
618
|
var rules = {}
|
|
624
619
|
|
|
620
|
+
// v1.1.3: передаём массив напрямую и создаём новый (без лишнего .slice())
|
|
625
621
|
// Same-origin prefetch
|
|
626
622
|
if (specBuffer.prefetch.length > 0) {
|
|
627
623
|
rules.prefetch = rules.prefetch || []
|
|
628
|
-
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch
|
|
629
|
-
specBuffer.prefetch
|
|
624
|
+
rules.prefetch.push({ source: 'list', urls: specBuffer.prefetch })
|
|
625
|
+
specBuffer.prefetch = []
|
|
630
626
|
}
|
|
631
627
|
|
|
632
628
|
// Same-origin prerender
|
|
633
629
|
if (specBuffer.prerender.length > 0) {
|
|
634
630
|
rules.prerender = rules.prerender || []
|
|
635
|
-
rules.prerender.push({ source: 'list', urls: specBuffer.prerender
|
|
636
|
-
specBuffer.prerender
|
|
631
|
+
rules.prerender.push({ source: 'list', urls: specBuffer.prerender })
|
|
632
|
+
specBuffer.prerender = []
|
|
637
633
|
}
|
|
638
634
|
|
|
639
635
|
// Cross-origin (только prefetch, с privacy requirements)
|
|
@@ -641,11 +637,11 @@ export function createPrefetchCore(options) {
|
|
|
641
637
|
rules.prefetch = rules.prefetch || []
|
|
642
638
|
rules.prefetch.push({
|
|
643
639
|
source: 'list',
|
|
644
|
-
urls: specBuffer.crossOrigin
|
|
640
|
+
urls: specBuffer.crossOrigin,
|
|
645
641
|
referrer_policy: 'no-referrer',
|
|
646
642
|
requires: ['anonymous-client-ip-when-cross-origin']
|
|
647
643
|
})
|
|
648
|
-
specBuffer.crossOrigin
|
|
644
|
+
specBuffer.crossOrigin = []
|
|
649
645
|
}
|
|
650
646
|
|
|
651
647
|
// Если нет правил — выходим
|
|
@@ -660,7 +656,7 @@ export function createPrefetchCore(options) {
|
|
|
660
656
|
head.removeChild(s)
|
|
661
657
|
}
|
|
662
658
|
|
|
663
|
-
function preloadLink(url, key) {
|
|
659
|
+
function preloadLink(url, key, isCrossOrigin) {
|
|
664
660
|
var head = document.head
|
|
665
661
|
// v1.0.10: если head недоступен, откатываем ключ
|
|
666
662
|
if (!head) { preloaded.delete(key); return }
|
|
@@ -674,13 +670,10 @@ export function createPrefetchCore(options) {
|
|
|
674
670
|
try { l.fetchPriority = 'low' } catch (e) {}
|
|
675
671
|
|
|
676
672
|
// v1.0.11: для cross-origin: referrerPolicy + crossOrigin
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
l.crossOrigin = 'anonymous' // не отправлять cookies на внешние домены
|
|
682
|
-
}
|
|
683
|
-
} catch (e) {}
|
|
673
|
+
if (isCrossOrigin) {
|
|
674
|
+
l.referrerPolicy = 'no-referrer'
|
|
675
|
+
l.crossOrigin = 'anonymous' // не отправлять cookies на внешние домены
|
|
676
|
+
}
|
|
684
677
|
|
|
685
678
|
// v1.0.13: safety timeout — предохранитель если onload/onerror не сработают
|
|
686
679
|
// (экзотические браузеры, сетевые ошибки без событий)
|
|
@@ -704,7 +697,7 @@ export function createPrefetchCore(options) {
|
|
|
704
697
|
head.appendChild(l)
|
|
705
698
|
}
|
|
706
699
|
|
|
707
|
-
function preloadFetch(url, key) {
|
|
700
|
+
function preloadFetch(url, key, isCrossOrigin) {
|
|
708
701
|
// v1.0.10: если fetch недоступен, откатываем ключ
|
|
709
702
|
if (typeof fetch !== 'function') { preloaded.delete(key); return }
|
|
710
703
|
|
|
@@ -737,12 +730,6 @@ export function createPrefetchCore(options) {
|
|
|
737
730
|
}, 5000)
|
|
738
731
|
}
|
|
739
732
|
|
|
740
|
-
// v1.0.13: определяем cross-origin для корректных настроек fetch
|
|
741
|
-
var isCrossOrigin = false
|
|
742
|
-
try {
|
|
743
|
-
isCrossOrigin = new URL(url, location.href).origin !== location.origin
|
|
744
|
-
} catch (e) {}
|
|
745
|
-
|
|
746
733
|
var opts = {
|
|
747
734
|
method: 'GET',
|
|
748
735
|
cache: 'force-cache',
|
|
@@ -812,30 +799,36 @@ export function createPrefetchCore(options) {
|
|
|
812
799
|
|
|
813
800
|
// Mutation Observer
|
|
814
801
|
var mutObserver = null
|
|
815
|
-
var mutTimer = null
|
|
816
802
|
|
|
817
803
|
function startMutationObserver() {
|
|
818
804
|
// v1.0.11: защита от вызова после destroy()
|
|
819
805
|
if (disabled) return
|
|
820
806
|
if (mutObserver) return
|
|
821
807
|
mutObserver = new MutationObserver(function (muts) {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
808
|
+
if (!vpObserver) return
|
|
809
|
+
// v1.1.3: собираем только новые ссылки из addedNodes (не пересканируем весь DOM)
|
|
810
|
+
var pending = []
|
|
811
|
+
for (var i = 0; i < muts.length; i++) {
|
|
825
812
|
var nodes = muts[i].addedNodes
|
|
826
813
|
for (var j = 0; j < nodes.length; j++) {
|
|
827
814
|
var n = nodes[j]
|
|
828
|
-
if (n.nodeType
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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])
|
|
833
821
|
}
|
|
834
822
|
}
|
|
835
823
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
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
|
+
})
|
|
839
832
|
}
|
|
840
833
|
})
|
|
841
834
|
mutObserver.observe(document.body, { childList: true, subtree: true })
|
|
@@ -896,6 +889,11 @@ export function createPrefetchCore(options) {
|
|
|
896
889
|
document.removeEventListener('mouseover', onMouseOver, opts)
|
|
897
890
|
document.removeEventListener('mousedown', onMouseDown, opts)
|
|
898
891
|
|
|
892
|
+
// v1.1.3: снимаем listener сети
|
|
893
|
+
if (conn && typeof conn.removeEventListener === 'function') {
|
|
894
|
+
conn.removeEventListener('change', onConnectionChange)
|
|
895
|
+
}
|
|
896
|
+
|
|
899
897
|
// v1.0.11: снимаем слушатели навигации
|
|
900
898
|
window.removeEventListener('popstate', updateCurrentKey)
|
|
901
899
|
window.removeEventListener('hashchange', updateCurrentKey)
|