From 08ada6844af424e8ff28e592fc6b9dbc1a9a97eb Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 2 May 2020 12:47:15 -0400 Subject: [PATCH] Remove Frontend inheritance (#486) * Make Frontend use composition instead of inheritance for TextScanner * Use push instead of concat * Update setOptions and setEnabled APIs * Update how onWindowMessage event listener is added/removed * Rename options to _options * Use bind instead of arrow function * Fix selection being cleared due to settings changes --- ext/bg/js/search-query-parser.js | 1 + ext/bg/js/settings/popup-preview-frame.js | 9 +-- ext/fg/js/frontend.js | 94 ++++++++++++++--------- ext/mixed/js/text-scanner.js | 22 ++++-- 4 files changed, 77 insertions(+), 49 deletions(-) diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js index 3215f8e4..137234e8 100644 --- a/ext/bg/js/search-query-parser.js +++ b/ext/bg/js/search-query-parser.js @@ -86,6 +86,7 @@ class QueryParser extends TextScanner { setOptions(options) { super.setOptions(options); + super.setEnabled(true); this.queryParser.dataset.termSpacing = `${options.parsing.termSpacing}`; } diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index e73c04a0..cb548ed7 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -66,12 +66,10 @@ class SettingsPopupPreview { this.popup.setCustomOuterCss = this.popupSetCustomOuterCss.bind(this); this.frontend = new Frontend(this.popup); - this.frontend.getOptionsContext = async () => this.optionsContext; - this.frontend.setEnabled = () => {}; - this.frontend.clearSelection = () => {}; - await this.frontend.prepare(); + this.frontend.setDisabledOverride(true); + this.frontend.canClearSelection = false; // Update search this.updateSearch(); @@ -169,8 +167,7 @@ class SettingsPopupPreview { const source = new TextSourceRange(range, range.toString(), null, null); try { - await this.frontend.onSearchSource(source, 'script'); - this.frontend.setCurrentTextSource(source); + await this.frontend.setTextSource(source); } finally { source.cleanup(); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 50f52724..76ad27e0 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -25,14 +25,8 @@ * docSentenceExtract */ -class Frontend extends TextScanner { +class Frontend { constructor(popup, getUrl=null) { - super( - window, - () => this.popup.isProxy() ? [] : [this.popup.getContainer()], - [(x, y) => this.popup.containsPoint(x, y)] - ); - this._id = yomichan.generateId(16); this.popup = popup; @@ -41,15 +35,23 @@ class Frontend extends TextScanner { this._disabledOverride = false; - this.options = null; + this._options = null; this._pageZoomFactor = 1.0; this._contentScale = 1.0; this._orphaned = false; this._lastShowPromise = Promise.resolve(); + this._enabledEventListeners = new EventListenerCollection(); + this._textScanner = new TextScanner( + window, + () => this.popup.isProxy() ? [] : [this.popup.getContainer()], + [(x, y) => this.popup.containsPoint(x, y)] + ); + this._textScanner.onSearchSource = this.onSearchSource.bind(this); + this._windowMessageHandlers = new Map([ - ['popupClose', () => this.clearSelection(false)], + ['popupClose', () => this._textScanner.clearSelection(false)], ['selectionCopy', () => document.execCommand('copy')] ]); @@ -60,6 +62,14 @@ class Frontend extends TextScanner { ]); } + get canClearSelection() { + return this._textScanner.canClearSelection; + } + + set canClearSelection(value) { + this._textScanner.canClearSelection = value; + } + async prepare() { try { await this.updateOptions(); @@ -79,7 +89,7 @@ class Frontend extends TextScanner { yomichan.on('zoomChanged', this.onZoomChanged.bind(this)); chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); - this.on('clearSelection', this.onClearSelection.bind(this)); + this._textScanner.on('clearSelection', this.onClearSelection.bind(this)); this._updateContentScale(); this._broadcastRootPopupInformation(); @@ -129,44 +139,45 @@ class Frontend extends TextScanner { this._updateContentScale(); } - getMouseEventListeners() { - return [ - ...super.getMouseEventListeners(), - [window, 'message', this.onWindowMessage.bind(this)] - ]; - } - setDisabledOverride(disabled) { this._disabledOverride = disabled; - this.setEnabled(this.options.general.enable, this._canEnable()); + this._updateTextScannerEnabled(); } async setPopup(popup) { - this.clearSelection(true); + this._textScanner.clearSelection(true); this.popup = popup; await popup.setOptionsContext(await this.getOptionsContext(), this._id); } async updateOptions() { const optionsContext = await this.getOptionsContext(); - this.options = await apiOptionsGet(optionsContext); - this.setOptions(this.options, this._canEnable()); + this._options = await apiOptionsGet(optionsContext); + this._textScanner.setOptions(this._options); + this._updateTextScannerEnabled(); const ignoreNodes = ['.scan-disable', '.scan-disable *']; - if (!this.options.scanning.enableOnPopupExpressions) { + if (!this._options.scanning.enableOnPopupExpressions) { ignoreNodes.push('.source-text', '.source-text *'); } - this.ignoreNodes = ignoreNodes.join(','); + this._textScanner.ignoreNodes = ignoreNodes.join(','); await this.popup.setOptionsContext(optionsContext, this._id); this._updateContentScale(); - if (this.textSourceCurrent !== null && this.causeCurrent !== null) { - await this.onSearchSource(this.textSourceCurrent, this.causeCurrent); + const textSourceCurrent = this._textScanner.getCurrentTextSource(); + const causeCurrent = this._textScanner.causeCurrent; + if (textSourceCurrent !== null && causeCurrent !== null) { + await this.onSearchSource(textSourceCurrent, causeCurrent); } } + async setTextSource(textSource) { + await this.onSearchSource(textSource, 'script'); + this._textScanner.setCurrentTextSource(textSource); + } + async onSearchSource(textSource, cause) { let results = null; @@ -184,15 +195,15 @@ class Frontend extends TextScanner { } } catch (e) { if (this._orphaned) { - if (textSource !== null && this.options.scanning.modifier !== 'none') { + if (textSource !== null && this._options.scanning.modifier !== 'none') { this._showPopupContent(textSource, await this.getOptionsContext(), 'orphaned'); } } else { yomichan.logError(e); } } finally { - if (results === null && this.options.scanning.autoHideResults) { - this.clearSelection(false); + if (results === null && this._options.scanning.autoHideResults) { + this._textScanner.clearSelection(false); } } @@ -201,7 +212,7 @@ class Frontend extends TextScanner { showContent(textSource, focus, definitions, type, optionsContext) { const {url} = optionsContext; - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + const sentence = docSentenceExtract(textSource, this._options.anki.sentenceExt); this._showPopupContent( textSource, optionsContext, @@ -215,7 +226,7 @@ class Frontend extends TextScanner { } async findTerms(textSource, optionsContext) { - 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; } @@ -229,7 +240,7 @@ class Frontend extends TextScanner { } async findKanji(textSource, optionsContext) { - this.setTextSourceScanLength(textSource, 1); + this._textScanner.setTextSourceScanLength(textSource, 1); const searchText = textSource.text(); if (searchText.length === 0) { return null; } @@ -263,8 +274,21 @@ class Frontend extends TextScanner { return this._lastShowPromise; } + _updateTextScannerEnabled() { + const enabled = ( + this._options.general.enable && + this.popup.depth <= this._options.scanning.popupNestingMaxDepth && + !this._disabledOverride + ); + this._enabledEventListeners.removeAllEventListeners(); + this._textScanner.setEnabled(enabled); + if (enabled) { + this._enabledEventListeners.addEventListener(window, 'message', this.onWindowMessage.bind(this)); + } + } + _updateContentScale() { - const {popupScalingFactor, popupScaleRelativeToPageZoom, popupScaleRelativeToVisualViewport} = this.options.general; + const {popupScalingFactor, popupScaleRelativeToPageZoom, popupScaleRelativeToVisualViewport} = this._options.general; let contentScale = popupScalingFactor; if (popupScaleRelativeToPageZoom) { contentScale /= this._pageZoomFactor; @@ -295,12 +319,8 @@ class Frontend extends TextScanner { }); } - _canEnable() { - return this.popup.depth <= this.options.scanning.popupNestingMaxDepth && !this._disabledOverride; - } - async _updatePopupPosition() { - const textSource = this.getCurrentTextSource(); + const textSource = this._textScanner.getCurrentTextSource(); if (textSource !== null && await this.popup.isVisible()) { this._showPopupContent(textSource, await this.getOptionsContext()); } diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index c582ccd8..774eef44 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -45,6 +45,16 @@ class TextScanner extends EventDispatcher { this.preventNextMouseDown = false; this.preventNextClick = false; this.preventScroll = false; + + this._canClearSelection = true; + } + + get canClearSelection() { + return this._canClearSelection; + } + + set canClearSelection(value) { + this._canClearSelection = value; } onMouseOver(e) { @@ -222,9 +232,9 @@ class TextScanner extends EventDispatcher { } } - setEnabled(enabled, canEnable) { + setEnabled(enabled) { this.eventListeners.removeAllEventListeners(); - this.enabled = enabled && canEnable; + this.enabled = enabled; if (this.enabled) { this.hookEvents(); } else { @@ -233,9 +243,9 @@ class TextScanner extends EventDispatcher { } hookEvents() { - let eventListenerInfos = this.getMouseEventListeners(); + const eventListenerInfos = this.getMouseEventListeners(); if (this.options.scanning.touchInputEnabled) { - eventListenerInfos = eventListenerInfos.concat(this.getTouchEventListeners()); + eventListenerInfos.push(...this.getTouchEventListeners()); } for (const [node, type, listener, options] of eventListenerInfos) { @@ -264,9 +274,8 @@ class TextScanner extends EventDispatcher { ]; } - setOptions(options, canEnable=true) { + setOptions(options) { this.options = options; - this.setEnabled(this.options.general.enable, canEnable); } async searchAt(x, y, cause) { @@ -324,6 +333,7 @@ class TextScanner extends EventDispatcher { } clearSelection(passive) { + if (!this._canClearSelection) { return; } if (this.textSourceCurrent !== null) { if (this.textSourceCurrentSelected) { this.textSourceCurrent.deselect();