From 184cc4cf28594f3bef9e141a8cbf9d7eb7a39e88 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 9 Nov 2019 16:34:39 -0500 Subject: [PATCH] Allow templates to be tested on the settings page --- ext/bg/css/settings.css | 18 ++++---- ext/bg/js/dictionary.js | 3 +- ext/bg/js/settings.js | 91 ++++++++++++++++++++++++++++++++++++++++- ext/bg/settings.html | 57 ++++++++++++++++++++++++-- 4 files changed, 156 insertions(+), 13 deletions(-) diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css index 35b4a152..5dfbd931 100644 --- a/ext/bg/css/settings.css +++ b/ext/bg/css/settings.css @@ -37,12 +37,6 @@ html:root:not([data-options-general-result-output-mode=merge]) #dict-main-group padding: 10px; } -#field-templates { - font-family: monospace; - overflow-x: hidden; - white-space: pre; -} - .bottom-links { padding-bottom: 1em; } @@ -136,14 +130,24 @@ html:root:not([data-options-general-result-output-mode=merge]) #dict-main-group } #custom-popup-css, -#custom-popup-outer-css { +#custom-popup-outer-css, +#field-templates { width: 100%; min-height: 34px; + line-height: 18px; height: 96px; resize: vertical; font-family: 'Courier New', Courier, monospace; white-space: pre; } +#field-templates { + height: 240px; + border-bottom-left-radius: 0; +} +#field-templates-reset { + border-top-left-radius: 0; + border-top-right-radius: 0; +} .btn-inner-middle { vertical-align: middle; diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js index e786c4e2..a4cf34ed 100644 --- a/ext/bg/js/dictionary.js +++ b/ext/bg/js/dictionary.js @@ -326,7 +326,7 @@ function dictFieldSplit(field) { return field.length === 0 ? [] : field.split(' '); } -async function dictFieldFormat(field, definition, mode, options) { +async function dictFieldFormat(field, definition, mode, options, exceptions) { const data = { marker: null, definition, @@ -347,6 +347,7 @@ async function dictFieldFormat(field, definition, mode, options) { try { return await apiTemplateRender(options.anki.fieldTemplates, data, true); } catch (e) { + if (exceptions) { exceptions.push(e); } return `{${marker}-render-error}`; } }); diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index daa997d4..9d95e358 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -134,6 +134,8 @@ async function formWrite(options) { $('#screenshot-quality').val(options.anki.screenshot.quality); $('#field-templates').val(options.anki.fieldTemplates); + onAnkiTemplatesValidateCompile(); + try { await ankiDeckAndModelPopulate(options); } catch (e) { @@ -144,7 +146,6 @@ async function formWrite(options) { } function formSetupEventListeners() { - $('#field-templates-reset').click(utilAsync(onAnkiFieldTemplatesReset)); $('input, select, textarea').not('.anki-model').not('.ignore-form-changes *').change(utilAsync(onFormOptionsChanged)); $('.anki-model').change(utilAsync(onAnkiModelChanged)); } @@ -202,6 +203,7 @@ async function onReady() { await audioSettingsInitialize(); await profileOptionsSetup(); await dictSettingsInitialize(); + ankiTemplatesInitialize(); storageInfoInitialize(); @@ -607,20 +609,105 @@ async function onAnkiModelChanged(e) { } } -async function onAnkiFieldTemplatesReset(e) { +function onAnkiFieldTemplatesReset(e) { + e.preventDefault(); + $('#field-template-reset-modal').modal('show'); +} + +async function onAnkiFieldTemplatesResetConfirm(e) { try { e.preventDefault(); + + $('#field-template-reset-modal').modal('hide'); + const optionsContext = getOptionsContext(); const options = await apiOptionsGet(optionsContext); const fieldTemplates = profileOptionsGetDefaultFieldTemplates(); options.anki.fieldTemplates = fieldTemplates; $('#field-templates').val(fieldTemplates); + onAnkiTemplatesValidateCompile(); await settingsSaveOptions(); } catch (e) { ankiErrorShow(e); } } +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', onAnkiTemplatesValidateCompile); + $('#field-template-render').on('click', onAnkiTemplateRender); + $('#field-templates-reset').on('click', onAnkiFieldTemplatesReset); + $('#field-templates-reset-confirm').on('click', onAnkiFieldTemplatesResetConfirm); +} + +const ankiTemplatesValidateGetDefinition = (() => { + let cachedValue = null; + let cachedText = null; + + return async (text, optionsContext) => { + if (cachedText !== text) { + const {definitions} = await apiTermsFind(text, optionsContext); + if (definitions.length === 0) { return null; } + + cachedValue = definitions[0]; + cachedText = text; + } + return cachedValue; + }; +})(); + +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 apiOptionsGet(optionsContext); + result = await dictFieldFormat(field, definition, mode, options, exceptions); + } + } 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); + } +} + +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); +} + /* * Storage diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 86d8935d..bdcc11d3 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -272,7 +272,7 @@
@@ -699,10 +699,60 @@

Fields are formatted using the Handlebars.js template rendering engine. Advanced users can modify these templates for ultimate control of what information gets included in - their Anki cards. If you encounter problems with your changes you can always reset to default - template settings. + their Anki cards. If you encounter problems with your changes, you can always reset to the default template settings.

+
+ +
+

+ + +

Templates can be tested using the inputs below.

+ +
+
+
+ + +
+
+ +
+
+ +
+ +
+ + +
+
+
+
+
+ +

+ +
+ +