Anki settings controllers (#567)

* Convert anki-templates.js to a class

* Convert anki.js to a class
This commit is contained in:
toasted-nutbread 2020-05-29 19:52:51 -04:00 committed by GitHub
parent fde0072118
commit 5f9889fd26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 384 additions and 378 deletions

View File

@ -17,141 +17,147 @@
/* global /* global
* AnkiNoteBuilder * AnkiNoteBuilder
* ankiGetFieldMarkers
* ankiGetFieldMarkersHtml
* api * api
* getOptionsContext * getOptionsContext
* getOptionsMutable * getOptionsMutable
* settingsSaveOptions * settingsSaveOptions
*/ */
function onAnkiFieldTemplatesReset(e) { class AnkiTemplatesController {
e.preventDefault(); constructor(ankiController) {
$('#field-template-reset-modal').modal('show'); this._ankiController = ankiController;
} this._cachedDefinitionValue = null;
this._cachedDefinitionText = null;
async function onAnkiFieldTemplatesResetConfirm(e) {
e.preventDefault();
$('#field-template-reset-modal').modal('hide');
const value = await api.getDefaultAnkiFieldTemplates();
const element = document.querySelector('#field-templates');
element.value = value;
element.dispatchEvent(new Event('change'));
}
function ankiTemplatesInitialize() {
const markers = new Set(ankiGetFieldMarkers('terms').concat(ankiGetFieldMarkers('kanji')));
const fragment = ankiGetFieldMarkersHtml(markers);
const list = document.querySelector('#field-templates-list');
list.appendChild(fragment);
for (const node of list.querySelectorAll('.marker-link')) {
node.addEventListener('click', onAnkiTemplateMarkerClicked, false);
} }
$('#field-templates').on('change', onAnkiFieldTemplatesChanged); prepare() {
$('#field-template-render').on('click', onAnkiTemplateRender); const markers = new Set([
$('#field-templates-reset').on('click', onAnkiFieldTemplatesReset); ...this._ankiController.getFieldMarkers('terms'),
$('#field-templates-reset-confirm').on('click', onAnkiFieldTemplatesResetConfirm); ...this._ankiController.getFieldMarkers('kanji')
]);
const fragment = this._ankiController.getFieldMarkersHtml(markers);
ankiTemplatesUpdateValue(); const list = document.querySelector('#field-templates-list');
} list.appendChild(fragment);
for (const node of list.querySelectorAll('.marker-link')) {
node.addEventListener('click', this._onMarkerClicked.bind(this), false);
}
async function ankiTemplatesUpdateValue() { $('#field-templates').on('change', this._onChanged.bind(this));
const optionsContext = getOptionsContext(); $('#field-template-render').on('click', this._onRender.bind(this));
const options = await api.optionsGet(optionsContext); $('#field-templates-reset').on('click', this._onReset.bind(this));
let templates = options.anki.fieldTemplates; $('#field-templates-reset-confirm').on('click', this._onResetConfirm.bind(this));
if (typeof templates !== 'string') { templates = await api.getDefaultAnkiFieldTemplates(); }
$('#field-templates').val(templates);
onAnkiTemplatesValidateCompile(); this.updateValue();
} }
const ankiTemplatesValidateGetDefinition = (() => { async updateValue() {
let cachedValue = null; const optionsContext = getOptionsContext();
let cachedText = null; const options = await api.optionsGet(optionsContext);
let templates = options.anki.fieldTemplates;
if (typeof templates !== 'string') { templates = await api.getDefaultAnkiFieldTemplates(); }
$('#field-templates').val(templates);
return async (text, optionsContext) => { this._onValidateCompile();
if (cachedText !== text) { }
// Private
_onReset(e) {
e.preventDefault();
$('#field-template-reset-modal').modal('show');
}
async _onResetConfirm(e) {
e.preventDefault();
$('#field-template-reset-modal').modal('hide');
const value = await api.getDefaultAnkiFieldTemplates();
const element = document.querySelector('#field-templates');
element.value = value;
element.dispatchEvent(new Event('change'));
}
async _onChanged(e) {
// Get value
let templates = e.currentTarget.value;
if (templates === await api.getDefaultAnkiFieldTemplates()) {
// Default
templates = null;
}
// Overwrite
const optionsContext = getOptionsContext();
const options = await getOptionsMutable(optionsContext);
options.anki.fieldTemplates = templates;
await settingsSaveOptions();
// Compile
this._onValidateCompile();
}
_onValidateCompile() {
const infoNode = document.querySelector('#field-template-compile-result');
this._validate(infoNode, '{expression}', 'term-kanji', false, true);
}
_onMarkerClicked(e) {
e.preventDefault();
document.querySelector('#field-template-render-text').value = `{${e.target.textContent}}`;
}
_onRender(e) {
e.preventDefault();
const field = document.querySelector('#field-template-render-text').value;
const infoNode = document.querySelector('#field-template-render-result');
infoNode.hidden = true;
this._validate(infoNode, field, 'term-kanji', true, false);
}
async _getDefinition(text, optionsContext) {
if (this._cachedDefinitionText !== text) {
const {definitions} = await api.termsFind(text, {}, optionsContext); const {definitions} = await api.termsFind(text, {}, optionsContext);
if (definitions.length === 0) { return null; } if (definitions.length === 0) { return null; }
cachedValue = definitions[0]; this._cachedDefinitionValue = definitions[0];
cachedText = text; this._cachedDefinitionText = text;
} }
return cachedValue; return this._cachedDefinitionValue;
}; }
})();
async function ankiTemplatesValidate(infoNode, field, mode, showSuccessResult, invalidateInput) { async _validate(infoNode, field, mode, showSuccessResult, invalidateInput) {
const text = document.querySelector('#field-templates-preview-text').value || ''; const text = document.querySelector('#field-templates-preview-text').value || '';
const exceptions = []; const exceptions = [];
let result = `No definition found for ${text}`; let result = `No definition found for ${text}`;
try { try {
const optionsContext = getOptionsContext(); const optionsContext = getOptionsContext();
const definition = await ankiTemplatesValidateGetDefinition(text, optionsContext); const definition = await this._getDefinition(text, optionsContext);
if (definition !== null) { if (definition !== null) {
const options = await api.optionsGet(optionsContext); const options = await api.optionsGet(optionsContext);
const context = { const context = {
document: { document: {
title: document.title title: document.title
} }
}; };
let templates = options.anki.fieldTemplates; let templates = options.anki.fieldTemplates;
if (typeof templates !== 'string') { templates = await api.getDefaultAnkiFieldTemplates(); } if (typeof templates !== 'string') { templates = await api.getDefaultAnkiFieldTemplates(); }
const ankiNoteBuilder = new AnkiNoteBuilder({renderTemplate: api.templateRender.bind(api)}); const ankiNoteBuilder = new AnkiNoteBuilder({renderTemplate: api.templateRender.bind(api)});
result = await ankiNoteBuilder.formatField(field, definition, mode, context, options, templates, exceptions); result = await ankiNoteBuilder.formatField(field, definition, mode, context, options, templates, exceptions);
}
} catch (e) {
exceptions.push(e);
} }
} catch (e) {
exceptions.push(e);
}
const hasException = exceptions.length > 0; const hasException = exceptions.length > 0;
infoNode.hidden = !(showSuccessResult || hasException); infoNode.hidden = !(showSuccessResult || hasException);
infoNode.textContent = hasException ? exceptions.map((e) => `${e}`).join('\n') : (showSuccessResult ? result : ''); infoNode.textContent = hasException ? exceptions.map((e) => `${e}`).join('\n') : (showSuccessResult ? result : '');
infoNode.classList.toggle('text-danger', hasException); infoNode.classList.toggle('text-danger', hasException);
if (invalidateInput) { if (invalidateInput) {
const input = document.querySelector('#field-templates'); const input = document.querySelector('#field-templates');
input.classList.toggle('is-invalid', hasException); input.classList.toggle('is-invalid', hasException);
}
} }
} }
async function onAnkiFieldTemplatesChanged(e) {
// Get value
let templates = e.currentTarget.value;
if (templates === await api.getDefaultAnkiFieldTemplates()) {
// Default
templates = null;
}
// Overwrite
const optionsContext = getOptionsContext();
const options = await getOptionsMutable(optionsContext);
options.anki.fieldTemplates = templates;
await settingsSaveOptions();
// Compile
onAnkiTemplatesValidateCompile();
}
function onAnkiTemplatesValidateCompile() {
const infoNode = document.querySelector('#field-template-compile-result');
ankiTemplatesValidate(infoNode, '{expression}', 'term-kanji', false, true);
}
function onAnkiTemplateMarkerClicked(e) {
e.preventDefault();
document.querySelector('#field-template-render-text').value = `{${e.target.textContent}}`;
}
function onAnkiTemplateRender(e) {
e.preventDefault();
const field = document.querySelector('#field-template-render-text').value;
const infoNode = document.querySelector('#field-template-render-result');
infoNode.hidden = true;
ankiTemplatesValidate(infoNode, field, 'term-kanji', true, false);
}

View File

@ -23,287 +23,281 @@
* utilBackgroundIsolate * utilBackgroundIsolate
*/ */
// Private class AnkiController {
prepare() {
$('#anki-fields-container input,#anki-fields-container select,#anki-fields-container textarea').change(this._onFieldsChanged.bind(this));
function _ankiSpinnerShow(show) { for (const node of document.querySelectorAll('#anki-terms-model,#anki-kanji-model')) {
const spinner = $('#anki-spinner'); node.addEventListener('change', this._onModelChanged.bind(this), false);
if (show) {
spinner.show();
} else {
spinner.hide();
}
}
function _ankiSetError(error) {
const node = document.querySelector('#anki-error');
const node2 = document.querySelector('#anki-invalid-response-error');
if (error) {
const errorString = `${error}`;
if (node !== null) {
node.hidden = false;
node.textContent = errorString;
_ankiSetErrorData(node, error);
} }
if (node2 !== null) { this.optionsChanged();
node2.hidden = (errorString.indexOf('Invalid response') < 0); }
}
} else { async optionsChanged(options=null) {
if (node !== null) { if (options === null) {
node.hidden = true; const optionsContext = getOptionsContext();
node.textContent = ''; options = await getOptionsMutable(optionsContext);
} }
if (node2 !== null) { if (!options.anki.enable) {
node2.hidden = true; return;
}
await this._deckAndModelPopulate(options);
await Promise.all([
this._fieldsPopulate('terms', options),
this._fieldsPopulate('kanji', options)
]);
}
getFieldMarkers(type) {
switch (type) {
case 'terms':
return [
'audio',
'cloze-body',
'cloze-prefix',
'cloze-suffix',
'dictionary',
'document-title',
'expression',
'furigana',
'furigana-plain',
'glossary',
'glossary-brief',
'reading',
'screenshot',
'sentence',
'tags',
'url'
];
case 'kanji':
return [
'character',
'dictionary',
'document-title',
'glossary',
'kunyomi',
'onyomi',
'screenshot',
'sentence',
'tags',
'url'
];
default:
return [];
} }
} }
}
function _ankiSetErrorData(node, error) { getFieldMarkersHtml(markers) {
const data = error.data; const template = document.querySelector('#anki-field-marker-template').content;
let message = ''; const fragment = document.createDocumentFragment();
if (typeof data !== 'undefined') { for (const marker of markers) {
message += `${JSON.stringify(data, null, 4)}\n\n`; const markerNode = document.importNode(template, true).firstChild;
} markerNode.querySelector('.marker-link').textContent = marker;
message += `${error.stack}`.trimRight(); fragment.appendChild(markerNode);
}
const button = document.createElement('a'); return fragment;
button.className = 'error-data-show-button';
const content = document.createElement('div');
content.className = 'error-data-container';
content.textContent = message;
content.hidden = true;
button.addEventListener('click', () => content.hidden = !content.hidden, false);
node.appendChild(button);
node.appendChild(content);
}
function _ankiSetDropdownOptions(dropdown, optionValues) {
const fragment = document.createDocumentFragment();
for (const optionValue of optionValues) {
const option = document.createElement('option');
option.value = optionValue;
option.textContent = optionValue;
fragment.appendChild(option);
}
dropdown.textContent = '';
dropdown.appendChild(fragment);
}
async function _ankiDeckAndModelPopulate(options) {
const termsDeck = {value: options.anki.terms.deck, selector: '#anki-terms-deck'};
const kanjiDeck = {value: options.anki.kanji.deck, selector: '#anki-kanji-deck'};
const termsModel = {value: options.anki.terms.model, selector: '#anki-terms-model'};
const kanjiModel = {value: options.anki.kanji.model, selector: '#anki-kanji-model'};
try {
_ankiSpinnerShow(true);
const [deckNames, modelNames] = await Promise.all([api.getAnkiDeckNames(), api.getAnkiModelNames()]);
deckNames.sort();
modelNames.sort();
termsDeck.values = deckNames;
kanjiDeck.values = deckNames;
termsModel.values = modelNames;
kanjiModel.values = modelNames;
_ankiSetError(null);
} catch (error) {
_ankiSetError(error);
} finally {
_ankiSpinnerShow(false);
} }
for (const {value, values, selector} of [termsDeck, kanjiDeck, termsModel, kanjiModel]) { // Private
const node = document.querySelector(selector);
_ankiSetDropdownOptions(node, Array.isArray(values) ? values : [value]);
node.value = value;
}
}
function _ankiCreateFieldTemplate(name, value, markers) { _fieldsToDict(elements) {
const template = document.querySelector('#anki-field-template').content; const result = {};
const content = document.importNode(template, true).firstChild; for (const element of elements) {
result[element.dataset.field] = element.value;
content.querySelector('.anki-field-name').textContent = name; }
return result;
const field = content.querySelector('.anki-field-value');
field.dataset.field = name;
field.value = value;
content.querySelector('.anki-field-marker-list').appendChild(ankiGetFieldMarkersHtml(markers));
return content;
}
async function _ankiFieldsPopulate(tabId, options) {
const tab = document.querySelector(`.tab-pane[data-anki-card-type=${tabId}]`);
const container = tab.querySelector('tbody');
const markers = ankiGetFieldMarkers(tabId);
const fragment = document.createDocumentFragment();
const fields = options.anki[tabId].fields;
for (const name of Object.keys(fields)) {
const value = fields[name];
const html = _ankiCreateFieldTemplate(name, value, markers);
fragment.appendChild(html);
} }
container.textContent = ''; _spinnerShow(show) {
container.appendChild(fragment); const spinner = $('#anki-spinner');
if (show) {
for (const node of container.querySelectorAll('.anki-field-value')) { spinner.show();
node.addEventListener('change', _onAnkiFieldsChanged, false); } else {
} spinner.hide();
for (const node of container.querySelectorAll('.marker-link')) { }
node.addEventListener('click', _onAnkiMarkerClicked, false);
}
}
function _onAnkiMarkerClicked(e) {
e.preventDefault();
const link = e.currentTarget;
const input = $(link).closest('.input-group').find('.anki-field-value')[0];
input.value = `{${link.textContent}}`;
input.dispatchEvent(new Event('change'));
}
async function _onAnkiModelChanged(e) {
const node = e.currentTarget;
let fieldNames;
try {
const modelName = node.value;
fieldNames = await api.getAnkiModelFieldNames(modelName);
_ankiSetError(null);
} catch (error) {
_ankiSetError(error);
return;
} finally {
_ankiSpinnerShow(false);
} }
const tabId = node.dataset.ankiCardType; _setError(error) {
if (tabId !== 'terms' && tabId !== 'kanji') { return; } const node = document.querySelector('#anki-error');
const node2 = document.querySelector('#anki-invalid-response-error');
if (error) {
const errorString = `${error}`;
if (node !== null) {
node.hidden = false;
node.textContent = errorString;
this._setErrorData(node, error);
}
const fields = {}; if (node2 !== null) {
for (const name of fieldNames) { node2.hidden = (errorString.indexOf('Invalid response') < 0);
fields[name] = ''; }
} else {
if (node !== null) {
node.hidden = true;
node.textContent = '';
}
if (node2 !== null) {
node2.hidden = true;
}
}
} }
const optionsContext = getOptionsContext(); _setErrorData(node, error) {
const options = await getOptionsMutable(optionsContext); const data = error.data;
options.anki[tabId].fields = utilBackgroundIsolate(fields); let message = '';
await settingsSaveOptions(); if (typeof data !== 'undefined') {
message += `${JSON.stringify(data, null, 4)}\n\n`;
}
message += `${error.stack}`.trimRight();
await _ankiFieldsPopulate(tabId, options); const button = document.createElement('a');
} button.className = 'error-data-show-button';
async function _onAnkiFieldsChanged() { const content = document.createElement('div');
const optionsContext = getOptionsContext(); content.className = 'error-data-container';
const options = await getOptionsMutable(optionsContext); content.textContent = message;
content.hidden = true;
options.anki.terms.deck = $('#anki-terms-deck').val(); button.addEventListener('click', () => content.hidden = !content.hidden, false);
options.anki.terms.model = $('#anki-terms-model').val();
options.anki.terms.fields = utilBackgroundIsolate(ankiFieldsToDict(document.querySelectorAll('#terms .anki-field-value')));
options.anki.kanji.deck = $('#anki-kanji-deck').val();
options.anki.kanji.model = $('#anki-kanji-model').val();
options.anki.kanji.fields = utilBackgroundIsolate(ankiFieldsToDict(document.querySelectorAll('#kanji .anki-field-value')));
await settingsSaveOptions(); node.appendChild(button);
node.appendChild(content);
await onAnkiOptionsChanged(options);
}
// Public
function ankiErrorShown() {
const node = document.querySelector('#anki-error');
return node && !node.hidden;
}
function ankiFieldsToDict(elements) {
const result = {};
for (const element of elements) {
result[element.dataset.field] = element.value;
}
return result;
}
function ankiGetFieldMarkersHtml(markers) {
const template = document.querySelector('#anki-field-marker-template').content;
const fragment = document.createDocumentFragment();
for (const marker of markers) {
const markerNode = document.importNode(template, true).firstChild;
markerNode.querySelector('.marker-link').textContent = marker;
fragment.appendChild(markerNode);
}
return fragment;
}
function ankiGetFieldMarkers(type) {
switch (type) {
case 'terms':
return [
'audio',
'cloze-body',
'cloze-prefix',
'cloze-suffix',
'dictionary',
'document-title',
'expression',
'furigana',
'furigana-plain',
'glossary',
'glossary-brief',
'reading',
'screenshot',
'sentence',
'tags',
'url'
];
case 'kanji':
return [
'character',
'dictionary',
'document-title',
'glossary',
'kunyomi',
'onyomi',
'screenshot',
'sentence',
'tags',
'url'
];
default:
return [];
}
}
function ankiInitialize() {
$('#anki-fields-container input,#anki-fields-container select,#anki-fields-container textarea').change(_onAnkiFieldsChanged);
for (const node of document.querySelectorAll('#anki-terms-model,#anki-kanji-model')) {
node.addEventListener('change', _onAnkiModelChanged, false);
} }
onAnkiOptionsChanged(); _setDropdownOptions(dropdown, optionValues) {
} const fragment = document.createDocumentFragment();
for (const optionValue of optionValues) {
const option = document.createElement('option');
option.value = optionValue;
option.textContent = optionValue;
fragment.appendChild(option);
}
dropdown.textContent = '';
dropdown.appendChild(fragment);
}
async _deckAndModelPopulate(options) {
const termsDeck = {value: options.anki.terms.deck, selector: '#anki-terms-deck'};
const kanjiDeck = {value: options.anki.kanji.deck, selector: '#anki-kanji-deck'};
const termsModel = {value: options.anki.terms.model, selector: '#anki-terms-model'};
const kanjiModel = {value: options.anki.kanji.model, selector: '#anki-kanji-model'};
try {
this._spinnerShow(true);
const [deckNames, modelNames] = await Promise.all([api.getAnkiDeckNames(), api.getAnkiModelNames()]);
deckNames.sort();
modelNames.sort();
termsDeck.values = deckNames;
kanjiDeck.values = deckNames;
termsModel.values = modelNames;
kanjiModel.values = modelNames;
this._setError(null);
} catch (error) {
this._setError(error);
} finally {
this._spinnerShow(false);
}
for (const {value, values, selector} of [termsDeck, kanjiDeck, termsModel, kanjiModel]) {
const node = document.querySelector(selector);
this._setDropdownOptions(node, Array.isArray(values) ? values : [value]);
node.value = value;
}
}
_createFieldTemplate(name, value, markers) {
const template = document.querySelector('#anki-field-template').content;
const content = document.importNode(template, true).firstChild;
content.querySelector('.anki-field-name').textContent = name;
const field = content.querySelector('.anki-field-value');
field.dataset.field = name;
field.value = value;
content.querySelector('.anki-field-marker-list').appendChild(this.getFieldMarkersHtml(markers));
return content;
}
async _fieldsPopulate(tabId, options) {
const tab = document.querySelector(`.tab-pane[data-anki-card-type=${tabId}]`);
const container = tab.querySelector('tbody');
const markers = this.getFieldMarkers(tabId);
const fragment = document.createDocumentFragment();
const fields = options.anki[tabId].fields;
for (const name of Object.keys(fields)) {
const value = fields[name];
const html = this._createFieldTemplate(name, value, markers);
fragment.appendChild(html);
}
container.textContent = '';
container.appendChild(fragment);
for (const node of container.querySelectorAll('.anki-field-value')) {
node.addEventListener('change', this._onFieldsChanged.bind(this), false);
}
for (const node of container.querySelectorAll('.marker-link')) {
node.addEventListener('click', this._onMarkerClicked.bind(this), false);
}
}
_onMarkerClicked(e) {
e.preventDefault();
const link = e.currentTarget;
const input = $(link).closest('.input-group').find('.anki-field-value')[0];
input.value = `{${link.textContent}}`;
input.dispatchEvent(new Event('change'));
}
async _onModelChanged(e) {
const node = e.currentTarget;
let fieldNames;
try {
const modelName = node.value;
fieldNames = await api.getAnkiModelFieldNames(modelName);
this._setError(null);
} catch (error) {
this._setError(error);
return;
} finally {
this._spinnerShow(false);
}
const tabId = node.dataset.ankiCardType;
if (tabId !== 'terms' && tabId !== 'kanji') { return; }
const fields = {};
for (const name of fieldNames) {
fields[name] = '';
}
async function onAnkiOptionsChanged(options=null) {
if (options === null) {
const optionsContext = getOptionsContext(); const optionsContext = getOptionsContext();
options = await getOptionsMutable(optionsContext); const options = await getOptionsMutable(optionsContext);
options.anki[tabId].fields = utilBackgroundIsolate(fields);
await settingsSaveOptions();
await this._fieldsPopulate(tabId, options);
} }
if (!options.anki.enable) { async _onFieldsChanged() {
return; const optionsContext = getOptionsContext();
} const options = await getOptionsMutable(optionsContext);
await _ankiDeckAndModelPopulate(options); options.anki.terms.deck = $('#anki-terms-deck').val();
await Promise.all([_ankiFieldsPopulate('terms', options), _ankiFieldsPopulate('kanji', options)]); options.anki.terms.model = $('#anki-terms-model').val();
options.anki.terms.fields = utilBackgroundIsolate(this._fieldsToDict(document.querySelectorAll('#terms .anki-field-value')));
options.anki.kanji.deck = $('#anki-kanji-deck').val();
options.anki.kanji.model = $('#anki-kanji-model').val();
options.anki.kanji.fields = utilBackgroundIsolate(this._fieldsToDict(document.querySelectorAll('#kanji .anki-field-value')));
await settingsSaveOptions();
await this.optionsChanged(options);
}
} }

