diff options
| -rw-r--r-- | ext/bg/css/settings.css | 18 | ||||
| -rw-r--r-- | ext/bg/js/dictionary.js | 3 | ||||
| -rw-r--r-- | ext/bg/js/settings.js | 91 | ||||
| -rw-r--r-- | 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 @@                  <div class="form-group ignore-form-changes" style="display: none;" id="settings-popup-preview-settings">                      <label for="settings-popup-preview-text">Popup preview text</label> -                    <input type="text" id="settings-popup-preview-text" class="form-control" value="読め"> +                    <input type="text" id="settings-popup-preview-text" class="form-control" value="読め" placeholder="Preview text">                  </div>                  <div class="form-group ignore-form-changes"> @@ -699,10 +699,60 @@                              <p class="help-block">                                  Fields are formatted using the <a href="https://handlebarsjs.com/" target="_blank" rel="noopener">Handlebars.js</a> 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 <a href="#" id="field-templates-reset">reset to default</a> -                                template settings. +                                their Anki cards. If you encounter problems with your changes, you can always reset to the default template settings.                              </p>                              <textarea autocomplete="off" spellcheck="false" wrap="soft" class="form-control" rows="10" id="field-templates"></textarea> +                            <div> +                                <button class="btn btn-danger" id="field-templates-reset">Reset Templates</button> +                            </div> +                            <p></p> +                            <pre id="field-template-compile-result" hidden></pre> + +                            <p>Templates can be tested using the inputs below.</p> + +                            <div class="form-group"> +                                <div class="row"> +                                    <div class="col-xs-6"> +                                        <label for="field-templates-preview-text">Preview text</label> +                                        <input type="text" id="field-templates-preview-text" class="form-control" value="読め" placeholder="Preview text"> +                                    </div> +                                    <div class="col-xs-6"> +                                        <label for="field-template-render-text">Test field</label> +                                        <div class="input-group"> +                                            <div class="input-group-btn"> +                                                <button class="btn btn-default" id="field-template-render" title="Test"><span class="glyphicon glyphicon-play"></span></button> +                                            </div> +                                            <input type="text" class="form-control" id="field-template-render-text" value="{expression}" placeholder="{marker}"> +                                            <div class="input-group-btn"> +                                                <button class="btn btn-default dropdown-toggle" id="field-templates-dropdown" data-toggle="dropdown"><span class="caret"></span></button> +                                                <ul class="dropdown-menu dropdown-menu-right" id="field-templates-list"></ul> +                                            </div> +                                        </div> +                                    </div> +                                </div> +                            </div> + +                            <p></p> +                            <pre id="field-template-render-result" hidden></pre> +                        </div> + +                        <div class="modal fade" tabindex="-1" role="dialog" id="field-template-reset-modal"> +                            <div class="modal-dialog modal-dialog-centered"> +                                <div class="modal-content"> +                                    <div class="modal-header"> +                                        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> +                                        <h4 class="modal-title">Confirm template reset</h4> +                                    </div> +                                    <div class="modal-body"> +                                        Are you sure you want to reset the field templates to the default value? +                                        Any changes you made will be lost. +                                    </div> +                                    <div class="modal-footer"> +                                        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> +                                        <button type="button" class="btn btn-danger" id="field-templates-reset-confirm">Reset Templates</button> +                                    </div> +                                </div> +                            </div>                          </div>                          <template id="anki-field-template"><tr> @@ -777,6 +827,7 @@          <script src="/mixed/lib/wanakana.min.js"></script>          <script src="/mixed/js/extension.js"></script> +        <script src="/mixed/js/japanese.js"></script>          <script src="/bg/js/anki.js"></script>          <script src="/bg/js/api.js"></script> |