diff options
Diffstat (limited to 'ext/mixed/js')
-rw-r--r-- | ext/mixed/js/api.js | 12 | ||||
-rw-r--r-- | ext/mixed/js/display.js | 128 |
2 files changed, 115 insertions, 25 deletions
diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index 4a1b0c11..1e7625da 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -89,22 +89,10 @@ const api = (() => { return this._invoke('injectAnkiNoteMedia', {expression, reading, timestamp, audioDetails, screenshotDetails, clipboardImage}); } - definitionAdd(definition, mode, context, ownerFrameId, optionsContext) { - return this._invoke('definitionAdd', {definition, mode, context, ownerFrameId, optionsContext}); - } - - definitionsAddable(definitions, modes, context, optionsContext) { - return this._invoke('definitionsAddable', {definitions, modes, context, optionsContext}); - } - noteView(noteId) { return this._invoke('noteView', {noteId}); } - templateRender(template, data, marker) { - return this._invoke('templateRender', {data, template, marker}); - } - audioGetUri(source, expression, reading, details) { return this._invoke('audioGetUri', {source, expression, reading, details}); } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 7dd5920a..4c42a48d 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -16,6 +16,7 @@ */ /* global + * AnkiNoteBuilder * AudioSystem * DisplayGenerator * DisplayHistory @@ -24,6 +25,7 @@ * MediaLoader * PopupFactory * QueryParser + * TemplateRenderer * WindowScroll * api * dynamicLoader @@ -83,6 +85,12 @@ class Display extends EventDispatcher { }); this._mode = null; this._ownerFrameId = null; + this._defaultAnkiFieldTemplates = null; + this._defaultAnkiFieldTemplatesPromise = null; + this._templateRenderer = new TemplateRenderer(); + this._ankiNoteBuilder = new AnkiNoteBuilder({ + renderTemplate: this._renderTemplate.bind(this) + }); this.registerActions([ ['close', () => { this.onEscape(); }], @@ -891,7 +899,8 @@ class Display extends EventDispatcher { const modes = isTerms ? ['term-kanji', 'term-kana'] : ['kanji']; let states; try { - states = await this._getDefinitionsAddable(definitions, modes); + const noteContext = await this._getNoteContext(); + states = await this._areDefinitionsAddable(definitions, modes, noteContext); } catch (e) { return; } @@ -1054,10 +1063,8 @@ class Display extends EventDispatcher { try { this.setSpinnerVisible(true); - const ownerFrameId = this._ownerFrameId; - const optionsContext = this.getOptionsContext(); const noteContext = await this._getNoteContext(); - const noteId = await api.definitionAdd(definition, mode, noteContext, ownerFrameId, optionsContext); + const noteId = await this._addDefinition(definition, mode, noteContext); if (noteId) { const index = this._definitions.indexOf(definition); const adderButton = this._adderButtonFind(index, mode); @@ -1196,15 +1203,6 @@ class Display extends EventDispatcher { return container !== null ? container.querySelector('.action-play-audio>img') : null; } - async _getDefinitionsAddable(definitions, modes) { - try { - const noteContext = await this._getNoteContext(); - return await api.definitionsAddable(definitions, modes, noteContext, this.getOptionsContext()); - } catch (e) { - return []; - } - } - _indexOf(nodeList, node) { for (let i = 0, ii = nodeList.length; i < ii; ++i) { if (nodeList[i] === node) { @@ -1315,4 +1313,108 @@ class Display extends EventDispatcher { this._mode = mode; this.trigger('modeChange', {mode}); } + + async _getTemplates(options) { + let templates = options.anki.fieldTemplates; + if (typeof templates === 'string') { return templates; } + + templates = this._defaultAnkiFieldTemplates; + if (typeof templates === 'string') { return templates; } + + return await this._getDefaultTemplatesPromise(); + } + + _getDefaultTemplatesPromise() { + if (this._defaultAnkiFieldTemplatesPromise === null) { + this._defaultAnkiFieldTemplatesPromise = this._getDefaultTemplates(); + this._defaultAnkiFieldTemplatesPromise.then( + () => { this._defaultAnkiFieldTemplatesPromise = null; }, + () => {} // NOP + ); + } + return this._defaultAnkiFieldTemplatesPromise; + } + + async _getDefaultTemplates() { + const value = await api.getDefaultAnkiFieldTemplates(); + this._defaultAnkiFieldTemplates = value; + return value; + } + + async _renderTemplate(template, data, marker) { + return await this._templateRenderer.render(template, data, marker); + } + + async _addDefinition(definition, mode, context) { + const options = this._options; + const templates = await this._getTemplates(options); + const note = await this._createNote(definition, mode, context, options, templates, true); + return await api.addAnkiNote(note); + } + + async _areDefinitionsAddable(definitions, modes, context) { + const options = this._options; + const templates = await this._getTemplates(options); + + const modeCount = modes.length; + const {duplicateScope} = options.anki; + const notePromises = []; + for (const definition of definitions) { + for (const mode of modes) { + const notePromise = this._createNote(definition, mode, context, options, templates, false); + notePromises.push(notePromise); + } + } + const notes = await Promise.all(notePromises); + + const infos = await api.getAnkiNoteInfo(notes, duplicateScope); + const results = []; + for (let i = 0, ii = infos.length; i < ii; i += modeCount) { + results.push(infos.slice(i, i + modeCount)); + } + return results; + } + + async _createNote(definition, mode, context, options, templates, injectMedia) { + const { + general: {resultOutputMode, compactGlossaries}, + anki: {tags, duplicateScope, kanji, terms, screenshot: {format, quality}}, + audio: {sources, customSourceUrl} + } = options; + const modeOptions = (mode === 'kanji') ? kanji : terms; + + if (injectMedia) { + const timestamp = Date.now(); + const ownerFrameId = this._ownerFrameId; + const {fields} = modeOptions; + const definitionExpressions = definition.expressions; + const {expression, reading} = Array.isArray(definitionExpressions) ? definitionExpressions[0] : definition; + const audioDetails = (mode !== 'kanji' && this._ankiNoteBuilder.containsMarker(fields, 'audio') ? {sources, customSourceUrl} : null); + const screenshotDetails = (this._ankiNoteBuilder.containsMarker(fields, 'screenshot') ? {ownerFrameId, format, quality} : null); + const clipboardImage = (this._ankiNoteBuilder.containsMarker(fields, 'clipboard-image')); + const {screenshotFileName, clipboardImageFileName, audioFileName} = await api.injectAnkiNoteMedia( + expression, + reading, + timestamp, + audioDetails, + screenshotDetails, + clipboardImage + ); + if (screenshotFileName !== null) { definition.screenshotFileName = screenshotFileName; } + if (clipboardImageFileName !== null) { definition.clipboardImageFileName = clipboardImageFileName; } + if (audioFileName !== null) { definition.audioFileName = audioFileName; } + } + + return await this._ankiNoteBuilder.createNote({ + definition, + mode, + context, + templates, + tags, + duplicateScope, + resultOutputMode, + compactGlossaries, + modeOptions + }); + } } |