Use Anki classes directly in Display (#804)
* Add _getTemplates function * Add template renderer to display pages * Add AnkiNoteBuilder to Display * Update AnkiTemplatesController to directly use TemplateRenderer * Remove old APIs
This commit is contained in:
parent
9ce682272c
commit
a531618c48
@ -24,7 +24,6 @@
|
||||
<script src="/mixed/js/japanese.js"></script>
|
||||
|
||||
<script src="/bg/js/anki.js"></script>
|
||||
<script src="/bg/js/anki-note-builder.js"></script>
|
||||
<script src="/bg/js/backend.js"></script>
|
||||
<script src="/bg/js/mecab.js"></script>
|
||||
<script src="/bg/js/audio-uri-builder.js"></script>
|
||||
@ -36,7 +35,6 @@
|
||||
<script src="/bg/js/options.js"></script>
|
||||
<script src="/bg/js/profile-conditions.js"></script>
|
||||
<script src="/bg/js/request-builder.js"></script>
|
||||
<script src="/bg/js/template-renderer.js"></script>
|
||||
<script src="/bg/js/simple-dom-parser.js"></script>
|
||||
<script src="/bg/js/text-source-map.js"></script>
|
||||
<script src="/bg/js/translator.js"></script>
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* AnkiConnect
|
||||
* AnkiNoteBuilder
|
||||
* AudioSystem
|
||||
* AudioUriBuilder
|
||||
* ClipboardMonitor
|
||||
@ -29,7 +28,6 @@
|
||||
* OptionsUtil
|
||||
* ProfileConditions
|
||||
* RequestBuilder
|
||||
* TemplateRenderer
|
||||
* Translator
|
||||
* jp
|
||||
*/
|
||||
@ -57,10 +55,6 @@ class Backend {
|
||||
requestBuilder: this._requestBuilder,
|
||||
useCache: false
|
||||
});
|
||||
this._ankiNoteBuilder = new AnkiNoteBuilder({
|
||||
renderTemplate: this._renderTemplate.bind(this)
|
||||
});
|
||||
this._templateRenderer = new TemplateRenderer();
|
||||
|
||||
this._clipboardPasteTarget = null;
|
||||
this._clipboardPasteTargetInitialized = false;
|
||||
@ -94,10 +88,7 @@ class Backend {
|
||||
['addAnkiNote', {async: true, contentScript: true, handler: this._onApiAddAnkiNote.bind(this)}],
|
||||
['getAnkiNoteInfo', {async: true, contentScript: true, handler: this._onApiGetAnkiNoteInfo.bind(this)}],
|
||||
['injectAnkiNoteMedia', {async: true, contentScript: true, handler: this._onApiInjectAnkiNoteMedia.bind(this)}],
|
||||
['definitionAdd', {async: true, contentScript: true, handler: this._onApiDefinitionAdd.bind(this)}],
|
||||
['definitionsAddable', {async: true, contentScript: true, handler: this._onApiDefinitionsAddable.bind(this)}],
|
||||
['noteView', {async: true, contentScript: true, handler: this._onApiNoteView.bind(this)}],
|
||||
['templateRender', {async: true, contentScript: true, handler: this._onApiTemplateRender.bind(this)}],
|
||||
['commandExec', {async: false, contentScript: true, handler: this._onApiCommandExec.bind(this)}],
|
||||
['audioGetUri', {async: true, contentScript: true, handler: this._onApiAudioGetUri.bind(this)}],
|
||||
['screenshotGet', {async: true, contentScript: true, handler: this._onApiScreenshotGet.bind(this)}],
|
||||
@ -473,7 +464,11 @@ class Backend {
|
||||
return results;
|
||||
}
|
||||
|
||||
async _onApiInjectAnkiNoteMedia({expression, reading, timestamp, audioDetails, screenshotDetails, clipboardImage}) {
|
||||
async _onApiInjectAnkiNoteMedia({expression, reading, timestamp, audioDetails, screenshotDetails, clipboardImage}, sender) {
|
||||
if (isObject(screenshotDetails)) {
|
||||
const {id: tabId, windowId} = (sender && sender.tab ? sender.tab : {});
|
||||
screenshotDetails = Object.assign({}, screenshotDetails, {tabId, windowId});
|
||||
}
|
||||
return await this._injectAnkNoteMedia(
|
||||
this._anki,
|
||||
expression,
|
||||
@ -485,45 +480,10 @@ class Backend {
|
||||
);
|
||||
}
|
||||
|
||||
async _onApiDefinitionAdd({definition, mode, context, ownerFrameId, optionsContext}, sender) {
|
||||
const options = this.getOptions(optionsContext);
|
||||
const templates = this._getTemplates(options);
|
||||
const {id: tabId, windowId} = (sender && sender.tab ? sender.tab : {});
|
||||
const note = await this._createNote(definition, mode, context, options, templates, true, {windowId, tabId, ownerFrameId});
|
||||
return await this._onApiAddAnkiNote({note});
|
||||
}
|
||||
|
||||
async _onApiDefinitionsAddable({definitions, modes, context, optionsContext}) {
|
||||
const options = this.getOptions(optionsContext);
|
||||
const templates = 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, null);
|
||||
notePromises.push(notePromise);
|
||||
}
|
||||
}
|
||||
const notes = await Promise.all(notePromises);
|
||||
|
||||
const infos = await this._onApiGetAnkiNoteInfo({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 _onApiNoteView({noteId}) {
|
||||
return await this._anki.guiBrowseNote(noteId);
|
||||
}
|
||||
|
||||
async _onApiTemplateRender({template, data, marker}) {
|
||||
return this._renderTemplate(template, data, marker);
|
||||
}
|
||||
|
||||
_onApiCommandExec({command, params}) {
|
||||
return this._runCommand(command, params);
|
||||
}
|
||||
@ -1381,10 +1341,6 @@ class Backend {
|
||||
return false;
|
||||
}
|
||||
|
||||
async _renderTemplate(template, data, marker) {
|
||||
return await this._templateRenderer.render(template, data, marker);
|
||||
}
|
||||
|
||||
_getTemplates(options) {
|
||||
const templates = options.anki.fieldTemplates;
|
||||
return typeof templates === 'string' ? templates : this._defaultAnkiFieldTemplates;
|
||||
@ -1595,52 +1551,6 @@ class Backend {
|
||||
});
|
||||
}
|
||||
|
||||
async _createNote(definition, mode, context, options, templates, injectMedia, screenshotTarget) {
|
||||
const {
|
||||
general: {resultOutputMode, compactGlossaries},
|
||||
anki: {tags, duplicateScope, kanji, terms, screenshot: {format, quality}},
|
||||
audio: {sources, customSourceUrl}
|
||||
} = options;
|
||||
const modeOptions = (mode === 'kanji') ? kanji : terms;
|
||||
const {windowId, tabId, ownerFrameId} = (isObject(screenshotTarget) ? screenshotTarget : {});
|
||||
|
||||
if (injectMedia) {
|
||||
const fields = modeOptions.fields;
|
||||
const timestamp = Date.now();
|
||||
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') ? {windowId, tabId, ownerFrameId, format, quality} : null);
|
||||
const clipboardImage = (this._ankiNoteBuilder.containsMarker(fields, 'clipboard-image'));
|
||||
const {screenshotFileName, clipboardImageFileName, audioFileName} = await this._onApiInjectAnkiNoteMedia({
|
||||
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,
|
||||
audioDetails: {sources, customSourceUrl},
|
||||
screenshotDetails: {windowId, tabId, ownerFrameId, format, quality},
|
||||
clipboardImage: true
|
||||
});
|
||||
}
|
||||
|
||||
async _getScreenshot(windowId, tabId, ownerFrameId, format, quality) {
|
||||
if (typeof windowId !== 'number') {
|
||||
throw new Error('Invalid window ID');
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
/* global
|
||||
* AnkiNoteBuilder
|
||||
* TemplateRenderer
|
||||
* api
|
||||
*/
|
||||
|
||||
@ -27,6 +28,7 @@ class AnkiTemplatesController {
|
||||
this._cachedDefinitionValue = null;
|
||||
this._cachedDefinitionText = null;
|
||||
this._defaultFieldTemplates = null;
|
||||
this._templateRenderer = new TemplateRenderer();
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
@ -144,7 +146,7 @@ class AnkiTemplatesController {
|
||||
let templates = options.anki.fieldTemplates;
|
||||
if (typeof templates !== 'string') { templates = this._defaultFieldTemplates; }
|
||||
const ankiNoteBuilder = new AnkiNoteBuilder({
|
||||
renderTemplate: api.templateRender.bind(api)
|
||||
renderTemplate: this._renderTemplate.bind(this)
|
||||
});
|
||||
const {general: {resultOutputMode, compactGlossaries}} = options;
|
||||
const note = await ankiNoteBuilder.createNote({
|
||||
@ -176,4 +178,8 @@ class AnkiTemplatesController {
|
||||
input.classList.toggle('is-invalid', hasException);
|
||||
}
|
||||
}
|
||||
|
||||
async _renderTemplate(template, data, marker) {
|
||||
return await this._templateRenderer.render(template, data, marker);
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,9 @@
|
||||
<script src="/mixed/js/template-handler.js"></script>
|
||||
<script src="/mixed/js/text-to-speech-audio.js"></script>
|
||||
|
||||
<script src="/bg/js/anki-note-builder.js"></script>
|
||||
<script src="/bg/js/template-renderer.js"></script>
|
||||
|
||||
<script src="/bg/js/query-parser-generator.js"></script>
|
||||
<script src="/bg/js/query-parser.js"></script>
|
||||
<script src="/bg/js/clipboard-monitor.js"></script>
|
||||
|
@ -1183,6 +1183,7 @@
|
||||
<script src="/bg/js/dictionary-importer.js"></script>
|
||||
<script src="/bg/js/json-schema.js"></script>
|
||||
<script src="/bg/js/media-utility.js"></script>
|
||||
<script src="/bg/js/template-renderer.js"></script>
|
||||
|
||||
<script src="/bg/js/settings/keyboard-mouse-input-field.js"></script>
|
||||
<script src="/bg/js/settings/profile-conditions-ui.js"></script>
|
||||
|
@ -44,6 +44,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/mixed/lib/handlebars.min.js"></script>
|
||||
|
||||
<script src="/mixed/js/core.js"></script>
|
||||
<script src="/mixed/js/yomichan.js"></script>
|
||||
<script src="/mixed/js/comm.js"></script>
|
||||
@ -66,6 +68,9 @@
|
||||
<script src="/mixed/js/template-handler.js"></script>
|
||||
<script src="/mixed/js/text-to-speech-audio.js"></script>
|
||||
|
||||
<script src="/bg/js/anki-note-builder.js"></script>
|
||||
<script src="/bg/js/template-renderer.js"></script>
|
||||
|
||||
<script src="/bg/js/query-parser-generator.js"></script>
|
||||
<script src="/bg/js/query-parser.js"></script>
|
||||
<script src="/fg/js/float.js"></script>
|
||||
|
@ -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});
|
||||
}
|
||||
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user