View File

@ -16,17 +16,15 @@
*/ */
/* global /* global
* AnkiController
* AnkiTemplatesController
* ProfileController * ProfileController
* SettingsBackup * SettingsBackup
* SettingsController * SettingsController
* ankiInitialize
* ankiTemplatesInitialize
* ankiTemplatesUpdateValue
* api * api
* appearanceInitialize * appearanceInitialize
* audioSettingsInitialize * audioSettingsInitialize
* dictSettingsInitialize * dictSettingsInitialize
* onAnkiOptionsChanged
* onDictionaryOptionsChanged * onDictionaryOptionsChanged
* storageInfoInitialize * storageInfoInitialize
* utilBackend * utilBackend
@ -269,9 +267,13 @@ async function onOptionsUpdated({source}) {
const options = await getOptionsMutable(optionsContext); const options = await getOptionsMutable(optionsContext);
document.querySelector('#enable-clipboard-popups').checked = options.general.enableClipboardPopups; document.querySelector('#enable-clipboard-popups').checked = options.general.enableClipboardPopups;
ankiTemplatesUpdateValue(); if (ankiTemplatesController !== null) {
ankiTemplatesController.updateValue();
}
onDictionaryOptionsChanged(); onDictionaryOptionsChanged();
onAnkiOptionsChanged(); if (ankiController !== null) {
ankiController.optionsChanged();
}
await formWrite(options); await formWrite(options);
} }
@ -302,6 +304,8 @@ async function settingsPopulateModifierKeys() {
} }
} }
let ankiController = null;
let ankiTemplatesController = null;
async function onReady() { async function onReady() {
api.forwardLogsToBackend(); api.forwardLogsToBackend();
@ -318,8 +322,10 @@ async function onReady() {
await audioSettingsInitialize(); await audioSettingsInitialize();
await (new ProfileController()).prepare(); await (new ProfileController()).prepare();
await dictSettingsInitialize(); await dictSettingsInitialize();
ankiInitialize(); ankiController = new AnkiController();
ankiTemplatesInitialize(); ankiController.prepare();
ankiTemplatesController = new AnkiTemplatesController(ankiController);
ankiTemplatesController.prepare();
new SettingsBackup().prepare(); new SettingsBackup().prepare();
storageInfoInitialize(); storageInfoInitialize();