|
|
@@ -30,6 +30,8 @@ this.docHelper = this.docHelper || (() => {
|
|
|
const SHADOW_ROOT_ATTRIBUTE_NAME = "data-single-file-shadow-root-element";
|
|
|
const WIN_ID_ATTRIBUTE_NAME = "data-frame-tree-win-id";
|
|
|
const IMAGE_ATTRIBUTE_NAME = "data-single-file-image";
|
|
|
+ const POSTER_ATTRIBUTE_NAME = "data-single-file-poster";
|
|
|
+ const CANVAS_ATTRIBUTE_NAME = "data-single-file-canvas";
|
|
|
const INPUT_VALUE_ATTRIBUTE_NAME = "data-single-file-value";
|
|
|
const LAZY_SRC_ATTRIBUTE_NAME = "data-lazy-loaded-src";
|
|
|
const IGNORED_REMOVED_TAG_NAMES = ["NOSCRIPT", "DISABLED-NOSCRIPT", "META", "LINK", "STYLE", "TITLE", "TEMPLATE", "SOURCE", "OBJECT"];
|
|
|
@@ -50,6 +52,8 @@ this.docHelper = this.docHelper || (() => {
|
|
|
PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME,
|
|
|
REMOVED_CONTENT_ATTRIBUTE_NAME,
|
|
|
IMAGE_ATTRIBUTE_NAME,
|
|
|
+ POSTER_ATTRIBUTE_NAME,
|
|
|
+ CANVAS_ATTRIBUTE_NAME,
|
|
|
INPUT_VALUE_ATTRIBUTE_NAME,
|
|
|
SHADOW_ROOT_ATTRIBUTE_NAME
|
|
|
};
|
|
|
@@ -73,147 +77,148 @@ this.docHelper = this.docHelper || (() => {
|
|
|
if (doc.head) {
|
|
|
doc.head.querySelectorAll("*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)").forEach(element => element.hidden = true);
|
|
|
}
|
|
|
- let canvasData, imageData, usedFonts;
|
|
|
- if (win) {
|
|
|
- canvasData = getCanvasData(doc, win);
|
|
|
- imageData = getImageData(doc, win, options);
|
|
|
- }
|
|
|
- if (win) {
|
|
|
- if (doc.body && (options.removeHiddenElements || options.removeUnusedFonts || options.compressHTML)) {
|
|
|
- let elementsInfo = getElementsInfo(win, doc.body);
|
|
|
- if (options.removeHiddenElements) {
|
|
|
- let ignoredTags = JSON.parse(JSON.stringify(IGNORED_REMOVED_TAG_NAMES));
|
|
|
- if (!options.removeScripts) {
|
|
|
- ignoredTags = ignoredTags.concat("SCRIPT");
|
|
|
- }
|
|
|
- markHiddenCandidates(win, doc.body, elementsInfo, false, new Set(), ignoredTags);
|
|
|
- markHiddenElements(doc.body, imageData);
|
|
|
- doc.querySelectorAll("iframe").forEach(element => {
|
|
|
- if (element.getBoundingClientRect) {
|
|
|
- const boundingRect = element.getBoundingClientRect();
|
|
|
- if (element.hidden || element.style.display == "none" || boundingRect.width <= 1 && boundingRect.height <= 1) {
|
|
|
- element.setAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME, "");
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- elementsInfo = new Map(Array.from(elementsInfo).filter(([element]) => !element.attributes || element.getAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME) != ""));
|
|
|
- }
|
|
|
- if (options.removeUnusedFonts) {
|
|
|
- let loadedFonts;
|
|
|
- if (doc.fonts) {
|
|
|
- loadedFonts = Array.from(doc.fonts).filter(font => font.status == "loaded" || font.status == "loading");
|
|
|
- }
|
|
|
- usedFonts = getUsedFonts(elementsInfo, loadedFonts);
|
|
|
- }
|
|
|
- if (options.compressHTML) {
|
|
|
- elementsInfo.forEach((elementInfo, element) => {
|
|
|
- if (element.attributes && elementInfo.whiteSpace.startsWith("pre")) {
|
|
|
- element.setAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, "");
|
|
|
- }
|
|
|
- });
|
|
|
+ let elementsInfo;
|
|
|
+ if (win && doc.body) {
|
|
|
+ if (options.removeHiddenElements) {
|
|
|
+ options.ignoredTags = Array.from(IGNORED_REMOVED_TAG_NAMES);
|
|
|
+ if (!options.removeScripts) {
|
|
|
+ options.ignoredTags = options.ignoredTags.concat("SCRIPT");
|
|
|
}
|
|
|
}
|
|
|
+ elementsInfo = getElementsInfo(win, doc, doc.body, options);
|
|
|
}
|
|
|
- retrieveInputValues(doc);
|
|
|
+ saveInputValues(doc);
|
|
|
return {
|
|
|
- canvasData,
|
|
|
+ canvasData: elementsInfo.canvasData,
|
|
|
fontsData: getFontsData(doc),
|
|
|
stylesheetsData: getStylesheetsData(doc),
|
|
|
- imageData,
|
|
|
- postersData: getPostersData(doc),
|
|
|
- usedFonts,
|
|
|
- shadowRootsData: getShadowRootsData(doc.body),
|
|
|
+ imageData: elementsInfo.imagesData,
|
|
|
+ postersData: elementsInfo.postersData,
|
|
|
+ usedFonts: elementsInfo.usedFonts,
|
|
|
+ shadowRootsData: elementsInfo.shadowRootsData,
|
|
|
referrer: doc.referrer
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- function getShadowRootsData(element, data = { indexElement: 0, shadowRootsData: [] }) {
|
|
|
- element.querySelectorAll("*").forEach(element => {
|
|
|
+ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Set(), canvasData: [], imagesData: [], postersData: [], shadowRootsData: [] }, ascendantHidden) {
|
|
|
+ const elements = Array.from(element.childNodes).filter(node => node instanceof win.HTMLElement);
|
|
|
+ elements.forEach(element => {
|
|
|
+ let elementHidden;
|
|
|
+ if (options.removeHiddenElements || options.removeUnusedFonts || options.compressHTML) {
|
|
|
+ const computedStyle = win.getComputedStyle(element);
|
|
|
+ if (options.removeHiddenElements) {
|
|
|
+ const display = computedStyle.getPropertyValue("display");
|
|
|
+ const opacity = computedStyle.getPropertyValue("opacity");
|
|
|
+ const visibility = computedStyle.getPropertyValue("visibility");
|
|
|
+ if (ascendantHidden && !options.ignoredTags.includes(element.tagName)) {
|
|
|
+ if (elements.length) {
|
|
|
+ const elements = Array.from(element.childNodes).filter(node => node instanceof win.HTMLElement);
|
|
|
+ elements.forEach(element => element.setAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME, ""));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ elementHidden = ascendantHidden || testHiddenElement(element, { display, opacity, visibility });
|
|
|
+ }
|
|
|
+ if (options.compressHTML) {
|
|
|
+ const whiteSpace = computedStyle.getPropertyValue("white-space");
|
|
|
+ if (whiteSpace.startsWith("pre")) {
|
|
|
+ element.setAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, "");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (options.removeUnusedFonts) {
|
|
|
+ getUsedFont(computedStyle, options, data.usedFonts);
|
|
|
+ getUsedFont(win.getComputedStyle(element, ":first-letter"), options, data.usedFonts);
|
|
|
+ getUsedFont(win.getComputedStyle(element, ":before"), options, data.usedFonts);
|
|
|
+ getUsedFont(win.getComputedStyle(element, ":after"), options, data.usedFonts);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ getResourcesInfo(win, doc, element, options, data, elementHidden);
|
|
|
if (element.shadowRoot) {
|
|
|
- const indexEntry = data.indexElement;
|
|
|
- element.setAttribute(SHADOW_ROOT_ATTRIBUTE_NAME, indexEntry);
|
|
|
- data.indexElement++;
|
|
|
- getShadowRootsData(element.shadowRoot, data);
|
|
|
- data.shadowRootsData[indexEntry] = { content: element.shadowRoot.innerHTML };
|
|
|
+ const shadowRootInfo = {};
|
|
|
+ element.setAttribute(SHADOW_ROOT_ATTRIBUTE_NAME, data.shadowRootsData.length);
|
|
|
+ data.shadowRootsData.push(shadowRootInfo);
|
|
|
+ getElementsInfo(win, doc, element.shadowRoot, options, data, elementHidden);
|
|
|
+ shadowRootInfo.content = element.shadowRoot.innerHTML;
|
|
|
}
|
|
|
+ getElementsInfo(win, doc, element, options, data, elementHidden);
|
|
|
});
|
|
|
- return data.shadowRootsData;
|
|
|
+ data.usedFonts = Array.from(data.usedFonts);
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
- function getUsedFonts(styles, loadedFonts) {
|
|
|
- const usedFonts = new Set();
|
|
|
- styles.forEach(style => {
|
|
|
- const fontFamilyNames = style.fontFamily.split(",");
|
|
|
- fontFamilyNames.forEach(fontFamilyName => {
|
|
|
- fontFamilyName = normalizeFontFamily(fontFamilyName);
|
|
|
- if (!loadedFonts || loadedFonts.find(font => normalizeFontFamily(font.family) == fontFamilyName && font.style == style.fontStyle)) {
|
|
|
- const { fontWeight, fontStyle, fontVariant } = style;
|
|
|
- usedFonts.add([fontFamilyName, fontWeight, fontStyle, fontVariant]);
|
|
|
+ function getResourcesInfo(win, doc, element, options, data, elementHidden) {
|
|
|
+ if (element.tagName == "CANVAS") {
|
|
|
+ try {
|
|
|
+ const size = getSize(win, element);
|
|
|
+ data.canvasData.push({ dataURI: element.toDataURL("image/png", ""), width: size.width, height: size.height });
|
|
|
+ element.setAttribute(CANVAS_ATTRIBUTE_NAME, data.canvasData.length - 1);
|
|
|
+ } catch (error) {
|
|
|
+ // ignored
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (element.tagName == "IMG") {
|
|
|
+ element.setAttribute(IMAGE_ATTRIBUTE_NAME, data.imagesData.length);
|
|
|
+ const imageData = {
|
|
|
+ currentSrc: elementHidden ?
|
|
|
+ "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" :
|
|
|
+ (options.loadDeferredImages && element.getAttribute(LAZY_SRC_ATTRIBUTE_NAME)) || element.currentSrc
|
|
|
+ };
|
|
|
+ element.removeAttribute(LAZY_SRC_ATTRIBUTE_NAME);
|
|
|
+ const computedStyle = win.getComputedStyle(element);
|
|
|
+ if (computedStyle) {
|
|
|
+ imageData.size = getSize(win, element);
|
|
|
+ if ((!computedStyle.getPropertyValue("box-shadow") || computedStyle.getPropertyValue("box-shadow") == "none") &&
|
|
|
+ (!computedStyle.getPropertyValue("background-image") || computedStyle.getPropertyValue("background-image") == "none") &&
|
|
|
+ (imageData.size.pxWidth > 1 || imageData.size.pxHeight > 1)) {
|
|
|
+ imageData.replaceable = true;
|
|
|
+ imageData.backgroundColor = computedStyle.getPropertyValue("background-color");
|
|
|
+ imageData.objectFit = computedStyle.getPropertyValue("object-fit");
|
|
|
+ imageData.boxSizing = computedStyle.getPropertyValue("box-sizing");
|
|
|
+ imageData.objectPosition = computedStyle.getPropertyValue("object-position");
|
|
|
}
|
|
|
- });
|
|
|
- });
|
|
|
- return Array.from(usedFonts);
|
|
|
- }
|
|
|
-
|
|
|
- function normalizeFontFamily(fontFamilyName) {
|
|
|
- return removeQuotes(fontFamilyName.trim()).toLowerCase();
|
|
|
- }
|
|
|
-
|
|
|
- function getElementsInfo(win, element) {
|
|
|
- const elementsInfo = new Map();
|
|
|
- element.querySelectorAll("*").forEach(element => {
|
|
|
- setInfo(win, element, elementsInfo);
|
|
|
- setInfo(win, element, elementsInfo, ":first-letter");
|
|
|
- setInfo(win, element, elementsInfo, ":before");
|
|
|
- setInfo(win, element, elementsInfo, ":after");
|
|
|
- });
|
|
|
- return elementsInfo;
|
|
|
- }
|
|
|
-
|
|
|
- function setInfo(win, element, elementsInfo, pseudoElement) {
|
|
|
- const computedStyle = win.getComputedStyle(element, pseudoElement);
|
|
|
- const key = pseudoElement ? { element, pseudoElement } : element;
|
|
|
- elementsInfo.set(key, {
|
|
|
- display: computedStyle.getPropertyValue("display"),
|
|
|
- opacity: computedStyle.getPropertyValue("opacity"),
|
|
|
- visibility: computedStyle.getPropertyValue("visibility"),
|
|
|
- fontFamily: computedStyle.getPropertyValue("font-family"),
|
|
|
- fontWeight: getFontWeight(computedStyle.getPropertyValue("font-weight")),
|
|
|
- fontStyle: computedStyle.getPropertyValue("font-style") || "normal",
|
|
|
- fontVariant: computedStyle.getPropertyValue("font-variant") || "normal",
|
|
|
- whiteSpace: computedStyle.getPropertyValue("white-space")
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- function markHiddenCandidates(win, element, elementsInfo, elementHidden, removedCandidates, ignoredTags) {
|
|
|
- const elements = Array.from(element.childNodes).filter(node => node instanceof win.HTMLElement);
|
|
|
- elements.forEach(element => markHiddenCandidates(win, element, elementsInfo, elementHidden || testHiddenElement(element, elementsInfo.get(element)), removedCandidates, ignoredTags));
|
|
|
- if (elementHidden && !ignoredTags.includes(element.tagName)) {
|
|
|
- if (elements.length) {
|
|
|
- if (!elements.find(element => !removedCandidates.has(element))) {
|
|
|
- removedCandidates.add(element);
|
|
|
- elements.forEach(element => element.setAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME, ""));
|
|
|
+ }
|
|
|
+ data.imagesData.push(imageData);
|
|
|
+ }
|
|
|
+ if (element.tagName == "VIDEO") {
|
|
|
+ if (!element.poster) {
|
|
|
+ const canvasElement = doc.createElement("canvas");
|
|
|
+ const context = canvasElement.getContext("2d");
|
|
|
+ canvasElement.width = element.clientWidth;
|
|
|
+ canvasElement.height = element.clientHeight;
|
|
|
+ try {
|
|
|
+ context.drawImage(element, 0, 0, canvasElement.width, canvasElement.height);
|
|
|
+ data.postersData.push(canvasElement.toDataURL("image/png", ""));
|
|
|
+ element.setAttribute(POSTER_ATTRIBUTE_NAME, data.postersData.length - 1);
|
|
|
+ } catch (error) {
|
|
|
+ // ignored
|
|
|
}
|
|
|
- } else {
|
|
|
- removedCandidates.add(element);
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- function markHiddenElements(element, imageData) {
|
|
|
- if (element.getAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME) == "") {
|
|
|
- element.removeAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME);
|
|
|
- if (element.tagName == "IMG") {
|
|
|
- const imgData = imageData[Number(element.getAttribute(IMAGE_ATTRIBUTE_NAME))];
|
|
|
- if (imgData) {
|
|
|
- imgData.currentSrc = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
|
|
+ if (element.tagName == "IFRAME") {
|
|
|
+ if (element.getBoundingClientRect) {
|
|
|
+ const boundingRect = element.getBoundingClientRect();
|
|
|
+ if (element.hidden || element.style.display == "none" || boundingRect.width <= 1 && boundingRect.height <= 1) {
|
|
|
+ element.setAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME, "");
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
- Array.from(element.childNodes).filter(node => node.nodeType == 1).forEach(element => markHiddenElements(element, imageData));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function getUsedFont(computedStyle, options, usedFonts) {
|
|
|
+ const fontStyle = computedStyle.getPropertyValue("font-style") || "normal";
|
|
|
+ computedStyle.getPropertyValue("font-family").split(",").forEach(fontFamilyName => {
|
|
|
+ fontFamilyName = normalizeFontFamily(fontFamilyName);
|
|
|
+ if (!options.loadedFonts || options.loadedFonts.find(font => normalizeFontFamily(font.family) == fontFamilyName && font.style == fontStyle)) {
|
|
|
+ const fontWeight = getFontWeight(computedStyle.getPropertyValue("font-weight"));
|
|
|
+ const fontVariant = computedStyle.getPropertyValue("font-variant") || "normal";
|
|
|
+ usedFonts.add([fontFamilyName, fontWeight, fontStyle, fontVariant]);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function normalizeFontFamily(fontFamilyName) {
|
|
|
+ return removeQuotes(fontFamilyName.trim()).toLowerCase();
|
|
|
+ }
|
|
|
+
|
|
|
function testHiddenElement(element, style) {
|
|
|
let hidden = false;
|
|
|
if (style) {
|
|
|
@@ -246,25 +251,12 @@ this.docHelper = this.docHelper || (() => {
|
|
|
doc.querySelectorAll("[" + PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME));
|
|
|
}
|
|
|
doc.querySelectorAll("[" + IMAGE_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(IMAGE_ATTRIBUTE_NAME));
|
|
|
+ doc.querySelectorAll("[" + POSTER_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(POSTER_ATTRIBUTE_NAME));
|
|
|
+ doc.querySelectorAll("[" + CANVAS_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(CANVAS_ATTRIBUTE_NAME));
|
|
|
doc.querySelectorAll("[" + INPUT_VALUE_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(INPUT_VALUE_ATTRIBUTE_NAME));
|
|
|
doc.querySelectorAll("[" + SHADOW_ROOT_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(SHADOW_ROOT_ATTRIBUTE_NAME));
|
|
|
}
|
|
|
|
|
|
- function getCanvasData(doc, win) {
|
|
|
- if (doc) {
|
|
|
- const canvasData = [];
|
|
|
- doc.querySelectorAll("canvas").forEach(canvasElement => {
|
|
|
- try {
|
|
|
- const size = getSize(win, canvasElement);
|
|
|
- canvasData.push({ dataURI: canvasElement.toDataURL("image/png", ""), width: size.width, height: size.height });
|
|
|
- } catch (error) {
|
|
|
- canvasData.push(null);
|
|
|
- }
|
|
|
- });
|
|
|
- return canvasData;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
function getStylesheetsData(doc) {
|
|
|
if (doc) {
|
|
|
const contents = [];
|
|
|
@@ -287,34 +279,6 @@ this.docHelper = this.docHelper || (() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- function getImageData(doc, win, options) {
|
|
|
- if (doc) {
|
|
|
- const data = [];
|
|
|
- doc.querySelectorAll("img").forEach((imageElement, imageElementIndex) => {
|
|
|
- imageElement.setAttribute(IMAGE_ATTRIBUTE_NAME, imageElementIndex);
|
|
|
- const imageData = {
|
|
|
- currentSrc: (options.loadDeferredImages && imageElement.getAttribute(LAZY_SRC_ATTRIBUTE_NAME)) || imageElement.currentSrc
|
|
|
- };
|
|
|
- imageElement.removeAttribute(LAZY_SRC_ATTRIBUTE_NAME);
|
|
|
- const computedStyle = win.getComputedStyle(imageElement);
|
|
|
- if (computedStyle) {
|
|
|
- imageData.size = getSize(win, imageElement);
|
|
|
- if ((!computedStyle.getPropertyValue("box-shadow") || computedStyle.getPropertyValue("box-shadow") == "none") &&
|
|
|
- (!computedStyle.getPropertyValue("background-image") || computedStyle.getPropertyValue("background-image") == "none") &&
|
|
|
- (imageData.size.pxWidth > 1 || imageData.size.pxHeight > 1)) {
|
|
|
- imageData.replaceable = true;
|
|
|
- imageData.backgroundColor = computedStyle.getPropertyValue("background-color");
|
|
|
- imageData.objectFit = computedStyle.getPropertyValue("object-fit");
|
|
|
- imageData.boxSizing = computedStyle.getPropertyValue("box-sizing");
|
|
|
- imageData.objectPosition = computedStyle.getPropertyValue("object-position");
|
|
|
- }
|
|
|
- }
|
|
|
- data.push(imageData);
|
|
|
- });
|
|
|
- return data;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
function getSize(win, imageElement) {
|
|
|
let pxWidth = imageElement.naturalWidth;
|
|
|
let pxHeight = imageElement.naturalHeight;
|
|
|
@@ -358,36 +322,13 @@ this.docHelper = this.docHelper || (() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- function getPostersData(doc) {
|
|
|
- if (doc) {
|
|
|
- const postersData = [];
|
|
|
- doc.querySelectorAll("video").forEach(videoElement => {
|
|
|
- if (videoElement.poster) {
|
|
|
- postersData.push(null);
|
|
|
- } else {
|
|
|
- const canvasElement = doc.createElement("canvas");
|
|
|
- const context = canvasElement.getContext("2d");
|
|
|
- canvasElement.width = videoElement.clientWidth;
|
|
|
- canvasElement.height = videoElement.clientHeight;
|
|
|
- try {
|
|
|
- context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
|
|
|
- postersData.push(canvasElement.toDataURL("image/png", ""));
|
|
|
- } catch (error) {
|
|
|
- postersData.push(null);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- return postersData;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
function getFontsData() {
|
|
|
if (typeof hooksFrame != "undefined") {
|
|
|
return hooksFrame.getFontsData();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- function retrieveInputValues(doc) {
|
|
|
+ function saveInputValues(doc) {
|
|
|
doc.querySelectorAll("input").forEach(input => input.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, input.value));
|
|
|
doc.querySelectorAll("input[type=radio], input[type=checkbox]").forEach(input => input.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, input.checked));
|
|
|
doc.querySelectorAll("textarea").forEach(textarea => textarea.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, textarea.value));
|