Prechádzať zdrojové kódy

added "save to clipboard" option (fix #163)

Gildas 6 rokov pred
rodič
commit
40ddf81481

+ 4 - 0
_locales/de/messages.json

@@ -351,6 +351,10 @@
         "message": "Originalseite sichern",
         "description": "Options page label: 'save raw page'"
     },
+    "optionSaveToClipboard": {
+        "message": "In die Zwischenablage speichern",
+        "description": "Options page label: 'save to clipboard'"
+    },
     "optionsHelpLink": {
         "message": "Hilfe",
         "description": "Options help link"

+ 4 - 0
_locales/en/messages.json

@@ -351,6 +351,10 @@
         "message": "save raw page",
         "description": "Options page label: 'save raw page'"
     },
+    "optionSaveToClipboard": {
+        "message": "save to clipboard",
+        "description": "Options page label: 'save to clipboard'"
+    },
     "optionsHelpLink": {
         "message": "help",
         "description": "Options help link"

+ 4 - 0
_locales/fr/messages.json

@@ -351,6 +351,10 @@
         "message": "sauvegarder la page brute",
         "description": "Options page label: 'save raw page'"
     },
+    "optionSaveToClipboard": {
+        "message": "enregistrer dans le presse-papiers",
+        "description": "Options page label: 'save to clipboard'"
+    },
     "optionsHelpLink": {
         "message": "aide (anglais)",
         "description": "Options help link"

+ 4 - 0
_locales/ja/messages.json

@@ -351,6 +351,10 @@
         "message": "生のページを保存",
         "description": "オプションのページラベル: '生のページを保存'"
     },
+    "optionSaveToClipboard": {
+        "message": "save to clipboard",
+        "description": "Options page label: 'save to clipboard'"
+    },
     "optionsHelpLink": {
         "message": "ヘルプ",
         "description": "オプションの「ヘルプ」リンク"

+ 4 - 0
_locales/pl/messages.json

@@ -351,6 +351,10 @@
         "message": "zapisuj surową stronę",
         "description": "Options page label: 'save raw page'"
     },
+    "optionSaveToClipboard": {
+        "message": "save to clipboard",
+        "description": "Options page label: 'save to clipboard'"
+    },
     "optionsHelpLink": {
         "message": "pomoc (w języku angielskim)",
         "description": "Options help link"

+ 4 - 0
_locales/ru/messages.json

@@ -351,6 +351,10 @@
         "message": "сохранить исходную страницу",
         "description": "Options page label: 'save raw page'"
     },
+    "optionSaveToClipboard": {
+        "message": "save to clipboard",
+        "description": "Options page label: 'save to clipboard'"
+    },
     "optionsHelpLink": {
         "message": "помощь",
         "description": "Options help link"

+ 4 - 0
_locales/zh_CN/messages.json

@@ -351,6 +351,10 @@
         "message": "保存原始页面",
         "description": "选项页标签: '保存原始页面'"
     },
