Explorar o código

preserve space when white-space style starts with "pre"

Gildas %!s(int64=7) %!d(string=hai) anos
pai
achega
1f32311759

+ 19 - 0
extension/core/content/content.js

@@ -64,12 +64,18 @@ this.singlefile.top = this.singlefile.top || (() => {
 		if (options.removeHiddenElements) {
 			selectRemovedElements(processor.REMOVED_CONTENT_ATTRIBUTE_NAME);
 		}
+		if (options.compressHTML) {
+			selectPreserveElements(processor.PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME);
+		}
 		options.url = options.url || document.location.href;
 		options.content = options.content || getDoctype(document) + document.documentElement.outerHTML;
 		await processor.initialize();
 		if (options.removeHiddenElements) {
 			unselectRemovedElements(processor.REMOVED_CONTENT_ATTRIBUTE_NAME);
 		}
+		if (options.compressHTML) {
+			unselectPreserveElements(processor.PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME);
+		}
 		if (options.shadowEnabled) {
 			singlefile.ui.init();
 		}
@@ -103,6 +109,19 @@ this.singlefile.top = this.singlefile.top || (() => {
 		document.head.querySelectorAll("noscript").forEach(noscriptElement => document.body.insertBefore(noscriptElement, document.body.firstChild));
 	}
 
+	function selectPreserveElements(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME) {
+		document.querySelectorAll("*").forEach(element => {
+			const style = getComputedStyle(element);
+			if (style.whiteSpace.startsWith("pre")) {
+				element.setAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, "");
+			}
+		});
+	}
+
+	function unselectPreserveElements(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME) {
+		document.querySelectorAll("[" + PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME));
+	}
+
 	function selectRemovedElements(REMOVED_CONTENT_ATTRIBUTE_NAME) {
 		document.querySelectorAll("html > body *:not(style):not(script):not(link)").forEach(element => {
 			const style = getComputedStyle(element);

+ 8 - 5
lib/single-file/htmlmini.js

@@ -124,11 +124,11 @@ this.htmlmini = this.htmlmini || (() => {
 	const modules = [collapseBooleanAttributes, mergeTextNodes, collapseWhitespace, removeComments, removeEmptyAttributes, removeRedundantAttributes];
 
 	return {
-		process: doc => {
+		process: (doc, options) => {
 			const nodesWalker = doc.createTreeWalker(doc.documentElement, NodeFilter.SHOW_ALL, null, false);
 			let node = nodesWalker.nextNode();
 			while (node) {
-				const deletedNode = modules.find(module => module(node));
+				const deletedNode = modules.find(module => module(node, options));
 				const previousNode = node;
 				node = nodesWalker.nextNode();
 				if (deletedNode) {
@@ -160,14 +160,17 @@ this.htmlmini = this.htmlmini || (() => {
 		}
 	}
 
-	function collapseWhitespace(node) {
+	function collapseWhitespace(node, options) {
 		if (node.nodeType == Node.TEXT_NODE) {
 			let element = node.parentElement;
+			const spacePreserved = element.getAttribute(options.preservedSpaceAttributeName) === "";
 			let textContent = node.textContent;
-			while (noWhitespaceCollapse(element)) {
+			let noWhitespace = !spacePreserved && noWhitespaceCollapse(element);
+			while (noWhitespace) {
 				element = element.parentElement;
+				noWhitespace = element && noWhitespaceCollapse(element);
 			}
-			if ((!element || noWhitespaceCollapse(element)) && textContent.match(/\s+/) && textContent.length > 1) {
+			if ((!element || noWhitespace) && textContent.match(/\s+/) && textContent.length > 1) {
 				let lastTextContent;
 				while (lastTextContent != textContent) {
 					lastTextContent = textContent;

+ 1 - 1
lib/single-file/single-file-browser.js

@@ -103,7 +103,7 @@ this.SingleFile = this.SingleFile || (() => {
 					imageSelectors: lazyLoader.imageSelectors
 				},
 				htmlmini: {
-					process: doc => htmlmini.process(doc),
+					process: (doc, options) => htmlmini.process(doc, options),
 					postProcess: doc => htmlmini.postProcess(doc),
 				},
 				rulesMinifier: doc => rulesMinifier.process(doc)

+ 4 - 1
lib/single-file/single-file-core.js

@@ -23,6 +23,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 	const SELECTED_CONTENT_ATTRIBUTE_NAME = "data-single-file-selected-content";
 	const SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME = "data-single-file-selected-content-root";
 	const REMOVED_CONTENT_ATTRIBUTE_NAME = "data-single-file-removed-content";
+	const PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME = "data-single-file-preserved-space-element";
 
 	let Download, DOM, URL;
 
@@ -34,6 +35,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 				this.SELECTED_CONTENT_ATTRIBUTE_NAME = SELECTED_CONTENT_ATTRIBUTE_NAME;
 				this.REMOVED_CONTENT_ATTRIBUTE_NAME = REMOVED_CONTENT_ATTRIBUTE_NAME;
 				this.SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME = SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME;
+				this.PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME = PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME;
 			}
 			async initialize() {
 				this.processor = new PageProcessor(this.options);
@@ -346,7 +348,8 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			if (postProcess) {
 				this.dom.htmlmini.postProcess(this.doc);
 			} else {
-				this.dom.htmlmini.process(this.doc);
+				this.dom.htmlmini.process(this.doc, { preservedSpaceAttributeName: PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME });
+				this.doc.querySelectorAll("[" + PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME));
 			}
 		}