diff options
Diffstat (limited to 'ext/js')
| -rw-r--r-- | ext/js/data/anki-template-util.js | 51 | ||||
| -rw-r--r-- | ext/js/data/anki-util.js | 4 | ||||
| -rw-r--r-- | ext/js/data/options-util.js | 12 | ||||
| -rw-r--r-- | ext/js/display/display-anki.js | 11 | ||||
| -rw-r--r-- | ext/js/pages/settings/anki-controller.js | 28 | ||||
| -rw-r--r-- | ext/js/pages/settings/anki-templates-controller.js | 15 | 
6 files changed, 107 insertions, 14 deletions
| diff --git a/ext/js/data/anki-template-util.js b/ext/js/data/anki-template-util.js index 380d87f9..b9c5ba84 100644 --- a/ext/js/data/anki-template-util.js +++ b/ext/js/data/anki-template-util.js @@ -93,3 +93,54 @@ export function getStandardFieldMarkers(type) {              throw new Error(`Unsupported type: ${type}`);      }  } + +/** + * @param {import('settings').ProfileOptions} options + * @returns {string} + */ +export function getDynamicTemplates(options) { +    let dynamicTemplates = '\n'; +    for (const dictionary of options.dictionaries) { +        if (!dictionary.enabled) { continue; } +        dynamicTemplates += ` +{{#*inline "single-glossary-${getKebabCase(dictionary.name)}"}} +    {{~> glossary selectedDictionary='${dictionary.name}'}} +{{/inline}} + +{{#*inline "single-glossary-${getKebabCase(dictionary.name)}-no-dictionary"}} +    {{~> glossary selectedDictionary='${dictionary.name}' noDictionaryTag=true}} +{{/inline}} + +{{#*inline "single-glossary-${getKebabCase(dictionary.name)}-brief"}} +    {{~> glossary selectedDictionary='${dictionary.name}' brief=true}} +{{/inline}} +`; +    } +    return dynamicTemplates; +} + +/** + * @param {import('settings').DictionariesOptions} dictionaries + * @returns {string[]} The list of field markers. + */ +export function getDynamicFieldMarkers(dictionaries) { +    const markers = []; +    for (const dictionary of dictionaries) { +        if (!dictionary.enabled) { continue; } +        markers.push(`single-glossary-${getKebabCase(dictionary.name)}`); +    } +    return markers; +} + +/** + * @param {string} str + * @returns {string} + */ +function getKebabCase(str) { +    return str +        .replace(/[\s_\u3000]/g, '-') +        .replace(/[^\p{L}\p{N}-]/gu, '') +        .replace(/--+/g, '-') +        .replace(/^-|-$/g, '') +        .toLowerCase(); +} diff --git a/ext/js/data/anki-util.js b/ext/js/data/anki-util.js index a063980f..c076c482 100644 --- a/ext/js/data/anki-util.js +++ b/ext/js/data/anki-util.js @@ -19,7 +19,7 @@  import {isObjectNotArray} from '../core/object-utilities.js';  /** @type {RegExp} @readonly */ -const markerPattern = /\{([\w-]+)\}/g; +const markerPattern = /\{([\p{Letter}\p{Number}_-]+)\}/gu;  /**   * Gets the root deck name of a full deck name. If the deck is a root deck, @@ -65,7 +65,7 @@ export function getFieldMarkers(string) {   * @returns {RegExp} A new `RegExp` instance.   */  export function cloneFieldMarkerPattern(global) { -    return new RegExp(markerPattern.source, global ? 'g' : ''); +    return new RegExp(markerPattern.source, global ? 'gu' : 'u');  }  /** diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index d6c667aa..b6fb6686 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -539,7 +539,8 @@ export class OptionsUtil {              this._updateVersion30,              this._updateVersion31,              this._updateVersion32, -            this._updateVersion33 +            this._updateVersion33, +            this._updateVersion34          ];          /* eslint-enable @typescript-eslint/unbound-method */          if (typeof targetVersion === 'number' && targetVersion < result.length) { @@ -1268,6 +1269,15 @@ export class OptionsUtil {      }      /** +     *  - Added dynamic handlebars for single dictionaries. +     *  @type {import('options-util').UpdateFunction} +     */ +    async _updateVersion34(options) { +        await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v34.handlebars'); +    } + + +    /**       * @param {string} url       * @returns {Promise<chrome.tabs.Tab>}       */ diff --git a/ext/js/display/display-anki.js b/ext/js/display/display-anki.js index 23f7157f..6a6ec215 100644 --- a/ext/js/display/display-anki.js +++ b/ext/js/display/display-anki.js @@ -21,6 +21,7 @@ import {log} from '../core/log.js';  import {toError} from '../core/to-error.js';  import {deferPromise} from '../core/utilities.js';  import {AnkiNoteBuilder} from '../data/anki-note-builder.js'; +import {getDynamicTemplates} from '../data/anki-template-util.js';  import {invalidNoteId, isNoteDataValid} from '../data/anki-util.js';  import {PopupMenu} from '../dom/popup-menu.js';  import {querySelectorNotNull} from '../dom/query-selector.js'; @@ -659,6 +660,16 @@ export class DisplayAnki {       * @returns {Promise<string>}       */      async _getAnkiFieldTemplates(options) { +        const staticTemplates = await this._getStaticAnkiFieldTemplates(options); +        const dynamicTemplates = getDynamicTemplates(options); +        return staticTemplates + dynamicTemplates; +    } + +    /** +     * @param {import('settings').ProfileOptions} options +     * @returns {Promise<string>} +     */ +    async _getStaticAnkiFieldTemplates(options) {          let templates = options.anki.fieldTemplates;          if (typeof templates === 'string') { return templates; } diff --git a/ext/js/pages/settings/anki-controller.js b/ext/js/pages/settings/anki-controller.js index d166d3f0..9640ed62 100644 --- a/ext/js/pages/settings/anki-controller.js +++ b/ext/js/pages/settings/anki-controller.js @@ -21,7 +21,7 @@ import {EventListenerCollection} from '../../core/event-listener-collection.js';  import {ExtensionError} from '../../core/extension-error.js';  import {log} from '../../core/log.js';  import {toError} from '../../core/to-error.js'; -import {getStandardFieldMarkers} from '../../data/anki-template-util.js'; +import {getDynamicFieldMarkers, getStandardFieldMarkers} from '../../data/anki-template-util.js';  import {stringContainsAnyFieldMarker} from '../../data/anki-util.js';  import {getRequiredPermissionsForAnkiFieldValue, hasPermissions, setPermissionsGranted} from '../../data/permissions-util.js';  import {querySelectorNotNull} from '../../dom/query-selector.js'; @@ -85,7 +85,6 @@ export class AnkiController {          /** @type {HTMLElement} */          const ankiErrorLog = querySelectorNotNull(document, '#anki-error-log'); -        this._setupFieldMenus();          this._ankiErrorMessageDetailsToggle.addEventListener('click', this._onAnkiErrorMessageDetailsToggleClick.bind(this), false);          if (this._ankiEnableCheckbox !== null) { @@ -110,14 +109,14 @@ export class AnkiController {          ankiApiKeyInput.addEventListener('focus', this._onApiKeyInputFocus.bind(this));          ankiApiKeyInput.addEventListener('blur', this._onApiKeyInputBlur.bind(this)); +        await this._updateOptions(); +        this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); +          const onAnkiSettingChanged = () => { void this._updateOptions(); };          const nodes = [ankiApiKeyInput, ...document.querySelectorAll('[data-setting="anki.enable"]')];          for (const node of nodes) {              node.addEventListener('settingChanged', onAnkiSettingChanged);          } - -        await this._updateOptions(); -        this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this));      }      /** @@ -161,7 +160,7 @@ export class AnkiController {      /**       * @param {import('settings-controller').EventArgument<'optionsChanged'>} details       */ -    _onOptionsChanged({options: {anki}}) { +    _onOptionsChanged({options: {anki, dictionaries}}) {          /** @type {?string} */          let apiKey = anki.apiKey;          if (apiKey === '') { apiKey = null; } @@ -171,6 +170,8 @@ export class AnkiController {          this._selectorObserver.disconnect();          this._selectorObserver.observe(document.documentElement, true); + +        this._setupFieldMenus(dictionaries);      }      /** */ @@ -279,8 +280,10 @@ export class AnkiController {          return cardController.isStale();      } -    /** */ -    _setupFieldMenus() { +    /** +     * @param {import('settings').DictionariesOptions} dictionaries +     */ +    _setupFieldMenus(dictionaries) {          /** @type {[types: import('dictionary').DictionaryEntryType[], templateName: string][]} */          const fieldMenuTargets = [              [['term'], 'anki-card-terms-field-menu'], @@ -301,11 +304,18 @@ export class AnkiController {                  return;              } +            while (container.firstChild) { +                container.removeChild(container.firstChild); +            } +              let markers = [];              for (const type of types) {                  markers.push(...getStandardFieldMarkers(type));              } -            markers = [...new Set(markers)]; +            if (types.includes('term')) { +                markers.push(...getDynamicFieldMarkers(dictionaries)); +            } +            markers = [...new Set(markers.sort())];              const fragment = document.createDocumentFragment();              for (const marker of markers) { diff --git a/ext/js/pages/settings/anki-templates-controller.js b/ext/js/pages/settings/anki-templates-controller.js index a0a6bd6c..dd96f69c 100644 --- a/ext/js/pages/settings/anki-templates-controller.js +++ b/ext/js/pages/settings/anki-templates-controller.js @@ -19,6 +19,7 @@  import {ExtensionError} from '../../core/extension-error.js';  import {toError} from '../../core/to-error.js';  import {AnkiNoteBuilder} from '../../data/anki-note-builder.js'; +import {getDynamicTemplates} from '../../data/anki-template-util.js';  import {querySelectorNotNull} from '../../dom/query-selector.js';  import {TemplateRendererProxy} from '../../templates/template-renderer-proxy.js'; @@ -244,8 +245,7 @@ export class AnkiTemplatesController {                      query: sentenceText,                      fullQuery: sentenceText                  }; -                let template = options.anki.fieldTemplates; -                if (typeof template !== 'string') { template = this._defaultFieldTemplates; } +                const template = this._getAnkiTemplate(options);                  const {general: {resultOutputMode, glossaryLayoutMode, compactTags}} = options;                  const {note, errors} = await this._ankiNoteBuilder.createNote(/** @type {import('anki-note-builder').CreateNoteDetails} */ ({                      dictionaryEntry, @@ -293,4 +293,15 @@ export class AnkiTemplatesController {              /** @type {HTMLTextAreaElement} */ (this._fieldTemplatesTextarea).dataset.invalid = `${hasError}`;          }      } + +    /** +     * @param {import('settings').ProfileOptions} options +     * @returns {string} +     */ +    _getAnkiTemplate(options) { +        let staticTemplates = options.anki.fieldTemplates; +        if (typeof staticTemplates !== 'string') { staticTemplates = this._defaultFieldTemplates; } +        const dynamicTemplates = getDynamicTemplates(options); +        return staticTemplates + '\n' + dynamicTemplates; +    }  } |