diff options
author | Alex Yatskov <alex@foosoft.net> | 2019-11-05 19:04:13 -0800 |
---|---|---|
committer | Alex Yatskov <alex@foosoft.net> | 2019-11-05 19:04:13 -0800 |
commit | 08ad2779678cd447bd747c2b155ef9b5135fdf5d (patch) | |
tree | faa54cbf9176989f9bd3c3b90ff3e032189adb20 /ext/bg/js/search.js | |
parent | 438498435227cfa59cf9ed3430045b288cd2a7c0 (diff) | |
parent | 91c01e0a7eeeb851344a22ace8a5fa0b873a3e57 (diff) |
Merge branch 'master' into testing
Diffstat (limited to 'ext/bg/js/search.js')
-rw-r--r-- | ext/bg/js/search.js | 173 |
1 files changed, 168 insertions, 5 deletions
diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 431478c9..dbfcb15d 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -17,6 +17,12 @@ */ +let IS_FIREFOX = null; +(async () => { + const {browser} = await apiGetEnvironmentInfo(); + IS_FIREFOX = ['firefox', 'firefox-mobile'].includes(browser); +})(); + class DisplaySearch extends Display { constructor() { super(document.querySelector('#spinner'), document.querySelector('#content')); @@ -29,8 +35,14 @@ class DisplaySearch extends Display { this.search = document.querySelector('#search'); this.query = document.querySelector('#query'); this.intro = document.querySelector('#intro'); + this.clipboardMonitorEnable = document.querySelector('#clipboard-monitor-enable'); + this.wanakanaEnable = document.querySelector('#wanakana-enable'); + this.introVisible = true; this.introAnimationTimer = null; + + this.clipboardMonitorIntervalId = null; + this.clipboardPrevText = null; } static create() { @@ -49,16 +61,69 @@ class DisplaySearch extends Display { if (this.query !== null) { this.query.addEventListener('input', () => this.onSearchInput(), false); + if (this.wanakanaEnable !== null) { + if (this.options.general.enableWanakana === true) { + this.wanakanaEnable.checked = true; + window.wanakana.bind(this.query); + } else { + this.wanakanaEnable.checked = false; + } + this.wanakanaEnable.addEventListener('change', (e) => { + const query = DisplaySearch.getSearchQueryFromLocation(window.location.href) || ''; + if (e.target.checked) { + window.wanakana.bind(this.query); + this.query.value = window.wanakana.toKana(query); + apiOptionsSet({general: {enableWanakana: true}}, this.getOptionsContext()); + } else { + window.wanakana.unbind(this.query); + this.query.value = query; + apiOptionsSet({general: {enableWanakana: false}}, this.getOptionsContext()); + } + this.onSearchQueryUpdated(this.query.value, false); + }); + } + const query = DisplaySearch.getSearchQueryFromLocation(window.location.href); if (query !== null) { - this.query.value = window.wanakana.toKana(query); - this.onSearchQueryUpdated(query, false); + if (this.isWanakanaEnabled()) { + this.query.value = window.wanakana.toKana(query); + } else { + this.query.value = query; + } + this.onSearchQueryUpdated(this.query.value, false); } - - window.wanakana.bind(this.query); + } + if (this.clipboardMonitorEnable !== null) { + if (this.options.general.enableClipboardMonitor === true) { + this.clipboardMonitorEnable.checked = true; + this.startClipboardMonitor(); + } else { + this.clipboardMonitorEnable.checked = false; + } + this.clipboardMonitorEnable.addEventListener('change', (e) => { + if (e.target.checked) { + chrome.permissions.request( + {permissions: ['clipboardRead']}, + (granted) => { + if (granted) { + this.startClipboardMonitor(); + apiOptionsSet({general: {enableClipboardMonitor: true}}, this.getOptionsContext()); + } else { + e.target.checked = false; + } + } + ); + } else { + this.stopClipboardMonitor(); + apiOptionsSet({general: {enableClipboardMonitor: false}}, this.getOptionsContext()); + } + }); } + window.addEventListener('popstate', (e) => this.onPopState(e)); + this.updateSearchButton(); + this.initClipboardMonitor(); } catch (e) { this.onError(e); } @@ -79,6 +144,11 @@ class DisplaySearch extends Display { onSearchInput() { this.updateSearchButton(); + + const queryElementRect = this.query.getBoundingClientRect(); + if (queryElementRect.top < 0 || queryElementRect.bottom > window.innerHeight) { + this.query.scrollIntoView(); + } } onSearch(e) { @@ -90,10 +160,60 @@ class DisplaySearch extends Display { const query = this.query.value; const queryString = query.length > 0 ? `?query=${encodeURIComponent(query)}` : ''; - window.history.replaceState(null, '', `${window.location.pathname}${queryString}`); + window.history.pushState(null, '', `${window.location.pathname}${queryString}`); this.onSearchQueryUpdated(query, true); } + onPopState(e) { + const query = DisplaySearch.getSearchQueryFromLocation(window.location.href) || ''; + if (this.query !== null) { + if (this.isWanakanaEnabled()) { + this.query.value = window.wanakana.toKana(query); + } else { + this.query.value = query; + } + } + + this.onSearchQueryUpdated(this.query.value, false); + } + + onKeyDown(e) { + const key = Display.getKeyFromEvent(e); + + let activeModifierMap = { + 'Control': e.ctrlKey, + 'Meta': e.metaKey, + 'ANY_MOD': true + }; + + const ignoreKeys = { + 'ANY_MOD': ['Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'PageDown', 'PageUp', 'Home', 'End'] + .concat( + Array.from(Array(24).keys()) + .map(i => `F${i + 1}`) + ), + 'Control': ['C', 'A', 'Z', 'Y', 'X', 'F', 'G'], + 'Meta': ['C', 'A', 'Z', 'Y', 'X', 'F', 'G'], + 'OS': [], + 'Alt': [], + 'AltGraph': [], + 'Shift': [] + } + + let preventFocus = false; + for (const [modifier, keys] of Object.entries(ignoreKeys)) { + const modifierActive = activeModifierMap[modifier]; + if (key === modifier || (modifierActive && keys.includes(key))) { + preventFocus = true; + break; + } + } + + if (!super.onKeyDown(e) && !preventFocus && document.activeElement !== this.query) { + this.query.focus({preventScroll: true}); + } + } + async onSearchQueryUpdated(query, animate) { try { const valid = (query.length > 0); @@ -125,6 +245,49 @@ class DisplaySearch extends Display { } } + initClipboardMonitor() { + // ignore copy from search page + window.addEventListener('copy', (e) => { + this.clipboardPrevText = document.getSelection().toString().trim(); + }); + } + + startClipboardMonitor() { + this.clipboardMonitorIntervalId = setInterval(async () => { + let curText = null; + // TODO get rid of this and figure out why apiClipboardGet doesn't work on Firefox + if (IS_FIREFOX) { + curText = (await navigator.clipboard.readText()).trim(); + } else if (IS_FIREFOX === false) { + curText = (await apiClipboardGet()).trim(); + } + if (curText && (curText !== this.clipboardPrevText)) { + if (this.isWanakanaEnabled()) { + this.query.value = window.wanakana.toKana(curText); + } else { + this.query.value = curText; + } + + const queryString = curText.length > 0 ? `?query=${encodeURIComponent(curText)}` : ''; + window.history.pushState(null, '', `${window.location.pathname}${queryString}`); + this.onSearchQueryUpdated(this.query.value, true); + + this.clipboardPrevText = curText; + } + }, 100); + } + + stopClipboardMonitor() { + if (this.clipboardMonitorIntervalId) { + clearInterval(this.clipboardMonitorIntervalId); + this.clipboardMonitorIntervalId = null; + } + } + + isWanakanaEnabled() { + return this.wanakanaEnable !== null && this.wanakanaEnable.checked; + } + getOptionsContext() { return this.optionsContext; } |