Răsfoiți Sursa

improved save selection

Gildas 7 ani în urmă
părinte
comite
c821eae746
2 a modificat fișierele cu 49 adăugiri și 29 ștergeri
  1. 17 13
      extension/core/content/content.js
  2. 32 16
      lib/single-file/single-file-core.js

+ 17 - 13
extension/core/content/content.js

@@ -18,7 +18,7 @@
  *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* global browser, SingleFile, singlefile, FrameTree, document, Blob, MouseEvent, getSelection, getComputedStyle, prompt, addEventListener */
+/* global browser, SingleFile, singlefile, FrameTree, document, Blob, MouseEvent, getSelection, getComputedStyle, prompt, addEventListener, Node */
 
 this.singlefile.top = this.singlefile.top || (() => {
 
@@ -58,7 +58,7 @@ this.singlefile.top = this.singlefile.top || (() => {
 		fixInlineScripts();
 		fixHeadNoScripts();
 		if (options.selected) {
-			selectSelectedContent(processor.SELECTED_CONTENT_ATTRIBUTE_NAME);
+			selectSelectedContent(processor.SELECTED_CONTENT_ATTRIBUTE_NAME, processor.SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME);
 		}
 		if (!options.removeFrames) {
 			hideHeadFrames();
@@ -78,7 +78,7 @@ this.singlefile.top = this.singlefile.top || (() => {
 		await processor.preparePageData();
 		const page = processor.getPageData();
 		if (options.selected) {
-			unselectSelectedContent(processor.SELECTED_CONTENT_ATTRIBUTE_NAME);
+			unselectSelectedContent(processor.SELECTED_CONTENT_ATTRIBUTE_NAME, processor.SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME);
 		}
 		const date = new Date();
 		page.filename = page.title + (options.appendSaveDate ? " (" + date.toISOString().split("T")[0] + " " + date.toLocaleTimeString() + ")" : "") + ".html";
@@ -118,23 +118,27 @@ this.singlefile.top = this.singlefile.top || (() => {
 		document.querySelectorAll("[" + REMOVED_CONTENT_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME));
 	}
 
-	function selectSelectedContent(SELECTED_CONTENT_ATTRIBUTE_NAME) {
+	function selectSelectedContent(SELECTED_CONTENT_ATTRIBUTE_NAME, SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME) {
 		const selection = getSelection();
 		const range = selection.rangeCount ? selection.getRangeAt(0) : null;
-		let node;
-		if (range) {
-			node = range.commonAncestorContainer;
-			if (node.nodeType != node.ELEMENT_NODE) {
-				node = node.parentElement;
+		const treeWalker = document.createTreeWalker(range.commonAncestorContainer);
+		let selectionFound = false;
+		const ancestorElement = range.commonAncestorContainer != Node.ELEMENT_NODE ? range.commonAncestorContainer.parentElement : range.commonAncestorContainer;
+		ancestorElement.setAttribute(SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME, "");
+		while (treeWalker.nextNode() && treeWalker.currentNode != range.endContainer) {
+			if (treeWalker.currentNode == range.startContainer) {
+				selectionFound = true;
+			}
+			if (selectionFound) {
+				const element = treeWalker.currentNode.nodeType == Node.ELEMENT_NODE ? treeWalker.currentNode : treeWalker.currentNode.parentElement;
+				element.setAttribute(SELECTED_CONTENT_ATTRIBUTE_NAME, "");
 			}
-		}
-		if (node) {
-			node.setAttribute(SELECTED_CONTENT_ATTRIBUTE_NAME, "");
 		}
 	}
 
-	function unselectSelectedContent(SELECTED_CONTENT_ATTRIBUTE_NAME) {
+	function unselectSelectedContent(SELECTED_CONTENT_ATTRIBUTE_NAME, SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME) {
 		document.querySelectorAll("[" + SELECTED_CONTENT_ATTRIBUTE_NAME + "]").forEach(selectedContent => selectedContent.removeAttribute(SELECTED_CONTENT_ATTRIBUTE_NAME));
+		document.querySelectorAll("[" + SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME + "]").forEach(selectedContent => selectedContent.removeAttribute(SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME));
 	}
 
 	async function getOptions(options) {

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

@@ -21,6 +21,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";
 
 	let Download, DOM, URL;
@@ -32,6 +33,7 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 				this.options = options;
 				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;
 			}
 			async initialize() {
 				this.processor = new PageProcessor(this.options);
@@ -247,10 +249,11 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 
 		getPageData() {
 			if (this.options.selected) {
-				const selectedElement = this.doc.querySelector("[" + SELECTED_CONTENT_ATTRIBUTE_NAME + "]");
-				if (selectedElement) {
-					DomProcessorHelper.isolateElement(selectedElement.parentElement, selectedElement);
-					selectedElement.removeAttribute(SELECTED_CONTENT_ATTRIBUTE_NAME);
+				const rootElement = this.doc.querySelector("[" + SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME + "]");
+				if (rootElement) {
+					DomProcessorHelper.isolateElements(rootElement);
+					rootElement.removeAttribute(SELECTED_CONTENT_ROOT_ATTRIBUTE_NAME);
+					rootElement.removeAttribute(SELECTED_CONTENT_ATTRIBUTE_NAME);
 				}
 			}
 			const titleElement = this.doc.querySelector("title");
@@ -530,20 +533,33 @@ this.SingleFileCore = this.SingleFileCore || (() => {
 			}
 		}
 
-		static isolateElement(parentElement, element) {
-			Array.from(parentElement.childNodes).forEach(node => {
-				if (node == element) {
-					node.removeAttribute("style");
-					node.style.all = "unset";
-				} else {
-					if (node.tagName != "HEAD" && node.tagName != "STYLE") {
-						node.remove();
-					}
+		static isolateElements(rootElement) {
+			rootElement.querySelectorAll("*").forEach(element => {
+				if (element.getAttribute(SELECTED_CONTENT_ATTRIBUTE_NAME) === "") {
+					element.removeAttribute(SELECTED_CONTENT_ATTRIBUTE_NAME);
+				} else if (!element.querySelector("[" + SELECTED_CONTENT_ATTRIBUTE_NAME + "]")) {
+					element.remove();
 				}
 			});
-			element = element.parentElement;
-			if (element.parentElement) {
-				DomProcessorHelper.isolateElement(element.parentElement, element);
+			isolateParentElements(rootElement.parentElement, rootElement);
+
+			function isolateParentElements(parentElement, element) {
+				if (parentElement) {
+					Array.from(parentElement.childNodes).forEach(node => {
+						if (node == element) {
+							node.removeAttribute("style");
+							node.style.all = "unset";
+						} else {
+							if (node.tagName != "HEAD" && node.tagName != "STYLE") {
+								node.remove();
+							}
+						}
+					});
+				}
+				element = element.parentElement;
+				if (element && element.parentElement) {
+					isolateParentElements(element.parentElement, element);
+				}
 			}
 		}