summaryrefslogtreecommitdiff
path: root/ext/bg/js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/bg/js')
-rw-r--r--ext/bg/js/backend.js40
-rw-r--r--ext/bg/js/profile-conditions.js256
-rw-r--r--ext/bg/js/settings/conditions-ui.js6
-rw-r--r--ext/bg/js/settings/main.js19
-rw-r--r--ext/bg/js/settings/profiles.js4
5 files changed, 169 insertions, 156 deletions
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index 9936baf8..557ceb29 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -24,6 +24,7 @@
* ClipboardMonitor
* Database
* DictionaryImporter
+ * Environment
* JsonSchema
* Mecab
* ObjectPropertyAccessor
@@ -35,6 +36,7 @@
* optionsLoad
* optionsSave
* profileConditionsDescriptor
+ * profileConditionsDescriptorPromise
* requestJson
* requestText
* utilIsolate
@@ -42,6 +44,7 @@
class Backend {
constructor() {
+ this.environment = new Environment();
this.database = new Database();
this.dictionaryImporter = new DictionaryImporter();
this.translator = new Translator(this.database);
@@ -100,7 +103,7 @@ class Backend {
['broadcastTab', {async: false, contentScript: true, handler: this._onApiBroadcastTab.bind(this)}],
['frameInformationGet', {async: true, contentScript: true, handler: this._onApiFrameInformationGet.bind(this)}],
['injectStylesheet', {async: true, contentScript: true, handler: this._onApiInjectStylesheet.bind(this)}],
- ['getEnvironmentInfo', {async: true, contentScript: true, handler: this._onApiGetEnvironmentInfo.bind(this)}],
+ ['getEnvironmentInfo', {async: false, contentScript: true, handler: this._onApiGetEnvironmentInfo.bind(this)}],
['clipboardGet', {async: true, contentScript: true, handler: this._onApiClipboardGet.bind(this)}],
['getDisplayTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetDisplayTemplatesHtml.bind(this)}],
['getQueryParserTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetQueryParserTemplatesHtml.bind(this)}],
@@ -140,9 +143,12 @@ class Backend {
}, 1000);
this._updateBadge();
+ await this.environment.prepare();
await this.database.prepare();
await this.translator.prepare();
+ await profileConditionsDescriptorPromise;
+
this.optionsSchema = await requestJson(chrome.runtime.getURL('/bg/data/options-schema.json'), 'GET');
this.defaultAnkiFieldTemplates = await requestText(chrome.runtime.getURL('/bg/data/default-anki-field-templates.handlebars'), 'GET');
this.options = await optionsLoad();
@@ -635,15 +641,8 @@ class Backend {
});
}
- async _onApiGetEnvironmentInfo() {
- const browser = await Backend._getBrowser();
- const platform = await new Promise((resolve) => chrome.runtime.getPlatformInfo(resolve));
- return {
- browser,
- platform: {
- os: platform.os
- }
- };
+ _onApiGetEnvironmentInfo() {
+ return this.environment.getInfo();
}
async _onApiClipboardGet() {
@@ -659,7 +658,7 @@ class Backend {
being an extension with clipboard permissions. It effectively asks for the
non-extension permission for clipboard access.
*/
- const browser = await Backend._getBrowser();
+ const {browser} = this.environment.getInfo();
if (browser === 'firefox' || browser === 'firefox-mobile') {
return await navigator.clipboard.readText();
} else {
@@ -1211,23 +1210,4 @@ class Backend {
// Edge throws exception for no reason here.
}
}
-
- static async _getBrowser() {
- if (EXTENSION_IS_BROWSER_EDGE) {
- return 'edge';
- }
- if (typeof browser !== 'undefined') {
- try {
- const info = await browser.runtime.getBrowserInfo();
- if (info.name === 'Fennec') {
- return 'firefox-mobile';
- }
- } catch (e) {
- // NOP
- }
- return 'firefox';
- } else {
- return 'chrome';
- }
- }
}
diff --git a/ext/bg/js/profile-conditions.js b/ext/bg/js/profile-conditions.js
index 32309c64..97e09f1c 100644
--- a/ext/bg/js/profile-conditions.js
+++ b/ext/bg/js/profile-conditions.js
@@ -15,6 +15,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+/* global
+ * Environment
+ */
function _profileConditionTestDomain(urlDomain, domain) {
return (
@@ -36,135 +39,140 @@ function _profileConditionTestDomainList(url, domainList) {
return false;
}
-const _profileModifierKeys = [
- {optionValue: 'alt', name: 'Alt'},
- {optionValue: 'ctrl', name: 'Ctrl'},
- {optionValue: 'shift', name: 'Shift'}
-];
+let profileConditionsDescriptor = null;
-if (!hasOwn(window, 'netscape')) {
- _profileModifierKeys.push({optionValue: 'meta', name: 'Meta'});
-}
+const profileConditionsDescriptorPromise = (async () => {
+ const environment = new Environment();
+ await environment.prepare();
-const _profileModifierValueToName = new Map(
- _profileModifierKeys.map(({optionValue, name}) => [optionValue, name])
-);
+ const modifiers = environment.getInfo().modifiers;
+ const modifierSeparator = modifiers.separator;
+ const modifierKeyValues = modifiers.keys.map(
+ ({value, name}) => ({optionValue: value, name})
+ );
-const _profileModifierNameToValue = new Map(
- _profileModifierKeys.map(({optionValue, name}) => [name, optionValue])
-);
+ const modifierValueToName = new Map(
+ modifierKeyValues.map(({optionValue, name}) => [optionValue, name])
+ );
-const profileConditionsDescriptor = {
- popupLevel: {
- name: 'Popup Level',
- description: 'Use profile depending on the level of the popup.',
- placeholder: 'Number',
- type: 'number',
- step: 1,
- defaultValue: 0,
- defaultOperator: 'equal',
- transform: (optionValue) => parseInt(optionValue, 10),
- transformReverse: (transformedOptionValue) => `${transformedOptionValue}`,
- validateTransformed: (transformedOptionValue) => Number.isFinite(transformedOptionValue),
- operators: {
- equal: {
- name: '=',
- test: ({depth}, optionValue) => (depth === optionValue)
- },
- notEqual: {
- name: '\u2260',
- test: ({depth}, optionValue) => (depth !== optionValue)
- },
- lessThan: {
- name: '<',
- test: ({depth}, optionValue) => (depth < optionValue)
- },
- greaterThan: {
- name: '>',
- test: ({depth}, optionValue) => (depth > optionValue)
- },
- lessThanOrEqual: {
- name: '\u2264',
- test: ({depth}, optionValue) => (depth <= optionValue)
- },
- greaterThanOrEqual: {
- name: '\u2265',
- test: ({depth}, optionValue) => (depth >= optionValue)
+ const modifierNameToValue = new Map(
+ modifierKeyValues.map(({optionValue, name}) => [name, optionValue])
+ );
+
+ profileConditionsDescriptor = {
+ popupLevel: {
+ name: 'Popup Level',
+ description: 'Use profile depending on the level of the popup.',
+ placeholder: 'Number',
+ type: 'number',
+ step: 1,
+ defaultValue: 0,
+ defaultOperator: 'equal',
+ transform: (optionValue) => parseInt(optionValue, 10),
+ transformReverse: (transformedOptionValue) => `${transformedOptionValue}`,
+ validateTransformed: (transformedOptionValue) => Number.isFinite(transformedOptionValue),
+ operators: {
+ equal: {
+ name: '=',
+ test: ({depth}, optionValue) => (depth === optionValue)
+ },
+ notEqual: {
+ name: '\u2260',
+ test: ({depth}, optionValue) => (depth !== optionValue)
+ },
+ lessThan: {
+ name: '<',
+ test: ({depth}, optionValue) => (depth < optionValue)
+ },
+ greaterThan: {
+ name: '>',
+ test: ({depth}, optionValue) => (depth > optionValue)
+ },
+ lessThanOrEqual: {
+ name: '\u2264',
+ test: ({depth}, optionValue) => (depth <= optionValue)
+ },
+ greaterThanOrEqual: {
+ name: '\u2265',
+ test: ({depth}, optionValue) => (depth >= optionValue)
+ }
}
- }
- },
- url: {
- name: 'URL',
- description: 'Use profile depending on the URL of the current website.',
- defaultOperator: 'matchDomain',
- operators: {
- matchDomain: {
- name: 'Matches Domain',
- placeholder: 'Comma separated list of domains',
- defaultValue: 'example.com',
- transformCache: {},
- transform: (optionValue) => optionValue.split(/[,;\s]+/).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0),
- transformReverse: (transformedOptionValue) => transformedOptionValue.join(', '),
- validateTransformed: (transformedOptionValue) => (transformedOptionValue.length > 0),
- test: ({url}, transformedOptionValue) => _profileConditionTestDomainList(url, transformedOptionValue)
- },
- matchRegExp: {
- name: 'Matches RegExp',
- placeholder: 'Regular expression',
- defaultValue: 'example\\.com',
- transformCache: {},
- transform: (optionValue) => new RegExp(optionValue, 'i'),
- transformReverse: (transformedOptionValue) => transformedOptionValue.source,
- test: ({url}, transformedOptionValue) => (transformedOptionValue !== null && transformedOptionValue.test(url))
+ },
+ url: {
+ name: 'URL',
+ description: 'Use profile depending on the URL of the current website.',
+ defaultOperator: 'matchDomain',
+ operators: {
+ matchDomain: {
+ name: 'Matches Domain',
+ placeholder: 'Comma separated list of domains',
+ defaultValue: 'example.com',
+ transformCache: {},
+ transform: (optionValue) => optionValue.split(/[,;\s]+/).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0),
+ transformReverse: (transformedOptionValue) => transformedOptionValue.join(', '),
+ validateTransformed: (transformedOptionValue) => (transformedOptionValue.length > 0),
+ test: ({url}, transformedOptionValue) => _profileConditionTestDomainList(url, transformedOptionValue)
+ },
+ matchRegExp: {
+ name: 'Matches RegExp',
+ placeholder: 'Regular expression',
+ defaultValue: 'example\\.com',
+ transformCache: {},
+ transform: (optionValue) => new RegExp(optionValue, 'i'),
+ transformReverse: (transformedOptionValue) => transformedOptionValue.source,
+ test: ({url}, transformedOptionValue) => (transformedOptionValue !== null && transformedOptionValue.test(url))
+ }
}
- }
- },
- modifierKeys: {
- name: 'Modifier Keys',
- description: 'Use profile depending on the active modifier keys.',
- values: _profileModifierKeys,
- defaultOperator: 'are',
- operators: {
- are: {
- name: 'are',
- placeholder: 'Press one or more modifier keys here',
- defaultValue: [],
- type: 'keyMulti',
- transformInput: (optionValue) => optionValue
- .split(' + ')
- .filter((v) => v.length > 0)
- .map((v) => _profileModifierNameToValue.get(v)),
- transformReverse: (transformedOptionValue) => transformedOptionValue
- .map((v) => _profileModifierValueToName.get(v))
- .join(' + '),
- test: ({modifierKeys}, optionValue) => areSetsEqual(new Set(modifierKeys), new Set(optionValue))
- },
- areNot: {
- name: 'are not',
- placeholder: 'Press one or more modifier keys here',
- defaultValue: [],
- type: 'keyMulti',
- transformInput: (optionValue) => optionValue
- .split(' + ')
- .filter((v) => v.length > 0)
- .map((v) => _profileModifierNameToValue.get(v)),
- transformReverse: (transformedOptionValue) => transformedOptionValue
- .map((v) => _profileModifierValueToName.get(v))
- .join(' + '),
- test: ({modifierKeys}, optionValue) => !areSetsEqual(new Set(modifierKeys), new Set(optionValue))
- },
- include: {
- name: 'include',
- type: 'select',
- defaultValue: 'alt',
- test: ({modifierKeys}, optionValue) => modifierKeys.includes(optionValue)
- },
- notInclude: {
- name: 'don\'t include',
- type: 'select',
- defaultValue: 'alt',
- test: ({modifierKeys}, optionValue) => !modifierKeys.includes(optionValue)
+ },
+ modifierKeys: {
+ name: 'Modifier Keys',
+ description: 'Use profile depending on the active modifier keys.',
+ values: modifierKeyValues,
+ defaultOperator: 'are',
+ operators: {
+ are: {
+ name: 'are',
+ placeholder: 'Press one or more modifier keys here',
+ defaultValue: [],
+ type: 'keyMulti',
+ keySeparator: modifierSeparator,
+ transformInput: (optionValue) => optionValue
+ .split(modifierSeparator)
+ .filter((v) => v.length > 0)
+ .map((v) => modifierNameToValue.get(v)),
+ transformReverse: (transformedOptionValue) => transformedOptionValue
+ .map((v) => modifierValueToName.get(v))
+ .join(modifierSeparator),
+ test: ({modifierKeys}, optionValue) => areSetsEqual(new Set(modifierKeys), new Set(optionValue))
+ },
+ areNot: {
+ name: 'are not',
+ placeholder: 'Press one or more modifier keys here',
+ defaultValue: [],
+ type: 'keyMulti',
+ keySeparator: modifierSeparator,
+ transformInput: (optionValue) => optionValue
+ .split(modifierSeparator)
+ .filter((v) => v.length > 0)
+ .map((v) => modifierNameToValue.get(v)),
+ transformReverse: (transformedOptionValue) => transformedOptionValue
+ .map((v) => modifierValueToName.get(v))
+ .join(modifierSeparator),
+ test: ({modifierKeys}, optionValue) => !areSetsEqual(new Set(modifierKeys), new Set(optionValue))
+ },
+ include: {
+ name: 'include',
+ type: 'select',
+ defaultValue: 'alt',
+ test: ({modifierKeys}, optionValue) => modifierKeys.includes(optionValue)
+ },
+ notInclude: {
+ name: 'don\'t include',
+ type: 'select',
+ defaultValue: 'alt',
+ test: ({modifierKeys}, optionValue) => !modifierKeys.includes(optionValue)
+ }
}
}
- }
-};
+ };
+})();
diff --git a/ext/bg/js/settings/conditions-ui.js b/ext/bg/js/settings/conditions-ui.js
index 0670de5a..031689a7 100644
--- a/ext/bg/js/settings/conditions-ui.js
+++ b/ext/bg/js/settings/conditions-ui.js
@@ -310,10 +310,14 @@ ConditionsUI.Condition = class Condition {
inputInner.prop('readonly', true);
let values = [];
+ let keySeparator = ' + ';
for (const object of objects) {
if (hasOwn(object, 'values')) {
values = object.values;
}
+ if (hasOwn(object, 'keySeparator')) {
+ keySeparator = object.keySeparator;
+ }
}
const pressedKeyIndices = new Set();
@@ -347,7 +351,7 @@ ConditionsUI.Condition = class Condition {
}
}
- const inputValue = [...pressedKeyIndices].map((i) => values[i].name).join(' + ');
+ const inputValue = [...pressedKeyIndices].map((i) => values[i].name).join(keySeparator);
inputInner.val(inputValue);
inputInner.change();
};
diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js
index cf75d629..61395b1c 100644
--- a/ext/bg/js/settings/main.js
+++ b/ext/bg/js/settings/main.js
@@ -22,6 +22,7 @@
* ankiTemplatesInitialize
* ankiTemplatesUpdateValue
* apiForwardLogsToBackend
+ * apiGetEnvironmentInfo
* apiOptionsSave
* appearanceInitialize
* audioSettingsInitialize
@@ -285,6 +286,23 @@ function showExtensionInformation() {
node.textContent = `${manifest.name} v${manifest.version}`;
}
+async function settingsPopulateModifierKeys() {
+ const scanModifierKeySelect = document.querySelector('#scan-modifier-key');
+ scanModifierKeySelect.textContent = '';
+
+ const environment = await apiGetEnvironmentInfo();
+ const modifierKeys = [
+ {value: 'none', name: 'None'},
+ ...environment.modifiers.keys
+ ];
+ for (const {value, name} of modifierKeys) {
+ const option = document.createElement('option');
+ option.value = value;
+ option.textContent = name;
+ scanModifierKeySelect.appendChild(option);
+ }
+}
+
async function onReady() {
apiForwardLogsToBackend();
@@ -292,6 +310,7 @@ async function onReady() {
showExtensionInformation();
+ await settingsPopulateModifierKeys();
formSetupEventListeners();
appearanceInitialize();
await audioSettingsInitialize();
diff --git a/ext/bg/js/settings/profiles.js b/ext/bg/js/settings/profiles.js
index 3f4b1da7..bdf5a13d 100644
--- a/ext/bg/js/settings/profiles.js
+++ b/ext/bg/js/settings/profiles.js
@@ -23,6 +23,7 @@
* getOptionsFullMutable
* getOptionsMutable
* profileConditionsDescriptor
+ * profileConditionsDescriptorPromise
* settingsSaveOptions
* utilBackgroundIsolate
*/
@@ -98,6 +99,7 @@ async function profileFormWrite(optionsFull) {
profileConditionsContainer.cleanup();
}
+ await profileConditionsDescriptorPromise;
profileConditionsContainer = new ConditionsUI.Container(
profileConditionsDescriptor,
'popupLevel',
@@ -128,7 +130,7 @@ function profileOptionsPopulateSelect(select, profiles, currentValue, ignoreIndi
}
async function profileOptionsUpdateTarget(optionsFull) {
- profileFormWrite(optionsFull);
+ await profileFormWrite(optionsFull);
const optionsContext = getOptionsContext();
const options = await getOptionsMutable(optionsContext);