diff options
| -rw-r--r-- | ext/bg/js/clipboard-monitor.js | 78 | ||||
| -rw-r--r-- | ext/bg/js/search.js | 78 | ||||
| -rw-r--r-- | ext/bg/search.html | 1 | 
3 files changed, 97 insertions, 60 deletions
| diff --git a/ext/bg/js/clipboard-monitor.js b/ext/bg/js/clipboard-monitor.js new file mode 100644 index 00000000..579cdc56 --- /dev/null +++ b/ext/bg/js/clipboard-monitor.js @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020  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 <https://www.gnu.org/licenses/>. + */ + + +class ClipboardMonitor { +    constructor() { +        this.timerId = null; +        this.timerToken = null; +        this.interval = 250; +        this.previousText = null; +    } + +    onClipboardText(_text) { +        throw new Error('Override me'); +    } + +    start() { +        // The token below is used as a unique identifier to ensure that a new clipboard monitor +        // hasn't been started during the await call. The check below the await apiClipboardGet() +        // call will exit early if the reference has changed. +        const token = {}; +        const intervalCallback = async () => { +            this.timerId = null; + +            let text = null; +            try { +                text = await apiClipboardGet(); +            } catch (e) { +                // NOP +            } +            if (this.timerToken !== token) { return; } + +            if ( +                typeof text === 'string' && +                (text = text.trim()).length > 0 && +                text !== this.previousText +            ) { +                this.previousText = text; +                if (jpIsStringPartiallyJapanese(text)) { +                    this.onClipboardText(text); +                } +            } + +            this.timerId = setTimeout(intervalCallback, this.interval); +        }; + +        this.timerToken = token; + +        intervalCallback(); +    } + +    stop() { +        this.timerToken = null; +        if (this.timerId !== null) { +            clearTimeout(this.timerId); +            this.timerId = null; +        } +    } + +    setPreviousText(text) { +        this.previousText = text; +    } +} diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 9508346e..e32ba46e 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -36,10 +36,7 @@ class DisplaySearch extends Display {          this.introVisible = true;          this.introAnimationTimer = null; -        this.clipboardMonitorTimerId = null; -        this.clipboardMonitorTimerToken = null; -        this.clipboardInterval = 250; -        this.clipboardPreviousText = null; +        this.clipboardMonitor = new ClipboardMonitor();      }      static create() { @@ -93,7 +90,7 @@ class DisplaySearch extends Display {              if (this.clipboardMonitorEnable !== null) {                  if (this.options.general.enableClipboardMonitor === true) {                      this.clipboardMonitorEnable.checked = true; -                    this.startClipboardMonitor(); +                    this.clipboardMonitor.start();                  } else {                      this.clipboardMonitorEnable.checked = false;                  } @@ -103,7 +100,7 @@ class DisplaySearch extends Display {                              {permissions: ['clipboardRead']},                              (granted) => {                                  if (granted) { -                                    this.startClipboardMonitor(); +                                    this.clipboardMonitor.start();                                      apiOptionsSet({general: {enableClipboardMonitor: true}}, this.getOptionsContext());                                  } else {                                      e.target.checked = false; @@ -111,16 +108,18 @@ class DisplaySearch extends Display {                              }                          );                      } else { -                        this.stopClipboardMonitor(); +                        this.clipboardMonitor.stop();                          apiOptionsSet({general: {enableClipboardMonitor: false}}, this.getOptionsContext());                      }                  });              }              window.addEventListener('popstate', (e) => this.onPopState(e)); +            window.addEventListener('copy', (e) => this.onCopy(e)); + +            this.clipboardMonitor.onClipboardText = (text) => this.onClipboardText(text);              this.updateSearchButton(); -            this.initClipboardMonitor();          } catch (e) {              this.onError(e);          } @@ -199,6 +198,17 @@ class DisplaySearch extends Display {          }      } +    onCopy() { +        // ignore copy from search page +        this.clipboardMonitor.setPreviousText(document.getSelection().toString().trim()); +    } + +    onClipboardText(text) { +        this.setQuery(this.isWanakanaEnabled() ? window.wanakana.toKana(text) : text); +        window.history.pushState(null, '', `${window.location.pathname}?query=${encodeURIComponent(text)}`); +        this.onSearchQueryUpdated(this.query.value, true); +    } +      async onSearchQueryUpdated(query, animate) {          try {              const details = {}; @@ -238,58 +248,6 @@ class DisplaySearch extends Display {          this.queryParser.setOptions(this.options);      } -    initClipboardMonitor() { -        // ignore copy from search page -        window.addEventListener('copy', () => { -            this.clipboardPreviousText = document.getSelection().toString().trim(); -        }); -    } - -    startClipboardMonitor() { -        // The token below is used as a unique identifier to ensure that a new clipboard monitor -        // hasn't been started during the await call. The check below the await apiClipboardGet() -        // call will exit early if the reference has changed. -        const token = {}; -        const intervalCallback = async () => { -            this.clipboardMonitorTimerId = null; - -            let text = null; -            try { -                text = await apiClipboardGet(); -            } catch (e) { -                // NOP -            } -            if (this.clipboardMonitorTimerToken !== token) { return; } - -            if ( -                typeof text === 'string' && -                (text = text.trim()).length > 0 && -                text !== this.clipboardPreviousText -            ) { -                this.clipboardPreviousText = text; -                if (jpIsStringPartiallyJapanese(text)) { -                    this.setQuery(this.isWanakanaEnabled() ? window.wanakana.toKana(text) : text); -                    window.history.pushState(null, '', `${window.location.pathname}?query=${encodeURIComponent(text)}`); -                    this.onSearchQueryUpdated(this.query.value, true); -                } -            } - -            this.clipboardMonitorTimerId = setTimeout(intervalCallback, this.clipboardInterval); -        }; - -        this.clipboardMonitorTimerToken = token; - -        intervalCallback(); -    } - -    stopClipboardMonitor() { -        this.clipboardMonitorTimerToken = null; -        if (this.clipboardMonitorTimerId !== null) { -            clearTimeout(this.clipboardMonitorTimerId); -            this.clipboardMonitorTimerId = null; -        } -    } -      isWanakanaEnabled() {          return this.wanakanaEnable !== null && this.wanakanaEnable.checked;      } diff --git a/ext/bg/search.html b/ext/bg/search.html index bb7ac095..bb555e92 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -87,6 +87,7 @@          <script src="/mixed/js/text-scanner.js"></script>          <script src="/bg/js/search-query-parser.js"></script> +        <script src="/bg/js/clipboard-monitor.js"></script>          <script src="/bg/js/search.js"></script>          <script src="/bg/js/search-frontend.js"></script>      </body> |