From 86c2d0baa31235686fd4a2591c78c2ae0a048443 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 26 Jun 2017 20:38:36 -0700 Subject: [PATCH 01/68] Updating README.md --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 74ddf1cc..e79824b8 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,32 @@ Yomichan provides advanced features not available in other browser-based diction review process can take several months to complete, the approved version of the extension is often out of date. If you are interested in using the latest and greatest version, please consider using the [locally hosted](https://foosoft.net/projects/yomichan/dl/latest.xpi) version instead. +## Dictionaries ## + +There are three free Japanese dictionaries available for Yomichan, with two of them having versions with glossaries in +different languages. You must download and import the dictionaries you wish to use in order to enable Yomichan +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. + +* **JMdict** (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) + * [jmdict_french.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_french.zip) + * [jmdict_german.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_german.zip) + * [jmdict_hungarian.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_hungarian.zip) + * [jmdict_italian.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_italian.zip) + * [jmdict_russian.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_russian.zip) + * [jmdict_slovenian.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_slovenian.zip) + * [jmdict_spanish.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_spanish.zip) + * [jmdict_swedish.zip](https://foosoft.net/projects/yomichan/dl/dict/jmdict_swedish.zip) +* **JMnedict** (Japanese names) + * [jmnedict.zip](https://foosoft.net/projects/yomichan/dl/dict/jmnedict.zip) +* **KANJIDIC** (Japanese Kanji) + * [kanjidic_english.zip](https://foosoft.net/projects/yomichan/dl/dict/kanjidic_english.zip) + * [kanjidic_french.zip](https://foosoft.net/projects/yomichan/dl/dict/kanjidic_french.zip) + * [kanjidic_portuguese.zip](https://foosoft.net/projects/yomichan/dl/dict/kanjidic_portuguese.zip) + * [kanjidic_spanish.zip](https://foosoft.net/projects/yomichan/dl/dict/kanjidic_spanish.zip) + ## Basic Usage ## 1. Click on the ![](https://foosoft.net/projects/yomichan/img/yomichan-icon.png) icon in the browser toolbar to open the Yomichan actions dialog. From 390cb12896607144fcd1046950f3a2aa680db71b Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 26 Jun 2017 23:28:18 -0700 Subject: [PATCH 02/68] Updating README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e79824b8..c32c867f 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,7 @@ exact versions used for distribution. * Dexie: [homepage](http://dexie.org/) - [snapshot](https://github.com/dfahlander/Dexie.js/archive/v2.0.0-beta.10.zip) * Handlebars: [homepage](http://handlebarsjs.com/) - [snapshot](http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars.min-714a4c4.js) * JQuery: [homepage](https://blog.jquery.com/) - [snapshot](https://code.jquery.com/jquery-3.2.1.min.js) +* JSZip: [homepage](http://stuk.github.io/jszip/) - [snapshot](https://raw.githubusercontent.com/Stuk/jszip/de7f52fbcba485737bef7923a83f0fad92d9f5bc/dist/jszip.min.js) * WanaKana: [homepage](http://wanakana.com/) - [snapshot](https://raw.githubusercontent.com/WaniKani/WanaKana/f2152985f5f2953d74edfe1b6a78e0e329a7cdfb/lib/wanakana.min.js) ## Frequently Asked Questions ## From d57c5530b7ad56a7cc89782b4d186d8fddb55d86 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 1 Jul 2017 18:27:49 -0700 Subject: [PATCH 03/68] view added notes --- ext/bg/js/anki-connect.js | 16 ++++++++++------ ext/bg/js/anki-null.js | 4 ++++ ext/bg/js/display-window.js | 4 ++++ ext/bg/js/templates.js | 12 ++++++------ ext/bg/js/yomichan.js | 8 ++++++++ ext/fg/js/display-frame.js | 4 ++++ ext/fg/js/util.js | 3 +++ ext/mixed/img/view-note.png | Bin 0 -> 622 bytes ext/mixed/js/display.js | 36 +++++++++++++++++++++++++++++++++--- tmpl/kanji.html | 3 ++- tmpl/terms.html | 3 ++- 11 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 ext/mixed/img/view-note.png diff --git a/ext/bg/js/anki-connect.js b/ext/bg/js/anki-connect.js index 9759c8f5..173feefd 100644 --- a/ext/bg/js/anki-connect.js +++ b/ext/bg/js/anki-connect.js @@ -26,7 +26,7 @@ class AnkiConnect { } addNote(note) { - return this.checkVersion().then(() => this.ankiInvoke('addNote', {note}, null)); + return this.checkVersion().then(() => this.ankiInvoke('addNote', {note})); } canAddNotes(notes) { @@ -34,15 +34,19 @@ class AnkiConnect { } getDeckNames() { - return this.checkVersion().then(() => this.ankiInvoke('deckNames', {}, null)); + return this.checkVersion().then(() => this.ankiInvoke('deckNames', {})); } getModelNames() { - return this.checkVersion().then(() => this.ankiInvoke('modelNames', {}, null)); + return this.checkVersion().then(() => this.ankiInvoke('modelNames', {})); } getModelFieldNames(modelName) { - return this.checkVersion().then(() => this.ankiInvoke('modelFieldNames', {modelName}, null)); + return this.checkVersion().then(() => this.ankiInvoke('modelFieldNames', {modelName})); + } + + guiBrowse(query) { + return this.checkVersion().then(() => this.ankiInvoke('guiBrowse', {query})); } checkVersion() { @@ -60,13 +64,13 @@ class AnkiConnect { ankiInvoke(action, params, pool) { return new Promise((resolve, reject) => { - if (pool !== null && this.asyncPools.hasOwnProperty(pool)) { + if (pool && this.asyncPools.hasOwnProperty(pool)) { this.asyncPools[pool].abort(); } const xhr = new XMLHttpRequest(); xhr.addEventListener('loadend', () => { - if (pool !== null) { + if (pool) { delete this.asyncPools[pool]; } diff --git a/ext/bg/js/anki-null.js b/ext/bg/js/anki-null.js index 99dc2f30..8dad6915 100644 --- a/ext/bg/js/anki-null.js +++ b/ext/bg/js/anki-null.js @@ -37,4 +37,8 @@ class AnkiNull { getModelFieldNames(modelName) { return Promise.resolve([]); } + + guiBrowse(query) { + return Promise.resolve([]); + } } diff --git a/ext/bg/js/display-window.js b/ext/bg/js/display-window.js index ae97cd36..64e56f72 100644 --- a/ext/bg/js/display-window.js +++ b/ext/bg/js/display-window.js @@ -37,6 +37,10 @@ window.displayWindow = new class extends Display { return instYomi().definitionsAddable(definitions, modes).catch(() => []); } + noteView(noteId) { + return instYomi().noteView(noteId); + } + templateRender(template, data) { return instYomi().templateRender(template, data); } diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js index f10134ee..50686ed4 100644 --- a/ext/bg/js/templates.js +++ b/ext/bg/js/templates.js @@ -315,10 +315,10 @@ templates['fields.html'] = template({"1":function(container,depth0,helpers,parti templates['kanji.html'] = template({"1":function(container,depth0,helpers,partials,data) { var stack1, helper, alias1=depth0 != null ? depth0 : {}; - return "
\n
\n \n" + return "
\n
\n" + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.addable : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.source : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + "
\n\n
" + + " \n
\n\n
" + container.escapeExpression(((helper = (helper = helpers.character || (depth0 != null ? depth0.character : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"character","hash":{},"data":data}) : helper))) + "
\n\n
\n \n \n \n \n \n\n"; },"useData":true}); templates['terms.html'] = template({"1":function(container,depth0,helpers,partials,data) { - var stack1, alias1=depth0 != null ? depth0 : {}; + var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"if","hash":{},"fn":container.program(2, 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(5, data, 0),"inverse":container.program(9, data, 0),"data":data})) != null ? stack1 : ""); @@ -458,10 +458,10 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia var stack1; return "
\n" - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "
\n"; },"3":function(container,depth0,helpers,partials,data) { - var helper, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; + var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; return " \n" - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "\n"; },"6":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(7, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper)); + stack1 = ((helper = (helper = helpers.multiLine || (depth0 != null ? depth0.multiLine : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"multiLine","hash":{},"fn":container.program(7, 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 + "
  • \n"; @@ -488,7 +488,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia },"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 : {},options) : helper)); + 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 + "
    \n"; @@ -497,7 +497,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0)); },"12":function(container,depth0,helpers,partials,data) { - var stack1, alias1=depth0 != null ? depth0 : {}; + var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); return "
    \n
    \n" + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.addable : depth0),{"name":"if","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") @@ -516,7 +516,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia },"15":function(container,depth0,helpers,partials,data) { return " \n"; },"17":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function", buffer = + var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", buffer = "
    "; stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : alias2),(options={"name":"kanjiLinks","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data}),(typeof helper === alias3 ? helper.call(alias1,options) : helper)); if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} @@ -527,11 +527,11 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia },"18":function(container,depth0,helpers,partials,data) { var helper; - return container.escapeExpression(((helper = (helper = helpers.expression || (depth0 != null ? depth0.expression : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"expression","hash":{},"data":data}) : helper))); + return container.escapeExpression(((helper = (helper = helpers.expression || (depth0 != null ? depth0.expression : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"expression","hash":{},"data":data}) : helper))); },"20":function(container,depth0,helpers,partials,data) { var stack1, helper, options, buffer = "
    "; - stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"kanjiLinks","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper)); + stack1 = ((helper = (helper = helpers.kanjiLinks || (depth0 != null ? depth0.kanjiLinks : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"kanjiLinks","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper)); if (!helpers.kanjiLinks) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} if (stack1 != null) { buffer += stack1; } return buffer + "
    \n"; @@ -539,7 +539,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia var stack1; return "
    \n" - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.reasons : depth0),{"name":"each","hash":{},"fn":container.program(23, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.reasons : depth0),{"name":"each","hash":{},"fn":container.program(23, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "
    \n"; },"23":function(container,depth0,helpers,partials,data) { var stack1; @@ -547,19 +547,19 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia return " " + container.escapeExpression(container.lambda(depth0, depth0)) + " " - + ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(data && data.last),{"name":"unless","hash":{},"fn":container.program(24, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.last),{"name":"unless","hash":{},"fn":container.program(24, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "\n"; },"24":function(container,depth0,helpers,partials,data) { return "«"; },"26":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definitions : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(27, data, 0),"inverse":container.program(30, data, 0),"data":data})) != null ? stack1 : ""); + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = (depth0 != null ? depth0.definitions : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(27, data, 0),"inverse":container.program(30, data, 0),"data":data})) != null ? stack1 : ""); },"27":function(container,depth0,helpers,partials,data) { var stack1; return "
      \n" - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(28, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(28, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "
    \n"; },"28":function(container,depth0,helpers,partials,data) { var stack1; @@ -578,7 +578,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia },"34":function(container,depth0,helpers,partials,data) { var stack1, helper, options, buffer = "
    ";
    -  stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(35, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper));
    +  stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(35, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),options) : helper));
       if (!helpers.dumpObject) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
       if (stack1 != null) { buffer += stack1; }
       return buffer + "
    \n"; @@ -589,11 +589,11 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia },"37":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1; - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(38, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); + return ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(38, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); },"38":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1; - return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(data && data.first),{"name":"unless","hash":{},"fn":container.program(39, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "") + return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : (container.nullContext || {}),(data && data.first),{"name":"unless","hash":{},"fn":container.program(39, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "\n" + ((stack1 = container.invokePartial(partials.term,depth0,{"name":"term","hash":{"playback":(depths[1] != null ? depths[1].playback : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1]),"grouped":(depths[1] != null ? depths[1].grouped : depths[1]),"debug":(depths[1] != null ? depths[1].debug : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); },"39":function(container,depth0,helpers,partials,data) { @@ -604,7 +604,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia var stack1; return "\n\n" - + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(37, data, 0, blockParams, depths),"inverse":container.program(41, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); + + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(37, data, 0, blockParams, depths),"inverse":container.program(41, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); },"main_d": function(fn, props, container, depth0, data, blockParams, depths) { var decorators = container.decorators; diff --git a/ext/bg/search.html b/ext/bg/search.html index 7cbae392..655d7819 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -32,13 +32,19 @@
    + + + + - - + + - + + + diff --git a/ext/fg/frame.html b/ext/fg/frame.html index 9ff2c585..e0b9eac5 100644 --- a/ext/fg/frame.html +++ b/ext/fg/frame.html @@ -30,12 +30,13 @@
    - - + + - - + + + From ef43b742b0b0705e8fe44792933878c161d55d8f Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 30 Jul 2017 17:32:36 -0700 Subject: [PATCH 42/68] Updating README.md --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d467f82..1ed2bb77 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,6 @@ Flashcard fields can be configured with the following steps: `{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. 日本語にほんご). `{glossary}` | List of definitions for the term (output format depends on whether running in *grouped* mode). - `{glossary-brief}` | Shorter version of `{glossary}`, without `{tags}`. `{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. `{tags}` | Grammar and usage tags providing information about the term (unavailable in *grouped* mode). @@ -290,4 +289,16 @@ exact versions used for distribution. ## License ## -GPL +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + From 817dfc25f2954680bfaa671b227d7b4caf4132ab Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 30 Jul 2017 17:40:04 -0700 Subject: [PATCH 43/68] Updating README.md --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c32c867f..1ed2bb77 100644 --- a/README.md +++ b/README.md @@ -289,4 +289,16 @@ exact versions used for distribution. ## License ## -GPL +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + From b2003a0a560a9bd469e23e860c39dc5b21412021 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 5 Aug 2017 13:13:06 -0700 Subject: [PATCH 44/68] cleanup --- ext/bg/js/api.js | 102 ++++++++++++------------------------------- ext/bg/js/backend.js | 50 ++++++++++++++++++++- ext/bg/js/popup.js | 8 ++-- 3 files changed, 79 insertions(+), 81 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index ac53a7a3..103d247c 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -21,81 +21,6 @@ * Helpers */ -function utilMessageDispatch({action, params}, sender, callback) { - const forward = (promise, callback) => { - return promise.then(result => { - callback({result}); - }).catch(error => { - callback({error}); - }); - }; - - const handlers = { - optionsGet: ({callback}) => { - forward(optionsLoad(), callback); - }, - - kanjiFind: ({text, callback}) => { - forward(apiKanjiFind(text), callback); - }, - - termsFind: ({text, callback}) => { - forward(apiTermsFind(text), callback); - }, - - templateRender: ({template, data, callback}) => { - forward(apiTemplateRender(template, data), callback); - }, - - definitionAdd: ({definition, mode, callback}) => { - forward(apiDefinitionAdd(definition, mode), callback); - }, - - definitionsAddable: ({definitions, modes, callback}) => { - forward(apiDefinitionsAddable(definitions, modes), callback); - }, - - noteView: ({noteId}) => { - forward(apiNoteView(noteId), callback); - } - }; - - const handler = handlers[action]; - if (handler) { - params.callback = callback; - handler(params); - } - - return true; -} - -function utilCommandDispatch(command) { - const handlers = { - search: () => { - chrome.tabs.create({url: chrome.extension.getURL('/bg/search.html')}); - }, - - help: () => { - chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'}); - }, - - options: () => { - chrome.runtime.openOptionsPage(); - }, - - toggle: () => { - const options = chrome.extension.getBackgroundPage().yomichan.options; - options.general.enable = !options.general.enable; - optionsSave(options).then(() => apiOptionsSet(options)); - } - }; - - const handler = handlers[command]; - if (handler) { - handler(); - } -} - function utilNoteFormat(definition, mode) { const options = chrome.extension.getBackgroundPage().yomichan.options; const note = {fields: {}, tags: options.anki.tags}; @@ -142,6 +67,33 @@ function utilNoteFormat(definition, mode) { * API */ +async function apiCommandExec(command) { + const handlers = { + search: () => { + chrome.tabs.create({url: chrome.extension.getURL('/bg/search.html')}); + }, + + help: () => { + chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'}); + }, + + options: () => { + chrome.runtime.openOptionsPage(); + }, + + toggle: () => { + const options = chrome.extension.getBackgroundPage().yomichan.options; + options.general.enable = !options.general.enable; + optionsSave(options).then(() => apiOptionsSet(options)); + } + }; + + const handler = handlers[command]; + if (handler) { + handler(); + } +} + async function apiOptionsSet(options) { // In Firefox, setting options from the options UI somehow carries references // to the DOM across to the background page, causing the options object to diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index f7823047..2f60b4b7 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -26,8 +26,54 @@ window.yomichan = new class { this.translator.prepare().then(optionsLoad).then(options => { apiOptionsSet(options); - chrome.commands.onCommand.addListener(utilCommandDispatch); - chrome.runtime.onMessage.addListener(utilMessageDispatch); + chrome.commands.onCommand.addListener(apiCommandExec); + chrome.runtime.onMessage.addListener(({action, params}, sender, callback) => { + const forward = (promise, callback) => { + return promise.then(result => { + callback({result}); + }).catch(error => { + callback({error}); + }); + }; + + const handlers = { + optionsGet: ({callback}) => { + forward(optionsLoad(), callback); + }, + + kanjiFind: ({text, callback}) => { + forward(apiKanjiFind(text), callback); + }, + + termsFind: ({text, callback}) => { + forward(apiTermsFind(text), callback); + }, + + templateRender: ({template, data, callback}) => { + forward(apiTemplateRender(template, data), callback); + }, + + definitionAdd: ({definition, mode, callback}) => { + forward(apiDefinitionAdd(definition, mode), callback); + }, + + definitionsAddable: ({definitions, modes, callback}) => { + forward(apiDefinitionsAddable(definitions, modes), callback); + }, + + noteView: ({noteId}) => { + forward(apiNoteView(noteId), callback); + } + }; + + const handler = handlers[action]; + if (handler) { + params.callback = callback; + handler(params); + } + + return true; + }); if (options.general.showGuide) { chrome.tabs.create({url: chrome.extension.getURL('/bg/guide.html')}); diff --git a/ext/bg/js/popup.js b/ext/bg/js/popup.js index 712839d9..77cb5166 100644 --- a/ext/bg/js/popup.js +++ b/ext/bg/js/popup.js @@ -18,14 +18,14 @@ $(document).ready(() => { - $('#open-search').click(() => utilCommandDispatch('search')); - $('#open-options').click(() => utilCommandDispatch('options')); - $('#open-help').click(() => utilCommandDispatch('help')); + $('#open-search').click(() => apiCommandExec('search')); + $('#open-options').click(() => apiCommandExec('options')); + $('#open-help').click(() => apiCommandExec('help')); optionsLoad().then(options => { const toggle = $('#enable-search'); toggle.prop('checked', options.general.enable).change(); toggle.bootstrapToggle(); - toggle.change(() => utilCommandDispatch('toggle')); + toggle.change(() => apiCommandExec('toggle')); }); }); From 257c864bb5ca5ef9afb4d92b9d29a7f479bc50b4 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 5 Aug 2017 14:57:33 -0700 Subject: [PATCH 45/68] fix anki --- ext/bg/js/api.js | 60 +++++------------------------------------ ext/bg/js/dictionary.js | 40 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 54 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 103d247c..36e283b9 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -17,56 +17,6 @@ */ -/* - * Helpers - */ - -function utilNoteFormat(definition, mode) { - const options = chrome.extension.getBackgroundPage().yomichan.options; - const note = {fields: {}, tags: options.anki.tags}; - let fields = []; - - if (mode === 'kanji') { - fields = options.anki.kanji.fields; - note.deckName = options.anki.kanji.deck; - note.modelName = options.anki.kanji.model; - } else { - fields = options.anki.terms.fields; - note.deckName = options.anki.terms.deck; - note.modelName = options.anki.terms.model; - - if (definition.audio) { - const audio = { - url: definition.audio.url, - filename: definition.audio.filename, - skipHash: '7e2c2f954ef6051373ba916f000168dc', - fields: [] - }; - - for (const name in fields) { - if (fields[name].includes('{audio}')) { - audio.fields.push(name); - } - } - - if (audio.fields.length > 0) { - note.audio = audio; - } - } - } - - for (const name in fields) { - note.fields[name] = dictFieldFormat(fields[name], definition, mode, options); - } - - return note; -} - - -/* - * API - */ - async function apiCommandExec(command) { const handlers = { search: () => { @@ -159,9 +109,9 @@ async function apiKanjiFind(text) { async function apiDefinitionAdd(definition, mode) { const yomichan = chrome.extension.getBackgroundPage().yomichan; + const options = yomichan.options; if (mode !== 'kanji') { - const options = yomichan.options; await audioInject( definition, options.anki.terms.fields, @@ -169,18 +119,20 @@ async function apiDefinitionAdd(definition, mode) { ); } - return yomichan.anki.addNote(utilNoteFormat(definition, mode)); + return yomichan.anki.addNote(dictNoteFormat(definition, mode, options)); } async function apiDefinitionsAddable(definitions, modes) { + const yomichan = chrome.extension.getBackgroundPage().yomichan; + const options = yomichan.options; + const notes = []; for (const definition of definitions) { for (const mode of modes) { - notes.push(utilNoteFormat(definition, mode)); + notes.push(dictNoteFormat(definition, mode, options)); } } - const yomichan = chrome.extension.getBackgroundPage().yomichan; const results = await yomichan.anki.canAddNotes(notes); const states = []; for (let resultBase = 0; resultBase < results.length; resultBase += modes.length) { diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js index 73efe7d1..6f9b30e4 100644 --- a/ext/bg/js/dictionary.js +++ b/ext/bg/js/dictionary.js @@ -231,3 +231,43 @@ function dictFieldFormat(field, definition, mode, options) { return field; } + +function dictNoteFormat(definition, mode, options) { + const note = {fields: {}, tags: options.anki.tags}; + let fields = []; + + if (mode === 'kanji') { + fields = options.anki.kanji.fields; + note.deckName = options.anki.kanji.deck; + note.modelName = options.anki.kanji.model; + } else { + fields = options.anki.terms.fields; + note.deckName = options.anki.terms.deck; + note.modelName = options.anki.terms.model; + + if (definition.audio) { + const audio = { + url: definition.audio.url, + filename: definition.audio.filename, + skipHash: '7e2c2f954ef6051373ba916f000168dc', + fields: [] + }; + + for (const name in fields) { + if (fields[name].includes('{audio}')) { + audio.fields.push(name); + } + } + + if (audio.fields.length > 0) { + note.audio = audio; + } + } + } + + for (const name in fields) { + note.fields[name] = dictFieldFormat(fields[name], definition, mode, options); + } + + return note; +} From 8e1c6776d1a6149c222ac0bda6b7b0d373453d11 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 5 Aug 2017 19:02:03 -0700 Subject: [PATCH 46/68] more cleanup --- ext/bg/js/api.js | 115 ++++++++++++++++-------------------- ext/bg/js/backend.js | 133 ++++++++++++++++++++++++++---------------- ext/bg/js/settings.js | 18 ++---- 3 files changed, 140 insertions(+), 126 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 36e283b9..9dbb69c1 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -17,72 +17,34 @@ */ -async function apiCommandExec(command) { - const handlers = { - search: () => { - chrome.tabs.create({url: chrome.extension.getURL('/bg/search.html')}); - }, +/* + * Backend + */ - help: () => { - chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'}); - }, - - options: () => { - chrome.runtime.openOptionsPage(); - }, - - toggle: () => { - const options = chrome.extension.getBackgroundPage().yomichan.options; - options.general.enable = !options.general.enable; - optionsSave(options).then(() => apiOptionsSet(options)); - } - }; - - const handler = handlers[command]; - if (handler) { - handler(); - } +function backend() { + return chrome.extension.getBackgroundPage().yomichan_backend; } + +/* + * API + */ + async function apiOptionsSet(options) { // In Firefox, setting options from the options UI somehow carries references // to the DOM across to the background page, causing the options object to // become a "DeadObject" after the options page is closed. The workaround used // here is to create a deep copy of the options object. - const yomichan = chrome.extension.getBackgroundPage().yomichan; - yomichan.options = JSON.parse(JSON.stringify(options)); - - if (!options.general.enable) { - chrome.browserAction.setBadgeBackgroundColor({color: '#d9534f'}); - chrome.browserAction.setBadgeText({text: 'off'}); - } else if (!dictConfigured(options)) { - chrome.browserAction.setBadgeBackgroundColor({color: '#f0ad4e'}); - chrome.browserAction.setBadgeText({text: '!'}); - } else { - chrome.browserAction.setBadgeText({text: ''}); - } - - if (options.anki.enable) { - yomichan.anki = new AnkiConnect(options.anki.server); - } else { - yomichan.anki = new AnkiNull(); - } - - chrome.tabs.query({}, tabs => { - for (const tab of tabs) { - chrome.tabs.sendMessage(tab.id, {action: 'optionsSet', params: options}, () => null); - } - }); + backend().optionsSet(JSON.parse(JSON.stringify(options))); } async function apiOptionsGet() { - return chrome.extension.getBackgroundPage().yomichan.options; + return backend().options; } async function apiTermsFind(text) { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - const options = yomichan.options; - const translator = yomichan.translator; + const options = backend().options; + const translator = backend().translator; const searcher = options.general.groupResults ? translator.findTermsGrouped.bind(translator) : @@ -101,15 +63,13 @@ async function apiTermsFind(text) { } async function apiKanjiFind(text) { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - const options = yomichan.options; - const definitions = await yomichan.translator.findKanji(text, dictEnabledSet(options)); + const options = backend().options; + const definitions = await backend().translator.findKanji(text, dictEnabledSet(options)); return definitions.slice(0, options.general.maxResults); } async function apiDefinitionAdd(definition, mode) { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - const options = yomichan.options; + const options = backend().options; if (mode !== 'kanji') { await audioInject( @@ -119,21 +79,18 @@ async function apiDefinitionAdd(definition, mode) { ); } - return yomichan.anki.addNote(dictNoteFormat(definition, mode, options)); + return backend().anki.addNote(dictNoteFormat(definition, mode, options)); } async function apiDefinitionsAddable(definitions, modes) { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - const options = yomichan.options; - const notes = []; for (const definition of definitions) { for (const mode of modes) { - notes.push(dictNoteFormat(definition, mode, options)); + notes.push(dictNoteFormat(definition, mode, backend().options)); } } - const results = await yomichan.anki.canAddNotes(notes); + const results = await backend().anki.canAddNotes(notes); const states = []; for (let resultBase = 0; resultBase < results.length; resultBase += modes.length) { const state = {}; @@ -148,10 +105,38 @@ async function apiDefinitionsAddable(definitions, modes) { } async function apiNoteView(noteId) { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - return yomichan.anki.guiBrowse(`nid:${noteId}`); + return backend().anki.guiBrowse(`nid:${noteId}`); } async function apiTemplateRender(template, data) { return handlebarsRender(template, data); } + +async function apiCommandExec(command) { + const handlers = { + search: () => { + chrome.tabs.create({url: chrome.extension.getURL('/bg/search.html')}); + }, + + help: () => { + chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'}); + }, + + options: () => { + chrome.runtime.openOptionsPage(); + }, + + toggle: async () => { + const options = backend().options; + options.general.enable = !options.general.enable; + await optionsSave(options); + await apiOptionsSet(options); + } + }; + + const handler = handlers[command]; + if (handler) { + handler(); + } +} + diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 2f60b4b7..6b345e6d 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -17,67 +17,102 @@ */ -window.yomichan = new class { +window.yomichan_backend = new class { constructor() { this.translator = new Translator(); this.anki = new AnkiNull(); this.options = null; + } - this.translator.prepare().then(optionsLoad).then(options => { - apiOptionsSet(options); + async prepare() { + await this.translator.prepare(); + await apiOptionsSet(await optionsLoad()); - chrome.commands.onCommand.addListener(apiCommandExec); - chrome.runtime.onMessage.addListener(({action, params}, sender, callback) => { - const forward = (promise, callback) => { - return promise.then(result => { - callback({result}); - }).catch(error => { - callback({error}); - }); - }; + chrome.commands.onCommand.addListener(this.onCommand.bind(this)); + chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); - const handlers = { - optionsGet: ({callback}) => { - forward(optionsLoad(), callback); - }, + if (this.options.general.showGuide) { + chrome.tabs.create({url: chrome.extension.getURL('/bg/guide.html')}); + } + } - kanjiFind: ({text, callback}) => { - forward(apiKanjiFind(text), callback); - }, + optionsSet(options) { + this.options = options; - termsFind: ({text, callback}) => { - forward(apiTermsFind(text), callback); - }, + if (!options.general.enable) { + chrome.browserAction.setBadgeBackgroundColor({color: '#d9534f'}); + chrome.browserAction.setBadgeText({text: 'off'}); + } else if (!dictConfigured(options)) { + chrome.browserAction.setBadgeBackgroundColor({color: '#f0ad4e'}); + chrome.browserAction.setBadgeText({text: '!'}); + } else { + chrome.browserAction.setBadgeText({text: ''}); + } - templateRender: ({template, data, callback}) => { - forward(apiTemplateRender(template, data), callback); - }, + if (options.anki.enable) { + backend().anki = new AnkiConnect(options.anki.server); + } else { + backend().anki = new AnkiNull(); + } - definitionAdd: ({definition, mode, callback}) => { - forward(apiDefinitionAdd(definition, mode), callback); - }, - - definitionsAddable: ({definitions, modes, callback}) => { - forward(apiDefinitionsAddable(definitions, modes), callback); - }, - - noteView: ({noteId}) => { - forward(apiNoteView(noteId), callback); - } - }; - - const handler = handlers[action]; - if (handler) { - params.callback = callback; - handler(params); - } - - return true; - }); - - if (options.general.showGuide) { - chrome.tabs.create({url: chrome.extension.getURL('/bg/guide.html')}); + chrome.tabs.query({}, tabs => { + for (const tab of tabs) { + chrome.tabs.sendMessage(tab.id, {action: 'optionsSet', params: options}, () => null); } }); } + + onCommand(command) { + apiCommandExec(command); + } + + onMessage({action, params}, sender, callback) { + const forward = (promise, callback) => { + return promise.then(result => { + callback({result}); + }).catch(error => { + callback({error}); + }); + }; + + const handlers = { + optionsGet: ({callback}) => { + forward(optionsLoad(), callback); + }, + + kanjiFind: ({text, callback}) => { + forward(apiKanjiFind(text), callback); + }, + + termsFind: ({text, callback}) => { + forward(apiTermsFind(text), callback); + }, + + templateRender: ({template, data, callback}) => { + forward(apiTemplateRender(template, data), callback); + }, + + definitionAdd: ({definition, mode, callback}) => { + forward(apiDefinitionAdd(definition, mode), callback); + }, + + definitionsAddable: ({definitions, modes, callback}) => { + forward(apiDefinitionsAddable(definitions, modes), callback); + }, + + noteView: ({noteId}) => { + forward(apiNoteView(noteId), callback); + } + }; + + const handler = handlers[action]; + if (handler) { + params.callback = callback; + handler(params); + } + + return true; + } }; + +window.yomichan_backend.prepare(); diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index fa44c8da..1edbab01 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -22,33 +22,27 @@ */ function utilAnkiGetModelNames() { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - return yomichan.anki.getModelNames(); + return backend().anki.getModelNames(); } function utilAnkiGetDeckNames() { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - return yomichan.anki.getDeckNames(); + return backend().anki.getDeckNames(); } function utilAnkiGetModelFieldNames(modelName) { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - return yomichan.anki.getModelFieldNames(modelName); + return backend().anki.getModelFieldNames(modelName); } function utilDatabaseGetDictionaries() { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - return yomichan.translator.database.getDictionaries(); + return backend().translator.database.getDictionaries(); } function utilDatabasePurge() { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - return yomichan.translator.database.purge(); + return backend().translator.database.purge(); } function utilDatabaseImport(data, progress) { - const yomichan = chrome.extension.getBackgroundPage().yomichan; - return yomichan.translator.database.importDictionary(data, progress); + return backend().translator.database.importDictionary(data, progress); } From dfecef1f23435c933237794de6665f6fbacf90ec Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 5 Aug 2017 19:11:06 -0700 Subject: [PATCH 47/68] more cleanup --- ext/bg/js/api.js | 2 +- ext/bg/js/backend.js | 2 +- ext/bg/js/display-window.js | 19 +++++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 9dbb69c1..bc2693e5 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -35,7 +35,7 @@ async function apiOptionsSet(options) { // to the DOM across to the background page, causing the options object to // become a "DeadObject" after the options page is closed. The workaround used // here is to create a deep copy of the options object. - backend().optionsSet(JSON.parse(JSON.stringify(options))); + backend().onOptionsUpdated(JSON.parse(JSON.stringify(options))); } async function apiOptionsGet() { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 6b345e6d..1c058433 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -36,7 +36,7 @@ window.yomichan_backend = new class { } } - optionsSet(options) { + onOptionsUpdated(options) { this.options = options; if (!options.general.enable) { diff --git a/ext/bg/js/display-window.js b/ext/bg/js/display-window.js index 1f607510..52c0cafa 100644 --- a/ext/bg/js/display-window.js +++ b/ext/bg/js/display-window.js @@ -21,12 +21,11 @@ window.displayWindow = new class extends Display { constructor() { super($('#spinner'), $('#content')); - const search = $('#search'); - search.click(this.onSearch.bind(this)); + this.search = $('#search').click(this.onSearch.bind(this)); + this.query = $('#query').on('input', this.inputSearch.bind(this)); + this.intro = $('#intro'); - const query = $('#query'); - query.on('input', () => search.prop('disabled', query.val().length === 0)); - window.wanakana.bind(query.get(0)); + window.wanakana.bind(this.query.get(0)); } handleError(error) { @@ -34,15 +33,19 @@ window.displayWindow = new class extends Display { } clearSearch() { - $('#query').focus().select(); + this.query.focus().select(); + } + + inputSearch() { + this.search.prop('disabled', this.query.val().length === 0); } async onSearch(e) { e.preventDefault(); try { - $('#intro').slideUp(); - const {length, definitions} = await apiTermsFind($('#query').val()); + this.intro.slideUp(); + const {length, definitions} = await apiTermsFind(this.query.val()); super.showTermDefs(definitions, await apiOptionsGet()); } catch (e) { this.handleError(e); From 7e635d6382b0d96f596e2440b93e5935230367aa Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 5 Aug 2017 19:23:17 -0700 Subject: [PATCH 48/68] more cleanup --- ext/bg/background.html | 1 + ext/bg/js/api.js | 37 +++++++++++---------------------- ext/bg/js/backend.js | 4 ++-- ext/bg/js/settings.js | 33 ------------------------------ ext/bg/js/util.js | 46 ++++++++++++++++++++++++++++++++++++++++++ ext/bg/settings.html | 1 + 6 files changed, 62 insertions(+), 60 deletions(-) create mode 100644 ext/bg/js/util.js diff --git a/ext/bg/background.html b/ext/bg/background.html index 1e9f3809..40f37b11 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -19,6 +19,7 @@ + diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index bc2693e5..024ba75e 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -17,34 +17,21 @@ */ -/* - * Backend - */ - -function backend() { - return chrome.extension.getBackgroundPage().yomichan_backend; -} - - -/* - * API - */ - async function apiOptionsSet(options) { // In Firefox, setting options from the options UI somehow carries references // to the DOM across to the background page, causing the options object to // become a "DeadObject" after the options page is closed. The workaround used // here is to create a deep copy of the options object. - backend().onOptionsUpdated(JSON.parse(JSON.stringify(options))); + utilBackend().onOptionsUpdated(JSON.parse(JSON.stringify(options))); } async function apiOptionsGet() { - return backend().options; + return utilBackend().options; } async function apiTermsFind(text) { - const options = backend().options; - const translator = backend().translator; + const options = utilBackend().options; + const translator = utilBackend().translator; const searcher = options.general.groupResults ? translator.findTermsGrouped.bind(translator) : @@ -63,13 +50,13 @@ async function apiTermsFind(text) { } async function apiKanjiFind(text) { - const options = backend().options; - const definitions = await backend().translator.findKanji(text, dictEnabledSet(options)); + const options = utilBackend().options; + const definitions = await utilBackend().translator.findKanji(text, dictEnabledSet(options)); return definitions.slice(0, options.general.maxResults); } async function apiDefinitionAdd(definition, mode) { - const options = backend().options; + const options = utilBackend().options; if (mode !== 'kanji') { await audioInject( @@ -79,18 +66,18 @@ async function apiDefinitionAdd(definition, mode) { ); } - return backend().anki.addNote(dictNoteFormat(definition, mode, options)); + return utilBackend().anki.addNote(dictNoteFormat(definition, mode, options)); } async function apiDefinitionsAddable(definitions, modes) { const notes = []; for (const definition of definitions) { for (const mode of modes) { - notes.push(dictNoteFormat(definition, mode, backend().options)); + notes.push(dictNoteFormat(definition, mode, utilBackend().options)); } } - const results = await backend().anki.canAddNotes(notes); + const results = await utilBackend().anki.canAddNotes(notes); const states = []; for (let resultBase = 0; resultBase < results.length; resultBase += modes.length) { const state = {}; @@ -105,7 +92,7 @@ async function apiDefinitionsAddable(definitions, modes) { } async function apiNoteView(noteId) { - return backend().anki.guiBrowse(`nid:${noteId}`); + return utilBackend().anki.guiBrowse(`nid:${noteId}`); } async function apiTemplateRender(template, data) { @@ -127,7 +114,7 @@ async function apiCommandExec(command) { }, toggle: async () => { - const options = backend().options; + const options = utilBackend().options; options.general.enable = !options.general.enable; await optionsSave(options); await apiOptionsSet(options); diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 1c058433..f61e9742 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -50,9 +50,9 @@ window.yomichan_backend = new class { } if (options.anki.enable) { - backend().anki = new AnkiConnect(options.anki.server); + this.anki = new AnkiConnect(options.anki.server); } else { - backend().anki = new AnkiNull(); + this.anki = new AnkiNull(); } chrome.tabs.query({}, tabs => { diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 1edbab01..ce9e14a2 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -17,39 +17,6 @@ */ -/* - * Utilities - */ - -function utilAnkiGetModelNames() { - return backend().anki.getModelNames(); -} - -function utilAnkiGetDeckNames() { - return backend().anki.getDeckNames(); -} - -function utilAnkiGetModelFieldNames(modelName) { - return backend().anki.getModelFieldNames(modelName); -} - -function utilDatabaseGetDictionaries() { - return backend().translator.database.getDictionaries(); -} - -function utilDatabasePurge() { - return backend().translator.database.purge(); -} - -function utilDatabaseImport(data, progress) { - return backend().translator.database.importDictionary(data, progress); -} - - -/* - * General - */ - async function formRead() { const optionsOld = await optionsLoad(); const optionsNew = $.extend(true, {}, optionsOld); diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js new file mode 100644 index 00000000..9dc57950 --- /dev/null +++ b/ext/bg/js/util.js @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Alex Yatskov + * Author: Alex Yatskov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +function utilBackend() { + return chrome.extension.getBackgroundPage().yomichan_backend; +} + +function utilAnkiGetModelNames() { + return utilBackend().anki.getModelNames(); +} + +function utilAnkiGetDeckNames() { + return utilBackend().anki.getDeckNames(); +} + +function utilAnkiGetModelFieldNames(modelName) { + return utilBackend().anki.getModelFieldNames(modelName); +} + +function utilDatabaseGetDictionaries() { + return utilBackend().translator.database.getDictionaries(); +} + +function utilDatabasePurge() { + return utilBackend().translator.database.purge(); +} + +function utilDatabaseImport(data, progress) { + return utilBackend().translator.database.importDictionary(data, progress); +} diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 7833a21d..719c67a2 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -283,6 +283,7 @@ + From 7fbe2ddaf33bad05fb26aec759806e0f6ae250d2 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 5 Aug 2017 20:20:22 -0700 Subject: [PATCH 49/68] more cleanup --- ext/fg/frame.html | 1 + ext/fg/js/api.js | 31 +++++++------------------------ ext/fg/js/util.js | 35 +++++++++++++++++++++++++++++++++++ ext/manifest.json | 8 +++++--- 4 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 ext/fg/js/util.js diff --git a/ext/fg/frame.html b/ext/fg/frame.html index e0b9eac5..3fe42eb2 100644 --- a/ext/fg/frame.html +++ b/ext/fg/frame.html @@ -32,6 +32,7 @@ + diff --git a/ext/fg/js/api.js b/ext/fg/js/api.js index e252637e..b4d75c3c 100644 --- a/ext/fg/js/api.js +++ b/ext/fg/js/api.js @@ -17,47 +17,30 @@ */ -function apiInvoke(action, params={}) { - return new Promise((resolve, reject) => { - try { - chrome.runtime.sendMessage({action, params}, ({result, error}) => { - if (error) { - reject(error); - } else { - resolve(result); - } - }); - } catch (e) { - window.yomichanOrphaned = true; - reject(e.message); - } - }); -} - function apiOptionsGet() { - return apiInvoke('optionsGet'); + return utilInvoke('optionsGet'); } function apiTermsFind(text) { - return apiInvoke('termsFind', {text}); + return utilInvoke('termsFind', {text}); } function apiKanjiFind(text) { - return apiInvoke('kanjiFind', {text}); + return utilInvoke('kanjiFind', {text}); } function apiTemplateRender(template, data) { - return apiInvoke('templateRender', {data, template}); + return utilInvoke('templateRender', {data, template}); } function apiDefinitionsAddable(definitions, modes) { - return apiInvoke('definitionsAddable', {definitions, modes}).catch(() => null); + return utilInvoke('definitionsAddable', {definitions, modes}).catch(() => null); } function apiDefinitionAdd(definition, mode) { - return apiInvoke('definitionAdd', {definition, mode}); + return utilInvoke('definitionAdd', {definition, mode}); } function apiNoteView(noteId) { - return apiInvoke('noteView', {noteId}); + return utilInvoke('noteView', {noteId}); } diff --git a/ext/fg/js/util.js b/ext/fg/js/util.js new file mode 100644 index 00000000..311fc065 --- /dev/null +++ b/ext/fg/js/util.js @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Alex Yatskov + * Author: Alex Yatskov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +function utilInvoke(action, params={}) { + return new Promise((resolve, reject) => { + try { + chrome.runtime.sendMessage({action, params}, ({result, error}) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + } catch (e) { + window.yomichanOrphaned = true; + reject(e.message); + } + }); +} diff --git a/ext/manifest.json b/ext/manifest.json index 288976f3..48308b17 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -15,11 +15,13 @@ "content_scripts": [{ "matches": ["http://*/*", "https://*/*", "file://*/*"], "js": [ - "fg/js/document.js", - "fg/js/source-range.js", - "fg/js/source-element.js", "fg/js/api.js", + "fg/js/document.js", "fg/js/popup.js", + "fg/js/source-element.js", + "fg/js/source-range.js", + "fg/js/util.js", + "fg/js/frontend.js" ], "css": ["fg/css/client.css"] From aac2a58b5f821c6f90c95bb19f3b0a755d5e1739 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 13 Aug 2017 16:11:51 -0700 Subject: [PATCH 50/68] wip --- ext/bg/background.html | 1 + ext/bg/js/display-window.js | 14 +- ext/bg/search.html | 2 +- ext/fg/frame.html | 4 +- ext/fg/js/display-frame.js | 58 ++---- ext/fg/js/frontend.js | 135 +++++++------- ext/fg/js/util.js | 2 +- ext/mixed/js/display.js | 344 +++++++++++++++++++----------------- 8 files changed, 288 insertions(+), 272 deletions(-) diff --git a/ext/bg/background.html b/ext/bg/background.html index 40f37b11..fd3b7dd1 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -20,6 +20,7 @@ + diff --git a/ext/bg/js/display-window.js b/ext/bg/js/display-window.js index 52c0cafa..cbb96681 100644 --- a/ext/bg/js/display-window.js +++ b/ext/bg/js/display-window.js @@ -17,26 +17,26 @@ */ -window.displayWindow = new class extends Display { +window.yomichan_window = new class extends Display { constructor() { super($('#spinner'), $('#content')); this.search = $('#search').click(this.onSearch.bind(this)); - this.query = $('#query').on('input', this.inputSearch.bind(this)); + this.query = $('#query').on('input', this.onSearchInput.bind(this)); this.intro = $('#intro'); window.wanakana.bind(this.query.get(0)); } - handleError(error) { + onError(error) { window.alert(`Error: ${error}`); } - clearSearch() { + onSearchClear() { this.query.focus().select(); } - inputSearch() { + onSearchInput() { this.search.prop('disabled', this.query.val().length === 0); } @@ -46,9 +46,9 @@ window.displayWindow = new class extends Display { try { this.intro.slideUp(); const {length, definitions} = await apiTermsFind(this.query.val()); - super.showTermDefs(definitions, await apiOptionsGet()); + super.termsShow(definitions, await apiOptionsGet()); } catch (e) { - this.handleError(e); + this.onError(e); } } }; diff --git a/ext/bg/search.html b/ext/bg/search.html index 655d7819..fe44d74e 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -40,10 +40,10 @@ + - diff --git a/ext/fg/frame.html b/ext/fg/frame.html index 3fe42eb2..dda3ef06 100644 --- a/ext/fg/frame.html +++ b/ext/fg/frame.html @@ -18,9 +18,9 @@ -
    +
    -
    +

    Yomichan Updated!

    diff --git a/ext/fg/js/display-frame.js b/ext/fg/js/display-frame.js index 09bd9255..5ea376c2 100644 --- a/ext/fg/js/display-frame.js +++ b/ext/fg/js/display-frame.js @@ -17,65 +17,45 @@ */ -window.displayFrame = new class extends Display { +window.yomichan_frame = new class extends Display { constructor() { super($('#spinner'), $('#content')); $(window).on('message', this.onMessage.bind(this)); } - definitionAdd(definition, mode) { - return apiDefinitionAdd(definition, mode); - } - - definitionsAddable(definitions, modes) { - return apiDefinitionsAddable(definitions, modes); - } - - noteView(noteId) { - return apiNoteView(noteId); - } - - templateRender(template, data) { - return apiTemplateRender(template, data); - } - - kanjiFind(character) { - return apiKanjiFind(character); - } - - handleError(error) { - if (window.yomichanOrphaned) { - this.showOrphaned(); + onError(error) { + if (window.yomichan_orphaned) { + this.onOrphaned(); } else { window.alert(`Error: ${error}`); } } - clearSearch() { + onOrphaned() { + $('#definitions').hide(); + $('#error-orphaned').show(); + } + + onSearchClear() { window.parent.postMessage('popupClose', '*'); } - selectionCopy() { + onSelectionCopy() { window.parent.postMessage('selectionCopy', '*'); } - showOrphaned() { - $('#content').hide(); - $('#orphan').show(); - } - onMessage(e) { const handlers = { - showTermDefs: ({definitions, options, context}) => { - this.showTermDefs(definitions, options, context); + termsShow: ({definitions, options, context}) => { + this.termsShow(definitions, options, context); }, - showKanjiDefs: ({definitions, options, context}) => { - this.showKanjiDefs(definitions, options, context); + kanjiShow: ({definitions, options, context}) => { + this.kanjiShow(definitions, options, context); }, - showOrphaned: () => { - this.showOrphaned(); + orphaned: () => { + this.onOrphaned(); } }; @@ -89,8 +69,8 @@ window.displayFrame = new class extends Display { onKeyDown(e) { const handlers = { 67: /* c */ () => { - if (e.ctrlKey && window.getSelection().toString() === '') { - this.selectionCopy(); + if (e.ctrlKey && !window.getSelection().toString()) { + this.onSelectionCopy(); return true; } } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 9974d878..37389766 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -17,7 +17,7 @@ */ -window.yomichanFrontend = new class { +window.yomichan_frontend = new class { constructor() { this.popup = new Popup(); this.popupTimer = null; @@ -27,17 +27,23 @@ window.yomichanFrontend = new class { this.lastTextSource = null; this.pendingLookup = false; this.options = null; + } - apiOptionsGet().then(options => { - this.options = options; - window.addEventListener('mouseover', this.onMouseOver.bind(this)); - window.addEventListener('mousedown', this.onMouseDown.bind(this)); - window.addEventListener('mouseup', this.onMouseUp.bind(this)); - window.addEventListener('mousemove', this.onMouseMove.bind(this)); - window.addEventListener('resize', e => this.searchClear()); - window.addEventListener('message', this.onFrameMessage.bind(this)); - chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this)); - }).catch(this.handleError.bind(this)); + async prepare() { + try { + this.options = await apiOptionsGet(); + } catch (e) { + this.onError(e); + } + + window.addEventListener('message', this.onFrameMessage.bind(this)); + window.addEventListener('mousedown', this.onMouseDown.bind(this)); + window.addEventListener('mousemove', this.onMouseMove.bind(this)); + window.addEventListener('mouseover', this.onMouseOver.bind(this)); + window.addEventListener('mouseup', this.onMouseUp.bind(this)); + window.addEventListener('resize', this.onResize.bind(this)); + + chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this)); } popupTimerSet(callback) { @@ -144,7 +150,11 @@ window.yomichanFrontend = new class { callback(); } - searchAt(point) { + onResize() { + this.onSearchClear(); + } + + async searchAt(point) { if (this.pendingLookup) { return; } @@ -160,70 +170,69 @@ window.yomichanFrontend = new class { } this.pendingLookup = true; - this.searchTerms(textSource).then(found => { - if (!found) { - return this.searchKanji(textSource); + + try { + if (!await this.searchTerms(textSource)) { + await this.searchKanji(textSource); } - }).catch(error => { - this.handleError(error, textSource); - }).then(() => { - docImposterDestroy(); - this.pendingLookup = false; - }); + } catch (e) { + this.onError(e); + } + + docImposterDestroy(); + this.pendingLookup = false; } - searchTerms(textSource) { + async searchTerms(textSource) { textSource.setEndOffset(this.options.scanning.length); - return apiTermsFind(textSource.text()).then(({definitions, length}) => { - if (definitions.length === 0) { - return false; - } else { - textSource.setEndOffset(length); + const {definitions, length} = await apiTermsFind(textSource.text()); + if (definitions.length === 0) { + return false; + } - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); - const url = window.location.href; - this.popup.showTermDefs( - textSource.getRect(), - definitions, - this.options, - {sentence, url} - ); + textSource.setEndOffset(length); - this.lastTextSource = textSource; - if (this.options.scanning.selectText) { - textSource.select(); - } + const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + const url = window.location.href; + this.popup.termsShow( + textSource.getRect(), + definitions, + this.options, + {sentence, url} + ); - return true; - } - }); + this.lastTextSource = textSource; + if (this.options.scanning.selectText) { + textSource.select(); + } + + return true; } - searchKanji(textSource) { + async searchKanji(textSource) { textSource.setEndOffset(1); - return apiKanjiFind(textSource.text()).then(definitions => { - if (definitions.length === 0) { - return false; - } else { - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); - const url = window.location.href; - this.popup.showKanjiDefs( - textSource.getRect(), - definitions, - this.options, - {sentence, url} - ); + const definitions = await apiKanjiFind(textSource.text()); + if (definitions.length === 0) { + return false; + } - this.lastTextSource = textSource; - if (this.options.scanning.selectText) { - textSource.select(); - } + const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + const url = window.location.href; + this.popup.showKanji( + textSource.getRect(), + definitions, + this.options, + {sentence, url} + ); - return true; - } - }); + this.lastTextSource = textSource; + if (this.options.scanning.selectText) { + textSource.select(); + } + + return true; } searchClear() { @@ -238,7 +247,7 @@ window.yomichanFrontend = new class { } handleError(error, textSource) { - if (window.yomichanOrphaned) { + if (window.yomichan_orphaned) { if (textSource && this.options.scanning.modifier !== 'none') { this.popup.showOrphaned(textSource.getRect(), this.options); } diff --git a/ext/fg/js/util.js b/ext/fg/js/util.js index 311fc065..afa895ba 100644 --- a/ext/fg/js/util.js +++ b/ext/fg/js/util.js @@ -28,7 +28,7 @@ function utilInvoke(action, params={}) { } }); } catch (e) { - window.yomichanOrphaned = true; + window.yomichan_orphaned = true; reject(e.message); } }); diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index f408fb25..97dd7d5c 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -32,171 +32,57 @@ class Display { $(document).keydown(this.onKeyDown.bind(this)); } - handleError(error) { + onError(error) { throw 'override me'; } - clearSearch() { + onSearchClear() { throw 'override me'; } - showTermDefs(definitions, options, context) { - window.focus(); - - this.spinner.hide(); - this.definitions = definitions; - this.options = options; - this.context = context; - - const sequence = ++this.sequence; - const params = { - definitions, - addable: options.anki.enable, - grouped: options.general.groupResults, - playback: options.general.audioSource !== 'disabled', - debug: options.general.debugInfo - }; - - if (context) { - for (const definition of definitions) { - if (context.sentence) { - definition.cloze = Display.clozeBuild(context.sentence, definition.source); - } - - definition.url = context.url; - } - } - - apiTemplateRender('terms.html', params).then(content => { - this.container.html(content); - this.entryScroll(context && context.index || 0); - - $('.action-add-note').click(this.onAddNote.bind(this)); - $('.action-view-note').click(this.onViewNote.bind(this)); - $('.action-play-audio').click(this.onPlayAudio.bind(this)); - $('.kanji-link').click(this.onKanjiLookup.bind(this)); - - return this.adderButtonsUpdate(['term-kanji', 'term-kana'], sequence); - }).catch(this.handleError.bind(this)); - } - - showKanjiDefs(definitions, options, context) { - window.focus(); - - this.spinner.hide(); - this.definitions = definitions; - this.options = options; - this.context = context; - - const sequence = ++this.sequence; - const params = { - definitions, - source: context && context.source, - addable: options.anki.enable, - debug: options.general.debugInfo - }; - - if (context) { - for (const definition of definitions) { - if (context.sentence) { - definition.cloze = Display.clozeBuild(context.sentence); - } - - definition.url = context.url; - } - } - - apiTemplateRender('kanji.html', params).then(content => { - this.container.html(content); - this.entryScroll(context && context.index || 0); - - $('.action-add-note').click(this.onAddNote.bind(this)); - $('.source-term').click(this.onSourceTerm.bind(this)); - - return this.adderButtonsUpdate(['kanji'], sequence); - }).catch(this.handleError.bind(this)); - } - - adderButtonsUpdate(modes, sequence) { - return apiDefinitionsAddable(this.definitions, modes).then(states => { - if (!states || sequence !== this.sequence) { - return; - } - - states.forEach((state, index) => { - for (const mode in state) { - const button = Display.adderButtonFind(index, mode); - if (state[mode]) { - button.removeClass('disabled'); - } else { - button.addClass('disabled'); - } - - button.removeClass('pending'); - } - }); - }); - } - - entryScroll(index, smooth) { - index = Math.min(index, this.definitions.length - 1); - index = Math.max(index, 0); - - $('.current').hide().eq(index).show(); - - const container = $('html,body').stop(); - const entry = $('.entry').eq(index); - const target = index === 0 ? 0 : entry.offset().top; - - if (smooth) { - container.animate({scrollTop: target}, 200); - } else { - container.scrollTop(target); - } - - this.index = index; - } - - onSourceTerm(e) { + onSourceTermView(e) { e.preventDefault(); this.sourceBack(); } - onKanjiLookup(e) { - e.preventDefault(); + async onKanjiLookup(e) { + try { + e.preventDefault(); - const link = $(e.target); - const context = { - source: { - definitions: this.definitions, - index: Display.entryIndexFind(link) + const link = $(e.target); + const context = { + source: { + definitions: this.definitions, + index: Display.entryIndexFind(link) + } + }; + + if (this.context) { + context.sentence = this.context.sentence; + context.url = this.context.url; } - }; - if (this.context) { - context.sentence = this.context.sentence; - context.url = this.context.url; + const kanjiDefs = await apiKanjiFind(link.text()); + this.kanjiShow(kanjiDefs, this.options, context); + } catch (e) { + this.onError(e); } - - apiKanjiFind(link.text()).then(kanjiDefs => { - this.showKanjiDefs(kanjiDefs, this.options, context); - }).catch(this.handleError.bind(this)); } - onPlayAudio(e) { + onAudioPlay(e) { e.preventDefault(); const index = Display.entryIndexFind($(e.currentTarget)); this.audioPlay(this.definitions[index]); } - onAddNote(e) { + onNoteAdd(e) { e.preventDefault(); const link = $(e.currentTarget); const index = Display.entryIndexFind(link); this.noteAdd(this.definitions[index], link.data('mode')); } - onViewNote(e) { + onNoteView(e) { e.preventDefault(); const link = $(e.currentTarget); const index = Display.entryIndexFind(link); @@ -220,48 +106,48 @@ class Display { const handlers = { 27: /* escape */ () => { - this.clearSearch(); + this.onSearchClear(); return true; }, 33: /* page up */ () => { if (e.altKey) { - this.entryScroll(this.index - 3, true); + this.entryScrollIntoView(this.index - 3, true); return true; } }, 34: /* page down */ () => { if (e.altKey) { - this.entryScroll(this.index + 3, true); + this.entryScrollIntoView(this.index + 3, true); return true; } }, 35: /* end */ () => { if (e.altKey) { - this.entryScroll(this.definitions.length - 1, true); + this.entryScrollIntoView(this.definitions.length - 1, true); return true; } }, 36: /* home */ () => { if (e.altKey) { - this.entryScroll(0, true); + this.entryScrollIntoView(0, true); return true; } }, 38: /* up */ () => { if (e.altKey) { - this.entryScroll(this.index - 1, true); + this.entryScrollIntoView(this.index - 1, true); return true; } }, 40: /* down */ () => { if (e.altKey) { - this.entryScroll(this.index + 1, true); + this.entryScrollIntoView(this.index + 1, true); return true; } }, @@ -317,6 +203,135 @@ class Display { } } + async termsShow(definitions, options, context) { + try { + window.focus(); + + this.definitions = definitions; + this.options = options; + this.context = context; + + const sequence = ++this.sequence; + const params = { + definitions, + addable: options.anki.enable, + grouped: options.general.groupResults, + playback: options.general.audioSource !== 'disabled', + debug: options.general.debugInfo + }; + + if (context) { + for (const definition of definitions) { + if (context.sentence) { + definition.cloze = Display.clozeBuild(context.sentence, definition.source); + } + + definition.url = context.url; + } + } + + const content = await apiTemplateRender('terms.html', params); + this.container.html(content); + this.entryScrollIntoView(context && context.index || 0); + + $('.action-add-note').click(this.onNoteAdd.bind(this)); + $('.action-view-note').click(this.onNoteView.bind(this)); + $('.action-play-audio').click(this.onAudioPlay.bind(this)); + $('.kanji-link').click(this.onKanjiLookup.bind(this)); + + await this.adderButtonUpdate(['term-kanji', 'term-kana'], sequence); + } catch (e) { + this.onError(e); + } + } + + async kanjiShow(definitions, options, context) { + try { + window.focus(); + + this.definitions = definitions; + this.options = options; + this.context = context; + + const sequence = ++this.sequence; + const params = { + definitions, + source: context && context.source, + addable: options.anki.enable, + debug: options.general.debugInfo + }; + + if (context) { + for (const definition of definitions) { + if (context.sentence) { + definition.cloze = Display.clozeBuild(context.sentence); + } + + definition.url = context.url; + } + } + + const content = await apiTemplateRender('kanji.html', params); + this.container.html(content); + this.entryScrollIntoView(context && context.index || 0); + + $('.action-add-note').click(this.onNoteAdd.bind(this)); + $('.source-term').click(this.onSourceTermView.bind(this)); + + await this.adderButtonUpdate(['kanji'], sequence); + } catch (e) { + this.onError(e); + } + } + + async adderButtonUpdate(modes, sequence) { + try { + this.spinner.show(); + + const states = apiDefinitionsAddable(this.definitions, modes); + if (!states || sequence !== this.sequence) { + return; + } + + for (let i = 0; i < states.length; ++i) { + const state = states[i]; + for (const mode in state) { + const button = Display.adderButtonFind(i, mode); + if (state[mode]) { + button.removeClass('disabled'); + } else { + button.addClass('disabled'); + } + + button.removeClass('pending'); + } + } + } catch (e) { + this.onError(e); + } finally { + this.spinner.hide(); + } + } + + entryScrollIntoView(index, smooth) { + index = Math.min(index, this.definitions.length - 1); + index = Math.max(index, 0); + + $('.current').hide().eq(index).show(); + + const container = $('html,body').stop(); + const entry = $('.entry').eq(index); + const target = index === 0 ? 0 : entry.offset().top; + + if (smooth) { + container.animate({scrollTop: target}, 200); + } else { + container.scrollTop(target); + } + + this.index = index; + } + sourceBack() { if (this.context && this.context.source) { const context = { @@ -325,35 +340,42 @@ class Display { index: this.context.source.index }; - this.showTermDefs(this.context.source.definitions, this.options, context); + this.termsShow(this.context.source.definitions, this.options, context); } } - noteAdd(definition, mode) { - this.spinner.show(); - return apiDefinitionAdd(definition, mode).then(noteId => { + async noteAdd(definition, mode) { + try { + this.spinner.show(); + + const noteId = await apiDefinitionAdd(definition, mode); if (noteId) { const index = this.definitions.indexOf(definition); Display.adderButtonFind(index, mode).addClass('disabled'); Display.viewerButtonFind(index).removeClass('pending disabled').data('noteId', noteId); } else { - this.handleError('note could not be added'); + throw 'note could note be added'; } - }).catch(this.handleError.bind(this)).then(() => this.spinner.hide()); + } catch (e) { + this.onError(e); + } finally { + this.spinner.hide(); + } } - audioPlay(definition) { - this.spinner.show(); + async audioPlay(definition) { + try { + this.spinner.show(); - for (const key in this.audioCache) { - this.audioCache[key].pause(); - } - - audioBuildUrl(definition, this.options.general.audioSource, this.responseCache).then(url => { + let url = await audioBuildUrl(definition, this.options.general.audioSource, this.responseCache); if (!url) { url = '/mixed/mp3/button.mp3'; } + for (const key in this.audioCache) { + this.audioCache[key].pause(); + } + let audio = this.audioCache[url]; if (audio) { audio.currentTime = 0; @@ -371,7 +393,11 @@ class Display { audio.play(); }; } - }).catch(this.handleError.bind(this)).then(() => this.spinner.hide()); + } catch (e) { + this.onError(e); + } finally { + this.spinner.hide(); + } } static clozeBuild(sentence, source) { From 3ca28a93746ed0860bf19ede83e3e9bac979bfb5 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 13 Aug 2017 16:42:22 -0700 Subject: [PATCH 51/68] wip --- ext/fg/js/frontend.js | 108 +++++++++++++++++++++--------------------- ext/fg/js/popup.js | 103 +++++++++++++++++++--------------------- 2 files changed, 105 insertions(+), 106 deletions(-) diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 37389766..de5fa953 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -32,30 +32,18 @@ window.yomichan_frontend = new class { async prepare() { try { this.options = await apiOptionsGet(); + + window.addEventListener('message', e => this.onFrameMessage(e)); + window.addEventListener('mousedown', e => this.onMouseDown(e)); + window.addEventListener('mousemove', e => this.onMouseMove(e)); + window.addEventListener('mouseover', e => this.onMouseOver(e)); + window.addEventListener('mouseup', e => this.onMouseUp(e)); + window.addEventListener('resize', e => this.onResize(e)); + + chrome.runtime.onMessage.addListener(({action, params}, sender, callback) => this.onBgMessage(action, params, sender, callback)); } catch (e) { this.onError(e); } - - window.addEventListener('message', this.onFrameMessage.bind(this)); - window.addEventListener('mousedown', this.onMouseDown.bind(this)); - window.addEventListener('mousemove', this.onMouseMove.bind(this)); - window.addEventListener('mouseover', this.onMouseOver.bind(this)); - window.addEventListener('mouseup', this.onMouseUp.bind(this)); - window.addEventListener('resize', this.onResize.bind(this)); - - chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this)); - } - - popupTimerSet(callback) { - this.popupTimerClear(); - this.popupTimer = window.setTimeout(callback, this.options.scanning.delay); - } - - popupTimerClear() { - if (this.popupTimer) { - window.clearTimeout(this.popupTimer); - this.popupTimer = null; - } } onMouseOver(e) { @@ -132,7 +120,11 @@ window.yomichan_frontend = new class { } } - onBgMessage({action, params}, sender, callback) { + onResize() { + this.onSearchClear(); + } + + onBgMessage(action, params, sender, callback) { const handlers = { optionsSet: options => { this.options = options; @@ -150,37 +142,55 @@ window.yomichan_frontend = new class { callback(); } - onResize() { - this.onSearchClear(); + onError(error) { + if (window.yomichan_orphaned) { + if (this.lastTextSource && this.options.scanning.modifier !== 'none') { + this.popup.showOrphaned(this.lastTextSource.getRect(), this.options); + } + } else { + window.alert(`Error: ${error}`); + } + } + + popupTimerSet(callback) { + this.popupTimerClear(); + this.popupTimer = window.setTimeout(callback, this.options.scanning.delay); + } + + popupTimerClear() { + if (this.popupTimer) { + window.clearTimeout(this.popupTimer); + this.popupTimer = null; + } } async searchAt(point) { - if (this.pendingLookup) { - return; - } - - const textSource = docRangeFromPoint(point); - if (!textSource || !textSource.containsPoint(point)) { - docImposterDestroy(); - return; - } - - if (this.lastTextSource && this.lastTextSource.equals(textSource)) { - return; - } - - this.pendingLookup = true; - try { + if (this.pendingLookup) { + return; + } + + const textSource = docRangeFromPoint(point); + if (!textSource || !textSource.containsPoint(point)) { + docImposterDestroy(); + return; + } + + if (this.lastTextSource && this.lastTextSource.equals(textSource)) { + return; + } + + this.pendingLookup = true; + if (!await this.searchTerms(textSource)) { await this.searchKanji(textSource); } } catch (e) { this.onError(e); + } finally { + docImposterDestroy(); + this.pendingLookup = false; } - - docImposterDestroy(); - this.pendingLookup = false; } async searchTerms(textSource) { @@ -245,14 +255,6 @@ window.yomichan_frontend = new class { this.lastTextSource = null; } +}(); - handleError(error, textSource) { - if (window.yomichan_orphaned) { - if (textSource && this.options.scanning.modifier !== 'none') { - this.popup.showOrphaned(textSource.getRect(), this.options); - } - } else { - window.alert(`Error: ${error}`); - } - } -}; +window.yomichan_frontend.prepare(); diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index cd7e846a..ba3289d4 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -40,51 +40,51 @@ class Popup { return this.injected; } - show(elementRect, options) { - return this.inject().then(() => { - const containerStyle = window.getComputedStyle(this.container); - const containerHeight = parseInt(containerStyle.height); - const containerWidth = parseInt(containerStyle.width); + async show(elementRect, options) { + await this.inject(); - const limitX = document.body.clientWidth; - const limitY = window.innerHeight; + const containerStyle = window.getComputedStyle(this.container); + const containerHeight = parseInt(containerStyle.height); + const containerWidth = parseInt(containerStyle.width); - let x = elementRect.left; - let width = Math.max(containerWidth, options.general.popupWidth); - const overflowX = Math.max(x + width - limitX, 0); - if (overflowX > 0) { - if (x >= overflowX) { - x -= overflowX; - } else { - width = limitX; - x = 0; - } - } + const limitX = document.body.clientWidth; + const limitY = window.innerHeight; - let y = 0; - let height = Math.max(containerHeight, options.general.popupHeight); - const yBelow = elementRect.bottom + options.general.popupOffset; - const yAbove = elementRect.top - options.general.popupOffset; - const overflowBelow = Math.max(yBelow + height - limitY, 0); - const overflowAbove = Math.max(height - yAbove, 0); - if (overflowBelow > 0 || overflowAbove > 0) { - if (overflowBelow < overflowAbove) { - height = Math.max(height - overflowBelow, 0); - y = yBelow; - } else { - height = Math.max(height - overflowAbove, 0); - y = Math.max(yAbove - height, 0); - } + let x = elementRect.left; + let width = Math.max(containerWidth, options.general.popupWidth); + const overflowX = Math.max(x + width - limitX, 0); + if (overflowX > 0) { + if (x >= overflowX) { + x -= overflowX; } else { - y = yBelow; + width = limitX; + x = 0; } + } - this.container.style.left = `${x}px`; - this.container.style.top = `${y}px`; - this.container.style.width = `${width}px`; - this.container.style.height = `${height}px`; - this.container.style.visibility = 'visible'; - }); + let y = 0; + let height = Math.max(containerHeight, options.general.popupHeight); + const yBelow = elementRect.bottom + options.general.popupOffset; + const yAbove = elementRect.top - options.general.popupOffset; + const overflowBelow = Math.max(yBelow + height - limitY, 0); + const overflowAbove = Math.max(height - yAbove, 0); + if (overflowBelow > 0 || overflowAbove > 0) { + if (overflowBelow < overflowAbove) { + height = Math.max(height - overflowBelow, 0); + y = yBelow; + } else { + height = Math.max(height - overflowAbove, 0); + y = Math.max(yAbove - height, 0); + } + } else { + y = yBelow; + } + + this.container.style.left = `${x}px`; + this.container.style.top = `${y}px`; + this.container.style.width = `${width}px`; + this.container.style.height = `${height}px`; + this.container.style.visibility = 'visible'; } hide() { @@ -95,25 +95,22 @@ class Popup { return this.injected && this.container.style.visibility !== 'hidden'; } - showTermDefs(elementRect, definitions, options, context) { - this.show(elementRect, options).then(() => { - this.invokeApi('showTermDefs', {definitions, options, context}); - }); + async termsShow(elementRect, definitions, options, context) { + await this.show(elementRect, options); + this.invokeApi('termsShow', {definitions, options, context}); } - showKanjiDefs(elementRect, definitions, options, context) { - this.show(elementRect, options).then(() => { - this.invokeApi('showKanjiDefs', {definitions, options, context}); - }); - } - - showOrphaned(elementRect, options) { - this.show(elementRect, options).then(() => { - this.invokeApi('showOrphaned'); - }); + async kanjiShow(elementRect, definitions, options, context) { + await this.show(elementRect, options); + this.invokeApi('termsShow', {definitions, options, context}); } invokeApi(action, params={}) { this.container.contentWindow.postMessage({action, params}, '*'); } + + async onOrphaned(elementRect, options) { + await this.show(elementRect, options); + this.invokeApi('orphaned'); + } } From a202817b987e0af82607d814f775bde26947747a Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 13 Aug 2017 20:50:43 -0700 Subject: [PATCH 52/68] wip --- ext/bg/popup.html | 3 ++- ext/fg/js/display-frame.js | 2 +- ext/fg/js/frontend.js | 7 ++++--- ext/mixed/css/frame.css | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ext/bg/popup.html b/ext/bg/popup.html index 4113b008..60253d6f 100644 --- a/ext/bg/popup.html +++ b/ext/bg/popup.html @@ -35,8 +35,9 @@ - + + diff --git a/ext/fg/js/display-frame.js b/ext/fg/js/display-frame.js index 5ea376c2..e3f3e692 100644 --- a/ext/fg/js/display-frame.js +++ b/ext/fg/js/display-frame.js @@ -19,7 +19,7 @@ window.yomichan_frame = new class extends Display { constructor() { - super($('#spinner'), $('#content')); + super($('#spinner'), $('#definitions')); $(window).on('message', this.onMessage.bind(this)); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index de5fa953..005139e6 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -17,7 +17,7 @@ */ -window.yomichan_frontend = new class { +class Frontend { constructor() { this.popup = new Popup(); this.popupTimer = null; @@ -121,7 +121,7 @@ window.yomichan_frontend = new class { } onResize() { - this.onSearchClear(); + this.searchClear(); } onBgMessage(action, params, sender, callback) { @@ -255,6 +255,7 @@ window.yomichan_frontend = new class { this.lastTextSource = null; } -}(); +} +window.yomichan_frontend = new Frontend(); window.yomichan_frontend.prepare(); diff --git a/ext/mixed/css/frame.css b/ext/mixed/css/frame.css index a0b45fa4..8b1819bd 100644 --- a/ext/mixed/css/frame.css +++ b/ext/mixed/css/frame.css @@ -42,7 +42,7 @@ hr { right: 5px; } -#orphan { +#error-orphaned { display: none; } From 8b50dfe1e9a8be7b8d2a7c69b25bc04babfc1c0c Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 13 Aug 2017 21:11:10 -0700 Subject: [PATCH 53/68] unify files --- ext/bg/background.html | 4 +- ext/bg/js/anki-null.js | 44 -------------- ext/bg/js/{anki-connect.js => anki.js} | 26 ++++++++ ext/bg/js/backend.js | 5 +- ext/fg/js/source-element.js | 77 ------------------------ ext/fg/js/{source-range.js => source.js} | 60 ++++++++++++++++++ ext/manifest.json | 3 +- 7 files changed, 91 insertions(+), 128 deletions(-) delete mode 100644 ext/bg/js/anki-null.js rename ext/bg/js/{anki-connect.js => anki.js} (85%) delete mode 100644 ext/fg/js/source-element.js rename ext/fg/js/{source-range.js => source.js} (82%) diff --git a/ext/bg/background.html b/ext/bg/background.html index fd3b7dd1..90ad9709 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -9,8 +9,7 @@ - - + @@ -20,7 +19,6 @@ - diff --git a/ext/bg/js/anki-null.js b/ext/bg/js/anki-null.js deleted file mode 100644 index d82f0e68..00000000 --- a/ext/bg/js/anki-null.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2016 Alex Yatskov - * Author: Alex Yatskov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -class AnkiNull { - async addNote(note) { - return null; - } - - async canAddNotes(notes) { - return []; - } - - async getDeckNames() { - return []; - } - - async getModelNames() { - return []; - } - - async getModelFieldNames(modelName) { - return []; - } - - async guiBrowse(query) { - return []; - } -} diff --git a/ext/bg/js/anki-connect.js b/ext/bg/js/anki.js similarity index 85% rename from ext/bg/js/anki-connect.js rename to ext/bg/js/anki.js index 80c075fd..f0ec4571 100644 --- a/ext/bg/js/anki-connect.js +++ b/ext/bg/js/anki.js @@ -67,3 +67,29 @@ class AnkiConnect { return requestJson(this.server, 'POST', {action, params, version: this.localVersion}); } } + +class AnkiNull { + async addNote(note) { + return null; + } + + async canAddNotes(notes) { + return []; + } + + async getDeckNames() { + return []; + } + + async getModelNames() { + return []; + } + + async getModelFieldNames(modelName) { + return []; + } + + async guiBrowse(query) { + return []; + } +} diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index f61e9742..e8c9452c 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -17,7 +17,7 @@ */ -window.yomichan_backend = new class { +class Backend { constructor() { this.translator = new Translator(); this.anki = new AnkiNull(); @@ -113,6 +113,7 @@ window.yomichan_backend = new class { return true; } -}; +} +window.yomichan_backend = new Backend(); window.yomichan_backend.prepare(); diff --git a/ext/fg/js/source-element.js b/ext/fg/js/source-element.js deleted file mode 100644 index a8101382..00000000 --- a/ext/fg/js/source-element.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2016 Alex Yatskov - * Author: Alex Yatskov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -class TextSourceElement { - constructor(element, content='') { - this.element = element; - this.content = content; - } - - clone() { - return new TextSourceElement(this.element, this.content); - } - - text() { - return this.content; - } - - setEndOffset(length) { - switch (this.element.nodeName) { - case 'BUTTON': - this.content = this.element.innerHTML; - break; - case 'IMG': - this.content = this.element.getAttribute('alt'); - break; - default: - this.content = this.element.value; - break; - } - - this.content = this.content || ''; - this.content = this.content.substring(0, length); - - return this.content.length; - } - - setStartOffset(length) { - return 0; - } - - containsPoint(point) { - const rect = this.getRect(); - return point.x >= rect.left && point.x <= rect.right; - } - - getRect() { - return this.element.getBoundingClientRect(); - } - - select() { - // NOP - } - - deselect() { - // NOP - } - - equals(other) { - return other.element === this.element && other.content === this.content; - } -} diff --git a/ext/fg/js/source-range.js b/ext/fg/js/source.js similarity index 82% rename from ext/fg/js/source-range.js rename to ext/fg/js/source.js index fa73b0a4..210dda12 100644 --- a/ext/fg/js/source-range.js +++ b/ext/fg/js/source.js @@ -174,3 +174,63 @@ class TextSourceRange { return state.remainder > 0; } } + + +class TextSourceElement { + constructor(element, content='') { + this.element = element; + this.content = content; + } + + clone() { + return new TextSourceElement(this.element, this.content); + } + + text() { + return this.content; + } + + setEndOffset(length) { + switch (this.element.nodeName) { + case 'BUTTON': + this.content = this.element.innerHTML; + break; + case 'IMG': + this.content = this.element.getAttribute('alt'); + break; + default: + this.content = this.element.value; + break; + } + + this.content = this.content || ''; + this.content = this.content.substring(0, length); + + return this.content.length; + } + + setStartOffset(length) { + return 0; + } + + containsPoint(point) { + const rect = this.getRect(); + return point.x >= rect.left && point.x <= rect.right; + } + + getRect() { + return this.element.getBoundingClientRect(); + } + + select() { + // NOP + } + + deselect() { + // NOP + } + + equals(other) { + return other.element === this.element && other.content === this.content; + } +} diff --git a/ext/manifest.json b/ext/manifest.json index 48308b17..ee3bd2ac 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -18,8 +18,7 @@ "fg/js/api.js", "fg/js/document.js", "fg/js/popup.js", - "fg/js/source-element.js", - "fg/js/source-range.js", + "fg/js/source.js", "fg/js/util.js", "fg/js/frontend.js" From 82863cd86156d48b9f18dc10a560bedb82641862 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 14 Aug 2017 19:55:04 -0700 Subject: [PATCH 54/68] renaming files --- ext/bg/{popup.html => context.html} | 2 +- ext/bg/js/{popup.js => context.js} | 0 ext/bg/js/{display-window.js => search.js} | 6 ++++-- ext/bg/search.html | 4 ++-- ext/fg/css/client.css | 2 +- ext/fg/{frame.html => float.html} | 14 +++++++------- ext/fg/js/{display-frame.js => float.js} | 8 +++++--- ext/fg/js/popup.js | 4 ++-- ext/manifest.json | 5 ++--- ext/mixed/css/{frame.css => display.css} | 0 10 files changed, 24 insertions(+), 21 deletions(-) rename ext/bg/{popup.html => context.html} (97%) rename ext/bg/js/{popup.js => context.js} (100%) rename ext/bg/js/{display-window.js => search.js} (94%) rename ext/fg/{frame.html => float.html} (88%) rename ext/fg/js/{display-frame.js => float.js} (94%) rename ext/mixed/css/{frame.css => display.css} (100%) diff --git a/ext/bg/popup.html b/ext/bg/context.html similarity index 97% rename from ext/bg/popup.html rename to ext/bg/context.html index 60253d6f..3828c9fe 100644 --- a/ext/bg/popup.html +++ b/ext/bg/context.html @@ -38,6 +38,6 @@ - + diff --git a/ext/bg/js/popup.js b/ext/bg/js/context.js similarity index 100% rename from ext/bg/js/popup.js rename to ext/bg/js/context.js diff --git a/ext/bg/js/display-window.js b/ext/bg/js/search.js similarity index 94% rename from ext/bg/js/display-window.js rename to ext/bg/js/search.js index cbb96681..87f50c32 100644 --- a/ext/bg/js/display-window.js +++ b/ext/bg/js/search.js @@ -17,7 +17,7 @@ */ -window.yomichan_window = new class extends Display { +class DisplaySearch extends Display { constructor() { super($('#spinner'), $('#content')); @@ -51,4 +51,6 @@ window.yomichan_window = new class extends Display { this.onError(e); } } -}; +} + +window.yomichan_search = new DisplaySearch(); diff --git a/ext/bg/search.html b/ext/bg/search.html index fe44d74e..d10530f9 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -5,7 +5,7 @@ Yomichan Search - +

    @@ -45,6 +45,6 @@ - + diff --git a/ext/fg/css/client.css b/ext/fg/css/client.css index 9f566480..b5b1f6bd 100644 --- a/ext/fg/css/client.css +++ b/ext/fg/css/client.css @@ -17,7 +17,7 @@ */ -iframe#yomichan-popup { +iframe#yomichan-float { all: initial; background-color: #fff; border: 1px solid #999; diff --git a/ext/fg/frame.html b/ext/fg/float.html similarity index 88% rename from ext/fg/frame.html rename to ext/fg/float.html index dda3ef06..a3b66c92 100644 --- a/ext/fg/frame.html +++ b/ext/fg/float.html @@ -5,7 +5,7 @@ - +
    Kunyomi:\n" + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.kunyomi : depth0),{"name":"each","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") @@ -332,7 +332,7 @@ templates['kanji.html'] = template({"1":function(container,depth0,helpers,partia + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.debug : depth0),{"name":"if","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "\n"; },"2":function(container,depth0,helpers,partials,data) { - return " \n"; + return " \n \n"; },"4":function(container,depth0,helpers,partials,data) { return " \n"; },"6":function(container,depth0,helpers,partials,data) { @@ -486,10 +486,10 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia },"12":function(container,depth0,helpers,partials,data) { var stack1, alias1=depth0 != null ? depth0 : {}; - return "
    \n
    \n \n" + return "
    \n
    \n" + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.addable : depth0),{"name":"if","hash":{},"fn":container.program(13, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.playback : depth0),{"name":"if","hash":{},"fn":container.program(15, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + "
    \n\n" + + " \n
    \n\n" + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.reading : depth0),{"name":"if","hash":{},"fn":container.program(17, data, 0),"inverse":container.program(20, data, 0),"data":data})) != null ? stack1 : "") + "\n" + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.reasons : depth0),{"name":"if","hash":{},"fn":container.program(22, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") @@ -499,7 +499,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.debug : depth0),{"name":"if","hash":{},"fn":container.program(34, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "
    \n"; },"13":function(container,depth0,helpers,partials,data) { - return " \n \n"; + return " \n \n \n"; },"15":function(container,depth0,helpers,partials,data) { return " \n"; },"17":function(container,depth0,helpers,partials,data) { diff --git a/ext/bg/js/yomichan.js b/ext/bg/js/yomichan.js index b4beb179..e0bdabc3 100644 --- a/ext/bg/js/yomichan.js +++ b/ext/bg/js/yomichan.js @@ -157,6 +157,10 @@ window.yomichan = new class { }); } + noteView(noteId) { + return this.anki.guiBrowse(`nid:${noteId}`); + } + templateRender(template, data) { return Promise.resolve(handlebarsRender(template, data)); } @@ -211,6 +215,10 @@ window.yomichan = new class { definitionsAddable: ({definitions, modes, callback}) => { promiseCallback(this.definitionsAddable(definitions, modes), callback); + }, + + noteView: ({noteId}) => { + promiseCallback(this.noteView(noteId), callback); } }; diff --git a/ext/fg/js/display-frame.js b/ext/fg/js/display-frame.js index 9fd09e74..b29a0379 100644 --- a/ext/fg/js/display-frame.js +++ b/ext/fg/js/display-frame.js @@ -31,6 +31,10 @@ window.displayFrame = new class extends Display { return bgDefinitionsAddable(definitions, modes); } + noteView(noteId) { + return bgNoteView(noteId); + } + templateRender(template, data) { return bgTemplateRender(template, data); } diff --git a/ext/fg/js/util.js b/ext/fg/js/util.js index c6270ce6..e1b0e080 100644 --- a/ext/fg/js/util.js +++ b/ext/fg/js/util.js @@ -62,6 +62,9 @@ function bgDefinitionAdd(definition, mode) { return bgInvoke('definitionAdd', {definition, mode}); } +function bgNoteView(noteId) { + return bgInvoke('noteView', {noteId}); +} /* * Document diff --git a/ext/mixed/img/view-note.png b/ext/mixed/img/view-note.png new file mode 100644 index 0000000000000000000000000000000000000000..7d863f949741ff83fd8373a77c0d95a3d95e441f GIT binary patch literal 622 zcmV-!0+IcRP)YeaZ-G+53gSTz{SPWVdFiPaPX+$~@n)fi9>qgJ zh4fN-QcEhq^wI<|vImjml9)pF*W2zl+qWbTPaSyKyqWKt`DS)j3xa?yVgf%ewhl|m zA$_0x^Rx4E@d&=>uRoSN*Cm&aL#`7&zr0;L(pKMn1G#$ZszFkMC^?B8f`0w&-UD$u zd-(?iK6#!;xZ`>J^A9DGiTfguewDOKwDEKoL}`ZE_M}0=xvsgf$usmJeV$oo28d0#yu!pdgpag$giC zhlKP!CMX3wsPMGgZ5n>Xg+hV--ENofAcH61#0B7Hvl-7Il}g2A6;lfG`FxbX8A>p$ z0P;GW4il9Mr9jO9)xB^Rgy%*CSTdQ6D;kDDnM{U{5Q21FA4#ZM71$_Dba8z<-Y_Na zS@|v#c0PZNDux9AoXD-ZoWP~q)7QrC`G { - if (states === null || sequence !== this.sequence) { + if (!states || sequence !== this.sequence) { return; } @@ -211,6 +216,13 @@ class Display { this.noteAdd(this.definitions[index], link.data('mode')); } + onViewNote(e) { + e.preventDefault(); + const link = $(e.currentTarget); + const index = Display.entryIndexFind(link); + this.noteView(link.data('noteId')); + } + onKeyDown(e) { const noteTryAdd = mode => { const button = Display.adderButtonFind(this.index, mode); @@ -219,6 +231,13 @@ class Display { } }; + const noteTryView = mode => { + const button = Display.viewerButtonFind(this.index); + if (button.length !== 0 && !button.hasClass('disabled')) { + this.noteView(button.data('noteId')); + } + }; + const handlers = { 27: /* escape */ () => { this.clearSearch(); @@ -303,6 +322,12 @@ class Display { return true; } + }, + + 86: /* v */ () => { + if (e.altKey) { + noteTryView(); + } } }; @@ -326,10 +351,11 @@ class Display { noteAdd(definition, mode) { this.spinner.show(); - return this.definitionAdd(definition, mode).then(success => { - if (success) { + return this.definitionAdd(definition, mode).then(noteId => { + if (noteId) { const index = this.definitions.indexOf(definition); Display.adderButtonFind(index, mode).addClass('disabled'); + Display.viewerButtonFind(index).removeClass('pending disabled').data('noteId', noteId); } else { this.handleError('note could not be added'); } @@ -375,4 +401,8 @@ class Display { static adderButtonFind(index, mode) { return $('.entry').eq(index).find(`.action-add-note[data-mode="${mode}"]`); } + + static viewerButtonFind(index) { + return $('.entry').eq(index).find('.action-view-note'); + } } diff --git a/tmpl/kanji.html b/tmpl/kanji.html index 28e4b8a4..bb62f488 100644 --- a/tmpl/kanji.html +++ b/tmpl/kanji.html @@ -1,13 +1,14 @@ {{#*inline "kanji"}}
    - {{#if addable}} + {{/if}} {{#if source}} {{/if}} +
    {{character}}
    diff --git a/tmpl/terms.html b/tmpl/terms.html index bf656cfb..db36e5cc 100644 --- a/tmpl/terms.html +++ b/tmpl/terms.html @@ -20,14 +20,15 @@ {{#*inline "term"}}
    - {{#if addable}} + {{/if}} {{#if playback}} {{/if}} +
    {{#if reading}} From b3984ccd54340195fc352033f61d33e4b5f492ea Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 9 Jul 2017 15:23:11 -0700 Subject: [PATCH 04/68] cleanup --- ext/bg/js/database.js | 14 +++++++------- ext/bg/js/options.js | 2 +- ext/fg/js/util.js | 18 +++++++----------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 70aeb0d7..400ebd6a 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -35,7 +35,7 @@ class Database { } prepare() { - if (this.db !== null) { + if (!this.db) { return Promise.reject('database already initialized'); } @@ -53,7 +53,7 @@ class Database { } purge() { - if (this.db === null) { + if (!this.db) { return Promise.reject('database not initialized'); } @@ -66,7 +66,7 @@ class Database { } findTerms(term, dictionaries) { - if (this.db === null) { + if (!this.db) { return Promise.reject('database not initialized'); } @@ -96,7 +96,7 @@ class Database { } findKanji(kanji, dictionaries) { - if (this.db === null) { + if (!this.db) { return Promise.reject('database not initialized'); } @@ -124,7 +124,7 @@ class Database { } cacheTagMeta(dictionaries) { - if (this.db === null) { + if (!this.db) { return Promise.reject('database not initialized'); } @@ -148,7 +148,7 @@ class Database { } getDictionaries() { - if (this.db === null) { + if (!this.db) { return Promise.reject('database not initialized'); } @@ -156,7 +156,7 @@ class Database { } importDictionary(archive, callback) { - if (this.db === null) { + if (!this.db) { return Promise.reject('database not initialized'); } diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 5aa18366..728ddae4 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -337,7 +337,7 @@ function ankiFieldsPopulate(element, options) { const container = tab.find('tbody').empty(); const modelName = element.val(); - if (modelName === null) { + if (!modelName) { return Promise.resolve(); } diff --git a/ext/fg/js/util.js b/ext/fg/js/util.js index e1b0e080..2acd81c4 100644 --- a/ext/fg/js/util.js +++ b/ext/fg/js/util.js @@ -117,7 +117,7 @@ function docImposterDestroy() { function docRangeFromPoint(point) { const element = document.elementFromPoint(point.x, point.y); - if (element !== null) { + if (element) { if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') { return new TextSourceElement(element); } else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { @@ -128,23 +128,19 @@ function docRangeFromPoint(point) { if (!document.caretRangeFromPoint) { document.caretRangeFromPoint = (x, y) => { const position = document.caretPositionFromPoint(x,y); - if (position === null) { - return null; + if (position) { + const range = document.createRange(); + range.setStart(position.offsetNode, position.offset); + range.setEnd(position.offsetNode, position.offset); + return range; } - - const range = document.createRange(); - range.setStart(position.offsetNode, position.offset); - range.setEnd(position.offsetNode, position.offset); - return range; }; } const range = document.caretRangeFromPoint(point.x, point.y); - if (range !== null) { + if (range) { return new TextSourceRange(range); } - - return null; } function docSentenceExtract(source, extent) { From 6e986bf1f5957be12ad610c627060f4d86eca98c Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 9 Jul 2017 16:29:52 -0700 Subject: [PATCH 05/68] cleanup --- ext/bg/js/database.js | 2 +- ext/bg/js/translator.js | 2 +- ext/bg/js/util.js | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 400ebd6a..69a3bbdd 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -35,7 +35,7 @@ class Database { } prepare() { - if (!this.db) { + if (this.db) { return Promise.reject('database already initialized'); } diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index 8d65a0cd..f1858247 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -53,7 +53,7 @@ class Translator { } return this.findTermsDeinflected(text, titles, cache).then(deinfLiteral => { - const textHiragana = wanakana._katakanaToHiragana(text); + const textHiragana = jpKatakanaToHiragana(text); if (text === textHiragana) { return deinfLiteral; } else { diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index cdd5ec31..b39b4b2f 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -43,6 +43,10 @@ function jpIsKana(c) { return wanakana.isKana(c); } +function jpKatakanaToHiragana(text) { + return wanakana._katakanaToHiragana(text); +} + /* * Commands From f49a69c993425cce57aea16e19adab378f8cbba9 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 13:16:24 -0700 Subject: [PATCH 06/68] move database to use async --- ext/bg/js/database.js | 198 +++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 101 deletions(-) diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 69a3bbdd..06312438 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -20,58 +20,62 @@ class Database { constructor() { this.db = null; - this.dbVersion = 2; - this.tagMetaCache = {}; + this.version = 2; + this.tagCache = {}; } - sanitize() { - const db = new Dexie('dict'); - return db.open().then(() => { + async sanitize() { + try { + const db = new Dexie('dict'); + await db.open(); db.close(); - if (db.verno !== this.dbVersion) { - return db.delete(); + if (db.verno !== this.version) { + await db.delete(); } - }).catch(() => {}); + } + catch(error) { + // NOP + } } - prepare() { + async prepare() { if (this.db) { - return Promise.reject('database already initialized'); + throw 'database already initialized'; } - return this.sanitize().then(() => { - this.db = new Dexie('dict'); - this.db.version(this.dbVersion).stores({ - terms: '++id,dictionary,expression,reading', - kanji: '++,dictionary,character', - tagMeta: '++,dictionary', - dictionaries: '++,title,version' - }); + await this.sanitize(); - return this.db.open(); + this.db = new Dexie('dict'); + this.db.version(this.version).stores({ + terms: '++id,dictionary,expression,reading', + kanji: '++,dictionary,character', + tagMeta: '++,dictionary', + dictionaries: '++,title,version' }); + + await this.db.open(); } - purge() { + async purge() { if (!this.db) { - return Promise.reject('database not initialized'); + throw 'database not initialized'; } this.db.close(); - return this.db.delete().then(() => { - this.db = null; - this.tagMetaCache = {}; - return this.prepare(); - }); + await this.db.delete(); + this.db = null; + this.tagCache = {}; + + await this.prepare(); } - findTerms(term, dictionaries) { + async findTerms(term, dictionaries) { if (!this.db) { - return Promise.reject('database not initialized'); + throw 'database not initialized'; } const results = []; - return this.db.terms.where('expression').equals(term).or('reading').equals(term).each(row => { + await this.db.terms.where('expression').equals(term).or('reading').equals(term).each(row => { if (dictionaries.includes(row.dictionary)) { results.push({ expression: row.expression, @@ -84,24 +88,23 @@ class Database { id: row.id }); } - }).then(() => { - return this.cacheTagMeta(dictionaries); - }).then(() => { - for (const result of results) { - result.tagMeta = this.tagMetaCache[result.dictionary] || {}; - } - - return results; }); + + await this.cacheTagMeta(dictionaries); + for (const result of results) { + result.tagMeta = this.tagCache[result.dictionary] || {}; + } + + return results; } - findKanji(kanji, dictionaries) { + async findKanji(kanji, dictionaries) { if (!this.db) { return Promise.reject('database not initialized'); } const results = []; - return this.db.kanji.where('character').equals(kanji).each(row => { + await this.db.kanji.where('character').equals(kanji).each(row => { if (dictionaries.includes(row.dictionary)) { results.push({ character: row.character, @@ -112,83 +115,79 @@ class Database { dictionary: row.dictionary }); } - }).then(() => { - return this.cacheTagMeta(dictionaries); - }).then(() => { - for (const result of results) { - result.tagMeta = this.tagMetaCache[result.dictionary] || {}; - } - - return results; }); - } - cacheTagMeta(dictionaries) { - if (!this.db) { - return Promise.reject('database not initialized'); + await this.cacheTagMeta(dictionaries); + for (const result of results) { + result.tagMeta = this.tagCache[result.dictionary] || {}; + } + + return results; + } + + async cacheTagMeta(dictionaries) { + if (!this.db) { + throw 'database not initialized'; } - const promises = []; for (const dictionary of dictionaries) { - if (this.tagMetaCache[dictionary]) { - continue; - } - - const tagMeta = {}; - promises.push( - this.db.tagMeta.where('dictionary').equals(dictionary).each(row => { + if (!this.tagCache[dictionary]) { + const tagMeta = {}; + await this.db.tagMeta.where('dictionary').equals(dictionary).each(row => { tagMeta[row.name] = {category: row.category, notes: row.notes, order: row.order}; - }).then(() => { - this.tagMetaCache[dictionary] = tagMeta; - }) - ); - } + }); - return Promise.all(promises); + this.tagCache[dictionary] = tagMeta; + } + } } - getDictionaries() { + async getDictionaries() { if (!this.db) { - return Promise.reject('database not initialized'); + throw 'database not initialized'; } return this.db.dictionaries.toArray(); } - importDictionary(archive, callback) { + async importDictionary(archive, callback) { if (!this.db) { return Promise.reject('database not initialized'); } let summary = null; - const indexLoaded = (title, version, revision, tagMeta, hasTerms, hasKanji) => { + const indexLoaded = async (title, version, revision, tagMeta, hasTerms, hasKanji) => { summary = {title, version, revision, hasTerms, hasKanji}; - return this.db.dictionaries.where('title').equals(title).count().then(count => { - if (count > 0) { - return Promise.reject(`dictionary "${title}" is already imported`); - } - return this.db.dictionaries.add({title, version, revision, hasTerms, hasKanji}).then(() => { - const rows = []; - for (const tag in tagMeta || {}) { - const meta = tagMeta[tag]; - const row = dictTagSanitize({ - name: tag, - category: meta.category, - notes: meta.notes, - order: meta.order, - dictionary: title - }); + const count = await this.db.dictionaries.where('title').equals(title).count(); + if (count > 0) { + throw `dictionary "${title}" is already imported`; + } - rows.push(row); - } + await this.db.dictionaries.add({title, version, revision, hasTerms, hasKanji}); - return this.db.tagMeta.bulkAdd(rows); + const rows = []; + for (const tag in tagMeta || {}) { + const meta = tagMeta[tag]; + const row = dictTagSanitize({ + name: tag, + category: meta.category, + notes: meta.notes, + order: meta.order, + dictionary: title }); - }); + + rows.push(row); + } + + await this.db.tagMeta.bulkAdd(rows); }; - const termsLoaded = (title, entries, total, current) => { + const termsLoaded = async (title, entries, total, current) => { + if (callback) { + callback(total, current); + } + const rows = []; for (const [expression, reading, tags, rules, score, ...glossary] of entries) { rows.push({ @@ -202,14 +201,14 @@ class Database { }); } - return this.db.terms.bulkAdd(rows).then(() => { - if (callback) { - callback(total, current); - } - }); + await this.db.terms.bulkAdd(rows); }; - const kanjiLoaded = (title, entries, total, current) => { + const kanjiLoaded = async (title, entries, total, current) => { + if (callback) { + callback(total, current); + } + const rows = []; for (const [character, onyomi, kunyomi, tags, ...meanings] of entries) { rows.push({ @@ -222,13 +221,10 @@ class Database { }); } - return this.db.kanji.bulkAdd(rows).then(() => { - if (callback) { - callback(total, current); - } - }); + await this.db.kanji.bulkAdd(rows); }; - return zipLoadDb(archive, indexLoaded, termsLoaded, kanjiLoaded).then(() => summary); + await zipLoadDb(archive, indexLoaded, termsLoaded, kanjiLoaded); + return summary; } } From b6f3919ef63f969769fbf030e8fd8b14b0e1c214 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 14:10:58 -0700 Subject: [PATCH 07/68] move translator to async --- ext/bg/js/database.js | 28 +++---- ext/bg/js/translator.js | 180 +++++++++++++++++----------------------- 2 files changed, 90 insertions(+), 118 deletions(-) diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 06312438..9eed8ea3 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -69,14 +69,14 @@ class Database { await this.prepare(); } - async findTerms(term, dictionaries) { + async findTerms(term, titles) { if (!this.db) { throw 'database not initialized'; } const results = []; await this.db.terms.where('expression').equals(term).or('reading').equals(term).each(row => { - if (dictionaries.includes(row.dictionary)) { + if (titles.includes(row.dictionary)) { results.push({ expression: row.expression, reading: row.reading, @@ -90,7 +90,7 @@ class Database { } }); - await this.cacheTagMeta(dictionaries); + await this.cacheTagMeta(titles); for (const result of results) { result.tagMeta = this.tagCache[result.dictionary] || {}; } @@ -98,14 +98,14 @@ class Database { return results; } - async findKanji(kanji, dictionaries) { + async findKanji(kanji, titles) { if (!this.db) { return Promise.reject('database not initialized'); } const results = []; await this.db.kanji.where('character').equals(kanji).each(row => { - if (dictionaries.includes(row.dictionary)) { + if (titles.includes(row.dictionary)) { results.push({ character: row.character, onyomi: dictFieldSplit(row.onyomi), @@ -117,7 +117,7 @@ class Database { } }); - await this.cacheTagMeta(dictionaries); + await this.cacheTagMeta(titles); for (const result of results) { result.tagMeta = this.tagCache[result.dictionary] || {}; } @@ -125,29 +125,29 @@ class Database { return results; } - async cacheTagMeta(dictionaries) { + async cacheTagMeta(titles) { if (!this.db) { throw 'database not initialized'; } - for (const dictionary of dictionaries) { - if (!this.tagCache[dictionary]) { + for (const title of titles) { + if (!this.tagCache[title]) { const tagMeta = {}; - await this.db.tagMeta.where('dictionary').equals(dictionary).each(row => { + await this.db.tagMeta.where('dictionary').equals(title).each(row => { tagMeta[row.name] = {category: row.category, notes: row.notes, order: row.order}; }); - this.tagCache[dictionary] = tagMeta; + this.tagCache[title] = tagMeta; } } } async getDictionaries() { - if (!this.db) { + if (this.db) { + return this.db.dictionaries.toArray(); + } else { throw 'database not initialized'; } - - return this.db.dictionaries.toArray(); } async importDictionary(archive, callback) { diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index f1858247..35b49608 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -19,135 +19,107 @@ class Translator { constructor() { - this.loaded = false; - this.ruleMeta = null; this.database = new Database(); this.deinflector = new Deinflector(); + this.loaded = false; } - prepare() { - if (this.loaded) { - return Promise.resolve(); - } - - const promises = [ - jsonLoadInt('/bg/lang/deinflect.json'), - this.database.prepare() - ]; - - return Promise.all(promises).then(([reasons]) => { + async prepare() { + if (!this.loaded) { + const reasons = await jsonLoadInt('/bg/lang/deinflect.json'); this.deinflector.setReasons(reasons); + await this.database.prepare(); this.loaded = true; - }); + } } - findTerms(text, dictionaries, alphanumeric) { - const titles = Object.keys(dictionaries); - const cache = {}; + async findTermsGrouped(text, dictionaries, alphanumeric) { + const {length, definitions} = await this.findTerms(text, dictionaries, alphanumeric); + return {length, definitions: dictTermsGroup(definitions, dictionaries)}; + } + async findTerms(text, dictionaries, alphanumeric) { if (!alphanumeric && text.length > 0) { const c = text[0]; if (!jpIsKana(c) && !jpIsKanji(c)) { - return Promise.resolve({length: 0, definitions: []}); + return {length: 0, definitions: []}; } } - return this.findTermsDeinflected(text, titles, cache).then(deinfLiteral => { - const textHiragana = jpKatakanaToHiragana(text); - if (text === textHiragana) { - return deinfLiteral; - } else { - return this.findTermsDeinflected(textHiragana, titles, cache).then(deinfHiragana => deinfLiteral.concat(deinfHiragana)); - } - }).then(deinflections => { - let definitions = []; - for (const deinflection of deinflections) { - for (const definition of deinflection.definitions) { - const tags = definition.tags.map(tag => dictTagBuild(tag, definition.tagMeta)); - tags.push(dictTagBuildSource(definition.dictionary)); - definitions.push({ - source: deinflection.source, - reasons: deinflection.reasons, - score: definition.score, - id: definition.id, - dictionary: definition.dictionary, - expression: definition.expression, - reading: definition.reading, - glossary: definition.glossary, - tags: dictTagsSort(tags) - }); - } - } - - definitions = dictTermsUndupe(definitions); - definitions = dictTermsSort(definitions, dictionaries); - - let length = 0; - for (const definition of definitions) { - length = Math.max(length, definition.source.length); - } - - return {length, definitions}; - }); - } - - findTermsGrouped(text, dictionaries, alphanumeric) { - return this.findTerms(text, dictionaries, alphanumeric).then(({length, definitions}) => { - return {length, definitions: dictTermsGroup(definitions, dictionaries)}; - }); - } - - findKanji(text, dictionaries) { + const cache = {}; const titles = Object.keys(dictionaries); - const processed = {}; - const promises = []; + let deinflections = await this.findTermsDeinflected(text, titles, cache); + const textHiragana = jpKatakanaToHiragana(text); + if (text !== textHiragana) { + deinflections = deinflections.concat(await this.findTermsDeinflected(textHiragana, titles, cache)); + } + let definitions = []; + for (const deinflection of deinflections) { + for (const definition of deinflection.definitions) { + const tags = definition.tags.map(tag => dictTagBuild(tag, definition.tagMeta)); + tags.push(dictTagBuildSource(definition.dictionary)); + definitions.push({ + source: deinflection.source, + reasons: deinflection.reasons, + score: definition.score, + id: definition.id, + dictionary: definition.dictionary, + expression: definition.expression, + reading: definition.reading, + glossary: definition.glossary, + tags: dictTagsSort(tags) + }); + } + } + + definitions = dictTermsUndupe(definitions); + definitions = dictTermsSort(definitions, dictionaries); + + let length = 0; + for (const definition of definitions) { + length = Math.max(length, definition.source.length); + } + + return {length, definitions}; + } + + async findTermsDeinflected(text, dictionaries, cache) { + await this.prepare(); + + const definer = async term => { + if (cache.hasOwnProperty(term)) { + return cache[term]; + } else { + return cache[term] = await this.database.findTerms(term, dictionaries); + } + }; + + let deinflections = []; + for (let i = text.length; i > 0; --i) { + const textSlice = text.slice(0, i); + deinflections = deinflections.concat(await this.deinflector.deinflect(textSlice, definer)); + } + + return deinflections; + } + + async findKanji(text, dictionaries) { + await this.prepare(); + + let definitions = []; + const processed = {}; + const titles = Object.keys(dictionaries); for (const c of text) { if (!processed[c]) { - promises.push(this.database.findKanji(c, titles)); + definitions = definitions.concat(await this.database.findKanji(c, titles)); processed[c] = true; } } - return Promise.all(promises).then(defSets => { - const definitions = defSets.reduce((a, b) => a.concat(b), []); - for (const definition of definitions) { - const tags = definition.tags.map(tag => dictTagBuild(tag, definition.tagMeta)); - tags.push(dictTagBuildSource(definition.dictionary)); - definition.tags = dictTagsSort(tags); - } - - return definitions; - }); - } - - findTermsDeinflected(text, dictionaries, cache) { - const definer = term => { - if (cache.hasOwnProperty(term)) { - return Promise.resolve(cache[term]); - } - - return this.database.findTerms(term, dictionaries).then(definitions => cache[term] = definitions); - }; - - const promises = []; - for (let i = text.length; i > 0; --i) { - promises.push(this.deinflector.deinflect(text.slice(0, i), definer)); - } - - return Promise.all(promises).then(results => { - let deinflections = []; - for (const result of results) { - deinflections = deinflections.concat(result); - } - - return deinflections; - }); - } - - processKanji(definitions) { for (const definition of definitions) { const tags = definition.tags.map(tag => dictTagBuild(tag, definition.tagMeta)); + tags.push(dictTagBuildSource(definition.dictionary)); definition.tags = dictTagsSort(tags); } From 49352c5fa1baea4a6ed7d71d1353c13a56b00bca Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 14:30:34 -0700 Subject: [PATCH 08/68] move deinflector to async --- ext/bg/js/deinflector.js | 64 ++++++++++++++++++---------------------- ext/bg/js/translator.js | 16 +++++----- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/ext/bg/js/deinflector.js b/ext/bg/js/deinflector.js index 6484f953..8b67761b 100644 --- a/ext/bg/js/deinflector.js +++ b/ext/bg/js/deinflector.js @@ -26,26 +26,7 @@ class Deinflection { this.children = []; } - deinflect(definer, reasons) { - const define = () => { - return definer(this.term).then(definitions => { - if (this.rules.length === 0) { - this.definitions = definitions; - } else { - for (const rule of this.rules) { - for (const definition of definitions) { - if (definition.rules.includes(rule)) { - this.definitions.push(definition); - } - } - } - } - - return this.definitions.length > 0; - }); - }; - - const promises = []; + async deinflect(definer, reasons) { for (const reason in reasons) { for (const variant of reasons[reason]) { let accept = this.rules.length === 0; @@ -68,20 +49,31 @@ class Deinflection { } const child = new Deinflection(term, {reason, rules: variant.rulesOut}); - promises.push( - child.deinflect(definer, reasons).then(valid => valid && this.children.push(child)) - ); + if (await child.deinflect(definer, reasons)) { + this.children.push(child); + } } } - return Promise.all(promises).then(define).then(valid => { - if (valid && this.children.length > 0) { - const child = new Deinflection(this.term, {rules: this.rules, definitions: this.definitions}); - this.children.push(child); + const definitions = await definer(this.term); + if (this.rules.length === 0) { + this.definitions = definitions; + } else { + for (const rule of this.rules) { + for (const definition of definitions) { + if (definition.rules.includes(rule)) { + this.definitions.push(definition); + } + } } + } - return valid || this.children.length > 0; - }); + if (this.definitions.length > 0 && this.children.length > 0) { + const child = new Deinflection(this.term, {rules: this.rules, definitions: this.definitions}); + this.children.push(child); + } + + return this.definitions.length > 0 || this.children.length > 0; } gather() { @@ -112,16 +104,16 @@ class Deinflection { class Deinflector { - constructor() { - this.reasons = {}; - } - - setReasons(reasons) { + constructor(reasons) { this.reasons = reasons; } - deinflect(term, definer) { + async deinflect(term, definer) { const node = new Deinflection(term); - return node.deinflect(definer, this.reasons).then(success => success ? node.gather() : []); + if (await node.deinflect(definer, this.reasons)) { + return node.gather(); + } else { + return []; + } } } diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index 35b49608..84a6e1d7 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -19,17 +19,19 @@ class Translator { constructor() { - this.database = new Database(); - this.deinflector = new Deinflector(); - this.loaded = false; + this.database = null; + this.deinflector = null; } async prepare() { - if (!this.loaded) { - const reasons = await jsonLoadInt('/bg/lang/deinflect.json'); - this.deinflector.setReasons(reasons); + if (!this.database) { + this.database = new Database(); await this.database.prepare(); - this.loaded = true; + } + + if (!this.deinflector) { + const reasons = await jsonLoadInt('/bg/lang/deinflect.json'); + this.deinflector = new Deinflector(reasons); } } From f694026827ab2ce3a884206f7494b98335137709 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 14:53:06 -0700 Subject: [PATCH 09/68] move zip import to async --- ext/bg/js/database.js | 48 +++++++++++++++++++++++++++++++- ext/bg/js/util.js | 64 ------------------------------------------- 2 files changed, 47 insertions(+), 65 deletions(-) diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 9eed8ea3..45ba3d08 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -224,7 +224,53 @@ class Database { await this.db.kanji.bulkAdd(rows); }; - await zipLoadDb(archive, indexLoaded, termsLoaded, kanjiLoaded); + await Database.importDictionaryZip(archive, indexLoaded, termsLoaded, kanjiLoaded); return summary; } + + static async importDictionaryZip(archive, indexLoaded, termsLoaded, kanjiLoaded) { + const files = (await JSZip.loadAsync(archive)).files; + + const indexFile = files['index.json']; + if (!indexFile) { + throw 'no dictionary index found in archive'; + } + + const index = JSON.parse(await indexFile.async('string')); + if (!index.title || !index.version || !index.revision) { + throw 'unrecognized dictionary format'; + } + + await indexLoaded( + index.title, + index.version, + index.revision, + index.tagMeta || {}, + index.termBanks > 0, + index.kanjiBanks > 0 + ); + + const banksTotal = index.termBanks + index.kanjiBanks; + let banksLoaded = 0; + + for (let i = 1; i <= index.termBanks; ++i) { + const bankFile = files[`term_bank_${i}.json`]; + if (bankFile) { + const bank = JSON.parse(await bankFile.async('string')); + await termsLoaded(index.title, bank, banksTotal, banksLoaded++); + } else { + throw 'missing term bank file'; + } + } + + for (let i = 1; i <= index.kanjiBanks; ++i) { + const bankFile = files[`kanji_bank_${i}.json`]; + if (bankFile) { + const bank = JSON.parse(await bankFile.async('string')); + await kanjiLoaded(index.title, bank, banksTotal, banksLoaded++); + } else { + throw 'missing kanji bank file'; + } + } + } } diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index b39b4b2f..1954e83b 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -455,70 +455,6 @@ function jsonLoadInt(url) { return jsonLoad(chrome.extension.getURL(url)); } -/* - * Zip - */ - -function zipLoadDb(archive, indexLoaded, termsLoaded, kanjiLoaded) { - return JSZip.loadAsync(archive).then(files => files.files).then(files => { - const indexFile = files['index.json']; - if (!indexFile) { - return Promise.reject('no dictionary index found in archive'); - } - - return indexFile.async('string').then(indexJson => { - const index = JSON.parse(indexJson); - if (!index.title || !index.version || !index.revision) { - return Promise.reject('unrecognized dictionary format'); - } - - return indexLoaded( - index.title, - index.version, - index.revision, - index.tagMeta || {}, - index.termBanks > 0, - index.kanjiBanks > 0 - ).then(() => index); - }).then(index => { - const loaders = []; - const banksTotal = index.termBanks + index.kanjiBanks; - let banksLoaded = 0; - - for (let i = 1; i <= index.termBanks; ++i) { - const bankFile = files[`term_bank_${i}.json`]; - if (!bankFile) { - return Promise.reject('missing term bank file'); - } - - loaders.push(() => bankFile.async('string').then(bankJson => { - const bank = JSON.parse(bankJson); - return termsLoaded(index.title, bank, banksTotal, banksLoaded++); - })); - } - - for (let i = 1; i <= index.kanjiBanks; ++i) { - const bankFile = files[`kanji_bank_${i}.json`]; - if (!bankFile) { - return Promise.reject('missing kanji bank file'); - } - - loaders.push(() => bankFile.async('string').then(bankJson => { - const bank = JSON.parse(bankJson); - return kanjiLoaded(index.title, bank, banksTotal, banksLoaded++); - })); - } - - let chain = Promise.resolve(); - for (const loader of loaders) { - chain = chain.then(loader); - } - - return chain; - }); - }); -} - /* * Helpers */ From 28bc1449d1b2260df2970318982385b0d8456c54 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 15:00:38 -0700 Subject: [PATCH 10/68] cleanup --- ext/bg/js/translator.js | 22 +++++++++++++++++++++- ext/bg/js/util.js | 26 -------------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index 84a6e1d7..dfe54623 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -30,7 +30,8 @@ class Translator { } if (!this.deinflector) { - const reasons = await jsonLoadInt('/bg/lang/deinflect.json'); + const url = chrome.extension.getURL('/bg/lang/deinflect.json'); + const reasons = await Translator.loadRules(url); this.deinflector = new Deinflector(reasons); } } @@ -127,4 +128,23 @@ class Translator { return definitions; } + + + static loadRules(url) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.overrideMimeType('application/json'); + xhr.addEventListener('load', () => resolve(xhr.responseText)); + xhr.addEventListener('error', () => reject('failed to execute network request')); + xhr.open('GET', url); + xhr.send(); + }).then(responseText => { + try { + return JSON.parse(responseText); + } + catch (e) { + return Promise.reject('invalid JSON response'); + } + }); + } } diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 1954e83b..b8a60217 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -429,32 +429,6 @@ function dictFieldFormat(field, definition, mode, options) { } -/* - * Json - */ - -function jsonLoad(url) { - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.overrideMimeType('application/json'); - xhr.addEventListener('load', () => resolve(xhr.responseText)); - xhr.addEventListener('error', () => reject('failed to execute network request')); - xhr.open('GET', url); - xhr.send(); - }).then(responseText => { - try { - return JSON.parse(responseText); - } - catch (e) { - return Promise.reject('invalid JSON response'); - } - }); -} - -function jsonLoadInt(url) { - return jsonLoad(chrome.extension.getURL(url)); -} - /* * Helpers */ From a73b8fbab747fc5d7d2802b88d4007421cda4d87 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 15:20:07 -0700 Subject: [PATCH 11/68] cleanup --- ext/bg/js/translator.js | 9 ++------- ext/bg/js/yomichan.js | 5 ++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index dfe54623..aa11ea63 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -87,14 +87,12 @@ class Translator { return {length, definitions}; } - async findTermsDeinflected(text, dictionaries, cache) { - await this.prepare(); - + async findTermsDeinflected(text, titles, cache) { const definer = async term => { if (cache.hasOwnProperty(term)) { return cache[term]; } else { - return cache[term] = await this.database.findTerms(term, dictionaries); + return cache[term] = await this.database.findTerms(term, titles); } }; @@ -108,8 +106,6 @@ class Translator { } async findKanji(text, dictionaries) { - await this.prepare(); - let definitions = []; const processed = {}; const titles = Object.keys(dictionaries); @@ -129,7 +125,6 @@ class Translator { return definitions; } - static loadRules(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); diff --git a/ext/bg/js/yomichan.js b/ext/bg/js/yomichan.js index e0bdabc3..acc560ce 100644 --- a/ext/bg/js/yomichan.js +++ b/ext/bg/js/yomichan.js @@ -25,9 +25,12 @@ window.yomichan = new class { this.anki = new AnkiNull(); this.options = null; - this.translator.prepare().then(optionsLoad).then(this.optionsSet.bind(this)).then(() => { + this.translator.prepare().then(optionsLoad).then(options => { + this.optionsSet(options); + chrome.commands.onCommand.addListener(this.onCommand.bind(this)); chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); + if (this.options.general.showGuide) { chrome.tabs.create({url: chrome.extension.getURL('/bg/guide.html')}); } From efb5ed2af8c0bcf1c0dbfdbeef7c89dad2a106b9 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 15:22:19 -0700 Subject: [PATCH 12/68] cleanup --- ext/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/manifest.json b/ext/manifest.json index ef0be459..b472fb1f 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -23,7 +23,7 @@ ], "css": ["fg/css/client.css"] }], - "minimum_chrome_version": "45.0.0.0", + "minimum_chrome_version": "57.0.0.0", "options_ui": { "page": "bg/options.html" }, @@ -50,7 +50,7 @@ "applications": { "gecko": { "id": "yomichan-live@foosoft.net", - "strict_min_version": "51.0", + "strict_min_version": "52.0", "update_url": "https://foosoft.net/projects/yomichan/dl/updates.json" } } From b0cdf59bd8dae8f44362b14bdf19b243514e31d3 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 16:24:31 -0700 Subject: [PATCH 13/68] move anki to async --- ext/bg/js/anki-connect.js | 69 ++++++++++++++------------------------- ext/bg/js/anki-null.js | 24 +++++++------- ext/bg/js/translator.js | 20 +----------- ext/bg/js/util.js | 25 ++++++++++++++ 4 files changed, 63 insertions(+), 75 deletions(-) diff --git a/ext/bg/js/anki-connect.js b/ext/bg/js/anki-connect.js index 173feefd..a4d8ba3f 100644 --- a/ext/bg/js/anki-connect.js +++ b/ext/bg/js/anki-connect.js @@ -20,69 +20,50 @@ class AnkiConnect { constructor(server) { this.server = server; - this.asyncPools = {}; this.localVersion = 2; - this.remoteVersion = null; + this.remoteVersion = 0; } - addNote(note) { - return this.checkVersion().then(() => this.ankiInvoke('addNote', {note})); + async addNote(note) { + await this.checkVersion(); + return await this.ankiInvoke('addNote', {note}); } - canAddNotes(notes) { - return this.checkVersion().then(() => this.ankiInvoke('canAddNotes', {notes}, 'notes')); + async canAddNotes(notes) { + await this.checkVersion(); + return await this.ankiInvoke('canAddNotes', {notes}); } - getDeckNames() { - return this.checkVersion().then(() => this.ankiInvoke('deckNames', {})); + async getDeckNames() { + await this.checkVersion(); + return await this.ankiInvoke('deckNames'); } - getModelNames() { - return this.checkVersion().then(() => this.ankiInvoke('modelNames', {})); + async getModelNames() { + await this.checkVersion(); + return await this.ankiInvoke('modelNames'); } - getModelFieldNames(modelName) { - return this.checkVersion().then(() => this.ankiInvoke('modelFieldNames', {modelName})); + async getModelFieldNames(modelName) { + await this.checkVersion(); + return await this.ankiInvoke('modelFieldNames', {modelName}); } - guiBrowse(query) { - return this.checkVersion().then(() => this.ankiInvoke('guiBrowse', {query})); + async guiBrowse(query) { + await this.checkVersion(); + return await this.ankiInvoke('guiBrowse', {query}); } - checkVersion() { - if (this.localVersion === this.remoteVersion) { - return Promise.resolve(true); - } - - return this.ankiInvoke('version', {}, null).then(version => { - this.remoteVersion = version; + async checkVersion() { + if (this.remoteVersion < this.localVersion) { + this.remoteVersion = await this.ankiInvoke('version'); if (this.remoteVersion < this.localVersion) { return Promise.reject('extension and plugin versions incompatible'); } - }); + } } - ankiInvoke(action, params, pool) { - return new Promise((resolve, reject) => { - if (pool && this.asyncPools.hasOwnProperty(pool)) { - this.asyncPools[pool].abort(); - } - - const xhr = new XMLHttpRequest(); - xhr.addEventListener('loadend', () => { - if (pool) { - delete this.asyncPools[pool]; - } - - if (xhr.responseText) { - resolve(JSON.parse(xhr.responseText)); - } else { - reject('unable to connect to plugin'); - } - }); - - xhr.open('POST', this.server); - xhr.send(JSON.stringify({action, params})); - }); + ankiInvoke(action, params) { + return jsonRequest(this.server, 'POST', {action, params, version: this.localVersion}); } } diff --git a/ext/bg/js/anki-null.js b/ext/bg/js/anki-null.js index 8dad6915..d82f0e68 100644 --- a/ext/bg/js/anki-null.js +++ b/ext/bg/js/anki-null.js @@ -18,27 +18,27 @@ class AnkiNull { - addNote(note) { - return Promise.reject('unsupported action'); + async addNote(note) { + return null; } - canAddNotes(notes) { - return Promise.resolve([]); + async canAddNotes(notes) { + return []; } - getDeckNames() { - return Promise.resolve([]); + async getDeckNames() { + return []; } - getModelNames() { - return Promise.resolve([]); + async getModelNames() { + return []; } - getModelFieldNames(modelName) { - return Promise.resolve([]); + async getModelFieldNames(modelName) { + return []; } - guiBrowse(query) { - return Promise.resolve([]); + async guiBrowse(query) { + return []; } } diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index aa11ea63..9232e529 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -31,7 +31,7 @@ class Translator { if (!this.deinflector) { const url = chrome.extension.getURL('/bg/lang/deinflect.json'); - const reasons = await Translator.loadRules(url); + const reasons = await jsonRequest(url, 'GET'); this.deinflector = new Deinflector(reasons); } } @@ -124,22 +124,4 @@ class Translator { return definitions; } - - static loadRules(url) { - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.overrideMimeType('application/json'); - xhr.addEventListener('load', () => resolve(xhr.responseText)); - xhr.addEventListener('error', () => reject('failed to execute network request')); - xhr.open('GET', url); - xhr.send(); - }).then(responseText => { - try { - return JSON.parse(responseText); - } - catch (e) { - return Promise.reject('invalid JSON response'); - } - }); - } } diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index b8a60217..4f907923 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -428,6 +428,31 @@ function dictFieldFormat(field, definition, mode, options) { return field; } +/* + * JSON + */ + +function jsonRequest(url, action, params) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.overrideMimeType('application/json'); + xhr.addEventListener('load', () => resolve(xhr.responseText)); + xhr.addEventListener('error', () => reject('failed to execute network request')); + xhr.open(action, url); + if (params) { + xhr.send(JSON.stringify(params)); + } else { + xhr.send(); + } + }).then(responseText => { + try { + return JSON.parse(responseText); + } + catch (e) { + return Promise.reject('invalid JSON response'); + } + }); +} /* * Helpers From e2373345a4a615ec59a45a6c3c75408826fef9c6 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 16:30:28 -0700 Subject: [PATCH 14/68] version bump --- ext/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/manifest.json b/ext/manifest.json index b472fb1f..ab180faa 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Yomichan", - "version": "1.2.0", + "version": "1.2.1", "description": "Japanese dictionary with Anki integration", "icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"}, From 1ed8997240ba5d8ee4fe57062d2dbe8ba46436e2 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 10 Jul 2017 16:48:26 -0700 Subject: [PATCH 15/68] work on audio --- ext/bg/js/database.js | 3 +-- ext/mixed/js/util.js | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 45ba3d08..b38e00db 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -32,8 +32,7 @@ class Database { if (db.verno !== this.version) { await db.delete(); } - } - catch(error) { + } catch(e) { // NOP } } diff --git a/ext/mixed/js/util.js b/ext/mixed/js/util.js index 5cf62000..0a8c914d 100644 --- a/ext/mixed/js/util.js +++ b/ext/mixed/js/util.js @@ -40,7 +40,7 @@ function clozeBuild(sentence, source) { * Audio */ -function audioBuildUrl(definition, mode, cache={}) { +async function audioBuildUrl(definition, mode, cache={}) { if (mode === 'jpod101') { let kana = definition.reading; let kanji = definition.expression; @@ -103,7 +103,7 @@ function audioBuildUrl(definition, mode, cache={}) { } }); } else { - return Promise.reject('unsupported audio source'); + return Promise.resolve(); } } @@ -121,16 +121,7 @@ function audioBuildFilename(definition) { } } -function audioInject(definition, fields, mode) { - if (mode === 'disabled') { - return Promise.resolve(true); - } - - const filename = audioBuildFilename(definition); - if (!filename) { - return Promise.resolve(true); - } - +async function audioInject(definition, fields, mode) { let usesAudio = false; for (const name in fields) { if (fields[name].includes('{audio}')) { @@ -140,11 +131,19 @@ function audioInject(definition, fields, mode) { } if (!usesAudio) { - return Promise.resolve(true); + return true; } - return audioBuildUrl(definition, mode).then(url => { - definition.audio = {url, filename}; + try { + const url = await audioBuildUrl(definition, mode); + const filename = audioBuildFilename(definition); + + if (url && filename) { + definition.audio = {url, filename}; + } + return true; - }).catch(() => false); + } catch (e) { + return false; + } } From ede139097c670f16ca3c332d8a79e9009b23bfac Mon Sep 17 00:00:00 2001 From: dequis Date: Sun, 16 Jul 2017 05:05:23 -0300 Subject: [PATCH 16/68] Add glossary-brief anki field, like glossary but without tags --- README.md | 1 + ext/bg/js/options.js | 1 + ext/bg/js/templates.js | 271 +++++++++++++++++++++-------------------- ext/bg/js/util.js | 1 + tmpl/fields.html | 20 ++- 5 files changed, 159 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index c32c867f..6ac97eea 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ Flashcard fields can be configured with the following steps: `{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. 日本語にほんご). `{glossary}` | List of definitions for the term (output format depends on whether running in *grouped* mode). + `{glossary-brief}` | Shorter version of `{glossary}`, without `{tags}` `{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. `{tags}` | Grammar and usage tags providing information about the term (unavailable in *grouped* mode). diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 728ddae4..e105f1b2 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -351,6 +351,7 @@ function ankiFieldsPopulate(element, options) { 'expression', 'furigana', 'glossary', + 'glossary-brief', 'reading', 'sentence', 'tags', diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js index 50686ed4..6fa1dad0 100644 --- a/ext/bg/js/templates.js +++ b/ext/bg/js/templates.js @@ -24,106 +24,114 @@ templates['dictionary.html'] = template({"1":function(container,depth0,helpers,p templates['fields.html'] = template({"1":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.program(14, data, 0),"data":data})) != null ? stack1 : ""); + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(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 : {}; - return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"if","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(7, data, 0),"inverse":container.program(11, data, 0),"data":data})) != null ? stack1 : ""); + 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.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + ") "; + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(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 : {},(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 : {}; 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(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"5":function(container,depth0,helpers,partials,data) { + + ((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 ", "; -},"7":function(container,depth0,helpers,partials,data) { +},"8":function(container,depth0,helpers,partials,data) { var stack1; return "
      " - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + "
    "; -},"8":function(container,depth0,helpers,partials,data) { +},"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(9, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper)); + 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 : {},options) : helper)); if (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} if (stack1 != null) { buffer += stack1; } return buffer + "
  • "; -},"9":function(container,depth0,helpers,partials,data) { +},"10":function(container,depth0,helpers,partials,data) { return container.escapeExpression(container.lambda(depth0, depth0)); -},"11":function(container,depth0,helpers,partials,data) { +},"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(12, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper)); + 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 : {},options) : helper)); if (!helpers.multiLine) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} if (stack1 != null) { buffer += stack1; } return buffer; -},"12":function(container,depth0,helpers,partials,data) { +},"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)); -},"14":function(container,depth0,helpers,partials,data) { +},"15":function(container,depth0,helpers,partials,data) { var stack1, alias1=depth0 != null ? depth0 : {}; - return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"if","hash":{},"fn":container.program(15, 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(17, data, 0),"inverse":container.program(12, data, 0),"data":data})) != null ? stack1 : ""); -},"15":function(container,depth0,helpers,partials,data) { + 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.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + ") "; + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(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 : {},(depth0 != null ? depth0.glossary : depth0),{"name":"each","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"18":function(container,depth0,helpers,partials,data) { + return "(" + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(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 : {},(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 : {},(data && data.last),{"name":"unless","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"20":function(container,depth0,helpers,partials,data) { - return ""; + + ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(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) { - var stack1; - - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.character : stack1), depth0)); + return ""; },"24":function(container,depth0,helpers,partials,data) { var stack1; - return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.dictionary : stack1), depth0)); + 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 ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.modeTermKana : depth0),{"name":"if","hash":{},"fn":container.program(27, data, 0),"inverse":container.program(30, data, 0),"data":data})) != null ? stack1 : ""); -},"27":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1),{"name":"if","hash":{},"fn":container.program(28, data, 0),"inverse":container.program(30, data, 0),"data":data})) != null ? 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 container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1), depth0)); + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(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 : {},((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.expression : stack1), depth0)); + 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 ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(33, data, 0),"inverse":container.program(36, data, 0),"data":data})) != null ? stack1 : ""); -},"33":function(container,depth0,helpers,partials,data) { + 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 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1),{"name":"if","hash":{},"fn":container.program(34, data, 0),"inverse":container.program(30, data, 0),"data":data})) != null ? stack1 : ""); -},"34":function(container,depth0,helpers,partials,data) { + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(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; + + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1),{"name":"if","hash":{},"fn":container.program(36, data, 0),"inverse":container.program(32, data, 0),"data":data})) != null ? stack1 : ""); +},"36":function(container,depth0,helpers,partials,data) { var stack1, alias1=container.lambda, alias2=container.escapeExpression; return "" @@ -131,147 +139,151 @@ templates['fields.html'] = template({"1":function(container,depth0,helpers,parti + "" + alias2(alias1(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1), depth0)) + ""; -},"36":function(container,depth0,helpers,partials,data) { +},"38":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1),{"name":"if","hash":{},"fn":container.program(37, data, 0),"inverse":container.program(30, data, 0),"data":data})) != null ? stack1 : ""); -},"37":function(container,depth0,helpers,partials,data) { + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1),{"name":"if","hash":{},"fn":container.program(39, data, 0),"inverse":container.program(32, data, 0),"data":data})) != null ? stack1 : ""); +},"39":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.expression : stack1), depth0)) + " [" + alias2(alias1(((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.reading : stack1), depth0)) + "]"; -},"39":function(container,depth0,helpers,partials,data,blockParams,depths) { +},"41":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1, alias1=depth0 != null ? depth0 : {}; - return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(40, 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(42, data, 0, blockParams, depths),"inverse":container.program(51, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "") - + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(64, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"40":function(container,depth0,helpers,partials,data) { - return "
    "; + return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(42, 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(44, data, 0, blockParams, depths),"inverse":container.program(53, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "") + + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(66, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); },"42":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(43, data, 0),"inverse":container.program(49, data, 0),"data":data})) != null ? stack1 : ""); -},"43":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(44, data, 0),"inverse":container.program(47, data, 0),"data":data})) != null ? stack1 : ""); + return "
    "; },"44":function(container,depth0,helpers,partials,data) { var stack1; - return "
      " - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1),{"name":"each","hash":{},"fn":container.program(45, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + "
    "; + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(45, data, 0),"inverse":container.program(51, data, 0),"data":data})) != null ? stack1 : ""); },"45":function(container,depth0,helpers,partials,data) { + var stack1; + + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(46, data, 0),"inverse":container.program(49, data, 0),"data":data})) != null ? stack1 : ""); +},"46":function(container,depth0,helpers,partials,data) { + var stack1; + + return "
      " + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1),{"name":"each","hash":{},"fn":container.program(47, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + "
    "; +},"47":function(container,depth0,helpers,partials,data) { return "
  • " + container.escapeExpression(container.lambda(depth0, depth0)) + "
  • "; -},"47":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.glossary : stack1),{"name":"each","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); },"49":function(container,depth0,helpers,partials,data) { var stack1; + return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((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 : ""); +},"51":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)); -},"51":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.group : depth0),{"name":"if","hash":{},"fn":container.program(52, data, 0, blockParams, depths),"inverse":container.program(62, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); -},"52":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(53, data, 0, blockParams, depths),"inverse":container.program(60, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); },"53":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1; - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(54, data, 0, blockParams, depths),"inverse":container.program(57, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.group : depth0),{"name":"if","hash":{},"fn":container.program(54, data, 0, blockParams, depths),"inverse":container.program(64, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); },"54":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1; - return "
      " - + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1),{"name":"each","hash":{},"fn":container.program(55, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "") - + "
    "; + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(55, data, 0, blockParams, depths),"inverse":container.program(62, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); },"55":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1; - return "
  • " - + ((stack1 = container.invokePartial(partials["glossary-single"],depth0,{"name":"glossary-single","hash":{"html":(depths[1] != null ? depths[1].html : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "") - + "
  • "; + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(56, data, 0, blockParams, depths),"inverse":container.program(59, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); +},"56":function(container,depth0,helpers,partials,data,blockParams,depths) { + var stack1; + + return "
      " + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1),{"name":"each","hash":{},"fn":container.program(57, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + "
    "; },"57":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1; - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((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) { + 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 : "") + + "
  • "; +},"59":function(container,depth0,helpers,partials,data,blockParams,depths) { + var stack1; + + return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.definitions : stack1),{"name":"each","hash":{},"fn":container.program(60, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); +},"60":function(container,depth0,helpers,partials,data,blockParams,depths) { var stack1; return " * " - + ((stack1 = container.invokePartial(partials["glossary-single"],depth0,{"name":"glossary-single","hash":{"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) { - 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":{"html":(depth0 != null ? depth0.html : depth0)},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); + + ((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 : ""); },"62":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = container.invokePartial(partials["glossary-single"],(depth0 != null ? depth0.definition : depth0),{"name":"glossary-single","hash":{"html":(depth0 != null ? depth0.html : depth0)},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? 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 : ""); },"64":function(container,depth0,helpers,partials,data) { - return "
    "; -},"66":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.kunyomi : stack1),{"name":"each","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? 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 : ""); +},"66":function(container,depth0,helpers,partials,data) { + return "
    "; },"68":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.onyomi : stack1),{"name":"each","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); + return ((stack1 = container.invokePartial(partials.glossary,depth0,{"name":"glossary","hash":{"brief":true},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); },"70":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.modeTermKana : depth0),{"name":"unless","hash":{},"fn":container.program(28, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); + return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((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 : ""); },"72":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(73, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"73":function(container,depth0,helpers,partials,data) { + return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((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 : ""); +},"74":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)); -},"75":function(container,depth0,helpers,partials,data) { - var stack1; - - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(76, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); + return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.modeTermKana : depth0),{"name":"unless","hash":{},"fn":container.program(30, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); },"76":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)); -},"78":function(container,depth0,helpers,partials,data) { + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(77, 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 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(79, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); + return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.sentence : stack1), depth0)); },"79":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)); -},"81":function(container,depth0,helpers,partials,data) { + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(80, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); +},"80":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(82, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); + return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.prefix : stack1), depth0)); },"82":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)); -},"84":function(container,depth0,helpers,partials,data) { + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(83, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); +},"83":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.tags : stack1),{"name":"each","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); + return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.body : stack1), depth0)); +},"85":function(container,depth0,helpers,partials,data) { + var stack1; + + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1),{"name":"if","hash":{},"fn":container.program(86, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : ""); },"86":function(container,depth0,helpers,partials,data) { var stack1; - return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(87, data, 0),"inverse":container.program(89, data, 0),"data":data})) != null ? stack1 : ""); -},"87":function(container,depth0,helpers,partials,data) { + return container.escapeExpression(container.lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.definition : depth0)) != null ? stack1.cloze : stack1)) != null ? stack1.suffix : stack1), depth0)); +},"88":function(container,depth0,helpers,partials,data) { + var stack1; + + return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},((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 : ""); +},"90":function(container,depth0,helpers,partials,data) { + var stack1; + + return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.html : depth0),{"name":"if","hash":{},"fn":container.program(91, data, 0),"inverse":container.program(93, data, 0),"data":data})) != null ? stack1 : ""); +},"91":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)) + ""; -},"89":function(container,depth0,helpers,partials,data) { +},"93":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" + return "\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 : {},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(20, data, 0, blockParams, depths),"inverse":container.noop,"args":["audio"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(22, data, 0, blockParams, depths),"inverse":container.noop,"args":["character"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(24, data, 0, blockParams, depths),"inverse":container.noop,"args":["dictionary"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(26, data, 0, blockParams, depths),"inverse":container.noop,"args":["expression"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(32, data, 0, blockParams, depths),"inverse":container.noop,"args":["furigana"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(39, data, 0, blockParams, depths),"inverse":container.noop,"args":["glossary"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(66, data, 0, blockParams, depths),"inverse":container.noop,"args":["kunyomi"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(68, data, 0, blockParams, depths),"inverse":container.noop,"args":["onyomi"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(70, data, 0, blockParams, depths),"inverse":container.noop,"args":["reading"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(72, data, 0, blockParams, depths),"inverse":container.noop,"args":["sentence"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(75, 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(78, 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(81, 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(84, data, 0, blockParams, depths),"inverse":container.noop,"args":["tags"],"data":data}) || fn; - fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(86, data, 0, blockParams, depths),"inverse":container.noop,"args":["url"],"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(41, data, 0, blockParams, depths),"inverse":container.noop,"args":["glossary"],"data":data}) || fn; + fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(68, 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(70, data, 0, blockParams, depths),"inverse":container.noop,"args":["kunyomi"],"data":data}) || fn; + fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(72, data, 0, blockParams, depths),"inverse":container.noop,"args":["onyomi"],"data":data}) || fn; + fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(74, data, 0, blockParams, depths),"inverse":container.noop,"args":["reading"],"data":data}) || fn; + fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(76, data, 0, blockParams, depths),"inverse":container.noop,"args":["sentence"],"data":data}) || fn; + fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(79, 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(82, 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(85, 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(88, data, 0, blockParams, depths),"inverse":container.noop,"args":["tags"],"data":data}) || fn; + fn = decorators.inline(fn,props,container,{"name":"inline","hash":{},"fn":container.program(90, data, 0, blockParams, depths),"inverse":container.noop,"args":["url"],"data":data}) || fn; return fn; } diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 4f907923..4d0aa27e 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -400,6 +400,7 @@ function dictFieldFormat(field, definition, mode, options) { 'expression', 'furigana', 'glossary', + 'glossary-brief', 'kunyomi', 'onyomi', 'reading', diff --git a/tmpl/fields.html b/tmpl/fields.html index ab1a9722..944a43e6 100644 --- a/tmpl/fields.html +++ b/tmpl/fields.html @@ -1,13 +1,17 @@ {{#*inline "glossary-single"}} {{~#if html~}} - {{~#if tags~}}({{#each tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}) {{/if~}} + {{~#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~}} - {{~#if tags~}}({{#each tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}) {{/if~}} + {{~#unless brief~}} + {{~#if tags~}}({{#each tags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}) {{/if~}} + {{~/unless~}} {{~#if glossary.[1]~}} {{#each glossary}}{{.}}{{#unless @last}}, {{/unless}}{{/each}} {{~else~}} @@ -58,18 +62,22 @@ {{~else~}} {{~#if group~}} {{~#if definition.definitions.[1]~}} - {{~#if html}}
      {{#each definition.definitions}}
    1. {{> glossary-single html=../html}}
    2. {{/each}}
    - {{~else}}{{#each definition.definitions}} * {{> glossary-single html=../html}}{{/each}}{{/if~}} + {{~#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~}} + {{~> glossary-single definition.definitions.[0] html=html brief=brief~}} {{~/if~}} {{~else~}} - {{~> glossary-single definition html=html~}} + {{~> 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}} From 516c7f5381700700dcfdfb06682f11de8205137b Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 16 Jul 2017 12:48:27 -0700 Subject: [PATCH 17/68] refactor mixed/js/util.js --- ext/bg/background.html | 2 +- ext/bg/search.html | 2 +- ext/fg/frame.html | 2 +- ext/mixed/js/{util.js => audio.js} | 19 ------------------- ext/mixed/js/display.js | 18 ++++++++++++++++-- 5 files changed, 19 insertions(+), 24 deletions(-) rename ext/mixed/js/{util.js => audio.js} (92%) diff --git a/ext/bg/background.html b/ext/bg/background.html index 4410c249..4a2d8c8d 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -8,7 +8,7 @@ - + diff --git a/ext/bg/search.html b/ext/bg/search.html index 4c07ee61..0fc3caf7 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -34,7 +34,7 @@ - + diff --git a/ext/fg/frame.html b/ext/fg/frame.html index c20745af..ecaee323 100644 --- a/ext/fg/frame.html +++ b/ext/fg/frame.html @@ -33,7 +33,7 @@ - + diff --git a/ext/mixed/js/util.js b/ext/mixed/js/audio.js similarity index 92% rename from ext/mixed/js/util.js rename to ext/mixed/js/audio.js index 0a8c914d..451fe1d9 100644 --- a/ext/mixed/js/util.js +++ b/ext/mixed/js/audio.js @@ -17,25 +17,6 @@ */ -/* - * Cloze - */ - -function clozeBuild(sentence, source) { - const result = { - sentence: sentence.text.trim() - }; - - if (source) { - result.prefix = sentence.text.substring(0, sentence.offset).trim(); - result.body = source.trim(); - result.suffix = sentence.text.substring(sentence.offset + source.length).trim(); - } - - return result; -} - - /* * Audio */ diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index da0cd351..e54bc0f9 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -80,7 +80,7 @@ class Display { if (context) { for (const definition of definitions) { if (context.sentence) { - definition.cloze = clozeBuild(context.sentence, definition.source); + definition.cloze = Display.clozeBuild(context.sentence, definition.source); } definition.url = context.url; @@ -119,7 +119,7 @@ class Display { if (context) { for (const definition of definitions) { if (context.sentence) { - definition.cloze = clozeBuild(context.sentence); + definition.cloze = Display.clozeBuild(context.sentence); } definition.url = context.url; @@ -394,6 +394,20 @@ class Display { }).catch(this.handleError.bind(this)).then(() => this.spinner.hide()); } + static clozeBuild(sentence, source) { + const result = { + sentence: sentence.text.trim() + }; + + if (source) { + result.prefix = sentence.text.substring(0, sentence.offset).trim(); + result.body = source.trim(); + result.suffix = sentence.text.substring(sentence.offset + source.length).trim(); + } + + return result; + } + static entryIndexFind(element) { return $('.entry').index(element.closest('.entry')); } From b26aea4d81416e95fbf12c4e8407bff3f25518f7 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 16 Jul 2017 12:50:00 -0700 Subject: [PATCH 18/68] . --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ac97eea..4d467f82 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Flashcard fields can be configured with the following steps: `{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. 日本語にほんご). `{glossary}` | List of definitions for the term (output format depends on whether running in *grouped* mode). - `{glossary-brief}` | Shorter version of `{glossary}`, without `{tags}` + `{glossary-brief}` | Shorter version of `{glossary}`, without `{tags}`. `{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. `{tags}` | Grammar and usage tags providing information about the term (unavailable in *grouped* mode). From 7dd75082abd31b725d9907a93ab97fe1434bab04 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 16 Jul 2017 12:59:16 -0700 Subject: [PATCH 19/68] rename options to settings --- ext/bg/js/{options.js => settings.js} | 0 ext/bg/{options.html => settings.html} | 2 +- ext/manifest.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename ext/bg/js/{options.js => settings.js} (100%) rename ext/bg/{options.html => settings.html} (99%) diff --git a/ext/bg/js/options.js b/ext/bg/js/settings.js similarity index 100% rename from ext/bg/js/options.js rename to ext/bg/js/settings.js diff --git a/ext/bg/options.html b/ext/bg/settings.html similarity index 99% rename from ext/bg/options.html rename to ext/bg/settings.html index 34728d90..19dfec22 100644 --- a/ext/bg/options.html +++ b/ext/bg/settings.html @@ -277,6 +277,6 @@ - + diff --git a/ext/manifest.json b/ext/manifest.json index ab180faa..be9f3208 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -25,7 +25,7 @@ }], "minimum_chrome_version": "57.0.0.0", "options_ui": { - "page": "bg/options.html" + "page": "bg/settings.html" }, "permissions": [ "", From 39f1f30dc9e9aaae8cbeac9afe120fbb5c2ecfd3 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 16 Jul 2017 13:14:28 -0700 Subject: [PATCH 20/68] refactor bg/js/util.js --- ext/bg/background.html | 1 + ext/bg/js/options.js | 132 +++++++++++++++++++++++++++++++++++++++++ ext/bg/js/util.js | 119 ------------------------------------- ext/bg/popup.html | 1 + ext/bg/settings.html | 1 + ext/mixed/js/audio.js | 4 -- 6 files changed, 135 insertions(+), 123 deletions(-) create mode 100644 ext/bg/js/options.js diff --git a/ext/bg/background.html b/ext/bg/background.html index 4a2d8c8d..a9071cc7 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -11,6 +11,7 @@ + diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js new file mode 100644 index 00000000..a9345fdd --- /dev/null +++ b/ext/bg/js/options.js @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 Alex Yatskov + * Author: Alex Yatskov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +function optionsSetDefaults(options) { + const defaults = { + general: { + enable: true, + audioSource: 'jpod101', + audioVolume: 100, + groupResults: true, + debugInfo: false, + maxResults: 32, + showAdvanced: false, + popupWidth: 400, + popupHeight: 250, + popupOffset: 10, + showGuide: true + }, + + scanning: { + middleMouse: true, + selectText: true, + alphanumeric: true, + delay: 15, + length: 10, + modifier: 'shift' + }, + + dictionaries: {}, + + anki: { + enable: false, + server: 'http://127.0.0.1:8765', + tags: ['yomichan'], + htmlCards: true, + sentenceExt: 200, + terms: {deck: '', model: '', fields: {}}, + kanji: {deck: '', model: '', fields: {}} + } + }; + + const combine = (target, source) => { + for (const key in source) { + if (!target.hasOwnProperty(key)) { + target[key] = source[key]; + } + } + }; + + combine(options, defaults); + combine(options.general, defaults.general); + combine(options.scanning, defaults.scanning); + combine(options.anki, defaults.anki); + combine(options.anki.terms, defaults.anki.terms); + combine(options.anki.kanji, defaults.anki.kanji); + + return options; +} + +function optionsVersion(options) { + const fixups = [ + () => {}, + () => {}, + () => {}, + () => {}, + () => { + if (options.general.audioPlayback) { + options.general.audioSource = 'jpod101'; + } else { + options.general.audioSource = 'disabled'; + } + }, + () => { + options.general.showGuide = false; + }, + () => { + if (options.scanning.requireShift) { + options.scanning.modifier = 'shift'; + } else { + options.scanning.modifier = 'none'; + } + } + ]; + + optionsSetDefaults(options); + if (!options.hasOwnProperty('version')) { + options.version = fixups.length; + } + + while (options.version < fixups.length) { + fixups[options.version++](); + } + + return options; +} + +function optionsLoad() { + return new Promise((resolve, reject) => { + chrome.storage.local.get(null, store => resolve(store.options)); + }).then(optionsStr => { + return optionsStr ? JSON.parse(optionsStr) : {}; + }).catch(error => { + return {}; + }).then(options => { + return optionsVersion(options); + }); +} + +function optionsSave(options) { + return new Promise((resolve, reject) => { + chrome.storage.local.set({options: JSON.stringify(options)}, resolve); + }).then(() => { + instYomi().optionsSet(options); + fgOptionsSet(options); + }); +} diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 4d0aa27e..5d80fdb7 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -91,125 +91,6 @@ function fgOptionsSet(options) { } -/* - * Options - */ - -function optionsSetDefaults(options) { - const defaults = { - general: { - enable: true, - audioSource: 'jpod101', - audioVolume: 100, - groupResults: true, - debugInfo: false, - maxResults: 32, - showAdvanced: false, - popupWidth: 400, - popupHeight: 250, - popupOffset: 10, - showGuide: true - }, - - scanning: { - middleMouse: true, - selectText: true, - alphanumeric: true, - delay: 15, - length: 10, - modifier: 'shift' - }, - - dictionaries: {}, - - anki: { - enable: false, - server: 'http://127.0.0.1:8765', - tags: ['yomichan'], - htmlCards: true, - sentenceExt: 200, - terms: {deck: '', model: '', fields: {}}, - kanji: {deck: '', model: '', fields: {}} - } - }; - - const combine = (target, source) => { - for (const key in source) { - if (!target.hasOwnProperty(key)) { - target[key] = source[key]; - } - } - }; - - combine(options, defaults); - combine(options.general, defaults.general); - combine(options.scanning, defaults.scanning); - combine(options.anki, defaults.anki); - combine(options.anki.terms, defaults.anki.terms); - combine(options.anki.kanji, defaults.anki.kanji); - - return options; -} - -function optionsVersion(options) { - const fixups = [ - () => {}, - () => {}, - () => {}, - () => {}, - () => { - if (options.general.audioPlayback) { - options.general.audioSource = 'jpod101'; - } else { - options.general.audioSource = 'disabled'; - } - }, - () => { - options.general.showGuide = false; - }, - () => { - if (options.scanning.requireShift) { - options.scanning.modifier = 'shift'; - } else { - options.scanning.modifier = 'none'; - } - } - ]; - - optionsSetDefaults(options); - if (!options.hasOwnProperty('version')) { - options.version = fixups.length; - } - - while (options.version < fixups.length) { - fixups[options.version++](); - } - - return options; -} - -function optionsLoad() { - return new Promise((resolve, reject) => { - chrome.storage.local.get(null, store => resolve(store.options)); - }).then(optionsStr => { - return optionsStr ? JSON.parse(optionsStr) : {}; - }).catch(error => { - return {}; - }).then(options => { - return optionsVersion(options); - }); -} - -function optionsSave(options) { - return new Promise((resolve, reject) => { - chrome.storage.local.set({options: JSON.stringify(options)}, resolve); - }).then(() => { - instYomi().optionsSet(options); - fgOptionsSet(options); - }); -} - - /* * Dictionary */ diff --git a/ext/bg/popup.html b/ext/bg/popup.html index dec52795..ab2ee1e0 100644 --- a/ext/bg/popup.html +++ b/ext/bg/popup.html @@ -31,6 +31,7 @@ + diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 19dfec22..ada3299b 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -277,6 +277,7 @@ + diff --git a/ext/mixed/js/audio.js b/ext/mixed/js/audio.js index 451fe1d9..eb8fde94 100644 --- a/ext/mixed/js/audio.js +++ b/ext/mixed/js/audio.js @@ -17,10 +17,6 @@ */ -/* - * Audio - */ - async function audioBuildUrl(definition, mode, cache={}) { if (mode === 'jpod101') { let kana = definition.reading; From a22facec98caedce379bdc5ac34c3586c08c41a2 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 16 Jul 2017 19:29:43 -0700 Subject: [PATCH 21/68] refactor options page --- ext/bg/js/settings.js | 406 ++++++++++++++++++++++-------------------- 1 file changed, 216 insertions(+), 190 deletions(-) diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index e105f1b2..f48afa2c 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -21,53 +21,52 @@ * General */ -function formRead() { - return optionsLoad().then(optionsOld => { - const optionsNew = $.extend(true, {}, optionsOld); +async function formRead() { + const optionsOld = await optionsLoad(); + const optionsNew = $.extend(true, {}, optionsOld); - optionsNew.general.showGuide = $('#show-usage-guide').prop('checked'); - optionsNew.general.audioSource = $('#audio-playback-source').val(); - optionsNew.general.audioVolume = parseFloat($('#audio-playback-volume').val()); - optionsNew.general.groupResults = $('#group-terms-results').prop('checked'); - optionsNew.general.debugInfo = $('#show-debug-info').prop('checked'); - optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked'); - optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10); - optionsNew.general.popupWidth = parseInt($('#popup-width').val(), 10); - optionsNew.general.popupHeight = parseInt($('#popup-height').val(), 10); - optionsNew.general.popupOffset = parseInt($('#popup-offset').val(), 10); + optionsNew.general.showGuide = $('#show-usage-guide').prop('checked'); + optionsNew.general.audioSource = $('#audio-playback-source').val(); + optionsNew.general.audioVolume = parseFloat($('#audio-playback-volume').val()); + optionsNew.general.groupResults = $('#group-terms-results').prop('checked'); + optionsNew.general.debugInfo = $('#show-debug-info').prop('checked'); + optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked'); + optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10); + optionsNew.general.popupWidth = parseInt($('#popup-width').val(), 10); + optionsNew.general.popupHeight = parseInt($('#popup-height').val(), 10); + optionsNew.general.popupOffset = parseInt($('#popup-offset').val(), 10); - optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked'); - optionsNew.scanning.selectText = $('#select-matched-text').prop('checked'); - optionsNew.scanning.alphanumeric = $('#search-alphanumeric').prop('checked'); - optionsNew.scanning.delay = parseInt($('#scan-delay').val(), 10); - optionsNew.scanning.length = parseInt($('#scan-length').val(), 10); - optionsNew.scanning.modifier = $('#scan-modifier-key').val(); + optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked'); + optionsNew.scanning.selectText = $('#select-matched-text').prop('checked'); + optionsNew.scanning.alphanumeric = $('#search-alphanumeric').prop('checked'); + optionsNew.scanning.delay = parseInt($('#scan-delay').val(), 10); + optionsNew.scanning.length = parseInt($('#scan-length').val(), 10); + optionsNew.scanning.modifier = $('#scan-modifier-key').val(); - 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.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(); - if (optionsOld.anki.enable && !$('#anki-error').is(':visible')) { - optionsNew.anki.terms.deck = $('#anki-terms-deck').val(); - optionsNew.anki.terms.model = $('#anki-terms-model').val(); - optionsNew.anki.terms.fields = ankiFieldsToDict($('#terms .anki-field-value')); - optionsNew.anki.kanji.deck = $('#anki-kanji-deck').val(); - optionsNew.anki.kanji.model = $('#anki-kanji-model').val(); - optionsNew.anki.kanji.fields = ankiFieldsToDict($('#kanji .anki-field-value')); - } + if (optionsOld.anki.enable && !$('#anki-error').is(':visible')) { + optionsNew.anki.terms.deck = $('#anki-terms-deck').val(); + optionsNew.anki.terms.model = $('#anki-terms-model').val(); + optionsNew.anki.terms.fields = ankiFieldsToDict($('#terms .anki-field-value')); + optionsNew.anki.kanji.deck = $('#anki-kanji-deck').val(); + optionsNew.anki.kanji.model = $('#anki-kanji-model').val(); + optionsNew.anki.kanji.fields = ankiFieldsToDict($('#kanji .anki-field-value')); + } - $('.dict-group').each((index, element) => { - const dictionary = $(element); - const title = dictionary.data('title'); - const priority = parseInt(dictionary.find('.dict-priority').val(), 10); - const enabled = dictionary.find('.dict-enabled').prop('checked'); - optionsNew.dictionaries[title] = {priority, enabled}; - }); - - return {optionsNew, optionsOld}; + $('.dict-group').each((index, element) => { + const dictionary = $(element); + const title = dictionary.data('title'); + const priority = parseInt(dictionary.find('.dict-priority').val(), 10); + const enabled = dictionary.find('.dict-enabled').prop('checked'); + optionsNew.dictionaries[title] = {priority, enabled}; }); + + return {optionsNew, optionsOld}; } function updateVisibility(options) { @@ -95,66 +94,84 @@ function updateVisibility(options) { } } -function onOptionsChanged(e) { +async function onOptionsChanged(e) { (async () => { if (!e.originalEvent && !e.isTrigger) { return; } - formRead().then(({optionsNew, optionsOld}) => { - return optionsSave(optionsNew).then(() => { - updateVisibility(optionsNew); + try { + ankiErrorShow(); + ankiSpinnerShow(true); - const ankiUpdated = - optionsNew.anki.enable !== optionsOld.anki.enable || - optionsNew.anki.server !== optionsOld.anki.server; + const {optionsNew, optionsOld} = await formRead(); + await optionsSave(optionsNew); - if (ankiUpdated) { - ankiErrorShow(null); - ankiSpinnerShow(true); - return ankiDeckAndModelPopulate(optionsNew); - } - }); - }).catch(ankiErrorShow).then(() => ankiSpinnerShow(false)); -} + updateVisibility(optionsNew); -$(document).ready(() => { + const ankiUpdated = + optionsNew.anki.enable !== optionsOld.anki.enable || + optionsNew.anki.server !== optionsOld.anki.server; + + if (ankiUpdated) { + await ankiDeckAndModelPopulate(optionsNew); + } + } catch (e) { + ankiErrorShow(e); + } finally { + ankiSpinnerShow(false); + } +})();} + +function onReady() {(async () => { handlebarsRegister(); - optionsLoad().then(options => { - $('#show-usage-guide').prop('checked', options.general.showGuide); - $('#audio-playback-source').val(options.general.audioSource); - $('#audio-playback-volume').val(options.general.audioVolume); - $('#group-terms-results').prop('checked', options.general.groupResults); - $('#show-debug-info').prop('checked', options.general.debugInfo); - $('#show-advanced-options').prop('checked', options.general.showAdvanced); - $('#max-displayed-results').val(options.general.maxResults); - $('#popup-width').val(options.general.popupWidth); - $('#popup-height').val(options.general.popupHeight); - $('#popup-offset').val(options.general.popupOffset); + const options = await optionsLoad(); - $('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse); - $('#select-matched-text').prop('checked', options.scanning.selectText); - $('#search-alphanumeric').prop('checked', options.scanning.alphanumeric); - $('#scan-delay').val(options.scanning.delay); - $('#scan-length').val(options.scanning.length); - $('#scan-modifier-key').val(options.scanning.modifier); + $('#show-usage-guide').prop('checked', options.general.showGuide); + $('#audio-playback-source').val(options.general.audioSource); + $('#audio-playback-volume').val(options.general.audioVolume); + $('#group-terms-results').prop('checked', options.general.groupResults); + $('#show-debug-info').prop('checked', options.general.debugInfo); + $('#show-advanced-options').prop('checked', options.general.showAdvanced); + $('#max-displayed-results').val(options.general.maxResults); + $('#popup-width').val(options.general.popupWidth); + $('#popup-height').val(options.general.popupHeight); + $('#popup-offset').val(options.general.popupOffset); - $('#dict-purge').click(onDictionaryPurge); - $('#dict-file').change(onDictionaryImport); + $('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse); + $('#select-matched-text').prop('checked', options.scanning.selectText); + $('#search-alphanumeric').prop('checked', options.scanning.alphanumeric); + $('#scan-delay').val(options.scanning.delay); + $('#scan-length').val(options.scanning.length); + $('#scan-modifier-key').val(options.scanning.modifier); - $('#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(onOptionsChanged); - $('.anki-model').change(onAnkiModelChanged); + $('#dict-purge').click(onDictionaryPurge); + $('#dict-file').change(onDictionaryImport); - dictionaryGroupsPopulate(options); - ankiDeckAndModelPopulate(options); - updateVisibility(options); - }); -}); + $('#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(onOptionsChanged); + $('.anki-model').change(onAnkiModelChanged); + + try { + await dictionaryGroupsPopulate(options); + } catch (e) { + dictionaryErrorShow(e); + } + + try { + await ankiDeckAndModelPopulate(options); + } catch (e) { + ankiErrorShow(e); + } + + updateVisibility(options); +})();} + +$(document).ready(onReady); /* @@ -196,84 +213,91 @@ function dictionaryGroupsSort() { dictGroups.append(dictGroupChildren); } -function dictionaryGroupsPopulate(options) { - dictionaryErrorShow(null); - dictionarySpinnerShow(true); - +async function dictionaryGroupsPopulate(options) { const dictGroups = $('#dict-groups').empty(); const dictWarning = $('#dict-warning').hide(); - return instDb().getDictionaries().then(rows => { - if (rows.length === 0) { - dictWarning.show(); - } + const dictRows = await instDb().getDictionaries(); + if (dictRows.length === 0) { + dictWarning.show(); + } - for (const row of dictRowsSort(rows, options)) { - const dictOptions = options.dictionaries[row.title] || {enabled: false, priority: 0}; - const dictHtml = handlebarsRender('dictionary.html', { - title: row.title, - version: row.version, - revision: row.revision, - priority: dictOptions.priority, - enabled: dictOptions.enabled - }); - - dictGroups.append($(dictHtml)); - } - - updateVisibility(options); - - $('.dict-enabled, .dict-priority').change(e => { - dictionaryGroupsSort(); - onOptionsChanged(e); + for (const dictRow of dictRowsSort(dictRows, options)) { + const dictOptions = options.dictionaries[dictRow.title] || {enabled: false, priority: 0}; + const dictHtml = handlebarsRender('dictionary.html', { + title: dictRow.title, + version: dictRow.version, + revision: dictRow.revision, + priority: dictOptions.priority, + enabled: dictOptions.enabled }); - }).catch(dictionaryErrorShow).then(() => dictionarySpinnerShow(false)); + + dictGroups.append($(dictHtml)); + } + + updateVisibility(options); + + $('.dict-enabled, .dict-priority').change(e => { + dictionaryGroupsSort(); + onOptionsChanged(e); + }); } -function onDictionaryPurge(e) { +async function onDictionaryPurge(e) { (async () => { e.preventDefault(); - dictionaryErrorShow(null); - dictionarySpinnerShow(true); - const dictControls = $('#dict-importer, #dict-groups').hide(); const dictProgress = $('#dict-purge-progress').show(); - instDb().purge().catch(dictionaryErrorShow).then(() => { + try { + dictionaryErrorShow(); + dictionarySpinnerShow(true); + + await instDb().purge(); + const options = await optionsLoad(); + options.dictionaries = {}; + await optionsSave(options); + + await dictionaryGroupsPopulate(options); + } catch (e) { + dictionaryErrorShow(e); + } finally { dictionarySpinnerShow(false); + dictControls.show(); dictProgress.hide(); - return optionsLoad(); - }).then(options => { - options.dictionaries = {}; - optionsSave(options).then(() => dictionaryGroupsPopulate(options)); - }); -} - -function onDictionaryImport(e) { - dictionaryErrorShow(null); - dictionarySpinnerShow(true); + } +})();} +function onDictionaryImport(e) { (async () => { const dictFile = $('#dict-file'); - const dictImporter = $('#dict-importer').hide(); + const dictControls = $('#dict-importer').hide(); const dictProgress = $('#dict-import-progress').show(); - const setProgress = percent => dictProgress.find('.progress-bar').css('width', `${percent}%`); - const updateProgress = (total, current) => setProgress(current / total * 100.0); - setProgress(0.0); + try { + dictionaryErrorShow(); + dictionarySpinnerShow(true); - optionsLoad().then(options => { - return instDb().importDictionary(e.target.files[0], updateProgress).then(summary => { - options.dictionaries[summary.title] = {enabled: true, priority: 0}; - return optionsSave(options); - }).then(() => dictionaryGroupsPopulate(options)); - }).catch(dictionaryErrorShow).then(() => { - dictFile.val(''); + const setProgress = percent => dictProgress.find('.progress-bar').css('width', `${percent}%`); + const updateProgress = (total, current) => setProgress(current / total * 100.0); + setProgress(0.0); + + const options = await optionsLoad(); + const summary = await instDb().importDictionary(e.target.files[0], updateProgress); + options.dictionaries[summary.title] = {enabled: true, priority: 0}; + await optionsSave(options); + + await dictionaryGroupsPopulate(options); + } catch (e) { + dictionaryErrorShow(e); + } finally { dictionarySpinnerShow(false); + + dictFile.val(''); + dictControls.show(); dictProgress.hide(); - dictImporter.show(); - }); -} + } +})();} /* * Anki @@ -307,40 +331,38 @@ function ankiFieldsToDict(selection) { return result; } -function ankiDeckAndModelPopulate(options) { - ankiErrorShow(null); - ankiSpinnerShow(true); - +async function ankiDeckAndModelPopulate(options) { const ankiFormat = $('#anki-format').hide(); - return Promise.all([instAnki().getDeckNames(), instAnki().getModelNames()]).then(([deckNames, modelNames]) => { - const ankiDeck = $('.anki-deck'); - ankiDeck.find('option').remove(); - deckNames.sort().forEach(name => ankiDeck.append($('
    - + diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 4c7198c3..9c3995eb 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -276,7 +276,7 @@ - + From 8bcc4ddf19a6c171ad1cf5d18b165da391089fd2 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 20 Jul 2017 21:21:22 -0700 Subject: [PATCH 29/68] cleanup --- ext/fg/frame.html | 3 +- ext/fg/js/background.js | 63 ++++++++++++++++++++++++++++++ ext/fg/js/{util.js => document.js} | 53 ------------------------- ext/manifest.json | 3 +- 4 files changed, 67 insertions(+), 55 deletions(-) create mode 100644 ext/fg/js/background.js rename ext/fg/js/{util.js => document.js} (82%) diff --git a/ext/fg/frame.html b/ext/fg/frame.html index ecaee323..80e69967 100644 --- a/ext/fg/frame.html +++ b/ext/fg/frame.html @@ -32,9 +32,10 @@ - + + diff --git a/ext/fg/js/background.js b/ext/fg/js/background.js new file mode 100644 index 00000000..52b533be --- /dev/null +++ b/ext/fg/js/background.js @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Alex Yatskov + * Author: Alex Yatskov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +function bgInvoke(action, params={}) { + return new Promise((resolve, reject) => { + try { + chrome.runtime.sendMessage({action, params}, ({result, error}) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + } catch (e) { + window.orphaned = true; + reject(e.message); + } + }); +} + +function bgOptionsGet() { + return bgInvoke('optionsGet'); +} + +function bgTermsFind(text) { + return bgInvoke('termsFind', {text}); +} + +function bgKanjiFind(text) { + return bgInvoke('kanjiFind', {text}); +} + +function bgTemplateRender(template, data) { + return bgInvoke('templateRender', {data, template}); +} + +function bgDefinitionsAddable(definitions, modes) { + return bgInvoke('definitionsAddable', {definitions, modes}).catch(() => null); +} + +function bgDefinitionAdd(definition, mode) { + return bgInvoke('definitionAdd', {definition, mode}); +} + +function bgNoteView(noteId) { + return bgInvoke('noteView', {noteId}); +} diff --git a/ext/fg/js/util.js b/ext/fg/js/document.js similarity index 82% rename from ext/fg/js/util.js rename to ext/fg/js/document.js index 2acd81c4..582b6770 100644 --- a/ext/fg/js/util.js +++ b/ext/fg/js/document.js @@ -17,59 +17,6 @@ */ -/* - * Background - */ - -function bgInvoke(action, params) { - return new Promise((resolve, reject) => { - try { - chrome.runtime.sendMessage({action, params}, ({result, error}) => { - if (error) { - reject(error); - } else { - resolve(result); - } - }); - } catch (e) { - window.orphaned = true; - reject(e.message); - } - }); -} - -function bgOptionsGet() { - return bgInvoke('optionsGet', {}); -} - -function bgTermsFind(text) { - return bgInvoke('termsFind', {text}); -} - -function bgKanjiFind(text) { - return bgInvoke('kanjiFind', {text}); -} - -function bgTemplateRender(template, data) { - return bgInvoke('templateRender', {data, template}); -} - -function bgDefinitionsAddable(definitions, modes) { - return bgInvoke('definitionsAddable', {definitions, modes}).catch(() => null); -} - -function bgDefinitionAdd(definition, mode) { - return bgInvoke('definitionAdd', {definition, mode}); -} - -function bgNoteView(noteId) { - return bgInvoke('noteView', {noteId}); -} - -/* - * Document - */ - function docOffsetCalc(element) { const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; diff --git a/ext/manifest.json b/ext/manifest.json index be9f3208..7a277fb2 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -15,9 +15,10 @@ "content_scripts": [{ "matches": ["http://*/*", "https://*/*", "file://*/*"], "js": [ - "fg/js/util.js", + "fg/js/document.js", "fg/js/source-range.js", "fg/js/source-element.js", + "fg/js/background.js", "fg/js/popup.js", "fg/js/driver.js" ], From d6c2f1cc38a2490afe79b0d8e1f24ad214ee9534 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 20 Jul 2017 21:29:27 -0700 Subject: [PATCH 30/68] cleanup --- ext/fg/js/{driver.js => frontend.js} | 2 +- ext/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename ext/fg/js/{driver.js => frontend.js} (99%) diff --git a/ext/fg/js/driver.js b/ext/fg/js/frontend.js similarity index 99% rename from ext/fg/js/driver.js rename to ext/fg/js/frontend.js index b0cc4613..5a51af6d 100644 --- a/ext/fg/js/driver.js +++ b/ext/fg/js/frontend.js @@ -17,7 +17,7 @@ */ -window.driver = new class { +window.frontend = new class { constructor() { this.popup = new Popup(); this.popupTimer = null; diff --git a/ext/manifest.json b/ext/manifest.json index 7a277fb2..a318e539 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -20,7 +20,7 @@ "fg/js/source-element.js", "fg/js/background.js", "fg/js/popup.js", - "fg/js/driver.js" + "fg/js/frontend.js" ], "css": ["fg/css/client.css"] }], From a0e2d9cb721d44a89715f3a9df10135c2aebfe29 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 20 Jul 2017 21:32:17 -0700 Subject: [PATCH 31/68] cleanup --- ext/bg/background.html | 2 +- ext/bg/js/{yomichan.js => backend.js} | 2 +- ext/bg/js/instance.js | 2 +- ext/fg/js/frontend.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename ext/bg/js/{yomichan.js => backend.js} (99%) diff --git a/ext/bg/background.html b/ext/bg/background.html index de3cbf20..27d9bc41 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -21,6 +21,6 @@ - + diff --git a/ext/bg/js/yomichan.js b/ext/bg/js/backend.js similarity index 99% rename from ext/bg/js/yomichan.js rename to ext/bg/js/backend.js index 214bdef3..f5415d93 100644 --- a/ext/bg/js/yomichan.js +++ b/ext/bg/js/backend.js @@ -17,7 +17,7 @@ */ -window.yomichan = new class { +window.yomichanBackend = new class { constructor() { handlebarsRegister(); diff --git a/ext/bg/js/instance.js b/ext/bg/js/instance.js index 0df267cc..bf858fbf 100644 --- a/ext/bg/js/instance.js +++ b/ext/bg/js/instance.js @@ -18,7 +18,7 @@ function instYomi() { - return chrome.extension.getBackgroundPage().yomichan; + return chrome.extension.getBackgroundPage().yomichanBackend; } function instDb() { diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 5a51af6d..e32a630d 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -17,7 +17,7 @@ */ -window.frontend = new class { +window.yomichanFrontend = new class { constructor() { this.popup = new Popup(); this.popupTimer = null; From 611b4420af9aa5c8ad6287996b73ae8fd2eb2f4b Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 20 Jul 2017 21:34:10 -0700 Subject: [PATCH 32/68] cleanup --- ext/fg/js/background.js | 2 +- ext/fg/js/display-frame.js | 2 +- ext/fg/js/frontend.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/fg/js/background.js b/ext/fg/js/background.js index 52b533be..c072468b 100644 --- a/ext/fg/js/background.js +++ b/ext/fg/js/background.js @@ -28,7 +28,7 @@ function bgInvoke(action, params={}) { } }); } catch (e) { - window.orphaned = true; + window.yomichanOrphaned = true; reject(e.message); } }); diff --git a/ext/fg/js/display-frame.js b/ext/fg/js/display-frame.js index b29a0379..c7da43e8 100644 --- a/ext/fg/js/display-frame.js +++ b/ext/fg/js/display-frame.js @@ -44,7 +44,7 @@ window.displayFrame = new class extends Display { } handleError(error) { - if (window.orphaned) { + if (window.yomichanOrphaned) { this.showOrphaned(); } else { window.alert(`Error: ${error}`); diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index e32a630d..8b4c182c 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -238,7 +238,7 @@ window.yomichanFrontend = new class { } handleError(error, textSource) { - if (window.orphaned) { + if (window.yomichanOrphaned) { if (textSource && this.options.scanning.modifier !== 'none') { this.popup.showOrphaned(textSource.getRect(), this.options); } From edf1c0ff6d9eadd17c98f00ef027c27d1b89a8ee Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 22 Jul 2017 23:19:38 -0700 Subject: [PATCH 33/68] cleanup --- ext/fg/frame.html | 2 +- ext/fg/js/{background.js => api.js} | 30 ++++++++++++++--------------- ext/fg/js/display-frame.js | 10 +++++----- ext/fg/js/frontend.js | 6 +++--- ext/manifest.json | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) rename ext/fg/js/{background.js => api.js} (64%) diff --git a/ext/fg/frame.html b/ext/fg/frame.html index 80e69967..9ff2c585 100644 --- a/ext/fg/frame.html +++ b/ext/fg/frame.html @@ -35,7 +35,7 @@ - + diff --git a/ext/fg/js/background.js b/ext/fg/js/api.js similarity index 64% rename from ext/fg/js/background.js rename to ext/fg/js/api.js index c072468b..e252637e 100644 --- a/ext/fg/js/background.js +++ b/ext/fg/js/api.js @@ -17,7 +17,7 @@ */ -function bgInvoke(action, params={}) { +function apiInvoke(action, params={}) { return new Promise((resolve, reject) => { try { chrome.runtime.sendMessage({action, params}, ({result, error}) => { @@ -34,30 +34,30 @@ function bgInvoke(action, params={}) { }); } -function bgOptionsGet() { - return bgInvoke('optionsGet'); +function apiOptionsGet() { + return apiInvoke('optionsGet'); } -function bgTermsFind(text) { - return bgInvoke('termsFind', {text}); +function apiTermsFind(text) { + return apiInvoke('termsFind', {text}); } -function bgKanjiFind(text) { - return bgInvoke('kanjiFind', {text}); +function apiKanjiFind(text) { + return apiInvoke('kanjiFind', {text}); } -function bgTemplateRender(template, data) { - return bgInvoke('templateRender', {data, template}); +function apiTemplateRender(template, data) { + return apiInvoke('templateRender', {data, template}); } -function bgDefinitionsAddable(definitions, modes) { - return bgInvoke('definitionsAddable', {definitions, modes}).catch(() => null); +function apiDefinitionsAddable(definitions, modes) { + return apiInvoke('definitionsAddable', {definitions, modes}).catch(() => null); } -function bgDefinitionAdd(definition, mode) { - return bgInvoke('definitionAdd', {definition, mode}); +function apiDefinitionAdd(definition, mode) { + return apiInvoke('definitionAdd', {definition, mode}); } -function bgNoteView(noteId) { - return bgInvoke('noteView', {noteId}); +function apiNoteView(noteId) { + return apiInvoke('noteView', {noteId}); } diff --git a/ext/fg/js/display-frame.js b/ext/fg/js/display-frame.js index c7da43e8..09bd9255 100644 --- a/ext/fg/js/display-frame.js +++ b/ext/fg/js/display-frame.js @@ -24,23 +24,23 @@ window.displayFrame = new class extends Display { } definitionAdd(definition, mode) { - return bgDefinitionAdd(definition, mode); + return apiDefinitionAdd(definition, mode); } definitionsAddable(definitions, modes) { - return bgDefinitionsAddable(definitions, modes); + return apiDefinitionsAddable(definitions, modes); } noteView(noteId) { - return bgNoteView(noteId); + return apiNoteView(noteId); } templateRender(template, data) { - return bgTemplateRender(template, data); + return apiTemplateRender(template, data); } kanjiFind(character) { - return bgKanjiFind(character); + return apiKanjiFind(character); } handleError(error) { diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 8b4c182c..9974d878 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -28,7 +28,7 @@ window.yomichanFrontend = new class { this.pendingLookup = false; this.options = null; - bgOptionsGet().then(options => { + apiOptionsGet().then(options => { this.options = options; window.addEventListener('mouseover', this.onMouseOver.bind(this)); window.addEventListener('mousedown', this.onMouseDown.bind(this)); @@ -175,7 +175,7 @@ window.yomichanFrontend = new class { searchTerms(textSource) { textSource.setEndOffset(this.options.scanning.length); - return bgTermsFind(textSource.text()).then(({definitions, length}) => { + return apiTermsFind(textSource.text()).then(({definitions, length}) => { if (definitions.length === 0) { return false; } else { @@ -203,7 +203,7 @@ window.yomichanFrontend = new class { searchKanji(textSource) { textSource.setEndOffset(1); - return bgKanjiFind(textSource.text()).then(definitions => { + return apiKanjiFind(textSource.text()).then(definitions => { if (definitions.length === 0) { return false; } else { diff --git a/ext/manifest.json b/ext/manifest.json index a318e539..288976f3 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -18,7 +18,7 @@ "fg/js/document.js", "fg/js/source-range.js", "fg/js/source-element.js", - "fg/js/background.js", + "fg/js/api.js", "fg/js/popup.js", "fg/js/frontend.js" ], From 32680c58b895e4c781cfb2f51a97fbff42e111b0 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 23 Jul 2017 22:48:33 -0700 Subject: [PATCH 34/68] cleanup --- ext/bg/background.html | 22 ++-- ext/bg/js/api.js | 236 ++++++++++++++++++++++++++++++++++++ ext/bg/js/backend.js | 223 ++-------------------------------- ext/bg/js/display-window.js | 20 --- ext/bg/js/instance.js | 30 ----- ext/bg/js/options.js | 2 +- ext/bg/popup.html | 1 - ext/bg/search.html | 1 - ext/bg/settings.html | 1 - ext/mixed/js/display.js | 34 ++---- 10 files changed, 265 insertions(+), 305 deletions(-) create mode 100644 ext/bg/js/api.js delete mode 100644 ext/bg/js/instance.js diff --git a/ext/bg/background.html b/ext/bg/background.html index 27d9bc41..1e9f3809 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -4,23 +4,25 @@ - - + - - - - - - - - + + + + + + + + + + + diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js new file mode 100644 index 00000000..3db0558b --- /dev/null +++ b/ext/bg/js/api.js @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2016 Alex Yatskov + * Author: Alex Yatskov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +function utilMessageDispatch({action, params}, sender, callback) { + const forward = (promise, callback) => { + return promise.then(result => { + callback({result}); + }).catch(error => { + callback({error}); + }); + }; + + const handlers = { + optionsGet: ({callback}) => { + forward(optionsLoad(), callback); + }, + + kanjiFind: ({text, callback}) => { + forward(apiKanjiFind(text), callback); + }, + + termsFind: ({text, callback}) => { + forward(apiTermsFind(text), callback); + }, + + templateRender: ({template, data, callback}) => { + forward(apiTemplateRender(template, data), callback); + }, + + definitionAdd: ({definition, mode, callback}) => { + forward(apiDefinitionAdd(definition, mode), callback); + }, + + definitionsAddable: ({definitions, modes, callback}) => { + forward(apiDefinitionsAddable(definitions, modes), callback); + }, + + noteView: ({noteId}) => { + forward(apiNoteView(noteId), callback); + } + }; + + const handler = handlers[action]; + if (handler) { + params.callback = callback; + handler(params); + } + + return true; +} + +function utilCommandDispatch(command) { + const handlers = { + search: () => { + chrome.tabs.create({url: chrome.extension.getURL('/bg/search.html')}); + }, + + help: () => { + chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'}); + }, + + options: () => { + chrome.runtime.openOptionsPage(); + }, + + toggle: () => { + this.options.general.enable = !this.options.general.enable; + optionsSave(this.options).then(() => this.optionsSet(this.options)); + } + }; + + const handler = handlers[command]; + if (handler) { + handler(); + } +} + +function utilNoteFormat(definition, mode) { + const options = Backend.instance().options; + const note = {fields: {}, tags: options.anki.tags}; + let fields = []; + + if (mode === 'kanji') { + fields = options.anki.kanji.fields; + note.deckName = options.anki.kanji.deck; + note.modelName = options.anki.kanji.model; + } else { + fields = options.anki.terms.fields; + note.deckName = options.anki.terms.deck; + note.modelName = options.anki.terms.model; + + if (definition.audio) { + const audio = { + url: definition.audio.url, + filename: definition.audio.filename, + skipHash: '7e2c2f954ef6051373ba916f000168dc', + fields: [] + }; + + for (const name in fields) { + if (fields[name].includes('{audio}')) { + audio.fields.push(name); + } + } + + if (audio.fields.length > 0) { + note.audio = audio; + } + } + } + + for (const name in fields) { + note.fields[name] = dictFieldFormat(fields[name], definition, mode, options); + } + + return note; +} + +async function apiOptionsSet(options) { + // In Firefox, setting options from the options UI somehow carries references + // to the DOM across to the background page, causing the options object to + // become a "DeadObject" after the options page is closed. The workaround used + // here is to create a deep copy of the options object. + Backend.instance().options = JSON.parse(JSON.stringify(options)); + + if (!options.general.enable) { + chrome.browserAction.setBadgeBackgroundColor({color: '#d9534f'}); + chrome.browserAction.setBadgeText({text: 'off'}); + } else if (!dictConfigured(options)) { + chrome.browserAction.setBadgeBackgroundColor({color: '#f0ad4e'}); + chrome.browserAction.setBadgeText({text: '!'}); + } else { + chrome.browserAction.setBadgeText({text: ''}); + } + + if (options.anki.enable) { + Backend.instance().anki = new AnkiConnect(options.anki.server); + } else { + Backend.instance().anki = new AnkiNull(); + } + + chrome.tabs.query({}, tabs => { + for (const tab of tabs) { + chrome.tabs.sendMessage(tab.id, {action: 'optionsSet', params: options}, () => null); + } + }); +} + +async function apiOptionsGet() { + return Backend.instance().options; +} + +async function apiTermsFind(text) { + const options = Backend.instance().options; + const translator = Backend.instance().translator; + + const searcher = options.general.groupResults ? + translator.findTermsGrouped.bind(translator) : + translator.findTerms.bind(translator); + + const {definitions, length} = await searcher( + text, + dictEnabledSet(options), + options.scanning.alphanumeric + ); + + return { + length, + definitions: definitions.slice(0, options.general.maxResults) + }; +} + +async function apiKanjiFind(text) { + const options = Backend.instance().options; + const definitions = await Backend.instance().translator.findKanji(text, dictEnabledSet(options)); + return definitions.slice(0, options.general.maxResults); +} + +async function apiDefinitionAdd(definition, mode) { + if (mode !== 'kanji') { + const options = Backend.instance().options; + await audioInject( + definition, + options.anki.terms.fields, + options.general.audioSource + ); + } + + return Backend.instance().anki.addNote(utilNoteFormat(definition, mode)); +} + +async function apiDefinitionsAddable(definitions, modes) { + const notes = []; + for (const definition of definitions) { + for (const mode of modes) { + notes.push(utilNoteFormat(definition, mode)); + } + } + + const results = await Backend.instance().anki.canAddNotes(notes); + const states = []; + for (let resultBase = 0; resultBase < results.length; resultBase += modes.length) { + const state = {}; + for (let modeOffset = 0; modeOffset < modes.length; ++modeOffset) { + state[modes[modeOffset]] = results[resultBase + modeOffset]; + } + + states.push(state); + } + + return states; +} + +async function apiNoteView(noteId) { + return Backend.instance().anki.guiBrowse(`nid:${noteId}`); +} + +async function apiTemplateRender(template, data) { + return handlebarsRender(template, data); +} diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index f5415d93..e977735e 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -17,7 +17,7 @@ */ -window.yomichanBackend = new class { +class Backend { constructor() { handlebarsRegister(); @@ -26,225 +26,20 @@ window.yomichanBackend = new class { this.options = null; this.translator.prepare().then(optionsLoad).then(options => { - this.optionsSet(options); + apiOptionsSet(options); - chrome.commands.onCommand.addListener(this.onCommand.bind(this)); - chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); + chrome.commands.onCommand.addListener(utilCommandDispatch); + chrome.runtime.onMessage.addListener(utilMessageDispatch); - if (this.options.general.showGuide) { + if (options.general.showGuide) { chrome.tabs.create({url: chrome.extension.getURL('/bg/guide.html')}); } }); } - optionsSet(options) { - // In Firefox, setting options from the options UI somehow carries references - // to the DOM across to the background page, causing the options object to - // become a "DeadObject" after the options page is closed. The workaround used - // here is to create a deep copy of the options object. - this.options = JSON.parse(JSON.stringify(options)); - - if (!this.options.general.enable) { - chrome.browserAction.setBadgeBackgroundColor({color: '#d9534f'}); - chrome.browserAction.setBadgeText({text: 'off'}); - } else if (!dictConfigured(this.options)) { - chrome.browserAction.setBadgeBackgroundColor({color: '#f0ad4e'}); - chrome.browserAction.setBadgeText({text: '!'}); - } else { - chrome.browserAction.setBadgeText({text: ''}); - } - - if (this.options.anki.enable) { - this.anki = new AnkiConnect(this.options.anki.server); - } else { - this.anki = new AnkiNull(); - } - - chrome.tabs.query({}, tabs => { - for (const tab of tabs) { - chrome.tabs.sendMessage(tab.id, {action: 'optionsSet', params: options}, () => null); - } - }); - } - - noteFormat(definition, mode) { - const note = { - fields: {}, - tags: this.options.anki.tags - }; - - let fields = []; - if (mode === 'kanji') { - fields = this.options.anki.kanji.fields; - note.deckName = this.options.anki.kanji.deck; - note.modelName = this.options.anki.kanji.model; - } else { - fields = this.options.anki.terms.fields; - note.deckName = this.options.anki.terms.deck; - note.modelName = this.options.anki.terms.model; - - if (definition.audio) { - const audio = { - url: definition.audio.url, - filename: definition.audio.filename, - skipHash: '7e2c2f954ef6051373ba916f000168dc', - fields: [] - }; - - for (const name in fields) { - if (fields[name].includes('{audio}')) { - audio.fields.push(name); - } - } - - if (audio.fields.length > 0) { - note.audio = audio; - } - } - } - - for (const name in fields) { - note.fields[name] = dictFieldFormat( - fields[name], - definition, - mode, - this.options - ); - } - - return note; - } - - termsFind(text) { - const searcher = this.options.general.groupResults ? - this.translator.findTermsGrouped.bind(this.translator) : - this.translator.findTerms.bind(this.translator); - - return searcher(text, dictEnabledSet(this.options), this.options.scanning.alphanumeric).then(({definitions, length}) => { - return {length, definitions: definitions.slice(0, this.options.general.maxResults)}; - }); - } - - kanjiFind(text) { - return this.translator.findKanji(text, dictEnabledSet(this.options)).then(definitions => { - return definitions.slice(0, this.options.general.maxResults); - }); - } - - definitionAdd(definition, mode) { - let promise = Promise.resolve(); - if (mode !== 'kanji') { - promise = audioInject(definition, this.options.anki.terms.fields, this.options.general.audioSource); - } - - return promise.then(() => { - const note = this.noteFormat(definition, mode); - return this.anki.addNote(note); - }); - } - - definitionsAddable(definitions, modes) { - const notes = []; - for (const definition of definitions) { - for (const mode of modes) { - notes.push(this.noteFormat(definition, mode)); - } - } - - return this.anki.canAddNotes(notes).then(raw => { - const states = []; - for (let resultBase = 0; resultBase < raw.length; resultBase += modes.length) { - const state = {}; - for (let modeOffset = 0; modeOffset < modes.length; ++modeOffset) { - state[modes[modeOffset]] = raw[resultBase + modeOffset]; - } - - states.push(state); - } - - return states; - }); - } - - noteView(noteId) { - return this.anki.guiBrowse(`nid:${noteId}`); - } - - templateRender(template, data) { - return Promise.resolve(handlebarsRender(template, data)); - } - - onCommand(command) { - const handlers = { - search: () => { - chrome.tabs.create({url: chrome.extension.getURL('/bg/search.html')}); - }, - - help: () => { - chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'}); - }, - - options: () => { - chrome.runtime.openOptionsPage(); - }, - - toggle: () => { - this.options.general.enable = !this.options.general.enable; - optionsSave(this.options).then(() => this.optionsSet(this.options)); - } - }; - - const handler = handlers[command]; - if (handler) { - handler(); - } - } - - onMessage({action, params}, sender, callback) { - const promiseCallback = (promise, callback) => { - return promise.then(result => { - callback({result}); - }).catch(error => { - callback({error}); - }); - }; - - const handlers = { - optionsGet: ({callback}) => { - promiseCallback(optionsLoad(), callback); - }, - - kanjiFind: ({text, callback}) => { - promiseCallback(this.kanjiFind(text), callback); - }, - - termsFind: ({text, callback}) => { - promiseCallback(this.termsFind(text), callback); - }, - - templateRender: ({template, data, callback}) => { - promiseCallback(this.templateRender(template, data), callback); - }, - - definitionAdd: ({definition, mode, callback}) => { - promiseCallback(this.definitionAdd(definition, mode), callback); - }, - - definitionsAddable: ({definitions, modes, callback}) => { - promiseCallback(this.definitionsAddable(definitions, modes), callback); - }, - - noteView: ({noteId}) => { - promiseCallback(this.noteView(noteId), callback); - } - }; - - const handler = handlers[action]; - if (handler) { - params.callback = callback; - handler(params); - } - - return true; + static instance() { + return chrome.extension.getBackgroundPage().yomichanBackend; } }; + +window.yomichanBackend = new Backend(); diff --git a/ext/bg/js/display-window.js b/ext/bg/js/display-window.js index 64e56f72..e5357bf9 100644 --- a/ext/bg/js/display-window.js +++ b/ext/bg/js/display-window.js @@ -29,26 +29,6 @@ window.displayWindow = new class extends Display { window.wanakana.bind(query.get(0)); } - definitionAdd(definition, mode) { - return instYomi().definitionAdd(definition, mode); - } - - definitionsAddable(definitions, modes) { - return instYomi().definitionsAddable(definitions, modes).catch(() => []); - } - - noteView(noteId) { - return instYomi().noteView(noteId); - } - - templateRender(template, data) { - return instYomi().templateRender(template, data); - } - - kanjiFind(character) { - return instYomi().kanjiFind(character); - } - handleError(error) { window.alert(`Error: ${error}`); } diff --git a/ext/bg/js/instance.js b/ext/bg/js/instance.js deleted file mode 100644 index bf858fbf..00000000 --- a/ext/bg/js/instance.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2016 Alex Yatskov - * Author: Alex Yatskov - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -function instYomi() { - return chrome.extension.getBackgroundPage().yomichanBackend; -} - -function instDb() { - return instYomi().translator.database; -} - -function instAnki() { - return instYomi().anki; -} diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index d611ae59..ea52d337 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -126,6 +126,6 @@ function optionsSave(options) { return new Promise((resolve, reject) => { chrome.storage.local.set({options: JSON.stringify(options)}, resolve); }).then(() => { - instYomi().optionsSet(options); + apiOptionsSet(options); }); } diff --git a/ext/bg/popup.html b/ext/bg/popup.html index baeb2ffb..26c0f0bb 100644 --- a/ext/bg/popup.html +++ b/ext/bg/popup.html @@ -30,7 +30,6 @@ - diff --git a/ext/bg/search.html b/ext/bg/search.html index 472907c2..7cbae392 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -33,7 +33,6 @@
    - diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 9c3995eb..2d0b4b4c 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -276,7 +276,6 @@ - diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index e54bc0f9..f408fb25 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -32,26 +32,6 @@ class Display { $(document).keydown(this.onKeyDown.bind(this)); } - definitionAdd(definition, mode) { - throw 'override me'; - } - - definitionsAddable(definitions, modes) { - throw 'override me'; - } - - noteView(noteId) { - throw 'override me'; - } - - templateRender(template, data) { - throw 'override me'; - } - - kanjiFind(character) { - throw 'override me'; - } - handleError(error) { throw 'override me'; } @@ -87,7 +67,7 @@ class Display { } } - this.templateRender('terms.html', params).then(content => { + apiTemplateRender('terms.html', params).then(content => { this.container.html(content); this.entryScroll(context && context.index || 0); @@ -126,7 +106,7 @@ class Display { } } - this.templateRender('kanji.html', params).then(content => { + apiTemplateRender('kanji.html', params).then(content => { this.container.html(content); this.entryScroll(context && context.index || 0); @@ -138,7 +118,7 @@ class Display { } adderButtonsUpdate(modes, sequence) { - return this.definitionsAddable(this.definitions, modes).then(states => { + return apiDefinitionsAddable(this.definitions, modes).then(states => { if (!states || sequence !== this.sequence) { return; } @@ -198,7 +178,7 @@ class Display { context.url = this.context.url; } - this.kanjiFind(link.text()).then(kanjiDefs => { + apiKanjiFind(link.text()).then(kanjiDefs => { this.showKanjiDefs(kanjiDefs, this.options, context); }).catch(this.handleError.bind(this)); } @@ -220,7 +200,7 @@ class Display { e.preventDefault(); const link = $(e.currentTarget); const index = Display.entryIndexFind(link); - this.noteView(link.data('noteId')); + apiNoteView(link.data('noteId')); } onKeyDown(e) { @@ -234,7 +214,7 @@ class Display { const noteTryView = mode => { const button = Display.viewerButtonFind(this.index); if (button.length !== 0 && !button.hasClass('disabled')) { - this.noteView(button.data('noteId')); + apiNoteView(button.data('noteId')); } }; @@ -351,7 +331,7 @@ class Display { noteAdd(definition, mode) { this.spinner.show(); - return this.definitionAdd(definition, mode).then(noteId => { + return apiDefinitionAdd(definition, mode).then(noteId => { if (noteId) { const index = this.definitions.indexOf(definition); Display.adderButtonFind(index, mode).addClass('disabled'); From eed630e9fa3c62610bda356e9c76393e70c1a08c Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Mon, 24 Jul 2017 22:44:19 -0700 Subject: [PATCH 35/68] cleanup --- ext/bg/js/backend.js | 2 +- ext/bg/js/popup.js | 10 ++++------ ext/bg/popup.html | 7 +++++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index e977735e..0615ffae 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -40,6 +40,6 @@ class Backend { static instance() { return chrome.extension.getBackgroundPage().yomichanBackend; } -}; +} window.yomichanBackend = new Backend(); diff --git a/ext/bg/js/popup.js b/ext/bg/js/popup.js index 01994827..712839d9 100644 --- a/ext/bg/js/popup.js +++ b/ext/bg/js/popup.js @@ -18,16 +18,14 @@ $(document).ready(() => { - const commandExec = command => instYomi().onCommand(command); - - $('#open-search').click(() => commandExec('search')); - $('#open-options').click(() => commandExec('options')); - $('#open-help').click(() => commandExec('help')); + $('#open-search').click(() => utilCommandDispatch('search')); + $('#open-options').click(() => utilCommandDispatch('options')); + $('#open-help').click(() => utilCommandDispatch('help')); optionsLoad().then(options => { const toggle = $('#enable-search'); toggle.prop('checked', options.general.enable).change(); toggle.bootstrapToggle(); - toggle.change(() => commandExec('toggle')); + toggle.change(() => utilCommandDispatch('toggle')); }); }); diff --git a/ext/bg/popup.html b/ext/bg/popup.html index 26c0f0bb..4113b008 100644 --- a/ext/bg/popup.html +++ b/ext/bg/popup.html @@ -27,13 +27,16 @@

    + + + - - + + From 1d5bdddfa62438e79384885eaa324c8ec4161229 Mon Sep 17 00:00:00 2001 From: Musee Ullah Date: Thu, 27 Jul 2017 00:18:57 -0700 Subject: [PATCH 36/68] Import terms with transactional adds instead of bulkAdd --- ext/bg/js/database.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index 70aeb0d7..4829356c 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -189,20 +189,19 @@ class Database { }; const termsLoaded = (title, entries, total, current) => { - const rows = []; - for (const [expression, reading, tags, rules, score, ...glossary] of entries) { - rows.push({ - expression, - reading, - tags, - rules, - score, - glossary, - dictionary: title - }); - } - - return this.db.terms.bulkAdd(rows).then(() => { + return this.db.transaction('rw', this.db.terms, function() { + for (const [expression, reading, tags, rules, score, ...glossary] of entries) { + this.db.terms.add({ + expression, + reading, + tags, + rules, + score, + glossary, + dictionary: title + }); + } + }).then(() => { if (callback) { callback(total, current); } From adbc7a32cc0cb050b29443e0719e36dd8fafde58 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 27 Jul 2017 21:42:14 -0700 Subject: [PATCH 37/68] cleanup --- ext/bg/js/api.js | 35 ++++++++++++++++++++------------ ext/bg/js/backend.js | 10 ++------- ext/bg/js/settings.js | 47 +++++++++++++++++++++++++++++++++++++------ ext/bg/settings.html | 9 ++++++--- 4 files changed, 71 insertions(+), 30 deletions(-) diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 3db0558b..6d6ec2ea 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -92,7 +92,8 @@ function utilCommandDispatch(command) { } function utilNoteFormat(definition, mode) { - const options = Backend.instance().options; + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + const options = yomichan.options; const note = {fields: {}, tags: options.anki.tags}; let fields = []; @@ -137,7 +138,8 @@ async function apiOptionsSet(options) { // to the DOM across to the background page, causing the options object to // become a "DeadObject" after the options page is closed. The workaround used // here is to create a deep copy of the options object. - Backend.instance().options = JSON.parse(JSON.stringify(options)); + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + yomichan.options = JSON.parse(JSON.stringify(options)); if (!options.general.enable) { chrome.browserAction.setBadgeBackgroundColor({color: '#d9534f'}); @@ -150,9 +152,9 @@ async function apiOptionsSet(options) { } if (options.anki.enable) { - Backend.instance().anki = new AnkiConnect(options.anki.server); + yomichan.anki = new AnkiConnect(options.anki.server); } else { - Backend.instance().anki = new AnkiNull(); + yomichan.anki = new AnkiNull(); } chrome.tabs.query({}, tabs => { @@ -163,12 +165,14 @@ async function apiOptionsSet(options) { } async function apiOptionsGet() { - return Backend.instance().options; + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.options; } async function apiTermsFind(text) { - const options = Backend.instance().options; - const translator = Backend.instance().translator; + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + const options = yomichan.options; + const translator = yomichan.translator; const searcher = options.general.groupResults ? translator.findTermsGrouped.bind(translator) : @@ -187,14 +191,17 @@ async function apiTermsFind(text) { } async function apiKanjiFind(text) { - const options = Backend.instance().options; - const definitions = await Backend.instance().translator.findKanji(text, dictEnabledSet(options)); + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + const options = yomichan.options; + const definitions = await yomichan.translator.findKanji(text, dictEnabledSet(options)); return definitions.slice(0, options.general.maxResults); } async function apiDefinitionAdd(definition, mode) { + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + if (mode !== 'kanji') { - const options = Backend.instance().options; + const options = yomichan.options; await audioInject( definition, options.anki.terms.fields, @@ -202,7 +209,7 @@ async function apiDefinitionAdd(definition, mode) { ); } - return Backend.instance().anki.addNote(utilNoteFormat(definition, mode)); + return yomichan.anki.addNote(utilNoteFormat(definition, mode)); } async function apiDefinitionsAddable(definitions, modes) { @@ -213,7 +220,8 @@ async function apiDefinitionsAddable(definitions, modes) { } } - const results = await Backend.instance().anki.canAddNotes(notes); + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + const results = await yomichan.anki.canAddNotes(notes); const states = []; for (let resultBase = 0; resultBase < results.length; resultBase += modes.length) { const state = {}; @@ -228,7 +236,8 @@ async function apiDefinitionsAddable(definitions, modes) { } async function apiNoteView(noteId) { - return Backend.instance().anki.guiBrowse(`nid:${noteId}`); + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.anki.guiBrowse(`nid:${noteId}`); } async function apiTemplateRender(template, data) { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 0615ffae..4fabe4b2 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -17,7 +17,7 @@ */ -class Backend { +window.yomichanBackend = new class { constructor() { handlebarsRegister(); @@ -36,10 +36,4 @@ class Backend { } }); } - - static instance() { - return chrome.extension.getBackgroundPage().yomichanBackend; - } -} - -window.yomichanBackend = new Backend(); +}; diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 3cd64fbd..09e92249 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -17,6 +17,41 @@ */ +/* + * Utilities + */ + +function utilAnkiGetModelNames() { + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.anki.getModelNames(); +} + +function utilAnkiGetDeckNames() { + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.anki.getDeckNames(); +} + +function utilAnkiGetModelFieldNames(modelName) { + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.anki.utilAnkiGetModelFieldNames(modelName); +} + +function utilDatabaseGetDictionaries() { + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.translator.database.getDictionaries(); +} + +function utilDatabasePurge() { + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.translator.database.purge(); +} + +function utilDatabaseImport(data, progress) { + const yomichan = chrome.extension.getBackgroundPage().yomichanBackend; + return yomichan.translator.database.importDictionary(data, progress); +} + + /* * General */ @@ -217,7 +252,7 @@ async function dictionaryGroupsPopulate(options) { const dictGroups = $('#dict-groups').empty(); const dictWarning = $('#dict-warning').hide(); - const dictRows = await instDb().getDictionaries(); + const dictRows = await utilDatabaseGetDictionaries(); if (dictRows.length === 0) { dictWarning.show(); } @@ -253,7 +288,7 @@ async function onDictionaryPurge(e) {(async () => { dictionaryErrorShow(); dictionarySpinnerShow(true); - await instDb().purge(); + await utilDatabasePurge(); const options = await optionsLoad(); options.dictionaries = {}; await optionsSave(options); @@ -283,7 +318,7 @@ function onDictionaryImport(e) {(async () => { setProgress(0.0); const options = await optionsLoad(); - const summary = await instDb().importDictionary(e.target.files[0], updateProgress); + const summary = await utilDatabaseImport(e.target.files[0], updateProgress); options.dictionaries[summary.title] = {enabled: true, priority: 0}; await optionsSave(options); @@ -344,12 +379,12 @@ async function ankiDeckAndModelPopulate(options) { $('#anki-terms-deck').val(options.anki.terms.deck); $('#anki-kanji-deck').val(options.anki.kanji.deck); - const deckNames = await instAnki().getDeckNames(); + const deckNames = await utilAnkiGetDeckNames(); const ankiDeck = $('.anki-deck'); ankiDeck.find('option').remove(); deckNames.sort().forEach(name => ankiDeck.append($('
    " + alias4(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data}) : helper))) @@ -450,7 +450,7 @@ templates['model.html'] = template({"1":function(container,depth0,helpers,partia + " \n \n \n