+    "optionSaveToClipboard": {
+        "message": "save to clipboard",
+        "description": "Options page label: 'save to clipboard'"
+    },
     "optionsHelpLink": {
         "message": "帮助",
         "description": "选项页帮助链接"

+ 2 - 1
extension/core/bg/data/config.js

@@ -61,7 +61,8 @@ singlefile.config = (() => {
 		removeAlternativeMedias: true,
 		removeAlternativeImages: true,
 		groupDuplicateImages: true,
-		saveRawPage: false
+		saveRawPage: false,
+		saveToClipboard: false
 	};
 
 	let pendingUpgradePromise = upgrade();

+ 38 - 17
extension/core/bg/download.js

@@ -18,7 +18,7 @@
  *   along with SingleFile.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* global browser, singlefile, Blob, URL */
+/* global browser, singlefile, Blob, URL, document */
 
 singlefile.download = (() => {
 
@@ -39,30 +39,38 @@ singlefile.download = (() => {
 			partialContent.push(message.content);
 			if (message.finished) {
 				partialContents.delete(sender.tab.id);
-				message.url = URL.createObjectURL(new Blob(partialContent, { type: "text/html" }));
+				if (message.saveToClipboard) {
+					message.content = partialContent.join("");
+				} else {
+					message.url = URL.createObjectURL(new Blob(partialContent, { type: "text/html" }));
+				}
 			} else {
 				return Promise.resolve({});
 			}
-		} else if (message.content) {
+		} else if (message.content && !message.saveToClipboard) {
 			message.url = URL.createObjectURL(new Blob([message.content], { type: "text/html" }));
 		}
-		return downloadPage(message, { confirmFilename: message.confirmFilename, incognito: sender.tab.incognito, filenameConflictAction: message.filenameConflictAction })
-			.catch(error => {
-				if (error.message) {
-					if (error.message.includes("'incognito'")) {
-						return downloadPage(message, { confirmFilename: message.confirmFilename, filenameConflictAction: message.filenameConflictAction });
-					} else if (error.message == "conflictAction prompt not yet implemented") {
-						return downloadPage(message, { confirmFilename: message.confirmFilename });
-					} else if (error.message.includes("illegal characters")) {
-						message.filename = message.filename.replace(/,/g, "_");
-						return downloadPage(message, { confirmFilename: message.confirmFilename, incognito: sender.tab.incognito, filenameConflictAction: message.filenameConflictAction });
+		if (message.saveToClipboard) {
+			saveToClipboard(message);
+		} else {
+			return downloadPage(message, { confirmFilename: message.confirmFilename, incognito: sender.tab.incognito, filenameConflictAction: message.filenameConflictAction })
+				.catch(error => {
+					if (error.message) {
+						if (error.message.includes("'incognito'")) {
+							return downloadPage(message, { confirmFilename: message.confirmFilename, filenameConflictAction: message.filenameConflictAction });
+						} else if (error.message == "conflictAction prompt not yet implemented") {
+							return downloadPage(message, { confirmFilename: message.confirmFilename });
+						} else if (error.message.includes("illegal characters")) {
+							message.filename = message.filename.replace(/,/g, "_");
+							return downloadPage(message, { confirmFilename: message.confirmFilename, incognito: sender.tab.incognito, filenameConflictAction: message.filenameConflictAction });
+						} else {
+							throw error;
+						}
 					} else {
 						throw error;
 					}
-				} else {
-					throw error;
-				}
-			});
+				});
+		}
 	}
 
 	async function downloadPage(page, options) {
@@ -107,4 +115,17 @@ singlefile.download = (() => {
 		});
 	}
 
+	function saveToClipboard(page) {
+		const command = "copy";
+		document.addEventListener(command, listener);
+		document.execCommand(command);
+		document.removeEventListener(command, listener);
+
+		function listener(event) {
+			event.clipboardData.setData("text/html", page.content);
+			event.clipboardData.setData("text/plain", page.content);
+			event.preventDefault();
+		}
+	}
+
 })();

+ 1 - 1
extension/core/content/content.js

@@ -168,7 +168,7 @@ this.singlefile.top = this.singlefile.top || (() => {
 		if (options.backgroundSave) {
 			let response;
 			for (let blockIndex = 0; !response && (blockIndex * MAX_CONTENT_SIZE < page.content.length); blockIndex++) {
-				const message = { download: true, confirmFilename: options.confirmFilename, filenameConflictAction: options.filenameConflictAction, filename: page.filename };
+				const message = { download: true, confirmFilename: options.confirmFilename, filenameConflictAction: options.filenameConflictAction, filename: page.filename, saveToClipboard: options.saveToClipboard };
 				message.truncated = page.content.length > MAX_CONTENT_SIZE;
 				if (message.truncated) {
 					message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE > page.content.length;

+ 8 - 0
extension/ui/bg/ui-options.js

@@ -30,6 +30,7 @@
 	const removeImportsLabel = document.getElementById("removeImportsLabel");
 	const removeScriptsLabel = document.getElementById("removeScriptsLabel");
 	const saveRawPageLabel = document.getElementById("saveRawPageLabel");
+	const saveToClipboardLabel = document.getElementById("saveToClipboardLabel");
 	const compressHTMLLabel = document.getElementById("compressHTMLLabel");
 	const compressCSSLabel = document.getElementById("compressCSSLabel");
 	const loadDeferredImagesLabel = document.getElementById("loadDeferredImagesLabel");
@@ -93,6 +94,7 @@
 	const removeImportsInput = document.getElementById("removeImportsInput");
 	const removeScriptsInput = document.getElementById("removeScriptsInput");
 	const saveRawPageInput = document.getElementById("saveRawPageInput");
+	const saveToClipboardInput = document.getElementById("saveToClipboardInput");
 	const compressHTMLInput = document.getElementById("compressHTMLInput");
 	const compressCSSInput = document.getElementById("compressCSSInput");
 	const loadDeferredImagesInput = document.getElementById("loadDeferredImagesInput");
@@ -356,6 +358,7 @@
 	removeImportsLabel.textContent = browser.i18n.getMessage("optionRemoveImports");
 	removeScriptsLabel.textContent = browser.i18n.getMessage("optionRemoveScripts");
 	saveRawPageLabel.textContent = browser.i18n.getMessage("optionSaveRawPage");
+	saveToClipboardLabel.textContent = browser.i18n.getMessage("optionSaveToClipboard");
 	compressHTMLLabel.textContent = browser.i18n.getMessage("optionCompressHTML");
 	compressCSSLabel.textContent = browser.i18n.getMessage("optionCompressCSS");
 	loadDeferredImagesLabel.textContent = browser.i18n.getMessage("optionLoadDeferredImages");
@@ -530,6 +533,7 @@
 		removeImportsInput.checked = profileOptions.removeImports;
 		removeScriptsInput.checked = profileOptions.removeScripts;
 		saveRawPageInput.checked = profileOptions.saveRawPage;
+		saveToClipboardInput.checked = profileOptions.saveToClipboard;
 		compressHTMLInput.checked = profileOptions.compressHTML;
 		compressCSSInput.checked = profileOptions.compressCSS;
 		loadDeferredImagesInput.checked = profileOptions.loadDeferredImages;
@@ -537,12 +541,15 @@
 		loadDeferredImagesMaxIdleTimeInput.disabled = !profileOptions.loadDeferredImages;
 		contextMenuEnabledInput.checked = profileOptions.contextMenuEnabled;
 		filenameTemplateInput.value = profileOptions.filenameTemplate;
+		filenameTemplateInput.disabled = profileOptions.saveToClipboard;
 		shadowEnabledInput.checked = profileOptions.shadowEnabled;
 		maxResourceSizeEnabledInput.checked = profileOptions.maxResourceSizeEnabled;
 		maxResourceSizeInput.value = profileOptions.maxResourceSize;
 		maxResourceSizeInput.disabled = !profileOptions.maxResourceSizeEnabled;
 		confirmFilenameInput.checked = profileOptions.confirmFilename;
+		confirmFilenameInput.disabled = profileOptions.saveToClipboard;
 		filenameConflictActionInput.value = profileOptions.filenameConflictAction;
+		filenameConflictActionInput.disabled = profileOptions.saveToClipboard;
 		removeAudioSrcInput.checked = profileOptions.removeAudioSrc;
 		removeVideoSrcInput.checked = profileOptions.removeVideoSrc;
 		displayInfobarInput.checked = profileOptions.displayInfobar;
@@ -591,6 +598,7 @@
 				removeImports: removeImportsInput.checked,
 				removeScripts: removeScriptsInput.checked,
 				saveRawPage: saveRawPageInput.checked,
+				saveToClipboard: saveToClipboardInput.checked,
 				compressHTML: compressHTMLInput.checked,
 				compressCSS: compressCSSInput.checked,
 				loadDeferredImages: loadDeferredImagesInput.checked,

+ 8 - 1
extension/ui/pages/help.html

@@ -382,7 +382,14 @@
 					<li>
 						<span class="option">Option: save raw page</span>
 						<p>Check this option to save the page without interpreting JavaScript. Checking this option may alter the
-							document, and force the options "remove frames" to be enabled and "load deferred images" to be disabled.</p>
+							document, and will force the options "remove frames" to be enabled and "load deferred images" to be disabled.</p>
+						<p class="notice">It is recommended to <u>uncheck</u> this option</p>
+					</li>
+
+					<li>
+						<span class="option">Option: save to clipboard</span>
+						<p>Check this option to save the page to the clipboard instead of downloading it. Checking this option may alter will 
+							force the "file name" options to be disabled.</p>
 						<p class="notice">It is recommended to <u>uncheck</u> this option</p>
 					</li>
 				</ul>

+ 4 - 0
extension/ui/pages/options.html

@@ -194,6 +194,10 @@
 				<label for="saveRawPageInput" id="saveRawPageLabel"></label>
 				<input type="checkbox" id="saveRawPageInput">
 			</div>
+			<div class="option">
+				<label for="saveToClipboardInput" id="saveToClipboardLabel"></label>
+				<input type="checkbox" id="saveToClipboardInput">
+			</div>
 		</details>
 		<details>
 			<summary id="autoSettingsLabel"></summary>

+ 1 - 0
manifest.json

@@ -121,6 +121,7 @@
 	},
 	"permissions": [
 		"menus",
+		"clipboardWrite",
 		"contextMenus",
 		"downloads",
 		"storage",