diff --git a/extension/Userscripts Extension/Resources/content.js b/extension/Userscripts Extension/Resources/content.js index 8441aabb..b5b88cdb 100644 --- a/extension/Userscripts Extension/Resources/content.js +++ b/extension/Userscripts Extension/Resources/content.js @@ -322,154 +322,164 @@ function cspFallback(e) { } } -const injection = () => browser.runtime.sendMessage({name: "REQ_USERSCRIPTS"}, response => { - // cancel injection if errors detected - if (!response || response.error) { - console.error(response?.error || "REQ_USERSCRIPTS returned undefined"); - return; - } - // save response locally in case CSP events occur - data = response; - // combine regular and context-menu scripts - const scripts = [...data.files.js, ...data.files.menu]; - // loop through each userscript and prepare for processing - for (let i = 0; i < scripts.length; i++) { - const userscript = scripts[i]; - userscript.preCode = ""; - // pass references to the api methods as needed - const gmMethods = []; - const filename = userscript.scriptObject.filename; - const grants = userscript.scriptObject.grant; - const injectInto = userscript.scriptObject["inject-into"]; - // create GM.info object - const scriptData = { - "script": userscript.scriptObject, - "scriptHandler": data.scriptHandler, - "scriptHandlerVersion": data.scriptHandlerVersion, - "scriptMetaStr": userscript.scriptMetaStr - }; - // all userscripts get access to GM.info - gmMethods.push("info: GM_info"); - // if @grant explicitly set to none, empty grants array - if (grants.includes("none")) grants.length = 0; - // @grant values exist for page scoped userscript - if (grants.length && injectInto === "page") { - // remove grants - grants.length = 0; - // log warning - console.warn(`${filename} @grant values removed due to @inject-into value: ${injectInto} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`); +function injection() { + browser.runtime.sendMessage({name: "REQ_USERSCRIPTS"}, response => { + // cancel injection if errors detected + if (!response || response.error) { + console.error(response?.error || "REQ_USERSCRIPTS returned undefined"); + return; } - // @grant exist for auto scoped userscript - if (grants.length && injectInto === "auto") { - // change scope - userscript.scriptObject["inject-into"] = "content"; - // log warning - console.warn(`${filename} @inject-into value set to 'content' due to @grant values: ${grants} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`); - } - // loop through each userscript @grant value, add methods as needed - for (let j = 0; j < grants.length; j++) { - const grant = grants[j]; - const method = grant.split(".")[1] || grant.split(".")[0]; - // ensure API method exists in apis object - if (!Object.keys(apis).includes(method)) continue; - // create the method string to be pushed to methods array - let methodStr = `${method}: apis.${method}`; - // add require variables to specific methods - switch (method) { - case "getValue": - case "setValue": - case "deleteValue": - case "listValues": - methodStr += `.bind({"US_filename": "${filename}"})`; - break; - case "info": - case "GM_info": - continue; - case "xmlHttpRequest": - gmMethods.push("xmlHttpRequest: apis.xhr"); - continue; - case "GM_xmlhttpRequest": - userscript.preCode += "const GM_xmlhttpRequest = apis.xhr;"; - continue; + // save response locally in case CSP events occur + data = response; + // combine regular and context-menu scripts + const scripts = [...data.files.js, ...data.files.menu]; + // loop through each userscript and prepare for processing + for (let i = 0; i < scripts.length; i++) { + const userscript = scripts[i]; + userscript.preCode = ""; + // pass references to the api methods as needed + const gmMethods = []; + const filename = userscript.scriptObject.filename; + const grants = userscript.scriptObject.grant; + const injectInto = userscript.scriptObject["inject-into"]; + // create GM.info object + const scriptData = { + "script": userscript.scriptObject, + "scriptHandler": data.scriptHandler, + "scriptHandlerVersion": data.scriptHandlerVersion, + "scriptMetaStr": userscript.scriptMetaStr + }; + // all userscripts get access to GM.info + gmMethods.push("info: GM_info"); + // if @grant explicitly set to none, empty grants array + if (grants.includes("none")) grants.length = 0; + // @grant values exist for page scoped userscript + if (grants.length && injectInto === "page") { + // remove grants + grants.length = 0; + // log warning + console.warn(`${filename} @grant values removed due to @inject-into value: ${injectInto} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`); + } + // @grant exist for auto scoped userscript + if (grants.length && injectInto === "auto") { + // change scope + userscript.scriptObject["inject-into"] = "content"; + // log warning + console.warn(`${filename} @inject-into value set to 'content' due to @grant values: ${grants} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`); + } + // loop through each userscript @grant value, add methods as needed + for (let j = 0; j < grants.length; j++) { + const grant = grants[j]; + const method = grant.split(".")[1] || grant.split(".")[0]; + // ensure API method exists in apis object + if (!Object.keys(apis).includes(method)) continue; + // create the method string to be pushed to methods array + let methodStr = `${method}: apis.${method}`; + // add require variables to specific methods + switch (method) { + case "getValue": + case "setValue": + case "deleteValue": + case "listValues": + methodStr += `.bind({"US_filename": "${filename}"})`; + break; + case "info": + case "GM_info": + continue; + case "xmlHttpRequest": + gmMethods.push("xmlHttpRequest: apis.xhr"); + continue; + case "GM_xmlhttpRequest": + userscript.preCode += "const GM_xmlhttpRequest = apis.xhr;"; + continue; + } + gmMethods.push(methodStr); } - gmMethods.push(methodStr); + // add GM.info + userscript.preCode += `const GM_info = ${JSON.stringify(scriptData)};`; + // add other included GM API methods + userscript.preCode += `const GM = {${gmMethods.join(",")}};`; + // process file for injection + processJS(userscript); } - // add GM.info - userscript.preCode += `const GM_info = ${JSON.stringify(scriptData)};`; - // add other included GM API methods - userscript.preCode += `const GM = {${gmMethods.join(",")}};`; - // process file for injection - processJS(userscript); - } - for (let i = 0; i < data.files.css.length; i++) { - const userstyle = data.files.css[i]; - injectCSS(userstyle.name, userstyle.code); - } -}); -// listens for messages from background, popup, etc... -browser.runtime.onMessage.addListener((request, sender, sendResponse) => { - const name = request.name; - if ( - name === "USERSCRIPT_INSTALL_00" - || name === "USERSCRIPT_INSTALL_01" - || name === "USERSCRIPT_INSTALL_02" - ) { - // only respond to top frame messages - if (window !== window.top) return; - const types = [ - "text/plain", - "application/ecmascript", - "application/javascript", - "text/ecmascript", - "text/javascript" - ]; - if ( - !document.contentType - || types.indexOf(document.contentType) === -1 - || !document.querySelector("pre") - ) { - sendResponse({invalid: true}); - } else { - const message = { - name: name, - content: - document.querySelector("pre").innerText - }; - browser.runtime.sendMessage(message, response => { - sendResponse(response); - }); - return true; + for (let i = 0; i < data.files.css.length; i++) { + const userstyle = data.files.css[i]; + injectCSS(userstyle.name, userstyle.code); } - } else if (name === "CONTEXT_RUN") { - // from bg script when context-menu item is clicked - // double check to ensure context-menu scripts only run in top windows - if (window !== window.top) return; + }); +} - // loop through context-menu scripts saved to data object and find match - // if no match found, nothing will execute and error will log - const filename = request.menuItemId; - for (let i = 0; i < data.files.menu.length; i++) { - const item = data.files.menu[i]; - if (item.scriptObject.filename === filename) { - console.info(`Injecting ${filename} %c(js)`, "color: #fff600"); - sendResponse({ - code: wrapCode( - item.preCode, - item.code, - filename - ) +function listeners() { + // listens for messages from background, popup, etc... + browser.runtime.onMessage.addListener((request, sender, sendResponse) => { + const name = request.name; + if ( + name === "USERSCRIPT_INSTALL_00" + || name === "USERSCRIPT_INSTALL_01" + || name === "USERSCRIPT_INSTALL_02" + ) { + // only respond to top frame messages + if (window !== window.top) return; + const types = [ + "text/plain", + "application/ecmascript", + "application/javascript", + "text/ecmascript", + "text/javascript" + ]; + if ( + !document.contentType + || types.indexOf(document.contentType) === -1 + || !document.querySelector("pre") + ) { + sendResponse({invalid: true}); + } else { + const message = { + name: name, + content: + document.querySelector("pre").innerText + }; + browser.runtime.sendMessage(message, response => { + sendResponse(response); }); - return; + return true; + } + } else if (name === "CONTEXT_RUN") { + // from bg script when context-menu item is clicked + // double check to ensure context-menu scripts only run in top windows + if (window !== window.top) return; + + // loop through context-menu scripts saved to data object and find match + // if no match found, nothing will execute and error will log + const filename = request.menuItemId; + for (let i = 0; i < data.files.menu.length; i++) { + const item = data.files.menu[i]; + if (item.scriptObject.filename === filename) { + console.info(`Injecting ${filename} %c(js)`, "color: #fff600"); + sendResponse({ + code: wrapCode( + item.preCode, + item.code, + filename + ) + }); + return; + } } + console.error(`Couldn't find ${filename} code!`); } - console.error(`Couldn't find ${filename} code!`); - } -}); -// listen for CSP violations -document.addEventListener("securitypolicyviolation", cspFallback); + }); + // listen for CSP violations + document.addEventListener("securitypolicyviolation", cspFallback); +} -// should import settings.js and use `settingsStorage.get("global_active")` -browser.storage.local.get("US_GLOBAL_ACTIVE").then(results => { +async function initialize() { + // should import settings.js and use `settings.get("global_active")` + const results = await browser.storage.local.get("US_GLOBAL_ACTIVE"); if (results?.US_GLOBAL_ACTIVE === false) return console.info("Userscripts off"); + // start the injection process and add the listeners injection(); -}); \ No newline at end of file + listeners(); +} + +initialize(); \ No newline at end of file diff --git a/extension/Userscripts Extension/Resources/page.html b/extension/Userscripts Extension/Resources/page.html index babce656..8440bfdd 100644 --- a/extension/Userscripts Extension/Resources/page.html +++ b/extension/Userscripts Extension/Resources/page.html @@ -1 +1 @@ -Userscripts Safari
\ No newline at end of file +Userscripts Safari
\ No newline at end of file diff --git a/extension/Userscripts Extension/Resources/page.js b/extension/Userscripts Extension/Resources/page.js index 94d08d13..9ced2c74 100644 --- a/extension/Userscripts Extension/Resources/page.js +++ b/extension/Userscripts Extension/Resources/page.js @@ -1167,6 +1167,689 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o "weight" ]); + // wrap a relatively independent settings storage with its own functions + + const storagePrefix = "US_"; + const storageKey = key => storagePrefix + key.toUpperCase(); + // const storageRef = async area => { // dynamic storage reference + // browser.storage.sync.area = "sync"; + // browser.storage.local.area = "local"; + // if (area === "sync") return browser.storage.sync; + // if (area === "local") return browser.storage.local; + // const key = storageKey("settings_sync"); + // const result = await browser.storage.local.get(key); + // if (result?.[key] === true) { + // return browser.storage.sync; + // } else { + // return browser.storage.local; + // } + // }; + + // https://developer.apple.com/documentation/safariservices/safari_web_extensions/assessing_your_safari_web_extension_s_browser_compatibility#3584139 + // since storage sync is not implemented in Safari, currently only returns using local storage + const storageRef = async () => { + browser.storage.local.area = "local"; + return browser.storage.local; + }; + + const settingDefault = deepFreeze({ + name: "setting_default", + type: undefined, + local: false, + values: [], + default: undefined, + protect: false, + confirm: false, + platforms: ["macos", "ipados", "ios"], + langLabel: {}, + langTitle: {}, + group: "", + legacy: "", + nodeType: "", + nodeClass: {} + }); + + const settingsDefine = deepFreeze([ + { + name: "legacy_imported", + type: "number", + local: true, + default: 0, + protect: true, + platforms: ["macos"], + group: "Internal" + }, + { + name: "language_code", + type: "string", + default: "en", + platforms: ["macos", "ipados", "ios"], + group: "Internal", + legacy: "languageCode" + }, + { + name: "scripts_settings", + type: "object", + default: {}, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts update check active", + zh_hans: "脚本更新检查激活" + }, + langTitle: { + en: "Whether to enable each single script update check", + zh_hans: "是否开启单个脚本更新检查" + }, + group: "Internal", + nodeType: "Subpage" + }, + // { + // name: "settings_sync", + // type: "boolean", + // local: true, + // default: false, + // protect: true, + // platforms: ["macos", "ipados", "ios"], + // langLabel: { + // en: "Sync settings", + // zh_hans: "同步设置" + // }, + // langTitle: { + // en: "Sync settings across devices", + // zh_hans: "跨设备同步设置" + // }, + // group: "General", + // nodeType: "Toggle" + // }, + { + name: "toolbar_badge_count", + type: "boolean", + default: true, + platforms: ["macos", "ipados"], + langLabel: { + en: "Show Toolbar Count", + zh_hans: "工具栏图标显示计数徽章" + }, + langTitle: { + en: "displays a badge on the toolbar icon with a number that represents how many enabled scripts match the url for the page you are on", + zh_hans: "简体中文描述" + }, + group: "General", + legacy: "showCount", + nodeType: "Toggle" + }, + { + name: "global_active", + type: "boolean", + local: true, + default: true, + platforms: ["macos"], + langLabel: { + en: "Enable Injection", + zh_hans: "启用注入" + }, + langTitle: { + en: "toggle on/off script injection for the pages you visit", + zh_hans: "简体中文描述" + }, + group: "General", + legacy: "active", + nodeType: "Toggle", + nodeClass: {red: false} + }, + { + name: "global_scripts_update_check", + type: "boolean", + default: true, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Global scripts update check", + zh_hans: "全局脚本更新检查" + }, + langTitle: { + en: "Whether to enable global periodic script update check", + zh_hans: "是否开启全局定期脚本更新检查" + }, + group: "General", + nodeType: "Toggle" + }, + { + name: "scripts_update_check_interval", + type: "number", + default: 86400000, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts update check interval", + zh_hans: "脚本更新检查间隔" + }, + langTitle: { + en: "The interval for script update check in background", + zh_hans: "脚本更新检查的间隔时间" + }, + group: "General", + nodeType: "Toggle" + }, + { + name: "scripts_update_check_lasttime", + type: "number", + default: 0, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts update check lasttime", + zh_hans: "脚本更新上次检查时间" + }, + langTitle: { + en: "The lasttime for script update check in background", + zh_hans: "后台脚本更新上次检查时间" + }, + group: "Internal", + nodeType: "Toggle" + }, + { + name: "scripts_auto_update", + type: "boolean", + default: false, + confirm: true, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts silent auto update", + zh_hans: "脚本后台静默自动更新" + }, + langTitle: { + en: "Script silently auto-updates in the background, which is dangerous and may introduce unconfirmed malicious code", + zh_hans: "脚本在后台静默自动更新,这是危险的,可能引入未经确认的恶意代码" + }, + group: "General", + nodeType: "Toggle", + nodeClass: {warn: true} + }, + { + name: "global_exclude_match", + type: "object", + default: [], + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Global exclude match patterns", + zh_hans: "全局排除匹配模式列表" + }, + langTitle: { + en: "this input accepts a comma separated list of @match patterns, a page url that matches against a pattern in this list will be ignored for script injection", + zh_hans: "简体中文描述" + }, + group: "General", + legacy: "blacklist", + nodeType: "textarea", + nodeClass: {red: "blacklistError"} + }, + { + name: "editor_close_brackets", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Auto Close Brackets", + zh_hans: "自动关闭括号" + }, + langTitle: { + en: "toggles on/off auto closing of brackets in the editor, this affects the following characters: () [] {} \"\" ''", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "autoCloseBrackets", + nodeType: "Toggle" + }, + { + name: "editor_auto_hint", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Auto Hint", + zh_hans: "自动提示(Hint)" + }, + langTitle: { + en: "automatically shows completion hints while editing", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "autoHint", + nodeType: "Toggle" + }, + { + name: "editor_list_sort", + type: "string", + values: ["nameAsc", "nameDesc", "lastModifiedAsc", "lastModifiedDesc"], + default: "lastModifiedDesc", + platforms: ["macos"], + langLabel: { + en: "Sort order", + zh_hans: "排序顺序" + }, + langTitle: { + en: "Display order of items in sidebar", + zh_hans: "侧栏中项目的显示顺序" + }, + group: "Editor", + legacy: "sortOrder", + nodeType: "Dropdown" + }, + { + name: "editor_list_descriptions", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Show List Descriptions", + zh_hans: "显示列表项目描述" + }, + langTitle: { + en: "show or hides the item descriptions in the sidebar", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "descriptions", + nodeType: "Toggle" + }, + { + name: "editor_javascript_lint", + type: "boolean", + default: false, + platforms: ["macos"], + langLabel: { + en: "Javascript Linter", + zh_hans: "Javascript Linter" + }, + langTitle: { + en: "toggles basic Javascript linting within the editor", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "lint", + nodeType: "Toggle" + }, + { + name: "editor_show_whitespace", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Show whitespace characters", + zh_hans: "显示空白字符" + }, + langTitle: { + en: "toggles the display of invisible characters in the editor", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "showInvisibles", + nodeType: "Toggle" + }, + { + name: "editor_tab_size", + type: "number", + values: [2, 4], + default: 4, + platforms: ["macos"], + langLabel: { + en: "Tab Size", + zh_hans: "制表符大小" + }, + langTitle: { + en: "the number of spaces a tab is equal to while editing", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "tabSize", + nodeType: "select" + } + ].reduce(settingsDefineReduceCallback, {})); + + // populate the settingsDefine with settingDefault + // and convert settingsDefine to storageKey object + function settingsDefineReduceCallback(settings, setting) { + setting.key = storageKey(setting.name); + settings[setting.key] = Object.assign({}, settingDefault, setting); + return settings; + } + + // prevent settings define from being modified in any case + // otherwise user settings may be lost in the worst case + function deepFreeze(object) { + for (const p in object) { + if (typeof object[p] == "object") { + deepFreeze(object[p]); + } + } + return Object.freeze(object); + } + + // export and define the operation method of settings storage + // they are similar to browser.storage but slightly different + + async function get(keys, area) { + if (![undefined, "local", "sync"].includes(area)) { + return console.error("Unexpected storage area:", area); + } + // validate setting value and fix surprises to default + const valueFix = (key, val) => { + if (!key || !Object.hasOwn(settingsDefine, key)) return; + const def = settingsDefine[key].default; + // check if value type conforms to settingsDefine + const type = settingsDefine[key].type; + if (typeof val != type) { + console.warn(`Unexpected ${key} value type '${typeof(val)}' should '${type}', fix to default`); + return def; + } + // check if value conforms to settingsDefine + const values = settingsDefine[key].values; + if (values.length && !values.includes(val)) { + console.warn(`Unexpected ${key} value '${val}' should one of '${values}', fix to default`); + return def; + } + // verified, pass original value + return val; + }; + if (typeof keys == "string") { // [single setting] + const key = storageKey(keys); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("unexpected settings key:", key); + } + // check if only locally stored setting + settingsDefine[key].local === true && (area = "local"); + const storage = await storageRef(); + const result = await storage.get(key); + if (Object.hasOwn(result, key)) { + return valueFix(key, result[key]); + } else { + return settingsDefine[key].default; + } + } + const complexGet = async (settingsDefault, areaKeys) => { + const storage = await storageRef(); + let local = {}, sync = {}; + if (storage.area === "sync") { + if (areaKeys.sync.length) { + sync = await storage.get(areaKeys.sync); + } + if (areaKeys.local.length) { + const storage = await storageRef(); + local = await storage.get(areaKeys.local); + } + } else { + local = await storage.get(areaKeys.all); + } + const result = Object.assign(settingsDefault, local, sync); + // revert settings object property name + return Object.entries(result).reduce((p, c) => ( + p[settingsDefine[c[0]].name] = valueFix(...c), p + ), {}); + }; + if (Array.isArray(keys)) { // [muilt settings] + if (!keys.length) { + return console.error("Settings keys empty:", keys); + } + const settingsDefault = {}; + const areaKeys = {local: [], sync: [], all: []}; + for (const k of keys) { + const key = storageKey(k); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("unexpected settings key:", key); + } + settingsDefault[key] = settingsDefine[key].default; + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local.push(key) + : areaKeys.sync.push(key); + // record all keys in case sync storage is not enabled + areaKeys.all.push(key); + } + return await complexGet(settingsDefault, areaKeys); + } + if (typeof keys == "undefined" || keys === null) { // [all settings] + const settingsDefault = {}; + const areaKeys = {local: [], sync: [], all: []}; + for (const key in settingsDefine) { + settingsDefault[key] = settingsDefine[key].default; + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local.push(key) + : areaKeys.sync.push(key); + // record all keys in case sync storage is not enabled + areaKeys.all.push(key); + } + return await complexGet(settingsDefault, areaKeys); + } + return console.error("Unexpected keys type:", keys); + } + + async function set(keys, area) { + if (![undefined, "local", "sync"].includes(area)) { + return console.error("unexpected storage area:", area); + } + if (typeof keys != "object") { + return console.error("Unexpected keys type:", keys); + } + if (!Object.keys(keys).length) { + return console.error("Settings object empty:", keys); + } + const areaKeys = {local: {}, sync: {}, all: {}}; + for (const k in keys) { + const key = storageKey(k); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("Unexpected settings keys:", key); + } + // check if value type conforms to settingsDefine + const type = settingsDefine[key].type; + if (typeof keys[k] != type) { + if (type === "number" && !isNaN(keys[k])) { // compatible with string numbers + keys[k] = Number(keys[k]); // still store it as a number type + } else { + return console.error(`Unexpected ${k} value type '${typeof(keys[k])}' should '${type}'`); + } + } + // check if value conforms to settingsDefine + const values = settingsDefine[key].values; + if (values.length && !values.includes(keys[k])) { + return console.error(`Unexpected ${k} value '${keys[k]}' should one of '${values}'`); + } + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local[key] = keys[k] + : areaKeys.sync[key] = keys[k]; + // record all keys in case sync storage is not enabled + areaKeys.all[key] = keys[k]; + } + const storage = await storageRef(); + // complexSet + try { + if (storage.area === "sync") { + if (Object.keys(areaKeys.sync).length) { + await storage.set(areaKeys.sync); + } + if (Object.keys(areaKeys.local).length) { + const storage = await storageRef("local"); + await storage.set(areaKeys.local); + } + } else { + await storage.set(areaKeys.all); + } + return true; + } catch (error) { + return console.error(error); + } + } + + async function reset(keys, area) { // reset to default + if (![undefined, "local", "sync"].includes(area)) { + return console.error("unexpected storage area:", area); + } + if (typeof keys == "string") { // [single setting] + const key = storageKey(keys); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("unexpected settings key:", key); + } + // check if key is protected + if (settingsDefine[key].protect === true) { + return console.error("protected settings key:", key); + } + settingsDefine[key].local === true && (area = "local"); + const storage = await storageRef(); + return storage.remove(key); + } + const complexRemove = async areaKeys => { + const storage = await storageRef(); + try { + if (storage.area === "sync") { + if (areaKeys.sync.length) { + await storage.remove(areaKeys.sync); + } + if (areaKeys.local.length) { + const storage = await storageRef("local"); + await storage.remove(areaKeys.local); + } + } else { + await storage.remove(areaKeys.all); + } + return true; + } catch (error) { + return console.error(error); + } + }; + if (Array.isArray(keys)) { // [muilt settings] + if (!keys.length) { + return console.error("Settings keys empty:", keys); + } + const areaKeys = {local: [], sync: [], all: []}; + for (const k of keys) { + const key = storageKey(k); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("unexpected settings key:", key); + } + // check if key is protected + if (settingsDefine[key].protect === true) { + return console.error("protected settings key:", key); + } + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local.push(key) + : areaKeys.sync.push(key); + // record all keys in case sync storage is not enabled + areaKeys.all.push(key); + } + return await complexRemove(areaKeys); + } + if (typeof keys == "undefined" || keys === null) { // [all settings] + const areaKeys = {local: [], sync: [], all: []}; + for (const key in settingsDefine) { + // skip protected keys + if (settingsDefine[key].protect === true) continue; + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local.push(key) + : areaKeys.sync.push(key); + // record all keys in case sync storage is not enabled + areaKeys.all.push(key); + } + return await complexRemove(areaKeys); + } + return console.error("Unexpected keys type:", keys); + } + + // this function is convenient for the svelte store to update the state + function onChanged(callback) { // complex onChanged + if (typeof callback != "function") { + return console.error("Unexpected callback:", callback); + } + console.info("storage onChanged addListener"); + const handle = (changes, area) => { + // console.log(`storage.${area}.onChanged`, changes); + try { + const settings = {}; + for (const key in changes) { + if (!Object.hasOwn(settingsDefine, key)) continue; + settings[settingsDefine[key].name] = changes[key].newValue; + } + callback(settings, area); + } catch (error) { + console.error("onChanged callback:", error); + } + }; + // comment for now same reason as `storageRef` function + // browser.storage.sync.onChanged.addListener(c => handle(c, "sync")); + browser.storage.local.onChanged.addListener(c => handle(c, "local")); + } + + // the following functions are used only for compatibility transition periods + // they are deliberately named in snake_case format + // they will be deleted at some point in the future + + async function legacy_get(keys) { + const result = await get(keys); + // console.log("legacy_get", keys, result); + for (const key in result) { + const legacy = settingsDefine[storageKey(key)]?.legacy; + if (legacy) result[legacy] = result[key]; + } + return result; + } + + async function legacy_set(keys) { + if (typeof keys != "object") { + return console.error("Unexpected arg type:", keys); + } + if (!Object.keys(keys).length) { + return console.error("Settings object empty:", keys); + } + const settings = {}; + for (const key in settingsDefine) { + const setting = settingsDefine[key]; + if (!setting.legacy) continue; + if (setting.legacy in keys) { + settings[setting.name] = keys[setting.legacy]; + } + } + // console.log("legacy_set", keys, settings); + return await set(settings); + } + + async function legacy_import() { + // if legacy data has already been imported, skip this process + const imported = await get("legacy_imported"); + if (imported) return console.info("Legacy settings has already imported"); + // start the one-time import process + const result = await browser.runtime.sendNativeMessage({name: "PAGE_LEGACY_IMPORT"}); + if (result.error) return console.error(result.error); + console.info("Import settings data from legacy manifest file"); + const settings = {}; + for (const key in settingsDefine) { + const legacy = settingsDefine[key].legacy; + if (legacy in result) { + let value = result[legacy]; + switch (settingsDefine[key].type) { + case "boolean": value = JSON.parse(value); break; + case "number": value = Number(value); break; + } + console.log(`Importing legacy setting: ${legacy}`, value); + settings[settingsDefine[key].name] = value; + } + } + // import complete tag, to ensure will only be import once + Object.assign(settings, {"legacy_imported": Date.now()}); + if (await set(settings, "local")) { + console.info("Import legacy settings complete"); + // send a message to the Swift layer to safely clean up legacy data + // browser.runtime.sendNativeMessage({name: "PAGE_LEGACY_IMPORTED"}); + return true; + } else { + return console.error("Import legacy settings abort"); + } + } + function notificationStore() { const {subscribe, update} = writable([]); const add = item => { @@ -1242,40 +1925,48 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o function settingsStore() { const {subscribe, update, set} = writable({}); - const updateSingleSetting = (key, value) => { - update(settings => { - settings[key] = value; - // blacklist not stored in normal setting object in manifest, so handle differently - if (key === "blacklist") { - // update blacklist on swift side - const message = {name: "PAGE_UPDATE_BLACKLIST", blacklist: value}; - browser.runtime.sendNativeMessage(message, response => { - if (response.error) { - log.add("Failed to save blacklist to disk", "error", true); - } - }); - return settings; - } - // settings are saved as strings on the swift side - // convert all booleans to strings before dispatching - const settingsClone = {...settings}; - for (const [key, value] of Object.entries(settingsClone)) { - if (typeof value === "boolean") settingsClone[key] = value.toString(); - } - // remove settings in clone that aren't save in user defaults - delete settingsClone.blacklist; - delete settingsClone.version; - // update settings on swift side - const message = {name: "PAGE_UPDATE_SETTINGS", settings: settingsClone}; + const init = async initData => { + // import legacy settings data just one-time + await legacy_import(); + // for compatibility with legacy getting names only + // once all new name is used, use settingsStorage.get() + const settings = await legacy_get(); + console.info("store.js settingsStore init", initData, settings); + set(Object.assign({}, initData, settings)); + // sync popup, backgound, etc... settings changes + onChanged((settings, area) => { + console.log(`store.js storage.${area}.onChanged`, settings); + update(obj => Object.assign(obj, settings)); + }); + }; + const reset$1 = async keys => { + await reset(keys); + // once all new name is used, use settingsStorage.get() + const settings = await legacy_get(); + console.info("store.js settingsStore reset", settings); + update(obj => Object.assign(obj, settings)); + }; + const updateSingleSetting_old = (key, value) => { + // blacklist not stored in normal setting object in manifest, so handle differently + if (key === "blacklist") { + // update blacklist on swift side + const message = {name: "PAGE_UPDATE_BLACKLIST", blacklist: value}; browser.runtime.sendNativeMessage(message, response => { if (response.error) { - log.add(response.error, "error", true); + log.add("Failed to save blacklist to disk", "error", true); } }); - return settings; - }); + } }; - return {subscribe, set, updateSingleSetting}; + const updateSingleSetting = (key, value) => { + update(settings => (settings[key] = value, settings)); + // for compatibility with legacy setting names only + // once all new name is used, use settingsStorage.set() + legacy_set({[key]: value}); // Durable Storage + // temporarily keep the old storage method until it is confirmed that all dependencies are removed + updateSingleSetting_old(key, value); + }; + return {subscribe, set, init, reset: reset$1, updateSingleSetting}; } const settings = settingsStore(); @@ -19419,107 +20110,111 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } function create_fragment$b(ctx) { + let div31; + let div0; + let t0; let div30; - let div29; - let div14; + let div15; + let div2; let div1; - let div0; - let t1; - let iconbutton0; let t2; + let iconbutton0; + let t3; + let div4; let div3; - let div2; - let t4; - let toggle0; let t5; + let toggle0; + let t6; + let div6; let div5; - let div4; - let t7; - let toggle1; let t8; + let toggle1; + let t9; + let div8; let div7; - let div6; - let t10; - let toggle2; let t11; + let toggle2; + let t12; + let div10; let div9; - let div8; - let t13; - let toggle3; let t14; + let toggle3; + let t15; + let div12; let div11; - let div10; - let t16; - let toggle4; let t17; + let toggle4; + let t18; + let div14; let div13; - let div12; - let t19; + let t20; let select; let option0; + let option0_value_value; let option1; - let t22; - let div26; - let div16; - let t24; - let div18; + let option1_value_value; + let t23; + let div27; let div17; - let t26; - let toggle5; + let t25; + let div19; + let div18; let t27; + let toggle5; + let t28; + let div21; let div20; - let div19; - let t29; - let toggle6; let t30; - let div23; - let div21; - let t32; + let toggle6; + let t31; + let div24; let div22; - let t33_value = /*$settings*/ ctx[0].saveLocation + ""; let t33; + let div23; + let t34_value = /*$settings*/ ctx[0].saveLocation + ""; let t34; - let iconbutton1; let t35; + let iconbutton1; + let t36; + let div26; let div25; - let div24; let span; - let t37; let t38; + let t39; let textarea; let textarea_disabled_value; - let t39; + let t40; + let div29; let div28; - let div27; - let t41; - let p; let t42; - let t43_value = /*$settings*/ ctx[0].version + ""; + let p; let t43; + let t44_value = /*$settings*/ ctx[0].version + ""; let t44; - let t45_value = /*$settings*/ ctx[0].build + ""; let t45; + let t46_value = /*$settings*/ ctx[0].build + ""; let t46; + let t47; let br0; let br1; - let t47; + let t48; let a0; let br2; let br3; - let t49; + let t50; let a1; - let t51; + let t52; let a2; - let t53; - let div29_intro; - let div29_outro; + let t54; let div30_intro; let div30_outro; + let div31_intro; + let div31_outro; let current; let mounted; let dispose; iconbutton0 = new IconButton({ props: { icon: iconClose } }); - iconbutton0.$on("click", /*click_handler*/ ctx[8]); + iconbutton0.$on("click", /*click_handler_1*/ ctx[9]); toggle0 = new Toggle({ props: { @@ -19527,13 +20222,13 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } }); - toggle0.$on("click", /*click_handler_1*/ ctx[9]); + toggle0.$on("click", /*click_handler_2*/ ctx[10]); toggle1 = new Toggle({ props: { checked: /*$settings*/ ctx[0].autoHint } }); - toggle1.$on("click", /*click_handler_2*/ ctx[10]); + toggle1.$on("click", /*click_handler_3*/ ctx[11]); toggle2 = new Toggle({ props: { @@ -19541,13 +20236,13 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } }); - toggle2.$on("click", /*click_handler_3*/ ctx[11]); + toggle2.$on("click", /*click_handler_4*/ ctx[12]); toggle3 = new Toggle({ props: { checked: /*$settings*/ ctx[0].lint } }); - toggle3.$on("click", /*click_handler_4*/ ctx[12]); + toggle3.$on("click", /*click_handler_5*/ ctx[13]); toggle4 = new Toggle({ props: { @@ -19555,19 +20250,19 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } }); - toggle4.$on("click", /*click_handler_5*/ ctx[13]); + toggle4.$on("click", /*click_handler_6*/ ctx[14]); toggle5 = new Toggle({ props: { checked: /*$settings*/ ctx[0].active } }); - toggle5.$on("click", /*click_handler_6*/ ctx[16]); + toggle5.$on("click", /*click_handler_7*/ ctx[17]); toggle6 = new Toggle({ props: { checked: /*$settings*/ ctx[0].showCount } }); - toggle6.$on("click", /*click_handler_7*/ ctx[17]); + toggle6.$on("click", /*click_handler_8*/ ctx[18]); iconbutton1 = new IconButton({ props: { @@ -19581,262 +20276,267 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o return { c() { + div31 = element("div"); + div0 = element("div"); + t0 = space(); div30 = element("div"); - div29 = element("div"); - div14 = element("div"); + div15 = element("div"); + div2 = element("div"); div1 = element("div"); - div0 = element("div"); - div0.textContent = "Editor Settings"; - t1 = space(); - create_component(iconbutton0.$$.fragment); + div1.textContent = "Editor Settings"; t2 = space(); + create_component(iconbutton0.$$.fragment); + t3 = space(); + div4 = element("div"); div3 = element("div"); - div2 = element("div"); - div2.textContent = "Auto Close Brackets"; - t4 = space(); - create_component(toggle0.$$.fragment); + div3.textContent = "Auto Close Brackets"; t5 = space(); + create_component(toggle0.$$.fragment); + t6 = space(); + div6 = element("div"); div5 = element("div"); - div4 = element("div"); - div4.textContent = "Auto Hint"; - t7 = space(); - create_component(toggle1.$$.fragment); + div5.textContent = "Auto Hint"; t8 = space(); + create_component(toggle1.$$.fragment); + t9 = space(); + div8 = element("div"); div7 = element("div"); - div6 = element("div"); - div6.textContent = "Hide Descriptions"; - t10 = space(); - create_component(toggle2.$$.fragment); + div7.textContent = "Hide Descriptions"; t11 = space(); + create_component(toggle2.$$.fragment); + t12 = space(); + div10 = element("div"); div9 = element("div"); - div8 = element("div"); - div8.textContent = "Javascript Linter"; - t13 = space(); - create_component(toggle3.$$.fragment); + div9.textContent = "Javascript Linter"; t14 = space(); + create_component(toggle3.$$.fragment); + t15 = space(); + div12 = element("div"); div11 = element("div"); - div10 = element("div"); - div10.textContent = "Show Invisibles"; - t16 = space(); - create_component(toggle4.$$.fragment); + div11.textContent = "Show Invisibles"; t17 = space(); + create_component(toggle4.$$.fragment); + t18 = space(); + div14 = element("div"); div13 = element("div"); - div12 = element("div"); - div12.textContent = "Tab Size"; - t19 = space(); + div13.textContent = "Tab Size"; + t20 = space(); select = element("select"); option0 = element("option"); option0.textContent = "2"; option1 = element("option"); option1.textContent = "4"; - t22 = space(); - div26 = element("div"); - div16 = element("div"); - div16.innerHTML = `
General Settings
`; - t24 = space(); - div18 = element("div"); + t23 = space(); + div27 = element("div"); div17 = element("div"); - div17.textContent = "Enable Injection"; - t26 = space(); - create_component(toggle5.$$.fragment); + div17.innerHTML = `
General Settings
`; + t25 = space(); + div19 = element("div"); + div18 = element("div"); + div18.textContent = "Enable Injection"; t27 = space(); + create_component(toggle5.$$.fragment); + t28 = space(); + div21 = element("div"); div20 = element("div"); - div19 = element("div"); - div19.textContent = "Show Toolbar Count"; - t29 = space(); - create_component(toggle6.$$.fragment); + div20.textContent = "Show Toolbar Count"; t30 = space(); - div23 = element("div"); - div21 = element("div"); - div21.textContent = "Save Location"; - t32 = space(); + create_component(toggle6.$$.fragment); + t31 = space(); + div24 = element("div"); div22 = element("div"); - t33 = text(t33_value); - t34 = space(); - create_component(iconbutton1.$$.fragment); + div22.textContent = "Save Location"; + t33 = space(); + div23 = element("div"); + t34 = text(t34_value); t35 = space(); + create_component(iconbutton1.$$.fragment); + t36 = space(); + div26 = element("div"); div25 = element("div"); - div24 = element("div"); span = element("span"); - span.textContent = "Global Blacklist"; - t37 = space(); - if (if_block) if_block.c(); + span.textContent = "Global exclude match patterns"; t38 = space(); - textarea = element("textarea"); + if (if_block) if_block.c(); t39 = space(); + textarea = element("textarea"); + t40 = space(); + div29 = element("div"); div28 = element("div"); - div27 = element("div"); - div27.textContent = "Information"; - t41 = space(); + div28.textContent = "Information"; + t42 = space(); p = element("p"); - t42 = text("Userscripts Safari Version "); - t43 = text(t43_value); - t44 = text(" ("); - t45 = text(t45_value); - t46 = text(")"); + t43 = text("Userscripts Safari Version "); + t44 = text(t44_value); + t45 = text(" ("); + t46 = text(t46_value); + t47 = text(")"); br0 = element("br"); br1 = element("br"); - t47 = text("You can review the documentation, report bugs and get more information about this extension by visiting "); + t48 = text("You can review the documentation, report bugs and get more information about this extension by visiting "); a0 = element("a"); a0.textContent = "the code repository."; br2 = element("br"); br3 = element("br"); - t49 = text("If you enjoy using this extension, please consider "); + t50 = text("If you enjoy using this extension, please consider "); a1 = element("a"); a1.textContent = "leaving a review"; - t51 = text(" on the App Store or "); + t52 = text(" on the App Store or "); a2 = element("a"); a2.textContent = "supporting the project"; - t53 = text("."); - attr(div0, "class", "svelte-krvk46"); - attr(div1, "class", "modal__title svelte-krvk46"); - attr(div2, "class", "svelte-krvk46"); - attr(div3, "class", "modal__row svelte-krvk46"); - attr(div4, "class", "svelte-krvk46"); - attr(div5, "class", "modal__row svelte-krvk46"); - attr(div6, "class", "svelte-krvk46"); - attr(div7, "class", "modal__row svelte-krvk46"); - attr(div8, "class", "svelte-krvk46"); - attr(div9, "class", "modal__row svelte-krvk46"); - attr(div10, "class", "svelte-krvk46"); - attr(div11, "class", "modal__row svelte-krvk46"); - attr(div12, "class", "svelte-krvk46"); - option0.__value = "2"; + t54 = text("."); + attr(div0, "class", "mask svelte-1d6jb7s"); + attr(div1, "class", "svelte-1d6jb7s"); + attr(div2, "class", "modal__title svelte-1d6jb7s"); + attr(div3, "class", "svelte-1d6jb7s"); + attr(div4, "class", "modal__row svelte-1d6jb7s"); + attr(div5, "class", "svelte-1d6jb7s"); + attr(div6, "class", "modal__row svelte-1d6jb7s"); + attr(div7, "class", "svelte-1d6jb7s"); + attr(div8, "class", "modal__row svelte-1d6jb7s"); + attr(div9, "class", "svelte-1d6jb7s"); + attr(div10, "class", "modal__row svelte-1d6jb7s"); + attr(div11, "class", "svelte-1d6jb7s"); + attr(div12, "class", "modal__row svelte-1d6jb7s"); + attr(div13, "class", "svelte-1d6jb7s"); + option0.__value = option0_value_value = 2; option0.value = option0.__value; - option1.__value = "4"; + option1.__value = option1_value_value = 4; option1.value = option1.__value; - if (/*$settings*/ ctx[0].tabSize === void 0) add_render_callback(() => /*select_change_handler*/ ctx[14].call(select)); - attr(div13, "class", "modal__row svelte-krvk46"); - attr(div14, "class", "modal__section"); - attr(div16, "class", "modal__title svelte-krvk46"); - attr(div17, "class", "svelte-krvk46"); - toggle_class(div17, "red", !/*$settings*/ ctx[0].active); - attr(div18, "class", "modal__row svelte-krvk46"); - attr(div19, "class", "svelte-krvk46"); - attr(div20, "class", "modal__row svelte-krvk46"); - attr(div21, "class", "svelte-krvk46"); - attr(div22, "class", "truncate svelte-krvk46"); - attr(div23, "class", "modal__row saveLocation svelte-krvk46"); - attr(div24, "class", "blacklist svelte-krvk46"); - toggle_class(div24, "red", /*blacklistError*/ ctx[3]); + if (/*$settings*/ ctx[0].tabSize === void 0) add_render_callback(() => /*select_change_handler*/ ctx[15].call(select)); + attr(div14, "class", "modal__row svelte-1d6jb7s"); + attr(div15, "class", "modal__section"); + attr(div17, "class", "modal__title svelte-1d6jb7s"); + attr(div18, "class", "svelte-1d6jb7s"); + toggle_class(div18, "red", !/*$settings*/ ctx[0].active); + attr(div19, "class", "modal__row svelte-1d6jb7s"); + attr(div20, "class", "svelte-1d6jb7s"); + attr(div21, "class", "modal__row svelte-1d6jb7s"); + attr(div22, "class", "svelte-1d6jb7s"); + attr(div23, "class", "truncate svelte-1d6jb7s"); + attr(div24, "class", "modal__row saveLocation svelte-1d6jb7s"); + attr(div25, "class", "blacklist svelte-1d6jb7s"); + toggle_class(div25, "red", /*blacklistError*/ ctx[3]); attr(textarea, "placeholder", "Comma separated list of @match patterns"); attr(textarea, "spellcheck", "false"); textarea.value = /*blacklisted*/ ctx[4]; textarea.disabled = textarea_disabled_value = /*$state*/ ctx[5].includes("blacklist-saving") || /*blacklistSaving*/ ctx[2]; - attr(textarea, "class", "svelte-krvk46"); + attr(textarea, "class", "svelte-1d6jb7s"); toggle_class(textarea, "error", /*blacklistError*/ ctx[3]); - attr(div25, "class", "modal__row modal__row--wrap svelte-krvk46"); - attr(div26, "class", "modal__section"); - attr(div27, "class", "modal__title svelte-krvk46"); + attr(div26, "class", "modal__row modal__row--wrap svelte-1d6jb7s"); + attr(div27, "class", "modal__section"); + attr(div28, "class", "modal__title svelte-1d6jb7s"); attr(a0, "href", "https://github.com/quoid/userscripts"); attr(a1, "href", "https://apps.apple.com/us/app/userscripts/id1463298887"); attr(a2, "href", "https://github.com/quoid/userscripts#support"); - attr(p, "class", "svelte-krvk46"); - attr(div28, "class", "modal__section"); - attr(div29, "class", "modal svelte-krvk46"); - attr(div30, "class", "settings svelte-krvk46"); + attr(p, "class", "svelte-1d6jb7s"); + attr(div29, "class", "modal__section"); + attr(div30, "class", "modal svelte-1d6jb7s"); + attr(div31, "class", "settings svelte-1d6jb7s"); }, m(target, anchor) { - insert(target, div30, anchor); - append(div30, div29); - append(div29, div14); - append(div14, div1); - append(div1, div0); - append(div1, t1); - mount_component(iconbutton0, div1, null); - append(div14, t2); - append(div14, div3); - append(div3, div2); - append(div3, t4); - mount_component(toggle0, div3, null); - append(div14, t5); - append(div14, div5); - append(div5, div4); - append(div5, t7); - mount_component(toggle1, div5, null); - append(div14, t8); - append(div14, div7); - append(div7, div6); - append(div7, t10); - mount_component(toggle2, div7, null); - append(div14, t11); - append(div14, div9); - append(div9, div8); - append(div9, t13); - mount_component(toggle3, div9, null); - append(div14, t14); - append(div14, div11); - append(div11, div10); - append(div11, t16); - mount_component(toggle4, div11, null); - append(div14, t17); + insert(target, div31, anchor); + append(div31, div0); + append(div31, t0); + append(div31, div30); + append(div30, div15); + append(div15, div2); + append(div2, div1); + append(div2, t2); + mount_component(iconbutton0, div2, null); + append(div15, t3); + append(div15, div4); + append(div4, div3); + append(div4, t5); + mount_component(toggle0, div4, null); + append(div15, t6); + append(div15, div6); + append(div6, div5); + append(div6, t8); + mount_component(toggle1, div6, null); + append(div15, t9); + append(div15, div8); + append(div8, div7); + append(div8, t11); + mount_component(toggle2, div8, null); + append(div15, t12); + append(div15, div10); + append(div10, div9); + append(div10, t14); + mount_component(toggle3, div10, null); + append(div15, t15); + append(div15, div12); + append(div12, div11); + append(div12, t17); + mount_component(toggle4, div12, null); + append(div15, t18); + append(div15, div14); append(div14, div13); - append(div13, div12); - append(div13, t19); - append(div13, select); + append(div14, t20); + append(div14, select); append(select, option0); append(select, option1); select_option(select, /*$settings*/ ctx[0].tabSize); - append(div29, t22); - append(div29, div26); - append(div26, div16); - append(div26, t24); - append(div26, div18); - append(div18, div17); - append(div18, t26); - mount_component(toggle5, div18, null); - append(div26, t27); - append(div26, div20); - append(div20, div19); - append(div20, t29); - mount_component(toggle6, div20, null); - append(div26, t30); - append(div26, div23); - append(div23, div21); - append(div23, t32); - append(div23, div22); - append(div22, t33); + append(div30, t23); + append(div30, div27); + append(div27, div17); + append(div27, t25); + append(div27, div19); + append(div19, div18); + append(div19, t27); + mount_component(toggle5, div19, null); + append(div27, t28); + append(div27, div21); + append(div21, div20); + append(div21, t30); + mount_component(toggle6, div21, null); + append(div27, t31); + append(div27, div24); + append(div24, div22); + append(div24, t33); + append(div24, div23); append(div23, t34); - mount_component(iconbutton1, div23, null); - append(div26, t35); + append(div24, t35); + mount_component(iconbutton1, div24, null); + append(div27, t36); + append(div27, div26); append(div26, div25); - append(div25, div24); - append(div24, span); - append(div24, t37); - if (if_block) if_block.m(div24, null); + append(div25, span); append(div25, t38); - append(div25, textarea); - /*textarea_binding*/ ctx[18](textarea); - append(div29, t39); + if (if_block) if_block.m(div25, null); + append(div26, t39); + append(div26, textarea); + /*textarea_binding*/ ctx[19](textarea); + append(div30, t40); + append(div30, div29); append(div29, div28); - append(div28, div27); - append(div28, t41); - append(div28, p); - append(p, t42); + append(div29, t42); + append(div29, p); append(p, t43); append(p, t44); append(p, t45); append(p, t46); + append(p, t47); append(p, br0); append(p, br1); - append(p, t47); + append(p, t48); append(p, a0); append(p, br2); append(p, br3); - append(p, t49); + append(p, t50); append(p, a1); - append(p, t51); + append(p, t52); append(p, a2); - append(p, t53); + append(p, t54); current = true; if (!mounted) { dispose = [ - listen(select, "change", /*select_change_handler*/ ctx[14]), - listen(select, "blur", /*blur_handler*/ ctx[15]), - listen(div22, "click", openSaveLocation), - listen(textarea, "blur", /*saveBlacklist*/ ctx[6]), - listen(div30, "mousedown", self$1(/*mousedown_handler*/ ctx[19])) + listen(div0, "click", self$1(/*click_handler*/ ctx[8])), + listen(select, "change", /*select_change_handler*/ ctx[15]), + listen(select, "blur", /*blur_handler*/ ctx[16]), + listen(div23, "click", openSaveLocation), + listen(textarea, "blur", /*saveBlacklist*/ ctx[6]) ]; mounted = true; @@ -19864,7 +20564,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } if (dirty & /*$settings*/ 1) { - toggle_class(div17, "red", !/*$settings*/ ctx[0].active); + toggle_class(div18, "red", !/*$settings*/ ctx[0].active); } const toggle5_changes = {}; @@ -19873,7 +20573,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o const toggle6_changes = {}; if (dirty & /*$settings*/ 1) toggle6_changes.checked = /*$settings*/ ctx[0].showCount; toggle6.$set(toggle6_changes); - if ((!current || dirty & /*$settings*/ 1) && t33_value !== (t33_value = /*$settings*/ ctx[0].saveLocation + "")) set_data(t33, t33_value); + if ((!current || dirty & /*$settings*/ 1) && t34_value !== (t34_value = /*$settings*/ ctx[0].saveLocation + "")) set_data(t34, t34_value); if (/*blacklistSaving*/ ctx[2]) { if (if_block) { @@ -19881,7 +20581,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } else { if_block = create_if_block$7(); if_block.c(); - if_block.m(div24, null); + if_block.m(div25, null); } } else if (if_block) { if_block.d(1); @@ -19889,7 +20589,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } if (dirty & /*blacklistError*/ 8) { - toggle_class(div24, "red", /*blacklistError*/ ctx[3]); + toggle_class(div25, "red", /*blacklistError*/ ctx[3]); } if (!current || dirty & /*blacklisted*/ 16) { @@ -19904,8 +20604,8 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o toggle_class(textarea, "error", /*blacklistError*/ ctx[3]); } - if ((!current || dirty & /*$settings*/ 1) && t43_value !== (t43_value = /*$settings*/ ctx[0].version + "")) set_data(t43, t43_value); - if ((!current || dirty & /*$settings*/ 1) && t45_value !== (t45_value = /*$settings*/ ctx[0].build + "")) set_data(t45, t45_value); + if ((!current || dirty & /*$settings*/ 1) && t44_value !== (t44_value = /*$settings*/ ctx[0].version + "")) set_data(t44, t44_value); + if ((!current || dirty & /*$settings*/ 1) && t46_value !== (t46_value = /*$settings*/ ctx[0].build + "")) set_data(t46, t46_value); }, i(local) { if (current) return; @@ -19920,15 +20620,15 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o transition_in(iconbutton1.$$.fragment, local); add_render_callback(() => { - if (div29_outro) div29_outro.end(1); - div29_intro = create_in_transition(div29, fly, { y: 50, duration: 150, delay: 75 }); - div29_intro.start(); + if (div30_outro) div30_outro.end(1); + div30_intro = create_in_transition(div30, fly, { y: 50, duration: 150, delay: 75 }); + div30_intro.start(); }); add_render_callback(() => { - if (div30_outro) div30_outro.end(1); - div30_intro = create_in_transition(div30, fade, { duration: 150 }); - div30_intro.start(); + if (div31_outro) div31_outro.end(1); + div31_intro = create_in_transition(div31, fade, { duration: 150 }); + div31_intro.start(); }); current = true; @@ -19943,14 +20643,14 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o transition_out(toggle5.$$.fragment, local); transition_out(toggle6.$$.fragment, local); transition_out(iconbutton1.$$.fragment, local); - if (div29_intro) div29_intro.invalidate(); - div29_outro = create_out_transition(div29, fly, { y: 50, duration: 150, delay: 0 }); if (div30_intro) div30_intro.invalidate(); - div30_outro = create_out_transition(div30, fade, { duration: 150, delay: 75 }); + div30_outro = create_out_transition(div30, fly, { y: 50, duration: 150, delay: 0 }); + if (div31_intro) div31_intro.invalidate(); + div31_outro = create_out_transition(div31, fade, { duration: 150, delay: 75 }); current = false; }, d(detaching) { - if (detaching) detach(div30); + if (detaching) detach(div31); destroy_component(iconbutton0); destroy_component(toggle0); destroy_component(toggle1); @@ -19961,9 +20661,9 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o destroy_component(toggle6); destroy_component(iconbutton1); if (if_block) if_block.d(); - /*textarea_binding*/ ctx[18](null); - if (detaching && div29_outro) div29_outro.end(); + /*textarea_binding*/ ctx[19](null); if (detaching && div30_outro) div30_outro.end(); + if (detaching && div31_outro) div31_outro.end(); mounted = false; run_all(dispose); } @@ -20015,10 +20715,11 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o for (const v of val) { if (re.exec(v) === null) { $$invalidate(3, blacklistError = true); - return console.warn("Global blacklist has wrong pattern:", v); + log.add(`Invalid match pattern: ${v}`, "error", true); } } + if (blacklistError) return console.warn("Global exclude includes invalid match patterns"); $$invalidate(3, blacklistError = false); // compare blacklist input to saved blacklist @@ -20040,11 +20741,12 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } const click_handler = () => state.remove("settings"); - const click_handler_1 = () => update("autoCloseBrackets", !$settings.autoCloseBrackets); - const click_handler_2 = () => update("autoHint", !$settings.autoHint); - const click_handler_3 = () => update("descriptions", !$settings.descriptions); - const click_handler_4 = () => update("lint", !$settings.lint); - const click_handler_5 = () => update("showInvisibles", !$settings.showInvisibles); + const click_handler_1 = () => state.remove("settings"); + const click_handler_2 = () => update("autoCloseBrackets", !$settings.autoCloseBrackets); + const click_handler_3 = () => update("autoHint", !$settings.autoHint); + const click_handler_4 = () => update("descriptions", !$settings.descriptions); + const click_handler_5 = () => update("lint", !$settings.lint); + const click_handler_6 = () => update("showInvisibles", !$settings.showInvisibles); function select_change_handler() { $settings.tabSize = select_value(this); @@ -20052,8 +20754,8 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o } const blur_handler = () => update("tabSize", $settings.tabSize); - const click_handler_6 = () => update("active", !$settings.active); - const click_handler_7 = () => update("showCount", !$settings.showCount); + const click_handler_7 = () => update("active", !$settings.active); + const click_handler_8 = () => update("showCount", !$settings.showCount); function textarea_binding($$value) { binding_callbacks[$$value ? 'unshift' : 'push'](() => { @@ -20062,8 +20764,6 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o }); } - const mousedown_handler = () => state.remove("settings"); - $$self.$$.update = () => { if ($$self.$$.dirty & /*$settings*/ 1) { // the saved blacklisted domain patterns @@ -20086,12 +20786,12 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o click_handler_3, click_handler_4, click_handler_5, + click_handler_6, select_change_handler, blur_handler, - click_handler_6, click_handler_7, - textarea_binding, - mousedown_handler + click_handler_8, + textarea_binding ]; } @@ -20380,7 +21080,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o return child_ctx; } - // (65:0) {#if $state.includes("init")} + // (60:0) {#if $state.includes("init")} function create_if_block_1$2(ctx) { let div; let html_tag; @@ -20443,7 +21143,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o }; } - // (70:8) {:else} + // (65:8) {:else} function create_else_block$1(ctx) { let span; @@ -20462,7 +21162,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o }; } - // (68:8) {#if $state.includes("init-error")} + // (63:8) {#if $state.includes("init-error")} function create_if_block_2$1(ctx) { let span; @@ -20481,7 +21181,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o }; } - // (80:4) {#each $notifications as item (item.id)} + // (75:4) {#each $notifications as item (item.id)} function create_each_block$1(key_1, ctx) { let first; let notification; @@ -20529,7 +21229,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o }; } - // (84:0) {#if $state.includes("settings")} + // (79:0) {#if $state.includes("settings")} function create_if_block$8(ctx) { let settings_1; let current; @@ -20777,14 +21477,7 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o log.add("Requesting initialization data", "info", false); const initData = await browser.runtime.sendNativeMessage({ name: "PAGE_INIT_DATA" }); if (initData.error) return console.error(initData.error); - - for (const [key, value] of Object.entries(initData)) { - if (value === "true" || value === "false") { - initData[key] = JSON.parse(value); - } - } - - settings.set(initData); + await settings.init(initData); state.add("items-loading"); state.remove("init"); log.add("Requesting all files in save location", "info", false); diff --git a/extension/Userscripts Extension/Resources/popup.js b/extension/Userscripts Extension/Resources/popup.js index 90817eae..0a53d5ea 100644 --- a/extension/Userscripts Extension/Resources/popup.js +++ b/extension/Userscripts Extension/Resources/popup.js @@ -56,13 +56,23 @@ } return $$scope.dirty; } - function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) { - const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn); + function update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) { if (slot_changes) { const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn); slot.p(slot_context, slot_changes); } } + function get_all_dirty_from_scope($$scope) { + if ($$scope.ctx.length > 32) { + const dirty = []; + const length = $$scope.ctx.length / 32; + for (let i = 0; i < length; i++) { + dirty[i] = -1; + } + return dirty; + } + return -1; + } const is_client = typeof window !== 'undefined'; let now = is_client @@ -98,10 +108,26 @@ } }; } - function append(target, node) { target.appendChild(node); } + function get_root_for_style(node) { + if (!node) + return document; + const root = node.getRootNode ? node.getRootNode() : node.ownerDocument; + if (root && root.host) { + return root; + } + return node.ownerDocument; + } + function append_empty_stylesheet(node) { + const style_element = element('style'); + append_stylesheet(get_root_for_style(node), style_element); + return style_element.sheet; + } + function append_stylesheet(node, style) { + append(node.head || node, style); + } function insert(target, node, anchor) { target.insertBefore(node, anchor || null); } @@ -117,6 +143,9 @@ function element(name) { return document.createElement(name); } + function svg_element(name) { + return document.createElementNS('http://www.w3.org/2000/svg', name); + } function text(data) { return document.createTextNode(data); } @@ -152,26 +181,38 @@ text.data = data; } function set_style(node, key, value, important) { - node.style.setProperty(key, value, important ? 'important' : ''); + if (value === null) { + node.style.removeProperty(key); + } + else { + node.style.setProperty(key, value, important ? 'important' : ''); + } } function toggle_class(element, name, toggle) { element.classList[toggle ? 'add' : 'remove'](name); } - function custom_event(type, detail) { + function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) { const e = document.createEvent('CustomEvent'); - e.initCustomEvent(type, false, false, detail); + e.initCustomEvent(type, bubbles, cancelable, detail); return e; } class HtmlTag { - constructor(anchor = null) { - this.a = anchor; + constructor(is_svg = false) { + this.is_svg = false; + this.is_svg = is_svg; this.e = this.n = null; } + c(html) { + this.h(html); + } m(html, target, anchor = null) { if (!this.e) { - this.e = element(target.nodeName); + if (this.is_svg) + this.e = svg_element(target.nodeName); + else + this.e = element(target.nodeName); this.t = target; - this.h(html); + this.c(html); } this.i(anchor); } @@ -194,7 +235,9 @@ } } - const active_docs = new Set(); + // we need to store the information for multiple documents because a Svelte application could also contain iframes + // https://github.com/sveltejs/svelte/issues/3624 + const managed_styles = new Map(); let active = 0; // https://github.com/darkskyapp/string-hash/blob/master/index.js function hash(str) { @@ -204,6 +247,11 @@ hash = ((hash << 5) - hash) ^ str.charCodeAt(i); return hash >>> 0; } + function create_style_information(doc, node) { + const info = { stylesheet: append_empty_stylesheet(node), rules: {} }; + managed_styles.set(doc, info); + return info; + } function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) { const step = 16.666 / duration; let keyframes = '{\n'; @@ -213,16 +261,14 @@ } const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const name = `__svelte_${hash(rule)}_${uid}`; - const doc = node.ownerDocument; - active_docs.add(doc); - const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style')).sheet); - const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); - if (!current_rules[name]) { - current_rules[name] = true; + const doc = get_root_for_style(node); + const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node); + if (!rules[name]) { + rules[name] = true; stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); } const animation = node.style.animation || ''; - node.style.animation = `${animation ? `${animation}, ` : ``}${name} ${duration}ms linear ${delay}ms 1 both`; + node.style.animation = `${animation ? `${animation}, ` : ''}${name} ${duration}ms linear ${delay}ms 1 both`; active += 1; return name; } @@ -244,14 +290,14 @@ raf(() => { if (active) return; - active_docs.forEach(doc => { - const stylesheet = doc.__svelte_stylesheet; + managed_styles.forEach(info => { + const { stylesheet } = info; let i = stylesheet.cssRules.length; while (i--) stylesheet.deleteRule(i); - doc.__svelte_rules = {}; + info.rules = {}; }); - active_docs.clear(); + managed_styles.clear(); }); } @@ -261,7 +307,7 @@ } function get_current_component() { if (!current_component) - throw new Error(`Function called outside component initialization`); + throw new Error('Function called outside component initialization'); return current_component; } function onMount(fn) { @@ -273,7 +319,8 @@ function bubble(component, event) { const callbacks = component.$$.callbacks[event.type]; if (callbacks) { - callbacks.slice().forEach(fn => fn(event)); + // @ts-ignore + callbacks.slice().forEach(fn => fn.call(this, event)); } } @@ -292,22 +339,40 @@ function add_render_callback(fn) { render_callbacks.push(fn); } - let flushing = false; + // flush() calls callbacks in this order: + // 1. All beforeUpdate callbacks, in order: parents before children + // 2. All bind:this callbacks, in reverse order: children before parents. + // 3. All afterUpdate callbacks, in order: parents before children. EXCEPT + // for afterUpdates called during the initial onMount, which are called in + // reverse order: children before parents. + // Since callbacks might update component values, which could trigger another + // call to flush(), the following steps guard against this: + // 1. During beforeUpdate, any updated components will be added to the + // dirty_components array and will cause a reentrant call to flush(). Because + // the flush index is kept outside the function, the reentrant call will pick + // up where the earlier call left off and go through all dirty components. The + // current_component value is saved and restored so that the reentrant call will + // not interfere with the "parent" flush() call. + // 2. bind:this callbacks cannot trigger new flush() calls. + // 3. During afterUpdate, any updated components will NOT have their afterUpdate + // callback called a second time; the seen_callbacks set, outside the flush() + // function, guarantees this behavior. const seen_callbacks = new Set(); + let flushidx = 0; // Do *not* move this inside the flush() function function flush() { - if (flushing) - return; - flushing = true; + const saved_component = current_component; do { // first, call beforeUpdate functions // and update components - for (let i = 0; i < dirty_components.length; i += 1) { - const component = dirty_components[i]; + while (flushidx < dirty_components.length) { + const component = dirty_components[flushidx]; + flushidx++; set_current_component(component); update(component.$$); } set_current_component(null); dirty_components.length = 0; + flushidx = 0; while (binding_callbacks.length) binding_callbacks.pop()(); // then, once components are updated, call @@ -327,8 +392,8 @@ flush_callbacks.pop()(); } update_scheduled = false; - flushing = false; seen_callbacks.clear(); + set_current_component(saved_component); } function update($$) { if ($$.fragment !== null) { @@ -390,6 +455,9 @@ }); block.o(local); } + else if (callback) { + callback(); + } } const null_transition = { duration: 0 }; function create_out_transition(node, fn, params) { @@ -459,7 +527,7 @@ delete_rule(node, animation_name); } function init(program, duration) { - const d = program.b - t; + const d = (program.b - t); duration *= Math.abs(d); return { a: t, @@ -647,22 +715,24 @@ function create_component(block) { block && block.c(); } - function mount_component(component, target, anchor) { + function mount_component(component, target, anchor, customElement) { const { fragment, on_mount, on_destroy, after_update } = component.$$; fragment && fragment.m(target, anchor); - // onMount happens before the initial afterUpdate - add_render_callback(() => { - const new_on_destroy = on_mount.map(run).filter(is_function); - if (on_destroy) { - on_destroy.push(...new_on_destroy); - } - else { - // Edge case - component was destroyed immediately, - // most likely as a result of a binding initialising - run_all(new_on_destroy); - } - component.$$.on_mount = []; - }); + if (!customElement) { + // onMount happens before the initial afterUpdate + add_render_callback(() => { + const new_on_destroy = on_mount.map(run).filter(is_function); + if (on_destroy) { + on_destroy.push(...new_on_destroy); + } + else { + // Edge case - component was destroyed immediately, + // most likely as a result of a binding initialising + run_all(new_on_destroy); + } + component.$$.on_mount = []; + }); + } after_update.forEach(add_render_callback); } function destroy_component(component, detaching) { @@ -684,10 +754,9 @@ } component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); } - function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { + function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) { const parent_component = current_component; set_current_component(component); - const prop_values = options.props || {}; const $$ = component.$$ = { fragment: null, ctx: null, @@ -699,17 +768,20 @@ // lifecycle on_mount: [], on_destroy: [], + on_disconnect: [], before_update: [], after_update: [], - context: new Map(parent_component ? parent_component.$$.context : []), + context: new Map(options.context || (parent_component ? parent_component.$$.context : [])), // everything else callbacks: blank_object(), dirty, - skip_bound: false + skip_bound: false, + root: options.target || parent_component.$$.root }; + append_styles && append_styles($$.root); let ready = false; $$.ctx = instance - ? instance(component, prop_values, (i, ret, ...rest) => { + ? instance(component, options.props || {}, (i, ret, ...rest) => { const value = rest.length ? rest[0] : ret; if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { if (!$$.skip_bound && $$.bound[i]) @@ -738,11 +810,14 @@ } if (options.intro) transition_in(component.$$.fragment); - mount_component(component, options.target, options.anchor); + mount_component(component, options.target, options.anchor, options.customElement); flush(); } set_current_component(parent_component); } + /** + * Base class for Svelte components. Used when dev=false. + */ class SvelteComponent { $destroy() { destroy_component(this, 1); @@ -766,7 +841,7 @@ } } - /* src/shared/Components/IconButton.svelte generated by Svelte v3.29.0 */ + /* src/shared/Components/IconButton.svelte generated by Svelte v3.49.0 */ function create_fragment(ctx) { let button; @@ -827,15 +902,15 @@ let { notification = false } = $$props; function click_handler(event) { - bubble($$self, event); + bubble.call(this, $$self, event); } $$self.$$set = $$props => { - if ("color" in $$props) $$invalidate(0, color = $$props.color); - if ("disabled" in $$props) $$invalidate(1, disabled = $$props.disabled); - if ("icon" in $$props) $$invalidate(2, icon = $$props.icon); - if ("title" in $$props) $$invalidate(3, title = $$props.title); - if ("notification" in $$props) $$invalidate(4, notification = $$props.notification); + if ('color' in $$props) $$invalidate(0, color = $$props.color); + if ('disabled' in $$props) $$invalidate(1, disabled = $$props.disabled); + if ('icon' in $$props) $$invalidate(2, icon = $$props.icon); + if ('title' in $$props) $$invalidate(3, title = $$props.title); + if ('notification' in $$props) $$invalidate(4, notification = $$props.notification); }; return [color, disabled, icon, title, notification, click_handler]; @@ -855,7 +930,7 @@ } } - /* src/shared/Components/Toggle.svelte generated by Svelte v3.29.0 */ + /* src/shared/Components/Toggle.svelte generated by Svelte v3.49.0 */ function create_fragment$1(ctx) { let label; @@ -933,7 +1008,7 @@ let { title = undefined } = $$props; function click_handler(event) { - bubble($$self, event); + bubble.call(this, $$self, event); } function input_change_handler() { @@ -942,9 +1017,9 @@ } $$self.$$set = $$props => { - if ("checked" in $$props) $$invalidate(0, checked = $$props.checked); - if ("disabled" in $$props) $$invalidate(1, disabled = $$props.disabled); - if ("title" in $$props) $$invalidate(2, title = $$props.title); + if ('checked' in $$props) $$invalidate(0, checked = $$props.checked); + if ('disabled' in $$props) $$invalidate(1, disabled = $$props.disabled); + if ('title' in $$props) $$invalidate(2, title = $$props.title); }; return [checked, disabled, title, click_handler, input_change_handler]; @@ -963,7 +1038,7 @@ return 0.5 * ((t -= 2) * t * t * t * t + 2); } - function fade(node, { delay = 0, duration = 400, easing = identity }) { + function fade(node, { delay = 0, duration = 400, easing = identity } = {}) { const o = +getComputedStyle(node).opacity; return { delay, @@ -975,7 +1050,7 @@ var iconLoader = ' '; - /* src/shared/Components/Loader.svelte generated by Svelte v3.29.0 */ + /* src/shared/Components/Loader.svelte generated by Svelte v3.49.0 */ function create_if_block(ctx) { let div; @@ -1028,9 +1103,10 @@ return { c() { div = element("div"); + html_tag = new HtmlTag(false); t = space(); if (if_block) if_block.c(); - html_tag = new HtmlTag(t); + html_tag.a = t; attr(div, "class", "loader svelte-tibcgr"); set_style(div, "background-color", /*backgroundColor*/ ctx[2]); }, @@ -1086,9 +1162,9 @@ let { backgroundColor = "var(--color-bg-secondary)" } = $$props; $$self.$$set = $$props => { - if ("abort" in $$props) $$invalidate(0, abort = $$props.abort); - if ("abortClick" in $$props) $$invalidate(1, abortClick = $$props.abortClick); - if ("backgroundColor" in $$props) $$invalidate(2, backgroundColor = $$props.backgroundColor); + if ('abort' in $$props) $$invalidate(0, abort = $$props.abort); + if ('abortClick' in $$props) $$invalidate(1, abortClick = $$props.abortClick); + if ('backgroundColor' in $$props) $$invalidate(2, backgroundColor = $$props.backgroundColor); }; return [abort, abortClick, backgroundColor]; @@ -1106,7 +1182,7 @@ } } - /* src/shared/Components/Tag.svelte generated by Svelte v3.29.0 */ + /* src/shared/Components/Tag.svelte generated by Svelte v3.49.0 */ function create_fragment$3(ctx) { let div; @@ -1137,7 +1213,7 @@ let { type = undefined } = $$props; $$self.$$set = $$props => { - if ("type" in $$props) $$invalidate(0, type = $$props.type); + if ('type' in $$props) $$invalidate(0, type = $$props.type); }; return [type]; @@ -1150,7 +1226,7 @@ } } - /* src/popup/Components/PopupItem.svelte generated by Svelte v3.29.0 */ + /* src/popup/Components/PopupItem.svelte generated by Svelte v3.49.0 */ function create_if_block$1(ctx) { let div; @@ -1272,15 +1348,15 @@ let { request = false } = $$props; function click_handler(event) { - bubble($$self, event); + bubble.call(this, $$self, event); } $$self.$$set = $$props => { - if ("enabled" in $$props) $$invalidate(0, enabled = $$props.enabled); - if ("name" in $$props) $$invalidate(1, name = $$props.name); - if ("type" in $$props) $$invalidate(2, type = $$props.type); - if ("subframe" in $$props) $$invalidate(3, subframe = $$props.subframe); - if ("request" in $$props) $$invalidate(4, request = $$props.request); + if ('enabled' in $$props) $$invalidate(0, enabled = $$props.enabled); + if ('name' in $$props) $$invalidate(1, name = $$props.name); + if ('type' in $$props) $$invalidate(2, type = $$props.type); + if ('subframe' in $$props) $$invalidate(3, subframe = $$props.subframe); + if ('request' in $$props) $$invalidate(4, request = $$props.request); }; return [enabled, name, type, subframe, request, click_handler]; @@ -1302,7 +1378,7 @@ var iconArrowLeft = ''; - /* src/popup/Components/View.svelte generated by Svelte v3.29.0 */ + /* src/popup/Components/View.svelte generated by Svelte v3.49.0 */ function create_else_block(ctx) { let current; @@ -1323,8 +1399,17 @@ }, p(ctx, dirty) { if (default_slot) { - if (default_slot.p && dirty & /*$$scope*/ 128) { - update_slot(default_slot, default_slot_template, ctx, /*$$scope*/ ctx[7], dirty, null, null); + if (default_slot.p && (!current || dirty & /*$$scope*/ 128)) { + update_slot_base( + default_slot, + default_slot_template, + ctx, + /*$$scope*/ ctx[7], + !current + ? get_all_dirty_from_scope(/*$$scope*/ ctx[7]) + : get_slot_changes(default_slot_template, /*$$scope*/ ctx[7], dirty, null), + null + ); } } }, @@ -1397,6 +1482,7 @@ m(target, anchor) { insert(target, div, anchor); }, + p: noop, d(detaching) { if (detaching) detach(div); } @@ -1481,6 +1567,8 @@ if (!if_block) { if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); if_block.c(); + } else { + if_block.p(ctx, dirty); } transition_in(if_block, 1); @@ -1537,13 +1625,13 @@ } $$self.$$set = $$props => { - if ("loading" in $$props) $$invalidate(0, loading = $$props.loading); - if ("headerTitle" in $$props) $$invalidate(1, headerTitle = $$props.headerTitle); - if ("closeClick" in $$props) $$invalidate(2, closeClick = $$props.closeClick); - if ("showLoaderOnDisabled" in $$props) $$invalidate(3, showLoaderOnDisabled = $$props.showLoaderOnDisabled); - if ("abort" in $$props) $$invalidate(4, abort = $$props.abort); - if ("abortClick" in $$props) $$invalidate(5, abortClick = $$props.abortClick); - if ("$$scope" in $$props) $$invalidate(7, $$scope = $$props.$$scope); + if ('loading' in $$props) $$invalidate(0, loading = $$props.loading); + if ('headerTitle' in $$props) $$invalidate(1, headerTitle = $$props.headerTitle); + if ('closeClick' in $$props) $$invalidate(2, closeClick = $$props.closeClick); + if ('showLoaderOnDisabled' in $$props) $$invalidate(3, showLoaderOnDisabled = $$props.showLoaderOnDisabled); + if ('abort' in $$props) $$invalidate(4, abort = $$props.abort); + if ('abortClick' in $$props) $$invalidate(5, abortClick = $$props.abortClick); + if ('$$scope' in $$props) $$invalidate(7, $$scope = $$props.$$scope); }; return [ @@ -1576,7 +1664,7 @@ var iconUpdate = ''; - /* src/popup/Components/Views/UpdateView.svelte generated by Svelte v3.29.0 */ + /* src/popup/Components/Views/UpdateView.svelte generated by Svelte v3.49.0 */ function get_each_context(ctx, list, i) { const child_ctx = ctx.slice(); @@ -1600,6 +1688,7 @@ return { c() { div2 = element("div"); + html_tag = new HtmlTag(false); t0 = space(); div1 = element("div"); t1 = text("There are no file updates available\n "); @@ -1607,7 +1696,7 @@ t2 = space(); div0 = element("div"); div0.textContent = "Check Updates"; - html_tag = new HtmlTag(t0); + html_tag.a = t0; attr(div0, "class", "link svelte-1v987ms"); attr(div1, "class", "svelte-1v987ms"); attr(div2, "class", "none svelte-1v987ms"); @@ -1697,7 +1786,7 @@ ctx = new_ctx; if (dirty & /*updateSingleClick, updates*/ 9) { - const each_value = /*updates*/ ctx[0]; + each_value = /*updates*/ ctx[0]; each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, t0.parentNode, destroy_block, create_each_block, t0, get_each_context); } }, @@ -1731,8 +1820,8 @@ let mounted; let dispose; - function click_handler(...args) { - return /*click_handler*/ ctx[4](/*item*/ ctx[5], ...args); + function click_handler() { + return /*click_handler*/ ctx[4](/*item*/ ctx[5]); } return { @@ -1837,10 +1926,10 @@ const click_handler = item => updateSingleClick(item); $$self.$$set = $$props => { - if ("updates" in $$props) $$invalidate(0, updates = $$props.updates); - if ("updateClick" in $$props) $$invalidate(1, updateClick = $$props.updateClick); - if ("checkClick" in $$props) $$invalidate(2, checkClick = $$props.checkClick); - if ("updateSingleClick" in $$props) $$invalidate(3, updateSingleClick = $$props.updateSingleClick); + if ('updates' in $$props) $$invalidate(0, updates = $$props.updates); + if ('updateClick' in $$props) $$invalidate(1, updateClick = $$props.updateClick); + if ('checkClick' in $$props) $$invalidate(2, checkClick = $$props.checkClick); + if ('updateSingleClick' in $$props) $$invalidate(3, updateSingleClick = $$props.updateSingleClick); }; return [updates, updateClick, checkClick, updateSingleClick, click_handler]; @@ -1863,7 +1952,7 @@ var iconError = ''; - /* src/popup/Components/Views/InstallView.svelte generated by Svelte v3.29.0 */ + /* src/popup/Components/Views/InstallView.svelte generated by Svelte v3.49.0 */ function get_each_context$1(ctx, list, i) { const child_ctx = ctx.slice(); @@ -2093,13 +2182,14 @@ return { c() { div1 = element("div"); + html_tag = new HtmlTag(false); t0 = space(); div0 = element("div"); div0.textContent = "Couldn't install userscript"; t2 = space(); p = element("p"); t3 = text(/*installError*/ ctx[1]); - html_tag = new HtmlTag(t0); + html_tag.a = t0; attr(p, "class", "svelte-p5i392"); attr(div1, "class", "install__error svelte-p5i392"); }, @@ -2563,10 +2653,10 @@ let { installConfirmClick } = $$props; $$self.$$set = $$props => { - if ("userscript" in $$props) $$invalidate(0, userscript = $$props.userscript); - if ("installError" in $$props) $$invalidate(1, installError = $$props.installError); - if ("installCancelClick" in $$props) $$invalidate(2, installCancelClick = $$props.installCancelClick); - if ("installConfirmClick" in $$props) $$invalidate(3, installConfirmClick = $$props.installConfirmClick); + if ('userscript' in $$props) $$invalidate(0, userscript = $$props.userscript); + if ('installError' in $$props) $$invalidate(1, installError = $$props.installError); + if ('installCancelClick' in $$props) $$invalidate(2, installCancelClick = $$props.installCancelClick); + if ('installConfirmClick' in $$props) $$invalidate(3, installConfirmClick = $$props.installConfirmClick); }; return [userscript, installError, installCancelClick, installConfirmClick]; @@ -2585,7 +2675,7 @@ } } - /* src/popup/Components/Views/AllItemsView.svelte generated by Svelte v3.29.0 */ + /* src/popup/Components/Views/AllItemsView.svelte generated by Svelte v3.49.0 */ function get_each_context$2(ctx, list, i) { const child_ctx = ctx.slice(); @@ -2622,7 +2712,7 @@ let each_1_lookup = new Map(); let div_class_value; let current; - let each_value = /*list*/ ctx[3]; + let each_value = /*list*/ ctx[2]; const get_key = ctx => /*item*/ ctx[6].filename; for (let i = 0; i < each_value.length; i += 1) { @@ -2639,7 +2729,7 @@ each_blocks[i].c(); } - attr(div, "class", div_class_value = "items view--all " + (/*rowColorsAll*/ ctx[2] || "") + " svelte-rd8r5o"); + attr(div, "class", div_class_value = "items view--all " + (/*rowColorsAll*/ ctx[3] || "") + " svelte-rd8r5o"); toggle_class(div, "disabled", /*disabled*/ ctx[4]); }, m(target, anchor) { @@ -2652,18 +2742,18 @@ current = true; }, p(ctx, dirty) { - if (dirty & /*list, allItemsToggleItem*/ 10) { - const each_value = /*list*/ ctx[3]; + if (dirty & /*list, allItemsToggleItem*/ 6) { + each_value = /*list*/ ctx[2]; group_outros(); each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block$2, null, get_each_context$2); check_outros(); } - if (!current || dirty & /*rowColorsAll*/ 4 && div_class_value !== (div_class_value = "items view--all " + (/*rowColorsAll*/ ctx[2] || "") + " svelte-rd8r5o")) { + if (!current || dirty & /*rowColorsAll*/ 8 && div_class_value !== (div_class_value = "items view--all " + (/*rowColorsAll*/ ctx[3] || "") + " svelte-rd8r5o")) { attr(div, "class", div_class_value); } - if (dirty & /*rowColorsAll, disabled*/ 20) { + if (dirty & /*rowColorsAll, disabled*/ 24) { toggle_class(div, "disabled", /*disabled*/ ctx[4]); } }, @@ -2699,8 +2789,8 @@ let popupitem; let current; - function click_handler(...args) { - return /*click_handler*/ ctx[5](/*item*/ ctx[6], ...args); + function click_handler() { + return /*click_handler*/ ctx[5](/*item*/ ctx[6]); } popupitem = new PopupItem({ @@ -2731,11 +2821,11 @@ p(new_ctx, dirty) { ctx = new_ctx; const popupitem_changes = {}; - if (dirty & /*list*/ 8) popupitem_changes.enabled = !/*item*/ ctx[6].disabled; - if (dirty & /*list*/ 8) popupitem_changes.name = /*item*/ ctx[6].name; - if (dirty & /*list*/ 8) popupitem_changes.subframe = /*item*/ ctx[6].subframe; - if (dirty & /*list*/ 8) popupitem_changes.type = /*item*/ ctx[6].type; - if (dirty & /*list*/ 8) popupitem_changes.request = /*item*/ ctx[6].request ? true : false; + if (dirty & /*list*/ 4) popupitem_changes.enabled = !/*item*/ ctx[6].disabled; + if (dirty & /*list*/ 4) popupitem_changes.name = /*item*/ ctx[6].name; + if (dirty & /*list*/ 4) popupitem_changes.subframe = /*item*/ ctx[6].subframe; + if (dirty & /*list*/ 4) popupitem_changes.type = /*item*/ ctx[6].type; + if (dirty & /*list*/ 4) popupitem_changes.request = /*item*/ ctx[6].request ? true : false; popupitem.$set(popupitem_changes); }, i(local) { @@ -2799,6 +2889,8 @@ if (!if_block) { if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); if_block.c(); + } else { + if_block.p(ctx, dirty); } transition_in(if_block, 1); @@ -2822,6 +2914,7 @@ } function instance$8($$self, $$props, $$invalidate) { + let list; let { allItems = [] } = $$props; let { allItemsToggleItem } = $$props; let disabled; @@ -2829,29 +2922,27 @@ const click_handler = item => allItemsToggleItem(item); $$self.$$set = $$props => { - if ("allItems" in $$props) $$invalidate(0, allItems = $$props.allItems); - if ("allItemsToggleItem" in $$props) $$invalidate(1, allItemsToggleItem = $$props.allItemsToggleItem); + if ('allItems' in $$props) $$invalidate(0, allItems = $$props.allItems); + if ('allItemsToggleItem' in $$props) $$invalidate(1, allItemsToggleItem = $$props.allItemsToggleItem); }; - let list; - $$self.$$.update = () => { if ($$self.$$.dirty & /*allItems*/ 1) { - $$invalidate(3, list = allItems.sort((a, b) => a.name.localeCompare(b.name))); + $$invalidate(2, list = allItems.sort((a, b) => a.name.localeCompare(b.name))); } - if ($$self.$$.dirty & /*list*/ 8) { + if ($$self.$$.dirty & /*list*/ 4) { if (list.length > 1 && list.length % 2 === 0) { - $$invalidate(2, rowColorsAll = "even--all"); + $$invalidate(3, rowColorsAll = "even--all"); } else if (list.length > 1 && list.length % 2 !== 0) { - $$invalidate(2, rowColorsAll = "odd--all"); + $$invalidate(3, rowColorsAll = "odd--all"); } else { - $$invalidate(2, rowColorsAll = undefined); + $$invalidate(3, rowColorsAll = undefined); } } }; - return [allItems, allItemsToggleItem, rowColorsAll, list, disabled, click_handler]; + return [allItems, allItemsToggleItem, list, rowColorsAll, disabled, click_handler]; } class AllItemsView extends SvelteComponent { @@ -2867,7 +2958,522 @@ var iconRefresh = ''; - /* src/popup/App.svelte generated by Svelte v3.29.0 */ + // wrap a relatively independent settings storage with its own functions + + const storagePrefix = "US_"; + const storageKey = key => storagePrefix + key.toUpperCase(); + // const storageRef = async area => { // dynamic storage reference + // browser.storage.sync.area = "sync"; + // browser.storage.local.area = "local"; + // if (area === "sync") return browser.storage.sync; + // if (area === "local") return browser.storage.local; + // const key = storageKey("settings_sync"); + // const result = await browser.storage.local.get(key); + // if (result?.[key] === true) { + // return browser.storage.sync; + // } else { + // return browser.storage.local; + // } + // }; + + // https://developer.apple.com/documentation/safariservices/safari_web_extensions/assessing_your_safari_web_extension_s_browser_compatibility#3584139 + // since storage sync is not implemented in Safari, currently only returns using local storage + const storageRef = async () => { + browser.storage.local.area = "local"; + return browser.storage.local; + }; + + const settingDefault = deepFreeze({ + name: "setting_default", + type: undefined, + local: false, + values: [], + default: undefined, + protect: false, + confirm: false, + platforms: ["macos", "ipados", "ios"], + langLabel: {}, + langTitle: {}, + group: "", + legacy: "", + nodeType: "", + nodeClass: {} + }); + + const settingsDefine = deepFreeze([ + { + name: "legacy_imported", + type: "number", + local: true, + default: 0, + protect: true, + platforms: ["macos"], + group: "Internal" + }, + { + name: "language_code", + type: "string", + default: "en", + platforms: ["macos", "ipados", "ios"], + group: "Internal", + legacy: "languageCode" + }, + { + name: "scripts_settings", + type: "object", + default: {}, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts update check active", + zh_hans: "脚本更新检查激活" + }, + langTitle: { + en: "Whether to enable each single script update check", + zh_hans: "是否开启单个脚本更新检查" + }, + group: "Internal", + nodeType: "Subpage" + }, + // { + // name: "settings_sync", + // type: "boolean", + // local: true, + // default: false, + // protect: true, + // platforms: ["macos", "ipados", "ios"], + // langLabel: { + // en: "Sync settings", + // zh_hans: "同步设置" + // }, + // langTitle: { + // en: "Sync settings across devices", + // zh_hans: "跨设备同步设置" + // }, + // group: "General", + // nodeType: "Toggle" + // }, + { + name: "toolbar_badge_count", + type: "boolean", + default: true, + platforms: ["macos", "ipados"], + langLabel: { + en: "Show Toolbar Count", + zh_hans: "工具栏图标显示计数徽章" + }, + langTitle: { + en: "displays a badge on the toolbar icon with a number that represents how many enabled scripts match the url for the page you are on", + zh_hans: "简体中文描述" + }, + group: "General", + legacy: "showCount", + nodeType: "Toggle" + }, + { + name: "global_active", + type: "boolean", + local: true, + default: true, + platforms: ["macos"], + langLabel: { + en: "Enable Injection", + zh_hans: "启用注入" + }, + langTitle: { + en: "toggle on/off script injection for the pages you visit", + zh_hans: "简体中文描述" + }, + group: "General", + legacy: "active", + nodeType: "Toggle", + nodeClass: {red: false} + }, + { + name: "global_scripts_update_check", + type: "boolean", + default: true, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Global scripts update check", + zh_hans: "全局脚本更新检查" + }, + langTitle: { + en: "Whether to enable global periodic script update check", + zh_hans: "是否开启全局定期脚本更新检查" + }, + group: "General", + nodeType: "Toggle" + }, + { + name: "scripts_update_check_interval", + type: "number", + default: 86400000, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts update check interval", + zh_hans: "脚本更新检查间隔" + }, + langTitle: { + en: "The interval for script update check in background", + zh_hans: "脚本更新检查的间隔时间" + }, + group: "General", + nodeType: "Toggle" + }, + { + name: "scripts_update_check_lasttime", + type: "number", + default: 0, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts update check lasttime", + zh_hans: "脚本更新上次检查时间" + }, + langTitle: { + en: "The lasttime for script update check in background", + zh_hans: "后台脚本更新上次检查时间" + }, + group: "Internal", + nodeType: "Toggle" + }, + { + name: "scripts_auto_update", + type: "boolean", + default: false, + confirm: true, + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Scripts silent auto update", + zh_hans: "脚本后台静默自动更新" + }, + langTitle: { + en: "Script silently auto-updates in the background, which is dangerous and may introduce unconfirmed malicious code", + zh_hans: "脚本在后台静默自动更新,这是危险的,可能引入未经确认的恶意代码" + }, + group: "General", + nodeType: "Toggle", + nodeClass: {warn: true} + }, + { + name: "global_exclude_match", + type: "object", + default: [], + platforms: ["macos", "ipados", "ios"], + langLabel: { + en: "Global exclude match patterns", + zh_hans: "全局排除匹配模式列表" + }, + langTitle: { + en: "this input accepts a comma separated list of @match patterns, a page url that matches against a pattern in this list will be ignored for script injection", + zh_hans: "简体中文描述" + }, + group: "General", + legacy: "blacklist", + nodeType: "textarea", + nodeClass: {red: "blacklistError"} + }, + { + name: "editor_close_brackets", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Auto Close Brackets", + zh_hans: "自动关闭括号" + }, + langTitle: { + en: "toggles on/off auto closing of brackets in the editor, this affects the following characters: () [] {} \"\" ''", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "autoCloseBrackets", + nodeType: "Toggle" + }, + { + name: "editor_auto_hint", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Auto Hint", + zh_hans: "自动提示(Hint)" + }, + langTitle: { + en: "automatically shows completion hints while editing", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "autoHint", + nodeType: "Toggle" + }, + { + name: "editor_list_sort", + type: "string", + values: ["nameAsc", "nameDesc", "lastModifiedAsc", "lastModifiedDesc"], + default: "lastModifiedDesc", + platforms: ["macos"], + langLabel: { + en: "Sort order", + zh_hans: "排序顺序" + }, + langTitle: { + en: "Display order of items in sidebar", + zh_hans: "侧栏中项目的显示顺序" + }, + group: "Editor", + legacy: "sortOrder", + nodeType: "Dropdown" + }, + { + name: "editor_list_descriptions", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Show List Descriptions", + zh_hans: "显示列表项目描述" + }, + langTitle: { + en: "show or hides the item descriptions in the sidebar", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "descriptions", + nodeType: "Toggle" + }, + { + name: "editor_javascript_lint", + type: "boolean", + default: false, + platforms: ["macos"], + langLabel: { + en: "Javascript Linter", + zh_hans: "Javascript Linter" + }, + langTitle: { + en: "toggles basic Javascript linting within the editor", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "lint", + nodeType: "Toggle" + }, + { + name: "editor_show_whitespace", + type: "boolean", + default: true, + platforms: ["macos"], + langLabel: { + en: "Show whitespace characters", + zh_hans: "显示空白字符" + }, + langTitle: { + en: "toggles the display of invisible characters in the editor", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "showInvisibles", + nodeType: "Toggle" + }, + { + name: "editor_tab_size", + type: "number", + values: [2, 4], + default: 4, + platforms: ["macos"], + langLabel: { + en: "Tab Size", + zh_hans: "制表符大小" + }, + langTitle: { + en: "the number of spaces a tab is equal to while editing", + zh_hans: "简体中文描述" + }, + group: "Editor", + legacy: "tabSize", + nodeType: "select" + } + ].reduce(settingsDefineReduceCallback, {})); + + // populate the settingsDefine with settingDefault + // and convert settingsDefine to storageKey object + function settingsDefineReduceCallback(settings, setting) { + setting.key = storageKey(setting.name); + settings[setting.key] = Object.assign({}, settingDefault, setting); + return settings; + } + + // prevent settings define from being modified in any case + // otherwise user settings may be lost in the worst case + function deepFreeze(object) { + for (const p in object) { + if (typeof object[p] == "object") { + deepFreeze(object[p]); + } + } + return Object.freeze(object); + } + + // export and define the operation method of settings storage + // they are similar to browser.storage but slightly different + + async function get(keys, area) { + if (![undefined, "local", "sync"].includes(area)) { + return console.error("Unexpected storage area:", area); + } + // validate setting value and fix surprises to default + const valueFix = (key, val) => { + if (!key || !Object.hasOwn(settingsDefine, key)) return; + const def = settingsDefine[key].default; + // check if value type conforms to settingsDefine + const type = settingsDefine[key].type; + if (typeof val != type) { + console.warn(`Unexpected ${key} value type '${typeof(val)}' should '${type}', fix to default`); + return def; + } + // check if value conforms to settingsDefine + const values = settingsDefine[key].values; + if (values.length && !values.includes(val)) { + console.warn(`Unexpected ${key} value '${val}' should one of '${values}', fix to default`); + return def; + } + // verified, pass original value + return val; + }; + if (typeof keys == "string") { // [single setting] + const key = storageKey(keys); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("unexpected settings key:", key); + } + // check if only locally stored setting + settingsDefine[key].local === true && (area = "local"); + const storage = await storageRef(); + const result = await storage.get(key); + if (Object.hasOwn(result, key)) { + return valueFix(key, result[key]); + } else { + return settingsDefine[key].default; + } + } + const complexGet = async (settingsDefault, areaKeys) => { + const storage = await storageRef(); + let local = {}, sync = {}; + if (storage.area === "sync") { + if (areaKeys.sync.length) { + sync = await storage.get(areaKeys.sync); + } + if (areaKeys.local.length) { + const storage = await storageRef(); + local = await storage.get(areaKeys.local); + } + } else { + local = await storage.get(areaKeys.all); + } + const result = Object.assign(settingsDefault, local, sync); + // revert settings object property name + return Object.entries(result).reduce((p, c) => ( + p[settingsDefine[c[0]].name] = valueFix(...c), p + ), {}); + }; + if (Array.isArray(keys)) { // [muilt settings] + if (!keys.length) { + return console.error("Settings keys empty:", keys); + } + const settingsDefault = {}; + const areaKeys = {local: [], sync: [], all: []}; + for (const k of keys) { + const key = storageKey(k); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("unexpected settings key:", key); + } + settingsDefault[key] = settingsDefine[key].default; + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local.push(key) + : areaKeys.sync.push(key); + // record all keys in case sync storage is not enabled + areaKeys.all.push(key); + } + return await complexGet(settingsDefault, areaKeys); + } + if (typeof keys == "undefined" || keys === null) { // [all settings] + const settingsDefault = {}; + const areaKeys = {local: [], sync: [], all: []}; + for (const key in settingsDefine) { + settingsDefault[key] = settingsDefine[key].default; + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local.push(key) + : areaKeys.sync.push(key); + // record all keys in case sync storage is not enabled + areaKeys.all.push(key); + } + return await complexGet(settingsDefault, areaKeys); + } + return console.error("Unexpected keys type:", keys); + } + + async function set(keys, area) { + if (![undefined, "local", "sync"].includes(area)) { + return console.error("unexpected storage area:", area); + } + if (typeof keys != "object") { + return console.error("Unexpected keys type:", keys); + } + if (!Object.keys(keys).length) { + return console.error("Settings object empty:", keys); + } + const areaKeys = {local: {}, sync: {}, all: {}}; + for (const k in keys) { + const key = storageKey(k); + // check if key exist in settingsDefine + if (!Object.hasOwn(settingsDefine, key)) { + return console.error("Unexpected settings keys:", key); + } + // check if value type conforms to settingsDefine + const type = settingsDefine[key].type; + if (typeof keys[k] != type) { + if (type === "number" && !isNaN(keys[k])) { // compatible with string numbers + keys[k] = Number(keys[k]); // still store it as a number type + } else { + return console.error(`Unexpected ${k} value type '${typeof(keys[k])}' should '${type}'`); + } + } + // check if value conforms to settingsDefine + const values = settingsDefine[key].values; + if (values.length && !values.includes(keys[k])) { + return console.error(`Unexpected ${k} value '${keys[k]}' should one of '${values}'`); + } + // detach only locally stored settings + settingsDefine[key].local === true + ? areaKeys.local[key] = keys[k] + : areaKeys.sync[key] = keys[k]; + // record all keys in case sync storage is not enabled + areaKeys.all[key] = keys[k]; + } + const storage = await storageRef(); + // complexSet + try { + if (storage.area === "sync") { + if (Object.keys(areaKeys.sync).length) { + await storage.set(areaKeys.sync); + } + if (Object.keys(areaKeys.local).length) { + const storage = await storageRef("local"); + await storage.set(areaKeys.local); + } + } else { + await storage.set(areaKeys.all); + } + return true; + } catch (error) { + return console.error(error); + } + } + + /* src/popup/App.svelte generated by Svelte v3.49.0 */ const { window: window_1 } = globals; @@ -2877,12 +3483,12 @@ return child_ctx; } - // (500:0) {#if !active} + // (496:0) {#if !active} function create_if_block_10(ctx) { return { c: noop, m: noop, d: noop }; } - // (503:0) {#if showInstallPrompt} + // (499:0) {#if showInstallPrompt} function create_if_block_9(ctx) { let div; let t0; @@ -2896,7 +3502,7 @@ div = element("div"); t0 = text("Userscript Detected: "); span = element("span"); - t1 = text(/*showInstallPrompt*/ ctx[15]); + t1 = text(/*showInstallPrompt*/ ctx[16]); attr(span, "class", "svelte-1w80sz6"); attr(div, "class", "warn svelte-1w80sz6"); }, @@ -2913,7 +3519,7 @@ } }, p(ctx, dirty) { - if (dirty[0] & /*showInstallPrompt*/ 32768) set_data(t1, /*showInstallPrompt*/ ctx[15]); + if (dirty[0] & /*showInstallPrompt*/ 65536) set_data(t1, /*showInstallPrompt*/ ctx[16]); }, d(detaching) { if (detaching) detach(div); @@ -2924,7 +3530,7 @@ }; } - // (508:0) {#if error} + // (504:0) {#if error} function create_if_block_8(ctx) { let div; let t0; @@ -2941,7 +3547,7 @@ return { c() { div = element("div"); - t0 = text(/*error*/ ctx[0]); + t0 = text(/*error*/ ctx[3]); t1 = space(); create_component(iconbutton.$$.fragment); attr(div, "class", "error svelte-1w80sz6"); @@ -2955,7 +3561,7 @@ current = true; }, p(ctx, dirty) { - if (!current || dirty[0] & /*error*/ 1) set_data(t0, /*error*/ ctx[0]); + if (!current || dirty[0] & /*error*/ 8) set_data(t0, /*error*/ ctx[3]); }, i(local) { if (current) return; @@ -2974,13 +3580,13 @@ }; } - // (536:8) {:else} + // (532:8) {:else} function create_else_block$3(ctx) { let div; let each_blocks = []; let each_1_lookup = new Map(); let current; - let each_value = /*list*/ ctx[22]; + let each_value = /*list*/ ctx[2]; const get_key = ctx => /*item*/ ctx[48].filename; for (let i = 0; i < each_value.length; i += 1) { @@ -2998,7 +3604,7 @@ } attr(div, "class", "items svelte-1w80sz6"); - toggle_class(div, "disabled", /*disabled*/ ctx[3]); + toggle_class(div, "disabled", /*disabled*/ ctx[6]); }, m(target, anchor) { insert(target, div, anchor); @@ -3010,15 +3616,15 @@ current = true; }, p(ctx, dirty) { - if (dirty[0] & /*list, toggleItem*/ 71303168) { - const each_value = /*list*/ ctx[22]; + if (dirty[0] & /*list, toggleItem*/ 67108868) { + each_value = /*list*/ ctx[2]; group_outros(); each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block$3, null, get_each_context$3); check_outros(); } - if (dirty[0] & /*disabled*/ 8) { - toggle_class(div, "disabled", /*disabled*/ ctx[3]); + if (dirty[0] & /*disabled*/ 64) { + toggle_class(div, "disabled", /*disabled*/ ctx[6]); } }, i(local) { @@ -3047,7 +3653,7 @@ }; } - // (534:35) + // (530:35) function create_if_block_7(ctx) { let div; @@ -3069,7 +3675,7 @@ }; } - // (524:28) + // (520:28) function create_if_block_6$1(ctx) { let div; let t0; @@ -3107,7 +3713,7 @@ }; } - // (522:8) {#if inactive} + // (518:8) {#if inactive} function create_if_block_5$1(ctx) { let div; @@ -3129,7 +3735,7 @@ }; } - // (519:4) {#if loading} + // (515:4) {#if loading} function create_if_block_4$1(ctx) { let loader; let current; @@ -3137,7 +3743,7 @@ loader = new Loader({ props: { abortClick: abortUpdates, - abort: /*abort*/ ctx[21] + abort: /*abort*/ ctx[22] } }); @@ -3151,7 +3757,7 @@ }, p(ctx, dirty) { const loader_changes = {}; - if (dirty[0] & /*abort*/ 2097152) loader_changes.abort = /*abort*/ ctx[21]; + if (dirty[0] & /*abort*/ 4194304) loader_changes.abort = /*abort*/ ctx[22]; loader.$set(loader_changes); }, i(local) { @@ -3169,14 +3775,14 @@ }; } - // (538:16) {#each list as item (item.filename)} + // (534:16) {#each list as item (item.filename)} function create_each_block$3(key_1, ctx) { let first; let popupitem; let current; - function click_handler_3(...args) { - return /*click_handler_3*/ ctx[39](/*item*/ ctx[48], ...args); + function click_handler_3() { + return /*click_handler_3*/ ctx[39](/*item*/ ctx[48]); } popupitem = new PopupItem({ @@ -3207,11 +3813,11 @@ p(new_ctx, dirty) { ctx = new_ctx; const popupitem_changes = {}; - if (dirty[0] & /*list*/ 4194304) popupitem_changes.enabled = !/*item*/ ctx[48].disabled; - if (dirty[0] & /*list*/ 4194304) popupitem_changes.name = /*item*/ ctx[48].name; - if (dirty[0] & /*list*/ 4194304) popupitem_changes.subframe = /*item*/ ctx[48].subframe; - if (dirty[0] & /*list*/ 4194304) popupitem_changes.type = /*item*/ ctx[48].type; - if (dirty[0] & /*list*/ 4194304) popupitem_changes.request = /*item*/ ctx[48].request ? true : false; + if (dirty[0] & /*list*/ 4) popupitem_changes.enabled = !/*item*/ ctx[48].disabled; + if (dirty[0] & /*list*/ 4) popupitem_changes.name = /*item*/ ctx[48].name; + if (dirty[0] & /*list*/ 4) popupitem_changes.subframe = /*item*/ ctx[48].subframe; + if (dirty[0] & /*list*/ 4) popupitem_changes.type = /*item*/ ctx[48].type; + if (dirty[0] & /*list*/ 4) popupitem_changes.request = /*item*/ ctx[48].request ? true : false; popupitem.$set(popupitem_changes); }, i(local) { @@ -3230,7 +3836,7 @@ }; } - // (552:0) {#if !inactive && platform === "macos"} + // (548:0) {#if !inactive && platform === "macos"} function create_if_block_3$1(ctx) { let div1; let div0; @@ -3263,7 +3869,7 @@ }; } - // (589:18) + // (585:18) function create_if_block_2$1(ctx) { let view; let current; @@ -3271,7 +3877,7 @@ view = new View({ props: { headerTitle: "All Userscripts", - loading: /*disabled*/ ctx[3], + loading: /*disabled*/ ctx[6], closeClick: /*func_3*/ ctx[44], showLoaderOnDisabled: false, $$slots: { default: [create_default_slot_2] }, @@ -3289,10 +3895,10 @@ }, p(ctx, dirty) { const view_changes = {}; - if (dirty[0] & /*disabled*/ 8) view_changes.loading = /*disabled*/ ctx[3]; - if (dirty[0] & /*showAll*/ 524288) view_changes.closeClick = /*func_3*/ ctx[44]; + if (dirty[0] & /*disabled*/ 64) view_changes.loading = /*disabled*/ ctx[6]; + if (dirty[0] & /*showAll*/ 1048576) view_changes.closeClick = /*func_3*/ ctx[44]; - if (dirty[0] & /*allItems*/ 1048576 | dirty[1] & /*$$scope*/ 1048576) { + if (dirty[0] & /*allItems*/ 2097152 | dirty[1] & /*$$scope*/ 1048576) { view_changes.$$scope = { dirty, ctx }; } @@ -3313,7 +3919,7 @@ }; } - // (575:22) + // (571:22) function create_if_block_1$1(ctx) { let view; let current; @@ -3321,7 +3927,7 @@ view = new View({ props: { headerTitle: "Install Userscript", - loading: /*disabled*/ ctx[3], + loading: /*disabled*/ ctx[6], closeClick: /*func_2*/ ctx[43], showLoaderOnDisabled: true, $$slots: { default: [create_default_slot_1] }, @@ -3339,10 +3945,10 @@ }, p(ctx, dirty) { const view_changes = {}; - if (dirty[0] & /*disabled*/ 8) view_changes.loading = /*disabled*/ ctx[3]; - if (dirty[0] & /*showInstall*/ 65536) view_changes.closeClick = /*func_2*/ ctx[43]; + if (dirty[0] & /*disabled*/ 64) view_changes.loading = /*disabled*/ ctx[6]; + if (dirty[0] & /*showInstall*/ 131072) view_changes.closeClick = /*func_2*/ ctx[43]; - if (dirty[0] & /*installViewUserscript, installViewUserscriptError, showInstall*/ 458752 | dirty[1] & /*$$scope*/ 1048576) { + if (dirty[0] & /*installViewUserscript, installViewUserscriptError, showInstall*/ 917504 | dirty[1] & /*$$scope*/ 1048576) { view_changes.$$scope = { dirty, ctx }; } @@ -3363,7 +3969,7 @@ }; } - // (559:0) {#if showUpdates} + // (555:0) {#if showUpdates} function create_if_block$6(ctx) { let view; let current; @@ -3371,11 +3977,11 @@ view = new View({ props: { headerTitle: "Updates", - loading: /*disabled*/ ctx[3], + loading: /*disabled*/ ctx[6], closeClick: /*func*/ ctx[41], showLoaderOnDisabled: true, abortClick: abortUpdates, - abort: /*showUpdates*/ ctx[5], + abort: /*showUpdates*/ ctx[7], $$slots: { default: [create_default_slot] }, $$scope: { ctx } } @@ -3391,11 +3997,11 @@ }, p(ctx, dirty) { const view_changes = {}; - if (dirty[0] & /*disabled*/ 8) view_changes.loading = /*disabled*/ ctx[3]; - if (dirty[0] & /*showUpdates*/ 32) view_changes.closeClick = /*func*/ ctx[41]; - if (dirty[0] & /*showUpdates*/ 32) view_changes.abort = /*showUpdates*/ ctx[5]; + if (dirty[0] & /*disabled*/ 64) view_changes.loading = /*disabled*/ ctx[6]; + if (dirty[0] & /*showUpdates*/ 128) view_changes.closeClick = /*func*/ ctx[41]; + if (dirty[0] & /*showUpdates*/ 128) view_changes.abort = /*showUpdates*/ ctx[7]; - if (dirty[0] & /*updates*/ 64 | dirty[1] & /*$$scope*/ 1048576) { + if (dirty[0] & /*updates*/ 256 | dirty[1] & /*$$scope*/ 1048576) { view_changes.$$scope = { dirty, ctx }; } @@ -3416,14 +4022,14 @@ }; } - // (590:4) { showAll = false; refreshView(); }} showLoaderOnDisabled={false} > + // (586:4) { showAll = false; refreshView(); }} showLoaderOnDisabled={false} > function create_default_slot_2(ctx) { let allitemsview; let current; allitemsview = new AllItemsView({ props: { - allItems: /*allItems*/ ctx[20], + allItems: /*allItems*/ ctx[21], allItemsToggleItem: /*toggleItem*/ ctx[26] } }); @@ -3438,7 +4044,7 @@ }, p(ctx, dirty) { const allitemsview_changes = {}; - if (dirty[0] & /*allItems*/ 1048576) allitemsview_changes.allItems = /*allItems*/ ctx[20]; + if (dirty[0] & /*allItems*/ 2097152) allitemsview_changes.allItems = /*allItems*/ ctx[21]; allitemsview.$set(allitemsview_changes); }, i(local) { @@ -3456,15 +4062,15 @@ }; } - // (576:4) showInstall = false} showLoaderOnDisabled={true} > + // (572:4) showInstall = false} showLoaderOnDisabled={true} > function create_default_slot_1(ctx) { let installview; let current; installview = new InstallView({ props: { - userscript: /*installViewUserscript*/ ctx[17], - installError: /*installViewUserscriptError*/ ctx[18], + userscript: /*installViewUserscript*/ ctx[18], + installError: /*installViewUserscriptError*/ ctx[19], installCancelClick: /*func_1*/ ctx[42], installConfirmClick: /*installConfirm*/ ctx[32] } @@ -3480,9 +4086,9 @@ }, p(ctx, dirty) { const installview_changes = {}; - if (dirty[0] & /*installViewUserscript*/ 131072) installview_changes.userscript = /*installViewUserscript*/ ctx[17]; - if (dirty[0] & /*installViewUserscriptError*/ 262144) installview_changes.installError = /*installViewUserscriptError*/ ctx[18]; - if (dirty[0] & /*showInstall*/ 65536) installview_changes.installCancelClick = /*func_1*/ ctx[42]; + if (dirty[0] & /*installViewUserscript*/ 262144) installview_changes.userscript = /*installViewUserscript*/ ctx[18]; + if (dirty[0] & /*installViewUserscriptError*/ 524288) installview_changes.installError = /*installViewUserscriptError*/ ctx[19]; + if (dirty[0] & /*showInstall*/ 131072) installview_changes.installCancelClick = /*func_1*/ ctx[42]; installview.$set(installview_changes); }, i(local) { @@ -3500,7 +4106,7 @@ }; } - // (560:4) showUpdates = false} showLoaderOnDisabled={true} abortClick={abortUpdates} abort={showUpdates} > + // (556:4) showUpdates = false} showLoaderOnDisabled={true} abortClick={abortUpdates} abort={showUpdates} > function create_default_slot(ctx) { let updateview; let current; @@ -3510,7 +4116,7 @@ checkClick: /*checkForUpdates*/ ctx[27], updateClick: /*updateAll*/ ctx[24], updateSingleClick: /*updateItem*/ ctx[25], - updates: /*updates*/ ctx[6] + updates: /*updates*/ ctx[8] } }); @@ -3524,7 +4130,7 @@ }, p(ctx, dirty) { const updateview_changes = {}; - if (dirty[0] & /*updates*/ 64) updateview_changes.updates = /*updates*/ ctx[6]; + if (dirty[0] & /*updates*/ 256) updateview_changes.updates = /*updates*/ ctx[8]; updateview.$set(updateview_changes); }, i(local) { @@ -3572,7 +4178,7 @@ props: { icon: iconOpen, title: "Open save location", - disabled: /*disabled*/ ctx[3] + disabled: /*disabled*/ ctx[6] } }); @@ -3581,9 +4187,9 @@ iconbutton1 = new IconButton({ props: { icon: iconUpdate, - notification: /*updates*/ ctx[6].length, + notification: /*updates*/ ctx[8].length, title: "Show updates", - disabled: /*disabled*/ ctx[3] + disabled: /*disabled*/ ctx[6] } }); @@ -3593,7 +4199,7 @@ props: { icon: iconRefresh, title: "Refresh view", - disabled: /*disabled*/ ctx[3] + disabled: /*disabled*/ ctx[6] } }); @@ -3601,16 +4207,16 @@ toggle = new Toggle({ props: { - checked: /*active*/ ctx[1], + checked: /*active*/ ctx[4], title: "Toggle injection", - disabled: /*disabled*/ ctx[3] + disabled: /*disabled*/ ctx[6] } }); toggle.$on("click", /*toggleExtension*/ ctx[23]); - let if_block0 = !/*active*/ ctx[1] && create_if_block_10(); - let if_block1 = /*showInstallPrompt*/ ctx[15] && create_if_block_9(ctx); - let if_block2 = /*error*/ ctx[0] && create_if_block_8(ctx); + let if_block0 = !/*active*/ ctx[4] && create_if_block_10(); + let if_block1 = /*showInstallPrompt*/ ctx[16] && create_if_block_9(ctx); + let if_block2 = /*error*/ ctx[3] && create_if_block_8(ctx); const if_block_creators = [ create_if_block_4$1, @@ -3623,23 +4229,23 @@ const if_blocks = []; function select_block_type(ctx, dirty) { - if (/*loading*/ ctx[2]) return 0; - if (/*inactive*/ ctx[9]) return 1; - if (/*initError*/ ctx[11]) return 2; - if (/*items*/ ctx[4].length < 1) return 3; + if (/*loading*/ ctx[5]) return 0; + if (/*inactive*/ ctx[11]) return 1; + if (/*initError*/ ctx[12]) return 2; + if (/*items*/ ctx[0].length < 1) return 3; return 4; } current_block_type_index = select_block_type(ctx); if_block3 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); - let if_block4 = !/*inactive*/ ctx[9] && /*platform*/ ctx[10] === "macos" && create_if_block_3$1(); + let if_block4 = !/*inactive*/ ctx[11] && /*platform*/ ctx[1] === "macos" && create_if_block_3$1(); const if_block_creators_1 = [create_if_block$6, create_if_block_1$1, create_if_block_2$1]; const if_blocks_1 = []; function select_block_type_1(ctx, dirty) { - if (/*showUpdates*/ ctx[5]) return 0; - if (/*showInstall*/ ctx[16]) return 1; - if (/*showAll*/ ctx[19]) return 2; + if (/*showUpdates*/ ctx[7]) return 0; + if (/*showInstall*/ ctx[17]) return 1; + if (/*showAll*/ ctx[20]) return 2; return -1; } @@ -3672,7 +4278,7 @@ if (if_block5) if_block5.c(); if_block5_anchor = empty(); attr(div0, "class", "header svelte-1w80sz6"); - attr(div1, "class", div1_class_value = "main " + (/*rowColors*/ ctx[8] || "") + " svelte-1w80sz6"); + attr(div1, "class", div1_class_value = "main " + (/*rowColors*/ ctx[10] || "") + " svelte-1w80sz6"); }, m(target, anchor) { insert(target, div0, anchor); @@ -3712,21 +4318,21 @@ }, p(ctx, dirty) { const iconbutton0_changes = {}; - if (dirty[0] & /*disabled*/ 8) iconbutton0_changes.disabled = /*disabled*/ ctx[3]; + if (dirty[0] & /*disabled*/ 64) iconbutton0_changes.disabled = /*disabled*/ ctx[6]; iconbutton0.$set(iconbutton0_changes); const iconbutton1_changes = {}; - if (dirty[0] & /*updates*/ 64) iconbutton1_changes.notification = /*updates*/ ctx[6].length; - if (dirty[0] & /*disabled*/ 8) iconbutton1_changes.disabled = /*disabled*/ ctx[3]; + if (dirty[0] & /*updates*/ 256) iconbutton1_changes.notification = /*updates*/ ctx[8].length; + if (dirty[0] & /*disabled*/ 64) iconbutton1_changes.disabled = /*disabled*/ ctx[6]; iconbutton1.$set(iconbutton1_changes); const iconbutton2_changes = {}; - if (dirty[0] & /*disabled*/ 8) iconbutton2_changes.disabled = /*disabled*/ ctx[3]; + if (dirty[0] & /*disabled*/ 64) iconbutton2_changes.disabled = /*disabled*/ ctx[6]; iconbutton2.$set(iconbutton2_changes); const toggle_changes = {}; - if (dirty[0] & /*active*/ 2) toggle_changes.checked = /*active*/ ctx[1]; - if (dirty[0] & /*disabled*/ 8) toggle_changes.disabled = /*disabled*/ ctx[3]; + if (dirty[0] & /*active*/ 16) toggle_changes.checked = /*active*/ ctx[4]; + if (dirty[0] & /*disabled*/ 64) toggle_changes.disabled = /*disabled*/ ctx[6]; toggle.$set(toggle_changes); - if (!/*active*/ ctx[1]) { + if (!/*active*/ ctx[4]) { if (if_block0) ; else { if_block0 = create_if_block_10(); if_block0.c(); @@ -3737,7 +4343,7 @@ if_block0 = null; } - if (/*showInstallPrompt*/ ctx[15]) { + if (/*showInstallPrompt*/ ctx[16]) { if (if_block1) { if_block1.p(ctx, dirty); } else { @@ -3750,11 +4356,11 @@ if_block1 = null; } - if (/*error*/ ctx[0]) { + if (/*error*/ ctx[3]) { if (if_block2) { if_block2.p(ctx, dirty); - if (dirty[0] & /*error*/ 1) { + if (dirty[0] & /*error*/ 8) { transition_in(if_block2, 1); } } else { @@ -3791,17 +4397,19 @@ if (!if_block3) { if_block3 = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx); if_block3.c(); + } else { + if_block3.p(ctx, dirty); } transition_in(if_block3, 1); if_block3.m(div1, null); } - if (!current || dirty[0] & /*rowColors*/ 256 && div1_class_value !== (div1_class_value = "main " + (/*rowColors*/ ctx[8] || "") + " svelte-1w80sz6")) { + if (!current || dirty[0] & /*rowColors*/ 1024 && div1_class_value !== (div1_class_value = "main " + (/*rowColors*/ ctx[10] || "") + " svelte-1w80sz6")) { attr(div1, "class", div1_class_value); } - if (!/*inactive*/ ctx[9] && /*platform*/ ctx[10] === "macos") { + if (!/*inactive*/ ctx[11] && /*platform*/ ctx[1] === "macos") { if (if_block4) { if_block4.p(ctx, dirty); } else { @@ -3838,6 +4446,8 @@ if (!if_block5) { if_block5 = if_blocks_1[current_block_type_index_1] = if_block_creators_1[current_block_type_index_1](ctx); if_block5.c(); + } else { + if_block5.p(ctx, dirty); } transition_in(if_block5, 1); @@ -3984,6 +4594,7 @@ } function instance$9($$self, $$props, $$invalidate) { + let list; let error = undefined; let active = true; let loading = true; @@ -4009,37 +4620,31 @@ let resizeTimer; let abort = false; - function toggleExtension(e) { - e.preventDefault(); // prevent check state from changing on click - $$invalidate(3, disabled = true); - - browser.runtime.sendNativeMessage({ name: "POPUP_TOGGLE_EXTENSION" }, response => { - $$invalidate(3, disabled = false); - if (response.error) return $$invalidate(0, error = response.error); - $$invalidate(1, active = !active); - }); + async function toggleExtension(e) { + await set({ "global_active": !active }); + $$invalidate(4, active = await get("global_active")); } function updateAll() { - $$invalidate(5, showUpdates = false); - $$invalidate(3, disabled = true); - $$invalidate(2, loading = true); + $$invalidate(7, showUpdates = false); + $$invalidate(6, disabled = true); + $$invalidate(5, loading = true); browser.runtime.sendNativeMessage({ name: "POPUP_UPDATE_ALL" }, response => { if (response.error) { - $$invalidate(0, error = response.error); + $$invalidate(3, error = response.error); } else { - if (response.items) $$invalidate(4, items = response.items); - $$invalidate(6, updates = response.updates); + if (response.items) $$invalidate(0, items = response.items); + $$invalidate(8, updates = response.updates); } - $$invalidate(3, disabled = false); - $$invalidate(2, loading = false); + $$invalidate(6, disabled = false); + $$invalidate(5, loading = false); }); } async function updateItem(item) { - $$invalidate(3, disabled = true); + $$invalidate(6, disabled = true); const currentTab = await browser.tabs.getCurrent(); const url = currentTab.url; const frameUrls = []; @@ -4059,80 +4664,80 @@ const response = await browser.runtime.sendNativeMessage(message); if (response.error) { - $$invalidate(0, error = response.error); - $$invalidate(5, showUpdates = false); + $$invalidate(3, error = response.error); + $$invalidate(7, showUpdates = false); } else { - $$invalidate(6, updates = updates.filter(e => e.filename !== item.filename)); - $$invalidate(4, items = response.items); + $$invalidate(8, updates = updates.filter(e => e.filename !== item.filename)); + $$invalidate(0, items = response.items); } - $$invalidate(3, disabled = false); + $$invalidate(6, disabled = false); } function toggleItem(item) { if (disabled) return; - $$invalidate(3, disabled = true); + $$invalidate(6, disabled = true); browser.runtime.sendNativeMessage({ name: "TOGGLE_ITEM", item }, response => { if (response.error) { - $$invalidate(0, error = response.error); + $$invalidate(3, error = response.error); } else { const i = items.findIndex(el => el === item); const j = allItems.findIndex(el => el === item); item.disabled = !item.disabled; - $$invalidate(4, items[i] = item, items); - if (j >= 0) $$invalidate(20, allItems[j] = item, allItems); + $$invalidate(0, items[i] = item, items); + if (j >= 0) $$invalidate(21, allItems[j] = item, allItems); } - $$invalidate(3, disabled = false); + $$invalidate(6, disabled = false); }); } function checkForUpdates() { - $$invalidate(3, disabled = true); - $$invalidate(11, initError = false); + $$invalidate(6, disabled = true); + $$invalidate(12, initError = false); browser.runtime.sendNativeMessage({ name: "POPUP_CHECK_UPDATES" }, response => { if (response.error) { - $$invalidate(0, error = response.error); - $$invalidate(5, showUpdates = false); + $$invalidate(3, error = response.error); + $$invalidate(7, showUpdates = false); } else { - $$invalidate(6, updates = response.updates); + $$invalidate(8, updates = response.updates); } - $$invalidate(3, disabled = false); + $$invalidate(6, disabled = false); }); } function refreshView() { - $$invalidate(0, error = undefined); - $$invalidate(2, loading = true); - $$invalidate(3, disabled = true); - $$invalidate(4, items = []); - $$invalidate(5, showUpdates = false); - $$invalidate(6, updates = []); - $$invalidate(9, inactive = false); - $$invalidate(21, abort = false); + $$invalidate(3, error = undefined); + $$invalidate(5, loading = true); + $$invalidate(6, disabled = true); + $$invalidate(0, items = []); + $$invalidate(7, showUpdates = false); + $$invalidate(8, updates = []); + $$invalidate(11, inactive = false); + $$invalidate(22, abort = false); initialize(); } async function openSaveLocation() { - $$invalidate(3, disabled = true); - $$invalidate(2, loading = true); + $$invalidate(6, disabled = true); + $$invalidate(5, loading = true); const response = await browser.runtime.sendNativeMessage({ name: "OPEN_SAVE_LOCATION" }); if (response.success) { window.close(); } else if (response.items) { - $$invalidate(19, showAll = true); - $$invalidate(20, allItems = response.items); + $$invalidate(20, showAll = true); + $$invalidate(21, allItems = response.items); } else if (response.error) { console.log(`Error opening save location: ${response.error}`); - $$invalidate(0, error = response.error); + $$invalidate(3, error = response.error); } - $$invalidate(3, disabled = false); - $$invalidate(2, loading = false); + $$invalidate(6, disabled = false); + $$invalidate(5, loading = false); } async function initialize() { @@ -4143,18 +4748,18 @@ pltfm = await browser.runtime.sendNativeMessage({ name: "REQ_PLATFORM" }); } catch(error) { console.log(`Error for pltfm promise: ${error}`); - $$invalidate(11, initError = true); - $$invalidate(2, loading = false); + $$invalidate(12, initError = true); + $$invalidate(5, loading = false); return; } if (pltfm.error) { - $$invalidate(0, error = pltfm.error); - $$invalidate(2, loading = false); - $$invalidate(3, disabled = false); + $$invalidate(3, error = pltfm.error); + $$invalidate(5, loading = false); + $$invalidate(6, disabled = false); return; } else { - $$invalidate(10, platform = pltfm.platform); + $$invalidate(1, platform = pltfm.platform); } // run init checks @@ -4165,20 +4770,20 @@ init = await browser.runtime.sendNativeMessage({ name: "POPUP_INIT" }); } catch(error) { console.log(`Error for init promise: ${error}`); - $$invalidate(11, initError = true); - $$invalidate(2, loading = false); + $$invalidate(12, initError = true); + $$invalidate(5, loading = false); return; } if (init.error) { - $$invalidate(0, error = init.error); - $$invalidate(2, loading = false); - $$invalidate(3, disabled = false); + $$invalidate(3, error = init.error); + $$invalidate(5, loading = false); + $$invalidate(6, disabled = false); return; - } else { - $$invalidate(1, active = init.initData.active === "true" ? true : false); } + $$invalidate(4, active = await get("global_active")); + // refresh session rules browser.runtime.sendMessage({ name: "REFRESH_SESSION_RULES" }); @@ -4195,16 +4800,16 @@ const url = currentTab.url; if (!url) { - $$invalidate(2, loading = false); - $$invalidate(3, disabled = false); + $$invalidate(5, loading = false); + $$invalidate(6, disabled = false); return; } if (url === extensionPageUrl) { // disable popup on extension page - $$invalidate(9, inactive = true); + $$invalidate(11, inactive = true); - $$invalidate(2, loading = false); + $$invalidate(5, loading = false); return; } @@ -4231,18 +4836,18 @@ matches = await browser.runtime.sendNativeMessage(message); } catch(error) { console.log(`Error for matches promise: ${error}`); // response = await browser.runtime.sendMessage(message); - $$invalidate(11, initError = true); - $$invalidate(2, loading = false); + $$invalidate(12, initError = true); + $$invalidate(5, loading = false); return; } if (matches.error) { - $$invalidate(0, error = matches.error); - $$invalidate(2, loading = false); - $$invalidate(3, disabled = false); + $$invalidate(3, error = matches.error); + $$invalidate(5, loading = false); + $$invalidate(6, disabled = false); return; } else { - $$invalidate(4, items = matches.matches); + $$invalidate(0, items = matches.matches); } // get updates @@ -4256,27 +4861,27 @@ const timestampMs = Date.now(); await browser.storage.local.set({ "lastUpdateCheck": timestampMs }); - $$invalidate(21, abort = true); + $$invalidate(22, abort = true); updatesResponse = await browser.runtime.sendNativeMessage({ name: "POPUP_UPDATES" }); } catch(error) { console.error(`Error for updates promise: ${error}`); - $$invalidate(11, initError = true); - $$invalidate(2, loading = false); - $$invalidate(21, abort = false); + $$invalidate(12, initError = true); + $$invalidate(5, loading = false); + $$invalidate(22, abort = false); return; } if (updatesResponse.error) { - $$invalidate(0, error = updatesResponse.error); - $$invalidate(2, loading = false); - $$invalidate(3, disabled = false); - $$invalidate(21, abort = false); + $$invalidate(3, error = updatesResponse.error); + $$invalidate(5, loading = false); + $$invalidate(6, disabled = false); + $$invalidate(22, abort = false); return; } else { - $$invalidate(6, updates = updatesResponse.updates); + $$invalidate(8, updates = updatesResponse.updates); } - $$invalidate(21, abort = false); + $$invalidate(22, abort = false); } // check if current page url is a userscript @@ -4298,18 +4903,18 @@ if (response.error) { console.log(`Error checking .user.js url: ${response.error}`); - $$invalidate(0, error = response.error); + $$invalidate(3, error = response.error); } else if (!response.invalid) { // the response will contain the string to display // ex: {success: "Click to install"} const prompt = response.success; - $$invalidate(15, showInstallPrompt = prompt); + $$invalidate(16, showInstallPrompt = prompt); } } - $$invalidate(2, loading = false); - $$invalidate(3, disabled = false); + $$invalidate(5, loading = false); + $$invalidate(6, disabled = false); } async function resize() { @@ -4327,7 +4932,7 @@ document.body.removeAttribute("style"); return; } else { - $$invalidate(7, main.style.maxHeight = "unset", main); + $$invalidate(9, main.style.maxHeight = "unset", main); document.body.style.width = "100vw"; } } @@ -4344,8 +4949,8 @@ if (err) addHeight += err.offsetHeight; windowHeight = window.outerHeight - (headerHeight + addHeight); - $$invalidate(7, main.style.height = `${windowHeight}px`, main); - $$invalidate(7, main.style.paddingBottom = `${headerHeight + addHeight}px`, main); + $$invalidate(9, main.style.height = `${windowHeight}px`, main); + $$invalidate(9, main.style.paddingBottom = `${headerHeight + addHeight}px`, main); }, 25 ); @@ -4353,10 +4958,10 @@ async function showInstallView() { // disable all buttons - $$invalidate(3, disabled = true); + $$invalidate(6, disabled = true); // show the install view - $$invalidate(16, showInstall = true); + $$invalidate(17, showInstall = true); // get the active tab const currentTab = await browser.tabs.getCurrent(); @@ -4373,23 +4978,23 @@ // if the response includes an error, display it in the view if (response.error) { console.log(`Can not install userscript: ${response.error}`); - $$invalidate(18, installViewUserscriptError = response.error); + $$invalidate(19, installViewUserscriptError = response.error); } else { - $$invalidate(17, installViewUserscript = response); + $$invalidate(18, installViewUserscript = response); } - $$invalidate(3, disabled = false); + $$invalidate(6, disabled = false); } async function installConfirm() { // disabled all buttons - $$invalidate(3, disabled = true); + $$invalidate(6, disabled = true); // show loading element - $$invalidate(2, loading = true); + $$invalidate(5, loading = true); // go back to main view - $$invalidate(16, showInstall = false); + $$invalidate(17, showInstall = false); // get the active tab const currentTab = await browser.tabs.getCurrent(); @@ -4398,9 +5003,9 @@ const response = await browser.tabs.sendMessage(currentTab.id, { name: "USERSCRIPT_INSTALL_02" }); if (response.error) { - $$invalidate(0, error = response.error); - $$invalidate(3, disabled = false); - $$invalidate(2, loading = false); + $$invalidate(3, error = response.error); + $$invalidate(6, disabled = false); + $$invalidate(5, loading = false); return; } else { // if response did not have an error, userscript installed successfully @@ -4416,28 +5021,28 @@ resize(); }); - const click_handler = () => $$invalidate(5, showUpdates = true); + const click_handler = () => $$invalidate(7, showUpdates = true); function div0_binding($$value) { - binding_callbacks[$$value ? "unshift" : "push"](() => { + binding_callbacks[$$value ? 'unshift' : 'push'](() => { header = $$value; - $$invalidate(12, header); + $$invalidate(13, header); }); } function div_binding($$value) { - binding_callbacks[$$value ? "unshift" : "push"](() => { + binding_callbacks[$$value ? 'unshift' : 'push'](() => { warn = $$value; - $$invalidate(13, warn); + $$invalidate(14, warn); }); } - const click_handler_1 = () => $$invalidate(0, error = undefined); + const click_handler_1 = () => $$invalidate(3, error = undefined); function div_binding_1($$value) { - binding_callbacks[$$value ? "unshift" : "push"](() => { + binding_callbacks[$$value ? 'unshift' : 'push'](() => { err = $$value; - $$invalidate(14, err); + $$invalidate(15, err); }); } @@ -4445,55 +5050,54 @@ const click_handler_3 = item => toggleItem(item); function div1_binding($$value) { - binding_callbacks[$$value ? "unshift" : "push"](() => { + binding_callbacks[$$value ? 'unshift' : 'push'](() => { main = $$value; - $$invalidate(7, main); + $$invalidate(9, main); }); } - const func = () => $$invalidate(5, showUpdates = false); - const func_1 = () => $$invalidate(16, showInstall = false); - const func_2 = () => $$invalidate(16, showInstall = false); + const func = () => $$invalidate(7, showUpdates = false); + const func_1 = () => $$invalidate(17, showInstall = false); + const func_2 = () => $$invalidate(17, showInstall = false); const func_3 = () => { - $$invalidate(19, showAll = false); + $$invalidate(20, showAll = false); refreshView(); }; - let list; - $$self.$$.update = () => { - if ($$self.$$.dirty[0] & /*items*/ 16) { - $$invalidate(22, list = items.sort((a, b) => a.name.localeCompare(b.name))); + if ($$self.$$.dirty[0] & /*items*/ 1) { + $$invalidate(2, list = items.sort((a, b) => a.name.localeCompare(b.name))); } - if ($$self.$$.dirty[0] & /*list*/ 4194304) { + if ($$self.$$.dirty[0] & /*list*/ 4) { if (list.length > 1 && list.length % 2 === 0) { - $$invalidate(8, rowColors = "even"); + $$invalidate(10, rowColors = "even"); } else if (list.length > 1 && list.length % 2 !== 0) { - $$invalidate(8, rowColors = "odd"); + $$invalidate(10, rowColors = "odd"); } else { - $$invalidate(8, rowColors = undefined); + $$invalidate(10, rowColors = undefined); } } - if ($$self.$$.dirty[0] & /*platform*/ 1024) { + if ($$self.$$.dirty[0] & /*platform*/ 2) { if (platform) document.body.classList.add(platform); } }; return [ + items, + platform, + list, error, active, loading, disabled, - items, showUpdates, updates, main, rowColors, inactive, - platform, initError, header, warn, @@ -4505,7 +5109,6 @@ showAll, allItems, abort, - list, toggleExtension, updateAll, updateItem, @@ -4534,7 +5137,7 @@ class App extends SvelteComponent { constructor(options) { super(); - init(this, options, instance$9, create_fragment$9, safe_not_equal, {}, [-1, -1]); + init(this, options, instance$9, create_fragment$9, safe_not_equal, {}, null, [-1, -1]); } } diff --git a/src/page/Components/Settings.svelte b/src/page/Components/Settings.svelte index a8933271..96833d7c 100644 --- a/src/page/Components/Settings.svelte +++ b/src/page/Components/Settings.svelte @@ -1,6 +1,6 @@