Explorar o código

handle ant type of images to be stored in CSS custom properties

Gildas %!s(int64=7) %!d(string=hai) anos
pai
achega
04de549c34
Modificáronse 1 ficheiros con 59 adicións e 19 borrados
  1. 59 19
      lib/single-file/single-file-core.js

+ 59 - 19
lib/single-file/single-file-core.js

@@ -18,6 +18,8 @@
  *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/* global CSSRule */
+
 this.SingleFileCore = this.SingleFileCore || (() => {
 
 	const SELECTED_CONTENT_ATTRIBUTE_NAME = "data-single-file-selected-content";
@@ -710,7 +712,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 		async processStylesheets() {
 			await Promise.all(Array.from(this.doc.querySelectorAll("style")).map(async (styleElement, indexStyle) => {
 				this.stats.add("processed", "stylesheets", 1);
-				styleElement.textContent = await DomProcessorHelper.processStylesheet(styleElement.textContent, this.baseURI, this.options, false, indexStyle, this.batchRequest);
+				styleElement.textContent = await DomProcessorHelper.processStylesheet(styleElement.textContent, styleElement.sheet.cssRules, this.baseURI, this.options, false, indexStyle, this.batchRequest);
 			}));
 		}
 
@@ -832,7 +834,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 		}
 
 		async processStyleAttributes() {
-			await Promise.all(Array.from(this.doc.querySelectorAll("[style]")).map(async element => element.setAttribute("style", await DomProcessorHelper.processStylesheet(element.getAttribute("style"), this.baseURI, this.options, true, 0, this.batchRequest))));
+			await Promise.all(Array.from(this.doc.querySelectorAll("[style]")).map(async element => element.setAttribute("style", await DomProcessorHelper.processStylesheet(element.getAttribute("style"), [{ type: CSSRule.STYLE_RULE, cssText: element.getAttribute("style") }], this.baseURI, this.options, true, 0, this.batchRequest))));
 		}
 
 		async resolveLinkedStylesheetURLs() {
@@ -1012,34 +1014,72 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			}
 		}
 
-		static async processStylesheet(stylesheetContent, baseURI, options, inline, indexStyle, batchRequest) {
+		static async processStylesheet(stylesheetContent, cssRules, baseURI, options, inline, indexStyle, batchRequest) {
+			let sheetContent = "", variablesInfo = { index: 0, cssText: "" };
 			const urlFunctions = DomUtil.getUrlFunctions(stylesheetContent);
-			let indexVariable = 0;
+			const variableNames = new Map();
+			const urlFunctionData = new Map();
 			await Promise.all(urlFunctions.map(async urlFunction => {
 				const originalResourceURL = DomUtil.matchURL(urlFunction);
 				const resourceURL = DomUtil.normalizeURL(originalResourceURL);
 				if (resourceURL && resourceURL != baseURI && DomUtil.testValidPath(resourceURL) && stylesheetContent.includes(urlFunction)) {
 					const dataURI = await batchRequest.addURL(resourceURL);
 					const regExpUrlFunction = DomUtil.getRegExp(urlFunction);
-					if (!inline && options.compressCSS && dataURI.startsWith(PREFIX_DATA_URI_IMAGE) && !dataURI.startsWith(PREFIX_DATA_URI_IMAGE_SVG)) {
-						const functions = stylesheetContent.match(regExpUrlFunction);
-						if (options.groupDuplicateImages && functions && functions.length > 1) {
-							const variableName = "--single-file-" + indexStyle + "-" + indexVariable;
-							stylesheetContent = variableName + ":url(\"" + dataURI + "\")" + (indexVariable ? ";" : "}") + stylesheetContent;
-							stylesheetContent = stylesheetContent.replace(regExpUrlFunction, "var(" + variableName + ")");
-							indexVariable++;
-						} else {
-							stylesheetContent = stylesheetContent.replace(regExpUrlFunction, urlFunction.replace(originalResourceURL, dataURI));
-						}
-					} else {
-						stylesheetContent = stylesheetContent.replace(regExpUrlFunction, urlFunction.replace(originalResourceURL, dataURI));
+					const functions = stylesheetContent.match(regExpUrlFunction);
+					if (options.groupDuplicateImages && functions.length > 1) {
+						variableNames.set(urlFunction, "--single-file-" + indexStyle + "-" + variableNames.size);
 					}
+					urlFunctionData.set(urlFunction, { regExpUrlFunction, dataURI });
 				}
 			}));
-			if (indexVariable) {
-				stylesheetContent = ":root{" + stylesheetContent;
+			const rulesContent = processRules(cssRules);
+			if (variablesInfo.cssText) {
+				sheetContent += ":root{" + variablesInfo.cssText + "}";
+			}
+			return sheetContent + rulesContent;
+
+			function processRules(cssRules) {
+				let rulesContent = "";
+				Array.from(cssRules).forEach(cssRule => {
+					if (cssRule.type == CSSRule.MEDIA_RULE) {
+						const mediaRulesContent = processRules(cssRule.cssRules);
+						rulesContent += "@media " + Array.from(cssRule.media).join(",") + "{" + mediaRulesContent + "}";
+					} else if (cssRule.type == CSSRule.STYLE_RULE) {
+						rulesContent += processURLFunctions(cssRule.cssText, !inline && options.compressCSS);
+					} else {
+						rulesContent += processURLFunctions(cssRule.cssText);
+					}
+				});
+				return rulesContent;
+			}
+
+			function processURLFunctions(cssText, useCustomProperties) {
+				const urlFunctions = DomUtil.getUrlFunctions(cssText);
+				urlFunctions.forEach(urlFunction => {
+					const originalResourceURL = DomUtil.matchURL(urlFunction);
+					const resourceURL = DomUtil.normalizeURL(originalResourceURL);
+					if (resourceURL && resourceURL != baseURI && DomUtil.testValidPath(resourceURL) && cssText.includes(urlFunction)) {
+						const resourceInfo = urlFunctionData.get(urlFunction);
+						const dataURI = resourceInfo.dataURI;
+						if (useCustomProperties) {
+							const variableName = variableNames.get(urlFunction);
+							if (variableName) {
+								cssText = cssText.replace(resourceInfo.regExpUrlFunction, "var(" + variableName + ")");
+								if (variablesInfo.cssText) {
+									variablesInfo.cssText += ";";
+								}
+								variablesInfo.cssText += variableName + ":url(\"" + dataURI + "\")";
+								variablesInfo.index++;
+							} else {
+								cssText = cssText.replace(resourceInfo.regExpUrlFunction, urlFunction.replace(originalResourceURL, dataURI));
+							}
+						} else {
+							cssText = cssText.replace(resourceInfo.regExpUrlFunction, urlFunction.replace(originalResourceURL, dataURI));
+						}
+					}
+				});
+				return cssText;
 			}
-			return stylesheetContent;
 		}
 
 		static async processAttribute(resourceElements, attributeName, prefixDataURI, baseURI, batchRequest) {