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.
@@ -68,7 +68,7 @@ const booleanAttributes = [
68
68
  "visible"
69
69
  ];
70
70
 
71
- const noWhitespaceCollapseElements = ["script", "style", "pre", "textarea"];
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
- "form": {
93
+ "FORM": {
94
94
  "method": "get"
95
95
  },
96
- "script": {
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
- "style": {
106
+ "STYLE": {
107
107
  "media": "all",
108
108
  "type": "text/css"
109
109
  },
110
- "link": {
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.tagName == "HEAD" && node.media == previousSibling.media && node.title == previousSibling.title)
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.tagName.toLowerCase() == tagName.toLowerCase()) {
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.tagName == "HEAD" ? "\n" : " ";
206
+ return node.parentElement && getTagName(node.parentElement) == "HEAD" ? "\n" : " ";
207
207
  }
208
208
 
209
209
  function noWhitespaceCollapse(element) {
210
- return element && !noWhitespaceCollapseElements.includes(element.tagName.toLowerCase());
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.tagName != "HTML") {
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.tagName.toLowerCase()];
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.tagName == "SCRIPT" && node.type == "application/ld+json" && node.textContent.trim()) {
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 = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"];
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: "head", accept: element => !element.childNodes.length || element.childNodes[0].nodeType == Node_ELEMENT_NODE },
33
- { tagName: "body", accept: element => !element.childNodes.length }
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: "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 && (element.parentElement.tagName == "UL" || element.parentElement.tagName == "OL")) || (next && ["LI"].includes(next.tagName)) },
40
- { tagName: "dt", accept: next => !next || ["DT", "DD"].includes(next.tagName) },
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(next.tagName) },
42
- { tagName: "dd", accept: next => !next || ["DT", "DD"].includes(next.tagName) },
43
- { tagName: "rt", accept: next => !next || ["RT", "RP"].includes(next.tagName) },
44
- { tagName: "rp", accept: next => !next || ["RT", "RP"].includes(next.tagName) },
45
- { tagName: "optgroup", accept: next => !next || ["OPTGROUP"].includes(next.tagName) },
46
- { tagName: "option", accept: next => !next || ["OPTION", "OPTGROUP"].includes(next.tagName) },
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(next.tagName) },
50
- { tagName: "tbody", accept: next => !next || ["TBODY", "TFOOT"].includes(next.tagName) },
51
- { tagName: "tfoot", accept: next => !next },
52
- { tagName: "tr", accept: next => !next || ["TR"].includes(next.tagName) },
53
- { tagName: "td", accept: next => !next || ["TD", "TH"].includes(next.tagName) },
54
- { tagName: "th", accept: next => !next || ["TD", "TH"].includes(next.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 = ["style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript"];
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.tagName.toLowerCase();
94
+ parentTagName = getTagName(parentNode);
95
95
  }
96
96
  if (!parentTagName || TEXT_NODE_TAGS.includes(parentTagName)) {
97
- if (parentTagName == "script" || parentTagName == "style") {
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.tagName.toLowerCase();
112
- const omittedStartTag = compressHTML && OMITTED_START_TAGS.find(omittedStartTag => tagName == omittedStartTag.tagName && omittedStartTag.accept(element));
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 (element.tagName == "TEMPLATE" && !element.childNodes.length) {
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.tagName && omittedEndTag.accept(element.nextSibling, element));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -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" || mutation.target.tagName == "SOURCE") {
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
  });
@@ -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.toLowerCase();
630
- return tagName != "svg" && tagName != "style" && tagName != "link";
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(resourceURL, { asBinary: true, expectedType, groupDuplicates: options.groupDuplicateImages && resourceElement.tagName == "IMG" && attributeName == "src" });
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 {
@@ -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
- if (element.tagName == "CANVAS") {
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 (element.tagName == "IMG") {
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 (element.tagName == "VIDEO") {
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 (element.tagName == "IFRAME") {
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 (element.tagName == "INPUT") {
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 (element.tagName == "TEXTAREA") {
338
+ if (tagName == "TEXTAREA") {
338
339
  element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.value);
339
340
  data.markedElements.push(element);
340
341
  }
341
- if (element.tagName == "SELECT") {
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 (element.tagName == "SCRIPT") {
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);