diff options
| -rw-r--r-- | ext/fg/js/frontend.js | 13 | ||||
| -rw-r--r-- | ext/mixed/js/text-scanner.js | 88 | 
2 files changed, 73 insertions, 28 deletions
| diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 43a4830f..2adbde36 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -20,7 +20,14 @@  class Frontend {      constructor(popup, ignoreNodes) {          this.popup = popup; -        this.textScanner = new TextScanner(window, ignoreNodes, this.popup, this.searchSource.bind(this)); +        this.textScanner = new TextScanner( +            window, +            ignoreNodes, +            [this.popup.container], +            [(x, y) => this.popup.containsPoint(x, y)] +        ); +        this.textScanner.subscribe('textSearch', ({textSource, cause}) => this.searchSource(textSource, cause)); +        this.textScanner.subscribe('searchClear', () => this.searchClear(true));          this.options = null;          this.optionsContext = { @@ -138,7 +145,6 @@ class Frontend {          let results = null;          try { -            this.textScanner.pendingLookup = true;              if (textSource !== null) {                  results = (                      await this.findTerms(textSource) || @@ -165,8 +171,6 @@ class Frontend {              if (results === null && this.options.scanning.autoHideResults) {                  this.searchClear(true);              } - -            this.textScanner.pendingLookup = false;          }          return results; @@ -182,7 +186,6 @@ class Frontend {              {definitions, context: {sentence, url, focus, disableHistory: true}}          ); -        this.textScanner.setCurrentTextSource(textSource);          if (this.options.scanning.selectText) {              textSource.select();          } diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index cf6e5397..fc57d6c3 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -18,19 +18,23 @@  class TextScanner { -    constructor(node, ignoreNodes, popup, onTextSearch) { +    constructor(node, ignoreNodes, ignoreElements, ignorePoints) {          this.node = node;          this.ignoreNodes = (Array.isArray(ignoreNodes) && ignoreNodes.length > 0 ? ignoreNodes.join(',') : null); -        this.popup = popup; -        this.onTextSearch = onTextSearch; +        this.ignoreElements = ignoreElements; +        this.ignorePoints = ignorePoints; -        this.popupTimerPromise = null; +        this.scanTimerPromise = null;          this.textSourceCurrent = null;          this.pendingLookup = false;          this.options = null;          this.enabled = false;          this.eventListeners = []; +        this.subscribers = { +            searchClear: [], +            textSearch: [] +        };          this.primaryTouchIdentifier = null;          this.preventNextContextMenu = false; @@ -40,13 +44,13 @@ class TextScanner {      }      onMouseOver(e) { -        if (this.popup && e.target === this.popup.container) { -            this.popupTimerClear(); +        if (this.ignoreElements.includes(e.target)) { +            this.scanTimerClear();          }      }      onMouseMove(e) { -        this.popupTimerClear(); +        this.scanTimerClear();          if (this.pendingLookup || DOM.isMouseButtonDown(e, 'primary')) {              return; @@ -63,7 +67,7 @@ class TextScanner {          const search = async () => {              if (scanningModifier === 'none') { -                if (!await this.popupTimerWait()) { +                if (!await this.scanTimerWait()) {                      // Aborted                      return;                  } @@ -84,14 +88,14 @@ class TextScanner {              return false;          } -        if (DOM.isMouseButtonPressed(e, 'primary')) { -            this.popupTimerClear(); -            this.searchClear(); +        if (DOM.isMouseButtonDown(e, 'primary')) { +            this.scanTimerClear(); +            this.onSearchClear();          }      }      onMouseOut() { -        this.popupTimerClear(); +        this.scanTimerClear();      }      onClick(e) { @@ -192,23 +196,55 @@ class TextScanner {          e.preventDefault(); // Disable scroll      } -    async popupTimerWait() { +    async onSearchClear() { +        this.searchClear(); +        await this.publish('searchClear', {}); +    } + +    async onTextSearch(textSource, cause) { +        this.pendingLookup = true; +        const results = await this.publish('textSearch', {textSource, cause}); +        if (results.some((r) => r)) { +            this.textSourceCurrent = textSource; +        } +        this.pendingLookup = false; +    } + +    onError(error) { +        logError(error, false); +    } + +    subscribe(eventName, subscriber) { +        if (this.subscribers[eventName].includes(subscriber)) { return; } +        this.subscribers[eventName].push(subscriber); +    } + +    async publish(eventName, data) { +        const results = []; +        for (const subscriber of this.subscribers[eventName]) { +            const result = await subscriber(data); +            results.push(result); +        } +        return results; +    } + +    async scanTimerWait() {          const delay = this.options.scanning.delay;          const promise = promiseTimeout(delay, true); -        this.popupTimerPromise = promise; +        this.scanTimerPromise = promise;          try {              return await promise;          } finally { -            if (this.popupTimerPromise === promise) { -                this.popupTimerPromise = null; +            if (this.scanTimerPromise === promise) { +                this.scanTimerPromise = null;              }          }      } -    popupTimerClear() { -        if (this.popupTimerPromise !== null) { -            this.popupTimerPromise.resolve(false); -            this.popupTimerPromise = null; +    scanTimerClear() { +        if (this.scanTimerPromise !== null) { +            this.scanTimerPromise.resolve(false); +            this.scanTimerPromise = null;          }      } @@ -223,7 +259,7 @@ class TextScanner {                  this.clearEventListeners();                  this.enabled = false;              } -            this.searchClear(); +            this.onSearchClear();          }      } @@ -262,12 +298,18 @@ class TextScanner {      async searchAt(x, y, cause) {          try { -            this.popupTimerClear(); +            this.scanTimerClear(); -            if (this.pendingLookup || (this.popup && await this.popup.containsPoint(x, y))) { +            if (this.pendingLookup) {                  return;              } +            for (const ignorePointFn of this.ignorePoints) { +                if (await ignorePointFn(x, y)) { +                    return; +                } +            } +              const textSource = docRangeFromPoint(x, y, this.options);              if (this.textSourceCurrent !== null && this.textSourceCurrent.equals(textSource)) {                  return; |