diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index fe9b9a58..728d2440 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -460,6 +460,15 @@ "exportOptions": { "message": "Import/Export All Options" }, + "exportOptionsCopy": { + "message": "Edit/copy" + }, + "exportOptionsDownload": { + "message": "Save to file" + }, + "exportOptionsUpload": { + "message": "Load from file" + }, "whatExportOptions": { "message": "This is your entire configuration in JSON. This includes your userID, so be sure to share this wisely." }, diff --git a/public/options/options.html b/public/options/options.html index c907889e..f689277c 100644 --- a/public/options/options.html +++ b/public/options/options.html @@ -355,8 +355,19 @@
-
- __MSG_exportOptions__ +

__MSG_exportOptions__

+ +
+
+ __MSG_exportOptionsCopy__ +
+
+ __MSG_exportOptionsDownload__ +
+ +
__MSG_whatExportOptions__
diff --git a/src/options.ts b/src/options.ts index 6f82cc9f..172ec0aa 100644 --- a/src/options.ts +++ b/src/options.ts @@ -176,6 +176,14 @@ async function init() { const button = optionsElements[i].querySelector(".trigger-button"); button.addEventListener("click", () => activatePrivateTextChange( optionsElements[i])); + if (option == "*") { + const downloadButton = optionsElements[i].querySelector(".download-button"); + downloadButton.addEventListener("click", downloadConfig); + + const uploadButton = optionsElements[i].querySelector(".upload-button"); + uploadButton.addEventListener("change", (e) => uploadConfig(e)); + } + const privateTextChangeOption = optionsElements[i].getAttribute("data-sync"); // See if anything extra must be done switch (privateTextChangeOption) { @@ -490,38 +498,7 @@ function activatePrivateTextChange(element: HTMLElement) { const setButton = element.querySelector(".text-change-set"); setButton.addEventListener("click", async () => { - const confirmMessage = element.getAttribute("data-confirm-message"); - - if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) { - - // See if anything extra must be done - switch (option) { - case "*": - try { - const newConfig = JSON.parse(textBox.value); - for (const key in newConfig) { - Config.config[key] = newConfig[key]; - } - Config.convertJSON(); - - if (newConfig.supportInvidious) { - const checkbox = document.querySelector("#support-invidious > div > label > input"); - - checkbox.checked = true; - await invidiousOnClick(checkbox, "supportInvidious"); - } - - window.location.reload(); - - } catch (e) { - alert(chrome.i18n.getMessage("incorrectlyFormattedOptions")); - } - - break; - default: - Config.config[option] = textBox.value; - } - } + setTextOption(option, element, textBox.value); }); // See if anything extra must be done @@ -543,6 +520,77 @@ function activatePrivateTextChange(element: HTMLElement) { element.querySelector(".option-hidden-section").classList.remove("hidden"); } +/** + * Function to run when a textbox change is submitted + * + * @param option data-sync value + * @param element main container div + * @param value new text + * @param callbackOnError function to run if confirmMessage was denied + */ +async function setTextOption(option: string, element: HTMLElement, value: string, callbackOnError?: () => void) { + const confirmMessage = element.getAttribute("data-confirm-message"); + + if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) { + + // See if anything extra must be done + switch (option) { + case "*": + try { + const newConfig = JSON.parse(value); + for (const key in newConfig) { + Config.config[key] = newConfig[key]; + } + Config.convertJSON(); + + if (newConfig.supportInvidious) { + const checkbox = document.querySelector("#support-invidious > div > label > input"); + + checkbox.checked = true; + await invidiousOnClick(checkbox, "supportInvidious"); + } + + window.location.reload(); + + } catch (e) { + alert(chrome.i18n.getMessage("incorrectlyFormattedOptions")); + } + + break; + default: + Config.config[option] = value; + } + } else { + if (typeof callbackOnError == "function") + callbackOnError(); + } +} + +function downloadConfig() { + const file = document.createElement("a"); + const jsonData = JSON.parse(JSON.stringify(Config.localConfig)); + jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes); + file.setAttribute("href", "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(jsonData))); + file.setAttribute("download", "SponsorBlockConfig.json"); + document.body.append(file); + file.click(); + file.remove(); +} + +function uploadConfig(e) { + if (e.target.files.length == 1) { + const file = e.target.files[0]; + const reader = new FileReader(); + const element = document.querySelector("[data-sync='*']") as HTMLElement; + reader.onload = function(ev) { + setTextOption("*", element, ev.target.result as string, () => { + e.target.value = null; + }); + }; + reader.readAsText(file); + } +} + /** * Validates the value used for the database server address. * Returns null and alerts the user if there is an issue.