Refactor anki note building (#1223)
* Move TemplateRendererProxy creation into AnkiNoteBuilder * Simplify _stringReplaceAsync * Organize note generation * Rename API * Make the template rendering function more generic
This commit is contained in:
parent
25080ac82e
commit
4ed9493645
@ -17,12 +17,13 @@
|
|||||||
|
|
||||||
/* global
|
/* global
|
||||||
* DictionaryDataUtil
|
* DictionaryDataUtil
|
||||||
|
* TemplateRendererProxy
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AnkiNoteBuilder {
|
class AnkiNoteBuilder {
|
||||||
constructor({renderTemplate}) {
|
constructor(enabled) {
|
||||||
this._renderTemplate = renderTemplate;
|
|
||||||
this._markerPattern = /\{([\w-]+)\}/g;
|
this._markerPattern = /\{([\w-]+)\}/g;
|
||||||
|
this._templateRenderer = enabled ? new TemplateRendererProxy() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createNote({
|
async createNote({
|
||||||
@ -49,8 +50,22 @@ class AnkiNoteBuilder {
|
|||||||
duplicateScopeCheckChildren = true;
|
duplicateScopeCheckChildren = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = this._createNoteData(definition, mode, context, resultOutputMode, glossaryLayoutMode, compactTags);
|
||||||
|
const formattedFieldValuePromises = [];
|
||||||
|
for (const [, fieldValue] of fields) {
|
||||||
|
const formattedFieldValuePromise = this._formatField(fieldValue, data, templates, errors);
|
||||||
|
formattedFieldValuePromises.push(formattedFieldValuePromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
const formattedFieldValues = await Promise.all(formattedFieldValuePromises);
|
||||||
const noteFields = {};
|
const noteFields = {};
|
||||||
const note = {
|
for (let i = 0, ii = fields.length; i < ii; ++i) {
|
||||||
|
const fieldName = fields[i][0];
|
||||||
|
const formattedFieldValue = formattedFieldValues[i];
|
||||||
|
noteFields[fieldName] = formattedFieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
fields: noteFields,
|
fields: noteFields,
|
||||||
tags,
|
tags,
|
||||||
deckName,
|
deckName,
|
||||||
@ -64,22 +79,6 @@ class AnkiNoteBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = this._createNoteData(definition, mode, context, resultOutputMode, glossaryLayoutMode, compactTags);
|
|
||||||
const formattedFieldValuePromises = [];
|
|
||||||
for (const [, fieldValue] of fields) {
|
|
||||||
const formattedFieldValuePromise = this._formatField(fieldValue, data, templates, errors);
|
|
||||||
formattedFieldValuePromises.push(formattedFieldValuePromise);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formattedFieldValues = await Promise.all(formattedFieldValuePromises);
|
|
||||||
for (let i = 0, ii = fields.length; i < ii; ++i) {
|
|
||||||
const fieldName = fields[i][0];
|
|
||||||
const formattedFieldValue = formattedFieldValues[i];
|
|
||||||
noteFields[fieldName] = formattedFieldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return note;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
containsMarker(fields, marker) {
|
containsMarker(fields, marker) {
|
||||||
@ -146,7 +145,7 @@ class AnkiNoteBuilder {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_stringReplaceAsync(str, regex, replacer) {
|
async _stringReplaceAsync(str, regex, replacer) {
|
||||||
let match;
|
let match;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const parts = [];
|
const parts = [];
|
||||||
@ -155,9 +154,13 @@ class AnkiNoteBuilder {
|
|||||||
index = regex.lastIndex;
|
index = regex.lastIndex;
|
||||||
}
|
}
|
||||||
if (parts.length === 0) {
|
if (parts.length === 0) {
|
||||||
return Promise.resolve(str);
|
return str;
|
||||||
}
|
}
|
||||||
parts.push(str.substring(index));
|
parts.push(str.substring(index));
|
||||||
return Promise.all(parts).then((v) => v.join(''));
|
return (await Promise.all(parts)).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async _renderTemplate(template, data, marker) {
|
||||||
|
return await this._templateRenderer.render(template, {data, marker}, 'ankiNote');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,7 @@ class AnkiController {
|
|||||||
constructor(settingsController) {
|
constructor(settingsController) {
|
||||||
this._settingsController = settingsController;
|
this._settingsController = settingsController;
|
||||||
this._ankiConnect = new AnkiConnect();
|
this._ankiConnect = new AnkiConnect();
|
||||||
this._ankiNoteBuilder = new AnkiNoteBuilder({
|
this._ankiNoteBuilder = new AnkiNoteBuilder(false);
|
||||||
renderTemplate: null
|
|
||||||
});
|
|
||||||
this._selectorObserver = new SelectorObserver({
|
this._selectorObserver = new SelectorObserver({
|
||||||
selector: '.anki-card',
|
selector: '.anki-card',
|
||||||
ignoreSelector: null,
|
ignoreSelector: null,
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
/* global
|
/* global
|
||||||
* AnkiNoteBuilder
|
* AnkiNoteBuilder
|
||||||
* TemplateRendererProxy
|
|
||||||
* api
|
* api
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ class AnkiTemplatesController {
|
|||||||
this._renderFieldInput = null;
|
this._renderFieldInput = null;
|
||||||
this._renderResult = null;
|
this._renderResult = null;
|
||||||
this._fieldTemplateResetModal = null;
|
this._fieldTemplateResetModal = null;
|
||||||
this._templateRenderer = new TemplateRendererProxy();
|
this._ankiNoteBuilder = new AnkiNoteBuilder(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
@ -180,11 +179,8 @@ class AnkiTemplatesController {
|
|||||||
};
|
};
|
||||||
let templates = options.anki.fieldTemplates;
|
let templates = options.anki.fieldTemplates;
|
||||||
if (typeof templates !== 'string') { templates = this._defaultFieldTemplates; }
|
if (typeof templates !== 'string') { templates = this._defaultFieldTemplates; }
|
||||||
const ankiNoteBuilder = new AnkiNoteBuilder({
|
|
||||||
renderTemplate: this._renderTemplate.bind(this)
|
|
||||||
});
|
|
||||||
const {general: {resultOutputMode, glossaryLayoutMode, compactTags}} = options;
|
const {general: {resultOutputMode, glossaryLayoutMode, compactTags}} = options;
|
||||||
const note = await ankiNoteBuilder.createNote({
|
const note = await this._ankiNoteBuilder.createNote({
|
||||||
definition,
|
definition,
|
||||||
mode,
|
mode,
|
||||||
context,
|
context,
|
||||||
@ -213,8 +209,4 @@ class AnkiTemplatesController {
|
|||||||
this._fieldTemplatesTextarea.dataset.invalid = `${hasException}`;
|
this._fieldTemplatesTextarea.dataset.invalid = `${hasException}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _renderTemplate(template, data, marker) {
|
|
||||||
return await this._templateRenderer.render(template, data, marker);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class TemplateRendererFrameApi {
|
|||||||
constructor(templateRenderer) {
|
constructor(templateRenderer) {
|
||||||
this._templateRenderer = templateRenderer;
|
this._templateRenderer = templateRenderer;
|
||||||
this._windowMessageHandlers = new Map([
|
this._windowMessageHandlers = new Map([
|
||||||
['renderHandlebarsTemplate', {async: true, handler: this._onRenderHandlebarsTemplate.bind(this)}]
|
['render', {async: true, handler: this._onRender.bind(this)}]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +27,8 @@ class TemplateRendererFrameApi {
|
|||||||
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
_onWindowMessage(e) {
|
_onWindowMessage(e) {
|
||||||
const {source, data: {action, params, id}} = e;
|
const {source, data: {action, params, id}} = e;
|
||||||
const messageHandler = this._windowMessageHandlers.get(action);
|
const messageHandler = this._windowMessageHandlers.get(action);
|
||||||
@ -51,8 +53,8 @@ class TemplateRendererFrameApi {
|
|||||||
source.postMessage({action: `${action}.response`, params: response, id}, '*');
|
source.postMessage({action: `${action}.response`, params: response, id}, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onRenderHandlebarsTemplate({template, data, marker}) {
|
async _onRender({template, data, type}) {
|
||||||
return await this._templateRenderer.render(template, data, marker);
|
return await this._templateRenderer.render(template, data, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
_errorToJson(error) {
|
_errorToJson(error) {
|
||||||
|
@ -24,6 +24,12 @@
|
|||||||
(() => {
|
(() => {
|
||||||
const japaneseUtil = new JapaneseUtil(null);
|
const japaneseUtil = new JapaneseUtil(null);
|
||||||
const templateRenderer = new TemplateRenderer(japaneseUtil);
|
const templateRenderer = new TemplateRenderer(japaneseUtil);
|
||||||
|
templateRenderer.registerDataType('ankiNote', {
|
||||||
|
modifier: ({data, marker}) => {
|
||||||
|
data.marker = marker;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
const api = new TemplateRendererFrameApi(templateRenderer);
|
const api = new TemplateRendererFrameApi(templateRenderer);
|
||||||
api.prepare();
|
api.prepare();
|
||||||
})();
|
})();
|
||||||
|
@ -25,9 +25,9 @@ class TemplateRendererProxy {
|
|||||||
this._invocations = new Set();
|
this._invocations = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(template, data, marker) {
|
async render(template, data, type) {
|
||||||
await this._prepareFrame();
|
await this._prepareFrame();
|
||||||
return await this._invoke('renderHandlebarsTemplate', {template, data, marker});
|
return await this._invoke('render', {template, data, type});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private
|
// Private
|
||||||
|
@ -26,9 +26,14 @@ class TemplateRenderer {
|
|||||||
this._cacheMaxSize = 5;
|
this._cacheMaxSize = 5;
|
||||||
this._helpersRegistered = false;
|
this._helpersRegistered = false;
|
||||||
this._stateStack = null;
|
this._stateStack = null;
|
||||||
|
this._dataTypes = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(template, data, marker) {
|
registerDataType(name, {modifier=null, modifierPost=null}) {
|
||||||
|
this._dataTypes.set(name, {modifier, modifierPost});
|
||||||
|
}
|
||||||
|
|
||||||
|
async render(template, data, type) {
|
||||||
if (!this._helpersRegistered) {
|
if (!this._helpersRegistered) {
|
||||||
this._registerHelpers();
|
this._registerHelpers();
|
||||||
this._helpersRegistered = true;
|
this._helpersRegistered = true;
|
||||||
@ -42,18 +47,27 @@ class TemplateRenderer {
|
|||||||
cache.set(template, instance);
|
cache.set(template, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
const markerPre = data.marker;
|
let modifier = null;
|
||||||
const markerPreHas = Object.prototype.hasOwnProperty.call(data, 'marker');
|
let modifierPost = null;
|
||||||
|
if (typeof type === 'string') {
|
||||||
|
const typeInfo = this._dataTypes.get(type);
|
||||||
|
if (typeof typeInfo !== 'undefined') {
|
||||||
|
({modifier, modifierPost} = typeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (typeof modifier === 'function') {
|
||||||
|
data = modifier(data);
|
||||||
|
}
|
||||||
|
|
||||||
this._stateStack = [new Map()];
|
this._stateStack = [new Map()];
|
||||||
data.marker = marker;
|
|
||||||
return instance(data).trim();
|
return instance(data).trim();
|
||||||
} finally {
|
} finally {
|
||||||
this._stateStack = null;
|
this._stateStack = null;
|
||||||
if (markerPreHas) {
|
|
||||||
data.marker = markerPre;
|
if (typeof modifierPost === 'function') {
|
||||||
} else {
|
modifierPost(data);
|
||||||
delete data.marker;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
* MediaLoader
|
* MediaLoader
|
||||||
* PopupFactory
|
* PopupFactory
|
||||||
* QueryParser
|
* QueryParser
|
||||||
* TemplateRendererProxy
|
|
||||||
* TextScanner
|
* TextScanner
|
||||||
* WindowScroll
|
* WindowScroll
|
||||||
* api
|
* api
|
||||||
@ -89,10 +88,7 @@ class Display extends EventDispatcher {
|
|||||||
this._mode = null;
|
this._mode = null;
|
||||||
this._defaultAnkiFieldTemplates = null;
|
this._defaultAnkiFieldTemplates = null;
|
||||||
this._defaultAnkiFieldTemplatesPromise = null;
|
this._defaultAnkiFieldTemplatesPromise = null;
|
||||||
this._templateRenderer = new TemplateRendererProxy();
|
this._ankiNoteBuilder = new AnkiNoteBuilder(true);
|
||||||
this._ankiNoteBuilder = new AnkiNoteBuilder({
|
|
||||||
renderTemplate: this._renderTemplate.bind(this)
|
|
||||||
});
|
|
||||||
this._updateAdderButtonsPromise = Promise.resolve();
|
this._updateAdderButtonsPromise = Promise.resolve();
|
||||||
this._contentScrollElement = document.querySelector('#content-scroll');
|
this._contentScrollElement = document.querySelector('#content-scroll');
|
||||||
this._contentScrollBodyElement = document.querySelector('#content-body');
|
this._contentScrollBodyElement = document.querySelector('#content-body');
|
||||||
@ -1493,10 +1489,6 @@ class Display extends EventDispatcher {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _renderTemplate(template, data, marker) {
|
|
||||||
return await this._templateRenderer.render(template, data, marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _addDefinition(definition, mode, context) {
|
async _addDefinition(definition, mode, context) {
|
||||||
const options = this._options;
|
const options = this._options;
|
||||||
const templates = await this._getTemplates(options);
|
const templates = await this._getTemplates(options);
|
||||||
|
Loading…
Reference in New Issue
Block a user