|
|
@@ -27,10 +27,10 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
const SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME = "data-single-file-selected-content-root";
|
|
|
const DEBUG = false;
|
|
|
|
|
|
- let Download, DOM, URL, cssTree, sessionId = 0;
|
|
|
+ let Download, docUtil, URL, cssTree, sessionId = 0;
|
|
|
|
|
|
function getClass(...args) {
|
|
|
- [Download, DOM, URL, cssTree] = args;
|
|
|
+ [Download, docUtil, URL, cssTree] = args;
|
|
|
return SingleFileClass;
|
|
|
}
|
|
|
|
|
|
@@ -149,7 +149,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
this.batchRequest = new BatchRequest();
|
|
|
this.processor = new DOMProcessor(options, this.batchRequest);
|
|
|
if (this.options.doc) {
|
|
|
- const docData = DOM.preProcessDoc(this.options.doc, this.options.win, this.options);
|
|
|
+ const docData = docUtil.preProcessDoc(this.options.doc, this.options.win, this.options);
|
|
|
this.options.canvasData = docData.canvasData;
|
|
|
this.options.fontsData = docData.fontsData;
|
|
|
this.options.stylesheetContents = docData.stylesheetContents;
|
|
|
@@ -158,7 +158,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
this.options.usedFonts = docData.usedFonts;
|
|
|
this.options.shadowRootContents = docData.shadowRootContents;
|
|
|
}
|
|
|
- this.options.content = this.options.content || (this.options.doc ? DOM.serialize(this.options.doc, false) : null);
|
|
|
+ this.options.content = this.options.content || (this.options.doc ? docUtil.serialize(this.options.doc, false) : null);
|
|
|
this.onprogress = options.onprogress || (() => { });
|
|
|
}
|
|
|
|
|
|
@@ -173,7 +173,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
await this.executeStage(RESOLVE_URLS_STAGE);
|
|
|
this.pendingPromises = this.executeStage(REPLACE_DATA_STAGE);
|
|
|
if (this.options.doc) {
|
|
|
- DOM.postProcessDoc(this.options.doc, this.options);
|
|
|
+ docUtil.postProcessDoc(this.options.doc, this.options);
|
|
|
this.options.doc = null;
|
|
|
this.options.win = null;
|
|
|
}
|
|
|
@@ -331,12 +331,12 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
pageContent = content.data;
|
|
|
this.baseURI = this.options.url = resourceURL;
|
|
|
}
|
|
|
- this.doc = DOM.createDoc(pageContent, this.baseURI);
|
|
|
- this.onEventAttributeNames = DOM.getOnEventAttributeNames(this.doc);
|
|
|
+ this.doc = docUtil.createDoc(pageContent, this.baseURI);
|
|
|
+ this.onEventAttributeNames = docUtil.getOnEventAttributeNames(this.doc);
|
|
|
}
|
|
|
|
|
|
async getPageData() {
|
|
|
- DOM.postProcessDoc(this.doc, this.options);
|
|
|
+ docUtil.postProcessDoc(this.doc, this.options);
|
|
|
const url = new URL(this.baseURI);
|
|
|
if (this.options.insertSingleFileComment) {
|
|
|
const infobarContent = (this.options.infobarContent || "").replace(/\\n/g, "\n").replace(/\\t/g, "\t");
|
|
|
@@ -348,11 +348,11 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
}
|
|
|
let size;
|
|
|
if (this.options.displayStats) {
|
|
|
- size = DOM.getContentSize(this.doc.documentElement.outerHTML);
|
|
|
+ size = docUtil.getContentSize(this.doc.documentElement.outerHTML);
|
|
|
}
|
|
|
- const content = DOM.serialize(this.doc, this.options.compressHTML);
|
|
|
+ const content = docUtil.serialize(this.doc, this.options.compressHTML);
|
|
|
if (this.options.displayStats) {
|
|
|
- const contentSize = DOM.getContentSize(content);
|
|
|
+ const contentSize = docUtil.getContentSize(content);
|
|
|
this.stats.set("processed", "HTML bytes", contentSize);
|
|
|
this.stats.add("discarded", "HTML bytes", size - contentSize);
|
|
|
}
|
|
|
@@ -390,22 +390,22 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
|
|
|
setInputValues() {
|
|
|
this.doc.querySelectorAll("input").forEach(input => {
|
|
|
- const value = input.getAttribute(DOM.inputValueAttributeName(this.options.sessionId));
|
|
|
+ const value = input.getAttribute(docUtil.inputValueAttributeName(this.options.sessionId));
|
|
|
input.setAttribute("value", value || "");
|
|
|
});
|
|
|
this.doc.querySelectorAll("input[type=radio], input[type=checkbox]").forEach(input => {
|
|
|
- const value = input.getAttribute(DOM.inputValueAttributeName(this.options.sessionId));
|
|
|
+ const value = input.getAttribute(docUtil.inputValueAttributeName(this.options.sessionId));
|
|
|
if (value == "true") {
|
|
|
input.setAttribute("checked", "");
|
|
|
}
|
|
|
});
|
|
|
this.doc.querySelectorAll("textarea").forEach(textarea => {
|
|
|
- const value = textarea.getAttribute(DOM.inputValueAttributeName(this.options.sessionId));
|
|
|
+ const value = textarea.getAttribute(docUtil.inputValueAttributeName(this.options.sessionId));
|
|
|
textarea.textContent = value || "";
|
|
|
});
|
|
|
this.doc.querySelectorAll("select").forEach(select => {
|
|
|
select.querySelectorAll("option").forEach(option => {
|
|
|
- const selected = option.getAttribute(DOM.inputValueAttributeName(this.options.sessionId)) != null;
|
|
|
+ const selected = option.getAttribute(docUtil.inputValueAttributeName(this.options.sessionId)) != null;
|
|
|
if (selected) {
|
|
|
option.setAttribute("selected", "");
|
|
|
}
|
|
|
@@ -502,7 +502,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
this.doc.body.querySelectorAll(":not(svg) title, meta, link[href][rel*=\"icon\"]").forEach(element => element instanceof this.options.win.HTMLElement && this.doc.head.appendChild(element));
|
|
|
}
|
|
|
if (this.options.imageData) {
|
|
|
- const dataAttributeName = DOM.imagesAttributeName(this.options.sessionId);
|
|
|
+ const dataAttributeName = docUtil.imagesAttributeName(this.options.sessionId);
|
|
|
this.doc.querySelectorAll("img").forEach(imgElement => {
|
|
|
const imgData = this.options.imageData[Number(imgElement.getAttribute(dataAttributeName))];
|
|
|
if (this.options.removeHiddenElements && imgData.size && !imgData.size.pxWidth && !imgData.size.pxHeight) {
|
|
|
@@ -612,27 +612,27 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
|
|
|
removeUnusedStyles() {
|
|
|
if (!this.mediaAllInfo) {
|
|
|
- this.mediaAllInfo = DOM.getMediaAllInfo(this.doc, this.stylesheets, this.styles);
|
|
|
+ this.mediaAllInfo = docUtil.getMediaAllInfo(this.doc, this.stylesheets, this.styles);
|
|
|
}
|
|
|
- const stats = DOM.minifyCSSRules(this.stylesheets, this.styles, this.mediaAllInfo);
|
|
|
+ const stats = docUtil.minifyCSSRules(this.stylesheets, this.styles, this.mediaAllInfo);
|
|
|
this.stats.set("processed", "CSS rules", stats.processed);
|
|
|
this.stats.set("discarded", "CSS rules", stats.discarded);
|
|
|
}
|
|
|
|
|
|
removeUnusedFonts() {
|
|
|
- DOM.removeUnusedFonts(this.doc, this.stylesheets, this.styles, this.options);
|
|
|
+ docUtil.removeUnusedFonts(this.doc, this.stylesheets, this.styles, this.options);
|
|
|
}
|
|
|
|
|
|
removeAlternativeFonts() {
|
|
|
- DOM.removeAlternativeFonts(this.doc, this.stylesheets);
|
|
|
+ docUtil.removeAlternativeFonts(this.doc, this.stylesheets);
|
|
|
}
|
|
|
|
|
|
removeAlternativeImages() {
|
|
|
- DOM.removeAlternativeImages(this.doc);
|
|
|
+ docUtil.removeAlternativeImages(this.doc);
|
|
|
}
|
|
|
|
|
|
removeHiddenElements() {
|
|
|
- const hiddenElements = this.doc.querySelectorAll("[" + DOM.removedContentAttributeName(this.options.sessionId) + "]");
|
|
|
+ const hiddenElements = this.doc.querySelectorAll("[" + docUtil.removedContentAttributeName(this.options.sessionId) + "]");
|
|
|
this.stats.set("discarded", "hidden elements", hiddenElements.length);
|
|
|
this.stats.set("processed", "hidden elements", hiddenElements.length);
|
|
|
hiddenElements.forEach(element => element.remove());
|
|
|
@@ -641,16 +641,16 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
compressHTML() {
|
|
|
let size;
|
|
|
if (this.options.displayStats) {
|
|
|
- size = DOM.getContentSize(this.doc.documentElement.outerHTML);
|
|
|
+ size = docUtil.getContentSize(this.doc.documentElement.outerHTML);
|
|
|
}
|
|
|
- DOM.minifyHTML(this.doc, { preservedSpaceAttributeName: DOM.preservedSpaceAttributeName(this.options.sessionId) });
|
|
|
+ docUtil.minifyHTML(this.doc, { preservedSpaceAttributeName: docUtil.preservedSpaceAttributeName(this.options.sessionId) });
|
|
|
if (this.options.displayStats) {
|
|
|
- this.stats.add("discarded", "HTML bytes", size - DOM.getContentSize(this.doc.documentElement.outerHTML));
|
|
|
+ this.stats.add("discarded", "HTML bytes", size - docUtil.getContentSize(this.doc.documentElement.outerHTML));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
removeAlternativeMedias() {
|
|
|
- const stats = DOM.minifyMedias(this.stylesheets);
|
|
|
+ const stats = docUtil.minifyMedias(this.stylesheets);
|
|
|
this.stats.set("processed", "medias", stats.processed);
|
|
|
this.stats.set("discarded", "medias", stats.discarded);
|
|
|
}
|
|
|
@@ -708,14 +708,14 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
|
|
|
insertShadowRootContents() {
|
|
|
if (this.options.shadowRootContents) {
|
|
|
- this.doc.querySelectorAll("[" + DOM.shadowRootAttributeName(this.options.sessionId) + "]").forEach((element, elementIndex) => {
|
|
|
- const DOMParser = DOM.getParser();
|
|
|
+ this.doc.querySelectorAll("[" + docUtil.shadowRootAttributeName(this.options.sessionId) + "]").forEach((element, elementIndex) => {
|
|
|
+ const DOMParser = docUtil.getParser();
|
|
|
const elementInfo = this.options.shadowRootContents[elementIndex];
|
|
|
if (DOMParser && elementInfo) {
|
|
|
const frameElement = this.doc.createElement("iframe");
|
|
|
frameElement.setAttribute("style", "all:initial!important;border:0!important;width:100%!important;height:" + elementInfo.height + "px!important");
|
|
|
const windowId = "shadow-" + this.options.framesData.length;
|
|
|
- frameElement.setAttribute(DOM.windowIdAttributeName(this.options.sessionId), windowId);
|
|
|
+ frameElement.setAttribute(docUtil.windowIdAttributeName(this.options.sessionId), windowId);
|
|
|
this.options.framesData.push({ windowId, content: elementInfo.content, baseURI: this.baseURI });
|
|
|
element.appendChild(frameElement);
|
|
|
}
|
|
|
@@ -881,7 +881,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
const frameElements = Array.from(this.doc.querySelectorAll("iframe, frame, object[type=\"text/html\"][data]"));
|
|
|
await Promise.all(frameElements.map(async frameElement => {
|
|
|
DomProcessorHelper.setFrameEmptySrc(frameElement);
|
|
|
- const frameWindowId = frameElement.getAttribute(DOM.windowIdAttributeName(this.options.sessionId));
|
|
|
+ const frameWindowId = frameElement.getAttribute(docUtil.windowIdAttributeName(this.options.sessionId));
|
|
|
if (frameWindowId) {
|
|
|
const frameData = this.options.framesData.find(frame => frame.windowId == frameWindowId);
|
|
|
if (frameData) {
|
|
|
@@ -918,7 +918,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
if (this.options.framesData) {
|
|
|
const frameElements = Array.from(this.doc.querySelectorAll("iframe, frame, object[type=\"text/html\"][data]"));
|
|
|
await Promise.all(frameElements.map(async frameElement => {
|
|
|
- const frameWindowId = frameElement.getAttribute(DOM.windowIdAttributeName(this.options.sessionId));
|
|
|
+ const frameWindowId = frameElement.getAttribute(docUtil.windowIdAttributeName(this.options.sessionId));
|
|
|
if (frameWindowId) {
|
|
|
const frameData = this.options.framesData.find(frame => frame.windowId == frameWindowId);
|
|
|
if (frameData) {
|
|
|
@@ -926,7 +926,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
this.stats.add("processed", "frames", 1);
|
|
|
await frameData.processor.run();
|
|
|
const pageData = await frameData.processor.getPageData();
|
|
|
- frameElement.removeAttribute(DOM.windowIdAttributeName(this.options.sessionId));
|
|
|
+ frameElement.removeAttribute(docUtil.windowIdAttributeName(this.options.sessionId));
|
|
|
if (pageData.content.match(NOSCRIPT_TAG_FOUND) || pageData.content.match(SCRIPT_TAG_FOUND)) {
|
|
|
frameElement.setAttribute("sandbox", "allow-scripts allow-same-origin");
|
|
|
} else {
|
|
|
@@ -986,7 +986,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
this.doc.querySelectorAll("[style]").forEach(element => {
|
|
|
let styleContent = element.getAttribute("style");
|
|
|
if (this.options.compressCSS) {
|
|
|
- styleContent = DOM.compressCSS(styleContent);
|
|
|
+ styleContent = docUtil.compressCSS(styleContent);
|
|
|
}
|
|
|
styleContent = DomProcessorHelper.resolveStylesheetURLs(styleContent, this.baseURI, this.options);
|
|
|
const declarationList = cssTree.parse(styleContent, { context: "declarationList" });
|
|
|
@@ -1073,9 +1073,9 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
template = await DomUtil.evalTemplateVariable(template, "tab-id", () => String(options.tabId || "No tab id"), dontReplaceSlash);
|
|
|
template = await DomUtil.evalTemplateVariable(template, "url-last-segment", () => DomUtil.getLastSegment(url), dontReplaceSlash);
|
|
|
if (content) {
|
|
|
- template = await DomUtil.evalTemplateVariable(template, "digest-sha-256", async () => DOM.digest("SHA-256", content), dontReplaceSlash);
|
|
|
- template = await DomUtil.evalTemplateVariable(template, "digest-sha-384", async () => DOM.digest("SHA-384", content), dontReplaceSlash);
|
|
|
- template = await DomUtil.evalTemplateVariable(template, "digest-sha-512", async () => DOM.digest("SHA-512", content), dontReplaceSlash);
|
|
|
+ template = await DomUtil.evalTemplateVariable(template, "digest-sha-256", async () => docUtil.digest("SHA-256", content), dontReplaceSlash);
|
|
|
+ template = await DomUtil.evalTemplateVariable(template, "digest-sha-384", async () => docUtil.digest("SHA-384", content), dontReplaceSlash);
|
|
|
+ template = await DomUtil.evalTemplateVariable(template, "digest-sha-512", async () => docUtil.digest("SHA-512", content), dontReplaceSlash);
|
|
|
}
|
|
|
return template;
|
|
|
}
|
|
|
@@ -1178,7 +1178,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
resourceURL = content.resourceURL;
|
|
|
let importedStylesheetContent = DomUtil.removeCssComments(content.data);
|
|
|
if (options.compressCSS) {
|
|
|
- importedStylesheetContent = DOM.compressCSS(importedStylesheetContent);
|
|
|
+ importedStylesheetContent = docUtil.compressCSS(importedStylesheetContent);
|
|
|
}
|
|
|
importedStylesheetContent = DomUtil.wrapMediaQuery(importedStylesheetContent, match.media);
|
|
|
if (stylesheetContent.includes(cssImport)) {
|
|
|
@@ -1237,7 +1237,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
resourceURL = content.resourceURL;
|
|
|
let stylesheetContent = DomUtil.removeCssComments(content.data);
|
|
|
if (options.compressCSS) {
|
|
|
- stylesheetContent = DOM.compressCSS(stylesheetContent);
|
|
|
+ stylesheetContent = docUtil.compressCSS(stylesheetContent);
|
|
|
}
|
|
|
stylesheetContent = await DomProcessorHelper.resolveImportURLs(stylesheetContent, resourceURL, options);
|
|
|
return stylesheetContent;
|
|
|
@@ -1276,13 +1276,13 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
let { content } = await batchRequest.addURL(resourceURL);
|
|
|
let validResource = content == EMPTY_DATA_URI || content.startsWith(PREFIX_DATA_URI_VND) || content.startsWith(PREFIX_DATA_URI_IMAGE_SVG);
|
|
|
if (!validResource) {
|
|
|
- validResource = await DOM.validFont(urlFunction);
|
|
|
+ validResource = await docUtil.validFont(urlFunction);
|
|
|
}
|
|
|
if (!validResource) {
|
|
|
content = EMPTY_DATA_URI;
|
|
|
}
|
|
|
declaration.value.children.forEach(token => {
|
|
|
- if (token.type == "Url" && DOM.removeQuotes(DomUtil.getCSSValue(token.value)) == originalResourceURL) {
|
|
|
+ if (token.type == "Url" && docUtil.removeQuotes(DomUtil.getCSSValue(token.value)) == originalResourceURL) {
|
|
|
token.value.value = content;
|
|
|
}
|
|
|
});
|
|
|
@@ -1326,7 +1326,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
if (token.data.children) {
|
|
|
findURLToken(url, token.data.children, callback);
|
|
|
}
|
|
|
- if (token.data.type == "Url" && DOM.removeQuotes(DomUtil.getCSSValue(token.data.value)) == url) {
|
|
|
+ if (token.data.type == "Url" && docUtil.removeQuotes(DomUtil.getCSSValue(token.data.value)) == url) {
|
|
|
callback(token, children);
|
|
|
}
|
|
|
}
|
|
|
@@ -1386,7 +1386,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
if (DomUtil.testValidURL(resourceURL, baseURI, options.url)) {
|
|
|
try {
|
|
|
const { content } = await batchRequest.addURL(resourceURL, false);
|
|
|
- const DOMParser = DOM.getParser();
|
|
|
+ const DOMParser = docUtil.getParser();
|
|
|
if (DOMParser) {
|
|
|
const svgDoc = new DOMParser().parseFromString(content, "image/svg+xml");
|
|
|
const hashMatch = originalResourceURL.match(REGEXP_URL_HASH);
|
|
|
@@ -1414,7 +1414,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
|
|
|
static async processSrcset(resourceElements, attributeName, prefixDataURI, baseURI, options, batchRequest) {
|
|
|
await Promise.all(Array.from(resourceElements).map(async resourceElement => {
|
|
|
- const srcset = DOM.parseSrcset(resourceElement.getAttribute(attributeName));
|
|
|
+ const srcset = docUtil.parseSrcset(resourceElement.getAttribute(attributeName));
|
|
|
const srcsetValues = await Promise.all(srcset.map(async srcsetValue => {
|
|
|
let resourceURL = DomUtil.normalizeURL(srcsetValue.url);
|
|
|
if (!DomUtil.testIgnoredPath(resourceURL)) {
|
|
|
@@ -1588,7 +1588,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
|
|
|
}
|
|
|
|
|
|
static replaceImageSource(imgElement, variableName, options) {
|
|
|
- const dataAttributeName = DOM.imagesAttributeName(options.sessionId);
|
|
|
+ const dataAttributeName = docUtil.imagesAttributeName(options.sessionId);
|
|
|
if (imgElement.getAttribute(dataAttributeName) != null) {
|
|
|
const imgData = options.imageData[Number(imgElement.getAttribute(dataAttributeName))];
|
|
|
if (imgData.replaceable) {
|