/* * Copyright 2010-2020 Gildas Lormeau * contact : gildas.lormeau gmail.com * * This file is part of SingleFile. * * The code in this file is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * (GNU AGPL) as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * The code in this file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero * General Public License for more details. * * As additional permission under GNU AGPL version 3 section 7, you may * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU * AGPL normally required by section 4, provided you include this license * notice and a URL through which recipients can access the Corresponding * Source. */ /* global browser, window, document, localStorage, FileReader, location, fetch, TextDecoder, DOMParser, HTMLElement */ (async () => { const HELP_ICON_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABIUlEQVQ4y+2TsarCMBSGvxTBRdqiUZAWOrhJB9EXcPKFfCvfQYfulUKHDqXg4CYUJSioYO4mSDX3ttzt3n87fMlHTpIjlsulxpDZbEYYhgghSNOUOI5Ny2mZYBAELBYLer0eAJ7ncTweKYri4x7LJJRS0u12n7XrukgpjSc0CpVSXK/XZ32/31FKNW85z3PW6zXT6RSAJEnIsqy5UGvNZrNhu90CcDqd+C6tT6J+v//2Th+PB2VZ1hN2Oh3G4zGTyQTbtl/YbrdjtVpxu91+Ljyfz0RRhG3bzOfzF+Y4TvNXvlwuaK2pE4tfzr/wzwsty0IIURlL0998KxRCMBqN8H2/wlzXJQxD2u12vVkeDoeUZUkURRU+GAw4HA7s9/sK+wK6CWHasQ/S/wAAAABJRU5ErkJggg=="; const HELP_PAGE_PATH = "/extension/ui/pages/help.html"; const { DEFAULT_PROFILE_NAME, DISABLED_PROFILE_NAME, CURRENT_PROFILE_NAME } = await browser.runtime.sendMessage({ method: "config.getConstants" }); const removeHiddenElementsLabel = document.getElementById("removeHiddenElementsLabel"); const removeUnusedStylesLabel = document.getElementById("removeUnusedStylesLabel"); const removeUnusedFontsLabel = document.getElementById("removeUnusedFontsLabel"); const removeFramesLabel = document.getElementById("removeFramesLabel"); const removeImportsLabel = document.getElementById("removeImportsLabel"); const removeScriptsLabel = document.getElementById("removeScriptsLabel"); const saveRawPageLabel = document.getElementById("saveRawPageLabel"); const saveToClipboardLabel = document.getElementById("saveToClipboardLabel"); const saveToFilesystemLabel = document.getElementById("saveToFilesystemLabel"); const addProofLabel = document.getElementById("addProofLabel"); const saveToGDriveLabel = document.getElementById("saveToGDriveLabel"); const compressHTMLLabel = document.getElementById("compressHTMLLabel"); const compressCSSLabel = document.getElementById("compressCSSLabel"); const loadDeferredImagesLabel = document.getElementById("loadDeferredImagesLabel"); const loadDeferredImagesMaxIdleTimeLabel = document.getElementById("loadDeferredImagesMaxIdleTimeLabel"); const loadDeferredImagesKeepZoomLevelLabel = document.getElementById("loadDeferredImagesKeepZoomLevelLabel"); const addMenuEntryLabel = document.getElementById("addMenuEntryLabel"); const filenameTemplateLabel = document.getElementById("filenameTemplateLabel"); const filenameMaxLengthLabel = document.getElementById("filenameMaxLengthLabel"); const shadowEnabledLabel = document.getElementById("shadowEnabledLabel"); const setMaxResourceSizeLabel = document.getElementById("setMaxResourceSizeLabel"); const maxResourceSizeLabel = document.getElementById("maxResourceSizeLabel"); const confirmFilenameLabel = document.getElementById("confirmFilenameLabel"); const filenameConflictActionLabel = document.getElementById("filenameConflictActionLabel"); const filenameConflictActionUniquifyLabel = document.getElementById("filenameConflictActionUniquifyLabel"); const filenameConflictActionOverwriteLabel = document.getElementById("filenameConflictActionOverwriteLabel"); const filenameConflictActionPromptLabel = document.getElementById("filenameConflictActionPromptLabel"); const filenameConflictActionSkipLabel = document.getElementById("filenameConflictActionSkipLabel"); const removeAudioLabel = document.getElementById("removeAudioLabel"); const removeVideoLabel = document.getElementById("removeVideoLabel"); const displayInfobarLabel = document.getElementById("displayInfobarLabel"); const displayStatsLabel = document.getElementById("displayStatsLabel"); const backgroundSaveLabel = document.getElementById("backgroundSaveLabel"); const autoSaveDelayLabel = document.getElementById("autoSaveDelayLabel"); const autoSaveLoadLabel = document.getElementById("autoSaveLoadLabel"); const autoSaveUnloadLabel = document.getElementById("autoSaveUnloadLabel"); const autoSaveLoadOrUnloadLabel = document.getElementById("autoSaveLoadOrUnloadLabel"); const autoSaveRepeatLabel = document.getElementById("autoSaveRepeatLabel"); const autoSaveRepeatDelayLabel = document.getElementById("autoSaveRepeatDelayLabel"); const autoSaveExternalSaveLabel = document.getElementById("autoSaveExternalSaveLabel"); const removeAlternativeFontsLabel = document.getElementById("removeAlternativeFontsLabel"); const removeAlternativeImagesLabel = document.getElementById("removeAlternativeImagesLabel"); const removeAlternativeMediasLabel = document.getElementById("removeAlternativeMediasLabel"); const saveCreatedBookmarksLabel = document.getElementById("saveCreatedBookmarksLabel"); const passReferrerOnErrorLabel = document.getElementById("passReferrerOnErrorLabel"); const replaceBookmarkURLLabel = document.getElementById("replaceBookmarkURLLabel"); const ignoredBookmarkFoldersLabel = document.getElementById("ignoredBookmarkFoldersLabel"); const titleLabel = document.getElementById("titleLabel"); const userInterfaceLabel = document.getElementById("userInterfaceLabel"); const filenameLabel = document.getElementById("filenameLabel"); const htmlContentLabel = document.getElementById("htmlContentLabel"); const imagesLabel = document.getElementById("imagesLabel"); const stylesheetsLabel = document.getElementById("stylesheetsLabel"); const fontsLabel = document.getElementById("fontsLabel"); const otherResourcesLabel = document.getElementById("otherResourcesLabel"); const destinationLabel = document.getElementById("destinationLabel"); const bookmarksLabel = document.getElementById("bookmarksLabel"); const autoSaveLabel = document.getElementById("autoSaveLabel"); const autoSettingsLabel = document.getElementById("autoSettingsLabel"); const autoSettingsUrlLabel = document.getElementById("autoSettingsUrlLabel"); const autoSettingsProfileLabel = document.getElementById("autoSettingsProfileLabel"); const autoSettingsAutoSaveProfileLabel = document.getElementById("autoSettingsAutoSaveProfileLabel"); const showAllProfilesLabel = document.getElementById("showAllProfilesLabel"); const showAutoSaveProfileLabel = document.getElementById("showAutoSaveProfileLabel"); const groupDuplicateImagesLabel = document.getElementById("groupDuplicateImagesLabel"); const confirmInfobarLabel = document.getElementById("confirmInfobarLabel"); const autoCloseLabel = document.getElementById("autoCloseLabel"); const editorLabel = document.getElementById("editorLabel"); const openEditorLabel = document.getElementById("openEditorLabel"); const autoOpenEditorLabel = document.getElementById("autoOpenEditorLabel"); const defaultEditorModeLabel = document.getElementById("defaultEditorModeLabel"); const applySystemThemeLabel = document.getElementById("applySystemThemeLabel"); const warnUnsavedPageLabel = document.getElementById("warnUnsavedPageLabel"); const infobarTemplateLabel = document.getElementById("infobarTemplateLabel"); const includeInfobarLabel = document.getElementById("includeInfobarLabel"); const miscLabel = document.getElementById("miscLabel"); const helpLabel = document.getElementById("helpLabel"); const synchronizeLabel = document.getElementById("synchronizeLabel"); const addProfileButton = document.getElementById("addProfileButton"); const deleteProfileButton = document.getElementById("deleteProfileButton"); const renameProfileButton = document.getElementById("renameProfileButton"); const resetButton = document.getElementById("resetButton"); const exportButton = document.getElementById("exportButton"); const importButton = document.getElementById("importButton"); const fileInput = document.getElementById("fileInput"); const profileNamesInput = document.getElementById("profileNamesInput"); const removeHiddenElementsInput = document.getElementById("removeHiddenElementsInput"); const removeUnusedStylesInput = document.getElementById("removeUnusedStylesInput"); const removeUnusedFontsInput = document.getElementById("removeUnusedFontsInput"); const removeFramesInput = document.getElementById("removeFramesInput"); const removeImportsInput = document.getElementById("removeImportsInput"); const removeScriptsInput = document.getElementById("removeScriptsInput"); const saveRawPageInput = document.getElementById("saveRawPageInput"); const saveToClipboardInput = document.getElementById("saveToClipboardInput"); const addProofInput = document.getElementById("addProofInput"); const saveToGDriveInput = document.getElementById("saveToGDriveInput"); const saveToFilesystemInput = document.getElementById("saveToFilesystemInput"); const compressHTMLInput = document.getElementById("compressHTMLInput"); const compressCSSInput = document.getElementById("compressCSSInput"); const loadDeferredImagesInput = document.getElementById("loadDeferredImagesInput"); const loadDeferredImagesMaxIdleTimeInput = document.getElementById("loadDeferredImagesMaxIdleTimeInput"); const loadDeferredImagesKeepZoomLevelInput = document.getElementById("loadDeferredImagesKeepZoomLevelInput"); const contextMenuEnabledInput = document.getElementById("contextMenuEnabledInput"); const filenameTemplateInput = document.getElementById("filenameTemplateInput"); const filenameMaxLengthInput = document.getElementById("filenameMaxLengthInput"); const shadowEnabledInput = document.getElementById("shadowEnabledInput"); const maxResourceSizeInput = document.getElementById("maxResourceSizeInput"); const maxResourceSizeEnabledInput = document.getElementById("maxResourceSizeEnabledInput"); const confirmFilenameInput = document.getElementById("confirmFilenameInput"); const filenameConflictActionInput = document.getElementById("filenameConflictActionInput"); const removeAudioSrcInput = document.getElementById("removeAudioSrcInput"); const removeVideoSrcInput = document.getElementById("removeVideoSrcInput"); const displayInfobarInput = document.getElementById("displayInfobarInput"); const displayStatsInput = document.getElementById("displayStatsInput"); const backgroundSaveInput = document.getElementById("backgroundSaveInput"); const autoSaveDelayInput = document.getElementById("autoSaveDelayInput"); const autoSaveLoadInput = document.getElementById("autoSaveLoadInput"); const autoSaveUnloadInput = document.getElementById("autoSaveUnloadInput"); const autoSaveLoadOrUnloadInput = document.getElementById("autoSaveLoadOrUnloadInput"); const autoSaveRepeatInput = document.getElementById("autoSaveRepeatInput"); const autoSaveRepeatDelayInput = document.getElementById("autoSaveRepeatDelayInput"); const autoSaveExternalSaveInput = document.getElementById("autoSaveExternalSaveInput"); const removeAlternativeFontsInput = document.getElementById("removeAlternativeFontsInput"); const removeAlternativeImagesInput = document.getElementById("removeAlternativeImagesInput"); const removeAlternativeMediasInput = document.getElementById("removeAlternativeMediasInput"); const saveCreatedBookmarksInput = document.getElementById("saveCreatedBookmarksInput"); const passReferrerOnErrorInput = document.getElementById("passReferrerOnErrorInput"); const replaceBookmarkURLInput = document.getElementById("replaceBookmarkURLInput"); const ignoredBookmarkFoldersInput = document.getElementById("ignoredBookmarkFoldersInput"); const groupDuplicateImagesInput = document.getElementById("groupDuplicateImagesInput"); const infobarTemplateInput = document.getElementById("infobarTemplateInput"); const includeInfobarInput = document.getElementById("includeInfobarInput"); const confirmInfobarInput = document.getElementById("confirmInfobarInput"); const autoCloseInput = document.getElementById("autoCloseInput"); const openEditorInput = document.getElementById("openEditorInput"); const autoOpenEditorInput = document.getElementById("autoOpenEditorInput"); const defaultEditorModeInput = document.getElementById("defaultEditorModeInput"); const defaultEditorModeNormalLabel = document.getElementById("defaultEditorModeNormalLabel"); const defaultEditorModeEditLabel = document.getElementById("defaultEditorModeEditLabel"); const defaultEditorModeFormatLabel = document.getElementById("defaultEditorModeFormatLabel"); const defaultEditorModeCutLabel = document.getElementById("defaultEditorModeCutLabel"); const defaultEditorModeCutExternalLabel = document.getElementById("defaultEditorModeCutExternalLabel"); const applySystemThemeInput = document.getElementById("applySystemThemeInput"); const warnUnsavedPageInput = document.getElementById("warnUnsavedPageInput"); const expandAllButton = document.getElementById("expandAllButton"); const rulesDeleteAllButton = document.getElementById("rulesDeleteAllButton"); const ruleUrlInput = document.getElementById("ruleUrlInput"); const ruleProfileInput = document.getElementById("ruleProfileInput"); const ruleAutoSaveProfileInput = document.getElementById("ruleAutoSaveProfileInput"); const ruleEditProfileInput = document.getElementById("ruleEditProfileInput"); const ruleEditAutoSaveProfileInput = document.getElementById("ruleEditAutoSaveProfileInput"); const ruleAddButton = document.getElementById("ruleAddButton"); const rulesElement = document.querySelector(".rules-table"); const rulesContainerElement = document.querySelector(".rules-table-container"); const ruleEditUrlInput = document.getElementById("ruleEditUrlInput"); const ruleEditButton = document.getElementById("ruleEditButton"); const createURLElement = rulesElement.querySelector(".rule-create"); const showAllProfilesInput = document.getElementById("showAllProfilesInput"); const showAutoSaveProfileInput = document.getElementById("showAutoSaveProfileInput"); const synchronizeInput = document.getElementById("synchronizeInput"); const resetAllButton = document.getElementById("resetAllButton"); const resetCurrentButton = document.getElementById("resetCurrentButton"); const resetCancelButton = document.getElementById("resetCancelButton"); const confirmButton = document.getElementById("confirmButton"); const cancelButton = document.getElementById("cancelButton"); const promptInput = document.getElementById("promptInput"); const promptCancelButton = document.getElementById("promptCancelButton"); const promptConfirmButton = document.getElementById("promptConfirmButton"); let sidePanelDisplay; if (location.href.endsWith("#side-panel")) { sidePanelDisplay = true; document.querySelector(".options-title").remove(); document.documentElement.classList.add("side-panel"); } browser.runtime.onMessage.addListener(message => { if (message.method == "options.refresh" || (message.method == "options.refreshPanel" && sidePanelDisplay)) { refresh(message.profileName); } }); let pendingSave = Promise.resolve(); let autoSaveProfileChanged; ruleProfileInput.onchange = () => { if (!autoSaveProfileChanged && ruleProfileInput.value != CURRENT_PROFILE_NAME) { ruleAutoSaveProfileInput.value = ruleProfileInput.value; } }; ruleAutoSaveProfileInput.onchange = () => { autoSaveProfileChanged = true; }; rulesDeleteAllButton.addEventListener("click", async event => { if (await confirm(browser.i18n.getMessage("optionsDeleteDisplayedRulesConfirm"), event.clientY - 100)) { await browser.runtime.sendMessage({ method: "config.deleteRules", profileName: !showAllProfilesInput.checked && profileNamesInput.value }); await refresh(); await refreshExternalComponents(); } }, false); createURLElement.onsubmit = async event => { event.preventDefault(); try { await browser.runtime.sendMessage({ method: "config.addRule", url: ruleUrlInput.value, profileName: ruleProfileInput.value, autoSaveProfileName: ruleAutoSaveProfileInput.value }); } catch (error) { // ignored } ruleUrlInput.value = ""; ruleProfileInput.value = ruleAutoSaveProfileInput.value = DEFAULT_PROFILE_NAME; autoSaveProfileChanged = false; await refresh(); await refreshExternalComponents(); ruleUrlInput.focus(); }; ruleUrlInput.onclick = ruleUrlInput.onkeyup = ruleUrlInput.onchange = async () => { ruleAddButton.disabled = !ruleUrlInput.value; const rules = await browser.runtime.sendMessage({ method: "config.getRules" }); if (rules.find(rule => rule.url == ruleUrlInput.value)) { ruleAddButton.disabled = true; } }; ruleEditUrlInput.onclick = ruleEditUrlInput.onkeyup = ruleEditUrlInput.onchange = async () => { ruleEditButton.disabled = !ruleEditUrlInput.value; const rules = await browser.runtime.sendMessage({ method: "config.getRules" }); if (rules.find(rule => rule.url == ruleEditUrlInput.value)) { ruleEditButton.disabled = true; } }; if (getLocalStorageItem("optionShowAutoSaveProfile")) { showAutoSaveProfileInput.checked = true; rulesContainerElement.classList.remove("compact"); } showAutoSaveProfileInput.addEventListener("click", () => { if (showAutoSaveProfileInput.checked) { setLocalStorageItem("optionShowAutoSaveProfile", 1); rulesContainerElement.classList.remove("compact"); } else { removeLocalStorageItem("optionShowAutoSaveProfile"); rulesContainerElement.classList.add("compact"); } }, false); if (getLocalStorageItem("optionShowAllProfiles")) { showAllProfilesInput.checked = true; } showAllProfilesInput.addEventListener("click", () => { if (showAllProfilesInput.checked) { setLocalStorageItem("optionShowAllProfiles", 1); } else { removeLocalStorageItem("optionShowAllProfiles"); } }, false); addProfileButton.addEventListener("click", async event => { const profileName = await prompt(browser.i18n.getMessage("profileAddPrompt"), event.clientY + 50); if (profileName) { try { await browser.runtime.sendMessage({ method: "config.createProfile", profileName, fromProfileName: profileNamesInput.value }); } catch (error) { // ignored } if (sidePanelDisplay) { await refresh(); } else { await refresh(profileName); } await refreshExternalComponents(); } }, false); deleteProfileButton.addEventListener("click", async event => { if (await confirm(browser.i18n.getMessage("profileDeleteConfirm"), event.clientY + 50)) { try { await browser.runtime.sendMessage({ method: "config.deleteProfile", profileName: profileNamesInput.value }); } catch (error) { // ignored } profileNamesInput.value = null; await refresh(); await refreshExternalComponents(); } }, false); renameProfileButton.addEventListener("click", async event => { const profileName = await prompt(browser.i18n.getMessage("profileRenamePrompt"), event.clientY + 50, profileNamesInput.value); if (profileName) { try { await browser.runtime.sendMessage({ method: "config.renameProfile", profileName: profileNamesInput.value, newProfileName: profileName }); } catch (error) { // ignored } await refresh(profileName); await refreshExternalComponents(); } }, false); resetButton.addEventListener("click", async event => { const choice = await reset(event.clientY - 250); if (choice) { if (choice == "all") { await browser.runtime.sendMessage({ method: "config.resetProfiles" }); await refresh(DEFAULT_PROFILE_NAME); await refreshExternalComponents(); } if (choice == "current") { await browser.runtime.sendMessage({ method: "config.resetProfile", profileName: profileNamesInput.value }); await refresh(); await refreshExternalComponents(); } await update(); } }, false); exportButton.addEventListener("click", async () => { await browser.runtime.sendMessage({ method: "config.exportConfig" }); }, false); importButton.addEventListener("click", () => { fileInput.onchange = async () => { if (fileInput.files.length) { const reader = new FileReader(); reader.readAsText(fileInput.files[0]); const serializedConfig = await new Promise((resolve, reject) => { reader.addEventListener("load", () => resolve(reader.result), false); reader.addEventListener("error", reject, false); }); const config = JSON.parse(serializedConfig); await browser.runtime.sendMessage({ method: "config.importConfig", config }); await refresh(DEFAULT_PROFILE_NAME); await refreshExternalComponents(); fileInput.value = ""; } }; fileInput.click(); }, false); autoSaveUnloadInput.addEventListener("click", async () => { if (!autoSaveLoadInput.checked && !autoSaveUnloadInput.checked) { autoSaveLoadOrUnloadInput.checked = true; } }, false); autoSaveLoadInput.addEventListener("click", async () => { if (!autoSaveLoadInput.checked && !autoSaveUnloadInput.checked) { autoSaveLoadOrUnloadInput.checked = true; } }, false); autoSaveLoadOrUnloadInput.addEventListener("click", async () => { if (autoSaveLoadOrUnloadInput.checked) { autoSaveUnloadInput.checked = autoSaveLoadInput.checked = false; } else { autoSaveUnloadInput.checked = false; autoSaveLoadInput.checked = true; } }, false); expandAllButton.addEventListener("click", () => { if (expandAllButton.className) { expandAllButton.className = ""; } else { expandAllButton.className = "opened"; } document.querySelectorAll("details").forEach(detailElement => detailElement.open = Boolean(expandAllButton.className)); }, false); removeScriptsInput.addEventListener("click", () => { if (!removeScriptsInput.checked) { removeHiddenElementsInput.checked = false; removeUnusedStylesInput.checked = false; } }, false); saveCreatedBookmarksInput.addEventListener("click", saveCreatedBookmarks, false); passReferrerOnErrorInput.addEventListener("click", passReferrerOnError, false); autoSaveExternalSaveInput.addEventListener("click", enableExternalSave, false); saveToFilesystemInput.addEventListener("click", async () => await browser.runtime.sendMessage({ method: "downloads.disableGDrive" }), false); saveToClipboardInput.addEventListener("click", async () => await browser.runtime.sendMessage({ method: "downloads.disableGDrive" }), false); addProofInput.addEventListener("click", async event => { if (addProofInput.checked) { addProofInput.checked = false; if (await confirm(browser.i18n.getMessage("optionsAddProofConfirm"), event.clientY - 100)) { addProofInput.checked = true; } await update(); } }); synchronizeInput.checked = (await browser.runtime.sendMessage({ method: "config.isSync" })).sync; synchronizeInput.addEventListener("click", async () => { if (synchronizeInput.checked) { await browser.runtime.sendMessage({ method: "config.enableSync" }); await refresh(DEFAULT_PROFILE_NAME); } else { await browser.runtime.sendMessage({ method: "config.disableSync" }); await refresh(); } }, false); document.body.onchange = async event => { let target = event.target; if (target != ruleUrlInput && target != ruleProfileInput && target != ruleAutoSaveProfileInput && target != ruleEditUrlInput && target != ruleEditProfileInput && target != ruleEditAutoSaveProfileInput && target != showAutoSaveProfileInput && target != saveCreatedBookmarksInput && target != passReferrerOnErrorInput) { if (target != profileNamesInput && target != showAllProfilesInput) { await update(); } if (target == profileNamesInput) { await refresh(profileNamesInput.value); if (sidePanelDisplay) { const tabsData = await browser.runtime.sendMessage({ method: "tabsData.get" }); tabsData.profileName = profileNamesInput.value; await browser.runtime.sendMessage({ method: "tabsData.set", tabsData: tabsData }); await browser.runtime.sendMessage({ method: "ui.refreshMenu" }); } } else { if (target == contextMenuEnabledInput) { await browser.runtime.sendMessage({ method: "ui.refreshMenu" }); } if (target == openEditorInput) { await browser.runtime.sendMessage({ method: "ui.refreshMenu" }); } await refresh(); } } }; addProfileButton.title = browser.i18n.getMessage("profileAddButtonTooltip"); deleteProfileButton.title = browser.i18n.getMessage("profileDeleteButtonTooltip"); renameProfileButton.title = browser.i18n.getMessage("profileRenameButtonTooltip"); removeHiddenElementsLabel.textContent = browser.i18n.getMessage("optionRemoveHiddenElements"); removeUnusedStylesLabel.textContent = browser.i18n.getMessage("optionRemoveUnusedStyles"); removeUnusedFontsLabel.textContent = browser.i18n.getMessage("optionRemoveUnusedFonts"); removeFramesLabel.textContent = browser.i18n.getMessage("optionRemoveFrames"); removeImportsLabel.textContent = browser.i18n.getMessage("optionRemoveImports"); removeScriptsLabel.textContent = browser.i18n.getMessage("optionRemoveScripts"); saveRawPageLabel.textContent = browser.i18n.getMessage("optionSaveRawPage"); saveToClipboardLabel.textContent = browser.i18n.getMessage("optionSaveToClipboard"); saveToFilesystemLabel.textContent = browser.i18n.getMessage("optionSaveToFilesystem"); addProofLabel.textContent = browser.i18n.getMessage("optionAddProof"); saveToGDriveLabel.textContent = browser.i18n.getMessage("optionSaveToGDrive"); compressHTMLLabel.textContent = browser.i18n.getMessage("optionCompressHTML"); compressCSSLabel.textContent = browser.i18n.getMessage("optionCompressCSS"); loadDeferredImagesLabel.textContent = browser.i18n.getMessage("optionLoadDeferredImages"); loadDeferredImagesMaxIdleTimeLabel.textContent = browser.i18n.getMessage("optionLoadDeferredImagesMaxIdleTime"); loadDeferredImagesKeepZoomLevelLabel.textContent = browser.i18n.getMessage("optionLoadDeferredImagesKeepZoomLevel"); addMenuEntryLabel.textContent = browser.i18n.getMessage("optionAddMenuEntry"); filenameTemplateLabel.textContent = browser.i18n.getMessage("optionFilenameTemplate"); filenameMaxLengthLabel.textContent = browser.i18n.getMessage("optionFilenameMaxLength"); shadowEnabledLabel.textContent = browser.i18n.getMessage("optionDisplayShadow"); setMaxResourceSizeLabel.textContent = browser.i18n.getMessage("optionSetMaxResourceSize"); maxResourceSizeLabel.textContent = browser.i18n.getMessage("optionMaxResourceSize"); confirmFilenameLabel.textContent = browser.i18n.getMessage("optionConfirmFilename"); filenameConflictActionLabel.textContent = browser.i18n.getMessage("optionFilenameConflictAction"); filenameConflictActionUniquifyLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionUniquify"); filenameConflictActionOverwriteLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionOverwrite"); filenameConflictActionPromptLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionPrompt"); filenameConflictActionSkipLabel.textContent = browser.i18n.getMessage("optionFilenameConflictActionSkip"); removeAudioLabel.textContent = browser.i18n.getMessage("optionRemoveAudio"); removeVideoLabel.textContent = browser.i18n.getMessage("optionRemoveVideo"); displayInfobarLabel.textContent = browser.i18n.getMessage("optionDisplayInfobar"); displayStatsLabel.textContent = browser.i18n.getMessage("optionDisplayStats"); backgroundSaveLabel.textContent = browser.i18n.getMessage("optionBackgroundSave"); autoSaveDelayLabel.textContent = browser.i18n.getMessage("optionAutoSaveDelay"); autoSaveLoadLabel.textContent = browser.i18n.getMessage("optionAutoSaveLoad"); autoSaveUnloadLabel.textContent = browser.i18n.getMessage("optionAutoSaveUnload"); autoSaveLoadOrUnloadLabel.textContent = browser.i18n.getMessage("optionAutoSaveLoadOrUnload"); autoSaveRepeatLabel.textContent = browser.i18n.getMessage("optionAutoSaveRepeat"); autoSaveRepeatDelayLabel.textContent = browser.i18n.getMessage("optionAutoSaveRepeatDelay"); autoSaveExternalSaveLabel.textContent = browser.i18n.getMessage("optionAutoSaveExternalSave"); removeAlternativeFontsLabel.textContent = browser.i18n.getMessage("optionRemoveAlternativeFonts"); removeAlternativeImagesLabel.textContent = browser.i18n.getMessage("optionRemoveAlternativeImages"); removeAlternativeMediasLabel.textContent = browser.i18n.getMessage("optionRemoveAlternativeMedias"); saveCreatedBookmarksLabel.textContent = browser.i18n.getMessage("optionSaveCreatedBookmarks"); passReferrerOnErrorLabel.textContent = browser.i18n.getMessage("optionPassReferrerOnError"); replaceBookmarkURLLabel.textContent = browser.i18n.getMessage("optionReplaceBookmarkURL"); ignoredBookmarkFoldersLabel.textContent = browser.i18n.getMessage("optionIgnoredBookmarkFolders"); groupDuplicateImagesLabel.textContent = browser.i18n.getMessage("optionGroupDuplicateImages"); titleLabel.textContent = browser.i18n.getMessage("optionsTitle"); userInterfaceLabel.textContent = browser.i18n.getMessage("optionsUserInterfaceSubTitle"); filenameLabel.textContent = browser.i18n.getMessage("optionsFileNameSubTitle"); htmlContentLabel.textContent = browser.i18n.getMessage("optionsHTMLContentSubTitle"); imagesLabel.textContent = browser.i18n.getMessage("optionsImagesSubTitle"); stylesheetsLabel.textContent = browser.i18n.getMessage("optionsStylesheetsSubTitle"); fontsLabel.textContent = browser.i18n.getMessage("optionsFontsSubTitle"); otherResourcesLabel.textContent = browser.i18n.getMessage("optionsOtherResourcesSubTitle"); destinationLabel.textContent = browser.i18n.getMessage("optionsDestionationSubTitle"); bookmarksLabel.textContent = browser.i18n.getMessage("optionsBookmarkSubTitle"); autoSaveLabel.textContent = browser.i18n.getMessage("optionsAutoSaveSubTitle"); miscLabel.textContent = browser.i18n.getMessage("optionsMiscSubTitle"); helpLabel.textContent = browser.i18n.getMessage("optionsHelpLink"); infobarTemplateLabel.textContent = browser.i18n.getMessage("optionInfobarTemplate"); includeInfobarLabel.textContent = browser.i18n.getMessage("optionIncludeInfobar"); confirmInfobarLabel.textContent = browser.i18n.getMessage("optionConfirmInfobar"); autoCloseLabel.textContent = browser.i18n.getMessage("optionAutoClose"); editorLabel.textContent = browser.i18n.getMessage("optionsEditorSubTitle"); openEditorLabel.textContent = browser.i18n.getMessage("optionOpenEditor"); autoOpenEditorLabel.textContent = browser.i18n.getMessage("optionAutoOpenEditor"); defaultEditorModeLabel.textContent = browser.i18n.getMessage("optionDefaultEditorMode"); defaultEditorModeNormalLabel.textContent = browser.i18n.getMessage("optionDefaultEditorModeNormal"); defaultEditorModeEditLabel.textContent = browser.i18n.getMessage("optionDefaultEditorModeEdit"); defaultEditorModeFormatLabel.textContent = browser.i18n.getMessage("optionDefaultEditorModeFormat"); defaultEditorModeCutLabel.textContent = browser.i18n.getMessage("optionDefaultEditorModeCut"); defaultEditorModeCutExternalLabel.textContent = browser.i18n.getMessage("optionDefaultEditorModeCutExternal"); applySystemThemeLabel.textContent = browser.i18n.getMessage("optionApplySystemTheme"); warnUnsavedPageLabel.textContent = browser.i18n.getMessage("optionWarnUnsavedPage"); resetButton.textContent = browser.i18n.getMessage("optionsResetButton"); exportButton.textContent = browser.i18n.getMessage("optionsExportButton"); importButton.textContent = browser.i18n.getMessage("optionsImportButton"); resetButton.title = browser.i18n.getMessage("optionsResetTooltip"); autoSettingsLabel.textContent = browser.i18n.getMessage("optionsAutoSettingsSubTitle"); autoSettingsUrlLabel.textContent = browser.i18n.getMessage("optionsAutoSettingsUrl"); autoSettingsProfileLabel.textContent = browser.i18n.getMessage("optionsAutoSettingsProfile"); autoSettingsAutoSaveProfileLabel.textContent = browser.i18n.getMessage("optionsAutoSettingsAutoSaveProfile"); ruleAddButton.title = browser.i18n.getMessage("optionsAddRuleTooltip"); ruleEditButton.title = browser.i18n.getMessage("optionsValidateChangesTooltip"); rulesDeleteAllButton.title = browser.i18n.getMessage("optionsDeleteRulesTooltip"); showAllProfilesLabel.textContent = browser.i18n.getMessage("optionsAutoSettingsShowAllProfiles"); showAutoSaveProfileLabel.textContent = browser.i18n.getMessage("optionsAutoSettingsShowAutoSaveProfile"); ruleUrlInput.placeholder = ruleEditUrlInput.placeholder = browser.i18n.getMessage("optionsAutoSettingsUrlPlaceholder"); synchronizeLabel.textContent = browser.i18n.getMessage("optionSynchronize"); resetAllButton.textContent = browser.i18n.getMessage("optionsResetAllButton"); resetCurrentButton.textContent = browser.i18n.getMessage("optionsResetCurrentButton"); resetCancelButton.textContent = promptCancelButton.textContent = cancelButton.textContent = browser.i18n.getMessage("optionsCancelButton"); confirmButton.textContent = promptConfirmButton.textContent = browser.i18n.getMessage("optionsOKButton"); document.getElementById("resetConfirmLabel").textContent = browser.i18n.getMessage("optionsResetConfirm"); if (location.href.endsWith("#")) { document.querySelector(".new-window-link").remove(); document.documentElement.classList.add("maximized"); } const tabsData = await browser.runtime.sendMessage({ method: "tabsData.get" }); getHelpContents(); refresh(tabsData.profileName); async function refresh(profileName) { const [profiles, rules, companionState] = await Promise.all([ browser.runtime.sendMessage({ method: "config.getProfiles" }), browser.runtime.sendMessage({ method: "config.getRules" }), browser.runtime.sendMessage({ method: "companion.state" })]); const selectedProfileName = profileName || profileNamesInput.value || DEFAULT_PROFILE_NAME; Array.from(profileNamesInput.childNodes).forEach(node => node.remove()); profileNamesInput.options.length = 0; ruleProfileInput.options.length = 0; ruleAutoSaveProfileInput.options.length = 0; ruleEditProfileInput.options.length = 0; ruleEditAutoSaveProfileInput.options.length = 0; let optionElement = document.createElement("option"); optionElement.value = DEFAULT_PROFILE_NAME; optionElement.textContent = browser.i18n.getMessage("profileDefaultSettings"); [CURRENT_PROFILE_NAME].concat(...Object.keys(profiles)).forEach(profileName => { const optionElement = document.createElement("option"); optionElement.value = optionElement.textContent = profileName; if (profileName == DEFAULT_PROFILE_NAME) { optionElement.textContent = browser.i18n.getMessage("profileDefaultSettings"); } if (profileName != CURRENT_PROFILE_NAME) { profileNamesInput.appendChild(optionElement); } ruleProfileInput.appendChild(optionElement.cloneNode(true)); ruleAutoSaveProfileInput.appendChild(optionElement.cloneNode(true)); ruleEditProfileInput.appendChild(optionElement.cloneNode(true)); ruleEditAutoSaveProfileInput.appendChild(optionElement.cloneNode(true)); }); profileNamesInput.disabled = profileNamesInput.options.length == 1; optionElement = document.createElement("option"); optionElement.value = DISABLED_PROFILE_NAME; optionElement.textContent = browser.i18n.getMessage("profileDisabled"); ruleAutoSaveProfileInput.appendChild(optionElement); ruleEditAutoSaveProfileInput.appendChild(optionElement.cloneNode(true)); const rulesDataElement = rulesElement.querySelector(".rules-data"); Array.from(rulesDataElement.childNodes).forEach(node => (!node.className || !node.className.includes("rule-edit")) && node.remove()); const editURLElement = rulesElement.querySelector(".rule-edit"); createURLElement.hidden = false; editURLElement.hidden = true; ruleProfileInput.value = ruleAutoSaveProfileInput.value = selectedProfileName; let rulesDisplayed; rules.forEach(rule => { if (showAllProfilesInput.checked || selectedProfileName == rule.profile || selectedProfileName == rule.autoSaveProfile) { rulesDisplayed = true; const ruleElement = rulesElement.querySelector(".rule-view").cloneNode(true); const ruleUrlElement = ruleElement.querySelector(".rule-url"); const ruleProfileElement = ruleElement.querySelector(".rule-profile"); const ruleAutoSaveProfileElement = ruleElement.querySelector(".rule-autosave-profile"); ruleUrlElement.textContent = ruleUrlElement.title = rule.url; ruleProfileElement.textContent = ruleProfileElement.title = getProfileText(rule.profile); ruleAutoSaveProfileElement.textContent = ruleAutoSaveProfileElement.title = getProfileText(rule.autoSaveProfile); ruleElement.hidden = false; ruleElement.className = "tr data"; rulesDataElement.appendChild(ruleElement); const ruleDeleteButton = ruleElement.querySelector(".rule-delete-button"); const ruleUpdateButton = ruleElement.querySelector(".rule-update-button"); ruleDeleteButton.title = browser.i18n.getMessage("optionsDeleteRuleTooltip"); ruleDeleteButton.addEventListener("click", async event => { if (await confirm(browser.i18n.getMessage("optionsDeleteRuleConfirm"), event.clientY - 100)) { await browser.runtime.sendMessage({ method: "config.deleteRule", url: rule.url }); await refresh(); await refreshExternalComponents(); } }, false); ruleUpdateButton.title = browser.i18n.getMessage("optionsUpdateRuleTooltip"); ruleUpdateButton.addEventListener("click", async () => { if (editURLElement.hidden) { createURLElement.hidden = true; editURLElement.hidden = false; rulesDataElement.replaceChild(editURLElement, ruleElement); ruleEditUrlInput.value = rule.url; ruleEditProfileInput.value = rule.profile; ruleEditAutoSaveProfileInput.value = rule.autoSaveProfile; ruleEditUrlInput.focus(); editURLElement.onsubmit = async event => { event.preventDefault(); rulesElement.appendChild(editURLElement); await browser.runtime.sendMessage({ method: "config.updateRule", url: rule.url, newUrl: ruleEditUrlInput.value, profileName: ruleEditProfileInput.value, autoSaveProfileName: ruleEditAutoSaveProfileInput.value }); await refresh(); await refreshExternalComponents(); ruleUrlInput.focus(); }; } }, false); } }); rulesDeleteAllButton.disabled = !rulesDisplayed; rulesElement.appendChild(createURLElement); profileNamesInput.value = selectedProfileName; renameProfileButton.disabled = deleteProfileButton.disabled = profileNamesInput.value == DEFAULT_PROFILE_NAME; const profileOptions = profiles[selectedProfileName]; removeHiddenElementsInput.checked = profileOptions.removeHiddenElements; removeUnusedStylesInput.checked = profileOptions.removeUnusedStyles; removeUnusedFontsInput.checked = profileOptions.removeUnusedFonts; removeFramesInput.checked = profileOptions.removeFrames; removeImportsInput.checked = profileOptions.removeImports; removeScriptsInput.checked = profileOptions.removeScripts; saveRawPageInput.checked = profileOptions.saveRawPage; saveToClipboardInput.checked = profileOptions.saveToClipboard; addProofInput.checked = profileOptions.addProof; saveToGDriveInput.checked = profileOptions.saveToGDrive; saveToFilesystemInput.checked = !profileOptions.saveToGDrive && !saveToClipboardInput.checked; compressHTMLInput.checked = profileOptions.compressHTML; compressCSSInput.checked = profileOptions.compressCSS; loadDeferredImagesInput.checked = profileOptions.loadDeferredImages; loadDeferredImagesMaxIdleTimeInput.value = profileOptions.loadDeferredImagesMaxIdleTime; loadDeferredImagesKeepZoomLevelInput.checked = profileOptions.loadDeferredImagesKeepZoomLevel; loadDeferredImagesKeepZoomLevelInput.disabled = !profileOptions.loadDeferredImages; loadDeferredImagesMaxIdleTimeInput.disabled = !profileOptions.loadDeferredImages; contextMenuEnabledInput.checked = profileOptions.contextMenuEnabled; filenameTemplateInput.value = profileOptions.filenameTemplate; filenameMaxLengthInput.value = profileOptions.filenameMaxLength; shadowEnabledInput.checked = profileOptions.shadowEnabled; maxResourceSizeEnabledInput.checked = profileOptions.maxResourceSizeEnabled; maxResourceSizeInput.value = profileOptions.maxResourceSize; maxResourceSizeInput.disabled = !profileOptions.maxResourceSizeEnabled; confirmFilenameInput.checked = profileOptions.confirmFilename; filenameConflictActionInput.value = profileOptions.filenameConflictAction; removeAudioSrcInput.checked = profileOptions.removeAudioSrc; removeVideoSrcInput.checked = profileOptions.removeVideoSrc; displayInfobarInput.checked = profileOptions.displayInfobar; displayStatsInput.checked = profileOptions.displayStats; backgroundSaveInput.checked = profileOptions.backgroundSave; autoSaveDelayInput.value = profileOptions.autoSaveDelay; autoSaveDelayInput.disabled = !profileOptions.autoSaveLoadOrUnload && !profileOptions.autoSaveLoad; autoSaveLoadInput.checked = !profileOptions.autoSaveLoadOrUnload && profileOptions.autoSaveLoad; autoSaveLoadOrUnloadInput.checked = profileOptions.autoSaveLoadOrUnload; autoSaveUnloadInput.checked = !profileOptions.autoSaveLoadOrUnload && profileOptions.autoSaveUnload; autoSaveLoadInput.disabled = profileOptions.autoSaveLoadOrUnload; autoSaveUnloadInput.disabled = profileOptions.autoSaveLoadOrUnload; autoSaveRepeatInput.checked = profileOptions.autoSaveRepeat; autoSaveRepeatInput.disabled = !profileOptions.autoSaveLoadOrUnload && !profileOptions.autoSaveLoad; autoSaveRepeatDelayInput.value = profileOptions.autoSaveRepeatDelay; autoSaveRepeatDelayInput.disabled = !profileOptions.autoSaveRepeat; autoSaveExternalSaveInput.checked = profileOptions.autoSaveExternalSave; autoSaveExternalSaveInput.parentElement.hidden = !companionState.enabled; removeAlternativeFontsInput.checked = profileOptions.removeAlternativeFonts; removeAlternativeImagesInput.checked = profileOptions.removeAlternativeImages; groupDuplicateImagesInput.checked = profileOptions.groupDuplicateImages; removeAlternativeMediasInput.checked = profileOptions.removeAlternativeMedias; saveCreatedBookmarksInput.checked = profileOptions.saveCreatedBookmarks; passReferrerOnErrorInput.checked = profileOptions.passReferrerOnError; replaceBookmarkURLInput.checked = profileOptions.replaceBookmarkURL; replaceBookmarkURLInput.disabled = !profileOptions.saveCreatedBookmarks; ignoredBookmarkFoldersInput.value = profileOptions.ignoredBookmarkFolders.map(folder => folder.replace(/,/g, "\\,")).join(","); // eslint-disable-line no-useless-escape ignoredBookmarkFoldersInput.disabled = !profileOptions.saveCreatedBookmarks; infobarTemplateInput.value = profileOptions.infobarTemplate; includeInfobarInput.checked = profileOptions.includeInfobar; confirmInfobarInput.checked = profileOptions.confirmInfobarContent; autoCloseInput.checked = profileOptions.autoClose; openEditorInput.checked = profileOptions.openEditor; autoOpenEditorInput.checked = profileOptions.autoOpenEditor; defaultEditorModeInput.value = profileOptions.defaultEditorMode; applySystemThemeInput.checked = profileOptions.applySystemTheme; warnUnsavedPageInput.checked = profileOptions.warnUnsavedPage; } function getProfileText(profileName) { return profileName == DEFAULT_PROFILE_NAME ? browser.i18n.getMessage("profileDefaultSettings") : profileName == DISABLED_PROFILE_NAME ? browser.i18n.getMessage("profileDisabled") : profileName; } async function update() { try { await pendingSave; } catch (error) { // ignored } pendingSave = browser.runtime.sendMessage({ method: "config.updateProfile", profileName: profileNamesInput.value, profile: { removeHiddenElements: removeHiddenElementsInput.checked, removeUnusedStyles: removeUnusedStylesInput.checked, removeUnusedFonts: removeUnusedFontsInput.checked, removeFrames: removeFramesInput.checked, removeImports: removeImportsInput.checked, removeScripts: removeScriptsInput.checked, saveRawPage: saveRawPageInput.checked, saveToClipboard: saveToClipboardInput.checked, addProof: addProofInput.checked, saveToGDrive: saveToGDriveInput.checked, compressHTML: compressHTMLInput.checked, compressCSS: compressCSSInput.checked, loadDeferredImages: loadDeferredImagesInput.checked, loadDeferredImagesMaxIdleTime: Math.max(loadDeferredImagesMaxIdleTimeInput.value, 0), loadDeferredImagesKeepZoomLevel: loadDeferredImagesKeepZoomLevelInput.checked, contextMenuEnabled: contextMenuEnabledInput.checked, filenameTemplate: filenameTemplateInput.value, filenameMaxLength: filenameMaxLengthInput.value, shadowEnabled: shadowEnabledInput.checked, maxResourceSizeEnabled: maxResourceSizeEnabledInput.checked, maxResourceSize: Math.max(maxResourceSizeInput.value, 0), confirmFilename: confirmFilenameInput.checked, filenameConflictAction: filenameConflictActionInput.value, removeAudioSrc: removeAudioSrcInput.checked, removeVideoSrc: removeVideoSrcInput.checked, displayInfobar: displayInfobarInput.checked, displayStats: displayStatsInput.checked, backgroundSave: backgroundSaveInput.checked, autoSaveDelay: Math.max(autoSaveDelayInput.value, 0), autoSaveLoad: autoSaveLoadInput.checked, autoSaveUnload: autoSaveUnloadInput.checked, autoSaveLoadOrUnload: autoSaveLoadOrUnloadInput.checked, autoSaveRepeat: autoSaveRepeatInput.checked, autoSaveRepeatDelay: Math.max(autoSaveRepeatDelayInput.value, 1), autoSaveExternalSave: autoSaveExternalSaveInput.checked, removeAlternativeFonts: removeAlternativeFontsInput.checked, removeAlternativeImages: removeAlternativeImagesInput.checked, removeAlternativeMedias: removeAlternativeMediasInput.checked, saveCreatedBookmarks: saveCreatedBookmarksInput.checked, passReferrerOnError: passReferrerOnErrorInput.checked, replaceBookmarkURL: replaceBookmarkURLInput.checked, ignoredBookmarkFolders: ignoredBookmarkFoldersInput.value.replace(/([^\\]),/g, "$1 ,").split(/[^\\],/).map(folder => folder.replace(/\\,/g, ",")), groupDuplicateImages: groupDuplicateImagesInput.checked, infobarTemplate: infobarTemplateInput.value, includeInfobar: includeInfobarInput.checked, confirmInfobarContent: confirmInfobarInput.checked, autoClose: autoCloseInput.checked, openEditor: openEditorInput.checked, autoOpenEditor: autoOpenEditorInput.checked, defaultEditorMode: defaultEditorModeInput.value, applySystemTheme: applySystemThemeInput.checked, warnUnsavedPage: warnUnsavedPageInput.checked } }); try { await pendingSave; } catch (error) { // ignored } } async function refreshExternalComponents() { try { await browser.runtime.sendMessage({ method: "ui.refreshMenu" }); if (sidePanelDisplay) { await browser.runtime.sendMessage({ method: "options.refresh", profileName: profileNamesInput.value }); } else { await browser.runtime.sendMessage({ method: "options.refreshPanel", profileName: profileNamesInput.value }); } } catch (error) { // ignored } } async function saveCreatedBookmarks() { if (saveCreatedBookmarksInput.checked) { saveCreatedBookmarksInput.checked = false; try { const permissionGranted = await browser.permissions.request({ permissions: ["bookmarks"] }); if (permissionGranted) { saveCreatedBookmarksInput.checked = true; await update(); await refresh(); await browser.runtime.sendMessage({ method: "bookmarks.saveCreatedBookmarks" }); } else { await disableOption(); } } catch (error) { saveCreatedBookmarksInput.checked = false; await disableOption(); } } else { await disableOption(); } async function disableOption() { await update(); await refresh(); await browser.runtime.sendMessage({ method: "bookmarks.disable" }); } } async function passReferrerOnError() { if (passReferrerOnErrorInput.checked) { passReferrerOnErrorInput.checked = false; try { const permissionGranted = await browser.permissions.request({ permissions: ["webRequest", "webRequestBlocking"] }); if (permissionGranted) { passReferrerOnErrorInput.checked = true; await update(); await refresh(); await browser.runtime.sendMessage({ method: "requests.enableReferrerOnError" }); } else { await disableOption(); } } catch (error) { await disableOption(); } } else { await disableOption(); } async function disableOption() { await update(); await refresh(); await browser.runtime.sendMessage({ method: "requests.disableReferrerOnError" }); await browser.permissions.remove({ permissions: ["webRequest", "webRequestBlocking"] }); } } async function enableExternalSave() { if (autoSaveExternalSaveInput.checked) { autoSaveExternalSaveInput.checked = false; try { const permissionGranted = await browser.permissions.request({ permissions: ["nativeMessaging"] }); if (permissionGranted) { autoSaveExternalSaveInput.checked = true; await refreshOption(); if (window.chrome) { window.chrome.runtime.reload(); location.reload(); } } else { await refreshOption(); } } catch (error) { autoSaveExternalSaveInput.checked = true; await refreshOption(); } } else { await refreshOption(); } async function refreshOption() { await update(); await refresh(); } } async function confirm(message, positionY) { document.getElementById("confirmLabel").textContent = message; document.getElementById("formConfirmContainer").style.setProperty("display", "flex"); document.querySelector("#formConfirmContainer .popup-content").style.setProperty("margin-top", positionY + "px"); confirmButton.focus(); document.body.style.setProperty("overflow-y", "hidden"); return new Promise(resolve => { confirmButton.onclick = event => hideAndResolve(event, true); cancelButton.onclick = event => hideAndResolve(event); window.onkeyup = event => { if (event.key == "Escape") { hideAndResolve(event); } }; function hideAndResolve(event, value) { event.preventDefault(); document.getElementById("formConfirmContainer").style.setProperty("display", "none"); document.body.style.setProperty("overflow-y", ""); resolve(value); } }); } async function reset(positionY) { document.getElementById("formResetContainer").style.setProperty("display", "flex"); document.querySelector("#formResetContainer .popup-content").style.setProperty("margin-top", positionY + "px"); resetCancelButton.focus(); document.body.style.setProperty("overflow-y", "hidden"); return new Promise(resolve => { resetAllButton.onclick = event => hideAndResolve(event, "all"); resetCurrentButton.onclick = event => hideAndResolve(event, "current"); resetCancelButton.onclick = event => hideAndResolve(event); window.onkeyup = event => { if (event.key == "Escape") { hideAndResolve(event); } }; function hideAndResolve(event, value) { event.preventDefault(); document.getElementById("formResetContainer").style.setProperty("display", "none"); document.body.style.setProperty("overflow-y", ""); resolve(value); } }); } async function prompt(message, positionY, defaultValue = "") { document.getElementById("promptLabel").textContent = message; document.getElementById("formPromptContainer").style.setProperty("display", "flex"); document.querySelector("#formPromptContainer .popup-content").style.setProperty("margin-top", positionY + "px"); promptInput.value = defaultValue; promptInput.focus(); document.body.style.setProperty("overflow-y", "hidden"); return new Promise(resolve => { promptConfirmButton.onclick = event => hideAndResolve(event, promptInput.value); promptCancelButton.onclick = event => hideAndResolve(event); window.onkeyup = event => { if (event.key == "Escape") { hideAndResolve(event); } }; function hideAndResolve(event, value) { event.preventDefault(); document.getElementById("formPromptContainer").style.setProperty("display", "none"); document.body.style.setProperty("overflow-y", ""); resolve(value); } }); } async function getHelpContents() { const helpPage = await fetch(browser.runtime.getURL(HELP_PAGE_PATH)); const content = new TextDecoder().decode(await helpPage.arrayBuffer()); const doc = (new DOMParser()).parseFromString(content, "text/html"); const items = doc.querySelectorAll("[data-options-label]"); items.forEach(itemElement => { const optionLabel = document.getElementById(itemElement.dataset.optionsLabel); const helpIconWrapper = document.createElement("span"); const helpIconContainer = document.createElement("span"); const helpIcon = document.createElement("img"); helpIcon.src = HELP_ICON_URL; helpIconWrapper.className = "help-icon-wrapper"; const labelWords = optionLabel.textContent.split(/\s+/); if (labelWords.length > 1) { helpIconWrapper.textContent = labelWords.pop(); optionLabel.textContent = labelWords.join(" ") + " "; } helpIconContainer.className = "help-icon"; helpIconContainer.onclick = () => { helpContent.hidden = !helpContent.hidden; return false; }; helpIcon.tabIndex = 0; helpIconContainer.onkeyup = event => { if (event.code == "Enter") { helpContent.hidden = !helpContent.hidden; return false; } }; helpIconContainer.appendChild(helpIcon); helpIconWrapper.appendChild(helpIconContainer); optionLabel.appendChild(helpIconWrapper); const helpContent = document.createElement("div"); helpContent.hidden = true; helpContent.className = "help-content"; itemElement.childNodes.forEach(node => { if (node instanceof HTMLElement && node.className != "option") { helpContent.appendChild(document.importNode(node, true)); } }); helpContent.querySelectorAll("a[href]").forEach(linkElement => { const hrefValue = linkElement.getAttribute("href"); if (hrefValue.startsWith("#")) { linkElement.href = browser.runtime.getURL(HELP_PAGE_PATH + linkElement.getAttribute("href")); linkElement.target = "_blank"; } }); optionLabel.parentElement.insertAdjacentElement("afterEnd", helpContent); }); } function getLocalStorageItem(key) { try { return localStorage.getItem(key); } catch (error) { // ignored } } function setLocalStorageItem(key, value) { try { return localStorage.setItem(key, value); } catch (error) { // ignored } } function removeLocalStorageItem(key) { try { return localStorage.removeItem(key); } catch (error) { // ignored } } })();