2019-12-01 22:01:41 +00:00
|
|
|
/*
|
2021-01-01 19:50:41 +00:00
|
|
|
* Copyright (C) 2019-2021 Yomichan Authors
|
2019-12-01 22:01:41 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2020-01-01 17:00:31 +00:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2019-12-01 22:01:41 +00:00
|
|
|
*/
|
|
|
|
|
2020-03-11 02:30:36 +00:00
|
|
|
/* global
|
|
|
|
* AnkiNoteBuilder
|
|
|
|
*/
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-05-29 23:52:51 +00:00
|
|
|
class AnkiTemplatesController {
|
2020-10-11 00:58:38 +00:00
|
|
|
constructor(settingsController, modalController, ankiController) {
|
2020-05-30 13:33:13 +00:00
|
|
|
this._settingsController = settingsController;
|
2020-10-11 00:58:38 +00:00
|
|
|
this._modalController = modalController;
|
2020-05-29 23:52:51 +00:00
|
|
|
this._ankiController = ankiController;
|
2021-04-04 20:22:35 +00:00
|
|
|
this._cachedDictionaryEntryValue = null;
|
|
|
|
this._cachedDictionaryEntryText = null;
|
2020-05-30 13:33:13 +00:00
|
|
|
this._defaultFieldTemplates = null;
|
2020-10-30 21:41:52 +00:00
|
|
|
this._fieldTemplatesTextarea = null;
|
|
|
|
this._compileResultInfo = null;
|
|
|
|
this._renderFieldInput = null;
|
|
|
|
this._renderResult = null;
|
2020-09-19 21:14:51 +00:00
|
|
|
this._fieldTemplateResetModal = null;
|
2021-02-25 02:54:58 +00:00
|
|
|
this._ankiNoteBuilder = new AnkiNoteBuilder();
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-05-30 13:33:13 +00:00
|
|
|
async prepare() {
|
2021-02-14 20:53:35 +00:00
|
|
|
this._defaultFieldTemplates = await yomichan.api.getDefaultAnkiFieldTemplates();
|
2020-05-30 13:33:13 +00:00
|
|
|
|
2020-10-30 21:41:52 +00:00
|
|
|
this._fieldTemplatesTextarea = document.querySelector('#anki-card-templates-textarea');
|
|
|
|
this._compileResultInfo = document.querySelector('#anki-card-templates-compile-result');
|
|
|
|
this._renderFieldInput = document.querySelector('#anki-card-templates-test-field-input');
|
|
|
|
this._renderTextInput = document.querySelector('#anki-card-templates-test-text-input');
|
|
|
|
this._renderResult = document.querySelector('#anki-card-templates-render-result');
|
|
|
|
const menuButton = document.querySelector('#anki-card-templates-test-field-menu-button');
|
|
|
|
const testRenderButton = document.querySelector('#anki-card-templates-test-render-button');
|
|
|
|
const resetButton = document.querySelector('#anki-card-templates-reset-button');
|
|
|
|
const resetConfirmButton = document.querySelector('#anki-card-templates-reset-button-confirm');
|
|
|
|
this._fieldTemplateResetModal = this._modalController.getModal('anki-card-templates-reset');
|
2020-09-19 21:14:51 +00:00
|
|
|
|
2020-10-30 21:41:52 +00:00
|
|
|
this._fieldTemplatesTextarea.addEventListener('change', this._onChanged.bind(this), false);
|
|
|
|
testRenderButton.addEventListener('click', this._onRender.bind(this), false);
|
|
|
|
resetButton.addEventListener('click', this._onReset.bind(this), false);
|
|
|
|
resetConfirmButton.addEventListener('click', this._onResetConfirm.bind(this), false);
|
|
|
|
if (menuButton !== null) {
|
2021-01-20 01:52:57 +00:00
|
|
|
menuButton.addEventListener('menuClose', this._onFieldMenuClose.bind(this), false);
|
2020-10-30 21:41:52 +00:00
|
|
|
}
|
2020-02-28 01:33:13 +00:00
|
|
|
|
2020-05-30 13:33:13 +00:00
|
|
|
this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this));
|
|
|
|
|
|
|
|
const options = await this._settingsController.getOptions();
|
|
|
|
this._onOptionsChanged({options});
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-05-30 13:33:13 +00:00
|
|
|
// Private
|
|
|
|
|
|
|
|
_onOptionsChanged({options}) {
|
2020-05-29 23:52:51 +00:00
|
|
|
let templates = options.anki.fieldTemplates;
|
2020-05-30 13:33:13 +00:00
|
|
|
if (typeof templates !== 'string') { templates = this._defaultFieldTemplates; }
|
2020-10-30 21:41:52 +00:00
|
|
|
this._fieldTemplatesTextarea.value = templates;
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-05-29 23:52:51 +00:00
|
|
|
this._onValidateCompile();
|
2019-12-01 22:01:41 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:52:51 +00:00
|
|
|
_onReset(e) {
|
|
|
|
e.preventDefault();
|
2020-09-19 21:14:51 +00:00
|
|
|
this._fieldTemplateResetModal.setVisible(true);
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
2019-12-15 05:02:52 +00:00
|
|
|
|
2020-05-30 20:22:05 +00:00
|
|
|
_onResetConfirm(e) {
|
2020-05-29 23:52:51 +00:00
|
|
|
e.preventDefault();
|
2019-12-15 05:02:52 +00:00
|
|
|
|
2020-09-19 21:14:51 +00:00
|
|
|
this._fieldTemplateResetModal.setVisible(false);
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-05-30 20:22:05 +00:00
|
|
|
const value = this._defaultFieldTemplates;
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-10-30 21:41:52 +00:00
|
|
|
this._fieldTemplatesTextarea.value = value;
|
|
|
|
this._fieldTemplatesTextarea.dispatchEvent(new Event('change'));
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-05-29 23:52:51 +00:00
|
|
|
async _onChanged(e) {
|
|
|
|
// Get value
|
|
|
|
let templates = e.currentTarget.value;
|
2020-05-30 20:22:05 +00:00
|
|
|
if (templates === this._defaultFieldTemplates) {
|
2020-05-29 23:52:51 +00:00
|
|
|
// Default
|
|
|
|
templates = null;
|
2019-12-01 22:01:41 +00:00
|
|
|
}
|
2020-05-29 23:52:51 +00:00
|
|
|
|
|
|
|
// Overwrite
|
2020-05-30 20:22:05 +00:00
|
|
|
await this._settingsController.setProfileSetting('anki.fieldTemplates', templates);
|
2020-05-29 23:52:51 +00:00
|
|
|
|
|
|
|
// Compile
|
|
|
|
this._onValidateCompile();
|
2019-12-01 22:01:41 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:52:51 +00:00
|
|
|
_onValidateCompile() {
|
2020-10-30 21:41:52 +00:00
|
|
|
this._validate(this._compileResultInfo, '{expression}', 'term-kanji', false, true);
|
2019-12-01 22:01:41 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:52:51 +00:00
|
|
|
_onRender(e) {
|
|
|
|
e.preventDefault();
|
2019-12-15 05:02:52 +00:00
|
|
|
|
2020-10-30 21:41:52 +00:00
|
|
|
const field = this._renderFieldInput.value;
|
|
|
|
const infoNode = this._renderResult;
|
2020-05-29 23:52:51 +00:00
|
|
|
infoNode.hidden = true;
|
2021-04-04 20:22:35 +00:00
|
|
|
this._cachedDictionaryEntryText = null;
|
2020-05-29 23:52:51 +00:00
|
|
|
this._validate(infoNode, field, 'term-kanji', true, false);
|
|
|
|
}
|
2019-12-15 05:02:52 +00:00
|
|
|
|
2021-01-20 01:52:57 +00:00
|
|
|
_onFieldMenuClose({currentTarget: button, detail: {action, item}}) {
|
2020-10-30 21:41:52 +00:00
|
|
|
switch (action) {
|
|
|
|
case 'setFieldMarker':
|
2021-01-20 01:52:57 +00:00
|
|
|
this._setFieldMarker(button, item.dataset.marker);
|
2020-10-30 21:41:52 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_setFieldMarker(element, marker) {
|
|
|
|
const input = this._renderFieldInput;
|
|
|
|
input.value = `{${marker}}`;
|
|
|
|
input.dispatchEvent(new Event('change'));
|
|
|
|
}
|
|
|
|
|
2021-04-04 20:22:35 +00:00
|
|
|
async _getDictionaryEntry(text, optionsContext) {
|
|
|
|
if (this._cachedDictionaryEntryText !== text) {
|
|
|
|
const {dictionaryEntries} = await yomichan.api.termsFind(text, {}, optionsContext);
|
|
|
|
if (dictionaryEntries.length === 0) { return null; }
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2021-04-04 20:22:35 +00:00
|
|
|
this._cachedDictionaryEntryValue = dictionaryEntries[0];
|
|
|
|
this._cachedDictionaryEntryText = text;
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
2021-04-04 20:22:35 +00:00
|
|
|
return {
|
|
|
|
dictionaryEntry: this._cachedDictionaryEntryValue,
|
|
|
|
text: this._cachedDictionaryEntryText
|
|
|
|
};
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2020-05-29 23:52:51 +00:00
|
|
|
async _validate(infoNode, field, mode, showSuccessResult, invalidateInput) {
|
2020-10-30 21:41:52 +00:00
|
|
|
const text = this._renderTextInput.value || '';
|
2021-01-30 17:33:29 +00:00
|
|
|
const errors = [];
|
2020-05-29 23:52:51 +00:00
|
|
|
let result = `No definition found for ${text}`;
|
|
|
|
try {
|
2020-05-30 13:33:13 +00:00
|
|
|
const optionsContext = this._settingsController.getOptionsContext();
|
2021-04-04 20:22:35 +00:00
|
|
|
const {dictionaryEntry, text: sentenceText} = await this._getDictionaryEntry(text, optionsContext);
|
|
|
|
if (dictionaryEntry !== null) {
|
2020-05-30 13:33:13 +00:00
|
|
|
const options = await this._settingsController.getOptions();
|
2020-05-29 23:52:51 +00:00
|
|
|
const context = {
|
2021-01-15 22:30:43 +00:00
|
|
|
url: window.location.href,
|
2021-04-04 20:22:35 +00:00
|
|
|
sentence: {
|
|
|
|
text: sentenceText,
|
|
|
|
offset: 0
|
|
|
|
},
|
2021-05-18 00:18:37 +00:00
|
|
|
documentTitle: document.title,
|
|
|
|
query: sentenceText,
|
|
|
|
fullQuery: sentenceText
|
2020-05-29 23:52:51 +00:00
|
|
|
};
|
2021-04-02 16:42:06 +00:00
|
|
|
let template = options.anki.fieldTemplates;
|
|
|
|
if (typeof template !== 'string') { template = this._defaultFieldTemplates; }
|
2020-11-26 04:22:05 +00:00
|
|
|
const {general: {resultOutputMode, glossaryLayoutMode, compactTags}} = options;
|
2021-01-11 00:28:50 +00:00
|
|
|
const note = await this._ankiNoteBuilder.createNote({
|
2021-04-04 20:22:35 +00:00
|
|
|
dictionaryEntry,
|
2020-09-09 15:54:40 +00:00
|
|
|
mode,
|
|
|
|
context,
|
2021-04-02 16:42:06 +00:00
|
|
|
template,
|
2021-01-04 00:40:12 +00:00
|
|
|
deckName: '',
|
|
|
|
modelName: '',
|
|
|
|
fields: [
|
|
|
|
['field', field]
|
|
|
|
],
|
2020-09-09 15:54:40 +00:00
|
|
|
resultOutputMode,
|
2020-11-26 04:22:05 +00:00
|
|
|
glossaryLayoutMode,
|
2020-11-13 01:34:11 +00:00
|
|
|
compactTags,
|
2021-01-30 17:33:29 +00:00
|
|
|
errors
|
2020-09-09 15:54:40 +00:00
|
|
|
});
|
|
|
|
result = note.fields.field;
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
2021-01-30 17:33:29 +00:00
|
|
|
errors.push(e);
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
2019-12-01 22:01:41 +00:00
|
|
|
|
2021-01-30 17:33:29 +00:00
|
|
|
const errorToMessageString = (e) => {
|
|
|
|
if (isObject(e)) {
|
|
|
|
let v = e.data;
|
|
|
|
if (isObject(v)) {
|
|
|
|
v = v.error;
|
|
|
|
if (isObject(v)) {
|
|
|
|
e = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v = e.message;
|
|
|
|
if (typeof v === 'string') { return v; }
|
|
|
|
}
|
|
|
|
return `${e}`;
|
|
|
|
};
|
|
|
|
|
|
|
|
const hasError = errors.length > 0;
|
|
|
|
infoNode.hidden = !(showSuccessResult || hasError);
|
|
|
|
infoNode.textContent = hasError ? errors.map(errorToMessageString).join('\n') : (showSuccessResult ? result : '');
|
|
|
|
infoNode.classList.toggle('text-danger', hasError);
|
2020-05-29 23:52:51 +00:00
|
|
|
if (invalidateInput) {
|
2021-01-30 17:33:29 +00:00
|
|
|
this._fieldTemplatesTextarea.dataset.invalid = `${hasError}`;
|
2020-05-29 23:52:51 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-01 22:01:41 +00:00
|
|
|
}
|