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
This commit is contained in:
parent
ce861ce079
commit
08ada6844a
@ -86,6 +86,7 @@ class QueryParser extends TextScanner {
|
||||
|
||||
setOptions(options) {
|
||||
super.setOptions(options);
|
||||
super.setEnabled(true);
|
||||
this.queryParser.dataset.termSpacing = `${options.parsing.termSpacing}`;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user