single-file-core 1.1.78 → 1.2.0
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/constants.js +3 -1
- package/core/helper.js +9 -2
- package/core/index.js +61 -751
- package/core/infobar.js +1 -1
- package/core/lib/processor-helper-common.js +323 -0
- package/core/lib/processor-helper-inline.js +876 -0
- package/core/lib/processor-helper.js +801 -0
- package/core/processor-helper.js +34 -0
- package/core/util.js +48 -13
- package/modules/css-fonts-minifier.js +41 -18
- package/modules/index.js +0 -2
- package/package.json +1 -1
- package/processors/compression/compression-display.js +74 -0
- package/processors/compression/compression-extract.js +151 -0
- package/processors/compression/compression.js +305 -0
- package/processors/hooks/content/content-hooks-frames.js +4 -3
- package/processors/index.js +2 -0
- package/single-file-infobar.js +1 -0
- package/single-file.js +24 -2
- package/vendor/index.js +2 -2
- package/vendor/zip/z-worker.js +1 -0
- package/vendor/zip/zip.js +4955 -0
- package/vendor/zip/zip.min.js +1 -0
- package/modules/css-fonts-alt-minifier.js +0 -343
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2022 Gildas Lormeau
|
|
3
|
+
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
+
*
|
|
5
|
+
* This file is part of SingleFile.
|
|
6
|
+
*
|
|
7
|
+
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
+
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
+
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
+
* of the License, or (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
+
* General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
+
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
+
* AGPL normally required by section 4, provided you include this license
|
|
20
|
+
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
+
* Source.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { getProcessorHelperClass as getHelperInlineClass, cssTree } from "./lib/processor-helper-inline.js";
|
|
25
|
+
import { getProcessorHelperClass as getHelperClass } from "./lib/processor-helper.js";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
getProcessorHelperClass,
|
|
29
|
+
cssTree
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function getProcessorHelperClass(options, utilInstance) {
|
|
33
|
+
return options.compressContent ? getHelperClass(utilInstance) : getHelperInlineClass(utilInstance);
|
|
34
|
+
}
|
package/core/util.js
CHANGED
|
@@ -32,6 +32,13 @@ const ONE_MB = 1024 * 1024;
|
|
|
32
32
|
const PREFIX_CONTENT_TYPE_TEXT = "text/";
|
|
33
33
|
const DEFAULT_REPLACED_CHARACTERS = ["~", "+", "\\\\", "?", "%", "*", ":", "|", "\"", "<", ">", "\x00-\x1f", "\x7F"];
|
|
34
34
|
const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
35
|
+
const CONTENT_TYPE_EXTENSIONS = {
|
|
36
|
+
"image/svg+xml": ".svg",
|
|
37
|
+
"image/png": ".png",
|
|
38
|
+
"image/jpeg": ".jpg",
|
|
39
|
+
"image/gif": ".gif",
|
|
40
|
+
"image/webp": ".webp"
|
|
41
|
+
};
|
|
35
42
|
|
|
36
43
|
const URL = globalThis.URL;
|
|
37
44
|
const DOMParser = globalThis.DOMParser;
|
|
@@ -51,6 +58,14 @@ function getInstance(utilOptions) {
|
|
|
51
58
|
utilOptions.fetch = utilOptions.fetch || fetch;
|
|
52
59
|
utilOptions.frameFetch = utilOptions.frameFetch || utilOptions.fetch || fetch;
|
|
53
60
|
return {
|
|
61
|
+
getDoctypeString,
|
|
62
|
+
getFilenameExtension(resourceURL, replacedCharacters, replacementCharacter) {
|
|
63
|
+
const matchExtension = new URL(resourceURL).pathname.match(/(\.[^\\/.]*)$/);
|
|
64
|
+
return ((matchExtension && matchExtension[1] && this.getValidFilename(matchExtension[1], replacedCharacters, replacementCharacter)) || "").toLowerCase();
|
|
65
|
+
},
|
|
66
|
+
getContentTypeExtension(contentType) {
|
|
67
|
+
return CONTENT_TYPE_EXTENSIONS[contentType] || "";
|
|
68
|
+
},
|
|
54
69
|
getContent,
|
|
55
70
|
parseURL(resourceURL, baseURI) {
|
|
56
71
|
if (baseURI === undefined) {
|
|
@@ -130,9 +145,6 @@ function getInstance(utilOptions) {
|
|
|
130
145
|
removeUnusedFonts(doc, stylesheets, styles, options) {
|
|
131
146
|
return modules.fontsMinifier.process(doc, stylesheets, styles, options);
|
|
132
147
|
},
|
|
133
|
-
removeAlternativeFonts(doc, stylesheets, fontDeclarations, fontTests) {
|
|
134
|
-
return modules.fontsAltMinifier.process(doc, stylesheets, fontDeclarations, fontTests);
|
|
135
|
-
},
|
|
136
148
|
getMediaAllInfo(doc, stylesheets, styles) {
|
|
137
149
|
return modules.matchedRules.getMediaAllInfo(doc, stylesheets, styles);
|
|
138
150
|
},
|
|
@@ -185,7 +197,8 @@ function getInstance(utilOptions) {
|
|
|
185
197
|
SINGLE_FILE_UI_ELEMENT_CLASS: helper.SINGLE_FILE_UI_ELEMENT_CLASS,
|
|
186
198
|
EMPTY_RESOURCE: helper.EMPTY_RESOURCE,
|
|
187
199
|
INFOBAR_TAGNAME: helper.INFOBAR_TAGNAME,
|
|
188
|
-
WAIT_FOR_USERSCRIPT_PROPERTY_NAME: helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME
|
|
200
|
+
WAIT_FOR_USERSCRIPT_PROPERTY_NAME: helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
201
|
+
NO_SCRIPT_PROPERTY_NAME: helper.NO_SCRIPT_PROPERTY_NAME
|
|
189
202
|
};
|
|
190
203
|
|
|
191
204
|
async function getContent(resourceURL, options) {
|
|
@@ -241,7 +254,7 @@ function getInstance(utilOptions) {
|
|
|
241
254
|
try {
|
|
242
255
|
buffer = await response.arrayBuffer();
|
|
243
256
|
} catch (error) {
|
|
244
|
-
return { data: options.asBinary ? helper.EMPTY_RESOURCE : "", resourceURL };
|
|
257
|
+
return options.inline ? { data: options.asBinary ? helper.EMPTY_RESOURCE : "", resourceURL } : { resourceURL };
|
|
245
258
|
}
|
|
246
259
|
resourceURL = response.url || resourceURL;
|
|
247
260
|
let contentType = "", charset;
|
|
@@ -300,12 +313,16 @@ function getInstance(utilOptions) {
|
|
|
300
313
|
async function getFetchResponse(resourceURL, options, data, charset, contentType) {
|
|
301
314
|
if (data) {
|
|
302
315
|
if (options.asBinary) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
316
|
+
if (options.inline) {
|
|
317
|
+
const reader = new FileReader();
|
|
318
|
+
reader.readAsDataURL(new Blob([data], { type: contentType + (options.charset ? ";charset=" + options.charset : "") }));
|
|
319
|
+
data = await new Promise((resolve, reject) => {
|
|
320
|
+
reader.addEventListener("load", () => resolve(reader.result), false);
|
|
321
|
+
reader.addEventListener("error", reject, false);
|
|
322
|
+
});
|
|
323
|
+
} else {
|
|
324
|
+
data = new Uint8Array(data);
|
|
325
|
+
}
|
|
309
326
|
} else {
|
|
310
327
|
const firstBytes = new Uint8Array(data.slice(0, 4));
|
|
311
328
|
if (firstBytes[0] == 132 && firstBytes[1] == 49 && firstBytes[2] == 149 && firstBytes[3] == 51) {
|
|
@@ -323,10 +340,10 @@ async function getFetchResponse(resourceURL, options, data, charset, contentType
|
|
|
323
340
|
}
|
|
324
341
|
data = data.replace(/\ufeff/gi, "");
|
|
325
342
|
}
|
|
326
|
-
} else {
|
|
343
|
+
} else if (options.inline) {
|
|
327
344
|
data = options.asBinary ? helper.EMPTY_RESOURCE : "";
|
|
328
345
|
}
|
|
329
|
-
return { data, resourceURL, charset };
|
|
346
|
+
return { data, resourceURL, charset, contentType };
|
|
330
347
|
}
|
|
331
348
|
|
|
332
349
|
function guessMIMEType(expectedType, buffer) {
|
|
@@ -404,6 +421,24 @@ function hex(buffer) {
|
|
|
404
421
|
return hexCodes.join("");
|
|
405
422
|
}
|
|
406
423
|
|
|
424
|
+
function getDoctypeString(doc) {
|
|
425
|
+
const docType = doc.doctype;
|
|
426
|
+
let docTypeString = "";
|
|
427
|
+
if (docType) {
|
|
428
|
+
docTypeString = "<!DOCTYPE " + docType.nodeName;
|
|
429
|
+
if (docType.publicId) {
|
|
430
|
+
docTypeString += " PUBLIC \"" + docType.publicId + "\"";
|
|
431
|
+
if (docType.systemId)
|
|
432
|
+
docTypeString += " \"" + docType.systemId + "\"";
|
|
433
|
+
} else if (docType.systemId)
|
|
434
|
+
docTypeString += " SYSTEM \"" + docType.systemId + "\"";
|
|
435
|
+
if (docType.internalSubset)
|
|
436
|
+
docTypeString += " [" + docType.internalSubset + "]";
|
|
437
|
+
docTypeString += "> ";
|
|
438
|
+
}
|
|
439
|
+
return docTypeString;
|
|
440
|
+
}
|
|
441
|
+
|
|
407
442
|
function log(...args) {
|
|
408
443
|
console.log("S-File <browser>", ...args); // eslint-disable-line no-console
|
|
409
444
|
}
|
|
@@ -218,27 +218,13 @@ function getFontFamilyNames(declarations) {
|
|
|
218
218
|
let fontFamilyName = declarations.children.filter(node => node.property == "font-family").tail;
|
|
219
219
|
let fontFamilyNames = [];
|
|
220
220
|
if (fontFamilyName) {
|
|
221
|
-
let familyName = "";
|
|
222
221
|
if (fontFamilyName.data.value.children) {
|
|
223
|
-
|
|
224
|
-
fontFamilyName.data.value.children.forEach(node => {
|
|
225
|
-
if (node.type == "Operator" && node.value == "," && familyName) {
|
|
226
|
-
fontFamilyNames.push(helper.normalizeFontFamily(familyName));
|
|
227
|
-
familyName = "";
|
|
228
|
-
previousType = null;
|
|
229
|
-
} else {
|
|
230
|
-
if (previousType == "Identifier" && node.type == "Identifier") {
|
|
231
|
-
familyName += " ";
|
|
232
|
-
}
|
|
233
|
-
familyName += cssTree.generate(node);
|
|
234
|
-
}
|
|
235
|
-
previousType = node.type;
|
|
236
|
-
});
|
|
222
|
+
parseFamilyNames(fontFamilyName.data.value, fontFamilyNames);
|
|
237
223
|
} else {
|
|
238
224
|
fontFamilyName = cssTree.generate(fontFamilyName.data.value);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
225
|
+
if (fontFamilyName) {
|
|
226
|
+
fontFamilyNames.push(helper.normalizeFontFamily(fontFamilyName));
|
|
227
|
+
}
|
|
242
228
|
}
|
|
243
229
|
}
|
|
244
230
|
const font = declarations.children.filter(node => node.property == "font").tail;
|
|
@@ -253,6 +239,43 @@ function getFontFamilyNames(declarations) {
|
|
|
253
239
|
return fontFamilyNames;
|
|
254
240
|
}
|
|
255
241
|
|
|
242
|
+
function parseFamilyNames(fontFamilyNameTokenData, fontFamilyNames) {
|
|
243
|
+
let nextToken = fontFamilyNameTokenData.children.head;
|
|
244
|
+
while (nextToken) {
|
|
245
|
+
if (nextToken.data.type == "Identifier") {
|
|
246
|
+
let familyName = nextToken.data.name;
|
|
247
|
+
let nextIdentifierToken = nextToken.next;
|
|
248
|
+
while (nextIdentifierToken && nextIdentifierToken.data.type != "Operator" && nextIdentifierToken.data.value != ",") {
|
|
249
|
+
familyName += " " + nextIdentifierToken.data.name;
|
|
250
|
+
nextIdentifierToken = nextIdentifierToken.next;
|
|
251
|
+
}
|
|
252
|
+
fontFamilyNames.push(helper.normalizeFontFamily(familyName));
|
|
253
|
+
nextToken = nextToken.next;
|
|
254
|
+
} else if (nextToken.data.type == "Function" && nextToken.data.name == "var" && nextToken.data.children) {
|
|
255
|
+
const varName = nextToken.data.children.head.data.name;
|
|
256
|
+
fontFamilyNames.push(helper.normalizeFontFamily("var(" + varName + ")"));
|
|
257
|
+
let nextValueToken = nextToken.data.children.head.next;
|
|
258
|
+
while (nextValueToken && nextValueToken.data.type == "Operator" && nextValueToken.data.value == ",") {
|
|
259
|
+
nextValueToken = nextValueToken.next;
|
|
260
|
+
}
|
|
261
|
+
const fallbackToken = nextValueToken;
|
|
262
|
+
if (fallbackToken) {
|
|
263
|
+
if (fallbackToken.data.children) {
|
|
264
|
+
parseFamilyNames(fallbackToken.data, fontFamilyNames);
|
|
265
|
+
} else {
|
|
266
|
+
fontFamilyNames.push(helper.normalizeFontFamily(fallbackToken.data.value));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
nextToken = nextToken.next;
|
|
270
|
+
} else if (nextToken.data.type == "String") {
|
|
271
|
+
fontFamilyNames.push(helper.normalizeFontFamily(nextToken.data.value));
|
|
272
|
+
nextToken = nextToken.next;
|
|
273
|
+
} else if (nextToken.data.type == "Operator" && nextToken.data.value == ",") {
|
|
274
|
+
nextToken = nextToken.next;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
256
279
|
function getUsedFontWeight(fontInfo, fontStyle, fontWeights) {
|
|
257
280
|
let foundWeight;
|
|
258
281
|
fontWeights = fontWeights.map(weight => String(Number.parseInt(weight, 10)));
|
package/modules/index.js
CHANGED
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import * as fontsAltMinifier from "./css-fonts-alt-minifier.js";
|
|
25
24
|
import * as fontsMinifier from "./css-fonts-minifier.js";
|
|
26
25
|
import * as matchedRules from "./css-matched-rules.js";
|
|
27
26
|
import * as mediasAltMinifier from "./css-medias-alt-minifier.js";
|
|
@@ -32,7 +31,6 @@ import * as serializer from "./html-serializer.js";
|
|
|
32
31
|
import * as templateFormatter from "./template-formatter.js";
|
|
33
32
|
|
|
34
33
|
export {
|
|
35
|
-
fontsAltMinifier,
|
|
36
34
|
fontsMinifier,
|
|
37
35
|
matchedRules,
|
|
38
36
|
mediasAltMinifier,
|
package/package.json
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2022 Gildas Lormeau
|
|
3
|
+
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
+
*
|
|
5
|
+
* This file is part of SingleFile.
|
|
6
|
+
*
|
|
7
|
+
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
+
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
+
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
+
* of the License, or (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
+
* General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
+
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
+
* AGPL normally required by section 4, provided you include this license
|
|
20
|
+
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
+
* Source.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/* global DOMParser */
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
display
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
async function display(document, docContent, { disableFramePointerEvents } = {}) {
|
|
31
|
+
const DISABLED_NOSCRIPT_ATTRIBUTE_NAME = "data-single-filez-disabled-noscript";
|
|
32
|
+
const doc = (new DOMParser()).parseFromString(docContent, "text/html");
|
|
33
|
+
doc.querySelectorAll("noscript:not([" + DISABLED_NOSCRIPT_ATTRIBUTE_NAME + "])").forEach(element => {
|
|
34
|
+
element.setAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME, element.innerHTML);
|
|
35
|
+
element.textContent = "";
|
|
36
|
+
});
|
|
37
|
+
if (doc.doctype) {
|
|
38
|
+
if (document.doctype) {
|
|
39
|
+
document.replaceChild(doc.doctype, document.doctype);
|
|
40
|
+
} else {
|
|
41
|
+
document.insertBefore(doc.doctype, document.documentElement);
|
|
42
|
+
}
|
|
43
|
+
} else if (document.doctype) {
|
|
44
|
+
document.doctype.remove();
|
|
45
|
+
}
|
|
46
|
+
if (disableFramePointerEvents) {
|
|
47
|
+
doc.querySelectorAll("iframe").forEach(element => {
|
|
48
|
+
const pointerEvents = "pointer-events";
|
|
49
|
+
element.style.setProperty("-sf-" + pointerEvents, element.style.getPropertyValue(pointerEvents), element.style.getPropertyPriority(pointerEvents));
|
|
50
|
+
element.style.setProperty(pointerEvents, "none", "important");
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
document.replaceChild(document.importNode(doc.documentElement, true), document.documentElement);
|
|
54
|
+
document.documentElement.setAttribute("data-sfz", "");
|
|
55
|
+
document.querySelectorAll("link[rel*=icon]").forEach(element => element.parentElement.replaceChild(element, element));
|
|
56
|
+
document.open = document.write = document.close = () => { };
|
|
57
|
+
for (let element of Array.from(document.querySelectorAll("script"))) {
|
|
58
|
+
await new Promise((resolve, reject) => {
|
|
59
|
+
const scriptElement = document.createElement("script");
|
|
60
|
+
Array.from(element.attributes).forEach(attribute => scriptElement.setAttribute(attribute.name, attribute.value));
|
|
61
|
+
const async = element.getAttribute("async") == "" || element.getAttribute("async") == "async";
|
|
62
|
+
if (element.textContent) {
|
|
63
|
+
scriptElement.textContent = element.textContent;
|
|
64
|
+
} else if (!async) {
|
|
65
|
+
scriptElement.addEventListener("load", resolve);
|
|
66
|
+
scriptElement.addEventListener("error", reject);
|
|
67
|
+
}
|
|
68
|
+
element.parentElement.replaceChild(scriptElement, element);
|
|
69
|
+
if (element.textContent || async) {
|
|
70
|
+
resolve();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2010-2022 Gildas Lormeau
|
|
3
|
+
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
+
*
|
|
5
|
+
* This file is part of SingleFile.
|
|
6
|
+
*
|
|
7
|
+
* The code in this file is free software: you can redistribute it and/or
|
|
8
|
+
* modify it under the terms of the GNU Affero General Public License
|
|
9
|
+
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
|
+
* of the License, or (at your option) any later version.
|
|
11
|
+
*
|
|
12
|
+
* The code in this file is distributed in the hope that it will be useful,
|
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
|
|
15
|
+
* General Public License for more details.
|
|
16
|
+
*
|
|
17
|
+
* As additional permission under GNU AGPL version 3 section 7, you may
|
|
18
|
+
* distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
|
|
19
|
+
* AGPL normally required by section 4, provided you include this license
|
|
20
|
+
* notice and a URL through which recipients can access the Corresponding
|
|
21
|
+
* Source.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/* global zip, Blob, FileReader, URL */
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
extract
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
async function extract(content, { password, prompt = () => { }, shadowRootScriptURL, zipOptions = { useWebWorkers: false }, noBlobURL } = {}) {
|
|
31
|
+
const KNOWN_MIMETYPES = {
|
|
32
|
+
"gif": "image/gif",
|
|
33
|
+
"jpg": "image/jpeg",
|
|
34
|
+
"png": "image/png",
|
|
35
|
+
"tif": "image/tiff",
|
|
36
|
+
"tiff": "image/tiff",
|
|
37
|
+
"bmp": "image/bmp",
|
|
38
|
+
"ico": "image/vnd.microsoft.icon",
|
|
39
|
+
"webp": "image/webp",
|
|
40
|
+
"svg": "image/svg+xml",
|
|
41
|
+
"avi": "video/x-msvideo",
|
|
42
|
+
"ogv": "video/ogg",
|
|
43
|
+
"mp4": "video/mp4",
|
|
44
|
+
"mpeg": "video/mpeg",
|
|
45
|
+
"ts": "video/mp2t",
|
|
46
|
+
"webm": "video/webm",
|
|
47
|
+
"3gp": "video/3gpp",
|
|
48
|
+
"3g2": "video/3gpp",
|
|
49
|
+
"mp3": "audio/mpeg",
|
|
50
|
+
"oga": "audio/ogg",
|
|
51
|
+
"mid": "audio/midi",
|
|
52
|
+
"midi": "audio/midi",
|
|
53
|
+
"opus": "audio/opus",
|
|
54
|
+
"wav": "audio/wav",
|
|
55
|
+
"weba": "audio/webm"
|
|
56
|
+
};
|
|
57
|
+
if (Array.isArray(content)) {
|
|
58
|
+
content = new Blob([new Uint8Array(content)]);
|
|
59
|
+
}
|
|
60
|
+
zip.configure(zipOptions);
|
|
61
|
+
const blobReader = new zip.BlobReader(content);
|
|
62
|
+
let resources = [];
|
|
63
|
+
const zipReader = new zip.ZipReader(blobReader);
|
|
64
|
+
const entries = await zipReader.getEntries();
|
|
65
|
+
const options = { password };
|
|
66
|
+
await Promise.all(entries.map(async entry => {
|
|
67
|
+
let dataWriter, content, textContent, name, blob;
|
|
68
|
+
if (!options.password && entry.encrypted) {
|
|
69
|
+
options.password = prompt("Please enter the password to view the page");
|
|
70
|
+
}
|
|
71
|
+
name = entry.filename.match(/^([0-9_]+\/)?(.*)$/)[2];
|
|
72
|
+
if (entry.filename.match(/index\.html$/) || entry.filename.match(/stylesheet_[0-9]+\.css/) || entry.filename.match(/scripts\/[0-9]+\.js/)) {
|
|
73
|
+
dataWriter = new zip.TextWriter();
|
|
74
|
+
textContent = await entry.getData(dataWriter, options);
|
|
75
|
+
} else {
|
|
76
|
+
const extension = entry.filename.match(/\.([^.]+)/);
|
|
77
|
+
let mimeType;
|
|
78
|
+
if (extension && extension[1] && KNOWN_MIMETYPES[extension[1]]) {
|
|
79
|
+
mimeType = KNOWN_MIMETYPES[extension[1]];
|
|
80
|
+
} else {
|
|
81
|
+
mimeType = "application/octet-stream";
|
|
82
|
+
}
|
|
83
|
+
if (entry.filename.match(/frames\//) || noBlobURL) {
|
|
84
|
+
content = await entry.getData(new zip.Data64URIWriter(mimeType), options);
|
|
85
|
+
} else {
|
|
86
|
+
blob = await entry.getData(new zip.BlobWriter(mimeType), options);
|
|
87
|
+
content = URL.createObjectURL(blob);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
resources.push({ filename: entry.filename, name, url: entry.comment, content, blob, textContent, parentResources: [] });
|
|
91
|
+
}));
|
|
92
|
+
await zipReader.close();
|
|
93
|
+
let docContent, origDocContent, url;
|
|
94
|
+
resources = resources.sort((resourceLeft, resourceRight) => resourceRight.filename.length - resourceLeft.filename.length);
|
|
95
|
+
const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
|
|
96
|
+
for (const resource of resources) {
|
|
97
|
+
if (resource.textContent !== undefined) {
|
|
98
|
+
let prefixPath = "";
|
|
99
|
+
const prefixPathMatch = resource.filename.match(/(.*\/)[^/]+$/);
|
|
100
|
+
if (prefixPathMatch && prefixPathMatch[1]) {
|
|
101
|
+
prefixPath = prefixPathMatch[1];
|
|
102
|
+
}
|
|
103
|
+
if (resource.filename.match(/^([0-9_]+\/)?index\.html$/)) {
|
|
104
|
+
origDocContent = resource.textContent;
|
|
105
|
+
}
|
|
106
|
+
const isScript = resource.filename.match(/scripts\/[0-9]+\.js/);
|
|
107
|
+
if (!isScript) {
|
|
108
|
+
resources.forEach(innerResource => {
|
|
109
|
+
if (innerResource.filename.startsWith(prefixPath) && innerResource.filename != resource.filename) {
|
|
110
|
+
const filename = innerResource.filename.substring(prefixPath.length);
|
|
111
|
+
if (!filename.match(/manifest\.json$/)) {
|
|
112
|
+
const searchRegExp = new RegExp(filename.replace(REGEXP_ESCAPE, "\\$1"), "g");
|
|
113
|
+
const position = resource.textContent.search(searchRegExp);
|
|
114
|
+
if (position != -1) {
|
|
115
|
+
innerResource.parentResources.push(resource.filename);
|
|
116
|
+
resource.textContent = resource.textContent.replace(searchRegExp, innerResource.content);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
let mimeType;
|
|
123
|
+
if (resource.filename.match(/stylesheet_[0-9]+\.css/)) {
|
|
124
|
+
mimeType = "text/css";
|
|
125
|
+
} else if (isScript) {
|
|
126
|
+
mimeType = "text/javascript";
|
|
127
|
+
} else if (resource.filename.match(/index\.html$/)) {
|
|
128
|
+
mimeType = "text/html";
|
|
129
|
+
if (shadowRootScriptURL) {
|
|
130
|
+
resource.textContent = resource.textContent.replace(/<script data-template-shadow-root.*<\/script>/g, "<script data-template-shadow-root src=" + shadowRootScriptURL + "></" + "script>");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (resource.filename.match(/^([0-9_]+\/)?index\.html$/)) {
|
|
134
|
+
docContent = resource.textContent;
|
|
135
|
+
url = resource.url;
|
|
136
|
+
} else {
|
|
137
|
+
const reader = new FileReader();
|
|
138
|
+
if (resource.textContent) {
|
|
139
|
+
reader.readAsDataURL(new Blob([resource.textContent], { type: mimeType + ";charset=utf-8" }));
|
|
140
|
+
resource.content = await new Promise((resolve, reject) => {
|
|
141
|
+
reader.addEventListener("load", () => resolve(reader.result), false);
|
|
142
|
+
reader.addEventListener("error", reject, false);
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
resource.content = "data:text/plain,";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { docContent, origDocContent, resources, url };
|
|
151
|
+
}
|