aboutsummaryrefslogtreecommitdiff
path: root/ext/js
diff options
context:
space:
mode:
authorStefanVukovic99 <stefanvukovic44@gmail.com>2024-05-05 02:30:09 +0200
committerGitHub <noreply@github.com>2024-05-05 00:30:09 +0000
commit2d191bfdbd955a363e7afdc79c7a2b4b11a2e9b7 (patch)
treeeb3706b9ed98ba5347612088821ae046a0719fc8 /ext/js
parentc3c5d58688a411c6ed450b89494c59037197df55 (diff)
add single dictionary handlebars (#814)24.5.5.0
* add single dictionary handlebars * fix dicts with kanji in title * sort * rename to single-glossary-XYZ * add brief and no dict variants * add docs, only terms no kanji * allow testing single dict handlebars * remove empty comment
Diffstat (limited to 'ext/js')
-rw-r--r--ext/js/data/anki-template-util.js51
-rw-r--r--ext/js/data/anki-util.js4
-rw-r--r--ext/js/data/options-util.js12
-rw-r--r--ext/js/display/display-anki.js11
-rw-r--r--ext/js/pages/settings/anki-controller.js28
-rw-r--r--ext/js/pages/settings/anki-templates-controller.js15
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;
+ }
}