From 2561f2a16a6e3da3558229180ae44f1dc71d50f7 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Fri, 25 Dec 2020 21:39:48 +0300 Subject: [PATCH 1/8] Inserted an id (matching the id used by the ext on AMO) without which Firefox refuses to load the ext. --- manifest.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/manifest.json b/manifest.json index 38b80c4a..5e42c8f0 100644 --- a/manifest.json +++ b/manifest.json @@ -7,6 +7,11 @@ "48": "icons/icon-48x48.png", "128": "icons/icon-128x128.png" }, + "applications" : { + "gecko" : { + "id" : "{9a253c57-0e95-4589-be64-365b3602c564}" + } + }, "browser_action": { "default_icon": { "16": "icons/icon-16x16.png" From 50899406f691d23fbbb8682349ac494ffa83ed93 Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Fri, 25 Dec 2020 21:39:48 +0300 Subject: [PATCH 2/8] Refactored chrome.runtime.onMessage.addListener callbacks. --- src/background/background.js | 63 +++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/src/background/background.js b/src/background/background.js index 650b704a..4dda6b7d 100644 --- a/src/background/background.js +++ b/src/background/background.js @@ -36,22 +36,25 @@ bgapp.syncFunctions = []; }; - chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { - if (request.action === "saveDomain") { + const messageEventsProcessors = new Map([ + ["saveDomain", (request, sender) => { bgapp.mainStorage.put(request.data) .then(syncAllInstances) .catch(simpleError); bgapp.ruleDomains[request.data.id] = request.data; - } else if (request.action === "getDomains") { - bgapp.mainStorage.getAll().then(function(domains) { - sendResponse(domains || []); - }).catch(simpleError); - } else if (request.action === "deleteDomain") { + }], + ["getDomains", (request, sender) => { + return bgapp.mainStorage.getAll().then(function(domains) { + return domains || []; + }); + }], + ["deleteDomain", (request, sender) => { bgapp.mainStorage.delete(request.id) .then(syncAllInstances) .catch(simpleError); delete bgapp.ruleDomains[request.id]; - } else if (request.action === "import") { + }], + ["import", (request, sender) => { let maxId = 0; for (const id in bgapp.ruleDomains) { maxId = Math.max(maxId, parseInt(id.substring(1))); @@ -65,29 +68,31 @@ })) .then(syncAllInstances) .catch(simpleError); - } else if (request.action === "makeGetRequest") { - const xhr = new XMLHttpRequest(); - xhr.open("GET", request.url, true); - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - sendResponse(xhr.responseText); - } - }; - xhr.send(); - } else if (request.action === "setSetting") { + }], + ["makeGetRequest", (request, sender) => { + return fetch(request.url, {"headers": {"Origin": null}, "referrer": "no-referrer"}).then(e=>e.text()); + }], + ["setSetting", (request, sender) => { localStorage[request.setting] = request.value; - } else if (request.action === "getSetting") { - sendResponse(localStorage[request.setting]); - } else if (request.action === "syncMe") { - bgapp.syncFunctions.push(sendResponse); - } else if (request.action === "match") { - sendResponse(match(request.domainUrl, request.windowUrl).matched); - } else if (request.action === "extractMimeType") { - sendResponse(bgapp.extractMimeType(request.fileName, request.file)); - } + }], + ["getSetting", (request, sender) => { + return Promise.resolve(localStorage[request.setting]); + }], + ["syncMe", (request, sender) => { + return new Promise((resolve, reject) => { + bgapp.syncFunctions.push(resolve); + }); + }], + ["match", (request, sender) => { + return Promise.resolve(match(request.domainUrl, request.windowUrl).matched); + }], + ["extractMimeType", (request, sender) => { + return Promise.resolve(bgapp.extractMimeType(request.fileName, request.file)); + }], + ]); - // !!!Important!!! Need to return true for sendResponse to work. - return true; + chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { + return messageEventsProcessors.get(request.action)(request, sender); }); chrome.webRequest.onBeforeRequest.addListener(function(details) { From 96d9df34d80f1c35d1a7cd0519b7bd90b393cd6d Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Fri, 25 Dec 2020 21:39:48 +0300 Subject: [PATCH 3/8] Refactored some else if ladders into key lookups. --- src/ui/importExport.js | 41 ++++++++---------- src/ui/tabGroup.js | 95 +++++++++++++++++++++++------------------- 2 files changed, 70 insertions(+), 66 deletions(-) diff --git a/src/ui/importExport.js b/src/ui/importExport.js index 19a2b99a..b0a978cf 100644 --- a/src/ui/importExport.js +++ b/src/ui/importExport.js @@ -6,31 +6,24 @@ const app = window.app; const util = app.util; + const checkRulesMap = new Map([ + ["normalOverride", (rule) => { + return (rule.match !== undefined && rule.replace !== undefined && rule.on !== undefined); + }], + ["fileOverride", (rule) => { + return (rule.match !== undefined && rule.file !== undefined && (/^f[0-9]+$/).test(rule.fileId) && rule.on !== undefined); + }], + ["fileInject", (rule) => { + return (rule.fileName !== undefined && rule.file !== undefined && (/^f[0-9]+$/).test(rule.fileId) && rule.fileType !== undefined && rule.injectLocation !== undefined && rule.on !== undefined); + }], + ["headerRule", (rule) => { + return (rule.match !== undefined && rule.requestRules !== undefined && rule.responseRules !== undefined && rule.on !== undefined); + }], + ]); + + function checkRule(rule) { - let valid = true; - if (rule.type === "normalOverride") { - valid = valid && rule.match !== undefined; - valid = valid && rule.replace !== undefined; - valid = valid && rule.on !== undefined; - } else if (rule.type === "fileOverride") { - valid = valid && rule.match !== undefined; - valid = valid && rule.file !== undefined; - valid = valid && (/^f[0-9]+$/).test(rule.fileId); - valid = valid && rule.on !== undefined; - } else if (rule.type === "fileInject") { - valid = valid && rule.fileName !== undefined; - valid = valid && rule.file !== undefined; - valid = valid && (/^f[0-9]+$/).test(rule.fileId); - valid = valid && rule.fileType !== undefined; - valid = valid && rule.injectLocation !== undefined; - valid = valid && rule.on !== undefined; - } else if (rule.type === "headerRule") { - valid = valid && rule.match !== undefined; - valid = valid && rule.requestRules !== undefined; - valid = valid && rule.responseRules !== undefined; - valid = valid && rule.on !== undefined; - } - return valid; + return checkRulesMap.get(rule.type)(rule); } function checkDomain(domain) { diff --git a/src/ui/tabGroup.js b/src/ui/tabGroup.js index 84c43fb5..7b031b42 100644 --- a/src/ui/tabGroup.js +++ b/src/ui/tabGroup.js @@ -51,43 +51,55 @@ }; } + + const domainDataGetterMap = new Map([ + ["normalOverride", ($el) => { + return { + type: "normalOverride", + match: $el.find(".matchInput").val(), + replace: $el.find(".replaceInput").val(), + on: $el.find(".onoffswitch")[0].isOn + } + }], + ["fileOverride", ($el) => { + return { + type: "fileOverride", + match: $el.find(".matchInput").val(), + file: app.files[el.id] || "", + fileId: el.id, + on: $el.find(".onoffswitch")[0].isOn + } + }], + ["fileInject", ($el) => { + return { + type: "fileInject", + fileName: $el.find(".fileName").val(), + file: app.files[el.id] || "", + fileId: el.id, + fileType: $el.find(".fileTypeSelect").val(), + injectLocation: $el.find(".injectLocationSelect").val(), + on: $el.find(".onoffswitch")[0].isOn + } + }], + ["headerRule", ($el) => { + return { + type: "headerRule", + match: $el.find(".matchInput").val(), + requestRules: $el.find(".requestRules").data("rules") || "", + responseRules: $el.find(".responseRules").data("rules") || "", + on: $el.find(".onoffswitch")[0].isOn + } + }], + ]); function getDomainData(domain) { const rules = []; domain.find(".ruleContainer").each(function(idx, el) { const $el = $(el); - if ($el.hasClass("normalOverride")) { - rules.push({ - type: "normalOverride", - match: $el.find(".matchInput").val(), - replace: $el.find(".replaceInput").val(), - on: $el.find(".onoffswitch")[0].isOn - }); - } else if ($el.hasClass("fileOverride")) { - rules.push({ - type: "fileOverride", - match: $el.find(".matchInput").val(), - file: app.files[el.id] || "", - fileId: el.id, - on: $el.find(".onoffswitch")[0].isOn - }); - } else if ($el.hasClass("fileInject")) { - rules.push({ - type: "fileInject", - fileName: $el.find(".fileName").val(), - file: app.files[el.id] || "", - fileId: el.id, - fileType: $el.find(".fileTypeSelect").val(), - injectLocation: $el.find(".injectLocationSelect").val(), - on: $el.find(".onoffswitch")[0].isOn - }); - } else if ($el.hasClass("headerRule")) { - rules.push({ - type: "headerRule", - match: $el.find(".matchInput").val(), - requestRules: $el.find(".requestRules").data("rules") || "", - responseRules: $el.find(".responseRules").data("rules") || "", - on: $el.find(".onoffswitch")[0].isOn - }); + for(let [cand, domainDataGetter] of domainDataGetterMap){ + if ($el.hasClass()) { + rules.push(domainDataGetter($el)); + break; + } } }); @@ -99,6 +111,13 @@ }; } + let domainMarkupCreatorMap = new Map([ + ["normalOverride", app.createWebOverrideMarkup], + ["fileOverride", app.createFileOverrideMarkup], + ["fileInject", app.createFileInjectMarkup], + ["headerRule", app.createHeaderRuleMarkup], + ]); + function createDomainMarkup(savedData) { savedData = savedData || {}; const domain = util.instanceTemplate(ui.domainTemplate); @@ -115,15 +134,7 @@ if (rules.length) { rules.forEach(function(rule) { - if (rule.type === "normalOverride") { - overrideRulesContainer.append(app.createWebOverrideMarkup(rule, saveFunc)); - } else if (rule.type === "fileOverride") { - overrideRulesContainer.append(app.createFileOverrideMarkup(rule, saveFunc)); - } else if (rule.type === "fileInject") { - overrideRulesContainer.append(app.createFileInjectMarkup(rule, saveFunc)); - } else if (rule.type === "headerRule") { - overrideRulesContainer.append(app.createHeaderRuleMarkup(rule, saveFunc)); - } + overrideRulesContainer.append(domainMarkupCreatorMap.get(rule.type)(rule, saveFunc)); }); } From 30c1e4681e6d5878e44cdd6e63a46b59a671bf3d Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Fri, 25 Dec 2020 21:39:48 +0300 Subject: [PATCH 4/8] Moved rules into an own subdir. --- src/ui/devtoolstab.html | 8 ++++---- src/ui/{fileRule.js => rules/file.js} | 0 src/ui/{headerRule.js => rules/header.js} | 0 src/ui/{injectRule.js => rules/inject.js} | 0 src/ui/{webRule.js => rules/web.js} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename src/ui/{fileRule.js => rules/file.js} (100%) rename src/ui/{headerRule.js => rules/header.js} (100%) rename src/ui/{injectRule.js => rules/inject.js} (100%) rename src/ui/{webRule.js => rules/web.js} (100%) diff --git a/src/ui/devtoolstab.html b/src/ui/devtoolstab.html index 66b75b11..0714d943 100644 --- a/src/ui/devtoolstab.html +++ b/src/ui/devtoolstab.html @@ -372,10 +372,10 @@

