From 4cb02c398fb6498a1956b4d13ab46782382a05da Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 26 Aug 2017 13:03:29 -0700 Subject: [PATCH 01/10] Updating README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 2a529af1..4d32ad3a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,9 @@ different languages. You must download and import the dictionaries you wish to u definition lookups. If you have proprietary EPWING dictionaries that you would like to use, please see the [Yomichan Import](https://foosoft.net/projects/yomichan-import) page to learn how to convert and import them into Yomichan. +Please be aware that the non-English dictionaries contain fewer entries than their English counterparts. Even if your +primary language is not English, you may consider also importing the English version for better coverage. + * **[JMdict](http://www.edrdg.org/enamdict/enamdict_doc.html)** (Japanese vocabulary) * [jmdict_dutch.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_dutch.zip) * [jmdict_english.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_english.zip) @@ -133,6 +136,7 @@ Flashcard fields can be configured with the following steps: `{dictionary}` | Name of the dictionary from which the card is being created (unavailable in *grouped* mode). `{expression}` | Term expressed as Kanji (will be displayed in Kana if Kanji is not available). `{furigana}` | Term expressed as Kanji with Furigana displayed above it (e.g. 日本語にほんご). + `{furigana-plain}` | Term expressed as Kanji with Furigana displayed next to it in brackets (e.g. 日本語[にほんご]). `{glossary}` | List of definitions for the term (output format depends on whether running in *grouped* mode). `{reading}` | Kana reading for the term (empty for terms where the expression is the reading). `{sentence}` | Sentence, quote, or phrase in which the term appears in the source content. From 9eac50ea05ae48e77c1bb0ef7b42d9662094cbf8 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Tue, 5 Sep 2017 12:34:52 -0700 Subject: [PATCH 02/10] add sandbox stub --- ext/bg/background.html | 2 ++ ext/manifest.json | 5 ++++- ext/sb/js/sandbox.js | 30 ++++++++++++++++++++++++++++++ ext/sb/sandbox.html | 10 ++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 ext/sb/js/sandbox.js create mode 100644 ext/sb/sandbox.html diff --git a/ext/bg/background.html b/ext/bg/background.html index 97b20f46..151d7e81 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -24,5 +24,7 @@ + + diff --git a/ext/manifest.json b/ext/manifest.json index 2a5f1910..e95cc496 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Yomichan", - "version": "1.3.4", + "version": "1.3.5", "description": "Japanese dictionary with Anki integration", "icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"}, @@ -55,5 +55,8 @@ "strict_min_version": "52.0", "update_url": "https://foosoft.net/projects/yomichan/dl/updates.json" } + }, + "sandbox": { + "pages": ["sb/sandbox.html"] } } diff --git a/ext/sb/js/sandbox.js b/ext/sb/js/sandbox.js new file mode 100644 index 00000000..76b7b511 --- /dev/null +++ b/ext/sb/js/sandbox.js @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Alex Yatskov + * Author: Alex Yatskov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +window.addEventListener('message', event => { + if (event.data.command === 'render') { + const template = Handlebars.compile(event.data.template || ''); + const result = template(event.data.context || {}); + event.source.postMessage({result, sequence: event.data.sequence}); + } +}); diff --git a/ext/sb/sandbox.html b/ext/sb/sandbox.html new file mode 100644 index 00000000..b9d33cf6 --- /dev/null +++ b/ext/sb/sandbox.html @@ -0,0 +1,10 @@ + + + + + + + + + + From 722ddf163818035d603ba8042e1e32ff7dcdfc84 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Tue, 5 Sep 2017 20:35:39 -0700 Subject: [PATCH 03/10] work on sandbox --- ext/bg/background.html | 2 +- ext/bg/js/api.js | 17 +++++++++++++++++ ext/bg/js/backend.js | 9 +++++++++ ext/sb/js/sandbox.js | 4 ++-- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/ext/bg/background.html b/ext/bg/background.html index 151d7e81..7f4a5098 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -25,6 +25,6 @@ - + diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 96147d95..aba1b722 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -100,6 +100,23 @@ async function apiTemplateRender(template, data) { return handlebarsRender(template, data); } +async function apiTemplateRenderDynamic(template, data) { + return new Promise((resolve, reject) => { + const sequence = utilBackend().sequenceNew(); + const handler = event => { + if (event.data.sequence === sequence) { + resolve(event.data.result); + window.removeEventListener('message', handler); + } + }; + + window.addEventListener('message', handler); + + const sandbox = utilBackend().sandbox(); + sandbox.postMessage({template, data, sequence, command: 'render'}, '*'); + }); +} + async function apiCommandExec(command) { const handlers = { search: () => { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 6b3acaa9..9802ea7c 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -22,6 +22,7 @@ class Backend { this.translator = new Translator(); this.anki = new AnkiNull(); this.options = null; + this.sequence = 0; } async prepare() { @@ -36,6 +37,14 @@ class Backend { } } + sequenceNew() { + return this.sequence++; + } + + sandbox() { + return document.getElementById('sandbox').contentWindow; + } + onOptionsUpdated(options) { this.options = utilIsolate(options); diff --git a/ext/sb/js/sandbox.js b/ext/sb/js/sandbox.js index 76b7b511..bf1d56c1 100644 --- a/ext/sb/js/sandbox.js +++ b/ext/sb/js/sandbox.js @@ -24,7 +24,7 @@ window.addEventListener('message', event => { if (event.data.command === 'render') { const template = Handlebars.compile(event.data.template || ''); - const result = template(event.data.context || {}); - event.source.postMessage({result, sequence: event.data.sequence}); + const result = template(event.data.data || {}); + event.source.postMessage({result, sequence: event.data.sequence}, '*'); } }); From 083999212e262c0a51b9401cb9666e6a49e35de9 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Tue, 5 Sep 2017 21:10:19 -0700 Subject: [PATCH 04/10] work on sandbox --- ext/bg/js/api.js | 34 +++++++++++++++++----------------- ext/bg/js/backend.js | 4 ++-- ext/fg/js/api.js | 4 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index aba1b722..0aad436a 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -96,25 +96,25 @@ async function apiNoteView(noteId) { return utilBackend().anki.guiBrowse(`nid:${noteId}`); } -async function apiTemplateRender(template, data) { - return handlebarsRender(template, data); -} +async function apiTemplateRender(template, data, dynamic) { + if (dynamic) { + return new Promise((resolve, reject) => { + const sequence = utilBackend().sequenceNew(); + const handler = event => { + if (event.data.sequence === sequence) { + resolve(event.data.result); + window.removeEventListener('message', handler); + } + }; -async function apiTemplateRenderDynamic(template, data) { - return new Promise((resolve, reject) => { - const sequence = utilBackend().sequenceNew(); - const handler = event => { - if (event.data.sequence === sequence) { - resolve(event.data.result); - window.removeEventListener('message', handler); - } - }; + window.addEventListener('message', handler); - window.addEventListener('message', handler); - - const sandbox = utilBackend().sandbox(); - sandbox.postMessage({template, data, sequence, command: 'render'}, '*'); - }); + const sandbox = utilBackend().sandbox(); + sandbox.postMessage({template, data, sequence, command: 'render'}, '*'); + }); + } else { + return handlebarsRender(template, data); + } } async function apiCommandExec(command) { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 9802ea7c..5061557b 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -113,8 +113,8 @@ class Backend { forward(apiNoteView(noteId), callback); }, - templateRender: ({template, data, callback}) => { - forward(apiTemplateRender(template, data), callback); + templateRender: ({template, data, dynamic, callback}) => { + forward(apiTemplateRender(template, data, dynamic), callback); }, commandExec: ({command, callback}) => { diff --git a/ext/fg/js/api.js b/ext/fg/js/api.js index 151882eb..4b4d9d74 100644 --- a/ext/fg/js/api.js +++ b/ext/fg/js/api.js @@ -45,8 +45,8 @@ function apiNoteView(noteId) { return utilInvoke('noteView', {noteId}); } -function apiTemplateRender(template, data) { - return utilInvoke('templateRender', {data, template}); +function apiTemplateRender(template, data, dynamic) { + return utilInvoke('templateRender', {data, template, dynamic}); } function apiCommandExec(command) { From a3c8508031a1073629803d0616a2ee416cd3cccc Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Wed, 6 Sep 2017 13:18:06 -0700 Subject: [PATCH 05/10] work on sandbox --- ext/bg/js/dictionary.js | 1 - ext/bg/js/options.js | 113 ++++++++++++++- ext/bg/js/settings.js | 18 ++- ext/bg/js/templates.js | 307 ---------------------------------------- ext/bg/settings.html | 18 ++- tmpl/fields.html | 121 ---------------- 6 files changed, 138 insertions(+), 440 deletions(-) delete mode 100644 tmpl/fields.html diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js index 78240157..960ff84d 100644 --- a/ext/bg/js/dictionary.js +++ b/ext/bg/js/dictionary.js @@ -218,7 +218,6 @@ function dictFieldFormat(field, definition, mode, options) { marker, definition, group: options.general.groupResults, - html: options.anki.htmlCards, modeTermKanji: mode === 'term-kanji', modeTermKana: mode === 'term-kana', modeKanji: mode === 'kanji' diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index ea52d337..dcad97d4 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -17,6 +17,115 @@ */ +function optionsFieldTemplates() { + return ` +{{#*inline "glossary-single"}} + {{~#unless brief~}} + {{~#if tags~}}({{#each tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}) {{/if~}} + {{~/unless~}} + {{~#if glossary.[1]~}} +
    {{#each glossary}}
  • {{#multiLine}}{{.}}{{/multiLine}}
  • {{/each}}
+ {{~else~}} + {{~#multiLine}}{{glossary.[0]}}{{/multiLine~}} + {{~/if~}} +{{/inline}} + +{{#*inline "audio"}}{{/inline}} + +{{#*inline "character"}} + {{~definition.character~}} +{{/inline}} + +{{#*inline "dictionary"}} + {{~definition.dictionary~}} +{{/inline}} + +{{#*inline "expression"}} + {{~#if modeTermKana~}} + {{~#if definition.reading~}} + {{definition.reading}} + {{~else~}} + {{definition.expression}} + {{~/if~}} + {{~else~}} + {{definition.expression}} + {{~/if~}} +{{/inline}} + +{{#*inline "furigana"}} + {{#furigana}}{{{definition}}}{{/furigana}} +{{/inline}} + +{{#*inline "furigana-plain"}} + {{#furiganaPlain}}{{{definition}}}{{/furiganaPlain}} +{{/inline}} + +{{#*inline "glossary"}} +
+ {{~#if modeKanji~}} + {{~#if definition.glossary.[1]~}} +
    {{#each definition.glossary}}
  1. {{.}}
  2. {{/each}}
+ {{~else~}} + {{definition.glossary.[0]}} + {{~/if~}} + {{~else~}} + {{~#if group~}} + {{~#if definition.definitions.[1]~}} +
    {{#each definition.definitions}}
  1. {{> glossary-single brief=../brief}}
  2. {{/each}}
+ {{~else~}} + {{~> glossary-single definition.definitions.[0] brief=brief~}} + {{~/if~}} + {{~else~}} + {{~> glossary-single definition brief=brief~}} + {{~/if~}} + {{~/if~}} +
+{{/inline}} + +{{#*inline "glossary-brief"}} + {{~> glossary brief=true ~}} +{{/inline}} + +{{#*inline "kunyomi"}} + {{~#each definition.kunyomi}}{{.}}{{#unless @last}}, {{/unless}}{{/each~}} +{{/inline}} + +{{#*inline "onyomi"}} + {{~#each definition.onyomi}}{{.}}{{#unless @last}}, {{/unless}}{{/each~}} +{{/inline}} + +{{#*inline "reading"}} + {{~#unless modeTermKana}}{{definition.reading}}{{/unless~}} +{{/inline}} + +{{#*inline "sentence"}} + {{~#if definition.cloze}}{{definition.cloze.sentence}}{{/if~}} +{{/inline}} + +{{#*inline "cloze-prefix"}} + {{~#if definition.cloze}}{{definition.cloze.prefix}}{{/if~}} +{{/inline}} + +{{#*inline "cloze-body"}} + {{~#if definition.cloze}}{{definition.cloze.body}}{{/if~}} +{{/inline}} + +{{#*inline "cloze-suffix"}} + {{~#if definition.cloze}}{{definition.cloze.suffix}}{{/if~}} +{{/inline}} + +{{#*inline "tags"}} + {{~#each definition.tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each~}} +{{/inline}} + +{{#*inline "url"}} + {{definition.url}} +{{/inline}} + +{{~> (lookup . "marker") ~}} +`.trim(); +} + function optionsSetDefaults(options) { const defaults = { general: { @@ -48,10 +157,10 @@ function optionsSetDefaults(options) { enable: false, server: 'http://127.0.0.1:8765', tags: ['yomichan'], - htmlCards: true, sentenceExt: 200, terms: {deck: '', model: '', fields: {}}, - kanji: {deck: '', model: '', fields: {}} + kanji: {deck: '', model: '', fields: {}}, + fieldTemplates: optionsFieldTemplates() } }; diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index c029b30b..d4e6ab17 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -41,9 +41,9 @@ async function formRead() { optionsNew.anki.enable = $('#anki-enable').prop('checked'); optionsNew.anki.tags = $('#card-tags').val().split(/[,; ]+/); - optionsNew.anki.htmlCards = $('#generate-html-cards').prop('checked'); optionsNew.anki.sentenceExt = parseInt($('#sentence-detection-extent').val(), 10); optionsNew.anki.server = $('#interface-server').val(); + optionsNew.anki.fieldTemplates = $('#field-templates').val(); if (optionsOld.anki.enable && !ankiErrorShown()) { optionsNew.anki.terms.deck = $('#anki-terms-deck').val(); @@ -143,10 +143,11 @@ async function onReady() { $('#anki-enable').prop('checked', options.anki.enable); $('#card-tags').val(options.anki.tags.join(' ')); - $('#generate-html-cards').prop('checked', options.anki.htmlCards); $('#sentence-detection-extent').val(options.anki.sentenceExt); $('#interface-server').val(options.anki.server); - $('input, select').not('.anki-model').change(utilAsync(onFormOptionsChanged)); + $('#field-templates').val(options.anki.fieldTemplates); + $('#field-templates-reset').click(utilAsync(onAnkiFieldTemplatesReset)); + $('input, select, textarea').not('.anki-model').change(utilAsync(onFormOptionsChanged)); $('.anki-model').change(utilAsync(onAnkiModelChanged)); try { @@ -429,3 +430,14 @@ async function onAnkiModelChanged(e) { ankiSpinnerShow(false); } } + +async function onAnkiFieldTemplatesReset(e) { + try { + e.preventDefault(); + const options = await optionsLoad(); + $('#field-templates').val(options.anki.fieldTemplates = optionsFieldTemplates()); + await optionsSave(options); + } catch (e) { + ankiErrorShow(e); + } +} diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js index f18e70fb..1c059365 100644 --- a/ext/bg/js/templates.js +++ b/ext/bg/js/templates.js @@ -21,313 +21,6 @@ templates['dictionary.html'] = template({"1":function(container,depth0,helpers,p + alias4(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"title","hash":{},"data":data}) : helper))) + "\" class=\"form-control dict-priority\">\n \n\n"; },"useData":true}); -templates['fields.html'] = template({"1":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.program(15, data, 0),"data":data})) != null ? stack1 : ""); -},"2":function(container,depth0,helpers,partials,data) { - var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); - - return ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.brief : depth0),{"name":"unless","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.program(12, data, 0),"data":data})) != null ? stack1 : ""); -},"3":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"4":function(container,depth0,helpers,partials,data) { - var stack1; - - return "(" - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + ") "; -},"5":function(container,depth0,helpers,partials,data) { - var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}); - - return container.escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"name","hash":{},"data":data}) : helper))) - + ((stack1 = helpers.unless.call(alias1,(data && data.last),{"name":"unless","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"6":function(container,depth0,helpers,partials,data) { - return ", "; -},"8":function(container,depth0,helpers,partials,data) { - var stack1; - - return "
    " - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + "
"; -},"9":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, buffer = - "
  • "; - stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper)); - if (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} - if (stack1 != null) { buffer += stack1; } - return buffer + "
  • "; -},"10":function(container,depth0,helpers,partials,data) { - return container.escapeExpression(container.lambda(depth0, depth0)); -},"12":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, buffer = ""; - - stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper)); - if (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} - if (stack1 != null) { buffer += stack1; } - return buffer; -},"13":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0)); -},"15":function(container,depth0,helpers,partials,data) { - var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); - - return ((stack1 = helpers.unless.call(alias1,(depth0 != null ? depth0.brief : depth0),{"name":"unless","hash":{},"fn":container.program(16, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(19, data, 0),"inverse":container.program(13, data, 0),"data":data})) != null ? stack1 : ""); -},"16":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"if","hash":{},"fn":container.program(17, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"17":function(container,depth0,helpers,partials,data) { - var stack1; - - return "(" - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + ") "; -},"19":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(20, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"20":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(depth0, depth0)) - + ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.last),{"name":"unless","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"22":function(container,depth0,helpers,partials,data) { - return ""; -},"24":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.character : stack1), depth0)); -},"26":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.dictionary : stack1), depth0)); -},"28":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.modeTermKana : depth0),{"name":"if","hash":{},"fn":container.program(29, data, 0),"inverse":container.program(32, data, 0),"data":data})) != null ? stack1 : ""); -},"29":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1),{"name":"if","hash":{},"fn":container.program(30, data, 0),"inverse":container.program(32, data, 0),"data":data})) != null ? stack1 : ""); -},"30":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1), depth0)); -},"32":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.expression : stack1), depth0)); -},"34":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(35, data, 0),"inverse":container.program(38, data, 0),"data":data})) != null ? stack1 : ""); -},"35":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, buffer = ""; - - stack1 = ((helper = (helper = helpers.furigana || (depth0 != null ? depth0.furigana : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"furigana","hash":{},"fn":container.program(36, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper)); - if (!helpers.furigana) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} - if (stack1 != null) { buffer += stack1; } - return buffer; -},"36":function(container,depth0,helpers,partials,data) { - var stack1, helper; - - return ((stack1 = ((helper = (helper = helpers.definition || (depth0 != null ? depth0.definition : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"definition","hash":{},"data":data}) : helper))) != null ? stack1 : ""); -},"38":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, buffer = ""; - - stack1 = ((helper = (helper = helpers.furiganaPlain || (depth0 != null ? depth0.furiganaPlain : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"furiganaPlain","hash":{},"fn":container.program(36, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper)); - if (!helpers.furiganaPlain) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} - if (stack1 != null) { buffer += stack1; } - return buffer; -},"40":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, buffer = - " "; - stack1 = ((helper = (helper = helpers.furiganaPlain || (depth0 != null ? depth0.furiganaPlain : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"furiganaPlain","hash":{},"fn":container.program(36, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper)); - if (!helpers.furiganaPlain) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} - if (stack1 != null) { buffer += stack1; } - return buffer + "\n"; -},"42":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); - - return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(43, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.modeKanji : depth0),{"name":"if","hash":{},"fn":container.program(45, data, 0, blockParams, depths),"inverse":container.program(54, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "") - + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(67, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"43":function(container,depth0,helpers,partials,data) { - return "
    "; -},"45":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(46, data, 0),"inverse":container.program(52, data, 0),"data":data})) != null ? stack1 : ""); -},"46":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(47, data, 0),"inverse":container.program(50, data, 0),"data":data})) != null ? stack1 : ""); -},"47":function(container,depth0,helpers,partials,data) { - var stack1; - - return "
      " - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1),{"name":"each","hash":{},"fn":container.program(48, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + "
    "; -},"48":function(container,depth0,helpers,partials,data) { - return "
  • " - + container.escapeExpression(container.lambda(depth0, depth0)) - + "
  • "; -},"50":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1),{"name":"each","hash":{},"fn":container.program(20, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"52":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1)) != null ? stack1["0"] : stack1), depth0)); -},"54":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.group : depth0),{"name":"if","hash":{},"fn":container.program(55, data, 0, blockParams, depths),"inverse":container.program(65, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); -},"55":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(56, data, 0, blockParams, depths),"inverse":container.program(63, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); -},"56":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(57, data, 0, blockParams, depths),"inverse":container.program(60, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); -},"57":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return "
      " - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1),{"name":"each","hash":{},"fn":container.program(58, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + "
    "; -},"58":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return "
  • " - + ((stack1 = container.invokePartial(partials["glossary-single"],depth0,{"name":"glossary-single","hash":{"brief":(depths[1] != null ? depths[1].brief : depths[1]),"html":(depths[1] != null ? depths[1].html : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") - + "
  • "; -},"60":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1),{"name":"each","hash":{},"fn":container.program(61, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"61":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return " * " - + ((stack1 = container.invokePartial(partials["glossary-single"],depth0,{"name":"glossary-single","hash":{"brief":(depths[1] != null ? depths[1].brief : depths[1]),"html":(depths[1] != null ? depths[1].html : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"63":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = container.invokePartial(partials["glossary-single"],((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1)) != null ? stack1["0"] : stack1),{"name":"glossary-single","hash":{"brief":(depth0 != null ? depth0.brief : depth0),"html":(depth0 != null ? depth0.html : depth0)},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"65":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = container.invokePartial(partials["glossary-single"],(depth0 != null ? depth0.definition : depth0),{"name":"glossary-single","hash":{"brief":(depth0 != null ? depth0.brief : depth0),"html":(depth0 != null ? depth0.html : depth0)},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"67":function(container,depth0,helpers,partials,data) { - return "
    "; -},"69":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = container.invokePartial(partials.glossary,depth0,{"name":"glossary","hash":{"brief":true},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"71":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.kunyomi : stack1),{"name":"each","hash":{},"fn":container.program(20, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"73":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.onyomi : stack1),{"name":"each","hash":{},"fn":container.program(20, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"75":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.modeTermKana : depth0),{"name":"unless","hash":{},"fn":container.program(30, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"77":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(78, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"78":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.sentence : stack1), depth0)); -},"80":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(81, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"81":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.prefix : stack1), depth0)); -},"83":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(84, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"84":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.body : stack1), depth0)); -},"86":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(87, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"87":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.suffix : stack1), depth0)); -},"89":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.tags : stack1),{"name":"each","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"91":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(92, data, 0),"inverse":container.program(94, data, 0),"data":data})) != null ? stack1 : ""); -},"92":function(container,depth0,helpers,partials,data) { - var stack1, alias1=container.lambda, alias2=container.escapeExpression; - - return "" - + alias2(alias1(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.url : stack1), depth0)) - + ""; -},"94":function(container,depth0,helpers,partials,data) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.url : stack1), depth0)); -},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" - + ((stack1 = container.invokePartial(helpers.lookup.call(depth0 != null ? depth0 : (container.nullContext || {}),depth0,"marker",{"name":"lookup","hash":{},"data":data}),depth0,{"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"main_d": function(fn, props, container, depth0, data, blockParams, depths) { - - var decorators = container.decorators; - - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(1, data, 0, blockParams, depths),"inverse":container.noop,"args":["glossary-single"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(22, data, 0, blockParams, depths),"inverse":container.noop,"args":["audio"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(24, data, 0, blockParams, depths),"inverse":container.noop,"args":["character"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(26, data, 0, blockParams, depths),"inverse":container.noop,"args":["dictionary"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(28, data, 0, blockParams, depths),"inverse":container.noop,"args":["expression"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(34, data, 0, blockParams, depths),"inverse":container.noop,"args":["furigana"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(40, data, 0, blockParams, depths),"inverse":container.noop,"args":["furigana-plain"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(42, data, 0, blockParams, depths),"inverse":container.noop,"args":["glossary"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(69, data, 0, blockParams, depths),"inverse":container.noop,"args":["glossary-brief"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(71, data, 0, blockParams, depths),"inverse":container.noop,"args":["kunyomi"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(73, data, 0, blockParams, depths),"inverse":container.noop,"args":["onyomi"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(75, data, 0, blockParams, depths),"inverse":container.noop,"args":["reading"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(77, data, 0, blockParams, depths),"inverse":container.noop,"args":["sentence"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(80, data, 0, blockParams, depths),"inverse":container.noop,"args":["cloze-prefix"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(83, data, 0, blockParams, depths),"inverse":container.noop,"args":["cloze-body"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(86, data, 0, blockParams, depths),"inverse":container.noop,"args":["cloze-suffix"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(89, data, 0, blockParams, depths),"inverse":container.noop,"args":["tags"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(91, data, 0, blockParams, depths),"inverse":container.noop,"args":["url"],"data":data}) || fn; - return fn; - } - -,"useDecorators":true,"usePartial":true,"useData":true,"useDepths":true}); templates['kanji.html'] = template({"1":function(container,depth0,helpers,partials,data) { var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 8798aeb1..9aa9ea4d 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -8,8 +8,7 @@ @@ -175,10 +180,6 @@
    -
    - -
    -
    @@ -194,6 +195,11 @@
    +
    + + +
    +

    Specify the information you would like included in your flashcards in the field editor below. diff --git a/tmpl/fields.html b/tmpl/fields.html deleted file mode 100644 index 2794484c..00000000 --- a/tmpl/fields.html +++ /dev/null @@ -1,121 +0,0 @@ -{{#*inline "glossary-single"}} - {{~#if html~}} - {{~#unless brief~}} - {{~#if tags~}}({{#each tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}) {{/if~}} - {{~/unless~}} - {{~#if glossary.[1]~}} -

      {{#each glossary}}
    • {{#multiLine}}{{.}}{{/multiLine}}
    • {{/each}}
    - {{~else~}} - {{~#multiLine}}{{glossary.[0]}}{{/multiLine~}} - {{~/if~}} - {{~else~}} - {{~#unless brief~}} - {{~#if tags~}}({{#each tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}) {{/if~}} - {{~/unless~}} - {{~#if glossary.[1]~}} - {{#each glossary}}{{.}}{{#unless @last}}, {{/unless}}{{/each}} - {{~else~}} - {{glossary.[0]}} - {{~/if~}} - {{~/if~}} -{{/inline}} - -{{#*inline "audio"}}{{/inline}} - -{{#*inline "character"}} - {{~definition.character~}} -{{/inline}} - -{{#*inline "dictionary"}} - {{~definition.dictionary~}} -{{/inline}} - -{{#*inline "expression"}} - {{~#if modeTermKana~}} - {{~#if definition.reading~}} - {{definition.reading}} - {{~else~}} - {{definition.expression}} - {{~/if~}} - {{~else~}} - {{definition.expression}} - {{~/if~}} -{{/inline}} - -{{#*inline "furigana"}} - {{~#if html~}} - {{#furigana}}{{{definition}}}{{/furigana}} - {{~else~}} - {{#furiganaPlain}}{{{definition}}}{{/furiganaPlain}} - {{~/if~}} -{{/inline}} - -{{#*inline "furigana-plain"}} - {{#furiganaPlain}}{{{definition}}}{{/furiganaPlain}} -{{/inline}} - -{{#*inline "glossary"}} - {{~#if html}}
    {{/if~}} - {{~#if modeKanji~}} - {{~#if definition.glossary.[1]~}} - {{~#if html}}
      {{#each definition.glossary}}
    1. {{.}}
    2. {{/each}}
    - {{~else}}{{#each definition.glossary}}{{.}}{{#unless @last}}, {{/unless}}{{/each}}{{/if~}} - {{~else~}} - {{definition.glossary.[0]}} - {{~/if~}} - {{~else~}} - {{~#if group~}} - {{~#if definition.definitions.[1]~}} - {{~#if html}}
      {{#each definition.definitions}}
    1. {{> glossary-single html=../html brief=../brief}}
    2. {{/each}}
    - {{~else}}{{#each definition.definitions}} * {{> glossary-single html=../html brief=../brief}}{{/each}}{{/if~}} - {{~else~}} - {{~> glossary-single definition.definitions.[0] html=html brief=brief~}} - {{~/if~}} - {{~else~}} - {{~> glossary-single definition html=html brief=brief~}} - {{~/if~}} - {{~/if~}} - {{~#if html}}
    {{/if~}} -{{/inline}} - -{{#*inline "glossary-brief"}} - {{~> glossary brief=true ~}} -{{/inline}} - -{{#*inline "kunyomi"}} - {{~#each definition.kunyomi}}{{.}}{{#unless @last}}, {{/unless}}{{/each~}} -{{/inline}} - -{{#*inline "onyomi"}} - {{~#each definition.onyomi}}{{.}}{{#unless @last}}, {{/unless}}{{/each~}} -{{/inline}} - -{{#*inline "reading"}} - {{~#unless modeTermKana}}{{definition.reading}}{{/unless~}} -{{/inline}} - -{{#*inline "sentence"}} - {{~#if definition.cloze}}{{definition.cloze.sentence}}{{/if~}} -{{/inline}} - -{{#*inline "cloze-prefix"}} - {{~#if definition.cloze}}{{definition.cloze.prefix}}{{/if~}} -{{/inline}} - -{{#*inline "cloze-body"}} - {{~#if definition.cloze}}{{definition.cloze.body}}{{/if~}} -{{/inline}} - -{{#*inline "cloze-suffix"}} - {{~#if definition.cloze}}{{definition.cloze.suffix}}{{/if~}} -{{/inline}} - -{{#*inline "tags"}} - {{~#each definition.tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each~}} -{{/inline}} - -{{#*inline "url"}} - {{~#if html}}{{definition.url}}{{else}}{{definition.url}}{{/if~}} -{{/inline}} - -{{~> (lookup . "marker") ~}} From 9efe7bfe1b904db43fafda3520d702c17f44b362 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Wed, 6 Sep 2017 13:35:18 -0700 Subject: [PATCH 06/10] work on sandbox --- ext/bg/js/api.js | 6 ++++-- ext/bg/js/dictionary.js | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 0aad436a..5a307df3 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -62,7 +62,8 @@ async function apiDefinitionAdd(definition, mode) { ); } - return utilBackend().anki.addNote(dictNoteFormat(definition, mode, options)); + const note = await dictNoteFormat(definition, mode, options); + return utilBackend().anki.addNote(note); } async function apiDefinitionsAddable(definitions, modes) { @@ -72,7 +73,8 @@ async function apiDefinitionsAddable(definitions, modes) { const notes = []; for (const definition of definitions) { for (const mode of modes) { - notes.push(dictNoteFormat(definition, mode, utilBackend().options)); + const note = await dictNoteFormat(definition, mode, utilBackend().options); + notes.push(note); } } diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js index 960ff84d..e749390f 100644 --- a/ext/bg/js/dictionary.js +++ b/ext/bg/js/dictionary.js @@ -192,7 +192,7 @@ function dictFieldSplit(field) { return field.length === 0 ? [] : field.split(' '); } -function dictFieldFormat(field, definition, mode, options) { +async function dictFieldFormat(field, definition, mode, options) { const markers = [ 'audio', 'character', @@ -223,13 +223,14 @@ function dictFieldFormat(field, definition, mode, options) { modeKanji: mode === 'kanji' }; - field = field.replace(`{${marker}}`, handlebarsRender('fields.html', data)); + const html = await apiTemplateRender(options.anki.fieldTemplates, data, true); + field = field.replace(`{${marker}}`, html); } return field; } -function dictNoteFormat(definition, mode, options) { +async function dictNoteFormat(definition, mode, options) { const note = {fields: {}, tags: options.anki.tags}; let fields = []; @@ -263,7 +264,7 @@ function dictNoteFormat(definition, mode, options) { } for (const name in fields) { - note.fields[name] = dictFieldFormat(fields[name], definition, mode, options); + note.fields[name] = await dictFieldFormat(fields[name], definition, mode, options); } return note; From 124ecaf8247af673109085eee8fab8d5f2bc7caa Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Wed, 6 Sep 2017 14:19:26 -0700 Subject: [PATCH 07/10] sandbox template cache --- ext/sb/js/sandbox.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/sb/js/sandbox.js b/ext/sb/js/sandbox.js index bf1d56c1..0a0ff31b 100644 --- a/ext/sb/js/sandbox.js +++ b/ext/sb/js/sandbox.js @@ -23,7 +23,14 @@ window.addEventListener('message', event => { if (event.data.command === 'render') { - const template = Handlebars.compile(event.data.template || ''); + window.yomichan_cache = window.yomichan_cache || {}; + + let template = window.yomichan_cache[event.data.template]; + if (!template) { + template = Handlebars.compile(event.data.template || ''); + window.yomichan_cache[event.data.template] = template; + } + const result = template(event.data.data || {}); event.source.postMessage({result, sequence: event.data.sequence}, '*'); } From 9a5a9aa47fd9a6c7ea6830a821689321e8fd9fbb Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 9 Sep 2017 11:36:50 -0700 Subject: [PATCH 08/10] better error handling --- ext/bg/js/api.js | 7 ++++++- ext/sb/js/sandbox.js | 13 +++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 5a307df3..6ab130a7 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -104,7 +104,12 @@ async function apiTemplateRender(template, data, dynamic) { const sequence = utilBackend().sequenceNew(); const handler = event => { if (event.data.sequence === sequence) { - resolve(event.data.result); + if (event.data.command === 'error') { + reject(event.data.result); + } else { + resolve(event.data.result); + } + window.removeEventListener('message', handler); } }; diff --git a/ext/sb/js/sandbox.js b/ext/sb/js/sandbox.js index 0a0ff31b..c3430afe 100644 --- a/ext/sb/js/sandbox.js +++ b/ext/sb/js/sandbox.js @@ -31,7 +31,16 @@ window.addEventListener('message', event => { window.yomichan_cache[event.data.template] = template; } - const result = template(event.data.data || {}); - event.source.postMessage({result, sequence: event.data.sequence}, '*'); + let result = null; + let command = null; + try { + command = 'render'; + result = template(event.data.data || {}); + } catch (e) { + command = 'error'; + result = e; + } + + event.source.postMessage({result, command, sequence: event.data.sequence}, '*'); } }); From 5f46006e8da1d51a66291f25a2bc75959ec81efd Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 9 Sep 2017 12:59:49 -0700 Subject: [PATCH 09/10] scrap sandbox --- ext/bg/background.html | 2 -- ext/bg/js/api.js | 22 ++--------------- ext/bg/js/backend.js | 9 ------- ext/bg/js/handlebars.js | 21 +++++++++++++--- ext/bg/js/settings.js | 2 +- ext/bg/settings.html | 53 ++++++++++++++++++++++------------------- ext/manifest.json | 4 +--- ext/sb/js/sandbox.js | 46 ----------------------------------- ext/sb/sandbox.html | 10 -------- 9 files changed, 51 insertions(+), 118 deletions(-) delete mode 100644 ext/sb/js/sandbox.js delete mode 100644 ext/sb/sandbox.html diff --git a/ext/bg/background.html b/ext/bg/background.html index 7f4a5098..97b20f46 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -24,7 +24,5 @@ - - diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 6ab130a7..5c1aebb6 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -100,27 +100,9 @@ async function apiNoteView(noteId) { async function apiTemplateRender(template, data, dynamic) { if (dynamic) { - return new Promise((resolve, reject) => { - const sequence = utilBackend().sequenceNew(); - const handler = event => { - if (event.data.sequence === sequence) { - if (event.data.command === 'error') { - reject(event.data.result); - } else { - resolve(event.data.result); - } - - window.removeEventListener('message', handler); - } - }; - - window.addEventListener('message', handler); - - const sandbox = utilBackend().sandbox(); - sandbox.postMessage({template, data, sequence, command: 'render'}, '*'); - }); + return handlebarsRenderDynamic(template, data); } else { - return handlebarsRender(template, data); + return handlebarsRenderStatic(template, data); } } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 5061557b..7d68ed84 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -22,7 +22,6 @@ class Backend { this.translator = new Translator(); this.anki = new AnkiNull(); this.options = null; - this.sequence = 0; } async prepare() { @@ -37,14 +36,6 @@ class Backend { } } - sequenceNew() { - return this.sequence++; - } - - sandbox() { - return document.getElementById('sandbox').contentWindow; - } - onOptionsUpdated(options) { this.options = utilIsolate(options); diff --git a/ext/bg/js/handlebars.js b/ext/bg/js/handlebars.js index e0804986..66d5fa2b 100644 --- a/ext/bg/js/handlebars.js +++ b/ext/bg/js/handlebars.js @@ -75,7 +75,7 @@ function handlebarsMultiLine(options) { return options.fn(this).split('\n').join('
    '); } -function handlebarsRender(template, data) { +function handlebarsRegisterHelpers() { if (Handlebars.partials !== Handlebars.templates) { Handlebars.partials = Handlebars.templates; Handlebars.registerHelper('dumpObject', handlebarsDumpObject); @@ -84,6 +84,21 @@ function handlebarsRender(template, data) { Handlebars.registerHelper('kanjiLinks', handlebarsKanjiLinks); Handlebars.registerHelper('multiLine', handlebarsMultiLine); } - - return Handlebars.templates[template](data).trim(); +} + +function handlebarsRenderStatic(name, data) { + handlebarsRegisterHelpers(); + return Handlebars.templates[name](data).trim(); +} + +function handlebarsRenderDynamic(template, data) { + handlebarsRegisterHelpers(); + + Handlebars.yomichan_cache = Handlebars.yomichan_cache || {}; + let instance = Handlebars.yomichan_cache[template]; + if (!instance) { + instance = Handlebars.yomichan_cache[template] = Handlebars.compile(template); + } + + return instance(data).trim(); } diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index d4e6ab17..55b469d0 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -218,7 +218,7 @@ async function dictionaryGroupsPopulate(options) { for (const dictRow of dictRowsSort(dictRows, options)) { const dictOptions = options.dictionaries[dictRow.title] || {enabled: false, priority: 0}; - const dictHtml = handlebarsRender('dictionary.html', { + const dictHtml = await apiTemplateRender('dictionary.html', { title: dictRow.title, version: dictRow.version, revision: dictRow.revision, diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 9aa9ea4d..0a5c205c 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -22,7 +22,7 @@ #field-templates { font-family: monospace; overflow-x: hidden; - white-space: nowrap; + white-space: pre; } @@ -195,11 +195,6 @@
    -
    - - -
    -

    Specify the information you would like included in your flashcards in the field editor below. @@ -252,28 +247,38 @@

    + +
    +

    + 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. +

    + +
    - -
    -

    Support Development

    - -

    - Yomichan is provided to you completely free of charge. Unlike numerous other "free" services, you are not - shown ads, pestered with "offers", or have your browser usage information analyzed and sold to third parties. -

    -

    - If you find Yomichan useful, please consider making a small donation as a way to show your appreciation for the - countless hours that I have devoted to this extension. -

    -

    - -

    -
    - -
    
                 
     
    +            
    +

    Support Development

    + +

    + Yomichan is provided to you completely free of charge. Unlike numerous other "free" services, you are not + shown ads, pestered with "offers", or have your browser usage information analyzed and sold to third parties. +

    +

    + If you find Yomichan useful, please consider making a small donation as a way to show your appreciation for the + countless hours that I have devoted to this extension. +

    +

    + +

    +
    + +
    
    +
                 
    diff --git a/ext/manifest.json b/ext/manifest.json
    index e95cc496..0da3283c 100644
    --- a/ext/manifest.json
    +++ b/ext/manifest.json
    @@ -49,14 +49,12 @@
             }
         },
         "web_accessible_resources": ["fg/float.html"],
    +    "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
         "applications": {
             "gecko": {
                 "id": "yomichan-live@foosoft.net",
                 "strict_min_version": "52.0",
                 "update_url": "https://foosoft.net/projects/yomichan/dl/updates.json"
             }
    -    },
    -    "sandbox": {
    -        "pages": ["sb/sandbox.html"]
         }
     }
    diff --git a/ext/sb/js/sandbox.js b/ext/sb/js/sandbox.js
    deleted file mode 100644
    index c3430afe..00000000
    --- a/ext/sb/js/sandbox.js
    +++ /dev/null
    @@ -1,46 +0,0 @@
    -/*
    - * Copyright (c) 2017 Alex Yatskov 
    - * Author: Alex Yatskov 
    - *
    - * Permission is hereby granted, free of charge, to any person obtaining a copy of
    - * this software and associated documentation files (the "Software"), to deal in
    - * the Software without restriction, including without limitation the rights to
    - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    - * the Software, and to permit persons to whom the Software is furnished to do so,
    - * subject to the following conditions:
    - *
    - * The above copyright notice and this permission notice shall be included in all
    - * copies or substantial portions of the Software.
    - *
    - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    - */
    -
    -
    -window.addEventListener('message', event => {
    -    if (event.data.command === 'render') {
    -        window.yomichan_cache = window.yomichan_cache || {};
    -
    -        let template = window.yomichan_cache[event.data.template];
    -        if (!template) {
    -            template = Handlebars.compile(event.data.template || '');
    -            window.yomichan_cache[event.data.template] = template;
    -        }
    -
    -        let result = null;
    -        let command = null;
    -        try {
    -            command = 'render';
    -            result = template(event.data.data || {});
    -        } catch (e) {
    -            command = 'error';
    -            result = e;
    -        }
    -
    -        event.source.postMessage({result, command, sequence: event.data.sequence}, '*');
    -    }
    -});
    diff --git a/ext/sb/sandbox.html b/ext/sb/sandbox.html
    deleted file mode 100644
    index b9d33cf6..00000000
    --- a/ext/sb/sandbox.html
    +++ /dev/null
    @@ -1,10 +0,0 @@
    -
    -
    -    
    -        
    -    
    -    
    -        
    -        
    -    
    -
    
    From 2eb85cb835a4aece7839eba25c0030e9eb186f85 Mon Sep 17 00:00:00 2001
    From: Alex Yatskov 
    Date: Sun, 10 Sep 2017 10:16:47 -0700
    Subject: [PATCH 10/10] Updating README.md
    
    ---
     README.md | 9 +++++++++
     1 file changed, 9 insertions(+)
    
    diff --git a/README.md b/README.md
    index 4d32ad3a..eaaaa206 100644
    --- a/README.md
    +++ b/README.md
    @@ -240,6 +240,15 @@ exact versions used for distribution.
     
     ## Frequently Asked Questions ##
     
    +*   **Can I still create cards without HTML formatting? The option for it is gone!**
    +
    +    Developing Yomichan is a constant balance between including useful features and keeping complexity at a minimum.
    +    With the new user-editable card template system, it is possible to create text-only cards without having to double
    +    the number field of templates in the extension itself. If you would like to stop HTML tags from being added to your
    +    cards, simply copy the contents of the text-only field template into the template box on
    +    the Anki settings page (make sure you have the *Show advanced options* checkbox ticked), making sure to replace the
    +    existing values.
    +
     *   **Will you add support for online dictionaries?**
     
         Online dictionaries will never be implemented because it is impossible to support them in a robust way. In order to