single-file-core 1.5.84 → 1.5.86
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/package.json
CHANGED
|
@@ -29,7 +29,7 @@ export {
|
|
|
29
29
|
|
|
30
30
|
async function display(document, docContent, { disableFramePointerEvents } = {}) {
|
|
31
31
|
docContent = docContent.replace(/<noscript/gi, "<template disabled-noscript");
|
|
32
|
-
docContent = docContent.
|
|
32
|
+
docContent = docContent.replace(/<\/noscript/gi, "</template");
|
|
33
33
|
const doc = (new DOMParser()).parseFromString(docContent, "text/html");
|
|
34
34
|
if (disableFramePointerEvents) {
|
|
35
35
|
doc.querySelectorAll("iframe").forEach(element => {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import {
|
|
27
27
|
configure,
|
|
28
|
+
deflateRaw,
|
|
28
29
|
BlobReader,
|
|
29
30
|
TextReader,
|
|
30
31
|
ZipWriter,
|
|
@@ -37,7 +38,10 @@ import {
|
|
|
37
38
|
display
|
|
38
39
|
} from "./compression-display.js";
|
|
39
40
|
|
|
40
|
-
const { Blob, fetch, TextEncoder, DOMParser } = globalThis;
|
|
41
|
+
const { Blob, fetch, TextEncoder, TextDecoder, DOMParser } = globalThis;
|
|
42
|
+
|
|
43
|
+
// windows-1252 never decodes bytes >= 0x80 into the ASCII range, the scanned patterns are all ASCII
|
|
44
|
+
const TEXT_DECODER = new TextDecoder("windows-1252");
|
|
41
45
|
|
|
42
46
|
const NO_COMPRESSION_EXTENSIONS = [".jpg", ".jpeg", ".png", ".avi", ".apng", ".pdf", ".woff2", ".mp4", ".mp3", ".ogg", ".webp", ".webm", ".avi", ".mpeg", ".ts", ".ogv", ".heif", ".heic"];
|
|
43
47
|
const SCRIPT_PATH = "/lib/single-file-zip.min.js";
|
|
@@ -56,17 +60,17 @@ const EMBEDDED_DATA_TAGS = [
|
|
|
56
60
|
...EXTRA_DATA_TAGS,
|
|
57
61
|
];
|
|
58
62
|
const EXTRA_DATA_REGEXPS = [
|
|
59
|
-
[/<noscript/i, /<\/noscript
|
|
60
|
-
[/<noframes/i, /<\/noframes
|
|
61
|
-
[/<noembed/i, /<\/noembed
|
|
62
|
-
[/<script/i, /<\/script
|
|
63
|
-
[/<style/i, /<\/style
|
|
64
|
-
[/<iframe/i, /<\/iframe
|
|
65
|
-
[/<xmp/i, /<\/xmp
|
|
66
|
-
[/<plaintext/i, /<\/plaintext
|
|
63
|
+
[/<noscript/i, /<\/noscript[\t\n\f\r />]/i],
|
|
64
|
+
[/<noframes/i, /<\/noframes[\t\n\f\r />]/i],
|
|
65
|
+
[/<noembed/i, /<\/noembed[\t\n\f\r />]/i],
|
|
66
|
+
[/<script/i, /<\/script[\t\n\f\r />]/i],
|
|
67
|
+
[/<style/i, /<\/style[\t\n\f\r />]/i],
|
|
68
|
+
[/<iframe/i, /<\/iframe[\t\n\f\r />]/i],
|
|
69
|
+
[/<xmp/i, /<\/xmp[\t\n\f\r />]/i],
|
|
70
|
+
[/<plaintext/i, /<\/plaintext[\t\n\f\r />]/i]
|
|
67
71
|
];
|
|
68
72
|
const EMBEDDED_DATA_REGEXPS = [
|
|
69
|
-
[/<!--/i,
|
|
73
|
+
[/<!--/i, /--!?>/i],
|
|
70
74
|
...EXTRA_DATA_REGEXPS,
|
|
71
75
|
];
|
|
72
76
|
const CRC32_TABLE = new Uint32Array(256).map((_, indexTable) => {
|
|
@@ -83,7 +87,8 @@ const PNG_IHDR_LENGTH = 25;
|
|
|
83
87
|
const browser = globalThis.browser;
|
|
84
88
|
|
|
85
89
|
export {
|
|
86
|
-
process
|
|
90
|
+
process,
|
|
91
|
+
createArchive
|
|
87
92
|
};
|
|
88
93
|
|
|
89
94
|
async function process(pageData, options, lastModDate = new Date()) {
|
|
@@ -91,20 +96,28 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
91
96
|
if (options.zipScript) {
|
|
92
97
|
script = options.zipScript;
|
|
93
98
|
} else if (browser && browser.runtime && browser.runtime.getURL) {
|
|
94
|
-
configure({
|
|
99
|
+
configure({ workerURI: "/lib/single-file-z-worker.js" });
|
|
95
100
|
script = await (await fetch(browser.runtime.getURL(SCRIPT_PATH))).text();
|
|
96
101
|
}
|
|
102
|
+
return createArchive(pageData, options, script, zipWriter => {
|
|
103
|
+
pageData.url = options.url;
|
|
104
|
+
pageData.archiveTime = (new Date()).toISOString();
|
|
105
|
+
return addPageResources(zipWriter, pageData, { password: options.password, disableCompression: options.disableCompression }, options.createRootDirectory ? String(Date.now()) + "_" + (options.tabId || 0) + "/" : "", options.url);
|
|
106
|
+
}, lastModDate);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function createArchive(pageData, options, script, writeEntries, lastModDate = new Date()) {
|
|
97
110
|
const zipDataWriter = new Uint8ArrayWriter();
|
|
98
111
|
zipDataWriter.init();
|
|
99
112
|
zipDataWriter.writable.size = 0;
|
|
100
113
|
let extraDataOffset, extraData, embeddedImageDataOffset, endTag;
|
|
101
114
|
if (options.embeddedImage) {
|
|
102
|
-
options.embeddedImage =
|
|
115
|
+
options.embeddedImage = new Uint8Array(options.embeddedImage);
|
|
103
116
|
const embeddedImageData = options.embeddedImage.slice(PNG_SIGNATURE_LENGTH + PNG_IHDR_LENGTH, options.embeddedImage.length - PNG_IEND_LENGTH);
|
|
104
117
|
await writeData(zipDataWriter.writable, options.embeddedImage.slice(0, PNG_SIGNATURE_LENGTH + PNG_IHDR_LENGTH));
|
|
105
118
|
if (options.selfExtractingArchive) {
|
|
106
|
-
const embeddedImageText =
|
|
107
|
-
const tagIndex = EMBEDDED_DATA_REGEXPS.findIndex(tests => !embeddedImageText.match(tests[1]));
|
|
119
|
+
const embeddedImageText = TEXT_DECODER.decode(embeddedImageData);
|
|
120
|
+
const tagIndex = EMBEDDED_DATA_REGEXPS.slice(0, -1).findIndex(tests => !embeddedImageText.match(tests[1]));
|
|
108
121
|
let startTag;
|
|
109
122
|
[startTag, endTag] = tagIndex == -1 ? ["", ""] : EMBEDDED_DATA_TAGS[tagIndex];
|
|
110
123
|
const htmlArray = getStartHTMLArray(pageData, options, startTag);
|
|
@@ -129,33 +142,45 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
129
142
|
} else if (!options.embeddedImage && options.embeddedPdf) {
|
|
130
143
|
await writeData(zipDataWriter.writable, new Uint8Array(options.embeddedPdf));
|
|
131
144
|
}
|
|
132
|
-
const zipWriter = new ZipWriter(zipDataWriter, { bufferedWrite: true, keepOrder: true, lastModDate });
|
|
145
|
+
const zipWriter = new ZipWriter(zipDataWriter, { bufferedWrite: true, keepOrder: true, lastModDate, useCompressionStream: true });
|
|
133
146
|
const startOffset = zipDataWriter.offset;
|
|
134
|
-
|
|
135
|
-
pageData.archiveTime = (new Date()).toISOString();
|
|
136
|
-
await addPageResources(zipWriter, pageData, { password: options.password, disableCompression: options.disableCompression }, options.createRootDirectory ? String(Date.now()) + "_" + (options.tabId || 0) + "/" : "", options.url);
|
|
147
|
+
await writeEntries(zipWriter);
|
|
137
148
|
const data = await zipWriter.close(null, { preventClose: true });
|
|
138
149
|
if (options.selfExtractingArchive) {
|
|
139
|
-
const
|
|
140
|
-
|
|
150
|
+
const lfCodes = [];
|
|
151
|
+
let crc32 = -1;
|
|
141
152
|
if (options.extractDataFromPage) {
|
|
142
|
-
if (!options.extractDataFromPageTags) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
if (!options.extractDataFromPageTags || options.extractDataFromPageTags[0] != "<plaintext>") {
|
|
154
|
+
const textContent = TEXT_DECODER.decode(data.subarray(startOffset));
|
|
155
|
+
if (options.extractDataFromPageTags) {
|
|
156
|
+
const tagIndex = EXTRA_DATA_TAGS.indexOf(options.extractDataFromPageTags);
|
|
157
|
+
const regExpsTag = EXTRA_DATA_REGEXPS[tagIndex];
|
|
158
|
+
if (textContent.match(regExpsTag[0]) || textContent.match(regExpsTag[1])) {
|
|
159
|
+
return findExtraDataTags(textContent, pageData, options, script, writeEntries, lastModDate, tagIndex + 1);
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
const matchCommentTags = textContent.match(/<!--/i) || textContent.match(/--!?>/i);
|
|
163
|
+
if (matchCommentTags) {
|
|
164
|
+
return findExtraDataTags(textContent, pageData, options, script, writeEntries, lastModDate);
|
|
165
|
+
}
|
|
148
166
|
}
|
|
149
167
|
}
|
|
150
168
|
for (let index = startOffset; index < data.length; index++) {
|
|
151
|
-
|
|
169
|
+
const byte = data[index];
|
|
170
|
+
crc32 = (crc32 >>> 8) ^ CRC32_TABLE[(crc32 ^ byte) & 0xff];
|
|
171
|
+
if (byte == 10) {
|
|
172
|
+
lfCodes.push(0);
|
|
173
|
+
} else if (byte == 13) {
|
|
152
174
|
if (data[index + 1] == 10) {
|
|
153
|
-
|
|
175
|
+
index++;
|
|
176
|
+
crc32 = (crc32 >>> 8) ^ CRC32_TABLE[(crc32 ^ 10) & 0xff];
|
|
177
|
+
lfCodes.push(2);
|
|
154
178
|
} else {
|
|
155
|
-
|
|
179
|
+
lfCodes.push(1);
|
|
156
180
|
}
|
|
157
181
|
}
|
|
158
182
|
}
|
|
183
|
+
crc32 = (crc32 ^ -1) >>> 0;
|
|
159
184
|
}
|
|
160
185
|
let pageContent = "";
|
|
161
186
|
if (!options.preventAppendedData) {
|
|
@@ -167,21 +192,22 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
167
192
|
}
|
|
168
193
|
const endTags = options.preventAppendedData || options.embeddedImage ? "" : "</body></html>";
|
|
169
194
|
if (options.extractDataFromPage) {
|
|
170
|
-
|
|
171
|
-
payload
|
|
172
|
-
payload
|
|
173
|
-
payload
|
|
174
|
-
payload
|
|
175
|
-
|
|
195
|
+
// payload layout: [crc32, zip data length, LF codes count, 2-bit codes (0=LF, 1=CR, 2=CRLF) packed LSB-first]
|
|
196
|
+
const payload = new Uint32Array(3 + Math.ceil(lfCodes.length / 16));
|
|
197
|
+
payload[0] = crc32;
|
|
198
|
+
payload[1] = data.length - startOffset;
|
|
199
|
+
payload[2] = lfCodes.length;
|
|
200
|
+
lfCodes.forEach((lfCode, indexLFCode) => payload[3 + (indexLFCode >> 4)] |= lfCode << ((indexLFCode & 15) * 2));
|
|
201
|
+
extraData = "<sfz-extra-data>" + base64Encode(deflateRaw(new Uint8Array(payload.buffer))) + "</sfz-extra-data>";
|
|
176
202
|
if (options.preventAppendedData || extraData.length > 65535 - endTags.length - (options.embeddedImage ? PNG_IEND_LENGTH : 0)) {
|
|
177
203
|
if (!options.extraDataSize) {
|
|
178
204
|
options.extraDataSize = Math.floor(extraData.length * 1.001);
|
|
179
|
-
return
|
|
205
|
+
return createArchive(pageData, options, script, writeEntries, lastModDate);
|
|
180
206
|
}
|
|
181
207
|
} else {
|
|
182
208
|
if (options.extraDataSize) {
|
|
183
209
|
options.extraDataSize = undefined;
|
|
184
|
-
return
|
|
210
|
+
return createArchive(pageData, options, script, writeEntries, lastModDate);
|
|
185
211
|
} else {
|
|
186
212
|
pageContent += extraData;
|
|
187
213
|
}
|
|
@@ -194,11 +220,11 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
194
220
|
const pageContent = await zipDataWriter.getData();
|
|
195
221
|
if (options.extractDataFromPage && options.extraDataSize !== undefined) {
|
|
196
222
|
if (options.extraDataSize >= extraData.length) {
|
|
197
|
-
pageContent.set(
|
|
223
|
+
pageContent.set(new TextEncoder().encode(extraData), startOffset - extraDataOffset);
|
|
198
224
|
} else {
|
|
199
225
|
options.extraData = extraData;
|
|
200
226
|
options.extraDataSize = Math.floor(extraData.length * 1.001);
|
|
201
|
-
return
|
|
227
|
+
return createArchive(pageData, options, script, writeEntries, lastModDate);
|
|
202
228
|
}
|
|
203
229
|
}
|
|
204
230
|
if (options.embeddedImage) {
|
|
@@ -206,7 +232,7 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
206
232
|
return new Blob([
|
|
207
233
|
pageContent,
|
|
208
234
|
getCRC32(pageContent, embeddedImageDataOffset),
|
|
209
|
-
|
|
235
|
+
options.embeddedImage.slice(options.embeddedImage.length - PNG_IEND_LENGTH)
|
|
210
236
|
], { type: "application/octet-stream" });
|
|
211
237
|
} else {
|
|
212
238
|
return new Blob([pageContent], { type: "application/octet-stream" });
|
|
@@ -243,7 +269,10 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
|
|
|
243
269
|
await writeData(zipDataWriter.writable, getStartHTMLArray(pageData, options));
|
|
244
270
|
}
|
|
245
271
|
pageContent += "<div id=sfz-wait-message>Please wait...</div>";
|
|
246
|
-
if (
|
|
272
|
+
if (options.extractDataFromPage) {
|
|
273
|
+
pageContent += "<div id=sfz-error-message><strong>Error</strong>: Cannot extract the data of the page.";
|
|
274
|
+
pageContent += " The file is still a valid ZIP file, you can rename it with a \"zip\" extension and unzip it to display the page and its resources.</div>";
|
|
275
|
+
} else {
|
|
247
276
|
pageContent += "<div id=sfz-error-message><strong>Error</strong>: Cannot open the page from the filesystem.";
|
|
248
277
|
pageContent += "<ul style='line-height:20px;'>";
|
|
249
278
|
pageContent += "<li style='margin-bottom:10px'><strong>Chrome/Edge/Brave</strong>: Install <a href='https://www.getsinglefile.com'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension.</li>";
|
|
@@ -277,7 +306,13 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
|
|
|
277
306
|
"globalThis.bootstrap=(()=>{let bootstrapStarted;return async content=>{if (bootstrapStarted) return bootstrapStarted; bootstrapStarted = (" +
|
|
278
307
|
extract.toString().replace(/\n|\t/g, "") + ")(content,{prompt}).then(({docContent}) => " +
|
|
279
308
|
display.toString().replace(/\n|\t/g, "") + "(document,docContent," + JSON.stringify(displayOptions) + "));return bootstrapStarted;}})();(" +
|
|
280
|
-
getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).then(() => document.dispatchEvent(new CustomEvent(\"single-file-display-infobar\"))).catch(
|
|
309
|
+
getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).then(() => document.dispatchEvent(new CustomEvent(\"single-file-display-infobar\"))).catch(error => {" +
|
|
310
|
+
"console.error(error);" +
|
|
311
|
+
"const waitMessage = document.getElementById(\"sfz-wait-message\");" +
|
|
312
|
+
"if (waitMessage) { waitMessage.remove(); }" +
|
|
313
|
+
"const errorMessage = document.getElementById(\"sfz-error-message\");" +
|
|
314
|
+
"if (errorMessage) { errorMessage.hidden = false; document.body.hidden = false; }" +
|
|
315
|
+
"});" +
|
|
281
316
|
"</script>";
|
|
282
317
|
pageContent += script;
|
|
283
318
|
let extraData = "";
|
|
@@ -287,8 +322,9 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
|
|
|
287
322
|
}
|
|
288
323
|
pageContent += extraData;
|
|
289
324
|
const startTag = options.extractDataFromPageTags ? options.extractDataFromPageTags[0] : "<!--";
|
|
290
|
-
|
|
291
|
-
|
|
325
|
+
// the space guarantees a text node between <sfz-extra-data> and the start tag
|
|
326
|
+
pageContent += (extraData ? " " : "") + startTag;
|
|
327
|
+
const extraDataOffset = startTag.length + extraData.length + (extraData ? 1 : 0);
|
|
292
328
|
await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
|
|
293
329
|
return extraDataOffset;
|
|
294
330
|
}
|
|
@@ -306,8 +342,8 @@ function getStartHTMLArray(pageData, options, startTag = "") {
|
|
|
306
342
|
const htmlHeadData = getHTMLHeadData(pageData, options);
|
|
307
343
|
let htmlArray;
|
|
308
344
|
if (options.embeddedPdf) {
|
|
309
|
-
const embeddedPdfText =
|
|
310
|
-
const pdfTagIndex = EMBEDDED_DATA_REGEXPS.findIndex(tests => !embeddedPdfText.match(tests[1]));
|
|
345
|
+
const embeddedPdfText = TEXT_DECODER.decode(new Uint8Array(options.embeddedPdf));
|
|
346
|
+
const pdfTagIndex = EMBEDDED_DATA_REGEXPS.slice(0, -1).findIndex(tests => !embeddedPdfText.match(tests[1]));
|
|
311
347
|
const [pdfStartTag, pdfEndTag] = pdfTagIndex == -1 ? ["", ""] : EMBEDDED_DATA_TAGS[pdfTagIndex];
|
|
312
348
|
const htmlArray1 = new TextEncoder().encode(html + pdfStartTag);
|
|
313
349
|
const htmlArray2 = new TextEncoder().encode(pdfEndTag + htmlHeadData + startTag);
|
|
@@ -347,19 +383,24 @@ function getPageTitle(pageData) {
|
|
|
347
383
|
return pageData.title.replace(/</g, "<").replace(/>/g, ">") || "";
|
|
348
384
|
}
|
|
349
385
|
|
|
350
|
-
function findExtraDataTags(textContent, pageData, options, lastModDate, indexExtractDataFromPageTags = 0) {
|
|
386
|
+
function findExtraDataTags(textContent, pageData, options, script, writeEntries, lastModDate, indexExtractDataFromPageTags = 0) {
|
|
351
387
|
const regExpsTag = EXTRA_DATA_REGEXPS[indexExtractDataFromPageTags];
|
|
352
|
-
const
|
|
388
|
+
const plaintextTag = EXTRA_DATA_TAGS[indexExtractDataFromPageTags][0] == "<plaintext>";
|
|
389
|
+
const matchTag = !plaintextTag && (textContent.match(regExpsTag[0]) || textContent.match(regExpsTag[1]));
|
|
353
390
|
if (matchTag) {
|
|
354
391
|
if (indexExtractDataFromPageTags < EXTRA_DATA_TAGS.length - 1) {
|
|
355
|
-
return findExtraDataTags(textContent, pageData, options, lastModDate, indexExtractDataFromPageTags + 1);
|
|
392
|
+
return findExtraDataTags(textContent, pageData, options, script, writeEntries, lastModDate, indexExtractDataFromPageTags + 1);
|
|
356
393
|
} else {
|
|
357
394
|
options.extractDataFromPage = false;
|
|
358
|
-
return
|
|
395
|
+
return createArchive(pageData, options, script, writeEntries, lastModDate);
|
|
359
396
|
}
|
|
360
397
|
} else {
|
|
361
398
|
options.extractDataFromPageTags = EXTRA_DATA_TAGS[indexExtractDataFromPageTags];
|
|
362
|
-
|
|
399
|
+
if (options.extractDataFromPageTags[0] == "<plaintext>") {
|
|
400
|
+
// <plaintext> cannot be closed, the file must end with the zip data
|
|
401
|
+
options.preventAppendedData = true;
|
|
402
|
+
}
|
|
403
|
+
return createArchive(pageData, options, script, writeEntries, lastModDate);
|
|
363
404
|
}
|
|
364
405
|
}
|
|
365
406
|
|
|
@@ -422,29 +463,45 @@ async function getContent() {
|
|
|
422
463
|
[352, 138], [8249, 139], [338, 140], [381, 142], [8216, 145], [8217, 146], [8220, 147], [8221, 148], [8226, 149], [8211, 150],
|
|
423
464
|
[8212, 151], [732, 152], [8482, 153], [353, 154], [8250, 155], [339, 156], [382, 158], [376, 159]
|
|
424
465
|
]);
|
|
466
|
+
const crc32Table = new Uint32Array(256).map((_, indexTable) => {
|
|
467
|
+
let crc = indexTable;
|
|
468
|
+
for (let indexBits = 0; indexBits < 8; indexBits++) {
|
|
469
|
+
crc = crc & 1 ? 0xEDB88320 ^ (crc >>> 1) : crc >>> 1;
|
|
470
|
+
}
|
|
471
|
+
return crc;
|
|
472
|
+
});
|
|
425
473
|
return new Promise((resolve, reject) => {
|
|
426
474
|
let aborted = false;
|
|
427
|
-
|
|
475
|
+
if (location.protocol == "file:") {
|
|
476
|
+
extractDataFromDocument();
|
|
477
|
+
} else {
|
|
478
|
+
getPageData();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async function extractDataFromDocument() {
|
|
482
|
+
try {
|
|
483
|
+
await waitForDocumentReady(document);
|
|
484
|
+
document.body.querySelectorAll("meta, style").forEach(element => document.head.appendChild(element));
|
|
485
|
+
const pageData = extractPageData();
|
|
486
|
+
displayMessage("sfz-wait-message", 2);
|
|
487
|
+
resolve(pageData);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error(error);
|
|
490
|
+
displayMessage("sfz-error-message", 2);
|
|
491
|
+
reject(error);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
428
494
|
|
|
429
495
|
function getPageData() {
|
|
430
496
|
const xhr = new XMLHttpRequest();
|
|
431
497
|
xhr.responseType = "blob";
|
|
432
498
|
xhr.open("GET", "");
|
|
433
|
-
xhr.onerror =
|
|
499
|
+
xhr.onerror = () => {
|
|
434
500
|
if (aborted) {
|
|
435
501
|
displayMessage("sfz-error-message", 2);
|
|
436
502
|
reject();
|
|
437
503
|
} else {
|
|
438
|
-
|
|
439
|
-
await waitForDocumentReady(document);
|
|
440
|
-
document.body.querySelectorAll("meta, style").forEach(element => document.head.appendChild(element));
|
|
441
|
-
const pageData = extractPageData();
|
|
442
|
-
displayMessage("sfz-wait-message", 2);
|
|
443
|
-
resolve(pageData);
|
|
444
|
-
} catch {
|
|
445
|
-
displayMessage("sfz-error-message", 2);
|
|
446
|
-
reject();
|
|
447
|
-
};
|
|
504
|
+
extractDataFromDocument();
|
|
448
505
|
}
|
|
449
506
|
};
|
|
450
507
|
xhr.send();
|
|
@@ -485,9 +542,14 @@ async function getContent() {
|
|
|
485
542
|
if (element) {
|
|
486
543
|
Array.from(document.body.childNodes).forEach(node => {
|
|
487
544
|
if (node.id != elementId) {
|
|
488
|
-
node.
|
|
545
|
+
if (node.id == "sfz-wait-message" || node.id == "sfz-error-message") {
|
|
546
|
+
node.hidden = true;
|
|
547
|
+
} else {
|
|
548
|
+
node.remove();
|
|
549
|
+
}
|
|
489
550
|
}
|
|
490
551
|
});
|
|
552
|
+
element.hidden = false;
|
|
491
553
|
document.body.hidden = false;
|
|
492
554
|
element.style = "opacity: 0; animation: 0s linear " + delay + "s display-wait-message 1 normal forwards";
|
|
493
555
|
}
|
|
@@ -506,59 +568,46 @@ async function getContent() {
|
|
|
506
568
|
} else {
|
|
507
569
|
dataNode = zipDataElement.previousSibling;
|
|
508
570
|
}
|
|
509
|
-
const
|
|
510
|
-
|
|
571
|
+
const inflatedPayload = zip.inflateRaw(base64Decode(zipDataElement.textContent));
|
|
572
|
+
const payload = new Uint32Array(inflatedPayload.buffer, inflatedPayload.byteOffset, inflatedPayload.length >> 2);
|
|
573
|
+
const expectedCRC32 = payload[0];
|
|
574
|
+
const zipDataLength = payload[1];
|
|
575
|
+
const lfCodesLength = payload[2];
|
|
576
|
+
const zipData = new Uint8Array(zipDataLength);
|
|
577
|
+
const { textContent } = dataNode;
|
|
578
|
+
let offset = 0;
|
|
579
|
+
let indexLFCode = 0;
|
|
580
|
+
let crc32 = -1;
|
|
511
581
|
for (let index = 0; index < textContent.length; index++) {
|
|
512
582
|
const charCode = textContent.charCodeAt(index);
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
function decompress(src) {
|
|
528
|
-
src = base64Decode(src);
|
|
529
|
-
let out = new Uint8Array(1024);
|
|
530
|
-
let outLen = 0;
|
|
531
|
-
for (let i = 0; i < src.length;) {
|
|
532
|
-
const ctrl = src[i++];
|
|
533
|
-
if ((ctrl & 0x80) === 0) {
|
|
534
|
-
const L = ctrl;
|
|
535
|
-
ensure(outLen + L);
|
|
536
|
-
for (let j = 0; j < L && i < src.length; j++) {
|
|
537
|
-
out[outLen++] = src[i++];
|
|
538
|
-
}
|
|
539
|
-
} else {
|
|
540
|
-
const L = (ctrl & 0x7f) + 3;
|
|
541
|
-
const off = (src[i++] << 8) | src[i++];
|
|
542
|
-
const start = outLen - off;
|
|
543
|
-
ensure(outLen + L);
|
|
544
|
-
for (let k = 0; k < L; k++) {
|
|
545
|
-
out[outLen++] = out[start + k];
|
|
583
|
+
if (charCode == 10) {
|
|
584
|
+
const lfCode = (payload[3 + (indexLFCode >> 4)] >>> ((indexLFCode & 15) * 2)) & 3;
|
|
585
|
+
indexLFCode++;
|
|
586
|
+
if (lfCode == 0) {
|
|
587
|
+
writeByte(10);
|
|
588
|
+
} else {
|
|
589
|
+
writeByte(13);
|
|
590
|
+
if (lfCode == 2) {
|
|
591
|
+
writeByte(10);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
} else {
|
|
595
|
+
writeByte(charCode > 255 ? characterMap.get(charCode) : charCode);
|
|
546
596
|
}
|
|
547
597
|
}
|
|
548
|
-
|
|
549
|
-
|
|
598
|
+
crc32 = (crc32 ^ -1) >>> 0;
|
|
599
|
+
if (offset != zipDataLength || indexLFCode != lfCodesLength || crc32 != expectedCRC32) {
|
|
600
|
+
throw new Error("Invalid checksum of the extracted zip data");
|
|
601
|
+
}
|
|
602
|
+
return new Blob([zipData], { type: "application/octet-stream" });
|
|
550
603
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
nl *= 2;
|
|
556
|
-
}
|
|
557
|
-
const nbuf = new Uint8Array(nl);
|
|
558
|
-
nbuf.set(out.subarray(0, outLen));
|
|
559
|
-
out = nbuf;
|
|
604
|
+
function writeByte(byte) {
|
|
605
|
+
zipData[offset] = byte;
|
|
606
|
+
crc32 = (crc32 >>> 8) ^ crc32Table[(crc32 ^ byte) & 0xff];
|
|
607
|
+
offset++;
|
|
560
608
|
}
|
|
561
|
-
}
|
|
609
|
+
}
|
|
610
|
+
throw new Error("Extra zip data data not found");
|
|
562
611
|
}
|
|
563
612
|
|
|
564
613
|
function base64Decode(b64) {
|
|
@@ -603,94 +652,3 @@ function base64Encode(bytes) {
|
|
|
603
652
|
}
|
|
604
653
|
return out;
|
|
605
654
|
}
|
|
606
|
-
|
|
607
|
-
function compress(input) {
|
|
608
|
-
const src = new Uint8Array(input);
|
|
609
|
-
const N = src.length;
|
|
610
|
-
const out = [];
|
|
611
|
-
const litBuf = [];
|
|
612
|
-
const MAX_OFFSET = 0xffff;
|
|
613
|
-
const MAX_MATCH = 130;
|
|
614
|
-
const MAX_CANDIDATES = 64;
|
|
615
|
-
const map = new Map();
|
|
616
|
-
let i = 0;
|
|
617
|
-
while (i < N) {
|
|
618
|
-
let bestLen = 0, bestOff = 0;
|
|
619
|
-
if (i + 2 < N) {
|
|
620
|
-
const key = (src[i] << 16) | (src[i + 1] << 8) | src[i + 2];
|
|
621
|
-
const cand = map.get(key) || [];
|
|
622
|
-
for (let c = cand.length - 1; c >= 0; c--) {
|
|
623
|
-
const j = cand[c];
|
|
624
|
-
const off = i - j;
|
|
625
|
-
if (off <= 0 || off > MAX_OFFSET) {
|
|
626
|
-
continue;
|
|
627
|
-
}
|
|
628
|
-
let k = 0;
|
|
629
|
-
while (k < MAX_MATCH && i + k < N && src[j + k] === src[i + k]) {
|
|
630
|
-
k++;
|
|
631
|
-
}
|
|
632
|
-
if (k > bestLen && k >= 3) {
|
|
633
|
-
bestLen = k; bestOff = off;
|
|
634
|
-
if (bestLen === MAX_MATCH) {
|
|
635
|
-
break;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (bestLen >= 3) {
|
|
642
|
-
if (litBuf.length) {
|
|
643
|
-
flushLiterals();
|
|
644
|
-
}
|
|
645
|
-
let remain = bestLen;
|
|
646
|
-
let produced = 0;
|
|
647
|
-
while (remain > 0) {
|
|
648
|
-
const take = Math.min(remain, MAX_MATCH);
|
|
649
|
-
out.push(0x80 | ((take - 3) & 0x7f));
|
|
650
|
-
out.push((bestOff >> 8) & 0xff);
|
|
651
|
-
out.push(bestOff & 0xff);
|
|
652
|
-
remain -= take;
|
|
653
|
-
produced += take;
|
|
654
|
-
}
|
|
655
|
-
const start = i;
|
|
656
|
-
for (let p = start; p < start + produced; p++) {
|
|
657
|
-
addPos(p);
|
|
658
|
-
}
|
|
659
|
-
i += produced;
|
|
660
|
-
} else {
|
|
661
|
-
litBuf.push(src[i]);
|
|
662
|
-
addPos(i);
|
|
663
|
-
i++;
|
|
664
|
-
if (litBuf.length === 127) {
|
|
665
|
-
flushLiterals();
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
if (litBuf.length) {
|
|
670
|
-
flushLiterals();
|
|
671
|
-
}
|
|
672
|
-
const u8 = new Uint8Array(out);
|
|
673
|
-
return base64Encode(u8);
|
|
674
|
-
|
|
675
|
-
function flushLiterals() {
|
|
676
|
-
while (litBuf.length) {
|
|
677
|
-
const take = Math.min(127, litBuf.length);
|
|
678
|
-
out.push(take);
|
|
679
|
-
for (let t = 0; t < take; t++) {
|
|
680
|
-
out.push(litBuf.shift());
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function addPos(pos) {
|
|
686
|
-
if (pos + 2 < N) {
|
|
687
|
-
const key = (src[pos] << 16) | (src[pos + 1] << 8) | src[pos + 2];
|
|
688
|
-
const arr = map.get(key) || [];
|
|
689
|
-
arr.push(pos);
|
|
690
|
-
if (arr.length > MAX_CANDIDATES) {
|
|
691
|
-
arr.shift();
|
|
692
|
-
}
|
|
693
|
-
map.set(key, arr);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|