diff options
Diffstat (limited to 'ext/js')
| -rw-r--r-- | ext/js/comm/anki.js | 11 | ||||
| -rw-r--r-- | ext/js/data/anki-note-builder.js | 30 | ||||
| -rw-r--r-- | ext/js/data/anki-util.js | 87 | ||||
| -rw-r--r-- | ext/js/data/permissions-util.js | 21 | ||||
| -rw-r--r-- | ext/js/display/display.js | 11 | ||||
| -rw-r--r-- | ext/js/pages/settings/anki-controller.js | 9 | ||||
| -rw-r--r-- | ext/js/pages/settings/anki-templates-controller.js | 2 | 
7 files changed, 112 insertions, 59 deletions
| diff --git a/ext/js/comm/anki.js b/ext/js/comm/anki.js index 251e0e0c..da234eff 100644 --- a/ext/js/comm/anki.js +++ b/ext/js/comm/anki.js @@ -15,6 +15,10 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ +/* global + * AnkiUtil + */ +  class AnkiConnect {      constructor() {          this._enabled = false; @@ -113,7 +117,7 @@ class AnkiConnect {                      query = `"deck:${this._escapeQuery(note.deckName)}" `;                      break;                  case 'deck-root': -                    query = `"deck:${this._escapeQuery(this.getRootDeckName(note.deckName))}" `; +                    query = `"deck:${this._escapeQuery(AnkiUtil.getRootDeckName(note.deckName))}" `;                      break;              }              query += this._fieldsToQuery(note.fields); @@ -138,11 +142,6 @@ class AnkiConnect {          return await this.findCards(`nid:${noteId}`);      } -    getRootDeckName(deckName) { -        const index = deckName.indexOf('::'); -        return index >= 0 ? deckName.substring(0, index) : deckName; -    } -      // Private      async _checkVersion() { diff --git a/ext/js/data/anki-note-builder.js b/ext/js/data/anki-note-builder.js index e1399f66..f12846b1 100644 --- a/ext/js/data/anki-note-builder.js +++ b/ext/js/data/anki-note-builder.js @@ -16,13 +16,14 @@   */  /* global + * AnkiUtil   * TemplateRendererProxy   */  class AnkiNoteBuilder { -    constructor(enabled) { -        this._markerPattern = /\{([\w-]+)\}/g; -        this._templateRenderer = enabled ? new TemplateRendererProxy() : null; +    constructor() { +        this._markerPattern = AnkiUtil.cloneFieldMarkerPattern(true); +        this._templateRenderer = new TemplateRendererProxy();      }      async createNote({ @@ -46,7 +47,7 @@ class AnkiNoteBuilder {          let duplicateScopeCheckChildren = false;          if (duplicateScope === 'deck-root') {              duplicateScope = 'deck'; -            duplicateScopeDeckName = this.getRootDeckName(deckName); +            duplicateScopeDeckName = AnkiUtil.getRootDeckName(deckName);              duplicateScopeCheckChildren = true;          } @@ -89,27 +90,6 @@ class AnkiNoteBuilder {          };      } -    containsMarker(fields, marker) { -        marker = `{${marker}}`; -        for (const [, fieldValue] of fields) { -            if (fieldValue.includes(marker)) { -                return true; -            } -        } -        return false; -    } - -    containsAnyMarker(field) { -        const result = this._markerPattern.test(field); -        this._markerPattern.lastIndex = 0; -        return result; -    } - -    getRootDeckName(deckName) { -        const index = deckName.indexOf('::'); -        return index >= 0 ? deckName.substring(0, index) : deckName; -    } -      // Private      async _formatField(field, data, templates, errors=null) { diff --git a/ext/js/data/anki-util.js b/ext/js/data/anki-util.js new file mode 100644 index 00000000..fc081ddc --- /dev/null +++ b/ext/js/data/anki-util.js @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021  Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +/** + * This class has some general utility functions for working with Anki data. + */ +class AnkiUtil { +    /** +     * Gets the root deck name of a full deck name. If the deck is a root deck, +     * the same name is returned. Nested decks are separated using '::'. +     * @param deckName A string of the deck name. +     * @returns A string corresponding to the name of the root deck. +     */ +    static getRootDeckName(deckName) { +        const index = deckName.indexOf('::'); +        return index >= 0 ? deckName.substring(0, index) : deckName; +    } + +    /** +     * Checks whether or not any marker is contained in a string. +     * @param string A string to check. +     * @return `true` if the text contains an Anki field marker, `false` otherwise. +     */ +    static stringContainsAnyFieldMarker(string) { +        const result = this._markerPattern.test(string); +        this._markerPattern.lastIndex = 0; +        return result; +    } + +    /** +     * Gets a list of all markers that are contained in a string. +     * @param string A string to check. +     * @return An array of marker strings. +     */ +    static getFieldMarkers(string) { +        const pattern = this._markerPattern; +        const markers = []; +        while (true) { +            const match = pattern.exec(string); +            if (match === null) { break; } +            markers.push(match[1]); +        } +        return markers; +    } + +    /** +     * Checks whether an object of key-value pairs has a value which contains a specific marker. +     * @param fieldsObject An object with key-value pairs, where the value corresponds to the field value. +     * @param marker The marker string to check for, excluding brackets. +     * @returns `true` if any of the fields contains the marker, `false` otherwise. +     */ +    static fieldsObjectContainsMarker(fieldsObject, marker) { +        marker = `{${marker}}`; +        for (const [, fieldValue] of fieldsObject) { +            if (fieldValue.includes(marker)) { +                return true; +            } +        } +        return false; +    } + +    /** +     * Returns a regular expression which can be used to find markers in a string. +     * @param global Whether or not the regular expression should have the global flag. +     * @returns A new `RegExp` instance. +     */ +    static cloneFieldMarkerPattern(global) { +        return new RegExp(this._markerPattern.source, global ? 'g' : ''); +    } +} + +// eslint-disable-next-line no-underscore-dangle +AnkiUtil._markerPattern = /\{([\w-]+)\}/g; diff --git a/ext/js/data/permissions-util.js b/ext/js/data/permissions-util.js index bd3a18ce..3a5b5de5 100644 --- a/ext/js/data/permissions-util.js +++ b/ext/js/data/permissions-util.js @@ -15,13 +15,16 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ +/* global + * AnkiUtil + */ +  class PermissionsUtil {      constructor() {          this._ankiFieldMarkersRequiringClipboardPermission = new Set([              'clipboard-image',              'clipboard-text'          ]); -        this._ankiMarkerPattern = /\{([\w-]+)\}/g;      }      hasPermissions(permissions) { @@ -69,7 +72,7 @@ class PermissionsUtil {      }      getRequiredPermissionsForAnkiFieldValue(fieldValue) { -        const markers = this._getAnkiFieldMarkers(fieldValue); +        const markers = AnkiUtil.getFieldMarkers(fieldValue);          const markerPermissions = this._ankiFieldMarkersRequiringClipboardPermission;          for (const marker of markers) {              if (markerPermissions.has(marker)) { @@ -99,7 +102,7 @@ class PermissionsUtil {              ];              for (const fields of fieldsList) {                  for (const fieldValue of Object.values(fields)) { -                    const markers = this._getAnkiFieldMarkers(fieldValue); +                    const markers = AnkiUtil.getFieldMarkers(fieldValue);                      for (const marker of markers) {                          if (fieldMarkersRequiringClipboardPermission.has(marker)) {                              return false; @@ -111,16 +114,4 @@ class PermissionsUtil {          return true;      } - -    // Private - -    _getAnkiFieldMarkers(fieldValue) { -        const pattern = this._ankiMarkerPattern; -        const markers = []; -        let match; -        while ((match = pattern.exec(fieldValue)) !== null) { -            markers.push(match[1]); -        } -        return markers; -    }  } diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 517b391d..0e029748 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -17,6 +17,7 @@  /* global   * AnkiNoteBuilder + * AnkiUtil   * DisplayAudio   * DisplayGenerator   * DisplayHistory @@ -85,7 +86,7 @@ class Display extends EventDispatcher {          });          this._ankiFieldTemplates = null;          this._ankiFieldTemplatesDefault = null; -        this._ankiNoteBuilder = new AnkiNoteBuilder(true); +        this._ankiNoteBuilder = new AnkiNoteBuilder();          this._updateAdderButtonsPromise = Promise.resolve();          this._contentScrollElement = document.querySelector('#content-scroll');          this._contentScrollBodyElement = document.querySelector('#content-body'); @@ -1493,7 +1494,7 @@ class Display extends EventDispatcher {          const definitionDetails = this._getDefinitionDetailsForNote(definition);          let audioDetails = null; -        if (definitionDetails.type !== 'kanji' && this._ankiNoteBuilder.containsMarker(fields, 'audio')) { +        if (definitionDetails.type !== 'kanji' && AnkiUtil.fieldsObjectContainsMarker(fields, 'audio')) {              const primaryCardAudio = this._displayAudio.getPrimaryCardAudio(definitionDetails.expression, definitionDetails.reading);              let preferredAudioIndex = null;              let sources2 = sources; @@ -1504,11 +1505,11 @@ class Display extends EventDispatcher {              audioDetails = {sources: sources2, preferredAudioIndex, customSourceUrl, customSourceType};          } -        const screenshotDetails = (this._ankiNoteBuilder.containsMarker(fields, 'screenshot') ? {tabId: this._contentOriginTabId, frameId: this._contentOriginFrameId, format, quality} : null); +        const screenshotDetails = (AnkiUtil.fieldsObjectContainsMarker(fields, 'screenshot') ? {tabId: this._contentOriginTabId, frameId: this._contentOriginFrameId, format, quality} : null);          const clipboardDetails = { -            image: this._ankiNoteBuilder.containsMarker(fields, 'clipboard-image'), -            text: this._ankiNoteBuilder.containsMarker(fields, 'clipboard-text') +            image: AnkiUtil.fieldsObjectContainsMarker(fields, 'clipboard-image'), +            text: AnkiUtil.fieldsObjectContainsMarker(fields, 'clipboard-text')          };          return await yomichan.api.injectAnkiNoteMedia( diff --git a/ext/js/pages/settings/anki-controller.js b/ext/js/pages/settings/anki-controller.js index 26cab68f..509e263c 100644 --- a/ext/js/pages/settings/anki-controller.js +++ b/ext/js/pages/settings/anki-controller.js @@ -17,7 +17,7 @@  /* global   * AnkiConnect - * AnkiNoteBuilder + * AnkiUtil   * ObjectPropertyAccessor   * SelectorObserver   */ @@ -26,7 +26,6 @@ class AnkiController {      constructor(settingsController) {          this._settingsController = settingsController;          this._ankiConnect = new AnkiConnect(); -        this._ankiNoteBuilder = new AnkiNoteBuilder(false);          this._selectorObserver = new SelectorObserver({              selector: '.anki-card',              ignoreSelector: null, @@ -156,10 +155,6 @@ class AnkiController {          return this._settingsController.permissionsUtil.getRequiredPermissionsForAnkiFieldValue(fieldValue);      } -    containsAnyMarker(field) { -        return this._ankiNoteBuilder.containsAnyMarker(field); -    } -      // Private      async _onOptionsChanged({options: {anki}}) { @@ -439,7 +434,7 @@ class AnkiCardController {      _validateField(node, index) {          let valid = (node.dataset.hasPermissions !== 'false'); -        if (valid && index === 0 && !this._ankiController.containsAnyMarker(node.value)) { +        if (valid && index === 0 && !AnkiUtil.stringContainsAnyFieldMarker(node.value)) {              valid = false;          }          node.dataset.invalid = `${!valid}`; diff --git a/ext/js/pages/settings/anki-templates-controller.js b/ext/js/pages/settings/anki-templates-controller.js index 8e3a1a70..710946be 100644 --- a/ext/js/pages/settings/anki-templates-controller.js +++ b/ext/js/pages/settings/anki-templates-controller.js @@ -32,7 +32,7 @@ class AnkiTemplatesController {          this._renderFieldInput = null;          this._renderResult = null;          this._fieldTemplateResetModal = null; -        this._ankiNoteBuilder = new AnkiNoteBuilder(true); +        this._ankiNoteBuilder = new AnkiNoteBuilder();      }      async prepare() { |