Tips and Tricks

- - - - + + + + diff --git a/src/ui/fileRule.js b/src/ui/rules/file.js similarity index 100% rename from src/ui/fileRule.js rename to src/ui/rules/file.js diff --git a/src/ui/headerRule.js b/src/ui/rules/header.js similarity index 100% rename from src/ui/headerRule.js rename to src/ui/rules/header.js diff --git a/src/ui/injectRule.js b/src/ui/rules/inject.js similarity index 100% rename from src/ui/injectRule.js rename to src/ui/rules/inject.js diff --git a/src/ui/webRule.js b/src/ui/rules/web.js similarity index 100% rename from src/ui/webRule.js rename to src/ui/rules/web.js From a5667909c5c1fe659c6804d548570c5e498d4c7f Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Fri, 25 Dec 2020 21:39:48 +0300 Subject: [PATCH 5/8] Converted everything (except dependencies) in GUI into ECMAScript modules --- src/ui/devtools.html | 2 +- src/ui/devtools.js | 8 +- src/ui/devtoolstab.html | 41 ++-- src/ui/devtoolstab.js | 235 ++++++++++---------- src/ui/editor.js | 378 ++++++++++++++++---------------- src/ui/headerEditor.js | 257 +++++++++++----------- src/ui/headers.js | 200 +++++++++-------- src/ui/importExport.js | 113 +++++----- src/ui/init.js | 23 +- src/ui/moveableRules.js | 314 +++++++++++++-------------- src/ui/onOffSwitch.js | 41 ++-- src/ui/options.js | 178 ++++++++------- src/ui/rules/file.js | 105 +++++---- src/ui/rules/header.js | 227 ++++++++++--------- src/ui/rules/inject.js | 113 +++++----- src/ui/rules/web.js | 99 +++++---- src/ui/suggest.js | 467 ++++++++++++++++++++-------------------- src/ui/tabGroup.js | 370 +++++++++++++++---------------- src/ui/util.js | 200 +++++++++-------- 19 files changed, 1674 insertions(+), 1697 deletions(-) diff --git a/src/ui/devtools.html b/src/ui/devtools.html index d7bc422f..d8b7224a 100644 --- a/src/ui/devtools.html +++ b/src/ui/devtools.html @@ -4,6 +4,6 @@ - + diff --git a/src/ui/devtools.js b/src/ui/devtools.js index 715f06b0..f2e4a7f8 100644 --- a/src/ui/devtools.js +++ b/src/ui/devtools.js @@ -1,9 +1,15 @@ +"use strict"; + chrome.runtime.sendMessage({action: "getSetting", setting: "devTools"}, function(data) { if (data === "true") { chrome.devtools.panels.create("Overrides", "", //image file "/src/ui/devtoolstab.html", - function(panel) {} + function(panel) { + console.log(panel); + panel.onShown.addListener(console.log); + panel.onHidden.addListener(console.log); + } ); } }); diff --git a/src/ui/devtoolstab.html b/src/ui/devtoolstab.html index 0714d943..92062c78 100644 --- a/src/ui/devtoolstab.html +++ b/src/ui/devtoolstab.html @@ -362,24 +362,33 @@

Tips and Tricks

- - - + + + - - - + + + - - - - - - - - - + + + + + + + + + - + + + diff --git a/src/ui/devtoolstab.js b/src/ui/devtoolstab.js index 004d666f..42645408 100644 --- a/src/ui/devtoolstab.js +++ b/src/ui/devtoolstab.js @@ -1,128 +1,127 @@ -(function() { - "use strict"; - - /* globals chrome */ - - const app = window.app; - const ui = app.ui; - const util = app.util; - - app.mainSuggest = app.suggest(); - app.requestHeadersSuggest = app.suggest(); - app.responseHeadersSuggest = app.suggest(); - app.files = {}; - app.skipNextSync = false; - - function renderData() { - app.files = {}; - ui.domainDefs.children().remove(); - chrome.runtime.sendMessage({action: "getDomains"}, function(domains) { - if (domains.length) { - domains.forEach(function(domain) { - const domainMarkup = app.createDomainMarkup(domain); - ui.domainDefs.append(domainMarkup); - }); - } else { - const newDomain = app.createDomainMarkup({rules: [{type: "normalOverride"}]}); - ui.domainDefs.append(newDomain); - newDomain.find(".domainMatchInput").val("*"); - chrome.runtime.sendMessage({ - action: "saveDomain", - data: app.getDomainData(newDomain) - }); - app.skipNextSync = true; - } - util.getTabResources(function(res) { - app.mainSuggest.fillOptions(res); +"use strict"; + +/* globals chrome */ + +import {app, ui} from './init.js'; +import {suggest} from './suggest.js'; +import {createDomainMarkup, getDomainData} from './tabGroup.js'; +import {headersLists} from './headers.js'; +import {getTabResources} from './util.js'; + +export const mainSuggest = suggest(); +export const requestHeadersSuggest = suggest(); +export const responseHeadersSuggest = suggest(); +export let files = {}; +export let skipNextSync = false; + +function renderData() { + files = {}; + ui.domainDefs.children().remove(); + chrome.runtime.sendMessage({action: "getDomains"}, function(domains) { + if (domains.length) { + domains.forEach(function(domain) { + const domainMarkup = createDomainMarkup(domain); + ui.domainDefs.append(domainMarkup); }); - }); - } - - function setupSynchronizeConnection() { - chrome.runtime.sendMessage({action: "syncMe"}, function() { - if (!app.skipNextSync) { - renderData(); - } - app.skipNextSync = false; - setupSynchronizeConnection(); - }); - } - - function init() { - app.mainSuggest.init(); - app.requestHeadersSuggest.init(); - app.responseHeadersSuggest.init(); - app.requestHeadersSuggest.fillOptions(app.headersLists.requestHeaders); - app.responseHeadersSuggest.fillOptions(app.headersLists.responseHeaders); - - setupSynchronizeConnection(); - - renderData(); - - ui.addDomainBtn.on("click", function() { - const newDomain = app.createDomainMarkup(); - newDomain.find(".domainMatchInput").val("*"); + } else { + const newDomain = createDomainMarkup({rules: [{type: "normalOverride"}]}); ui.domainDefs.append(newDomain); - chrome.runtime.sendMessage({action: "saveDomain", data: app.getDomainData(newDomain)}); - app.skipNextSync = true; - }); - - ui.helpBtn.on("click", function() { - ui.helpOverlay.toggle(); - }); - - ui.helpCloseBtn.on("click", function() { - ui.helpOverlay.hide(); - }); - - if (!chrome.devtools) { - ui.showSuggestions.hide(); - ui.showSuggestionsText.hide(); + newDomain.find(".domainMatchInput").val("*"); chrome.runtime.sendMessage({ - action: "getSetting", - setting: "tabPageNotice" - }, function(data) { - - if (data !== "true") { - ui.tabPageNotice.find("a").on("click", function(e) { - e.preventDefault(); - chrome.runtime.sendMessage({ - action: "setSetting", - setting: "tabPageNotice", - value: "true" - }); - ui.tabPageNotice.fadeOut(); - }); - ui.tabPageNotice.fadeIn(); - setTimeout(function() { - ui.tabPageNotice.fadeOut(); - }, 6000); - } + action: "saveDomain", + data: getDomainData(newDomain) }); + app.skipNextSync = true; } + getTabResources(function(res) { + mainSuggest.fillOptions(res); + }); + }); +} - if (navigator.userAgent.indexOf("Firefox") > -1 && !!chrome.devtools) { - // Firefox is really broken with the "/" and "'" keys. They just dont work. - // So try to fix them here.. wow.. just wow. I can't believe I'm fixing the ability to type. - const brokenKeys = { "/": 1, "?": 1, "'": 1, '"': 1 }; - window.addEventListener("keydown", e => { - const brokenKey = brokenKeys[e.key]; - const activeEl = document.activeElement; - if (brokenKey && (activeEl.nodeName === "INPUT" || activeEl.nodeName === "TEXTAREA") && - activeEl.className !== "ace_text-input") { - - e.preventDefault(); - const start = activeEl.selectionStart; - const end = activeEl.selectionEnd; - activeEl.value = activeEl.value.substring(0, start) + e.key + - activeEl.value.substring(end, activeEl.value.length); - activeEl.selectionStart = start + 1; - activeEl.selectionEnd = start + 1; - } - }); +function setupSynchronizeConnection() { + chrome.runtime.sendMessage({action: "syncMe"}, function() { + if (!app.skipNextSync) { + renderData(); } + app.skipNextSync = false; + setupSynchronizeConnection(); + }); +} + +function init() { + mainSuggest.init(); + requestHeadersSuggest.init(); + responseHeadersSuggest.init(); + requestHeadersSuggest.fillOptions(headersLists.requestHeaders); + responseHeadersSuggest.fillOptions(headersLists.responseHeaders); + + setupSynchronizeConnection(); + + renderData(); + + ui.addDomainBtn.on("click", function() { + const newDomain = createDomainMarkup(); + newDomain.find(".domainMatchInput").val("*"); + ui.domainDefs.append(newDomain); + chrome.runtime.sendMessage({action: "saveDomain", data: getDomainData(newDomain)}); + app.skipNextSync = true; + }); + + ui.helpBtn.on("click", function() { + ui.helpOverlay.toggle(); + }); + + ui.helpCloseBtn.on("click", function() { + ui.helpOverlay.hide(); + }); + + if (!chrome.devtools) { + ui.showSuggestions.hide(); + ui.showSuggestionsText.hide(); + chrome.runtime.sendMessage({ + action: "getSetting", + setting: "tabPageNotice" + }, function(data) { + + if (data !== "true") { + ui.tabPageNotice.find("a").on("click", function(e) { + e.preventDefault(); + chrome.runtime.sendMessage({ + action: "setSetting", + setting: "tabPageNotice", + value: "true" + }); + ui.tabPageNotice.fadeOut(); + }); + ui.tabPageNotice.fadeIn(); + setTimeout(function() { + ui.tabPageNotice.fadeOut(); + }, 6000); + } + }); } - init(); + if (navigator.userAgent.indexOf("Firefox") > -1 && !!chrome.devtools) { + // Firefox is really broken with the "/" and "'" keys. They just dont work. + // So try to fix them here.. wow.. just wow. I can't believe I'm fixing the ability to type. + const brokenKeys = { "/": 1, "?": 1, "'": 1, '"': 1 }; + window.addEventListener("keydown", e => { + const brokenKey = brokenKeys[e.key]; + const activeEl = document.activeElement; + if (brokenKey && (activeEl.nodeName === "INPUT" || activeEl.nodeName === "TEXTAREA") && + activeEl.className !== "ace_text-input") { + + e.preventDefault(); + const start = activeEl.selectionStart; + const end = activeEl.selectionEnd; + activeEl.value = activeEl.value.substring(0, start) + e.key + + activeEl.value.substring(end, activeEl.value.length); + activeEl.selectionStart = start + 1; + activeEl.selectionEnd = start + 1; + } + }); + } +} -})(); +init(); diff --git a/src/ui/editor.js b/src/ui/editor.js index 4f4bf713..5c5f3e11 100644 --- a/src/ui/editor.js +++ b/src/ui/editor.js @@ -1,190 +1,188 @@ -(function() { - "use strict"; - - /* globals $, chrome, ace, js_beautify */ - - const app = window.app; - const ui = app.ui; - const util = app.util; - - let editor; - let editingFile; - let saveFunc; - - function updateSaveButtons(edited) { - if (edited) { - ui.fileSaveAndCloseBtn.css("color", "#ff0000"); - ui.fileSaveBtn.css("color", "#ff0000"); - } else { - ui.fileSaveAndCloseBtn.css("color", "#000000"); - ui.fileSaveBtn.css("color", "#000000"); - } - } - - function setEditorVal(str) { - editor.off("change", updateSaveButtons); - editor.setValue(str); - editor.gotoLine(0, 0, false); - editor.on("change", updateSaveButtons); - } - - function editorGuessMode(fileName, file) { - chrome.runtime.sendMessage({ - action: "extractMimeType", - file: file, - fileName: fileName - }, function(data) { - const mimeToEditorSyntax = { - "text/javascript": "javascript", - "text/html": "html", - "text/css": "css", - "text/xml": "xml" - }; - const mode = mimeToEditorSyntax[data.mime] || "javascript"; - ui.syntaxSelect.val(mode); - editor.getSession().setMode("ace/mode/" + mode); - }); - } - - function saveFile() { - updateSaveButtons(); - app.files[editingFile] = editor.getValue(); - saveFunc(); - } - - function saveFileAndClose() { - saveFile(); - ui.editorOverlay.hide(); - ui.body.css("overflow", "auto"); - } - - function setupEditor() { - editor = ace.edit("editor"); - editor.setTheme("ace/theme/monokai"); - editor.setShowPrintMargin(false); - - editor.on("change", updateSaveButtons); - editor.commands.addCommand({ - name: "multiEdit", - bindKey: { - win: "Ctrl-D", - mac: "Command-D" - }, - exec: function(editor, line) { - editor.selectMore(1); - }, - readOnly: true - }); - editor.commands.addCommand({ - name: "save", - bindKey: { - win: "Ctrl-S", - mac: "Command-S" - }, - exec: function(editor, line) { - saveFile(); - }, - readOnly: true - }); - editor.commands.addCommand({ - name: "saveAndClose", - bindKey: { - win: "Ctrl-Shift-S", - mac: "Command-Shift-S" - }, - exec: function(editor, line) { - saveFileAndClose(); - }, - readOnly: true - }); - } - - function openEditor(fileId, match, isInjectFile, saveFunction) { - saveFunc = saveFunction; - editingFile = fileId; - updateSaveButtons(); - ui.editorOverlay.css("display", "flex"); - ui.body.css("overflow", "hidden"); - if (!editor) { - setupEditor(); - } - match = match || ""; - ui.editLabel.text(isInjectFile ? "Editing file:" : "Editing file for match:"); - ui.matchContainer.text(match); - - editorGuessMode(match, app.files[fileId]); - - if (chrome.devtools && util.isChrome()) { - ui.loadSelect.show(); - util.getTabResources(function(filteredList) { - ui.loadSelect.html(""); - filteredList.forEach(function(url) { - const $newOpt = $("