single-file-core 1.5.83 → 1.5.85
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/core/lib/processor-helper-inline.js +1 -1
- package/modules/html-minifier.js +13 -2
- package/modules/template-parser.js +1 -1
- package/package.json +1 -1
- package/processors/compression/compression-display.js +1 -1
- package/processors/compression/compression-extract.js +16 -8
- package/processors/compression/compression.js +186 -200
- package/vendor/zip/z-worker.js +1 -1
- package/vendor/zip/zip.js +5135 -1412
- package/vendor/zip/zip.min.js +1 -1
|
@@ -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) => {
|
|
@@ -91,7 +95,7 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
91
95
|
if (options.zipScript) {
|
|
92
96
|
script = options.zipScript;
|
|
93
97
|
} else if (browser && browser.runtime && browser.runtime.getURL) {
|
|
94
|
-
configure({
|
|
98
|
+
configure({ workerURI: "/lib/single-file-z-worker.js" });
|
|
95
99
|
script = await (await fetch(browser.runtime.getURL(SCRIPT_PATH))).text();
|
|
96
100
|
}
|
|
97
101
|
const zipDataWriter = new Uint8ArrayWriter();
|
|
@@ -103,8 +107,8 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
103
107
|
const embeddedImageData = options.embeddedImage.slice(PNG_SIGNATURE_LENGTH + PNG_IHDR_LENGTH, options.embeddedImage.length - PNG_IEND_LENGTH);
|
|
104
108
|
await writeData(zipDataWriter.writable, options.embeddedImage.slice(0, PNG_SIGNATURE_LENGTH + PNG_IHDR_LENGTH));
|
|
105
109
|
if (options.selfExtractingArchive) {
|
|
106
|
-
const embeddedImageText =
|
|
107
|
-
const tagIndex = EMBEDDED_DATA_REGEXPS.findIndex(tests => !embeddedImageText.match(tests[1]));
|
|
110
|
+
const embeddedImageText = TEXT_DECODER.decode(new Uint8Array(embeddedImageData));
|
|
111
|
+
const tagIndex = EMBEDDED_DATA_REGEXPS.slice(0, -1).findIndex(tests => !embeddedImageText.match(tests[1]));
|
|
108
112
|
let startTag;
|
|
109
113
|
[startTag, endTag] = tagIndex == -1 ? ["", ""] : EMBEDDED_DATA_TAGS[tagIndex];
|
|
110
114
|
const htmlArray = getStartHTMLArray(pageData, options, startTag);
|
|
@@ -129,33 +133,47 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
129
133
|
} else if (!options.embeddedImage && options.embeddedPdf) {
|
|
130
134
|
await writeData(zipDataWriter.writable, new Uint8Array(options.embeddedPdf));
|
|
131
135
|
}
|
|
132
|
-
const zipWriter = new ZipWriter(zipDataWriter, { bufferedWrite: true, keepOrder:
|
|
136
|
+
const zipWriter = new ZipWriter(zipDataWriter, { bufferedWrite: true, keepOrder: true, lastModDate, useCompressionStream: true });
|
|
133
137
|
const startOffset = zipDataWriter.offset;
|
|
134
138
|
pageData.url = options.url;
|
|
135
139
|
pageData.archiveTime = (new Date()).toISOString();
|
|
136
140
|
await addPageResources(zipWriter, pageData, { password: options.password, disableCompression: options.disableCompression }, options.createRootDirectory ? String(Date.now()) + "_" + (options.tabId || 0) + "/" : "", options.url);
|
|
137
141
|
const data = await zipWriter.close(null, { preventClose: true });
|
|
138
142
|
if (options.selfExtractingArchive) {
|
|
139
|
-
const
|
|
140
|
-
|
|
143
|
+
const lfCodes = [];
|
|
144
|
+
let crc32 = -1;
|
|
141
145
|
if (options.extractDataFromPage) {
|
|
142
|
-
if (!options.extractDataFromPageTags) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
if (!options.extractDataFromPageTags || options.extractDataFromPageTags[0] != "<plaintext>") {
|
|
147
|
+
const textContent = TEXT_DECODER.decode(data.subarray(startOffset));
|
|
148
|
+
if (options.extractDataFromPageTags) {
|
|
149
|
+
const tagIndex = EXTRA_DATA_TAGS.indexOf(options.extractDataFromPageTags);
|
|
150
|
+
const regExpsTag = EXTRA_DATA_REGEXPS[tagIndex];
|
|
151
|
+
if (textContent.match(regExpsTag[0]) || textContent.match(regExpsTag[1])) {
|
|
152
|
+
return findExtraDataTags(textContent, pageData, options, lastModDate, tagIndex + 1);
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
const matchCommentTags = textContent.match(/<!--/i) || textContent.match(/--!?>/i);
|
|
156
|
+
if (matchCommentTags) {
|
|
157
|
+
return findExtraDataTags(textContent, pageData, options, lastModDate);
|
|
158
|
+
}
|
|
148
159
|
}
|
|
149
160
|
}
|
|
150
161
|
for (let index = startOffset; index < data.length; index++) {
|
|
151
|
-
|
|
162
|
+
const byte = data[index];
|
|
163
|
+
crc32 = (crc32 >>> 8) ^ CRC32_TABLE[(crc32 ^ byte) & 0xff];
|
|
164
|
+
if (byte == 10) {
|
|
165
|
+
lfCodes.push(0);
|
|
166
|
+
} else if (byte == 13) {
|
|
152
167
|
if (data[index + 1] == 10) {
|
|
153
|
-
|
|
168
|
+
index++;
|
|
169
|
+
crc32 = (crc32 >>> 8) ^ CRC32_TABLE[(crc32 ^ 10) & 0xff];
|
|
170
|
+
lfCodes.push(2);
|
|
154
171
|
} else {
|
|
155
|
-
|
|
172
|
+
lfCodes.push(1);
|
|
156
173
|
}
|
|
157
174
|
}
|
|
158
175
|
}
|
|
176
|
+
crc32 = (crc32 ^ -1) >>> 0;
|
|
159
177
|
}
|
|
160
178
|
let pageContent = "";
|
|
161
179
|
if (!options.preventAppendedData) {
|
|
@@ -167,12 +185,13 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
167
185
|
}
|
|
168
186
|
const endTags = options.preventAppendedData || options.embeddedImage ? "" : "</body></html>";
|
|
169
187
|
if (options.extractDataFromPage) {
|
|
170
|
-
|
|
171
|
-
payload
|
|
172
|
-
payload
|
|
173
|
-
payload
|
|
174
|
-
payload
|
|
175
|
-
|
|
188
|
+
// payload layout: [crc32, zip data length, LF codes count, 2-bit codes (0=LF, 1=CR, 2=CRLF) packed LSB-first]
|
|
189
|
+
const payload = new Uint32Array(3 + Math.ceil(lfCodes.length / 16));
|
|
190
|
+
payload[0] = crc32;
|
|
191
|
+
payload[1] = data.length - startOffset;
|
|
192
|
+
payload[2] = lfCodes.length;
|
|
193
|
+
lfCodes.forEach((lfCode, indexLFCode) => payload[3 + (indexLFCode >> 4)] |= lfCode << ((indexLFCode & 15) * 2));
|
|
194
|
+
extraData = "<sfz-extra-data>" + base64Encode(deflateRaw(new Uint8Array(payload.buffer))) + "</sfz-extra-data>";
|
|
176
195
|
if (options.preventAppendedData || extraData.length > 65535 - endTags.length - (options.embeddedImage ? PNG_IEND_LENGTH : 0)) {
|
|
177
196
|
if (!options.extraDataSize) {
|
|
178
197
|
options.extraDataSize = Math.floor(extraData.length * 1.001);
|
|
@@ -194,7 +213,7 @@ async function process(pageData, options, lastModDate = new Date()) {
|
|
|
194
213
|
const pageContent = await zipDataWriter.getData();
|
|
195
214
|
if (options.extractDataFromPage && options.extraDataSize !== undefined) {
|
|
196
215
|
if (options.extraDataSize >= extraData.length) {
|
|
197
|
-
pageContent.set(
|
|
216
|
+
pageContent.set(new TextEncoder().encode(extraData), startOffset - extraDataOffset);
|
|
198
217
|
} else {
|
|
199
218
|
options.extraData = extraData;
|
|
200
219
|
options.extraDataSize = Math.floor(extraData.length * 1.001);
|
|
@@ -243,11 +262,13 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
|
|
|
243
262
|
await writeData(zipDataWriter.writable, getStartHTMLArray(pageData, options));
|
|
244
263
|
}
|
|
245
264
|
pageContent += "<div id=sfz-wait-message>Please wait...</div>";
|
|
246
|
-
if (
|
|
265
|
+
if (options.extractDataFromPage) {
|
|
266
|
+
pageContent += "<div id=sfz-error-message><strong>Error</strong>: Cannot extract the data of the page.";
|
|
267
|
+
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>";
|
|
268
|
+
} else {
|
|
247
269
|
pageContent += "<div id=sfz-error-message><strong>Error</strong>: Cannot open the page from the filesystem.";
|
|
248
270
|
pageContent += "<ul style='line-height:20px;'>";
|
|
249
|
-
pageContent += "<li style='margin-bottom:10px'><strong>Chrome</strong>: Install <a href='https://
|
|
250
|
-
pageContent += "<li style='margin-bottom:10px'><strong>Microsoft Edge</strong>: Install <a href='https://microsoftedge.microsoft.com/addons/detail/singlefile/efnbkdcfmcmnhlkaijjjmhjjgladedno'>SingleFile</a> and enable the option \"Allow access to file URLs\" in the details page of the extension (edge://extensions/?id=efnbkdcfmcmnhlkaijjjmhjjgladedno).</li>";
|
|
271
|
+
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>";
|
|
251
272
|
pageContent += "<li><strong>Safari</strong>: Select \"Security > Disable Local File Restrictions\" in the \"Develop > Developer settings\" menu.</li></ul></div>";
|
|
252
273
|
}
|
|
253
274
|
if (options.insertTextBody) {
|
|
@@ -275,11 +296,15 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
|
|
|
275
296
|
script = "<script>" +
|
|
276
297
|
script +
|
|
277
298
|
"document.currentScript.remove();" +
|
|
278
|
-
"globalThis.addEventListener('load', () => {" +
|
|
279
299
|
"globalThis.bootstrap=(()=>{let bootstrapStarted;return async content=>{if (bootstrapStarted) return bootstrapStarted; bootstrapStarted = (" +
|
|
280
300
|
extract.toString().replace(/\n|\t/g, "") + ")(content,{prompt}).then(({docContent}) => " +
|
|
281
301
|
display.toString().replace(/\n|\t/g, "") + "(document,docContent," + JSON.stringify(displayOptions) + "));return bootstrapStarted;}})();(" +
|
|
282
|
-
getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).then(() => document.dispatchEvent(new CustomEvent(\"single-file-display-infobar\"))).catch(
|
|
302
|
+
getContent.toString().replace(/\n|\t/g, "") + ")().then(globalThis.bootstrap).then(() => document.dispatchEvent(new CustomEvent(\"single-file-display-infobar\"))).catch(error => {" +
|
|
303
|
+
"console.error(error);" +
|
|
304
|
+
"const waitMessage = document.getElementById(\"sfz-wait-message\");" +
|
|
305
|
+
"if (waitMessage) { waitMessage.remove(); }" +
|
|
306
|
+
"const errorMessage = document.getElementById(\"sfz-error-message\");" +
|
|
307
|
+
"if (errorMessage) { errorMessage.hidden = false; document.body.hidden = false; }" +
|
|
283
308
|
"});" +
|
|
284
309
|
"</script>";
|
|
285
310
|
pageContent += script;
|
|
@@ -290,8 +315,9 @@ async function prependHTMLData(pageData, zipDataWriter, script, options) {
|
|
|
290
315
|
}
|
|
291
316
|
pageContent += extraData;
|
|
292
317
|
const startTag = options.extractDataFromPageTags ? options.extractDataFromPageTags[0] : "<!--";
|
|
293
|
-
|
|
294
|
-
|
|
318
|
+
// the space guarantees a text node between <sfz-extra-data> and the start tag
|
|
319
|
+
pageContent += (extraData ? " " : "") + startTag;
|
|
320
|
+
const extraDataOffset = startTag.length + extraData.length + (extraData ? 1 : 0);
|
|
295
321
|
await writeData(zipDataWriter.writable, (new TextEncoder()).encode(pageContent));
|
|
296
322
|
return extraDataOffset;
|
|
297
323
|
}
|
|
@@ -309,8 +335,8 @@ function getStartHTMLArray(pageData, options, startTag = "") {
|
|
|
309
335
|
const htmlHeadData = getHTMLHeadData(pageData, options);
|
|
310
336
|
let htmlArray;
|
|
311
337
|
if (options.embeddedPdf) {
|
|
312
|
-
const embeddedPdfText =
|
|
313
|
-
const pdfTagIndex = EMBEDDED_DATA_REGEXPS.findIndex(tests => !embeddedPdfText.match(tests[1]));
|
|
338
|
+
const embeddedPdfText = TEXT_DECODER.decode(new Uint8Array(options.embeddedPdf));
|
|
339
|
+
const pdfTagIndex = EMBEDDED_DATA_REGEXPS.slice(0, -1).findIndex(tests => !embeddedPdfText.match(tests[1]));
|
|
314
340
|
const [pdfStartTag, pdfEndTag] = pdfTagIndex == -1 ? ["", ""] : EMBEDDED_DATA_TAGS[pdfTagIndex];
|
|
315
341
|
const htmlArray1 = new TextEncoder().encode(html + pdfStartTag);
|
|
316
342
|
const htmlArray2 = new TextEncoder().encode(pdfEndTag + htmlHeadData + startTag);
|
|
@@ -341,7 +367,7 @@ function getHTMLHeadData(pageData, options) {
|
|
|
341
367
|
const cspContent = "default-src 'none';connect-src 'self' data: blob:;font-src 'self' data: blob:;img-src 'self' data: blob:;style-src 'self' 'unsafe-inline' data: blob:;frame-src 'self' data: blob:;media-src 'self' data: blob:;script-src 'self' 'unsafe-inline' data: blob:;object-src 'self' data: blob:";
|
|
342
368
|
pageContent += `<meta http-equiv=content-security-policy content=${JSON.stringify(cspContent)}>`;
|
|
343
369
|
}
|
|
344
|
-
pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}};body{color:transparent};div{color:initial}</style>";
|
|
370
|
+
pageContent += "<style>@keyframes display-wait-message{0%{opacity:0}100%{opacity:1}};body{color:transparent};div{color:initial}body>:not(#sfz-wait-message,#sfz-error-message){display:none}</style>";
|
|
345
371
|
pageContent += "<body hidden>";
|
|
346
372
|
return pageContent;
|
|
347
373
|
}
|
|
@@ -352,7 +378,8 @@ function getPageTitle(pageData) {
|
|
|
352
378
|
|
|
353
379
|
function findExtraDataTags(textContent, pageData, options, lastModDate, indexExtractDataFromPageTags = 0) {
|
|
354
380
|
const regExpsTag = EXTRA_DATA_REGEXPS[indexExtractDataFromPageTags];
|
|
355
|
-
const
|
|
381
|
+
const plaintextTag = EXTRA_DATA_TAGS[indexExtractDataFromPageTags][0] == "<plaintext>";
|
|
382
|
+
const matchTag = !plaintextTag && (textContent.match(regExpsTag[0]) || textContent.match(regExpsTag[1]));
|
|
356
383
|
if (matchTag) {
|
|
357
384
|
if (indexExtractDataFromPageTags < EXTRA_DATA_TAGS.length - 1) {
|
|
358
385
|
return findExtraDataTags(textContent, pageData, options, lastModDate, indexExtractDataFromPageTags + 1);
|
|
@@ -362,6 +389,10 @@ function findExtraDataTags(textContent, pageData, options, lastModDate, indexExt
|
|
|
362
389
|
}
|
|
363
390
|
} else {
|
|
364
391
|
options.extractDataFromPageTags = EXTRA_DATA_TAGS[indexExtractDataFromPageTags];
|
|
392
|
+
if (options.extractDataFromPageTags[0] == "<plaintext>") {
|
|
393
|
+
// <plaintext> cannot be closed, the file must end with the zip data
|
|
394
|
+
options.preventAppendedData = true;
|
|
395
|
+
}
|
|
365
396
|
return process(pageData, options, lastModDate);
|
|
366
397
|
}
|
|
367
398
|
}
|
|
@@ -419,45 +450,105 @@ async function addFile(zipWriter, prefixName, data, disableCompresson) {
|
|
|
419
450
|
|
|
420
451
|
async function getContent() {
|
|
421
452
|
const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
422
|
-
const { Blob, XMLHttpRequest, document,
|
|
453
|
+
const { Blob, XMLHttpRequest, document, zip, location } = globalThis;
|
|
423
454
|
const characterMap = new Map([
|
|
424
455
|
[65533, 0], [8364, 128], [8218, 130], [402, 131], [8222, 132], [8230, 133], [8224, 134], [8225, 135], [710, 136], [8240, 137],
|
|
425
456
|
[352, 138], [8249, 139], [338, 140], [381, 142], [8216, 145], [8217, 146], [8220, 147], [8221, 148], [8226, 149], [8211, 150],
|
|
426
457
|
[8212, 151], [732, 152], [8482, 153], [353, 154], [8250, 155], [339, 156], [382, 158], [376, 159]
|
|
427
458
|
]);
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
459
|
+
const crc32Table = new Uint32Array(256).map((_, indexTable) => {
|
|
460
|
+
let crc = indexTable;
|
|
461
|
+
for (let indexBits = 0; indexBits < 8; indexBits++) {
|
|
462
|
+
crc = crc & 1 ? 0xEDB88320 ^ (crc >>> 1) : crc >>> 1;
|
|
463
|
+
}
|
|
464
|
+
return crc;
|
|
465
|
+
});
|
|
432
466
|
return new Promise((resolve, reject) => {
|
|
433
|
-
|
|
434
|
-
|
|
467
|
+
let aborted = false;
|
|
468
|
+
if (location.protocol == "file:") {
|
|
469
|
+
extractDataFromDocument();
|
|
470
|
+
} else {
|
|
471
|
+
getPageData();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function extractDataFromDocument() {
|
|
475
|
+
try {
|
|
476
|
+
await waitForDocumentReady(document);
|
|
477
|
+
document.body.querySelectorAll("meta, style").forEach(element => document.head.appendChild(element));
|
|
478
|
+
const pageData = extractPageData();
|
|
479
|
+
displayMessage("sfz-wait-message", 2);
|
|
480
|
+
resolve(pageData);
|
|
481
|
+
} catch (error) {
|
|
482
|
+
console.error(error);
|
|
435
483
|
displayMessage("sfz-error-message", 2);
|
|
436
|
-
reject();
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
484
|
+
reject(error);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function getPageData() {
|
|
489
|
+
const xhr = new XMLHttpRequest();
|
|
490
|
+
xhr.responseType = "blob";
|
|
491
|
+
xhr.open("GET", "");
|
|
492
|
+
xhr.onerror = () => {
|
|
493
|
+
if (aborted) {
|
|
494
|
+
displayMessage("sfz-error-message", 2);
|
|
495
|
+
reject();
|
|
496
|
+
} else {
|
|
497
|
+
extractDataFromDocument();
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
xhr.send();
|
|
501
|
+
xhr.onreadystatechange = () => {
|
|
502
|
+
if (xhr.readyState === 2 && xhr.status === 200 && !aborted) {
|
|
503
|
+
aborted = true;
|
|
504
|
+
const httpRangeSupport = xhr.getResponseHeader("Accept-Ranges") === "bytes";
|
|
505
|
+
xhr.abort();
|
|
506
|
+
displayMessage("sfz-wait-message", 2);
|
|
507
|
+
if (httpRangeSupport) {
|
|
508
|
+
resolve(new zip.HttpRangeReader(location.href, {
|
|
509
|
+
useXHR: true,
|
|
510
|
+
combineSizeEocd: true
|
|
511
|
+
}));
|
|
512
|
+
} else {
|
|
513
|
+
getPageData();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
if (aborted) {
|
|
518
|
+
xhr.onload = () => resolve(xhr.response);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
445
521
|
});
|
|
446
522
|
|
|
523
|
+
function waitForDocumentReady(document) {
|
|
524
|
+
return new Promise(resolve => {
|
|
525
|
+
if (document.readyState === "complete" || document.readyState === "interactive") {
|
|
526
|
+
resolve();
|
|
527
|
+
} else {
|
|
528
|
+
document.addEventListener("DOMContentLoaded", () => resolve());
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
447
533
|
function displayMessage(elementId, delay = 0) {
|
|
448
534
|
const element = document.getElementById(elementId);
|
|
449
535
|
if (element) {
|
|
450
536
|
Array.from(document.body.childNodes).forEach(node => {
|
|
451
537
|
if (node.id != elementId) {
|
|
452
|
-
node.
|
|
538
|
+
if (node.id == "sfz-wait-message" || node.id == "sfz-error-message") {
|
|
539
|
+
node.hidden = true;
|
|
540
|
+
} else {
|
|
541
|
+
node.remove();
|
|
542
|
+
}
|
|
453
543
|
}
|
|
454
544
|
});
|
|
545
|
+
element.hidden = false;
|
|
455
546
|
document.body.hidden = false;
|
|
456
547
|
element.style = "opacity: 0; animation: 0s linear " + delay + "s display-wait-message 1 normal forwards";
|
|
457
548
|
}
|
|
458
549
|
}
|
|
459
550
|
|
|
460
|
-
|
|
551
|
+
function extractPageData() {
|
|
461
552
|
const zipDataElement = document.querySelector("sfz-extra-data");
|
|
462
553
|
if (zipDataElement) {
|
|
463
554
|
let dataNode = zipDataElement.nextSibling;
|
|
@@ -470,60 +561,46 @@ async function getContent() {
|
|
|
470
561
|
} else {
|
|
471
562
|
dataNode = zipDataElement.previousSibling;
|
|
472
563
|
}
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
564
|
+
const inflatedPayload = zip.inflateRaw(base64Decode(zipDataElement.textContent));
|
|
565
|
+
const payload = new Uint32Array(inflatedPayload.buffer, inflatedPayload.byteOffset, inflatedPayload.length >> 2);
|
|
566
|
+
const expectedCRC32 = payload[0];
|
|
567
|
+
const zipDataLength = payload[1];
|
|
568
|
+
const lfCodesLength = payload[2];
|
|
569
|
+
const zipData = new Uint8Array(zipDataLength);
|
|
570
|
+
const { textContent } = dataNode;
|
|
571
|
+
let offset = 0;
|
|
572
|
+
let indexLFCode = 0;
|
|
573
|
+
let crc32 = -1;
|
|
476
574
|
for (let index = 0; index < textContent.length; index++) {
|
|
477
575
|
const charCode = textContent.charCodeAt(index);
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
function decompress(src) {
|
|
493
|
-
src = base64Decode(src);
|
|
494
|
-
let out = new Uint8Array(1024);
|
|
495
|
-
let outLen = 0;
|
|
496
|
-
for (let i = 0; i < src.length;) {
|
|
497
|
-
const ctrl = src[i++];
|
|
498
|
-
if ((ctrl & 0x80) === 0) {
|
|
499
|
-
const L = ctrl;
|
|
500
|
-
ensure(outLen + L);
|
|
501
|
-
for (let j = 0; j < L && i < src.length; j++) {
|
|
502
|
-
out[outLen++] = src[i++];
|
|
503
|
-
}
|
|
504
|
-
} else {
|
|
505
|
-
const L = (ctrl & 0x7f) + 3;
|
|
506
|
-
const off = (src[i++] << 8) | src[i++];
|
|
507
|
-
const start = outLen - off;
|
|
508
|
-
ensure(outLen + L);
|
|
509
|
-
for (let k = 0; k < L; k++) {
|
|
510
|
-
out[outLen++] = out[start + k];
|
|
576
|
+
if (charCode == 10) {
|
|
577
|
+
const lfCode = (payload[3 + (indexLFCode >> 4)] >>> ((indexLFCode & 15) * 2)) & 3;
|
|
578
|
+
indexLFCode++;
|
|
579
|
+
if (lfCode == 0) {
|
|
580
|
+
writeByte(10);
|
|
581
|
+
} else {
|
|
582
|
+
writeByte(13);
|
|
583
|
+
if (lfCode == 2) {
|
|
584
|
+
writeByte(10);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
writeByte(charCode > 255 ? characterMap.get(charCode) : charCode);
|
|
511
589
|
}
|
|
512
590
|
}
|
|
513
|
-
|
|
514
|
-
|
|
591
|
+
crc32 = (crc32 ^ -1) >>> 0;
|
|
592
|
+
if (offset != zipDataLength || indexLFCode != lfCodesLength || crc32 != expectedCRC32) {
|
|
593
|
+
throw new Error("Invalid checksum of the extracted zip data");
|
|
594
|
+
}
|
|
595
|
+
return new Blob([zipData], { type: "application/octet-stream" });
|
|
515
596
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
nl *= 2;
|
|
521
|
-
}
|
|
522
|
-
const nbuf = new Uint8Array(nl);
|
|
523
|
-
nbuf.set(out.subarray(0, outLen));
|
|
524
|
-
out = nbuf;
|
|
597
|
+
function writeByte(byte) {
|
|
598
|
+
zipData[offset] = byte;
|
|
599
|
+
crc32 = (crc32 >>> 8) ^ crc32Table[(crc32 ^ byte) & 0xff];
|
|
600
|
+
offset++;
|
|
525
601
|
}
|
|
526
|
-
}
|
|
602
|
+
}
|
|
603
|
+
throw new Error("Extra zip data data not found");
|
|
527
604
|
}
|
|
528
605
|
|
|
529
606
|
function base64Decode(b64) {
|
|
@@ -568,94 +645,3 @@ function base64Encode(bytes) {
|
|
|
568
645
|
}
|
|
569
646
|
return out;
|
|
570
647
|
}
|
|
571
|
-
|
|
572
|
-
function compress(input) {
|
|
573
|
-
const src = new Uint8Array(input);
|
|
574
|
-
const N = src.length;
|
|
575
|
-
const out = [];
|
|
576
|
-
const litBuf = [];
|
|
577
|
-
const MAX_OFFSET = 0xffff;
|
|
578
|
-
const MAX_MATCH = 130;
|
|
579
|
-
const MAX_CANDIDATES = 64;
|
|
580
|
-
const map = new Map();
|
|
581
|
-
let i = 0;
|
|
582
|
-
while (i < N) {
|
|
583
|
-
let bestLen = 0, bestOff = 0;
|
|
584
|
-
if (i + 2 < N) {
|
|
585
|
-
const key = (src[i] << 16) | (src[i + 1] << 8) | src[i + 2];
|
|
586
|
-
const cand = map.get(key) || [];
|
|
587
|
-
for (let c = cand.length - 1; c >= 0; c--) {
|
|
588
|
-
const j = cand[c];
|
|
589
|
-
const off = i - j;
|
|
590
|
-
if (off <= 0 || off > MAX_OFFSET) {
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
let k = 0;
|
|
594
|
-
while (k < MAX_MATCH && i + k < N && src[j + k] === src[i + k]) {
|
|
595
|
-
k++;
|
|
596
|
-
}
|
|
597
|
-
if (k > bestLen && k >= 3) {
|
|
598
|
-
bestLen = k; bestOff = off;
|
|
599
|
-
if (bestLen === MAX_MATCH) {
|
|
600
|
-
break;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
if (bestLen >= 3) {
|
|
607
|
-
if (litBuf.length) {
|
|
608
|
-
flushLiterals();
|
|
609
|
-
}
|
|
610
|
-
let remain = bestLen;
|
|
611
|
-
let produced = 0;
|
|
612
|
-
while (remain > 0) {
|
|
613
|
-
const take = Math.min(remain, MAX_MATCH);
|
|
614
|
-
out.push(0x80 | ((take - 3) & 0x7f));
|
|
615
|
-
out.push((bestOff >> 8) & 0xff);
|
|
616
|
-
out.push(bestOff & 0xff);
|
|
617
|
-
remain -= take;
|
|
618
|
-
produced += take;
|
|
619
|
-
}
|
|
620
|
-
const start = i;
|
|
621
|
-
for (let p = start; p < start + produced; p++) {
|
|
622
|
-
addPos(p);
|
|
623
|
-
}
|
|
624
|
-
i += produced;
|
|
625
|
-
} else {
|
|
626
|
-
litBuf.push(src[i]);
|
|
627
|
-
addPos(i);
|
|
628
|
-
i++;
|
|
629
|
-
if (litBuf.length === 127) {
|
|
630
|
-
flushLiterals();
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
if (litBuf.length) {
|
|
635
|
-
flushLiterals();
|
|
636
|
-
}
|
|
637
|
-
const u8 = new Uint8Array(out);
|
|
638
|
-
return base64Encode(u8);
|
|
639
|
-
|
|
640
|
-
function flushLiterals() {
|
|
641
|
-
while (litBuf.length) {
|
|
642
|
-
const take = Math.min(127, litBuf.length);
|
|
643
|
-
out.push(take);
|
|
644
|
-
for (let t = 0; t < take; t++) {
|
|
645
|
-
out.push(litBuf.shift());
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
function addPos(pos) {
|
|
651
|
-
if (pos + 2 < N) {
|
|
652
|
-
const key = (src[pos] << 16) | (src[pos + 1] << 8) | src[pos + 2];
|
|
653
|
-
const arr = map.get(key) || [];
|
|
654
|
-
arr.push(pos);
|
|
655
|
-
if (arr.length > MAX_CANDIDATES) {
|
|
656
|
-
arr.shift();
|
|
657
|
-
}
|
|
658
|
-
map.set(key, arr);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
}
|