diff options
Diffstat (limited to 'ext/bg')
| -rw-r--r-- | ext/bg/background.html | 1 | ||||
| -rw-r--r-- | ext/bg/js/backend.js | 40 | ||||
| -rw-r--r-- | ext/bg/js/profile-conditions.js | 256 | ||||
| -rw-r--r-- | ext/bg/js/settings/conditions-ui.js | 6 | ||||
| -rw-r--r-- | ext/bg/js/settings/main.js | 19 | ||||
| -rw-r--r-- | ext/bg/js/settings/profiles.js | 4 | ||||
| -rw-r--r-- | ext/bg/settings.html | 9 | 
7 files changed, 172 insertions, 163 deletions
| diff --git a/ext/bg/background.html b/ext/bg/background.html index 7cb76ec3..ca35a3c6 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -21,6 +21,7 @@          <script src="/mixed/js/core.js"></script>          <script src="/mixed/js/dom.js"></script> +        <script src="/mixed/js/environment.js"></script>          <script src="/mixed/js/japanese.js"></script>          <script src="/bg/js/anki.js"></script> 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); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index a0981687..3ce91f12 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -412,13 +412,7 @@                  <div class="form-group">                      <label for="scan-modifier-key">Scan modifier key</label> -                    <select class="form-control" id="scan-modifier-key"> -                        <option value="none">None</option> -                        <option value="alt">Alt</option> -                        <option value="ctrl">Ctrl</option> -                        <option value="shift">Shift</option> -                        <option data-hide-for-browser="firefox firefox-mobile" value="meta">Meta</option> -                    </select> +                    <select class="form-control" id="scan-modifier-key"></select>                  </div>              </div> @@ -1131,6 +1125,7 @@          <script src="/mixed/js/core.js"></script>          <script src="/mixed/js/dom.js"></script> +        <script src="/mixed/js/environment.js"></script>          <script src="/mixed/js/api.js"></script>          <script src="/mixed/js/japanese.js"></script> |