diff --git a/README.md b/README.md index 2a529af1..eaaaa206 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. @@ -236,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 diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 96147d95..5c1aebb6 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); } } @@ -96,8 +98,12 @@ 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 handlebarsRenderDynamic(template, data); + } else { + return handlebarsRenderStatic(template, data); + } } async function apiCommandExec(command) { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 6b3acaa9..7d68ed84 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -104,8 +104,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/bg/js/dictionary.js b/ext/bg/js/dictionary.js index 78240157..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', @@ -218,19 +218,19 @@ 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' }; - 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 = []; @@ -264,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; 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/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]~}} + + {{~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..55b469d0 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 { @@ -217,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, @@ -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 ""; -},"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..0a5c205c 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -8,8 +8,7 @@ @@ -175,10 +180,6 @@
    -
    - -
    -
    @@ -246,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. +

    +

    + +

    +
    + +
    
    +
                 
    HomepageLegal
    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) { diff --git a/ext/manifest.json b/ext/manifest.json index fbd413e6..7ff76437 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"}, @@ -49,6 +49,7 @@ } }, "web_accessible_resources": ["fg/float.html"], + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", "applications": { "gecko": { "id": "alex@foosoft.net", 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]~}} - - {{~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") ~}}