aboutsummaryrefslogtreecommitdiff
path: root/ext/bg/js/search.js
diff options
context:
space:
mode:
authorAlex Yatskov <alex@foosoft.net>2019-11-05 19:04:13 -0800
committerAlex Yatskov <alex@foosoft.net>2019-11-05 19:04:13 -0800
commit08ad2779678cd447bd747c2b155ef9b5135fdf5d (patch)
treefaa54cbf9176989f9bd3c3b90ff3e032189adb20 /ext/bg/js/search.js
parent438498435227cfa59cf9ed3430045b288cd2a7c0 (diff)
parent91c01e0a7eeeb851344a22ace8a5fa0b873a3e57 (diff)
Merge branch 'master' into testing
Diffstat (limited to 'ext/bg/js/search.js')
-rw-r--r--ext/bg/js/search.js173
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;
}