aboutsummaryrefslogtreecommitdiff
path: root/ext/js/data
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/data')
-rw-r--r--ext/js/data/anki-note-builder.js6
-rw-r--r--ext/js/data/anki-util.js117
-rw-r--r--ext/js/data/permissions-util.js191
-rw-r--r--ext/js/data/sandbox/anki-note-data-creator.js1523
-rw-r--r--ext/js/data/sandbox/array-buffer-util.js93
-rw-r--r--ext/js/data/sandbox/string-util.js89
6 files changed, 992 insertions, 1027 deletions
diff --git a/ext/js/data/anki-note-builder.js b/ext/js/data/anki-note-builder.js
index 815e7f3f..5bb943c2 100644
--- a/ext/js/data/anki-note-builder.js
+++ b/ext/js/data/anki-note-builder.js
@@ -20,7 +20,7 @@ import {ExtensionError} from '../core/extension-error.js';
import {deferPromise} from '../core/utilities.js';
import {convertHiraganaToKatakana, convertKatakanaToHiragana} from '../language/japanese.js';
import {yomitan} from '../yomitan.js';
-import {AnkiUtil} from './anki-util.js';
+import {cloneFieldMarkerPattern, getRootDeckName} from './anki-util.js';
export class AnkiNoteBuilder {
/**
@@ -29,7 +29,7 @@ export class AnkiNoteBuilder {
*/
constructor(templateRenderer) {
/** @type {RegExp} */
- this._markerPattern = AnkiUtil.cloneFieldMarkerPattern(true);
+ this._markerPattern = cloneFieldMarkerPattern(true);
/** @type {import('../templates/template-renderer-proxy.js').TemplateRendererProxy|import('../templates/sandbox/template-renderer.js').TemplateRenderer} */
this._templateRenderer = templateRenderer;
/** @type {import('anki-note-builder').BatchedRequestGroup[]} */
@@ -64,7 +64,7 @@ export class AnkiNoteBuilder {
let duplicateScopeCheckChildren = false;
if (duplicateScope === 'deck-root') {
duplicateScope = 'deck';
- duplicateScopeDeckName = AnkiUtil.getRootDeckName(deckName);
+ duplicateScopeDeckName = getRootDeckName(deckName);
duplicateScopeCheckChildren = true;
}
diff --git a/ext/js/data/anki-util.js b/ext/js/data/anki-util.js
index 57684887..123e5d2f 100644
--- a/ext/js/data/anki-util.js
+++ b/ext/js/data/anki-util.js
@@ -18,72 +18,67 @@
import {isObject} from '../core/utilities.js';
+/** @type {RegExp} @readonly */
+const markerPattern = /\{([\w-]+)\}/g;
+
/**
- * This class has some general utility functions for working with Anki data.
+ * 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 {string} deckName A string of the deck name.
+ * @returns {string} A string corresponding to the name of the root deck.
*/
-export class AnkiUtil {
- /** @type {RegExp} @readonly */
- static _markerPattern = /\{([\w-]+)\}/g;
-
- /**
- * 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 {string} deckName A string of the deck name.
- * @returns {string} 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;
- }
+export function 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} string A string to check.
- * @returns {boolean} `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;
- }
+/**
+ * Checks whether or not any marker is contained in a string.
+ * @param {string} string A string to check.
+ * @returns {boolean} `true` if the text contains an Anki field marker, `false` otherwise.
+ */
+export function stringContainsAnyFieldMarker(string) {
+ const result = markerPattern.test(string);
+ markerPattern.lastIndex = 0;
+ return result;
+}
- /**
- * Gets a list of all markers that are contained in a string.
- * @param {string} string A string to check.
- * @returns {string[]} 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;
+/**
+ * Gets a list of all markers that are contained in a string.
+ * @param {string} string A string to check.
+ * @returns {string[]} An array of marker strings.
+ */
+export function getFieldMarkers(string) {
+ const pattern = markerPattern;
+ const markers = [];
+ while (true) {
+ const match = pattern.exec(string);
+ if (match === null) { break; }
+ markers.push(match[1]);
}
+ return markers;
+}
- /**
- * Returns a regular expression which can be used to find markers in a string.
- * @param {boolean} global Whether or not the regular expression should have the global flag.
- * @returns {RegExp} A new `RegExp` instance.
- */
- static cloneFieldMarkerPattern(global) {
- return new RegExp(this._markerPattern.source, global ? 'g' : '');
- }
+/**
+ * Returns a regular expression which can be used to find markers in a string.
+ * @param {boolean} global Whether or not the regular expression should have the global flag.
+ * @returns {RegExp} A new `RegExp` instance.
+ */
+export function cloneFieldMarkerPattern(global) {
+ return new RegExp(markerPattern.source, global ? 'g' : '');
+}
- /**
- * Checks whether or not a note object is valid.
- * @param {import('anki').Note} note A note object to check.
- * @returns {boolean} `true` if the note is valid, `false` otherwise.
- */
- static isNoteDataValid(note) {
- if (!isObject(note)) { return false; }
- const {fields, deckName, modelName} = note;
- return (
- typeof deckName === 'string' &&
- typeof modelName === 'string' &&
- Object.entries(fields).length > 0
- );
- }
+/**
+ * Checks whether or not a note object is valid.
+ * @param {import('anki').Note} note A note object to check.
+ * @returns {boolean} `true` if the note is valid, `false` otherwise.
+ */
+export function isNoteDataValid(note) {
+ if (!isObject(note)) { return false; }
+ const {fields, deckName, modelName} = note;
+ return (
+ typeof deckName === 'string' &&
+ typeof modelName === 'string' &&
+ Object.entries(fields).length > 0
+ );
}
diff --git a/ext/js/data/permissions-util.js b/ext/js/data/permissions-util.js
index 43aaa87c..837b6d5f 100644
--- a/ext/js/data/permissions-util.js
+++ b/ext/js/data/permissions-util.js
@@ -16,123 +16,128 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import {AnkiUtil} from './anki-util.js';
+import {getFieldMarkers} from './anki-util.js';
-export class PermissionsUtil {
- constructor() {
- /** @type {Set<string>} */
- this._ankiFieldMarkersRequiringClipboardPermission = new Set([
- 'clipboard-image',
- 'clipboard-text'
- ]);
+/**
+ * This function returns whether an Anki field marker might require clipboard permissions.
+ * This is speculative and may not guarantee that the field marker actually does require the permission,
+ * as the custom handlebars template is not deeply inspected.
+ * @param {string} marker
+ * @returns {boolean}
+ */
+function ankiFieldMarkerMayUseClipboard(marker) {
+ switch (marker) {
+ case 'clipboard-image':
+ case 'clipboard-text':
+ return true;
+ default:
+ return false;
}
+}
+
+/**
+ * @param {chrome.permissions.Permissions} permissions
+ * @returns {Promise<boolean>}
+ */
+export function hasPermissions(permissions) {
+ return new Promise((resolve, reject) => chrome.permissions.contains(permissions, (result) => {
+ const e = chrome.runtime.lastError;
+ if (e) {
+ reject(new Error(e.message));
+ } else {
+ resolve(result);
+ }
+ }));
+}
- /**
- * @param {chrome.permissions.Permissions} permissions
- * @returns {Promise<boolean>}
- */
- hasPermissions(permissions) {
- return new Promise((resolve, reject) => chrome.permissions.contains(permissions, (result) => {
+/**
+ * @param {chrome.permissions.Permissions} permissions
+ * @param {boolean} shouldHave
+ * @returns {Promise<boolean>}
+ */
+export function setPermissionsGranted(permissions, shouldHave) {
+ return (
+ shouldHave ?
+ new Promise((resolve, reject) => chrome.permissions.request(permissions, (result) => {
const e = chrome.runtime.lastError;
if (e) {
reject(new Error(e.message));
} else {
resolve(result);
}
- }));
- }
-
- /**
- * @param {chrome.permissions.Permissions} permissions
- * @param {boolean} shouldHave
- * @returns {Promise<boolean>}
- */
- setPermissionsGranted(permissions, shouldHave) {
- return (
- shouldHave ?
- new Promise((resolve, reject) => chrome.permissions.request(permissions, (result) => {
- const e = chrome.runtime.lastError;
- if (e) {
- reject(new Error(e.message));
- } else {
- resolve(result);
- }
- })) :
- new Promise((resolve, reject) => chrome.permissions.remove(permissions, (result) => {
- const e = chrome.runtime.lastError;
- if (e) {
- reject(new Error(e.message));
- } else {
- resolve(!result);
- }
- }))
- );
- }
-
- /**
- * @returns {Promise<chrome.permissions.Permissions>}
- */
- getAllPermissions() {
- return new Promise((resolve, reject) => chrome.permissions.getAll((result) => {
+ })) :
+ new Promise((resolve, reject) => chrome.permissions.remove(permissions, (result) => {
const e = chrome.runtime.lastError;
if (e) {
reject(new Error(e.message));
} else {
- resolve(result);
+ resolve(!result);
}
- }));
- }
+ }))
+ );
+}
- /**
- * @param {string} fieldValue
- * @returns {string[]}
- */
- getRequiredPermissionsForAnkiFieldValue(fieldValue) {
- const markers = AnkiUtil.getFieldMarkers(fieldValue);
- const markerPermissions = this._ankiFieldMarkersRequiringClipboardPermission;
- for (const marker of markers) {
- if (markerPermissions.has(marker)) {
- return ['clipboardRead'];
- }
+/**
+ * @returns {Promise<chrome.permissions.Permissions>}
+ */
+export function getAllPermissions() {
+ return new Promise((resolve, reject) => chrome.permissions.getAll((result) => {
+ const e = chrome.runtime.lastError;
+ if (e) {
+ reject(new Error(e.message));
+ } else {
+ resolve(result);
+ }
+ }));
+}
+
+/**
+ * @param {string} fieldValue
+ * @returns {string[]}
+ */
+export function getRequiredPermissionsForAnkiFieldValue(fieldValue) {
+ const markers = getFieldMarkers(fieldValue);
+ for (const marker of markers) {
+ if (ankiFieldMarkerMayUseClipboard(marker)) {
+ return ['clipboardRead'];
}
- return [];
}
+ return [];
+}
- /**
- * @param {chrome.permissions.Permissions} permissions
- * @param {import('settings').ProfileOptions} options
- * @returns {boolean}
- */
- hasRequiredPermissionsForOptions(permissions, options) {
- const permissionsSet = new Set(permissions.permissions);
+/**
+ * @param {chrome.permissions.Permissions} permissions
+ * @param {import('settings').ProfileOptions} options
+ * @returns {boolean}
+ */
+export function hasRequiredPermissionsForOptions(permissions, options) {
+ const permissionsSet = new Set(permissions.permissions);
- if (!permissionsSet.has('nativeMessaging')) {
- if (options.parsing.enableMecabParser) {
- return false;
- }
+ if (!permissionsSet.has('nativeMessaging')) {
+ if (options.parsing.enableMecabParser) {
+ return false;
}
+ }
- if (!permissionsSet.has('clipboardRead')) {
- if (options.clipboard.enableBackgroundMonitor || options.clipboard.enableSearchPageMonitor) {
- return false;
- }
- const fieldMarkersRequiringClipboardPermission = this._ankiFieldMarkersRequiringClipboardPermission;
- const fieldsList = [
- options.anki.terms.fields,
- options.anki.kanji.fields
- ];
- for (const fields of fieldsList) {
- for (const fieldValue of Object.values(fields)) {
- const markers = AnkiUtil.getFieldMarkers(fieldValue);
- for (const marker of markers) {
- if (fieldMarkersRequiringClipboardPermission.has(marker)) {
- return false;
- }
+ if (!permissionsSet.has('clipboardRead')) {
+ if (options.clipboard.enableBackgroundMonitor || options.clipboard.enableSearchPageMonitor) {
+ return false;
+ }
+ const fieldsList = [
+ options.anki.terms.fields,
+ options.anki.kanji.fields
+ ];
+ for (const fields of fieldsList) {
+ for (const fieldValue of Object.values(fields)) {
+ const markers = getFieldMarkers(fieldValue);
+ for (const marker of markers) {
+ if (ankiFieldMarkerMayUseClipboard(marker)) {
+ return false;
}
}
}
}
-
- return true;
}
+
+ return true;
}
diff --git a/ext/js/data/sandbox/anki-note-data-creator.js b/ext/js/data/sandbox/anki-note-data-creator.js
index fc787a66..51679662 100644
--- a/ext/js/data/sandbox/anki-note-data-creator.js
+++ b/ext/js/data/sandbox/anki-note-data-creator.js
@@ -16,867 +16,842 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import {DictionaryDataUtil} from '../../dictionary/dictionary-data-util.js';
+import {getDisambiguations, getGroupedPronunciations, getPronunciationsOfType, getTermFrequency, groupTermTags} from '../../dictionary/dictionary-data-util.js';
import {distributeFurigana} from '../../language/japanese.js';
/**
- * This class is used to convert the internal dictionary entry format to the
- * format used by Anki, for backwards compatibility.
- */
-export class AnkiNoteDataCreator {
- /**
- * Creates a compatibility representation of the specified data.
- * @param {string} marker The marker that is being used for template rendering.
- * @param {import('anki-templates-internal').CreateDetails} details Information which is used to generate the data.
- * @returns {import('anki-templates').NoteData} An object used for rendering Anki templates.
- */
- create(marker, {
- dictionaryEntry,
- resultOutputMode,
- mode,
- glossaryLayoutMode,
- compactTags,
- context,
- media
- }) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
- const definition = this.createCachedValue(this._getDefinition.bind(this, dictionaryEntry, context, resultOutputMode));
- const uniqueExpressions = this.createCachedValue(this._getUniqueExpressions.bind(this, dictionaryEntry));
- const uniqueReadings = this.createCachedValue(this._getUniqueReadings.bind(this, dictionaryEntry));
- const context2 = this.createCachedValue(this._getPublicContext.bind(this, context));
- const pitches = this.createCachedValue(this._getPitches.bind(this, dictionaryEntry));
- const pitchCount = this.createCachedValue(this._getPitchCount.bind(this, pitches));
- const phoneticTranscriptions = this.createCachedValue(this._getPhoneticTranscriptions.bind(this, dictionaryEntry));
-
- if (typeof media !== 'object' || media === null || Array.isArray(media)) {
- media = {
- audio: void 0,
- screenshot: void 0,
- clipboardImage: void 0,
- clipboardText: void 0,
- selectionText: void 0,
- textFurigana: [],
- dictionaryMedia: {}
- };
- }
- /** @type {import('anki-templates').NoteData} */
- const result = {
- marker,
- get definition() { return self.getCachedValue(definition); },
- glossaryLayoutMode,
- compactTags,
- group: (resultOutputMode === 'group'),
- merge: (resultOutputMode === 'merge'),
- modeTermKanji: (mode === 'term-kanji'),
- modeTermKana: (mode === 'term-kana'),
- modeKanji: (mode === 'kanji'),
- compactGlossaries: (glossaryLayoutMode === 'compact'),
- get uniqueExpressions() { return self.getCachedValue(uniqueExpressions); },
- get uniqueReadings() { return self.getCachedValue(uniqueReadings); },
- get pitches() { return self.getCachedValue(pitches); },
- get pitchCount() { return self.getCachedValue(pitchCount); },
- get phoneticTranscriptions() { return self.getCachedValue(phoneticTranscriptions); },
- get context() { return self.getCachedValue(context2); },
- media,
- dictionaryEntry
+ * Creates a compatibility representation of the specified data.
+ * @param {string} marker The marker that is being used for template rendering.
+ * @param {import('anki-templates-internal').CreateDetails} details Information which is used to generate the data.
+ * @returns {import('anki-templates').NoteData} An object used for rendering Anki templates.
+ */
+export function createAnkiNoteData(marker, {
+ dictionaryEntry,
+ resultOutputMode,
+ mode,
+ glossaryLayoutMode,
+ compactTags,
+ context,
+ media
+}) {
+ const definition = createCachedValue(getDefinition.bind(null, dictionaryEntry, context, resultOutputMode));
+ const uniqueExpressions = createCachedValue(getUniqueExpressions.bind(null, dictionaryEntry));
+ const uniqueReadings = createCachedValue(getUniqueReadings.bind(null, dictionaryEntry));
+ const context2 = createCachedValue(getPublicContext.bind(null, context));
+ const pitches = createCachedValue(getPitches.bind(null, dictionaryEntry));
+ const pitchCount = createCachedValue(getPitchCount.bind(null, pitches));
+ const phoneticTranscriptions = createCachedValue(getPhoneticTranscriptions.bind(null, dictionaryEntry));
+
+ if (typeof media !== 'object' || media === null || Array.isArray(media)) {
+ media = {
+ audio: void 0,
+ screenshot: void 0,
+ clipboardImage: void 0,
+ clipboardText: void 0,
+ selectionText: void 0,
+ textFurigana: [],
+ dictionaryMedia: {}
};
- Object.defineProperty(result, 'dictionaryEntry', {
- configurable: false,
- enumerable: false,
- writable: false,
- value: dictionaryEntry
- });
- return result;
}
+ /** @type {import('anki-templates').NoteData} */
+ const result = {
+ marker,
+ get definition() { return getCachedValue(definition); },
+ glossaryLayoutMode,
+ compactTags,
+ group: (resultOutputMode === 'group'),
+ merge: (resultOutputMode === 'merge'),
+ modeTermKanji: (mode === 'term-kanji'),
+ modeTermKana: (mode === 'term-kana'),
+ modeKanji: (mode === 'kanji'),
+ compactGlossaries: (glossaryLayoutMode === 'compact'),
+ get uniqueExpressions() { return getCachedValue(uniqueExpressions); },
+ get uniqueReadings() { return getCachedValue(uniqueReadings); },
+ get pitches() { return getCachedValue(pitches); },
+ get pitchCount() { return getCachedValue(pitchCount); },
+ get phoneticTranscriptions() { return getCachedValue(phoneticTranscriptions); },
+ get context() { return getCachedValue(context2); },
+ media,
+ dictionaryEntry
+ };
+ Object.defineProperty(result, 'dictionaryEntry', {
+ configurable: false,
+ enumerable: false,
+ writable: false,
+ value: dictionaryEntry
+ });
+ return result;
+}
- /**
- * Creates a deferred-evaluation value.
- * @template [T=unknown]
- * @param {() => T} getter The function to invoke to get the return value.
- * @returns {import('anki-templates-internal').CachedValue<T>} An object which can be passed into `getCachedValue`.
- */
- createCachedValue(getter) {
- return {getter, hasValue: false, value: void 0};
- }
+/**
+ * Creates a deferred-evaluation value.
+ * @template [T=unknown]
+ * @param {() => T} getter The function to invoke to get the return value.
+ * @returns {import('anki-templates-internal').CachedValue<T>} An object which can be passed into `getCachedValue`.
+ */
+export function createCachedValue(getter) {
+ return {getter, hasValue: false, value: void 0};
+}
- /**
- * Gets the value of a cached object.
- * @template [T=unknown]
- * @param {import('anki-templates-internal').CachedValue<T>} item An object that was returned from `createCachedValue`.
- * @returns {T} The result of evaluating the getter, which is cached after the first invocation.
- */
- getCachedValue(item) {
- if (item.hasValue) { return /** @type {T} */ (item.value); }
- const value = item.getter();
- item.value = value;
- item.hasValue = true;
- return value;
- }
+/**
+ * Gets the value of a cached object.
+ * @template [T=unknown]
+ * @param {import('anki-templates-internal').CachedValue<T>} item An object that was returned from `createCachedValue`.
+ * @returns {T} The result of evaluating the getter, which is cached after the first invocation.
+ */
+export function getCachedValue(item) {
+ if (item.hasValue) { return /** @type {T} */ (item.value); }
+ const value = item.getter();
+ item.value = value;
+ item.hasValue = true;
+ return value;
+}
- // Private
+// Private
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {?import('dictionary').TermSource}
- */
- _getPrimarySource(dictionaryEntry) {
- for (const headword of dictionaryEntry.headwords) {
- for (const source of headword.sources) {
- if (source.isPrimary) { return source; }
- }
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {?import('dictionary').TermSource}
+ */
+function getPrimarySource(dictionaryEntry) {
+ for (const headword of dictionaryEntry.headwords) {
+ for (const source of headword.sources) {
+ if (source.isPrimary) { return source; }
}
- return null;
}
+ return null;
+}
- /**
- * @param {import('dictionary').DictionaryEntry} dictionaryEntry
- * @returns {string[]}
- */
- _getUniqueExpressions(dictionaryEntry) {
- if (dictionaryEntry.type === 'term') {
- const results = new Set();
- for (const {term} of dictionaryEntry.headwords) {
- results.add(term);
- }
- return [...results];
- } else {
- return [];
+/**
+ * @param {import('dictionary').DictionaryEntry} dictionaryEntry
+ * @returns {string[]}
+ */
+function getUniqueExpressions(dictionaryEntry) {
+ if (dictionaryEntry.type === 'term') {
+ const results = new Set();
+ for (const {term} of dictionaryEntry.headwords) {
+ results.add(term);
}
+ return [...results];
+ } else {
+ return [];
}
+}
- /**
- * @param {import('dictionary').DictionaryEntry} dictionaryEntry
- * @returns {string[]}
- */
- _getUniqueReadings(dictionaryEntry) {
- if (dictionaryEntry.type === 'term') {
- const results = new Set();
- for (const {reading} of dictionaryEntry.headwords) {
- results.add(reading);
- }
- return [...results];
- } else {
- return [];
+/**
+ * @param {import('dictionary').DictionaryEntry} dictionaryEntry
+ * @returns {string[]}
+ */
+function getUniqueReadings(dictionaryEntry) {
+ if (dictionaryEntry.type === 'term') {
+ const results = new Set();
+ for (const {reading} of dictionaryEntry.headwords) {
+ results.add(reading);
}
+ return [...results];
+ } else {
+ return [];
}
+}
- /**
- * @param {import('anki-templates-internal').Context} context
- * @returns {import('anki-templates').Context}
- */
- _getPublicContext(context) {
- let {documentTitle, query, fullQuery} = context;
- if (typeof documentTitle !== 'string') { documentTitle = ''; }
- return {
- query,
- fullQuery,
- document: {
- title: documentTitle
- }
- };
- }
+/**
+ * @param {import('anki-templates-internal').Context} context
+ * @returns {import('anki-templates').Context}
+ */
+function getPublicContext(context) {
+ let {documentTitle, query, fullQuery} = context;
+ if (typeof documentTitle !== 'string') { documentTitle = ''; }
+ return {
+ query,
+ fullQuery,
+ document: {
+ title: documentTitle
+ }
+ };
+}
- /**
- * @param {import('dictionary').DictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').PitchGroup[]}
- */
- _getPitches(dictionaryEntry) {
- /** @type {import('anki-templates').PitchGroup[]} */
- const results = [];
- if (dictionaryEntry.type === 'term') {
- for (const {dictionary, pronunciations} of DictionaryDataUtil.getGroupedPronunciations(dictionaryEntry)) {
- /** @type {import('anki-templates').Pitch[]} */
- const pitches = [];
- for (const groupedPronunciation of pronunciations) {
- const {pronunciation} = groupedPronunciation;
- if (pronunciation.type !== 'pitch-accent') { continue; }
- const {position, nasalPositions, devoicePositions, tags} = pronunciation;
- const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
- pitches.push({
- expressions: terms,
- reading,
- position,
- nasalPositions,
- devoicePositions,
- tags: this._convertPitchTags(tags),
- exclusiveExpressions: exclusiveTerms,
- exclusiveReadings
- });
- }
- results.push({dictionary, pitches});
+/**
+ * @param {import('dictionary').DictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').PitchGroup[]}
+ */
+function getPitches(dictionaryEntry) {
+ /** @type {import('anki-templates').PitchGroup[]} */
+ const results = [];
+ if (dictionaryEntry.type === 'term') {
+ for (const {dictionary, pronunciations} of getGroupedPronunciations(dictionaryEntry)) {
+ /** @type {import('anki-templates').Pitch[]} */
+ const pitches = [];
+ for (const groupedPronunciation of pronunciations) {
+ const {pronunciation} = groupedPronunciation;
+ if (pronunciation.type !== 'pitch-accent') { continue; }
+ const {position, nasalPositions, devoicePositions, tags} = pronunciation;
+ const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
+ pitches.push({
+ expressions: terms,
+ reading,
+ position,
+ nasalPositions,
+ devoicePositions,
+ tags: convertPitchTags(tags),
+ exclusiveExpressions: exclusiveTerms,
+ exclusiveReadings
+ });
}
+ results.push({dictionary, pitches});
}
- return results;
}
+ return results;
+}
- /**
- * @param {import('dictionary').DictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').TranscriptionGroup[]}
- */
- _getPhoneticTranscriptions(dictionaryEntry) {
- const results = [];
- if (dictionaryEntry.type === 'term') {
- for (const {dictionary, pronunciations} of DictionaryDataUtil.getGroupedPronunciations(dictionaryEntry)) {
- const phoneticTranscriptions = [];
- for (const groupedPronunciation of pronunciations) {
- const {pronunciation} = groupedPronunciation;
- if (pronunciation.type !== 'phonetic-transcription') { continue; }
- const {ipa, tags} = pronunciation;
- const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
- phoneticTranscriptions.push({
- expressions: terms,
- reading,
- ipa,
- tags,
- exclusiveExpressions: exclusiveTerms,
- exclusiveReadings
- });
- }
- results.push({dictionary, phoneticTranscriptions});
+/**
+ * @param {import('dictionary').DictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').TranscriptionGroup[]}
+ */
+function getPhoneticTranscriptions(dictionaryEntry) {
+ const results = [];
+ if (dictionaryEntry.type === 'term') {
+ for (const {dictionary, pronunciations} of getGroupedPronunciations(dictionaryEntry)) {
+ const phoneticTranscriptions = [];
+ for (const groupedPronunciation of pronunciations) {
+ const {pronunciation} = groupedPronunciation;
+ if (pronunciation.type !== 'phonetic-transcription') { continue; }
+ const {ipa, tags} = pronunciation;
+ const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
+ phoneticTranscriptions.push({
+ expressions: terms,
+ reading,
+ ipa,
+ tags,
+ exclusiveExpressions: exclusiveTerms,
+ exclusiveReadings
+ });
}
+ results.push({dictionary, phoneticTranscriptions});
}
- return results;
}
+ return results;
+}
- /**
- * @param {import('anki-templates-internal').CachedValue<import('anki-templates').PitchGroup[]>} cachedPitches
- * @returns {number}
- */
- _getPitchCount(cachedPitches) {
- const pitches = this.getCachedValue(cachedPitches);
- return pitches.reduce((i, v) => i + v.pitches.length, 0);
- }
+/**
+ * @param {import('anki-templates-internal').CachedValue<import('anki-templates').PitchGroup[]>} cachedPitches
+ * @returns {number}
+ */
+function getPitchCount(cachedPitches) {
+ const pitches = getCachedValue(cachedPitches);
+ return pitches.reduce((i, v) => i + v.pitches.length, 0);
+}
- /**
- * @param {import('dictionary').DictionaryEntry} dictionaryEntry
- * @param {import('anki-templates-internal').Context} context
- * @param {import('settings').ResultOutputMode} resultOutputMode
- * @returns {import('anki-templates').DictionaryEntry}
- */
- _getDefinition(dictionaryEntry, context, resultOutputMode) {
- switch (dictionaryEntry.type) {
- case 'term':
- return this._getTermDefinition(dictionaryEntry, context, resultOutputMode);
- case 'kanji':
- return this._getKanjiDefinition(dictionaryEntry, context);
- default:
- return /** @type {import('anki-templates').UnknownDictionaryEntry} */ ({});
- }
+/**
+ * @param {import('dictionary').DictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates-internal').Context} context
+ * @param {import('settings').ResultOutputMode} resultOutputMode
+ * @returns {import('anki-templates').DictionaryEntry}
+ */
+function getDefinition(dictionaryEntry, context, resultOutputMode) {
+ switch (dictionaryEntry.type) {
+ case 'term':
+ return getTermDefinition(dictionaryEntry, context, resultOutputMode);
+ case 'kanji':
+ return getKanjiDefinition(dictionaryEntry, context);
+ default:
+ return /** @type {import('anki-templates').UnknownDictionaryEntry} */ ({});
}
+}
- /**
- * @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
- * @param {import('anki-templates-internal').Context} context
- * @returns {import('anki-templates').KanjiDictionaryEntry}
- */
- _getKanjiDefinition(dictionaryEntry, context) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
-
- const {character, dictionary, onyomi, kunyomi, definitions} = dictionaryEntry;
+/**
+ * @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates-internal').Context} context
+ * @returns {import('anki-templates').KanjiDictionaryEntry}
+ */
+function getKanjiDefinition(dictionaryEntry, context) {
+ const {character, dictionary, onyomi, kunyomi, definitions} = dictionaryEntry;
+
+ let {url} = context;
+ if (typeof url !== 'string') { url = ''; }
+
+ const stats = createCachedValue(getKanjiStats.bind(null, dictionaryEntry));
+ const tags = createCachedValue(convertTags.bind(null, dictionaryEntry.tags));
+ const frequencies = createCachedValue(getKanjiFrequencies.bind(null, dictionaryEntry));
+ const cloze = createCachedValue(getCloze.bind(null, dictionaryEntry, context));
+
+ return {
+ type: 'kanji',
+ character,
+ dictionary,
+ onyomi,
+ kunyomi,
+ glossary: definitions,
+ get tags() { return getCachedValue(tags); },
+ get stats() { return getCachedValue(stats); },
+ get frequencies() { return getCachedValue(frequencies); },
+ url,
+ get cloze() { return getCachedValue(cloze); }
+ };
+}
- let {url} = context;
- if (typeof url !== 'string') { url = ''; }
+/**
+ * @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').KanjiStatGroups}
+ */
+function getKanjiStats(dictionaryEntry) {
+ /** @type {import('anki-templates').KanjiStatGroups} */
+ const results = {};
+ for (const [key, value] of Object.entries(dictionaryEntry.stats)) {
+ results[key] = value.map(convertKanjiStat);
+ }
+ return results;
+}
- const stats = this.createCachedValue(this._getKanjiStats.bind(this, dictionaryEntry));
- const tags = this.createCachedValue(this._convertTags.bind(this, dictionaryEntry.tags));
- const frequencies = this.createCachedValue(this._getKanjiFrequencies.bind(this, dictionaryEntry));
- const cloze = this.createCachedValue(this._getCloze.bind(this, dictionaryEntry, context));
+/**
+ * @param {import('dictionary').KanjiStat} kanjiStat
+ * @returns {import('anki-templates').KanjiStat}
+ */
+function convertKanjiStat({name, category, content, order, score, dictionary, value}) {
+ return {
+ name,
+ category,
+ notes: content,
+ order,
+ score,
+ dictionary,
+ value
+ };
+}
- return {
- type: 'kanji',
- character,
+/**
+ * @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').KanjiFrequency[]}
+ */
+function getKanjiFrequencies(dictionaryEntry) {
+ /** @type {import('anki-templates').KanjiFrequency[]} */
+ const results = [];
+ for (const {index, dictionary, dictionaryIndex, dictionaryPriority, character, frequency, displayValue} of dictionaryEntry.frequencies) {
+ results.push({
+ index,
dictionary,
- onyomi,
- kunyomi,
- glossary: definitions,
- get tags() { return self.getCachedValue(tags); },
- get stats() { return self.getCachedValue(stats); },
- get frequencies() { return self.getCachedValue(frequencies); },
- url,
- get cloze() { return self.getCachedValue(cloze); }
- };
+ dictionaryOrder: {
+ index: dictionaryIndex,
+ priority: dictionaryPriority
+ },
+ character,
+ frequency: displayValue !== null ? displayValue : frequency
+ });
}
+ return results;
+}
- /**
- * @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').KanjiStatGroups}
- */
- _getKanjiStats(dictionaryEntry) {
- /** @type {import('anki-templates').KanjiStatGroups} */
- const results = {};
- const convertKanjiStatBind = this._convertKanjiStat.bind(this);
- for (const [key, value] of Object.entries(dictionaryEntry.stats)) {
- results[key] = value.map(convertKanjiStatBind);
- }
- return results;
- }
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates-internal').Context} context
+ * @param {import('settings').ResultOutputMode} resultOutputMode
+ * @returns {import('anki-templates').TermDictionaryEntry}
+ */
+function getTermDefinition(dictionaryEntry, context, resultOutputMode) {
+ /** @type {import('anki-templates').TermDictionaryEntryType} */
+ let type = 'term';
+ switch (resultOutputMode) {
+ case 'group': type = 'termGrouped'; break;
+ case 'merge': type = 'termMerged'; break;
+ }
+
+ const {inflectionRuleChainCandidates, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, definitions} = dictionaryEntry;
+
+ let {url} = context;
+ if (typeof url !== 'string') { url = ''; }
+
+ const primarySource = getPrimarySource(dictionaryEntry);
+
+ const dictionaryNames = createCachedValue(getTermDictionaryNames.bind(null, dictionaryEntry));
+ const commonInfo = createCachedValue(getTermDictionaryEntryCommonInfo.bind(null, dictionaryEntry, type));
+ const termTags = createCachedValue(getTermTags.bind(null, dictionaryEntry, type));
+ const expressions = createCachedValue(getTermExpressions.bind(null, dictionaryEntry));
+ const frequencies = createCachedValue(getTermFrequencies.bind(null, dictionaryEntry));
+ const pitches = createCachedValue(getTermPitches.bind(null, dictionaryEntry));
+ const phoneticTranscriptions = createCachedValue(getTermPhoneticTranscriptions.bind(null, dictionaryEntry));
+ const glossary = createCachedValue(getTermGlossaryArray.bind(null, dictionaryEntry, type));
+ const cloze = createCachedValue(getCloze.bind(null, dictionaryEntry, context));
+ const furiganaSegments = createCachedValue(getTermFuriganaSegments.bind(null, dictionaryEntry, type));
+ const sequence = createCachedValue(getTermDictionaryEntrySequence.bind(null, dictionaryEntry));
+
+ return {
+ type,
+ id: (type === 'term' && definitions.length > 0 ? definitions[0].id : void 0),
+ source: (primarySource !== null ? primarySource.transformedText : null),
+ rawSource: (primarySource !== null ? primarySource.originalText : null),
+ sourceTerm: (type !== 'termMerged' ? (primarySource !== null ? primarySource.deinflectedText : null) : void 0),
+ inflectionRuleChainCandidates,
+ score,
+ isPrimary: (type === 'term' ? dictionaryEntry.isPrimary : void 0),
+ get sequence() { return getCachedValue(sequence); },
+ get dictionary() { return getCachedValue(dictionaryNames)[0]; },
+ dictionaryOrder: {
+ index: dictionaryIndex,
+ priority: dictionaryPriority
+ },
+ get dictionaryNames() { return getCachedValue(dictionaryNames); },
+ get expression() {
+ const {uniqueTerms} = getCachedValue(commonInfo);
+ return (type === 'term' || type === 'termGrouped' ? uniqueTerms[0] : uniqueTerms);
+ },
+ get reading() {
+ const {uniqueReadings} = getCachedValue(commonInfo);
+ return (type === 'term' || type === 'termGrouped' ? uniqueReadings[0] : uniqueReadings);
+ },
+ get expressions() { return getCachedValue(expressions); },
+ get glossary() { return getCachedValue(glossary); },
+ get definitionTags() { return type === 'term' ? getCachedValue(commonInfo).definitionTags : void 0; },
+ get termTags() { return getCachedValue(termTags); },
+ get definitions() { return getCachedValue(commonInfo).definitions; },
+ get frequencies() { return getCachedValue(frequencies); },
+ get pitches() { return getCachedValue(pitches); },
+ get phoneticTranscriptions() { return getCachedValue(phoneticTranscriptions); },
+ sourceTermExactMatchCount,
+ url,
+ get cloze() { return getCachedValue(cloze); },
+ get furiganaSegments() { return getCachedValue(furiganaSegments); }
+ };
+}
- /**
- * @param {import('dictionary').KanjiStat} kanjiStat
- * @returns {import('anki-templates').KanjiStat}
- */
- _convertKanjiStat({name, category, content, order, score, dictionary, value}) {
- return {
- name,
- category,
- notes: content,
- order,
- score,
- dictionary,
- value
- };
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {string[]}
+ */
+function getTermDictionaryNames(dictionaryEntry) {
+ const dictionaryNames = new Set();
+ for (const {dictionary} of dictionaryEntry.definitions) {
+ dictionaryNames.add(dictionary);
}
+ return [...dictionaryNames];
+}
- /**
- * @param {import('dictionary').KanjiDictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').KanjiFrequency[]}
- */
- _getKanjiFrequencies(dictionaryEntry) {
- /** @type {import('anki-templates').KanjiFrequency[]} */
- const results = [];
- for (const {index, dictionary, dictionaryIndex, dictionaryPriority, character, frequency, displayValue} of dictionaryEntry.frequencies) {
- results.push({
- index,
- dictionary,
- dictionaryOrder: {
- index: dictionaryIndex,
- priority: dictionaryPriority
- },
- character,
- frequency: displayValue !== null ? displayValue : frequency
- });
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates').TermDictionaryEntryType} type
+ * @returns {import('anki-templates').TermDictionaryEntryCommonInfo}
+ */
+function getTermDictionaryEntryCommonInfo(dictionaryEntry, type) {
+ const merged = (type === 'termMerged');
+ const hasDefinitions = (type !== 'term');
+
+ /** @type {Set<string>} */
+ const allTermsSet = new Set();
+ /** @type {Set<string>} */
+ const allReadingsSet = new Set();
+ for (const {term, reading} of dictionaryEntry.headwords) {
+ allTermsSet.add(term);
+ allReadingsSet.add(reading);
+ }
+ const uniqueTerms = [...allTermsSet];
+ const uniqueReadings = [...allReadingsSet];
+
+ /** @type {import('anki-templates').TermDefinition[]} */
+ const definitions = [];
+ /** @type {import('anki-templates').Tag[]} */
+ const definitionTags = [];
+ for (const {tags, headwordIndices, entries, dictionary, sequences} of dictionaryEntry.definitions) {
+ const definitionTags2 = [];
+ for (const tag of tags) {
+ definitionTags.push(convertTag(tag));
+ definitionTags2.push(convertTag(tag));
}
- return results;
+ if (!hasDefinitions) { continue; }
+ const only = merged ? getDisambiguations(dictionaryEntry.headwords, headwordIndices, allTermsSet, allReadingsSet) : void 0;
+ definitions.push({
+ sequence: sequences[0],
+ dictionary,
+ glossary: entries,
+ definitionTags: definitionTags2,
+ only
+ });
}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @param {import('anki-templates-internal').Context} context
- * @param {import('settings').ResultOutputMode} resultOutputMode
- * @returns {import('anki-templates').TermDictionaryEntry}
- */
- _getTermDefinition(dictionaryEntry, context, resultOutputMode) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
-
- /** @type {import('anki-templates').TermDictionaryEntryType} */
- let type = 'term';
- switch (resultOutputMode) {
- case 'group': type = 'termGrouped'; break;
- case 'merge': type = 'termMerged'; break;
- }
+ return {
+ uniqueTerms,
+ uniqueReadings,
+ definitionTags,
+ definitions: hasDefinitions ? definitions : void 0
+ };
+}
- const {inflectionRuleChainCandidates, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, definitions} = dictionaryEntry;
-
- let {url} = context;
- if (typeof url !== 'string') { url = ''; }
-
- const primarySource = this._getPrimarySource(dictionaryEntry);
-
- const dictionaryNames = this.createCachedValue(this._getTermDictionaryNames.bind(this, dictionaryEntry));
- const commonInfo = this.createCachedValue(this._getTermDictionaryEntryCommonInfo.bind(this, dictionaryEntry, type));
- const termTags = this.createCachedValue(this._getTermTags.bind(this, dictionaryEntry, type));
- const expressions = this.createCachedValue(this._getTermExpressions.bind(this, dictionaryEntry));
- const frequencies = this.createCachedValue(this._getTermFrequencies.bind(this, dictionaryEntry));
- const pitches = this.createCachedValue(this._getTermPitches.bind(this, dictionaryEntry));
- const phoneticTranscriptions = this.createCachedValue(this._getTermPhoneticTranscriptions.bind(this, dictionaryEntry));
- const glossary = this.createCachedValue(this._getTermGlossaryArray.bind(this, dictionaryEntry, type));
- const cloze = this.createCachedValue(this._getCloze.bind(this, dictionaryEntry, context));
- const furiganaSegments = this.createCachedValue(this._getTermFuriganaSegments.bind(this, dictionaryEntry, type));
- const sequence = this.createCachedValue(this._getTermDictionaryEntrySequence.bind(this, dictionaryEntry));
-
- return {
- type,
- id: (type === 'term' && definitions.length > 0 ? definitions[0].id : void 0),
- source: (primarySource !== null ? primarySource.transformedText : null),
- rawSource: (primarySource !== null ? primarySource.originalText : null),
- sourceTerm: (type !== 'termMerged' ? (primarySource !== null ? primarySource.deinflectedText : null) : void 0),
- inflectionRuleChainCandidates,
- score,
- isPrimary: (type === 'term' ? dictionaryEntry.isPrimary : void 0),
- get sequence() { return self.getCachedValue(sequence); },
- get dictionary() { return self.getCachedValue(dictionaryNames)[0]; },
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').TermFrequency[]}
+ */
+function getTermFrequencies(dictionaryEntry) {
+ const results = [];
+ const {headwords} = dictionaryEntry;
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, hasReading, frequency, displayValue} of dictionaryEntry.frequencies) {
+ const {term, reading} = headwords[headwordIndex];
+ results.push({
+ index: results.length,
+ expressionIndex: headwordIndex,
+ dictionary,
dictionaryOrder: {
index: dictionaryIndex,
priority: dictionaryPriority
},
- get dictionaryNames() { return self.getCachedValue(dictionaryNames); },
- get expression() {
- const {uniqueTerms} = self.getCachedValue(commonInfo);
- return (type === 'term' || type === 'termGrouped' ? uniqueTerms[0] : uniqueTerms);
- },
- get reading() {
- const {uniqueReadings} = self.getCachedValue(commonInfo);
- return (type === 'term' || type === 'termGrouped' ? uniqueReadings[0] : uniqueReadings);
- },
- get expressions() { return self.getCachedValue(expressions); },
- get glossary() { return self.getCachedValue(glossary); },
- get definitionTags() { return type === 'term' ? self.getCachedValue(commonInfo).definitionTags : void 0; },
- get termTags() { return self.getCachedValue(termTags); },
- get definitions() { return self.getCachedValue(commonInfo).definitions; },
- get frequencies() { return self.getCachedValue(frequencies); },
- get pitches() { return self.getCachedValue(pitches); },
- get phoneticTranscriptions() { return self.getCachedValue(phoneticTranscriptions); },
- sourceTermExactMatchCount,
- url,
- get cloze() { return self.getCachedValue(cloze); },
- get furiganaSegments() { return self.getCachedValue(furiganaSegments); }
- };
+ expression: term,
+ reading,
+ hasReading,
+ frequency: displayValue !== null ? displayValue : frequency
+ });
}
+ return results;
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {string[]}
- */
- _getTermDictionaryNames(dictionaryEntry) {
- const dictionaryNames = new Set();
- for (const {dictionary} of dictionaryEntry.definitions) {
- dictionaryNames.add(dictionary);
- }
- return [...dictionaryNames];
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').TermPitchAccent[]}
+ */
+function getTermPitches(dictionaryEntry) {
+ const results = [];
+ const {headwords} = dictionaryEntry;
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
+ const {term, reading} = headwords[headwordIndex];
+ const pitches = getPronunciationsOfType(pronunciations, 'pitch-accent');
+ const cachedPitches = createCachedValue(getTermPitchesInner.bind(null, pitches));
+ results.push({
+ index: results.length,
+ expressionIndex: headwordIndex,
+ dictionary,
+ dictionaryOrder: {
+ index: dictionaryIndex,
+ priority: dictionaryPriority
+ },
+ expression: term,
+ reading,
+ get pitches() { return getCachedValue(cachedPitches); }
+ });
}
+ return results;
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @param {import('anki-templates').TermDictionaryEntryType} type
- * @returns {import('anki-templates').TermDictionaryEntryCommonInfo}
- */
- _getTermDictionaryEntryCommonInfo(dictionaryEntry, type) {
- const merged = (type === 'termMerged');
- const hasDefinitions = (type !== 'term');
-
- /** @type {Set<string>} */
- const allTermsSet = new Set();
- /** @type {Set<string>} */
- const allReadingsSet = new Set();
- for (const {term, reading} of dictionaryEntry.headwords) {
- allTermsSet.add(term);
- allReadingsSet.add(reading);
- }
- const uniqueTerms = [...allTermsSet];
- const uniqueReadings = [...allReadingsSet];
-
- /** @type {import('anki-templates').TermDefinition[]} */
- const definitions = [];
- /** @type {import('anki-templates').Tag[]} */
- const definitionTags = [];
- for (const {tags, headwordIndices, entries, dictionary, sequences} of dictionaryEntry.definitions) {
- const definitionTags2 = [];
- for (const tag of tags) {
- definitionTags.push(this._convertTag(tag));
- definitionTags2.push(this._convertTag(tag));
- }
- if (!hasDefinitions) { continue; }
- const only = merged ? DictionaryDataUtil.getDisambiguations(dictionaryEntry.headwords, headwordIndices, allTermsSet, allReadingsSet) : void 0;
- definitions.push({
- sequence: sequences[0],
- dictionary,
- glossary: entries,
- definitionTags: definitionTags2,
- only
- });
- }
-
- return {
- uniqueTerms,
- uniqueReadings,
- definitionTags,
- definitions: hasDefinitions ? definitions : void 0
- };
+/**
+ * @param {import('dictionary').PitchAccent[]} pitches
+ * @returns {import('anki-templates').PitchAccent[]}
+ */
+function getTermPitchesInner(pitches) {
+ const results = [];
+ for (const {position, tags} of pitches) {
+ const cachedTags = createCachedValue(convertTags.bind(null, tags));
+ results.push({
+ position,
+ get tags() { return getCachedValue(cachedTags); }
+ });
}
+ return results;
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').TermFrequency[]}
- */
- _getTermFrequencies(dictionaryEntry) {
- const results = [];
- const {headwords} = dictionaryEntry;
- for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, hasReading, frequency, displayValue} of dictionaryEntry.frequencies) {
- const {term, reading} = headwords[headwordIndex];
- results.push({
- index: results.length,
- expressionIndex: headwordIndex,
- dictionary,
- dictionaryOrder: {
- index: dictionaryIndex,
- priority: dictionaryPriority
- },
- expression: term,
- reading,
- hasReading,
- frequency: displayValue !== null ? displayValue : frequency
- });
- }
- return results;
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').TermPhoneticTranscription[]}
+ */
+function getTermPhoneticTranscriptions(dictionaryEntry) {
+ const results = [];
+ const {headwords} = dictionaryEntry;
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
+ const {term, reading} = headwords[headwordIndex];
+ const phoneticTranscriptions = getPronunciationsOfType(pronunciations, 'phonetic-transcription');
+ const termPhoneticTranscriptions = getTermPhoneticTranscriptionsInner(phoneticTranscriptions);
+ results.push({
+ index: results.length,
+ expressionIndex: headwordIndex,
+ dictionary,
+ dictionaryOrder: {
+ index: dictionaryIndex,
+ priority: dictionaryPriority
+ },
+ expression: term,
+ reading,
+ get phoneticTranscriptions() { return termPhoneticTranscriptions; }
+ });
}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').TermPitchAccent[]}
- */
- _getTermPitches(dictionaryEntry) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
- const results = [];
- const {headwords} = dictionaryEntry;
- for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
- const {term, reading} = headwords[headwordIndex];
- const pitches = DictionaryDataUtil.getPronunciationsOfType(pronunciations, 'pitch-accent');
- const cachedPitches = this.createCachedValue(this._getTermPitchesInner.bind(this, pitches));
- results.push({
- index: results.length,
- expressionIndex: headwordIndex,
- dictionary,
- dictionaryOrder: {
- index: dictionaryIndex,
- priority: dictionaryPriority
- },
- expression: term,
- reading,
- get pitches() { return self.getCachedValue(cachedPitches); }
- });
- }
- return results;
- }
+ return results;
+}
- /**
- * @param {import('dictionary').PitchAccent[]} pitches
- * @returns {import('anki-templates').PitchAccent[]}
- */
- _getTermPitchesInner(pitches) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
- const results = [];
- for (const {position, tags} of pitches) {
- const cachedTags = this.createCachedValue(this._convertTags.bind(this, tags));
- results.push({
- position,
- get tags() { return self.getCachedValue(cachedTags); }
- });
- }
- return results;
+/**
+ * @param {import('dictionary').PhoneticTranscription[]} phoneticTranscriptions
+ * @returns {import('anki-templates').PhoneticTranscription[]}
+ */
+function getTermPhoneticTranscriptionsInner(phoneticTranscriptions) {
+ const results = [];
+ for (const {ipa, tags} of phoneticTranscriptions) {
+ const cachedTags = createCachedValue(convertTags.bind(null, tags));
+ results.push({
+ ipa,
+ get tags() { return getCachedValue(cachedTags); }
+ });
}
+ return results;
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').TermPhoneticTranscription[]}
- */
- _getTermPhoneticTranscriptions(dictionaryEntry) {
- const results = [];
- const {headwords} = dictionaryEntry;
- for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
- const {term, reading} = headwords[headwordIndex];
- const phoneticTranscriptions = DictionaryDataUtil.getPronunciationsOfType(pronunciations, 'phonetic-transcription');
- const termPhoneticTranscriptions = this._getTermPhoneticTranscriptionsInner(phoneticTranscriptions);
- results.push({
- index: results.length,
- expressionIndex: headwordIndex,
- dictionary,
- dictionaryOrder: {
- index: dictionaryIndex,
- priority: dictionaryPriority
- },
- expression: term,
- reading,
- get phoneticTranscriptions() { return termPhoneticTranscriptions; }
- });
- }
-
- return results;
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').TermHeadword[]}
+ */
+function getTermExpressions(dictionaryEntry) {
+ const results = [];
+ const {headwords} = dictionaryEntry;
+ for (let i = 0, ii = headwords.length; i < ii; ++i) {
+ const {term, reading, tags, sources: [{deinflectedText}], wordClasses} = headwords[i];
+ const termTags = createCachedValue(convertTags.bind(null, tags));
+ const frequencies = createCachedValue(getTermExpressionFrequencies.bind(null, dictionaryEntry, i));
+ const pitches = createCachedValue(getTermExpressionPitches.bind(null, dictionaryEntry, i));
+ const termFrequency = createCachedValue(getTermExpressionTermFrequency.bind(null, termTags));
+ const furiganaSegments = createCachedValue(getTermHeadwordFuriganaSegments.bind(null, term, reading));
+ const item = {
+ sourceTerm: deinflectedText,
+ expression: term,
+ reading,
+ get termTags() { return getCachedValue(termTags); },
+ get frequencies() { return getCachedValue(frequencies); },
+ get pitches() { return getCachedValue(pitches); },
+ get furiganaSegments() { return getCachedValue(furiganaSegments); },
+ get termFrequency() { return getCachedValue(termFrequency); },
+ wordClasses
+ };
+ results.push(item);
}
+ return results;
+}
- /**
- * @param {import('dictionary').PhoneticTranscription[]} phoneticTranscriptions
- * @returns {import('anki-templates').PhoneticTranscription[]}
- */
- _getTermPhoneticTranscriptionsInner(phoneticTranscriptions) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
- const results = [];
- for (const {ipa, tags} of phoneticTranscriptions) {
- const cachedTags = this.createCachedValue(this._convertTags.bind(this, tags));
- results.push({
- ipa,
- get tags() { return self.getCachedValue(cachedTags); }
- });
- }
- return results;
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {number} i
+ * @returns {import('anki-templates').TermFrequency[]}
+ */
+function getTermExpressionFrequencies(dictionaryEntry, i) {
+ const results = [];
+ const {headwords, frequencies} = dictionaryEntry;
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, hasReading, frequency, displayValue} of frequencies) {
+ if (headwordIndex !== i) { continue; }
+ const {term, reading} = headwords[headwordIndex];
+ results.push({
+ index: results.length,
+ expressionIndex: headwordIndex,
+ dictionary,
+ dictionaryOrder: {
+ index: dictionaryIndex,
+ priority: dictionaryPriority
+ },
+ expression: term,
+ reading,
+ hasReading,
+ frequency: displayValue !== null ? displayValue : frequency
+ });
}
+ return results;
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').TermHeadword[]}
- */
- _getTermExpressions(dictionaryEntry) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
- const results = [];
- const {headwords} = dictionaryEntry;
- for (let i = 0, ii = headwords.length; i < ii; ++i) {
- const {term, reading, tags, sources: [{deinflectedText}], wordClasses} = headwords[i];
- const termTags = this.createCachedValue(this._convertTags.bind(this, tags));
- const frequencies = this.createCachedValue(this._getTermExpressionFrequencies.bind(this, dictionaryEntry, i));
- const pitches = this.createCachedValue(this._getTermExpressionPitches.bind(this, dictionaryEntry, i));
- const termFrequency = this.createCachedValue(this._getTermExpressionTermFrequency.bind(this, termTags));
- const furiganaSegments = this.createCachedValue(this._getTermHeadwordFuriganaSegments.bind(this, term, reading));
- const item = {
- sourceTerm: deinflectedText,
- expression: term,
- reading,
- get termTags() { return self.getCachedValue(termTags); },
- get frequencies() { return self.getCachedValue(frequencies); },
- get pitches() { return self.getCachedValue(pitches); },
- get furiganaSegments() { return self.getCachedValue(furiganaSegments); },
- get termFrequency() { return self.getCachedValue(termFrequency); },
- wordClasses
- };
- results.push(item);
- }
- return results;
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {number} i
+ * @returns {import('anki-templates').TermPitchAccent[]}
+ */
+function getTermExpressionPitches(dictionaryEntry, i) {
+ const results = [];
+ const {headwords, pronunciations: termPronunciations} = dictionaryEntry;
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of termPronunciations) {
+ if (headwordIndex !== i) { continue; }
+ const {term, reading} = headwords[headwordIndex];
+ const pitches = getPronunciationsOfType(pronunciations, 'pitch-accent');
+ const cachedPitches = createCachedValue(getTermPitchesInner.bind(null, pitches));
+ results.push({
+ index: results.length,
+ expressionIndex: headwordIndex,
+ dictionary,
+ dictionaryOrder: {
+ index: dictionaryIndex,
+ priority: dictionaryPriority
+ },
+ expression: term,
+ reading,
+ get pitches() { return getCachedValue(cachedPitches); }
+ });
}
+ return results;
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @param {number} i
- * @returns {import('anki-templates').TermFrequency[]}
- */
- _getTermExpressionFrequencies(dictionaryEntry, i) {
- const results = [];
- const {headwords, frequencies} = dictionaryEntry;
- for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, hasReading, frequency, displayValue} of frequencies) {
- if (headwordIndex !== i) { continue; }
- const {term, reading} = headwords[headwordIndex];
- results.push({
- index: results.length,
- expressionIndex: headwordIndex,
- dictionary,
- dictionaryOrder: {
- index: dictionaryIndex,
- priority: dictionaryPriority
- },
- expression: term,
- reading,
- hasReading,
- frequency: displayValue !== null ? displayValue : frequency
- });
- }
- return results;
- }
+/**
+ * @param {import('anki-templates-internal').CachedValue<import('anki-templates').Tag[]>} cachedTermTags
+ * @returns {import('anki-templates').TermFrequencyType}
+ */
+function getTermExpressionTermFrequency(cachedTermTags) {
+ const termTags = getCachedValue(cachedTermTags);
+ return getTermFrequency(termTags);
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @param {number} i
- * @returns {import('anki-templates').TermPitchAccent[]}
- */
- _getTermExpressionPitches(dictionaryEntry, i) {
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const self = this;
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates').TermDictionaryEntryType} type
+ * @returns {import('dictionary-data').TermGlossary[]|undefined}
+ */
+function getTermGlossaryArray(dictionaryEntry, type) {
+ if (type === 'term') {
const results = [];
- const {headwords, pronunciations: termPronunciations} = dictionaryEntry;
- for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of termPronunciations) {
- if (headwordIndex !== i) { continue; }
- const {term, reading} = headwords[headwordIndex];
- const pitches = DictionaryDataUtil.getPronunciationsOfType(pronunciations, 'pitch-accent');
- const cachedPitches = this.createCachedValue(this._getTermPitchesInner.bind(this, pitches));
- results.push({
- index: results.length,
- expressionIndex: headwordIndex,
- dictionary,
- dictionaryOrder: {
- index: dictionaryIndex,
- priority: dictionaryPriority
- },
- expression: term,
- reading,
- get pitches() { return self.getCachedValue(cachedPitches); }
- });
+ for (const {entries} of dictionaryEntry.definitions) {
+ results.push(...entries);
}
return results;
}
+ return void 0;
+}
- /**
- * @param {import('anki-templates-internal').CachedValue<import('anki-templates').Tag[]>} cachedTermTags
- * @returns {import('anki-templates').TermFrequencyType}
- */
- _getTermExpressionTermFrequency(cachedTermTags) {
- const termTags = this.getCachedValue(cachedTermTags);
- return DictionaryDataUtil.getTermFrequency(termTags);
- }
-
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @param {import('anki-templates').TermDictionaryEntryType} type
- * @returns {import('dictionary-data').TermGlossary[]|undefined}
- */
- _getTermGlossaryArray(dictionaryEntry, type) {
- if (type === 'term') {
- const results = [];
- for (const {entries} of dictionaryEntry.definitions) {
- results.push(...entries);
- }
- return results;
- }
- return void 0;
- }
-
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @param {import('anki-templates').TermDictionaryEntryType} type
- * @returns {import('anki-templates').Tag[]|undefined}
- */
- _getTermTags(dictionaryEntry, type) {
- if (type !== 'termMerged') {
- const results = [];
- for (const {tag} of DictionaryDataUtil.groupTermTags(dictionaryEntry)) {
- results.push(this._convertTag(tag));
- }
- return results;
- }
- return void 0;
- }
-
- /**
- * @param {import('dictionary').Tag[]} tags
- * @returns {import('anki-templates').Tag[]}
- */
- _convertTags(tags) {
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates').TermDictionaryEntryType} type
+ * @returns {import('anki-templates').Tag[]|undefined}
+ */
+function getTermTags(dictionaryEntry, type) {
+ if (type !== 'termMerged') {
const results = [];
- for (const tag of tags) {
- results.push(this._convertTag(tag));
+ for (const {tag} of groupTermTags(dictionaryEntry)) {
+ results.push(convertTag(tag));
}
return results;
}
+ return void 0;
+}
- /**
- * @param {import('dictionary').Tag} tag
- * @returns {import('anki-templates').Tag}
- */
- _convertTag({name, category, content, order, score, dictionaries, redundant}) {
- return {
- name,
- category,
- notes: (content.length > 0 ? content[0] : ''),
- order,
- score,
- dictionary: (dictionaries.length > 0 ? dictionaries[0] : ''),
- redundant
- };
+/**
+ * @param {import('dictionary').Tag[]} tags
+ * @returns {import('anki-templates').Tag[]}
+ */
+function convertTags(tags) {
+ const results = [];
+ for (const tag of tags) {
+ results.push(convertTag(tag));
}
+ return results;
+}
- /**
- * @param {import('dictionary').Tag[]} tags
- * @returns {import('anki-templates').PitchTag[]}
- */
- _convertPitchTags(tags) {
- const results = [];
- for (const tag of tags) {
- results.push(this._convertPitchTag(tag));
- }
- return results;
- }
+/**
+ * @param {import('dictionary').Tag} tag
+ * @returns {import('anki-templates').Tag}
+ */
+function convertTag({name, category, content, order, score, dictionaries, redundant}) {
+ return {
+ name,
+ category,
+ notes: (content.length > 0 ? content[0] : ''),
+ order,
+ score,
+ dictionary: (dictionaries.length > 0 ? dictionaries[0] : ''),
+ redundant
+ };
+}
- /**
- * @param {import('dictionary').Tag} tag
- * @returns {import('anki-templates').PitchTag}
- */
- _convertPitchTag({name, category, content, order, score, dictionaries, redundant}) {
- return {
- name,
- category,
- order,
- score,
- content: [...content],
- dictionaries: [...dictionaries],
- redundant
- };
+/**
+ * @param {import('dictionary').Tag[]} tags
+ * @returns {import('anki-templates').PitchTag[]}
+ */
+function convertPitchTags(tags) {
+ const results = [];
+ for (const tag of tags) {
+ results.push(convertPitchTag(tag));
}
+ return results;
+}
- /**
- * @param {import('dictionary').DictionaryEntry} dictionaryEntry
- * @param {import('anki-templates-internal').Context} context
- * @returns {import('anki-templates').Cloze}
- */
- _getCloze(dictionaryEntry, context) {
- let originalText = '';
- switch (dictionaryEntry.type) {
- case 'term':
- {
- const primarySource = this._getPrimarySource(dictionaryEntry);
- if (primarySource !== null) { originalText = primarySource.originalText; }
- }
- break;
- case 'kanji':
- originalText = dictionaryEntry.character;
- break;
- }
-
- const {sentence} = context;
- let text;
- let offset;
- if (typeof sentence === 'object' && sentence !== null) {
- ({text, offset} = sentence);
- }
- if (typeof text !== 'string') { text = ''; }
- if (typeof offset !== 'number') { offset = 0; }
-
- return {
- sentence: text,
- prefix: text.substring(0, offset),
- body: text.substring(offset, offset + originalText.length),
- suffix: text.substring(offset + originalText.length)
- };
- }
+/**
+ * @param {import('dictionary').Tag} tag
+ * @returns {import('anki-templates').PitchTag}
+ */
+function convertPitchTag({name, category, content, order, score, dictionaries, redundant}) {
+ return {
+ name,
+ category,
+ order,
+ score,
+ content: [...content],
+ dictionaries: [...dictionaries],
+ redundant
+ };
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @param {import('anki-templates').TermDictionaryEntryType} type
- * @returns {import('anki-templates').FuriganaSegment[]|undefined}
- */
- _getTermFuriganaSegments(dictionaryEntry, type) {
- if (type === 'term') {
- for (const {term, reading} of dictionaryEntry.headwords) {
- return this._getTermHeadwordFuriganaSegments(term, reading);
+/**
+ * @param {import('dictionary').DictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates-internal').Context} context
+ * @returns {import('anki-templates').Cloze}
+ */
+function getCloze(dictionaryEntry, context) {
+ let originalText = '';
+ switch (dictionaryEntry.type) {
+ case 'term':
+ {
+ const primarySource = getPrimarySource(dictionaryEntry);
+ if (primarySource !== null) { originalText = primarySource.originalText; }
}
+ break;
+ case 'kanji':
+ originalText = dictionaryEntry.character;
+ break;
+ }
+
+ const {sentence} = context;
+ let text;
+ let offset;
+ if (typeof sentence === 'object' && sentence !== null) {
+ ({text, offset} = sentence);
+ }
+ if (typeof text !== 'string') { text = ''; }
+ if (typeof offset !== 'number') { offset = 0; }
+
+ return {
+ sentence: text,
+ prefix: text.substring(0, offset),
+ body: text.substring(offset, offset + originalText.length),
+ suffix: text.substring(offset + originalText.length)
+ };
+}
+
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @param {import('anki-templates').TermDictionaryEntryType} type
+ * @returns {import('anki-templates').FuriganaSegment[]|undefined}
+ */
+function getTermFuriganaSegments(dictionaryEntry, type) {
+ if (type === 'term') {
+ for (const {term, reading} of dictionaryEntry.headwords) {
+ return getTermHeadwordFuriganaSegments(term, reading);
}
- return void 0;
}
+ return void 0;
+}
- /**
- * @param {string} term
- * @param {string} reading
- * @returns {import('anki-templates').FuriganaSegment[]}
- */
- _getTermHeadwordFuriganaSegments(term, reading) {
- /** @type {import('anki-templates').FuriganaSegment[]} */
- const result = [];
- for (const {text, reading: reading2} of distributeFurigana(term, reading)) {
- result.push({text, furigana: reading2});
- }
- return result;
+/**
+ * @param {string} term
+ * @param {string} reading
+ * @returns {import('anki-templates').FuriganaSegment[]}
+ */
+function getTermHeadwordFuriganaSegments(term, reading) {
+ /** @type {import('anki-templates').FuriganaSegment[]} */
+ const result = [];
+ for (const {text, reading: reading2} of distributeFurigana(term, reading)) {
+ result.push({text, furigana: reading2});
}
+ return result;
+}
- /**
- * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {number}
- */
- _getTermDictionaryEntrySequence(dictionaryEntry) {
- let hasSequence = false;
- let mainSequence = -1;
- if (!dictionaryEntry.isPrimary) { return mainSequence; }
- for (const {sequences} of dictionaryEntry.definitions) {
- const sequence = sequences[0];
- if (!hasSequence) {
- mainSequence = sequence;
- hasSequence = true;
- if (mainSequence === -1) { break; }
- } else if (mainSequence !== sequence) {
- mainSequence = -1;
- break;
- }
+/**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {number}
+ */
+function getTermDictionaryEntrySequence(dictionaryEntry) {
+ let hasSequence = false;
+ let mainSequence = -1;
+ if (!dictionaryEntry.isPrimary) { return mainSequence; }
+ for (const {sequences} of dictionaryEntry.definitions) {
+ const sequence = sequences[0];
+ if (!hasSequence) {
+ mainSequence = sequence;
+ hasSequence = true;
+ if (mainSequence === -1) { break; }
+ } else if (mainSequence !== sequence) {
+ mainSequence = -1;
+ break;
}
- return mainSequence;
}
+ return mainSequence;
}
diff --git a/ext/js/data/sandbox/array-buffer-util.js b/ext/js/data/sandbox/array-buffer-util.js
index 1857ec74..487fcd24 100644
--- a/ext/js/data/sandbox/array-buffer-util.js
+++ b/ext/js/data/sandbox/array-buffer-util.js
@@ -17,61 +17,56 @@
*/
/**
- * Class containing generic ArrayBuffer utility functions.
+ * Decodes the contents of an ArrayBuffer using UTF8.
+ * @param {ArrayBuffer} arrayBuffer The input ArrayBuffer.
+ * @returns {string} A UTF8-decoded string.
*/
-export class ArrayBufferUtil {
- /**
- * Decodes the contents of an ArrayBuffer using UTF8.
- * @param {ArrayBuffer} arrayBuffer The input ArrayBuffer.
- * @returns {string} A UTF8-decoded string.
- */
- static arrayBufferUtf8Decode(arrayBuffer) {
- try {
- return new TextDecoder('utf-8').decode(arrayBuffer);
- } catch (e) {
- return decodeURIComponent(escape(this.arrayBufferToBinaryString(arrayBuffer)));
- }
+export function arrayBufferUtf8Decode(arrayBuffer) {
+ try {
+ return new TextDecoder('utf-8').decode(arrayBuffer);
+ } catch (e) {
+ return decodeURIComponent(escape(arrayBufferToBinaryString(arrayBuffer)));
}
+}
- /**
- * Converts the contents of an ArrayBuffer to a base64 string.
- * @param {ArrayBuffer} arrayBuffer The input ArrayBuffer.
- * @returns {string} A base64 string representing the binary content.
- */
- static arrayBufferToBase64(arrayBuffer) {
- return btoa(this.arrayBufferToBinaryString(arrayBuffer));
- }
+/**
+ * Converts the contents of an ArrayBuffer to a base64 string.
+ * @param {ArrayBuffer} arrayBuffer The input ArrayBuffer.
+ * @returns {string} A base64 string representing the binary content.
+ */
+export function arrayBufferToBase64(arrayBuffer) {
+ return btoa(arrayBufferToBinaryString(arrayBuffer));
+}
- /**
- * Converts the contents of an ArrayBuffer to a binary string.
- * @param {ArrayBuffer} arrayBuffer The input ArrayBuffer.
- * @returns {string} A string representing the binary content.
- */
- static arrayBufferToBinaryString(arrayBuffer) {
- const bytes = new Uint8Array(arrayBuffer);
- try {
- return String.fromCharCode(...bytes);
- } catch (e) {
- let binary = '';
- for (let i = 0, ii = bytes.byteLength; i < ii; ++i) {
- binary += String.fromCharCode(bytes[i]);
- }
- return binary;
+/**
+ * Converts the contents of an ArrayBuffer to a binary string.
+ * @param {ArrayBuffer} arrayBuffer The input ArrayBuffer.
+ * @returns {string} A string representing the binary content.
+ */
+export function arrayBufferToBinaryString(arrayBuffer) {
+ const bytes = new Uint8Array(arrayBuffer);
+ try {
+ return String.fromCharCode(...bytes);
+ } catch (e) {
+ let binary = '';
+ for (let i = 0, ii = bytes.byteLength; i < ii; ++i) {
+ binary += String.fromCharCode(bytes[i]);
}
+ return binary;
}
+}
- /**
- * Converts a base64 string to an ArrayBuffer.
- * @param {string} content The binary content string encoded in base64.
- * @returns {ArrayBuffer} A new `ArrayBuffer` object corresponding to the specified content.
- */
- static base64ToArrayBuffer(content) {
- const binaryContent = atob(content);
- const length = binaryContent.length;
- const array = new Uint8Array(length);
- for (let i = 0; i < length; ++i) {
- array[i] = binaryContent.charCodeAt(i);
- }
- return array.buffer;
+/**
+ * Converts a base64 string to an ArrayBuffer.
+ * @param {string} content The binary content string encoded in base64.
+ * @returns {ArrayBuffer} A new `ArrayBuffer` object corresponding to the specified content.
+ */
+export function base64ToArrayBuffer(content) {
+ const binaryContent = atob(content);
+ const length = binaryContent.length;
+ const array = new Uint8Array(length);
+ for (let i = 0; i < length; ++i) {
+ array[i] = binaryContent.charCodeAt(i);
}
+ return array.buffer;
}
diff --git a/ext/js/data/sandbox/string-util.js b/ext/js/data/sandbox/string-util.js
index 3bc1c549..45e52f08 100644
--- a/ext/js/data/sandbox/string-util.js
+++ b/ext/js/data/sandbox/string-util.js
@@ -17,59 +17,54 @@
*/
/**
- * Class containing generic string utility functions.
+ * Reads code points from a string in the forward direction.
+ * @param {string} text The text to read the code points from.
+ * @param {number} position The index of the first character to read.
+ * @param {number} count The number of code points to read.
+ * @returns {string} The code points from the string.
*/
-export class StringUtil {
- /**
- * Reads code points from a string in the forward direction.
- * @param {string} text The text to read the code points from.
- * @param {number} position The index of the first character to read.
- * @param {number} count The number of code points to read.
- * @returns {string} The code points from the string.
- */
- static readCodePointsForward(text, position, count) {
- const textLength = text.length;
- let result = '';
- for (; count > 0; --count) {
- const char = text[position];
- result += char;
- if (++position >= textLength) { break; }
- const charCode = char.charCodeAt(0);
- if (charCode >= 0xd800 && charCode < 0xdc00) { // charCode is a high surrogate code
- const char2 = text[position];
- const charCode2 = char2.charCodeAt(0);
- if (charCode2 >= 0xdc00 && charCode2 < 0xe000) { // charCode2 is a low surrogate code
- result += char2;
- if (++position >= textLength) { break; }
- }
+export function readCodePointsForward(text, position, count) {
+ const textLength = text.length;
+ let result = '';
+ for (; count > 0; --count) {
+ const char = text[position];
+ result += char;
+ if (++position >= textLength) { break; }
+ const charCode = char.charCodeAt(0);
+ if (charCode >= 0xd800 && charCode < 0xdc00) { // charCode is a high surrogate code
+ const char2 = text[position];
+ const charCode2 = char2.charCodeAt(0);
+ if (charCode2 >= 0xdc00 && charCode2 < 0xe000) { // charCode2 is a low surrogate code
+ result += char2;
+ if (++position >= textLength) { break; }
}
}
- return result;
}
+ return result;
+}
- /**
- * Reads code points from a string in the backward direction.
- * @param {string} text The text to read the code points from.
- * @param {number} position The index of the first character to read.
- * @param {number} count The number of code points to read.
- * @returns {string} The code points from the string.
- */
- static readCodePointsBackward(text, position, count) {
- let result = '';
- for (; count > 0; --count) {
- const char = text[position];
- result = char + result;
- if (--position < 0) { break; }
- const charCode = char.charCodeAt(0);
- if (charCode >= 0xdc00 && charCode < 0xe000) { // charCode is a low surrogate code
- const char2 = text[position];
- const charCode2 = char2.charCodeAt(0);
- if (charCode2 >= 0xd800 && charCode2 < 0xdc00) { // charCode2 is a high surrogate code
- result = char2 + result;
- if (--position < 0) { break; }
- }
+/**
+ * Reads code points from a string in the backward direction.
+ * @param {string} text The text to read the code points from.
+ * @param {number} position The index of the first character to read.
+ * @param {number} count The number of code points to read.
+ * @returns {string} The code points from the string.
+ */
+export function readCodePointsBackward(text, position, count) {
+ let result = '';
+ for (; count > 0; --count) {
+ const char = text[position];
+ result = char + result;
+ if (--position < 0) { break; }
+ const charCode = char.charCodeAt(0);
+ if (charCode >= 0xdc00 && charCode < 0xe000) { // charCode is a low surrogate code
+ const char2 = text[position];
+ const charCode2 = char2.charCodeAt(0);
+ if (charCode2 >= 0xd800 && charCode2 < 0xdc00) { // charCode2 is a high surrogate code
+ result = char2 + result;
+ if (--position < 0) { break; }
}
}
- return result;
}
+ return result;
}