|
22 | 22 | };
|
23 | 23 | if (localStorage.getItem('{{hu-lts}}-loader-key') !== navigator.userAgent)
|
24 | 24 | return displayErrorPage();
|
25 |
| - const loadPage = () => { |
26 |
| - removeEventListener('load', loadPage); |
27 |
| - fetch(location.pathname + '.ico', { mode: 'same-origin' }) |
28 |
| - .then((response) => { |
29 |
| - response.blob().then((blob) => { |
30 |
| - new Response( |
31 |
| - blob.stream().pipeThrough(new DecompressionStream('gzip')) |
32 |
| - ) |
33 |
| - .text() |
34 |
| - .then((text) => { |
35 |
| - ((currentDoc, newDoc) => { |
36 |
| - const deferScripts = [], |
37 |
| - syncScripts = []; |
38 |
| - let reachedEnd = false, |
39 |
| - waitForHead = false, |
40 |
| - headScripts = 0; |
41 |
| - const bodyLoader = () => { |
42 |
| - headScripts--; |
43 |
| - if (waitForHead && headScripts <= 0) { |
44 |
| - waitForHead = false; |
45 |
| - currentDoc.body.replaceWith(recursiveClone(newDoc.body)); |
46 |
| - if (reachedEnd) loadNextScript(false)(); |
47 |
| - } |
48 |
| - return waitForHead; |
49 |
| - }; |
50 |
| - const loadNextScript = (isDefer, currentScript) => () => { |
51 |
| - if ( |
52 |
| - !isDefer && |
53 |
| - currentScript && |
54 |
| - 'head' === currentScript.parentElement.tagName.toLowerCase() |
55 |
| - ) |
56 |
| - bodyLoader(); |
57 |
| - let nextScript = [...currentDoc.scripts].find( |
58 |
| - (script) => |
59 |
| - script.getAttribute('itemprop') === 'script-insert' && |
60 |
| - script.defer === isDefer |
61 |
| - ); |
62 |
| - if (nextScript) { |
63 |
| - const replacement = isDefer |
64 |
| - ? deferScripts.shift() |
65 |
| - : syncScripts.shift(); |
66 |
| - nextScript.replaceWith(replacement); |
67 |
| - if (replacement.childNodes.length > 0) |
68 |
| - loadNextScript(isDefer, replacement)(); |
69 |
| - } else if (!isDefer && !waitForHead) loadNextScript(true)(); |
70 |
| - else reachedEnd = true; |
71 |
| - }; |
72 |
| - const recursiveClone = (node) => { |
73 |
| - if (node.nodeType !== Node.ELEMENT_NODE) return node; |
74 |
| - const nodeName = node.tagName.toLowerCase(); |
75 |
| - if (['svg', 'xml'].includes(nodeName)) |
76 |
| - return node.cloneNode(1); |
77 |
| - let elementCopy = currentDoc.createElement(nodeName); |
78 |
| - let j = 0, |
79 |
| - nodeList = [...node.attributes]; |
80 |
| - for (; j < nodeList.length; j++) |
81 |
| - elementCopy.setAttribute( |
82 |
| - nodeList[j].nodeName, |
83 |
| - nodeList[j].nodeValue || '' |
84 |
| - ); |
85 |
| - nodeList = [...node.childNodes]; |
86 |
| - for (j = 0; j < nodeList.length; j++) |
87 |
| - elementCopy.appendChild(recursiveClone(nodeList[j])); |
88 |
| - if ('script' === nodeName && !node.async) { |
89 |
| - const isDefer = |
90 |
| - node.defer || 'module' === node.type.toLowerCase(); |
91 |
| - let replacement = currentDoc.createElement('script'); |
92 |
| - if (isDefer) replacement.setAttribute('defer', ''); |
93 |
| - replacement.setAttribute('itemprop', 'script-insert'); |
94 |
| - if (node.childNodes.length <= 0) { |
95 |
| - elementCopy.addEventListener( |
96 |
| - 'load', |
97 |
| - loadNextScript(isDefer, elementCopy) |
98 |
| - ); |
99 |
| - elementCopy.addEventListener( |
100 |
| - 'error', |
101 |
| - loadNextScript(isDefer, elementCopy) |
102 |
| - ); |
| 25 | + const loadPage = |
| 26 | + (destination = location, pushState = true) => |
| 27 | + () => { |
| 28 | + removeEventListener('load', loadPage); |
| 29 | + fetch(destination.pathname + '.ico', { mode: 'same-origin' }) |
| 30 | + .then((response) => { |
| 31 | + if (destination !== location && pushState) { |
| 32 | + if (response.status === 200) history.pushState({}, '', destination); |
| 33 | + else return location.assign(new URL(destination, location)); |
| 34 | + } |
| 35 | + response.blob().then((blob) => { |
| 36 | + new Response( |
| 37 | + blob.stream().pipeThrough(new DecompressionStream('gzip')) |
| 38 | + ) |
| 39 | + .text() |
| 40 | + .then((text) => { |
| 41 | + ((currentDoc, newDoc) => { |
| 42 | + const deferScripts = [], |
| 43 | + syncScripts = []; |
| 44 | + let reachedEnd = false, |
| 45 | + waitForHead = false, |
| 46 | + headScripts = 0; |
| 47 | + const bodyLoader = () => { |
| 48 | + headScripts--; |
| 49 | + if (waitForHead && headScripts <= 0) { |
| 50 | + waitForHead = false; |
| 51 | + currentDoc.body.replaceWith(recursiveClone(newDoc.body)); |
| 52 | + if (reachedEnd) loadNextScript(false)(); |
103 | 53 | }
|
104 |
| - if (isDefer) deferScripts.push(elementCopy); |
105 |
| - else { |
106 |
| - syncScripts.push(elementCopy); |
107 |
| - if ('head' === node.parentElement.tagName.toLowerCase()) |
108 |
| - headScripts++; |
| 54 | + return waitForHead; |
| 55 | + }; |
| 56 | + const loadNextScript = (isDefer, currentScript) => () => { |
| 57 | + if ( |
| 58 | + !isDefer && |
| 59 | + currentScript && |
| 60 | + 'head' === |
| 61 | + currentScript.parentElement.tagName.toLowerCase() |
| 62 | + ) |
| 63 | + bodyLoader(); |
| 64 | + let nextScript = [...currentDoc.scripts].find( |
| 65 | + (script) => |
| 66 | + script.getAttribute('itemprop') === 'script-insert' && |
| 67 | + script.defer === isDefer |
| 68 | + ); |
| 69 | + if (nextScript) { |
| 70 | + const replacement = isDefer |
| 71 | + ? deferScripts.shift() |
| 72 | + : syncScripts.shift(); |
| 73 | + nextScript.replaceWith(replacement); |
| 74 | + if (replacement.childNodes.length > 0) |
| 75 | + loadNextScript(isDefer, replacement)(); |
| 76 | + } else if (!isDefer && !waitForHead) loadNextScript(true)(); |
| 77 | + else reachedEnd = true; |
| 78 | + }; |
| 79 | + const recursiveClone = (node) => { |
| 80 | + if (node.nodeType !== Node.ELEMENT_NODE) return node; |
| 81 | + const nodeName = node.tagName.toLowerCase(); |
| 82 | + if (['svg', 'xml'].includes(nodeName)) |
| 83 | + return node.cloneNode(1); |
| 84 | + let elementCopy = currentDoc.createElement(nodeName); |
| 85 | + let j = 0, |
| 86 | + nodeList = [...node.attributes]; |
| 87 | + for (; j < nodeList.length; j++) { |
| 88 | + let attrName = nodeList[j].nodeName; |
| 89 | + let attrValue = nodeList[j].nodeValue; |
| 90 | + elementCopy.setAttribute(attrName, attrValue || ''); |
| 91 | + if (attrName.toLowerCase() === 'href') |
| 92 | + try { |
| 93 | + new URL(attrValue); |
| 94 | + } catch (e) { |
| 95 | + if (attrValue[0] === '.' || attrValue[0] === '/') |
| 96 | + elementCopy.addEventListener('click', (event) => { |
| 97 | + event.preventDefault(); |
| 98 | + if (attrValue === '{{route}}{{/}}') |
| 99 | + attrValue = '{{route}}{{/index}}'; |
| 100 | + loadPage(new URL(attrValue, location))(); |
| 101 | + }); |
| 102 | + } |
109 | 103 | }
|
110 |
| - return replacement; |
111 |
| - } else if (['style', 'link'].includes(nodeName)) { |
112 |
| - if ('link' === nodeName && !/^stylesheet$/i.test(node.rel)) |
113 |
| - return elementCopy; |
114 |
| - else if (node.childNodes.length <= 0) { |
115 |
| - elementCopy.addEventListener('load', bodyLoader); |
116 |
| - elementCopy.addEventListener('error', bodyLoader); |
117 |
| - if ('head' === node.parentElement.tagName.toLowerCase()) |
118 |
| - headScripts++; |
| 104 | + nodeList = [...node.childNodes]; |
| 105 | + for (j = 0; j < nodeList.length; j++) |
| 106 | + elementCopy.appendChild(recursiveClone(nodeList[j])); |
| 107 | + if ('script' === nodeName && !node.async) { |
| 108 | + const isDefer = |
| 109 | + node.defer || 'module' === node.type.toLowerCase(); |
| 110 | + let replacement = currentDoc.createElement('script'); |
| 111 | + if (isDefer) replacement.setAttribute('defer', ''); |
| 112 | + replacement.setAttribute('itemprop', 'script-insert'); |
| 113 | + if (node.childNodes.length <= 0) { |
| 114 | + elementCopy.addEventListener( |
| 115 | + 'load', |
| 116 | + loadNextScript(isDefer, elementCopy) |
| 117 | + ); |
| 118 | + elementCopy.addEventListener( |
| 119 | + 'error', |
| 120 | + loadNextScript(isDefer, elementCopy) |
| 121 | + ); |
| 122 | + } |
| 123 | + if (isDefer) deferScripts.push(elementCopy); |
| 124 | + else { |
| 125 | + syncScripts.push(elementCopy); |
| 126 | + if ('head' === node.parentElement.tagName.toLowerCase()) |
| 127 | + headScripts++; |
| 128 | + } |
| 129 | + return replacement; |
| 130 | + } else if (['style', 'link'].includes(nodeName)) { |
| 131 | + if ( |
| 132 | + 'link' === nodeName && |
| 133 | + !/^stylesheet$/i.test(node.rel) |
| 134 | + ) |
| 135 | + return elementCopy; |
| 136 | + else if (node.childNodes.length <= 0) { |
| 137 | + elementCopy.addEventListener('load', bodyLoader); |
| 138 | + elementCopy.addEventListener('error', bodyLoader); |
| 139 | + if ('head' === node.parentElement.tagName.toLowerCase()) |
| 140 | + headScripts++; |
| 141 | + } |
119 | 142 | }
|
120 |
| - } |
121 |
| - return elementCopy; |
122 |
| - }; |
123 |
| - let currentType = currentDoc.doctype, |
124 |
| - newType = newDoc.doctype, |
125 |
| - currentDocNode = currentDoc.documentElement, |
126 |
| - newDocNode = newDoc.documentElement; |
127 |
| - if (currentType) |
128 |
| - if (newType) currentType.replaceWith(newType); |
129 |
| - else currentType.remove(); |
130 |
| - else if (newType) currentDoc.prepend(newType); |
131 |
| - if (currentDocNode) |
132 |
| - if (newDocNode) { |
133 |
| - if ( |
134 |
| - currentDocNode.tagName === newDocNode.tagName && |
135 |
| - currentDoc.head && |
136 |
| - newDoc.head && |
137 |
| - currentDoc.body && |
138 |
| - newDoc.body |
139 |
| - ) { |
140 |
| - [...currentDocNode.attributes].forEach((attribute) => { |
141 |
| - currentDocNode.removeAttribute(attribute.nodeName); |
142 |
| - }); |
143 |
| - [...newDocNode.attributes].forEach((attribute) => { |
144 |
| - currentDocNode.setAttribute( |
145 |
| - attribute.nodeName, |
146 |
| - attribute.nodeValue || '' |
| 143 | + return elementCopy; |
| 144 | + }; |
| 145 | + let currentType = currentDoc.doctype, |
| 146 | + newType = newDoc.doctype, |
| 147 | + currentDocNode = currentDoc.documentElement, |
| 148 | + newDocNode = newDoc.documentElement; |
| 149 | + if (currentType) |
| 150 | + if (newType) currentType.replaceWith(newType); |
| 151 | + else currentType.remove(); |
| 152 | + else if (newType) currentDoc.prepend(newType); |
| 153 | + if (currentDocNode) |
| 154 | + if (newDocNode) { |
| 155 | + if ( |
| 156 | + currentDocNode.tagName === newDocNode.tagName && |
| 157 | + currentDoc.head && |
| 158 | + newDoc.head && |
| 159 | + currentDoc.body && |
| 160 | + newDoc.body |
| 161 | + ) { |
| 162 | + [...currentDocNode.attributes].forEach((attribute) => { |
| 163 | + currentDocNode.removeAttribute(attribute.nodeName); |
| 164 | + }); |
| 165 | + [...newDocNode.attributes].forEach((attribute) => { |
| 166 | + currentDocNode.setAttribute( |
| 167 | + attribute.nodeName, |
| 168 | + attribute.nodeValue || '' |
| 169 | + ); |
| 170 | + }); |
| 171 | + waitForHead = true; |
| 172 | + currentDoc.head.replaceWith( |
| 173 | + recursiveClone(newDoc.head) |
147 | 174 | );
|
148 |
| - }); |
149 |
| - waitForHead = true; |
150 |
| - currentDoc.head.replaceWith(recursiveClone(newDoc.head)); |
151 |
| - } else |
152 |
| - currentDocNode.replaceWith(recursiveClone(newDocNode)); |
153 |
| - } else currentDocNode.remove(); |
154 |
| - else if (newDocNode) |
155 |
| - currentDocNode.appendChild(recursiveClone(newDocNode)); |
| 175 | + } else |
| 176 | + currentDocNode.replaceWith(recursiveClone(newDocNode)); |
| 177 | + } else currentDocNode.remove(); |
| 178 | + else if (newDocNode) |
| 179 | + currentDocNode.appendChild(recursiveClone(newDocNode)); |
156 | 180 |
|
157 |
| - loadNextScript(false)(); |
158 |
| - })(document, new DOMParser().parseFromString(text, 'text/html')); |
159 |
| - }); |
| 181 | + loadNextScript(false)(); |
| 182 | + })( |
| 183 | + document, |
| 184 | + new DOMParser().parseFromString(text, 'text/html') |
| 185 | + ); |
| 186 | + }); |
| 187 | + }); |
| 188 | + }) |
| 189 | + .catch((error) => { |
| 190 | + console.log(error); |
| 191 | + displayErrorPage(); |
160 | 192 | });
|
161 |
| - }) |
162 |
| - .catch((error) => { |
163 |
| - console.log(error); |
164 |
| - displayErrorPage(); |
165 |
| - }); |
166 |
| - }; |
167 |
| - if (document.readyState === 'complete') loadPage(); |
168 |
| - else addEventListener('load', loadPage); |
| 193 | + }; |
| 194 | + if (document.readyState === 'complete') loadPage()(); |
| 195 | + else addEventListener('load', loadPage()); |
| 196 | + addEventListener('popstate', () => { |
| 197 | + loadPage(location, false)(); |
| 198 | + }); |
169 | 199 | })();
|
0 commit comments