single-file-core 1.0.47 → 1.0.49
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/modules/html-minifier.js +16 -12
- package/modules/html-serializer.js +35 -31
- package/modules/template-formatter.js +13 -13
- package/package.json +1 -1
- package/processors/lazy/content/content-lazy-loader.js +2 -1
- package/single-file-core.js +17 -12
- package/single-file-helper.js +10 -9
- package/vendor/css-tree.js +9 -9
package/modules/html-minifier.js
CHANGED
|
@@ -68,7 +68,7 @@ const booleanAttributes = [
|
|
|
68
68
|
"visible"
|
|
69
69
|
];
|
|
70
70
|
|
|
71
|
-
const noWhitespaceCollapseElements = ["
|
|
71
|
+
const noWhitespaceCollapseElements = ["SCRIPT", "STYLE", "PRE", "TEXTAREA"];
|
|
72
72
|
|
|
73
73
|
// Source: https://www.w3.org/TR/html4/sgml/dtd.html#events (Generic Attributes)
|
|
74
74
|
const safeToRemoveAttrs = [
|
|
@@ -90,10 +90,10 @@ const safeToRemoveAttrs = [
|
|
|
90
90
|
];
|
|
91
91
|
|
|
92
92
|
const redundantAttributes = {
|
|
93
|
-
"
|
|
93
|
+
"FORM": {
|
|
94
94
|
"method": "get"
|
|
95
95
|
},
|
|
96
|
-
"
|
|
96
|
+
"SCRIPT": {
|
|
97
97
|
"language": "javascript",
|
|
98
98
|
"type": "text/javascript",
|
|
99
99
|
// Remove attribute if the function returns false
|
|
@@ -103,11 +103,11 @@ const redundantAttributes = {
|
|
|
103
103
|
return !node.getAttribute("src");
|
|
104
104
|
}
|
|
105
105
|
},
|
|
106
|
-
"
|
|
106
|
+
"STYLE": {
|
|
107
107
|
"media": "all",
|
|
108
108
|
"type": "text/css"
|
|
109
109
|
},
|
|
110
|
-
"
|
|
110
|
+
"LINK": {
|
|
111
111
|
"media": "all"
|
|
112
112
|
}
|
|
113
113
|
};
|
|
@@ -128,7 +128,7 @@ const modules = [
|
|
|
128
128
|
removeEmptyAttributes,
|
|
129
129
|
removeRedundantAttributes,
|
|
130
130
|
compressJSONLD,
|
|
131
|
-
node => mergeElements(node, "style", (node, previousSibling) => node.parentElement && node.parentElement
|
|
131
|
+
node => mergeElements(node, "style", (node, previousSibling) => node.parentElement && getTagName(node.parentElement) == "HEAD" && node.media == previousSibling.media && node.title == previousSibling.title)
|
|
132
132
|
];
|
|
133
133
|
|
|
134
134
|
export {
|
|
@@ -169,7 +169,7 @@ function mergeTextNodes(node) {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
function mergeElements(node, tagName, acceptMerge) {
|
|
172
|
-
if (node.nodeType == Node_ELEMENT_NODE && node
|
|
172
|
+
if (node.nodeType == Node_ELEMENT_NODE && getTagName(node) == tagName.toUpperCase()) {
|
|
173
173
|
let previousSibling = node.previousSibling;
|
|
174
174
|
const previousSiblings = [];
|
|
175
175
|
while (previousSibling && previousSibling.nodeType == Node_TEXT_NODE && !previousSibling.textContent.trim()) {
|
|
@@ -203,15 +203,15 @@ function collapseWhitespace(node, options) {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
function getWhiteSpace(node) {
|
|
206
|
-
return node.parentElement && node.parentElement
|
|
206
|
+
return node.parentElement && getTagName(node.parentElement) == "HEAD" ? "\n" : " ";
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
function noWhitespaceCollapse(element) {
|
|
210
|
-
return element && !noWhitespaceCollapseElements.includes(element
|
|
210
|
+
return element && !noWhitespaceCollapseElements.includes(getTagName(element));
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
function removeComments(node) {
|
|
214
|
-
if (node.nodeType == Node_COMMENT_NODE && node.parentElement
|
|
214
|
+
if (node.nodeType == Node_COMMENT_NODE && getTagName(node.parentElement) != "HTML") {
|
|
215
215
|
return !node.textContent.toLowerCase().trim().startsWith("[if");
|
|
216
216
|
}
|
|
217
217
|
}
|
|
@@ -231,7 +231,7 @@ function removeEmptyAttributes(node) {
|
|
|
231
231
|
|
|
232
232
|
function removeRedundantAttributes(node) {
|
|
233
233
|
if (node.nodeType == Node_ELEMENT_NODE) {
|
|
234
|
-
const tagRedundantAttributes = redundantAttributes[node
|
|
234
|
+
const tagRedundantAttributes = redundantAttributes[getTagName(node)];
|
|
235
235
|
if (tagRedundantAttributes) {
|
|
236
236
|
Object.keys(tagRedundantAttributes).forEach(redundantAttributeName => {
|
|
237
237
|
const tagRedundantAttributeValue = tagRedundantAttributes[redundantAttributeName];
|
|
@@ -244,7 +244,7 @@ function removeRedundantAttributes(node) {
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
function compressJSONLD(node) {
|
|
247
|
-
if (node.nodeType == Node_ELEMENT_NODE && node
|
|
247
|
+
if (node.nodeType == Node_ELEMENT_NODE && getTagName(node) == "SCRIPT" && node.type == "application/ld+json" && node.textContent.trim()) {
|
|
248
248
|
try {
|
|
249
249
|
node.textContent = JSON.stringify(JSON.parse(node.textContent));
|
|
250
250
|
} catch (error) {
|
|
@@ -259,4 +259,8 @@ function removeEmptyInlineElements(doc) {
|
|
|
259
259
|
element.remove();
|
|
260
260
|
}
|
|
261
261
|
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function getTagName(element) {
|
|
265
|
+
return element.tagName && element.tagName.toUpperCase();
|
|
262
266
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
const SELF_CLOSED_TAG_NAMES = ["
|
|
24
|
+
const SELF_CLOSED_TAG_NAMES = ["AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "META", "PARAM", "SOURCE", "TRACK", "WBR"];
|
|
25
25
|
|
|
26
26
|
const Node_ELEMENT_NODE = 1;
|
|
27
27
|
const Node_TEXT_NODE = 3;
|
|
@@ -29,31 +29,31 @@ const Node_COMMENT_NODE = 8;
|
|
|
29
29
|
|
|
30
30
|
// see https://www.w3.org/TR/html5/syntax.html#optional-tags
|
|
31
31
|
const OMITTED_START_TAGS = [
|
|
32
|
-
{ tagName: "
|
|
33
|
-
{ tagName: "
|
|
32
|
+
{ tagName: "HEAD", accept: element => !element.childNodes.length || element.childNodes[0].nodeType == Node_ELEMENT_NODE },
|
|
33
|
+
{ tagName: "BODY", accept: element => !element.childNodes.length }
|
|
34
34
|
];
|
|
35
35
|
const OMITTED_END_TAGS = [
|
|
36
|
-
{ tagName: "
|
|
37
|
-
{ tagName: "
|
|
38
|
-
{ tagName: "
|
|
39
|
-
{ tagName: "
|
|
40
|
-
{ tagName: "
|
|
41
|
-
{ tagName: "
|
|
42
|
-
{ tagName: "
|
|
43
|
-
{ tagName: "
|
|
44
|
-
{ tagName: "
|
|
45
|
-
{ tagName: "
|
|
46
|
-
{ tagName: "
|
|
47
|
-
{ tagName: "
|
|
48
|
-
{ tagName: "
|
|
49
|
-
{ tagName: "
|
|
50
|
-
{ tagName: "
|
|
51
|
-
{ tagName: "
|
|
52
|
-
{ tagName: "
|
|
53
|
-
{ tagName: "
|
|
54
|
-
{ tagName: "
|
|
36
|
+
{ tagName: "HTML", accept: next => !next || next.nodeType != Node_COMMENT_NODE },
|
|
37
|
+
{ tagName: "HEAD", accept: next => !next || (next.nodeType != Node_COMMENT_NODE && (next.nodeType != Node_TEXT_NODE || !startsWithSpaceChar(next.textContent))) },
|
|
38
|
+
{ tagName: "BODY", accept: next => !next || next.nodeType != Node_COMMENT_NODE },
|
|
39
|
+
{ tagName: "LI", accept: (next, element) => (!next && element.parentElement && (getTagName(element.parentElement) == "UL" || getTagName(element.parentElement) == "OL")) || (next && ["LI"].includes(getTagName(next))) },
|
|
40
|
+
{ tagName: "DT", accept: next => !next || ["DT", "DD"].includes(getTagName(next)) },
|
|
41
|
+
{ tagName: "P", accept: next => next && ["ADDRESS", "ARTICLE", "ASIDE", "BLOCKQUOTE", "DETAILS", "DIV", "DL", "FIELDSET", "FIGCAPTION", "FIGURE", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HR", "MAIN", "NAV", "OL", "P", "PRE", "SECTION", "TABLE", "UL"].includes(getTagName(next)) },
|
|
42
|
+
{ tagName: "DD", accept: next => !next || ["DT", "DD"].includes(getTagName(next)) },
|
|
43
|
+
{ tagName: "RT", accept: next => !next || ["RT", "RP"].includes(getTagName(next)) },
|
|
44
|
+
{ tagName: "RP", accept: next => !next || ["RT", "RP"].includes(getTagName(next)) },
|
|
45
|
+
{ tagName: "OPTGROUP", accept: next => !next || ["OPTGROUP"].includes(getTagName(next)) },
|
|
46
|
+
{ tagName: "OPTION", accept: next => !next || ["OPTION", "OPTGROUP"].includes(getTagName(next)) },
|
|
47
|
+
{ tagName: "COLGROUP", accept: next => !next || (next.nodeType != Node_COMMENT_NODE && (next.nodeType != Node_TEXT_NODE || !startsWithSpaceChar(next.textContent))) },
|
|
48
|
+
{ tagName: "CAPTION", accept: next => !next || (next.nodeType != Node_COMMENT_NODE && (next.nodeType != Node_TEXT_NODE || !startsWithSpaceChar(next.textContent))) },
|
|
49
|
+
{ tagName: "THEAD", accept: next => !next || ["TBODY", "TFOOT"].includes(getTagName(next)) },
|
|
50
|
+
{ tagName: "TBODY", accept: next => !next || ["TBODY", "TFOOT"].includes(getTagName(next)) },
|
|
51
|
+
{ tagName: "TFOOT", accept: next => !next },
|
|
52
|
+
{ tagName: "TR", accept: next => !next || ["TR"].includes(getTagName(next)) },
|
|
53
|
+
{ tagName: "TD", accept: next => !next || ["TD", "TH"].includes(getTagName(next)) },
|
|
54
|
+
{ tagName: "TH", accept: next => !next || ["TD", "TH"].includes(getTagName(next)) }
|
|
55
55
|
];
|
|
56
|
-
const TEXT_NODE_TAGS = ["
|
|
56
|
+
const TEXT_NODE_TAGS = ["STYLE", "SCRIPT", "XMP", "IFRAME", "NOEMBED", "NOFRAMES", "PLAINTEXT", "NOSCRIPT"];
|
|
57
57
|
|
|
58
58
|
export {
|
|
59
59
|
process
|
|
@@ -91,10 +91,10 @@ function serializeTextNode(textNode) {
|
|
|
91
91
|
const parentNode = textNode.parentNode;
|
|
92
92
|
let parentTagName;
|
|
93
93
|
if (parentNode && parentNode.nodeType == Node_ELEMENT_NODE) {
|
|
94
|
-
parentTagName = parentNode
|
|
94
|
+
parentTagName = getTagName(parentNode);
|
|
95
95
|
}
|
|
96
96
|
if (!parentTagName || TEXT_NODE_TAGS.includes(parentTagName)) {
|
|
97
|
-
if (parentTagName == "
|
|
97
|
+
if (parentTagName == "SCRIPT" || parentTagName == "STYLE") {
|
|
98
98
|
return textNode.textContent.replace(/<\//gi, "<\\/").replace(/\/>/gi, "\\/>");
|
|
99
99
|
}
|
|
100
100
|
return textNode.textContent;
|
|
@@ -108,22 +108,22 @@ function serializeCommentNode(commentNode) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function serializeElement(element, compressHTML, isSVG) {
|
|
111
|
-
const tagName = element
|
|
112
|
-
const omittedStartTag = compressHTML && OMITTED_START_TAGS.find(omittedStartTag => tagName == omittedStartTag
|
|
111
|
+
const tagName = getTagName(element);
|
|
112
|
+
const omittedStartTag = compressHTML && OMITTED_START_TAGS.find(omittedStartTag => tagName == getTagName(omittedStartTag) && omittedStartTag.accept(element));
|
|
113
113
|
let content = "";
|
|
114
114
|
if (!omittedStartTag || element.attributes.length) {
|
|
115
|
-
content = "<" + tagName;
|
|
115
|
+
content = "<" + tagName.toLowerCase();
|
|
116
116
|
Array.from(element.attributes).forEach(attribute => content += serializeAttribute(attribute, element, compressHTML));
|
|
117
117
|
content += ">";
|
|
118
118
|
}
|
|
119
|
-
if (
|
|
119
|
+
if (tagName == "TEMPLATE" && !element.childNodes.length) {
|
|
120
120
|
content += element.innerHTML;
|
|
121
121
|
} else {
|
|
122
122
|
Array.from(element.childNodes).forEach(childNode => content += serialize(childNode, compressHTML, isSVG || tagName == "svg"));
|
|
123
123
|
}
|
|
124
|
-
const omittedEndTag = compressHTML && OMITTED_END_TAGS.find(omittedEndTag => tagName == omittedEndTag
|
|
124
|
+
const omittedEndTag = compressHTML && OMITTED_END_TAGS.find(omittedEndTag => tagName == getTagName(omittedEndTag) && omittedEndTag.accept(element.nextSibling, element));
|
|
125
125
|
if (isSVG || (!omittedEndTag && !SELF_CLOSED_TAG_NAMES.includes(tagName))) {
|
|
126
|
-
content += "</" + tagName + ">";
|
|
126
|
+
content += "</" + tagName.toLowerCase() + ">";
|
|
127
127
|
}
|
|
128
128
|
return content;
|
|
129
129
|
}
|
|
@@ -177,4 +177,8 @@ function serializeAttribute(attribute, element, compressHTML) {
|
|
|
177
177
|
|
|
178
178
|
function startsWithSpaceChar(textContent) {
|
|
179
179
|
return Boolean(textContent.match(/^[ \t\n\f\r]/));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function getTagName(element) {
|
|
183
|
+
return element.tagName && element.tagName.toUpperCase();
|
|
180
184
|
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Copyright 2010-2022 Gildas Lormeau
|
|
3
3
|
* contact : gildas.lormeau <at> gmail.com
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* This file is part of SingleFile.
|
|
6
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
|
|
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
9
|
* (GNU AGPL) as published by the Free Software Foundation, either version 3
|
|
10
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
|
|
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
15
|
* General Public License for more details.
|
|
16
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
|
|
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
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
@@ -1908,9 +1908,9 @@ const EMOJIS = Object.keys(EMOJI_NAMES);
|
|
|
1908
1908
|
export { formatFilename, evalTemplate };
|
|
1909
1909
|
|
|
1910
1910
|
async function formatFilename(content, options, util) {
|
|
1911
|
-
let filename = (await evalTemplate(options.filenameTemplate, options, content)) || "";
|
|
1911
|
+
let filename = (await evalTemplate(options.filenameTemplate, options, util, content)) || "";
|
|
1912
1912
|
if (options.replaceEmojisInFilename) {
|
|
1913
|
-
EMOJIS.forEach(emoji => filename = filename.replaceAll(emoji, " _" + EMOJI_NAMES[emoji] + "_ "));
|
|
1913
|
+
EMOJIS.forEach(emoji => (filename = filename.replaceAll(emoji, " _" + EMOJI_NAMES[emoji] + "_ ")));
|
|
1914
1914
|
}
|
|
1915
1915
|
const replacementCharacter = options.filenameReplacementCharacter;
|
|
1916
1916
|
filename = getValidFilename(filename, options.filenameReplacedCharacters, replacementCharacter);
|
package/package.json
CHANGED
|
@@ -98,7 +98,8 @@ function triggerLazyLoading(options) {
|
|
|
98
98
|
mutation.target.setAttribute(helper.LAZY_SRC_ATTRIBUTE_NAME, mutation.target.src);
|
|
99
99
|
mutation.target.addEventListener("load", onResourceLoad);
|
|
100
100
|
}
|
|
101
|
-
if (mutation.attributeName == "src" || mutation.attributeName == "srcset" ||
|
|
101
|
+
if (mutation.attributeName == "src" || mutation.attributeName == "srcset" ||
|
|
102
|
+
(mutation.target.tagName && mutation.target.tagName.toUpperCase() == "SOURCE")) {
|
|
102
103
|
return !mutation.target.classList || !mutation.target.classList.contains(helper.SINGLE_FILE_UI_ELEMENT_CLASS);
|
|
103
104
|
}
|
|
104
105
|
});
|
package/single-file-core.js
CHANGED
|
@@ -100,9 +100,9 @@ const STAGES = [
|
|
|
100
100
|
{ action: "replaceInvalidElements" },
|
|
101
101
|
{ action: "resetCharsetMeta" },
|
|
102
102
|
{ option: "saveFavicon", action: "saveFavicon" },
|
|
103
|
-
{ action: "replaceCanvasElements" },
|
|
104
103
|
{ action: "insertFonts" },
|
|
105
104
|
{ action: "insertShadowRootContents" },
|
|
105
|
+
{ action: "replaceCanvasElements" },
|
|
106
106
|
{ action: "setInputValues" },
|
|
107
107
|
{ option: "moveStylesInHead", action: "moveStylesInHead" },
|
|
108
108
|
{ option: "blockScripts", action: "removeEmbedScripts" },
|
|
@@ -620,14 +620,17 @@ class Processor {
|
|
|
620
620
|
function hideNode(node) {
|
|
621
621
|
if (canHideNode(node)) {
|
|
622
622
|
node.style.setProperty("display", "none", "important");
|
|
623
|
+
node.removeAttribute("src");
|
|
624
|
+
node.removeAttribute("srcset");
|
|
625
|
+
node.removeAttribute("srcdoc");
|
|
623
626
|
Array.from(node.childNodes).forEach(removeNode);
|
|
624
627
|
}
|
|
625
628
|
}
|
|
626
629
|
|
|
627
630
|
function canHideNode(node) {
|
|
628
631
|
if (node.nodeType == 1) {
|
|
629
|
-
const tagName = node.tagName && node.tagName.
|
|
630
|
-
return tagName != "
|
|
632
|
+
const tagName = node.tagName && node.tagName.toUpperCase();
|
|
633
|
+
return tagName != "SVG" && tagName != "STYLE" && tagName != "LINK";
|
|
631
634
|
}
|
|
632
635
|
}
|
|
633
636
|
}
|
|
@@ -636,7 +639,7 @@ class Processor {
|
|
|
636
639
|
if (this.options.posters) {
|
|
637
640
|
this.doc.querySelectorAll("video, video > source").forEach(element => {
|
|
638
641
|
let videoElement;
|
|
639
|
-
if (element.tagName == "VIDEO") {
|
|
642
|
+
if (element.tagName.toUpperCase() == "VIDEO") {
|
|
640
643
|
videoElement = element;
|
|
641
644
|
} else {
|
|
642
645
|
videoElement = element.parentElement;
|
|
@@ -916,7 +919,7 @@ class Processor {
|
|
|
916
919
|
resolveHrefs() {
|
|
917
920
|
this.doc.querySelectorAll("a[href], area[href], link[href]").forEach(element => {
|
|
918
921
|
const href = element.getAttribute("href").trim();
|
|
919
|
-
if (element.tagName == "LINK" && element.rel.includes("stylesheet")) {
|
|
922
|
+
if (element.tagName.toUpperCase() == "LINK" && element.rel.includes("stylesheet")) {
|
|
920
923
|
if (this.options.saveOriginalURLs && !isDataURL(href)) {
|
|
921
924
|
element.setAttribute("data-sf-original-href", href);
|
|
922
925
|
}
|
|
@@ -947,7 +950,7 @@ class Processor {
|
|
|
947
950
|
await Promise.all(
|
|
948
951
|
Array.from(this.doc.querySelectorAll("video[src], video > source[src]")).map(async element => {
|
|
949
952
|
let videoElement;
|
|
950
|
-
if (element.tagName == "VIDEO") {
|
|
953
|
+
if (element.tagName.toUpperCase() == "VIDEO") {
|
|
951
954
|
videoElement = element;
|
|
952
955
|
} else {
|
|
953
956
|
videoElement = element.parentElement;
|
|
@@ -1019,7 +1022,7 @@ class Processor {
|
|
|
1019
1022
|
mediaText,
|
|
1020
1023
|
scoped
|
|
1021
1024
|
};
|
|
1022
|
-
if (element.tagName == "LINK" && element.charset) {
|
|
1025
|
+
if (element.tagName.toUpperCase() == "LINK" && element.charset) {
|
|
1023
1026
|
options.charset = element.charset;
|
|
1024
1027
|
}
|
|
1025
1028
|
await processElement(element, stylesheetInfo, this.stylesheets, this.baseURI, options, this.workStyleElement);
|
|
@@ -1047,7 +1050,7 @@ class Processor {
|
|
|
1047
1050
|
let stylesheet;
|
|
1048
1051
|
stylesheets.set(element, stylesheetInfo);
|
|
1049
1052
|
if (!options.blockStylesheets) {
|
|
1050
|
-
if (element.tagName == "LINK") {
|
|
1053
|
+
if (element.tagName.toUpperCase() == "LINK") {
|
|
1051
1054
|
stylesheet = await ProcessorHelper.resolveLinkStylesheetURLs(element.href, baseURI, options, workStyleElement);
|
|
1052
1055
|
} else {
|
|
1053
1056
|
stylesheet = cssTree.parse(element.textContent, { context: "stylesheet", parseCustomProperty: true });
|
|
@@ -1073,7 +1076,7 @@ class Processor {
|
|
|
1073
1076
|
const frameElements = Array.from(this.doc.querySelectorAll("iframe, frame, object[type=\"text/html\"][data]"));
|
|
1074
1077
|
await Promise.all(
|
|
1075
1078
|
frameElements.map(async frameElement => {
|
|
1076
|
-
if (frameElement.tagName == "OBJECT") {
|
|
1079
|
+
if (frameElement.tagName.toUpperCase() == "OBJECT") {
|
|
1077
1080
|
frameElement.setAttribute("data", "data:text/html,");
|
|
1078
1081
|
} else {
|
|
1079
1082
|
const src = frameElement.getAttribute("src");
|
|
@@ -1302,10 +1305,10 @@ class Processor {
|
|
|
1302
1305
|
sandbox += " allow-scripts allow-same-origin";
|
|
1303
1306
|
}
|
|
1304
1307
|
frameElement.setAttribute("sandbox", sandbox);
|
|
1305
|
-
if (frameElement.tagName == "OBJECT") {
|
|
1308
|
+
if (frameElement.tagName.toUpperCase() == "OBJECT") {
|
|
1306
1309
|
frameElement.setAttribute("data", "data:text/html," + pageData.content);
|
|
1307
1310
|
} else {
|
|
1308
|
-
if (frameElement.tagName == "FRAME") {
|
|
1311
|
+
if (frameElement.tagName.toUpperCase() == "FRAME") {
|
|
1309
1312
|
frameElement.setAttribute("src", "data:text/html," + pageData.content.replace(/%/g, "%25").replace(/#/g, "%23"));
|
|
1310
1313
|
} else {
|
|
1311
1314
|
frameElement.setAttribute("srcdoc", pageData.content);
|
|
@@ -1763,7 +1766,9 @@ class ProcessorHelper {
|
|
|
1763
1766
|
// ignored
|
|
1764
1767
|
}
|
|
1765
1768
|
if (testValidURL(resourceURL)) {
|
|
1766
|
-
let { content, indexResource, duplicate } = await batchRequest.addURL(
|
|
1769
|
+
let { content, indexResource, duplicate } = await batchRequest.addURL(
|
|
1770
|
+
resourceURL,
|
|
1771
|
+
{ asBinary: true, expectedType, groupDuplicates: options.groupDuplicateImages && resourceElement.tagName.toUpperCase() == "IMG" && attributeName == "src" });
|
|
1767
1772
|
if (originURL) {
|
|
1768
1773
|
if (content == util.EMPTY_RESOURCE) {
|
|
1769
1774
|
try {
|
package/single-file-helper.js
CHANGED
|
@@ -191,7 +191,7 @@ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map
|
|
|
191
191
|
computedStyle = getComputedStyle(win, element);
|
|
192
192
|
if (element instanceof win.HTMLElement) {
|
|
193
193
|
if (options.removeHiddenElements) {
|
|
194
|
-
elementKept = ((ascendantHidden || element.closest("html > head")) && KEPT_TAG_NAMES.includes(element.tagName)) || element.closest("details");
|
|
194
|
+
elementKept = ((ascendantHidden || element.closest("html > head")) && KEPT_TAG_NAMES.includes(element.tagName.toUpperCase())) || element.closest("details");
|
|
195
195
|
if (!elementKept) {
|
|
196
196
|
elementHidden = ascendantHidden || testHiddenElement(element, computedStyle);
|
|
197
197
|
if (elementHidden) {
|
|
@@ -253,7 +253,8 @@ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
function getResourcesInfo(win, doc, element, options, data, elementHidden, computedStyle) {
|
|
256
|
-
|
|
256
|
+
const tagName = element.tagName && element.tagName.toUpperCase();
|
|
257
|
+
if (tagName == "CANVAS") {
|
|
257
258
|
try {
|
|
258
259
|
data.canvases.push({ dataURI: element.toDataURL("image/png", "") });
|
|
259
260
|
element.setAttribute(CANVAS_ATTRIBUTE_NAME, data.canvases.length - 1);
|
|
@@ -262,7 +263,7 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
262
263
|
// ignored
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
|
-
if (
|
|
266
|
+
if (tagName == "IMG") {
|
|
266
267
|
const imageData = {
|
|
267
268
|
currentSrc: elementHidden ?
|
|
268
269
|
EMPTY_RESOURCE :
|
|
@@ -288,7 +289,7 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
288
289
|
}
|
|
289
290
|
}
|
|
290
291
|
}
|
|
291
|
-
if (
|
|
292
|
+
if (tagName == "VIDEO") {
|
|
292
293
|
const src = element.currentSrc;
|
|
293
294
|
if (src && !src.startsWith("blob:") && !src.startsWith("data:")) {
|
|
294
295
|
const computedStyle = getComputedStyle(win, element.parentNode);
|
|
@@ -318,13 +319,13 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
318
319
|
}
|
|
319
320
|
}
|
|
320
321
|
}
|
|
321
|
-
if (
|
|
322
|
+
if (tagName == "IFRAME") {
|
|
322
323
|
if (elementHidden && options.removeHiddenElements) {
|
|
323
324
|
element.setAttribute(HIDDEN_FRAME_ATTRIBUTE_NAME, "");
|
|
324
325
|
data.markedElements.push(element);
|
|
325
326
|
}
|
|
326
327
|
}
|
|
327
|
-
if (
|
|
328
|
+
if (tagName == "INPUT") {
|
|
328
329
|
if (element.type != "password") {
|
|
329
330
|
element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.value);
|
|
330
331
|
data.markedElements.push(element);
|
|
@@ -334,11 +335,11 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
334
335
|
data.markedElements.push(element);
|
|
335
336
|
}
|
|
336
337
|
}
|
|
337
|
-
if (
|
|
338
|
+
if (tagName == "TEXTAREA") {
|
|
338
339
|
element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.value);
|
|
339
340
|
data.markedElements.push(element);
|
|
340
341
|
}
|
|
341
|
-
if (
|
|
342
|
+
if (tagName == "SELECT") {
|
|
342
343
|
element.querySelectorAll("option").forEach(option => {
|
|
343
344
|
if (option.selected) {
|
|
344
345
|
option.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, "");
|
|
@@ -346,7 +347,7 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
346
347
|
}
|
|
347
348
|
});
|
|
348
349
|
}
|
|
349
|
-
if (
|
|
350
|
+
if (tagName == "SCRIPT") {
|
|
350
351
|
if (element.async && element.getAttribute("async") != "" && element.getAttribute("async") != "async") {
|
|
351
352
|
element.setAttribute(ASYNC_SCRIPT_ATTRIBUTE_NAME, "");
|
|
352
353
|
data.markedElements.push(element);
|