diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js index 191058c1..e786c4e2 100644 --- a/ext/bg/js/dictionary.js +++ b/ext/bg/js/dictionary.js @@ -327,45 +327,50 @@ function dictFieldSplit(field) { } async function dictFieldFormat(field, definition, mode, options) { - const markers = [ - 'audio', - 'character', - 'cloze-body', - 'cloze-prefix', - 'cloze-suffix', - 'dictionary', - 'expression', - 'furigana', - 'furigana-plain', - 'glossary', - 'glossary-brief', - 'kunyomi', - 'onyomi', - 'reading', - 'screenshot', - 'sentence', - 'tags', - 'url' - ]; - - for (const marker of markers) { - const data = { - marker, - definition, - group: options.general.resultOutputMode === 'group', - merge: options.general.resultOutputMode === 'merge', - modeTermKanji: mode === 'term-kanji', - modeTermKana: mode === 'term-kana', - modeKanji: mode === 'kanji', - compactGlossaries: options.general.compactGlossaries - }; - - const html = await apiTemplateRender(options.anki.fieldTemplates, data, true); - field = field.replace(`{${marker}}`, html); - } - - return field; + const data = { + marker: null, + definition, + group: options.general.resultOutputMode === 'group', + merge: options.general.resultOutputMode === 'merge', + modeTermKanji: mode === 'term-kanji', + modeTermKana: mode === 'term-kana', + modeKanji: mode === 'kanji', + compactGlossaries: options.general.compactGlossaries + }; + const markers = dictFieldFormat.markers; + const pattern = /\{([\w\-]+)\}/g; + return await stringReplaceAsync(field, pattern, async (g0, marker) => { + if (!markers.has(marker)) { + return g0; + } + data.marker = marker; + try { + return await apiTemplateRender(options.anki.fieldTemplates, data, true); + } catch (e) { + return `{${marker}-render-error}`; + } + }); } +dictFieldFormat.markers = new Set([ + 'audio', + 'character', + 'cloze-body', + 'cloze-prefix', + 'cloze-suffix', + 'dictionary', + 'expression', + 'furigana', + 'furigana-plain', + 'glossary', + 'glossary-brief', + 'kunyomi', + 'onyomi', + 'reading', + 'screenshot', + 'sentence', + 'tags', + 'url' +]); async function dictNoteFormat(definition, mode, options) { const note = {fields: {}, tags: options.anki.tags}; diff --git a/ext/mixed/js/extension.js b/ext/mixed/js/extension.js index 54862e19..12ed9c1f 100644 --- a/ext/mixed/js/extension.js +++ b/ext/mixed/js/extension.js @@ -133,3 +133,18 @@ function promiseTimeout(delay, resolveValue) { return promise; } + +function stringReplaceAsync(str, regex, replacer) { + let match; + let index = 0; + const parts = []; + while ((match = regex.exec(str)) !== null) { + parts.push(str.substring(index, match.index), replacer(...match, match.index, str)); + index = regex.lastIndex; + } + if (parts.length === 0) { + return Promise.resolve(str); + } + parts.push(str.substring(index)); + return Promise.all(parts).then(v => v.join('')); +}