From 02a34bb4bc81471934e54b5440a20585527d76f5 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Thu, 5 Dec 2019 03:58:35 +0200 Subject: initial text scanner extract --- ext/fg/js/frontend.js | 287 ++-------------------------------------------- ext/fg/js/popup-nested.js | 1 + 2 files changed, 13 insertions(+), 275 deletions(-) (limited to 'ext/fg/js') diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 9a1d507b..43a4830f 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -20,23 +20,14 @@ class Frontend { constructor(popup, ignoreNodes) { this.popup = popup; - this.popupTimerPromise = null; - this.textSourceCurrent = null; - this.pendingLookup = false; + this.textScanner = new TextScanner(window, ignoreNodes, this.popup, this.searchSource.bind(this)); this.options = null; - this.ignoreNodes = (Array.isArray(ignoreNodes) && ignoreNodes.length > 0 ? ignoreNodes.join(',') : null); this.optionsContext = { depth: popup.depth, url: popup.url }; - this.primaryTouchIdentifier = null; - this.preventNextContextMenu = false; - this.preventNextMouseDown = false; - this.preventNextClick = false; - this.preventScroll = false; - this.enabled = false; this.eventListeners = []; @@ -71,162 +62,9 @@ class Frontend { return this.isPreparedPromise; } - onMouseOver(e) { - if (e.target === this.popup.container) { - this.popupTimerClear(); - } - } - - onMouseMove(e) { - this.popupTimerClear(); - - if (this.pendingLookup || DOM.isMouseButtonDown(e, 'primary')) { - return; - } - - const scanningOptions = this.options.scanning; - const scanningModifier = scanningOptions.modifier; - if (!( - Frontend.isScanningModifierPressed(scanningModifier, e) || - (scanningOptions.middleMouse && DOM.isMouseButtonDown(e, 'auxiliary')) - )) { - return; - } - - const search = async () => { - if (scanningModifier === 'none') { - if (!await this.popupTimerWait()) { - // Aborted - return; - } - } - - await this.searchAt(e.clientX, e.clientY, 'mouse'); - }; - - search(); - } - - onMouseDown(e) { - if (this.preventNextMouseDown) { - this.preventNextMouseDown = false; - this.preventNextClick = true; - e.preventDefault(); - e.stopPropagation(); - return false; - } - - if (e.button === 0) { - this.popupTimerClear(); - this.searchClear(true); - } - } - - onMouseOut() { - this.popupTimerClear(); - } - - onClick(e) { - if (this.preventNextClick) { - this.preventNextClick = false; - e.preventDefault(); - e.stopPropagation(); - return false; - } - } - - onAuxClick() { - this.preventNextContextMenu = false; - } - - onContextMenu(e) { - if (this.preventNextContextMenu) { - this.preventNextContextMenu = false; - e.preventDefault(); - e.stopPropagation(); - return false; - } - } - - onTouchStart(e) { - if (this.primaryTouchIdentifier !== null || e.changedTouches.length === 0) { - return; - } - - this.preventScroll = false; - this.preventNextContextMenu = false; - this.preventNextMouseDown = false; - this.preventNextClick = false; - - const primaryTouch = e.changedTouches[0]; - if (DOM.isPointInSelection(primaryTouch.clientX, primaryTouch.clientY, window.getSelection())) { - return; - } - - this.primaryTouchIdentifier = primaryTouch.identifier; - - if (this.pendingLookup) { - return; - } - - const textSourceCurrentPrevious = this.textSourceCurrent !== null ? this.textSourceCurrent.clone() : null; - - this.searchAt(primaryTouch.clientX, primaryTouch.clientY, 'touchStart') - .then(() => { - if ( - this.textSourceCurrent === null || - this.textSourceCurrent.equals(textSourceCurrentPrevious) - ) { - return; - } - - this.preventScroll = true; - this.preventNextContextMenu = true; - this.preventNextMouseDown = true; - }); - } - - onTouchEnd(e) { - if ( - this.primaryTouchIdentifier === null || - this.getIndexOfTouch(e.changedTouches, this.primaryTouchIdentifier) < 0 - ) { - return; - } - - this.primaryTouchIdentifier = null; - this.preventScroll = false; - this.preventNextClick = false; - // Don't revert context menu and mouse down prevention, - // since these events can occur after the touch has ended. - // this.preventNextContextMenu = false; - // this.preventNextMouseDown = false; - } - - onTouchCancel(e) { - this.onTouchEnd(e); - } - - onTouchMove(e) { - if (!this.preventScroll || !e.cancelable || this.primaryTouchIdentifier === null) { - return; - } - - const touches = e.changedTouches; - const index = this.getIndexOfTouch(touches, this.primaryTouchIdentifier); - if (index < 0) { - return; - } - - const primaryTouch = touches[index]; - this.searchAt(primaryTouch.clientX, primaryTouch.clientY, 'touchMove'); - - e.preventDefault(); // Disable scroll - } - async onResize() { - if (this.textSourceCurrent !== null && await this.popup.isVisibleAsync()) { - const textSource = this.textSourceCurrent; + const textSource = this.textScanner.getCurrentTextSource(); + if (textSource !== null && await this.popup.isVisibleAsync()) { this.lastShowPromise = this.popup.showContent( textSource.getRect(), textSource.getWritingMode() @@ -257,6 +95,7 @@ class Frontend { } setEnabled(enabled) { + this.textScanner.setEnabled(enabled); if (enabled) { if (!this.enabled) { this.hookEvents(); @@ -273,21 +112,7 @@ class Frontend { hookEvents() { this.addEventListener(window, 'message', this.onWindowMessage.bind(this)); - this.addEventListener(window, 'mousedown', this.onMouseDown.bind(this)); - this.addEventListener(window, 'mousemove', this.onMouseMove.bind(this)); - this.addEventListener(window, 'mouseover', this.onMouseOver.bind(this)); - this.addEventListener(window, 'mouseout', this.onMouseOut.bind(this)); this.addEventListener(window, 'resize', this.onResize.bind(this)); - - if (this.options.scanning.touchInputEnabled) { - this.addEventListener(window, 'click', this.onClick.bind(this)); - this.addEventListener(window, 'auxclick', this.onAuxClick.bind(this)); - this.addEventListener(window, 'touchstart', this.onTouchStart.bind(this)); - this.addEventListener(window, 'touchend', this.onTouchEnd.bind(this)); - this.addEventListener(window, 'touchcancel', this.onTouchCancel.bind(this)); - this.addEventListener(window, 'touchmove', this.onTouchMove.bind(this), {passive: false}); - this.addEventListener(window, 'contextmenu', this.onContextMenu.bind(this)); - } } addEventListener(node, type, listener, options) { @@ -304,60 +129,16 @@ class Frontend { async updateOptions() { this.options = await apiOptionsGet(this.getOptionsContext()); - this.setEnabled(this.options.general.enable); + this.textScanner.setOptions(this.options); await this.popup.setOptions(this.options); - } - - async popupTimerWait() { - const delay = this.options.scanning.delay; - const promise = promiseTimeout(delay, true); - this.popupTimerPromise = promise; - try { - return await promise; - } finally { - if (this.popupTimerPromise === promise) { - this.popupTimerPromise = null; - } - } - } - - popupTimerClear() { - if (this.popupTimerPromise !== null) { - this.popupTimerPromise.resolve(false); - this.popupTimerPromise = null; - } - } - - async searchAt(x, y, cause) { - try { - this.popupTimerClear(); - - if (this.pendingLookup || await this.popup.containsPoint(x, y)) { - return; - } - - const textSource = docRangeFromPoint(x, y, this.options); - if (this.textSourceCurrent !== null && this.textSourceCurrent.equals(textSource)) { - return; - } - - try { - await this.searchSource(textSource, cause); - } finally { - if (textSource !== null) { - textSource.cleanup(); - } - } - } catch (e) { - this.onError(e); - } + this.setEnabled(this.options.general.enable); } async searchSource(textSource, cause) { let results = null; try { - this.pendingLookup = true; + this.textScanner.pendingLookup = true; if (textSource !== null) { results = ( await this.findTerms(textSource) || @@ -385,7 +166,7 @@ class Frontend { this.searchClear(true); } - this.pendingLookup = false; + this.textScanner.pendingLookup = false; } return results; @@ -401,14 +182,14 @@ class Frontend { {definitions, context: {sentence, url, focus, disableHistory: true}} ); - this.textSourceCurrent = textSource; + this.textScanner.setCurrentTextSource(textSource); if (this.options.scanning.selectText) { textSource.select(); } } async findTerms(textSource) { - this.setTextSourceScanLength(textSource, this.options.scanning.length); + this.textScanner.setTextSourceScanLength(textSource, this.options.scanning.length); const searchText = textSource.text(); if (searchText.length === 0) { return null; } @@ -422,7 +203,7 @@ class Frontend { } async findKanji(textSource) { - this.setTextSourceScanLength(textSource, 1); + this.textScanner.setTextSourceScanLength(textSource, 1); const searchText = textSource.text(); if (searchText.length === 0) { return null; } @@ -436,57 +217,13 @@ class Frontend { searchClear(changeFocus) { this.popup.hide(changeFocus); this.popup.clearAutoPlayTimer(); - - if (this.textSourceCurrent !== null) { - if (this.options.scanning.selectText) { - this.textSourceCurrent.deselect(); - } - - this.textSourceCurrent = null; - } - } - - getIndexOfTouch(touchList, identifier) { - for (const i in touchList) { - const t = touchList[i]; - if (t.identifier === identifier) { - return i; - } - } - return -1; - } - - setTextSourceScanLength(textSource, length) { - textSource.setEndOffset(length); - if (this.ignoreNodes === null || !textSource.range) { - return; - } - - length = textSource.text().length; - while (textSource.range && length > 0) { - const nodes = TextSourceRange.getNodesInRange(textSource.range); - if (!TextSourceRange.anyNodeMatchesSelector(nodes, this.ignoreNodes)) { - break; - } - --length; - textSource.setEndOffset(length); - } + this.textScanner.searchClear(); } getOptionsContext() { this.optionsContext.url = this.popup.url; return this.optionsContext; } - - static isScanningModifierPressed(scanningModifier, mouseEvent) { - switch (scanningModifier) { - case 'alt': return mouseEvent.altKey; - case 'ctrl': return mouseEvent.ctrlKey; - case 'shift': return mouseEvent.shiftKey; - case 'none': return true; - default: return false; - } - } } Frontend.windowMessageHandlers = { diff --git a/ext/fg/js/popup-nested.js b/ext/fg/js/popup-nested.js index 31cb1cda..3df469fe 100644 --- a/ext/fg/js/popup-nested.js +++ b/ext/fg/js/popup-nested.js @@ -41,6 +41,7 @@ async function popupNestedInitialize(id, depth, parentFrameId, url) { window.frontendInitializationData = {id, depth, parentFrameId, ignoreNodes, url, proxy: true}; const scriptSrcs = [ + '/mixed/js/text-scanner.js', '/fg/js/frontend-api-sender.js', '/fg/js/popup.js', '/fg/js/popup-proxy.js', -- cgit v1.2.3