Anki settings controllers (#567)
* Convert anki-templates.js to a class * Convert anki.js to a class
This commit is contained in:
parent
fde0072118
commit
5f9889fd26
@ -17,141 +17,147 @@
|
||||
|
||||
/* global
|
||||
* AnkiNoteBuilder
|
||||
* ankiGetFieldMarkers
|
||||
* ankiGetFieldMarkersHtml
|
||||
* api
|
||||
* getOptionsContext
|
||||
* getOptionsMutable
|
||||
* settingsSaveOptions
|
||||
*/
|
||||
|
||||
function onAnkiFieldTemplatesReset(e) {
|
||||
e.preventDefault();
|
||||
$('#field-template-reset-modal').modal('show');
|
||||
}
|
||||
|
||||
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);
|
||||
class AnkiTemplatesController {
|
||||
constructor(ankiController) {
|
||||
this._ankiController = ankiController;
|
||||
this._cachedDefinitionValue = null;
|
||||
this._cachedDefinitionText = null;
|
||||
}
|
||||
|
||||
$('#field-templates').on('change', onAnkiFieldTemplatesChanged);
|
||||
$('#field-template-render').on('click', onAnkiTemplateRender);
|
||||
$('#field-templates-reset').on('click', onAnkiFieldTemplatesReset);
|
||||
$('#field-templates-reset-confirm').on('click', onAnkiFieldTemplatesResetConfirm);
|
||||
prepare() {
|
||||
const markers = new Set([
|
||||
...this._ankiController.getFieldMarkers('terms'),
|
||||
...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() {
|
||||
const optionsContext = getOptionsContext();
|
||||
const options = await api.optionsGet(optionsContext);
|
||||
let templates = options.anki.fieldTemplates;
|
||||
if (typeof templates !== 'string') { templates = await api.getDefaultAnkiFieldTemplates(); }
|
||||
$('#field-templates').val(templates);
|
||||
$('#field-templates').on('change', this._onChanged.bind(this));
|
||||
$('#field-template-render').on('click', this._onRender.bind(this));
|
||||
$('#field-templates-reset').on('click', this._onReset.bind(this));
|
||||
$('#field-templates-reset-confirm').on('click', this._onResetConfirm.bind(this));
|
||||
|
||||
onAnkiTemplatesValidateCompile();
|
||||
}
|
||||
this.updateValue();
|
||||
}
|
||||
|
||||
const ankiTemplatesValidateGetDefinition = (() => {
|
||||
let cachedValue = null;
|
||||
let cachedText = null;
|
||||
async updateValue() {
|
||||
const optionsContext = getOptionsContext();
|
||||
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) => {
|
||||
if (cachedText !== text) {
|
||||
this._onValidateCompile();
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (definitions.length === 0) { return null; }
|
||||
|
||||
cachedValue = definitions[0];
|
||||
cachedText = text;
|
||||
this._cachedDefinitionValue = definitions[0];
|
||||
this._cachedDefinitionText = text;
|
||||
}
|
||||
return cachedValue;
|
||||
};
|
||||
})();
|
||||
return this._cachedDefinitionValue;
|
||||
}
|
||||
|
||||
async function ankiTemplatesValidate(infoNode, field, mode, showSuccessResult, invalidateInput) {
|
||||
const text = document.querySelector('#field-templates-preview-text').value || '';
|
||||
const exceptions = [];
|
||||
let result = `No definition found for ${text}`;
|
||||
try {
|
||||
const optionsContext = getOptionsContext();
|
||||
const definition = await ankiTemplatesValidateGetDefinition(text, optionsContext);
|
||||
if (definition !== null) {
|
||||
const options = await api.optionsGet(optionsContext);
|
||||
const context = {
|
||||
document: {
|
||||
title: document.title
|
||||
}
|
||||
};
|
||||
let templates = options.anki.fieldTemplates;
|
||||
if (typeof templates !== 'string') { templates = await api.getDefaultAnkiFieldTemplates(); }
|
||||
const ankiNoteBuilder = new AnkiNoteBuilder({renderTemplate: api.templateRender.bind(api)});
|
||||
result = await ankiNoteBuilder.formatField(field, definition, mode, context, options, templates, exceptions);
|
||||
async _validate(infoNode, field, mode, showSuccessResult, invalidateInput) {
|
||||
const text = document.querySelector('#field-templates-preview-text').value || '';
|
||||
const exceptions = [];
|
||||
let result = `No definition found for ${text}`;
|
||||
try {
|
||||
const optionsContext = getOptionsContext();
|
||||
const definition = await this._getDefinition(text, optionsContext);
|
||||
if (definition !== null) {
|
||||
const options = await api.optionsGet(optionsContext);
|
||||
const context = {
|
||||
document: {
|
||||
title: document.title
|
||||
}
|
||||
};
|
||||
let templates = options.anki.fieldTemplates;
|
||||
if (typeof templates !== 'string') { templates = await api.getDefaultAnkiFieldTemplates(); }
|
||||
const ankiNoteBuilder = new AnkiNoteBuilder({renderTemplate: api.templateRender.bind(api)});
|
||||
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;
|
||||
infoNode.hidden = !(showSuccessResult || hasException);
|
||||
infoNode.textContent = hasException ? exceptions.map((e) => `${e}`).join('\n') : (showSuccessResult ? result : '');
|
||||
infoNode.classList.toggle('text-danger', hasException);
|
||||
if (invalidateInput) {
|
||||
const input = document.querySelector('#field-templates');
|
||||
input.classList.toggle('is-invalid', hasException);
|
||||
const hasException = exceptions.length > 0;
|
||||
infoNode.hidden = !(showSuccessResult || hasException);
|
||||
infoNode.textContent = hasException ? exceptions.map((e) => `${e}`).join('\n') : (showSuccessResult ? result : '');
|
||||
infoNode.classList.toggle('text-danger', hasException);
|
||||
if (invalidateInput) {
|
||||
const input = document.querySelector('#field-templates');
|
||||
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);
|
||||
}
|
||||
|
@ -23,287 +23,281 @@
|
||||
* 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) {
|
||||
const spinner = $('#anki-spinner');
|
||||
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);
|
||||
for (const node of document.querySelectorAll('#anki-terms-model,#anki-kanji-model')) {
|
||||
node.addEventListener('change', this._onModelChanged.bind(this), false);
|
||||
}
|
||||
|
||||
if (node2 !== null) {
|
||||
node2.hidden = (errorString.indexOf('Invalid response') < 0);
|
||||
}
|
||||
} else {
|
||||
if (node !== null) {
|
||||
node.hidden = true;
|
||||
node.textContent = '';
|
||||
this.optionsChanged();
|
||||
}
|
||||
|
||||
async optionsChanged(options=null) {
|
||||
if (options === null) {
|
||||
const optionsContext = getOptionsContext();
|
||||
options = await getOptionsMutable(optionsContext);
|
||||
}
|
||||
|
||||
if (node2 !== null) {
|
||||
node2.hidden = true;
|
||||
if (!options.anki.enable) {
|
||||
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) {
|
||||
const data = error.data;
|
||||
let message = '';
|
||||
if (typeof data !== 'undefined') {
|
||||
message += `${JSON.stringify(data, null, 4)}\n\n`;
|
||||
}
|
||||
message += `${error.stack}`.trimRight();
|
||||
|
||||
const button = document.createElement('a');
|
||||
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);
|
||||
getFieldMarkersHtml(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;
|
||||
}
|
||||
|
||||
for (const {value, values, selector} of [termsDeck, kanjiDeck, termsModel, kanjiModel]) {
|
||||
const node = document.querySelector(selector);
|
||||
_ankiSetDropdownOptions(node, Array.isArray(values) ? values : [value]);
|
||||
node.value = value;
|
||||
}
|
||||
}
|
||||
// Private
|
||||
|
||||
function _ankiCreateFieldTemplate(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(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);
|
||||
_fieldsToDict(elements) {
|
||||
const result = {};
|
||||
for (const element of elements) {
|
||||
result[element.dataset.field] = element.value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
container.textContent = '';
|
||||
container.appendChild(fragment);
|
||||
|
||||
for (const node of container.querySelectorAll('.anki-field-value')) {
|
||||
node.addEventListener('change', _onAnkiFieldsChanged, false);
|
||||
}
|
||||
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);
|
||||
_spinnerShow(show) {
|
||||
const spinner = $('#anki-spinner');
|
||||
if (show) {
|
||||
spinner.show();
|
||||
} else {
|
||||
spinner.hide();
|
||||
}
|
||||
}
|
||||
|
||||
const tabId = node.dataset.ankiCardType;
|
||||
if (tabId !== 'terms' && tabId !== 'kanji') { return; }
|
||||
_setError(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;
|
||||
this._setErrorData(node, error);
|
||||
}
|
||||
|
||||
const fields = {};
|
||||
for (const name of fieldNames) {
|
||||
fields[name] = '';
|
||||
if (node2 !== null) {
|
||||
node2.hidden = (errorString.indexOf('Invalid response') < 0);
|
||||
}
|
||||
} else {
|
||||
if (node !== null) {
|
||||
node.hidden = true;
|
||||
node.textContent = '';
|
||||
}
|
||||
|
||||
if (node2 !== null) {
|
||||
node2.hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const optionsContext = getOptionsContext();
|
||||
const options = await getOptionsMutable(optionsContext);
|
||||
options.anki[tabId].fields = utilBackgroundIsolate(fields);
|
||||
await settingsSaveOptions();
|
||||
_setErrorData(node, error) {
|
||||
const data = error.data;
|
||||
let message = '';
|
||||
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 optionsContext = getOptionsContext();
|
||||
const options = await getOptionsMutable(optionsContext);
|
||||
const content = document.createElement('div');
|
||||
content.className = 'error-data-container';
|
||||
content.textContent = message;
|
||||
content.hidden = true;
|
||||
|
||||
options.anki.terms.deck = $('#anki-terms-deck').val();
|
||||
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')));
|
||||
button.addEventListener('click', () => content.hidden = !content.hidden, false);
|
||||
|
||||
await settingsSaveOptions();
|
||||
|
||||
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);
|
||||
node.appendChild(button);
|
||||
node.appendChild(content);
|
||||
}
|
||||
|
||||
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();
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
async _onFieldsChanged() {
|
||||
const optionsContext = getOptionsContext();
|
||||
const options = await getOptionsMutable(optionsContext);
|
||||
|
||||
await _ankiDeckAndModelPopulate(options);
|
||||
await Promise.all([_ankiFieldsPopulate('terms', options), _ankiFieldsPopulate('kanji', options)]);
|
||||
options.anki.terms.deck = $('#anki-terms-deck').val();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -16,17 +16,15 @@
|
||||
*/
|
||||
|
||||
/* global
|
||||
* AnkiController
|
||||
* AnkiTemplatesController
|
||||
* ProfileController
|
||||
* SettingsBackup
|
||||
* SettingsController
|
||||
* ankiInitialize
|
||||
* ankiTemplatesInitialize
|
||||
* ankiTemplatesUpdateValue
|
||||
* api
|
||||
* appearanceInitialize
|
||||
* audioSettingsInitialize
|
||||
* dictSettingsInitialize
|
||||
* onAnkiOptionsChanged
|
||||
* onDictionaryOptionsChanged
|
||||
* storageInfoInitialize
|
||||
* utilBackend
|
||||
@ -269,9 +267,13 @@ async function onOptionsUpdated({source}) {
|
||||
const options = await getOptionsMutable(optionsContext);
|
||||
|
||||
document.querySelector('#enable-clipboard-popups').checked = options.general.enableClipboardPopups;
|
||||
ankiTemplatesUpdateValue();
|
||||
if (ankiTemplatesController !== null) {
|
||||
ankiTemplatesController.updateValue();
|
||||
}
|
||||
onDictionaryOptionsChanged();
|
||||
onAnkiOptionsChanged();
|
||||
if (ankiController !== null) {
|
||||
ankiController.optionsChanged();
|
||||
}
|
||||
|
||||
await formWrite(options);
|
||||
}
|
||||
@ -302,6 +304,8 @@ async function settingsPopulateModifierKeys() {
|
||||
}
|
||||
}
|
||||
|
||||
let ankiController = null;
|
||||
let ankiTemplatesController = null;
|
||||
|
||||
async function onReady() {
|
||||
api.forwardLogsToBackend();
|
||||
@ -318,8 +322,10 @@ async function onReady() {
|
||||
await audioSettingsInitialize();
|
||||
await (new ProfileController()).prepare();
|
||||
await dictSettingsInitialize();
|
||||
ankiInitialize();
|
||||
ankiTemplatesInitialize();
|
||||
ankiController = new AnkiController();
|
||||
ankiController.prepare();
|
||||
ankiTemplatesController = new AnkiTemplatesController(ankiController);
|
||||
ankiTemplatesController.prepare();
|
||||
new SettingsBackup().prepare();
|
||||
|
||||
storageInfoInitialize();
|
||||
|
Loading…
Reference in New Issue
Block a user