diff options
| author | Alex Yatskov <alex@foosoft.net> | 2017-05-25 23:06:10 -0700 | 
|---|---|---|
| committer | Alex Yatskov <alex@foosoft.net> | 2017-05-25 23:06:10 -0700 | 
| commit | 16baf9a400ac11a1c4f53ba6665db03a900511e0 (patch) | |
| tree | 685096179093f112a9263f15d3cf081f50e9634d | |
| parent | 1cdf496824709b4e4579d700c333e832d790925e (diff) | |
| parent | 618a3cb319c247c7196b1b83389d5f43241ab0c6 (diff) | |
Merge branch 'master' into firefox-amo
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | ext/bg/js/options.js | 21 | ||||
| -rw-r--r-- | ext/bg/js/templates.js | 62 | ||||
| -rw-r--r-- | ext/bg/js/translator.js | 12 | ||||
| -rw-r--r-- | ext/bg/js/util.js | 32 | ||||
| -rw-r--r-- | ext/bg/js/yomichan.js | 2 | ||||
| -rw-r--r-- | ext/bg/lang/deinflect.json | 8 | ||||
| -rw-r--r-- | ext/bg/options.html | 41 | ||||
| -rw-r--r-- | ext/fg/js/driver.js | 21 | ||||
| -rw-r--r-- | ext/fg/js/source-element.js | 33 | ||||
| -rw-r--r-- | ext/fg/js/source-range.js | 90 | ||||
| -rw-r--r-- | ext/fg/js/util.js | 22 | ||||
| -rw-r--r-- | ext/manifest.json | 2 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 6 | ||||
| -rw-r--r-- | tmpl/kanji.html | 6 | ||||
| -rw-r--r-- | tmpl/terms.html | 6 | 
16 files changed, 231 insertions, 137 deletions
| @@ -27,7 +27,7 @@ Yomichan provides advanced features not available in other browser-based diction  *   **Mozilla Firefox** (versions 51+)      *   [Locally hosted](https://foosoft.net/projects/yomichan/dl/latest.xpi) *(recommended)*: Latest and greatest, released simultaneously with the Chrome version.      *   [Marketplace hosted](https://addons.mozilla.org/en-US/firefox/addon/yomichan/): Officially hosted version, -        likely to be substantially out of date (queued for initial approval). +        likely to be substantially out of date.  ## Basic Features ## @@ -89,7 +89,7 @@ Flashcard fields can be configured with the following steps:  2.  Tick the checkbox labeled *Enable Anki integration* (Anki must be running with [AnkiConnect](https://foosoft.net/projects/anki-connect) installed).  3.  Select the type of template to configure by clicking on either the *Terms* or *Kanji* tabs.  4.  Select the Anki deck and model to use for new creating new flashcards of this type. -5.  Fill out the displayed model fields with markers representing the information you wish to include: +5.  Fill the model fields with markers corresponding to the information you wish to include (several can be used):      #### Markers for Term Cards #### diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 1ea6ed68..c3321012 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -27,22 +27,21 @@ function formRead() {          optionsNew.general.showGuide = $('#show-usage-guide').prop('checked');          optionsNew.general.audioSource = $('#audio-playback-source').val(); -        optionsNew.general.audioVolume = $('#audio-playback-volume').val(); +        optionsNew.general.audioVolume = parseFloat($('#audio-playback-volume').val());          optionsNew.general.groupResults = $('#group-terms-results').prop('checked'); -        optionsNew.general.softKatakana = $('#soft-katakana-search').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.requireShift = $('#hold-shift-to-scan').prop('checked');          optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked');          optionsNew.scanning.selectText = $('#select-matched-text').prop('checked'); -        optionsNew.scanning.imposter = $('#search-form-text-fields').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(/[,; ]+/); @@ -85,6 +84,15 @@ function updateVisibility(options) {      } else {          advanced.hide();      } + +    const debug = $('#debug'); +    if (options.general.debugInfo) { +        const text = JSON.stringify(options, null, 4); +        debug.html(handlebarsEscape(text)); +        debug.show(); +    } else { +        debug.hide(); +    }  }  function onOptionsChanged(e) { @@ -117,20 +125,19 @@ $(document).ready(() => {          $('#audio-playback-source').val(options.general.audioSource);          $('#audio-playback-volume').val(options.general.audioVolume);          $('#group-terms-results').prop('checked', options.general.groupResults); -        $('#soft-katakana-search').prop('checked', options.general.softKatakana); +        $('#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); -        $('#hold-shift-to-scan').prop('checked', options.scanning.requireShift);          $('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse);          $('#select-matched-text').prop('checked', options.scanning.selectText); -        $('#search-form-text-fields').prop('checked', options.scanning.imposter);          $('#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);          $('#dict-purge').click(onDictionaryPurge);          $('#dict-importer a').click(onDictionarySetUrl); diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js index d093b177..d2e5435e 100644 --- a/ext/bg/js/templates.js +++ b/ext/bg/js/templates.js @@ -328,7 +328,9 @@ templates['kanji.html'] = template({"1":function(container,depth0,helpers,partia      + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")      + "    </div>\n\n    <div class=\"glossary\">\n"      + ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(11, data, 0),"inverse":container.program(15, data, 0),"data":data})) != null ? stack1 : "") -    + "    </div>\n</div>\n"; +    + "    </div>\n\n" +    + ((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 : "") +    + "</div>\n";  },"2":function(container,depth0,helpers,partials,data) {      return "        <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"kanji\"><img src=\"/mixed/img/add-kanji.png\" title=\"Add Kanji (Alt + K)\" alt></a>\n";  },"4":function(container,depth0,helpers,partials,data) { @@ -378,25 +380,36 @@ templates['kanji.html'] = template({"1":function(container,depth0,helpers,partia      var stack1;    return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0)); -},"18":function(container,depth0,helpers,partials,data,blockParams,depths) { +},"18":function(container,depth0,helpers,partials,data) { +    var stack1, helper, options, buffer =  +  "    <pre>"; +  stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(19, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper)); +  if (!helpers.dumpObject) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} +  if (stack1 != null) { buffer += stack1; } +  return buffer + "</pre>\n"; +},"19":function(container,depth0,helpers,partials,data) {      var stack1; -  return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(19, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"19":function(container,depth0,helpers,partials,data,blockParams,depths) { +  return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : ""); +},"21":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(20, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "") +  return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(22, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); +},"22":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(23, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")      + "\n" -    + ((stack1 = container.invokePartial(partials.kanji,depth0,{"name":"kanji","hash":{"root":(depths[1] != null ? depths[1].root : depths[1]),"source":(depths[1] != null ? depths[1].source : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"20":function(container,depth0,helpers,partials,data) { +    + ((stack1 = container.invokePartial(partials.kanji,depth0,{"name":"kanji","hash":{"root":(depths[1] != null ? depths[1].root : depths[1]),"source":(depths[1] != null ? depths[1].source : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1]),"debug":(depths[1] != null ? depths[1].debug : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); +},"23":function(container,depth0,helpers,partials,data) {      return "<hr>"; -},"22":function(container,depth0,helpers,partials,data) { +},"25":function(container,depth0,helpers,partials,data) {      return "<p>No results found.</p>\n";  },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {      var stack1;    return "\n" -    + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(18, data, 0, blockParams, depths),"inverse":container.program(22, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); +    + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(21, data, 0, blockParams, depths),"inverse":container.program(25, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");  },"main_d":  function(fn, props, container, depth0, data, blockParams, depths) {    var decorators = container.decorators; @@ -482,7 +495,9 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia      + ((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 : "")      + "\n    <div class=\"glossary\">\n"      + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.grouped : depth0),{"name":"if","hash":{},"fn":container.program(26, data, 0),"inverse":container.program(32, data, 0),"data":data})) != null ? stack1 : "") -    + "    </div>\n</div>\n"; +    + "    </div>\n\n" +    + ((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 : "") +    + "</div>\n";  },"13":function(container,depth0,helpers,partials,data) {      return "        <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"term-kanji\"><img src=\"/mixed/img/add-term-kanji.png\" title=\"Add expression (Alt + E)\" alt></a>\n        <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"term-kana\"><img src=\"/mixed/img/add-term-kana.png\" title=\"Add reading (Alt + R)\" alt></a>\n";  },"15":function(container,depth0,helpers,partials,data) { @@ -547,25 +562,36 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia      var stack1;    return ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","data":data,"indent":"        ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"34":function(container,depth0,helpers,partials,data,blockParams,depths) { +},"34":function(container,depth0,helpers,partials,data) { +    var stack1, helper, options, buffer =  +  "    <pre>"; +  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)); +  if (!helpers.dumpObject) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)} +  if (stack1 != null) { buffer += stack1; } +  return buffer + "</pre>\n"; +},"35":function(container,depth0,helpers,partials,data) { +    var stack1; + +  return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : ""); +},"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(35, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : ""); -},"35":function(container,depth0,helpers,partials,data,blockParams,depths) { +  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 : ""); +},"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(36, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? 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 : "")      + "\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])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); -},"36":function(container,depth0,helpers,partials,data) { +    + ((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) {      return "<hr>"; -},"38":function(container,depth0,helpers,partials,data) { +},"41":function(container,depth0,helpers,partials,data) {      return "<p>No results found.</p>\n";  },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {      var stack1;    return "\n\n" -    + ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(34, data, 0, blockParams, depths),"inverse":container.program(38, data, 0, blockParams, depths),"data":data})) != null ? stack1 : ""); +    + ((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 : "");  },"main_d":  function(fn, props, container, depth0, data, blockParams, depths) {    var decorators = container.decorators; diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index b3b90641..8d65a0cd 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -41,7 +41,7 @@ class Translator {          });      } -    findTerms(text, dictionaries, softKatakana, alphanumeric) { +    findTerms(text, dictionaries, alphanumeric) {          const titles = Object.keys(dictionaries);          const cache = {}; @@ -54,10 +54,10 @@ class Translator {          return this.findTermsDeinflected(text, titles, cache).then(deinfLiteral => {              const textHiragana = wanakana._katakanaToHiragana(text); -            if (text !== textHiragana && softKatakana) { -                return this.findTermsDeinflected(textHiragana, titles, cache).then(deinfHiragana => deinfLiteral.concat(deinfHiragana)); -            } else { +            if (text === textHiragana) {                  return deinfLiteral; +            } else { +                return this.findTermsDeinflected(textHiragana, titles, cache).then(deinfHiragana => deinfLiteral.concat(deinfHiragana));              }          }).then(deinflections => {              let definitions = []; @@ -91,8 +91,8 @@ class Translator {          });      } -    findTermsGrouped(text, dictionaries, softKatakana, alphanumeric) { -        return this.findTerms(text, dictionaries, softKatakana, alphanumeric).then(({length, definitions}) => { +    findTermsGrouped(text, dictionaries, alphanumeric) { +        return this.findTerms(text, dictionaries, alphanumeric).then(({length, definitions}) => {              return {length, definitions: dictTermsGroup(definitions, dictionaries)};          });      } diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 09337c63..75833871 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -98,7 +98,7 @@ function optionsSetDefaults(options) {              audioSource: 'jpod101',              audioVolume: 100,              groupResults: true, -            softKatakana: true, +            debugInfo: false,              maxResults: 32,              showAdvanced: false,              popupWidth: 400, @@ -108,13 +108,12 @@ function optionsSetDefaults(options) {          },          scanning: { -            requireShift: true,              middleMouse: true,              selectText: true, -            imposter: true,              alphanumeric: true,              delay: 15, -            length: 10 +            length: 10, +            modifier: 'shift'          },          dictionaries: {}, @@ -150,10 +149,10 @@ function optionsSetDefaults(options) {  function optionsVersion(options) {      const fixups = [ -        () => { }, -        () => { }, -        () => { }, -        () => { }, +        () => {}, +        () => {}, +        () => {}, +        () => {},          () => {              if (options.general.audioPlayback) {                  options.general.audioSource = 'jpod101'; @@ -163,6 +162,13 @@ function optionsVersion(options) {          },          () => {              options.general.showGuide = false; +        }, +        () => { +            if (options.scanning.requireShift) { +                options.scanning.modifier = 'shift'; +            } else { +                options.scanning.modifier = 'none'; +            }          }      ]; @@ -503,6 +509,15 @@ function jsonLoadDb(indexUrl, indexLoaded, termsLoaded, kanjiLoaded) {   * Helpers   */ +function handlebarsEscape(text) { +    return Handlebars.Utils.escapeExpression(text); +} + +function handlebarsDumpObject(options) { +    const dump = JSON.stringify(options.fn(this), null, 4); +    return handlebarsEscape(dump); +} +  function handlebarsKanjiLinks(options) {      let result = '';      for (const c of options.fn(this)) { @@ -522,6 +537,7 @@ function handlebarsMultiLine(options) {  function handlebarsRegister() {      Handlebars.partials = Handlebars.templates; +    Handlebars.registerHelper('dumpObject', handlebarsDumpObject);      Handlebars.registerHelper('kanjiLinks', handlebarsKanjiLinks);      Handlebars.registerHelper('multiLine', handlebarsMultiLine);  } diff --git a/ext/bg/js/yomichan.js b/ext/bg/js/yomichan.js index 7be67d01..b4beb179 100644 --- a/ext/bg/js/yomichan.js +++ b/ext/bg/js/yomichan.js @@ -111,7 +111,7 @@ window.yomichan = new class {              this.translator.findTermsGrouped.bind(this.translator) :              this.translator.findTerms.bind(this.translator); -        return searcher(text, dictEnabledSet(this.options), this.options.general.softKatakana, this.options.scanning.alphanumeric).then(({definitions, length}) => { +        return searcher(text, dictEnabledSet(this.options), this.options.scanning.alphanumeric).then(({definitions, length}) => {              return {length, definitions: definitions.slice(0, this.options.general.maxResults)};          });      } diff --git a/ext/bg/lang/deinflect.json b/ext/bg/lang/deinflect.json index 532f8e62..7ee00b2f 100644 --- a/ext/bg/lang/deinflect.json +++ b/ext/bg/lang/deinflect.json @@ -961,14 +961,6 @@          },          {              "kanaIn": "って", -            "kanaOut": "く", -            "rulesIn": [], -            "rulesOut": [ -                "v5" -            ] -        }, -        { -            "kanaIn": "って",              "kanaOut": "つ",              "rulesIn": [],              "rulesOut": [ diff --git a/ext/bg/options.html b/ext/bg/options.html index c3799b61..6a359f5e 100644 --- a/ext/bg/options.html +++ b/ext/bg/options.html @@ -8,6 +8,7 @@          <style>              #anki-spinner, #anki-general, #anki-error,              #dict-spinner, #dict-error, #dict-warning, #dict-purge-progress, #dict-import-progress, +            #debug,              .options-advanced {                  display: none;              } @@ -38,7 +39,7 @@                  </div>                  <div class="checkbox options-advanced"> -                    <label><input type="checkbox" id="soft-katakana-search"> Soft Katakana search</label> +                    <label><input type="checkbox" id="show-debug-info"> Show debug information</label>                  </div>                  <div class="form-group"> @@ -46,7 +47,7 @@                      <select class="form-control" id="audio-playback-source">                          <option value="disabled">Disabled</option>                          <option value="jpod101">JapanesePod101</option> -                        <option value="jpod101-alternate">JapanesePod101 (Alternate)</option> +                        <option value="jpod101-alternate">JapanesePod101 (alternate)</option>                      </select>                  </div> @@ -61,7 +62,7 @@                  </div>                  <div class="form-group options-advanced"> -                    <label>Popup size (width x height, in pixels)</label> +                    <label>Popup size (width × height, in pixels)</label>                      <div class="row">                          <div class="col-xs-6"><input type="number" min="1" id="popup-width" class="form-control"></div>                          <div class="col-xs-6"><input type="number" min="1" id="popup-height" class="form-control"></div> @@ -82,30 +83,32 @@                  </div>                  <div class="checkbox"> -                    <label><input type="checkbox" id="hold-shift-to-scan"> Hold <kbd>Shift</kbd> to scan</label> -                </div> - -                <div class="checkbox">                      <label><input type="checkbox" id="select-matched-text"> Select matched text</label>                  </div>                  <div class="checkbox"> -                    <label><input type="checkbox" id="search-form-text-fields"> Search form text fields</label> -                </div> - -                <div class="checkbox">                      <label><input type="checkbox" id="search-alphanumeric"> Search alphanumeric text</label>                  </div>                  <div class="form-group options-advanced"> -                    <label for="scan-delay">Scan delay</label> +                    <label for="scan-delay">Scan delay (in milliseconds)</label>                      <input type="number" min="1" id="scan-delay" class="form-control">                  </div>                  <div class="form-group options-advanced"> -                    <label for="scan-length">Scan length</label> +                    <label for="scan-length">Scan length (in characters)</label>                      <input type="number" min="1" id="scan-length" class="form-control">                  </div> + +                <div class="form-group"> +                    <label for="scan-modifier-key">Scan modifier key</label> +                    <select class="form-control" id="scan-modifier-key"> +                        <option value="none">None</option> +                        <option value="alt">Alt</option> +                        <option value="ctrl">Ctrl</option> +                        <option value="shift">Shift</option> +                    </select> +                </div>              </div>              <div> @@ -172,15 +175,15 @@                      <a href="https://foosoft.net/projects/anki-connect/">AnkiConnect</a> plugin for Anki.                  </p> -                <div class="checkbox"> -                    <label><input type="checkbox" id="anki-enable"> Enable Anki integration</label> -                </div> -                  <div class="alert alert-danger" id="anki-error">                      <strong>Error:</strong>                      <span></span>                  </div> +                <div class="checkbox"> +                    <label><input type="checkbox" id="anki-enable"> Enable Anki integration</label> +                </div> +                  <div id="anki-general">                      <div class="checkbox options-advanced">                          <label><input type="checkbox" id="generate-html-cards"> Generate HTML cards</label> @@ -192,7 +195,7 @@                      </div>                      <div class="form-group options-advanced"> -                        <label for="sentence-detection-extent">Sentence detection extent</label> +                        <label for="sentence-detection-extent">Sentence detection extent (in characters)</label>                          <input type="number" min="1" id="sentence-detection-extent" class="form-control">                      </div> @@ -268,6 +271,8 @@                          <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4DBTN9A3CUAFN"><img src="/bg/img/paypal.gif" alt></a>                      </p>                  </div> + +                <pre id="debug"></pre>              </div>              <div class="pull-right"> diff --git a/ext/fg/js/driver.js b/ext/fg/js/driver.js index e94a4ac2..b0cc4613 100644 --- a/ext/fg/js/driver.js +++ b/ext/fg/js/driver.js @@ -70,15 +70,22 @@ window.driver = new class {              return;          } -        if (this.options.scanning.requireShift && !e.shiftKey && !(this.mouseDownMiddle && this.options.scanning.middleMouse)) { +        const mouseScan = this.mouseDownMiddle && this.options.scanning.middleMouse; +        const keyScan = +            this.options.scanning.modifier === 'alt' && e.altKey || +            this.options.scanning.modifier === 'ctrl' && e.ctrlKey || +            this.options.scanning.modifier === 'shift' && e.shiftKey || +            this.options.scanning.modifier === 'none'; + +        if (!keyScan && !mouseScan) {              return;          }          const searchFunc = () => this.searchAt(this.lastMousePos); -        if (this.options.scanning.requireShift) { -            searchFunc(); -        } else { +        if (this.options.scanning.modifier === 'none') {              this.popupTimerSet(searchFunc); +        } else { +            searchFunc();          }      } @@ -142,8 +149,9 @@ window.driver = new class {              return;          } -        const textSource = docRangeFromPoint(point, this.options.scanning.imposter); +        const textSource = docRangeFromPoint(point);          if (!textSource || !textSource.containsPoint(point)) { +            docImposterDestroy();              return;          } @@ -159,6 +167,7 @@ window.driver = new class {          }).catch(error => {              this.handleError(error, textSource);          }).then(() => { +            docImposterDestroy();              this.pendingLookup = false;          });      } @@ -230,7 +239,7 @@ window.driver = new class {      handleError(error, textSource) {          if (window.orphaned) { -            if (textSource && this.options.scanning.requireShift) { +            if (textSource && this.options.scanning.modifier !== 'none') {                  this.popup.showOrphaned(textSource.getRect(), this.options);              }          } else { diff --git a/ext/fg/js/source-element.js b/ext/fg/js/source-element.js index 69fbc5ab..a8101382 100644 --- a/ext/fg/js/source-element.js +++ b/ext/fg/js/source-element.js @@ -18,41 +18,42 @@  class TextSourceElement { -    constructor(element, length=-1) { +    constructor(element, content='') {          this.element = element; -        this.length = length; +        this.content = content;      }      clone() { -        return new TextSourceElement(this.element, this.length); +        return new TextSourceElement(this.element, this.content);      }      text() { -        const text = this.textRaw(); -        return this.length < 0 ? text : text.substring(0, this.length); +        return this.content;      } -    textRaw() { +    setEndOffset(length) {          switch (this.element.nodeName) {              case 'BUTTON': -                return this.element.innerHTML; +                this.content = this.element.innerHTML; +                break;              case 'IMG': -                return this.element.getAttribute('alt'); +                this.content = this.element.getAttribute('alt'); +                break;              default: -                return this.element.value || ''; +                this.content = this.element.value; +                break;          } + +        this.content = this.content || ''; +        this.content = this.content.substring(0, length); + +        return this.content.length;      }      setStartOffset(length) { -        // NOP          return 0;      } -    setEndOffset(length) { -        this.length = length; -        return length; -    } -      containsPoint(point) {          const rect = this.getRect();          return point.x >= rect.left && point.x <= rect.right; @@ -71,6 +72,6 @@ class TextSourceElement {      }      equals(other) { -        return other.element && other.textRaw() === this.textRaw(); +        return other.element === this.element && other.content === this.content;      }  } diff --git a/ext/fg/js/source-range.js b/ext/fg/js/source-range.js index 6d445f54..fa73b0a4 100644 --- a/ext/fg/js/source-range.js +++ b/ext/fg/js/source-range.js @@ -18,30 +18,31 @@  class TextSourceRange { -    constructor(range) { -        this.rng = range; +    constructor(range, content='') { +        this.range = range; +        this.content = content;      }      clone() { -        return new TextSourceRange(this.rng.cloneRange()); +        return new TextSourceRange(this.range.cloneRange(), this.content);      }      text() { -        return this.rng.toString(); +        return this.content;      }      setEndOffset(length) { -        const lengthAdj = length + this.rng.startOffset; -        const state = TextSourceRange.seekForward(this.rng.startContainer, lengthAdj); -        this.rng.setEnd(state.node, state.offset); -        return length - state.length; +        const state = TextSourceRange.seekForward(this.range.startContainer, this.range.startOffset, length); +        this.range.setEnd(state.node, state.offset); +        this.content = state.content; +        return length - state.remainder;      }      setStartOffset(length) { -        const lengthAdj = length + (this.rng.startContainer.length - this.rng.startOffset); -        const state = TextSourceRange.seekBackward(this.rng.startContainer, lengthAdj); -        this.rng.setStart(state.node, state.offset); -        return length - state.length; +        const state = TextSourceRange.seekBackward(this.range.startContainer, this.range.startOffset, length); +        this.range.setStart(state.node, state.offset); +        this.content = state.content; +        return length - state.remainder;      }      containsPoint(point) { @@ -50,11 +51,11 @@ class TextSourceRange {      }      getRect() { -        return this.rng.getBoundingClientRect(); +        return this.range.getBoundingClientRect();      }      getPaddedRect() { -        const range = this.rng.cloneRange(); +        const range = this.range.cloneRange();          const startOffset = range.startOffset;          const endOffset = range.endOffset;          const node = range.startContainer; @@ -68,7 +69,7 @@ class TextSourceRange {      select() {          const selection = window.getSelection();          selection.removeAllRanges(); -        selection.addRange(this.rng); +        selection.addRange(this.range);      }      deselect() { @@ -77,11 +78,30 @@ class TextSourceRange {      }      equals(other) { -        return other.rng && other.rng.compareBoundaryPoints(Range.START_TO_START, this.rng) === 0; +        return other.range && other.range.compareBoundaryPoints(Range.START_TO_START, this.range) === 0;      } -    static seekForward(node, length) { -        const state = {node, length, offset: 0}; +    static shouldEnter(node) { +        if (node.nodeType !== 1) { +            return false; +        } + +        const skip = ['RT', 'SCRIPT', 'STYLE']; +        if (skip.includes(node.nodeName)) { +            return false; +        } + +        const style = window.getComputedStyle(node); +        const hidden = +            style.visibility === 'hidden' || +            style.display === 'none' || +            parseFloat(style.fontSize) === 0; + +        return !hidden; +    } + +    static seekForward(node, offset, length) { +        const state = {node, offset, remainder: length, content: ''};          if (!TextSourceRange.seekForwardHelper(node, state)) {              return state;          } @@ -98,12 +118,15 @@ class TextSourceRange {      }      static seekForwardHelper(node, state) { -        if (node.nodeType === 3) { -            const consumed = Math.min(node.length, state.length); +        if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) { +            const offset = state.node === node ? state.offset : 0; +            const remaining = node.length - offset; +            const consumed = Math.min(remaining, state.remainder); +            state.content = state.content + node.nodeValue.substring(offset, offset + consumed);              state.node = node; -            state.offset = consumed; -            state.length -= consumed; -        } else { +            state.offset = offset + consumed; +            state.remainder -= consumed; +        } else if (TextSourceRange.shouldEnter(node)) {              for (let i = 0; i < node.childNodes.length; ++i) {                  if (!TextSourceRange.seekForwardHelper(node.childNodes[i], state)) {                      break; @@ -111,11 +134,11 @@ class TextSourceRange {              }          } -        return state.length > 0; +        return state.remainder > 0;      } -    static seekBackward(node, length) { -        const state = {node, length, offset: node.length}; +    static seekBackward(node, offset, length) { +        const state = {node, offset, remainder: length, content: ''};          if (!TextSourceRange.seekBackwardHelper(node, state)) {              return state;          } @@ -132,12 +155,15 @@ class TextSourceRange {      }      static seekBackwardHelper(node, state) { -        if (node.nodeType === 3) { -            const consumed = Math.min(node.length, state.length); +        if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) { +            const offset = state.node === node ? state.offset : node.length; +            const remaining = offset; +            const consumed = Math.min(remaining, state.remainder); +            state.content = node.nodeValue.substring(offset - consumed, offset) + state.content;              state.node = node; -            state.offset = node.length - consumed; -            state.length -= consumed; -        } else { +            state.offset = offset - consumed; +            state.remainder -= consumed; +        } else if (TextSourceRange.shouldEnter(node)) {              for (let i = node.childNodes.length - 1; i >= 0; --i) {                  if (!TextSourceRange.seekBackwardHelper(node.childNodes[i], state)) {                      break; @@ -145,6 +171,6 @@ class TextSourceRange {              }          } -        return state.length > 0; +        return state.remainder > 0;      }  } diff --git a/ext/fg/js/util.js b/ext/fg/js/util.js index e5705ffd..c6270ce6 100644 --- a/ext/fg/js/util.js +++ b/ext/fg/js/util.js @@ -112,18 +112,12 @@ function docImposterDestroy() {      }  } -function docImposterHide() { -    for (const element of document.getElementsByClassName('yomichan-imposter')) { -        element.style.visibility = 'hidden'; -    } -} - -function docRangeFromPoint(point, imposter) { +function docRangeFromPoint(point) {      const element = document.elementFromPoint(point.x, point.y);      if (element !== null) {          if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') {              return new TextSourceElement(element); -        } else if (imposter && (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA')) { +        } else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {              docImposterCreate(element);          }      } @@ -144,11 +138,9 @@ function docRangeFromPoint(point, imposter) {      const range = document.caretRangeFromPoint(point.x, point.y);      if (range !== null) { -        docImposterHide();          return new TextSourceRange(range);      } -    docImposterDestroy();      return null;  } @@ -168,6 +160,11 @@ function docSentenceExtract(source, extent) {      for (let i = position; i >= startPos; --i) {          const c = content[i]; +        if (c === '\n') { +            startPos = i + 1; +            break; +        } +          if (quoteStack.length === 0 && (terminators.includes(c) || c in quotesFwd)) {              startPos = i + 1;              break; @@ -186,6 +183,11 @@ function docSentenceExtract(source, extent) {      for (let i = position; i <= endPos; ++i) {          const c = content[i]; +        if (c === '\n') { +            endPos = i + 1; +            break; +        } +          if (quoteStack.length === 0) {              if (terminators.includes(c)) {                  endPos = i + 1; diff --git a/ext/manifest.json b/ext/manifest.json index e6c709dd..dcad034e 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -1,7 +1,7 @@  {      "manifest_version": 2,      "name": "Yomichan", -    "version": "1.1.15", +    "version": "1.1.18",      "description": "Japanese dictionary with Anki integration",      "icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"}, diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 0167c605..7982c69f 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -69,7 +69,8 @@ class Display {              definitions,              addable: options.anki.enable,              grouped: options.general.groupResults, -            playback: options.general.audioSource !== 'disabled' +            playback: options.general.audioSource !== 'disabled', +            debug: options.general.debugInfo          };          if (context) { @@ -106,7 +107,8 @@ class Display {          const params = {              definitions,              source: context && context.source, -            addable: options.anki.enable +            addable: options.anki.enable, +            debug: options.general.debugInfo          };          if (context) { diff --git a/tmpl/kanji.html b/tmpl/kanji.html index acd79036..a2f78aff 100644 --- a/tmpl/kanji.html +++ b/tmpl/kanji.html @@ -50,13 +50,17 @@          <div class="glossary-item">{{#multiLine}}{{glossary.[0]}}{{/multiLine}}</div>          {{/if}}      </div> + +    {{#if debug}} +    <pre>{{#dumpObject}}{{{.}}}{{/dumpObject}}</pre> +    {{/if}}  </div>  {{/inline}}  {{#if definitions}}  {{#each definitions}}  {{#unless @first}}<hr>{{/unless}} -{{> kanji addable=../addable source=../source root=../root}} +{{> kanji debug=../debug addable=../addable source=../source root=../root}}  {{/each}}  {{else}}  <p>No results found.</p> diff --git a/tmpl/terms.html b/tmpl/terms.html index dc50efe2..80735974 100644 --- a/tmpl/terms.html +++ b/tmpl/terms.html @@ -59,13 +59,17 @@          {{> definition}}          {{/if}}      </div> + +    {{#if debug}} +    <pre>{{#dumpObject}}{{{.}}}{{/dumpObject}}</pre> +    {{/if}}  </div>  {{/inline}}  {{#if definitions}}  {{#each definitions}}  {{#unless @first}}<hr>{{/unless}} -{{> term grouped=../grouped addable=../addable playback=../playback}} +{{> term debug=../debug grouped=../grouped addable=../addable playback=../playback}}  {{/each}}  {{else}}  <p>No results found.</p> |