diff options
| author | Alex Yatskov <alex@foosoft.net> | 2016-05-05 21:36:50 -0700 | 
|---|---|---|
| committer | Alex Yatskov <alex@foosoft.net> | 2016-05-05 21:36:50 -0700 | 
| commit | f1e078c7d8fa09e8471e66b245afb80a1b78cc6b (patch) | |
| tree | e6dd29654f7bbf3771d57b40e9d342a3b07cd823 | |
| parent | cc33a30b5eb531651346a407040f65b9ebb8a6c8 (diff) | |
Removing flicker
| -rw-r--r-- | ' | 197 | ||||
| -rw-r--r-- | ext/bg/js/templates.js | 32 | ||||
| -rw-r--r-- | ext/fg/js/client.js | 27 | ||||
| -rw-r--r-- | ext/fg/js/frame.js | 4 | ||||
| -rw-r--r-- | util/tmpl/kanji-list.html | 2 | ||||
| -rw-r--r-- | util/tmpl/kanji.html | 2 | ||||
| -rw-r--r-- | util/tmpl/term-list.html | 2 | ||||
| -rw-r--r-- | util/tmpl/term.html | 4 | 
8 files changed, 246 insertions, 24 deletions
| @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016  Alex Yatskov <alex@foosoft.net> + * Author: Alex Yatskov <alex@foosoft.net> + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +class Client { +    constructor() { +        this.popup        = new Popup(); +        this.lastMousePos = null; +        this.lastRange    = null; +        this.activateKey  = 16; +        this.activateBtn  = 2; +        this.enabled      = false; +        this.options      = {}; +        this.definitions  = null; +        this.sequence     = 0; +        this.fgRoot       = chrome.extension.getURL('fg'); + +        chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this)); +        window.addEventListener('message', this.onFrameMessage.bind(this)); +        window.addEventListener('mousedown', this.onMouseDown.bind(this)); +        window.addEventListener('mousemove', this.onMouseMove.bind(this)); +        window.addEventListener('keydown', this.onKeyDown.bind(this)); +        window.addEventListener('scroll', (e) => this.hidePopup()); +        window.addEventListener('resize', (e) => this.hidePopup()); + +        bgGetOptions((opts) => { +            this.setOptions(opts); +            bgGetState((state) => this.setEnabled(state === 'enabled')); +        }); +    } + +    onKeyDown(e) { +        if (this.enabled && this.lastMousePos !== null && (e.keyCode === this.activateKey || e.charCode === this.activateKey)) { +            this.searchAt(this.lastMousePos); +        } +    } + +    onMouseMove(e) { +        this.lastMousePos = {x: e.clientX, y: e.clientY}; +        if (this.enabled && (e.shiftKey || e.which === this.activateBtn)) { +            this.searchAt(this.lastMousePos); +        } +    } + +    onMouseDown(e) { +        this.lastMousePos = {x: e.clientX, y: e.clientY}; +        if (this.enabled && (e.shiftKey || e.which === this.activateBtn)) { +            this.searchAt(this.lastMousePos); +        } else { +            this.hidePopup(); +        } +    } + +    onBgMessage({name, value}, sender, callback) { +        switch (name) { +            case 'state': +                this.setEnabled(value === 'enabled'); +                break; +            case 'options': +                this.setOptions(value); +                break; +        } + +        callback(); +    } + +    onFrameMessage(e) { +        const {action, params} = e.data, handlers = { +            addNote:      ({mode, index}) => this.actionAddNote(mode, index, (data) => e.source.postMessage(data, e.origin)), +            displayKanji: this.actionDisplayKanji +        }; + +        if (handlers.hasOwnProperty(action)) { +            handlers[action].call(this, params); +        } +    } + +    searchAt(point) { +        const range = Range.fromPoint(point); +        if (range === null || !range.containsPoint(point)) { +            this.hidePopup(); +            return; +        } + +        if (this.lastRange !== null && this.lastRange.compareOrigin(range) === 0) { +            return; +        } + +        range.setLength(this.options.scanLength); +        bgFindTerm(range.text(), ({definitions, length}) => { +            if (length === 0) { +                this.hidePopup(); +            } else { +                const sequence = this.sequence++; +                range.setLength(length); +                bgRenderText( +                    {defs: definitions, root: this.fgRoot, options: this.options, sequence: sequence}, +                    'term-list.html', +                    (content) => { +                        this.definitions = definitions; +                        this.showPopup(range, content); + +                        bgCanAddNotes(definitions, (states) => { +                            if (states !== null) { +                                states.forEach((state, index) => this.popup.sendMessage( +                                    'setActionState', +                                    {index: index, state: state, sequence: sequence} +                                )); +                            } +                        }); +                    } +                ); +            } +        }); +    } + +    actionAddNote(mode, index, callback) { +        const state = {}; +        state[mode] = false; + +        this.popup.sendMessage( +            'setActionState', +            {index: index, state: state, sequence: this.sequence} +        ); +    } + +    actionDisplayKanji(kanji) { +        bgFindKanji(kanji, (definitions) => { +            const sequence = this.sequence++; +            bgRenderText( +                {defs: definitions, root: this.fgRoot, options: this.options, sequence: sequence}, +                'kanji-list.html', +                (content) => { +                    this.definitions = definitions; +                    this.popup.setContent(content, definitions); + +                    bgCanAddNotes(definitions, (states) => { +                        if (states !== null) { +                            states.forEach((state, index) => this.popup.sendMessage( +                                'setActionState', +                                {index: index, state: state, sequence: sequence} +                            )); +                        } +                    }); +                } +            ); +        }); +    } + +    showPopup(range, content) { +        this.popup.showNextTo(range.getRect(), content); + +        if (this.options.selectMatchedText) { +            range.select(); +        } + +        this.lastRange = range; +    } + +    hidePopup() { +        this.popup.hide(); + +        if (this.options.selectMatchedText && this.lastRange !== null) { +            this.lastRange.deselect(); +        } + +        this.lastRange   = null; +        this.definitions = null; +    } + +    setEnabled(enabled) { +        if (!(this.enabled = enabled)) { +            this.hidePopup(); +        } +    } + +    setOptions(opts) { +        this.options = opts; +    } +} + +window.yomiClient = new Client(); diff --git a/ext/bg/js/templates.js b/ext/bg/js/templates.js index 54033197..d0749d7b 100644 --- a/ext/bg/js/templates.js +++ b/ext/bg/js/templates.js @@ -21,12 +21,14 @@ templates['kanji.html'] = template({"1":function(container,depth0,helpers,partia    return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.enableAnkiConnect : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");  },"2":function(container,depth0,helpers,partials,data,blockParams,depths) { -    var helper, alias1=container.escapeExpression; +    var helper, alias1=container.lambda, alias2=container.escapeExpression; -  return "    <a href=\"#\" title=\"Add Kanji\" class=\"action-link disabled\" data-mode=\"kanji\" data-index=\"" -    + alias1(((helper = (helper = helpers.index || (data && data.index)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"index","hash":{},"data":data}) : helper))) +  return "    <a href=\"#\" title=\"Add Kanji\" class=\"action-link disabled\" data-sequence=\"" +    + alias2(alias1((depths[1] != null ? depths[1].sequence : depths[1]), depth0)) +    + "\" data-mode=\"kanji\" data-index=\"" +    + alias2(((helper = (helper = helpers.index || (data && data.index)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"index","hash":{},"data":data}) : helper)))      + "\"><img src=\"" -    + alias1(container.lambda((depths[1] != null ? depths[1].root : depths[1]), depth0)) +    + alias2(alias1((depths[1] != null ? depths[1].root : depths[1]), depth0))      + "/add_kanji.png\"></a>\n";  },"4":function(container,depth0,helpers,partials,data) {      var stack1; @@ -80,7 +82,7 @@ templates['kanji-link.html'] = template({"compiler":[7,">= 4.0.0"],"main":functi  templates['kanji-list.html'] = template({"1":function(container,depth0,helpers,partials,data,blockParams,depths) {      var stack1; -  return ((stack1 = container.invokePartial(partials["kanji.html"],depth0,{"name":"kanji.html","hash":{"options":(depths[1] != null ? depths[1].options : depths[1]),"root":(depths[1] != null ? depths[1].root : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); +  return ((stack1 = container.invokePartial(partials["kanji.html"],depth0,{"name":"kanji.html","hash":{"sequence":(depths[1] != null ? depths[1].sequence : depths[1]),"options":(depths[1] != null ? depths[1].options : depths[1]),"root":(depths[1] != null ? depths[1].root : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");  },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {      var stack1; @@ -93,16 +95,20 @@ templates['term.html'] = template({"1":function(container,depth0,helpers,partial    return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.enableAnkiConnect : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");  },"2":function(container,depth0,helpers,partials,data,blockParams,depths) { -    var helper, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression, alias5=container.lambda; +    var helper, alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : {}, alias4=helpers.helperMissing, alias5="function"; -  return "    <div class=\"action-bar\">\n        <a href=\"#\" title=\"Add as expression\" class=\"action-link disabled\" data-mode=\"vocabExp\" data-index=\"" -    + alias4(((helper = (helper = helpers.index || (data && data.index)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"index","hash":{},"data":data}) : helper))) +  return "    <div class=\"action-bar\">\n        <a href=\"#\" title=\"Add as expression\" class=\"action-link disabled\" data-sequence=\"" +    + alias2(alias1((depths[1] != null ? depths[1].sequence : depths[1]), depth0)) +    + "\" data-mode=\"vocabExp\" data-index=\"" +    + alias2(((helper = (helper = helpers.index || (data && data.index)) != null ? helper : alias4),(typeof helper === alias5 ? helper.call(alias3,{"name":"index","hash":{},"data":data}) : helper)))      + "\"><img src=\"" -    + alias4(alias5((depths[1] != null ? depths[1].root : depths[1]), depth0)) -    + "/img/add_expression.png\"></a>\n        <a href=\"#\" title=\"Add as reading\" class=\"action-link disabled\" data-mode=\"vocabReading\" data-index=\"" -    + alias4(((helper = (helper = helpers.index || (data && data.index)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"index","hash":{},"data":data}) : helper))) +    + alias2(alias1((depths[1] != null ? depths[1].root : depths[1]), depth0)) +    + "/img/add_expression.png\"></a>\n        <a href=\"#\" title=\"Add as reading\" class=\"action-link disabled\" data-sequence=\"" +    + alias2(alias1((depths[1] != null ? depths[1].sequence : depths[1]), depth0)) +    + "\" data-mode=\"vocabReading\" data-index=\"" +    + alias2(((helper = (helper = helpers.index || (data && data.index)) != null ? helper : alias4),(typeof helper === alias5 ? helper.call(alias3,{"name":"index","hash":{},"data":data}) : helper)))      + "\"><img src=\"" -    + alias4(alias5((depths[1] != null ? depths[1].root : depths[1]), depth0)) +    + alias2(alias1((depths[1] != null ? depths[1].root : depths[1]), depth0))      + "/img/add_reading.png\"></a>\n    </div>\n";  },"4":function(container,depth0,helpers,partials,data) {      var stack1, helper, options, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function", buffer =  @@ -178,7 +184,7 @@ templates['term.html'] = template({"1":function(container,depth0,helpers,partial  templates['term-list.html'] = template({"1":function(container,depth0,helpers,partials,data,blockParams,depths) {      var stack1; -  return ((stack1 = container.invokePartial(partials["term.html"],depth0,{"name":"term.html","hash":{"options":(depths[1] != null ? depths[1].options : depths[1]),"root":(depths[1] != null ? depths[1].root : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : ""); +  return ((stack1 = container.invokePartial(partials["term.html"],depth0,{"name":"term.html","hash":{"sequence":(depths[1] != null ? depths[1].sequence : depths[1]),"options":(depths[1] != null ? depths[1].options : depths[1]),"root":(depths[1] != null ? depths[1].root : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");  },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {      var stack1; diff --git a/ext/fg/js/client.js b/ext/fg/js/client.js index e7300874..14c0a95f 100644 --- a/ext/fg/js/client.js +++ b/ext/fg/js/client.js @@ -27,6 +27,7 @@ class Client {          this.enabled      = false;          this.options      = {};          this.definitions  = null; +        this.sequence     = 0;          this.fgRoot       = chrome.extension.getURL('fg');          chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this)); @@ -105,16 +106,21 @@ class Client {              if (length === 0) {                  this.hidePopup();              } else { +                const sequence = this.sequence++;                  range.setLength(length);                  bgRenderText( -                    {defs: definitions, root: this.fgRoot, options: this.options}, +                    {defs: definitions, root: this.fgRoot, options: this.options, sequence: sequence},                      'term-list.html',                      (content) => {                          this.definitions = definitions;                          this.showPopup(range, content); +                          bgCanAddNotes(definitions, (states) => {                              if (states !== null) { -                                states.forEach((state, index) => this.popup.sendMessage('setActionState', {index: index, state: state})); +                                states.forEach((state, index) => this.popup.sendMessage( +                                    'setActionState', +                                    {index: index, state: state, sequence: sequence} +                                ));                              }                          });                      } @@ -127,17 +133,30 @@ class Client {          const state = {};          state[mode] = false; -        this.popup.sendMessage('setActionState', {index: index, state: state}); +        this.popup.sendMessage( +            'setActionState', +            {index: index, state: state, sequence: this.sequence} +        );      }      actionDisplayKanji(kanji) {          bgFindKanji(kanji, (definitions) => { +            const sequence = this.sequence++;              bgRenderText( -                {defs: definitions, root: this.fgRoot, options: this.options}, +                {defs: definitions, root: this.fgRoot, options: this.options, sequence: sequence},                  'kanji-list.html',                  (content) => {                      this.definitions = definitions;                      this.popup.setContent(content, definitions); + +                    bgCanAddNotes(definitions, (states) => { +                        if (states !== null) { +                            states.forEach((state, index) => this.popup.sendMessage( +                                'setActionState', +                                {index: index, state: state, sequence: sequence} +                            )); +                        } +                    });                  }              );          }); diff --git a/ext/fg/js/frame.js b/ext/fg/js/frame.js index 38848bfc..66d3a054 100644 --- a/ext/fg/js/frame.js +++ b/ext/fg/js/frame.js @@ -43,9 +43,9 @@ function onDomContentLoaded() {  function onMessage(e) {      const {action, params} = e.data, handlers = { -        setActionState: ({index, state}) => { +        setActionState: ({index, state, sequence}) => {              for (const mode in state) { -                const matches = document.querySelectorAll(`.action-link[data-index="${index}"][data-mode="${mode}"]`); +                const matches = document.querySelectorAll(`.action-link[data-sequence="${sequence}"][data-index="${index}"][data-mode="${mode}"]`);                  if (matches.length === 0) {                      return;                  } diff --git a/util/tmpl/kanji-list.html b/util/tmpl/kanji-list.html index 094441f2..b5d0b627 100644 --- a/util/tmpl/kanji-list.html +++ b/util/tmpl/kanji-list.html @@ -1,5 +1,5 @@  {{> header.html}}  {{#each defs}} -{{> kanji.html root=../root options=../options}} +{{> kanji.html root=../root options=../options sequence=../sequence}}  {{/each}}  {{> footer.html}} diff --git a/util/tmpl/kanji.html b/util/tmpl/kanji.html index 119fead6..2b195577 100644 --- a/util/tmpl/kanji.html +++ b/util/tmpl/kanji.html @@ -1,7 +1,7 @@  <div class="kanji-definition">      {{#with options}}      {{#if enableAnkiConnect}} -    <a href="#" title="Add Kanji" class="action-link disabled" data-mode="kanji" data-index="{{@index}}"><img src="{{../root}}/add_kanji.png"></a> +    <a href="#" title="Add Kanji" class="action-link disabled" data-sequence="{{../sequence}}" data-mode="kanji" data-index="{{@index}}"><img src="{{../root}}/add_kanji.png"></a>      {{/if}}      {{/with}} diff --git a/util/tmpl/term-list.html b/util/tmpl/term-list.html index e4944a2f..5581b679 100644 --- a/util/tmpl/term-list.html +++ b/util/tmpl/term-list.html @@ -1,5 +1,5 @@  {{> header.html}}  {{#each defs}} -{{> term.html root=../root options=../options}} +{{> term.html root=../root options=../options sequence=../sequence}}  {{/each}}  {{> footer.html}} diff --git a/util/tmpl/term.html b/util/tmpl/term.html index da29271c..4d764f0a 100644 --- a/util/tmpl/term.html +++ b/util/tmpl/term.html @@ -2,8 +2,8 @@      {{#with options}}      {{#if enableAnkiConnect}}      <div class="action-bar"> -        <a href="#" title="Add as expression" class="action-link disabled" data-mode="vocabExp" data-index="{{@index}}"><img src="{{../root}}/img/add_expression.png"></a> -        <a href="#" title="Add as reading" class="action-link disabled" data-mode="vocabReading" data-index="{{@index}}"><img src="{{../root}}/img/add_reading.png"></a> +        <a href="#" title="Add as expression" class="action-link disabled" data-sequence="{{../sequence}}" data-mode="vocabExp" data-index="{{@index}}"><img src="{{../root}}/img/add_expression.png"></a> +        <a href="#" title="Add as reading" class="action-link disabled" data-sequence="{{../sequence}}" data-mode="vocabReading" data-index="{{@index}}"><img src="{{../root}}/img/add_reading.png"></a>      </div>      {{/if}}      {{/with}} |