extract ClipboardMonitor from DisplaySearch

This commit is contained in:
siikamiika 2020-01-26 03:02:33 +02:00
parent 37a922adc0
commit c685fd0e5f
3 changed files with 97 additions and 60 deletions

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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>