Procházet zdrojové kódy

moved all dependencies of sf-core except cssTree into docUtil

Gildas před 7 roky
rodič
revize
6e7b7fc768

+ 86 - 75
lib/single-file/single-file-browser.js

@@ -54,93 +54,92 @@ this.SingleFile = this.SingleFile || (() => {
 		};
 	}
 
-	class Download {
-		static async getContent(resourceURL, options) {
-			let resourceContent, startTime;
-			if (DEBUG) {
-				startTime = Date.now();
-				log("  // STARTED download url =", resourceURL, "asDataURI =", options.asDataURI);
+
+	async function getContent(resourceURL, options) {
+		let resourceContent, startTime;
+		if (DEBUG) {
+			startTime = Date.now();
+			log("  // STARTED download url =", resourceURL, "asDataURI =", options.asDataURI);
+		}
+		if (!fetchResource) {
+			fetchResource = typeof superFetch == "undefined" ? fetch : superFetch.fetch;
+		}
+		try {
+			resourceContent = await fetchResource(resourceURL);
+			if (resourceContent.url) {
+				resourceURL = resourceContent.url;
 			}
-			if (!fetchResource) {
-				fetchResource = typeof superFetch == "undefined" ? fetch : superFetch.fetch;
+		} catch (error) {
+			return { data: options && options.asDataURI ? "data:base64," : "", resourceURL };
+		}
+		let contentType = resourceContent.headers && resourceContent.headers.get("content-type");
+		let charset;
+		if (contentType) {
+			const matchContentType = contentType.toLowerCase().split(";");
+			contentType = matchContentType[0].trim();
+			if (contentType.indexOf("/") <= 0) {
+				contentType = null;
 			}
-			try {
-				resourceContent = await fetchResource(resourceURL);
-				if (resourceContent.url) {
-					resourceURL = resourceContent.url;
+			const charsetValue = matchContentType[1] && matchContentType[1].trim();
+			if (charsetValue) {
+				const matchCharset = charsetValue.match(/^charset=(.*)/);
+				if (matchCharset) {
+					charset = docHelper.removeQuotes(matchCharset[1]);
 				}
-			} catch (error) {
-				return { data: options && options.asDataURI ? "data:base64," : "", resourceURL };
 			}
-			let contentType = resourceContent.headers && resourceContent.headers.get("content-type");
-			let charset;
-			if (contentType) {
-				const matchContentType = contentType.toLowerCase().split(";");
-				contentType = matchContentType[0].trim();
-				if (contentType.indexOf("/") <= 0) {
-					contentType = null;
+		}
+		if (options && options.asDataURI) {
+			try {
+				if (DEBUG) {
+					log("  // ENDED   download url =", resourceURL, "delay =", Date.now() - startTime);
 				}
-				const charsetValue = matchContentType[1] && matchContentType[1].trim();
-				if (charsetValue) {
-					const matchCharset = charsetValue.match(/^charset=(.*)/);
-					if (matchCharset) {
-						charset = docHelper.removeQuotes(matchCharset[1]);
-					}
+				const buffer = await resourceContent.arrayBuffer();
+				if (options.maxResourceSizeEnabled && buffer.byteLength > options.maxResourceSize * ONE_MB) {
+					return { data: "data:base64,", resourceURL };
+				} else {
+					const reader = new FileReader();
+					reader.readAsDataURL(new Blob([buffer], { type: contentType }));
+					const dataURI = await new Promise((resolve, reject) => {
+						reader.addEventListener("load", () => resolve(reader.result), false);
+						reader.addEventListener("error", reject, false);
+					});
+					return { data: dataURI, resourceURL };
 				}
+			} catch (error) {
+				return { data: "data:base64,", resourceURL };
 			}
-			if (options && options.asDataURI) {
-				try {
-					if (DEBUG) {
-						log("  // ENDED   download url =", resourceURL, "delay =", Date.now() - startTime);
-					}
-					const buffer = await resourceContent.arrayBuffer();
-					if (options.maxResourceSizeEnabled && buffer.byteLength > options.maxResourceSize * ONE_MB) {
-						return { data: "data:base64,", resourceURL };
-					} else {
-						const reader = new FileReader();
-						reader.readAsDataURL(new Blob([buffer], { type: contentType }));
-						const dataURI = await new Promise((resolve, reject) => {
-							reader.addEventListener("load", () => resolve(reader.result), false);
-							reader.addEventListener("error", reject, false);
-						});
-						return { data: dataURI, resourceURL };
-					}
-				} catch (error) {
-					return { data: "data:base64,", resourceURL };
+		} else {
+			if (resourceContent.status >= 400 || (options.validateTextContentType && contentType && !contentType.startsWith(PREFIX_CONTENT_TYPE_TEXT))) {
+				return { data: "", resourceURL };
+			}
+			if (!charset) {
+				const matchCharset = contentType && contentType.match(/\s*;\s*charset\s*=\s*"?([^";]*)"?(;|$)/i);
+				if (matchCharset && matchCharset[1] || options.charset) {
+					charset = (matchCharset && matchCharset[1].toLowerCase()) || options.charset;
 				}
+			}
+			if (!charset) {
+				charset = "utf-8";
+			}
+			let buffer;
+			try {
+				buffer = await resourceContent.arrayBuffer();
+			} catch (error) {
+				return { data: "", resourceURL };
+			}
+			if (DEBUG) {
+				log("  // ENDED   download url =", resourceURL, "delay =", Date.now() - startTime);
+			}
+			if (options.maxResourceSizeEnabled && buffer.byteLength > options.maxResourceSize * ONE_MB) {
+				return { data: "", resourceURL };
 			} else {
-				if (resourceContent.status >= 400 || (options.validateTextContentType && contentType && !contentType.startsWith(PREFIX_CONTENT_TYPE_TEXT))) {
-					return { data: "", resourceURL };
-				}
-				if (!charset) {
-					const matchCharset = contentType && contentType.match(/\s*;\s*charset\s*=\s*"?([^";]*)"?(;|$)/i);
-					if (matchCharset && matchCharset[1] || options.charset) {
-						charset = (matchCharset && matchCharset[1].toLowerCase()) || options.charset;
-					}
-				}
-				if (!charset) {
-					charset = "utf-8";
-				}
-				let buffer;
 				try {
-					buffer = await resourceContent.arrayBuffer();
+					return { data: new TextDecoder(charset).decode(buffer), resourceURL };
 				} catch (error) {
-					return { data: "", resourceURL };
-				}
-				if (DEBUG) {
-					log("  // ENDED   download url =", resourceURL, "delay =", Date.now() - startTime);
-				}
-				if (options.maxResourceSizeEnabled && buffer.byteLength > options.maxResourceSize * ONE_MB) {
-					return { data: "", resourceURL };
-				} else {
 					try {
-						return { data: new TextDecoder(charset).decode(buffer), resourceURL };
+						return { data: new TextDecoder("utf-8").decode(buffer), resourceURL };
 					} catch (error) {
-						try {
-							return { data: new TextDecoder("utf-8").decode(buffer), resourceURL };
-						} catch (error) {
-							return { data: "", resourceURL };
-						}
+						return { data: "", resourceURL };
 					}
 				}
 			}
@@ -165,6 +164,18 @@ this.SingleFile = this.SingleFile || (() => {
 	// docUtil
 	// ---
 	class docUtil {
+		static async getContent(resourceURL, options) {
+			return getContent(resourceURL, options);
+		}
+
+		static parseURL(resourceURL, baseURI) {
+			return new URL(resourceURL, baseURI);
+		}
+
+		static resolveURL(resourceURL, baseURI) {
+			return this.parseURL(resourceURL, baseURI).href;
+		}
+
 		static createDoc(pageContent, baseURI) {
 			const doc = (new DOMParser()).parseFromString(pageContent, "text/html");
 			let baseElement = doc.querySelector("base");
@@ -298,6 +309,6 @@ this.SingleFile = this.SingleFile || (() => {
 		console.log("S-File <browser>", ...args); // eslint-disable-line no-console
 	}
 
-	return { getClass: () => SingleFileCore.getClass(Download, docUtil, URL, cssTree) };
+	return { getClass: () => SingleFileCore.getClass(docUtil, cssTree) };
 
 })();

+ 16 - 16
lib/single-file/single-file-core.js

@@ -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, docUtil, URL, cssTree, sessionId = 0;
+	let docUtil, cssTree, sessionId = 0;
 
 	function getClass(...args) {
-		[Download, docUtil, URL, cssTree] = args;
+		[docUtil, cssTree] = args;
 		return SingleFileClass;
 	}
 
@@ -283,7 +283,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 				const [resourceURL, asDataURI] = JSON.parse(requestKey);
 				const resourceRequests = this.requests.get(requestKey);
 				try {
-					const content = await Download.getContent(resourceURL, { asDataURI, maxResourceSize: options.maxResourceSize, maxResourceSizeEnabled: options.maxResourceSizeEnabled });
+					const content = await docUtil.getContent(resourceURL, { asDataURI, maxResourceSize: options.maxResourceSize, maxResourceSizeEnabled: options.maxResourceSizeEnabled });
 					indexResource = indexResource + 1;
 					onloadListener({ index: indexResource, url: resourceURL });
 					resourceRequests.forEach(resourceRequest => resourceRequest.resolve({ content: content.data, indexResource, duplicate: Boolean(resourceRequests.length > 1) }));
@@ -327,7 +327,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 		async loadPage(pageContent) {
 			let resourceURL;
 			if (!pageContent || this.options.saveRawPage) {
-				const content = await Download.getContent(this.baseURI, { asDataURI: false, maxResourceSize: this.options.maxResourceSize, maxResourceSizeEnabled: this.options.maxResourceSizeEnabled });
+				const content = await docUtil.getContent(this.baseURI, { asDataURI: false, maxResourceSize: this.options.maxResourceSize, maxResourceSizeEnabled: this.options.maxResourceSizeEnabled });
 				pageContent = content.data;
 				this.baseURI = this.options.url = resourceURL;
 			}
@@ -337,7 +337,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 
 		async getPageData() {
 			docUtil.postProcessDoc(this.doc, this.options);
-			const url = new URL(this.baseURI);
+			const url = docUtil.parseURL(this.baseURI);
 			if (this.options.insertSingleFileComment) {
 				const infobarContent = (this.options.infobarContent || "").replace(/\\n/g, "\n").replace(/\\t/g, "\t");
 				const commentNode = this.doc.createComment("\n Page saved with SingleFile" +
@@ -593,7 +593,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 					const normalizedHref = DomUtil.normalizeURL(href);
 					if (element.tagName == "LINK") {
 						try {
-							element.setAttribute("href", new URL(href, this.options.baseURI || this.options.url));
+							element.setAttribute("href", docUtil.resolveURL(href, this.options.baseURI || this.options.url));
 						} catch (error) {
 							if (element.tagName == "LINK") {
 								element.setAttribute("href", EMPTY_URL);
@@ -601,7 +601,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 						}
 					} else if (normalizedHref == href) {
 						try {
-							element.setAttribute("href", new URL(normalizedHref, this.options.baseURI || this.options.url));
+							element.setAttribute("href", docUtil.resolveURL(normalizedHref, this.options.baseURI || this.options.url));
 						} catch (error) {
 							// ignored
 						}
@@ -869,7 +869,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			await Promise.all(Array.from(this.doc.querySelectorAll("script[src]")).map(async scriptElement => {
 				if (scriptElement.src) {
 					this.stats.add("processed", "scripts", 1);
-					const content = await Download.getContent(scriptElement.src, { asDataURI: false, maxResourceSize: this.options.maxResourceSize, maxResourceSizeEnabled: this.options.maxResourceSizeEnabled });
+					const content = await docUtil.getContent(scriptElement.src, { asDataURI: false, maxResourceSize: this.options.maxResourceSize, maxResourceSizeEnabled: this.options.maxResourceSizeEnabled });
 					scriptElement.textContent = content.data.replace(/<\//gi, "<\\/").replace(/\/>/gi, "\\/>");
 				}
 				scriptElement.removeAttribute("src");
@@ -1034,7 +1034,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 	class DomProcessorHelper {
 		static async evalTemplate(template, options, content, dontReplaceSlash) {
 			const date = new Date();
-			const url = new URL(options.url);
+			const url = docUtil.parseURL(options.url);
 			template = await DomUtil.evalTemplateVariable(template, "page-title", () => options.title || "No title", dontReplaceSlash);
 			template = await DomUtil.evalTemplateVariable(template, "page-language", () => options.info.lang || "No language", dontReplaceSlash);
 			template = await DomUtil.evalTemplateVariable(template, "page-description", () => options.info.description || "No description", dontReplaceSlash);
@@ -1168,13 +1168,13 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 					let resourceURL = DomUtil.normalizeURL(match.resourceURL);
 					if (!DomUtil.testIgnoredPath(resourceURL) && DomUtil.testValidPath(resourceURL, baseURI, options.url)) {
 						try {
-							resourceURL = new URL(match.resourceURL, baseURI).href;
+							resourceURL = docUtil.resolveURL(match.resourceURL, baseURI);
 						} catch (error) {
 							// ignored
 						}
 						if (DomUtil.testValidURL(resourceURL, baseURI, options.url)) {
 							const downloadOptions = { asDataURI: false, maxResourceSize: options.maxResourceSize, maxResourceSizeEnabled: options.maxResourceSizeEnabled, validateTextContentType: true };
-							const content = await Download.getContent(resourceURL, downloadOptions);
+							const content = await docUtil.getContent(resourceURL, downloadOptions);
 							resourceURL = content.resourceURL;
 							let importedStylesheetContent = DomUtil.removeCssComments(content.data);
 							if (options.compressCSS) {
@@ -1201,7 +1201,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 					if (DomUtil.testValidPath(resourceURL, baseURI, options.url)) {
 						let resolvedURL;
 						try {
-							resolvedURL = new URL(resourceURL, baseURI).href;
+							resolvedURL = docUtil.resolveURL(resourceURL, baseURI);
 						} catch (error) {
 							// ignored
 						}
@@ -1233,7 +1233,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			resourceURL = DomUtil.normalizeURL(resourceURL);
 			if (resourceURL && resourceURL != baseURI && resourceURL != ABOUT_BLANK_URI) {
 				const downloadOptions = { asDataURI: false, maxResourceSize: options.maxResourceSize, maxResourceSizeEnabled: options.maxResourceSizeEnabled, charset: options.charset };
-				const content = await Download.getContent(resourceURL, downloadOptions);
+				const content = await docUtil.getContent(resourceURL, downloadOptions);
 				resourceURL = content.resourceURL;
 				let stylesheetContent = DomUtil.removeCssComments(content.data);
 				if (options.compressCSS) {
@@ -1341,7 +1341,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 					resourceElement.setAttribute(attributeName, EMPTY_IMAGE);
 					if (DomUtil.testValidPath(resourceURL, baseURI, options.url)) {
 						try {
-							resourceURL = new URL(resourceURL, baseURI).href;
+							resourceURL = docUtil.resolveURL(resourceURL, baseURI);
 						} catch (error) {
 							// ignored
 						}
@@ -1379,7 +1379,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 				if (DomUtil.testValidPath(resourceURL, baseURI, options.url) && !DomUtil.testIgnoredPath(resourceURL)) {
 					resourceElement.setAttribute(attributeName, EMPTY_IMAGE);
 					try {
-						resourceURL = new URL(resourceURL, baseURI).href;
+						resourceURL = docUtil.resolveURL(resourceURL, baseURI);
 					} catch (error) {
 						// ignored
 					}
@@ -1420,7 +1420,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 					if (!DomUtil.testIgnoredPath(resourceURL)) {
 						if (DomUtil.testValidPath(resourceURL, baseURI, options.url)) {
 							try {
-								resourceURL = new URL(resourceURL, baseURI).href;
+								resourceURL = docUtil.resolveURL(resourceURL, baseURI);
 							} catch (error) {
 								// ignored
 							}