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
|
/* 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);
|
|
||||||
}
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user