summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-05-08 19:05:50 -0400
committerGitHub <noreply@github.com>2020-05-08 19:05:50 -0400
commit3949db26d778bc3f593438211b148e8309921542 (patch)
tree7a93fdc9d4a58de8c29acaefa0e5f7eeddd2a5b3
parentb936c3e4b1bc993e535b02dee91bf6afc15a3564 (diff)
Text scanner refactor (#517)
* Fix return type * Pass search function as a constructor argument * Pass constructor as a details object For consistency with other complex constructors and improved semantics. * Convert _ignorePoints to a single optional function * Organize functions * Rename ignorePoints to ignorePoint
-rw-r--r--ext/bg/js/search-query-parser.js16
-rw-r--r--ext/fg/js/frontend.js18
-rw-r--r--ext/mixed/js/text-scanner.js203
3 files changed, 117 insertions, 120 deletions
diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js
index caace34b..e1e37d1c 100644
--- a/ext/bg/js/search-query-parser.js
+++ b/ext/bg/js/search-query-parser.js
@@ -34,12 +34,12 @@ class QueryParser {
this._queryParser = document.querySelector('#query-parser-content');
this._queryParserSelect = document.querySelector('#query-parser-select-container');
this._queryParserGenerator = new QueryParserGenerator();
- this._textScanner = new TextScanner(
- this._queryParser,
- () => [],
- []
- );
- this._textScanner.onSearchSource = this._onSearchSource.bind(this);
+ this._textScanner = new TextScanner({
+ node: this._queryParser,
+ ignoreElements: () => [],
+ ignorePoint: null,
+ search: this._search.bind(this)
+ });
}
async prepare() {
@@ -74,11 +74,11 @@ class QueryParser {
this._textScanner.searchAt(e.clientX, e.clientY, 'click');
}
- async _onSearchSource(textSource, cause) {
+ async _search(textSource, cause) {
if (textSource === null) { return null; }
const searchText = this._textScanner.getTextSourceContent(textSource, this._options.scanning.length);
- if (searchText.length === 0) { return; }
+ if (searchText.length === 0) { return null; }
const {definitions, length} = await apiTermsFind(searchText, {}, this._getOptionsContext());
if (definitions.length === 0) { return null; }
diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js
index 9de6597e..78440991 100644
--- a/ext/fg/js/frontend.js
+++ b/ext/fg/js/frontend.js
@@ -39,12 +39,12 @@ class Frontend {
this._enabledEventListeners = new EventListenerCollection();
this._activeModifiers = new Set();
this._optionsUpdatePending = false;
- 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._textScanner = new TextScanner({
+ node: window,
+ ignoreElements: () => this._popup.isProxy() ? [] : [this._popup.getContainer()],
+ ignorePoint: (x, y) => this._popup.containsPoint(x, y),
+ search: this._search.bind(this)
+ });
this._windowMessageHandlers = new Map([
['popupClose', () => this._textScanner.clearSelection(false)],
@@ -107,7 +107,7 @@ class Frontend {
}
async setTextSource(textSource) {
- await this._onSearchSource(textSource, 'script');
+ await this._search(textSource, 'script');
this._textScanner.setCurrentTextSource(textSource);
}
@@ -137,7 +137,7 @@ class Frontend {
const textSourceCurrent = this._textScanner.getCurrentTextSource();
const causeCurrent = this._textScanner.causeCurrent;
if (textSourceCurrent !== null && causeCurrent !== null) {
- await this._onSearchSource(textSourceCurrent, causeCurrent);
+ await this._search(textSourceCurrent, causeCurrent);
}
}
@@ -204,7 +204,7 @@ class Frontend {
await this.updateOptions();
}
- async _onSearchSource(textSource, cause) {
+ async _search(textSource, cause) {
await this._updatePendingOptions();
let results = null;
diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js
index 2ed6c3b9..b8688b08 100644
--- a/ext/mixed/js/text-scanner.js
+++ b/ext/mixed/js/text-scanner.js
@@ -22,11 +22,12 @@
*/
class TextScanner extends EventDispatcher {
- constructor(node, ignoreElements, ignorePoints) {
+ constructor({node, ignoreElements, ignorePoint, search}) {
super();
this._node = node;
this._ignoreElements = ignoreElements;
- this._ignorePoints = ignorePoints;
+ this._ignorePoint = ignorePoint;
+ this._search = search;
this._ignoreNodes = null;
@@ -69,6 +70,103 @@ class TextScanner extends EventDispatcher {
return this._causeCurrent;
}
+ setEnabled(enabled) {
+ this._eventListeners.removeAllEventListeners();
+ this._enabled = enabled;
+ if (this._enabled) {
+ this._hookEvents();
+ } else {
+ this.clearSelection(true);
+ }
+ }
+
+ setOptions(options) {
+ this._options = options;
+ }
+
+ async searchAt(x, y, cause) {
+ try {
+ this._scanTimerClear();
+
+ if (this._pendingLookup) {
+ return;
+ }
+
+ if (typeof this._ignorePoint === 'function' && await this._ignorePoint(x, y)) {
+ return;
+ }
+
+ const textSource = docRangeFromPoint(x, y, this._options.scanning.deepDomScan);
+ try {
+ if (this._textSourceCurrent !== null && this._textSourceCurrent.equals(textSource)) {
+ return;
+ }
+
+ this._pendingLookup = true;
+ const result = await this._search(textSource, cause);
+ if (result !== null) {
+ this._causeCurrent = cause;
+ this.setCurrentTextSource(textSource);
+ }
+ this._pendingLookup = false;
+ } finally {
+ if (textSource !== null) {
+ textSource.cleanup();
+ }
+ }
+ } catch (e) {
+ yomichan.logError(e);
+ }
+ }
+
+ getTextSourceContent(textSource, length) {
+ const clonedTextSource = textSource.clone();
+
+ clonedTextSource.setEndOffset(length);
+
+ if (this._ignoreNodes !== null && clonedTextSource.range) {
+ length = clonedTextSource.text().length;
+ while (clonedTextSource.range && length > 0) {
+ const nodes = TextSourceRange.getNodesInRange(clonedTextSource.range);
+ if (!TextSourceRange.anyNodeMatchesSelector(nodes, this._ignoreNodes)) {
+ break;
+ }
+ --length;
+ clonedTextSource.setEndOffset(length);
+ }
+ }
+
+ return clonedTextSource.text();
+ }
+
+ clearSelection(passive) {
+ if (!this._canClearSelection) { return; }
+ if (this._textSourceCurrent !== null) {
+ if (this._textSourceCurrentSelected) {
+ this._textSourceCurrent.deselect();
+ }
+ this._textSourceCurrent = null;
+ this._textSourceCurrentSelected = false;
+ }
+ this.trigger('clearSelection', {passive});
+ }
+
+ getCurrentTextSource() {
+ return this._textSourceCurrent;
+ }
+
+ setCurrentTextSource(textSource) {
+ this._textSourceCurrent = textSource;
+ if (this._options.scanning.selectText) {
+ this._textSourceCurrent.select();
+ this._textSourceCurrentSelected = true;
+ } else {
+ this._textSourceCurrentSelected = false;
+ }
+ }
+
+ // Private
+
_onMouseOver(e) {
if (this._ignoreElements().includes(e.target)) {
this._scanTimerClear();
@@ -221,10 +319,6 @@ class TextScanner extends EventDispatcher {
e.preventDefault(); // Disable scroll
}
- async onSearchSource(_textSource, _cause) {
- throw new Error('Override me');
- }
-
async _scanTimerWait() {
const delay = this._options.scanning.delay;
const promise = promiseTimeout(delay, true);
@@ -245,16 +339,6 @@ class TextScanner extends EventDispatcher {
}
}
- setEnabled(enabled) {
- this._eventListeners.removeAllEventListeners();
- this._enabled = enabled;
- if (this._enabled) {
- this._hookEvents();
- } else {
- this.clearSelection(true);
- }
- }
-
_hookEvents() {
const eventListenerInfos = this._getMouseEventListeners();
if (this._options.scanning.touchInputEnabled) {
@@ -287,93 +371,6 @@ class TextScanner extends EventDispatcher {
];
}
- setOptions(options) {
- this._options = options;
- }
-
- async searchAt(x, y, cause) {
- try {
- this._scanTimerClear();
-
- if (this._pendingLookup) {
- return;
- }
-
- for (const ignorePointFn of this._ignorePoints) {
- if (await ignorePointFn(x, y)) {
- return;
- }
- }
-
- const textSource = docRangeFromPoint(x, y, this._options.scanning.deepDomScan);
- try {
- if (this._textSourceCurrent !== null && this._textSourceCurrent.equals(textSource)) {
- return;
- }
-
- this._pendingLookup = true;
- const result = await this.onSearchSource(textSource, cause);
- if (result !== null) {
- this._causeCurrent = cause;
- this.setCurrentTextSource(textSource);
- }
- this._pendingLookup = false;
- } finally {
- if (textSource !== null) {
- textSource.cleanup();
- }
- }
- } catch (e) {
- yomichan.logError(e);
- }
- }
-
- getTextSourceContent(textSource, length) {
- const clonedTextSource = textSource.clone();
-
- clonedTextSource.setEndOffset(length);
-
- if (this._ignoreNodes !== null && clonedTextSource.range) {
- length = clonedTextSource.text().length;
- while (clonedTextSource.range && length > 0) {
- const nodes = TextSourceRange.getNodesInRange(clonedTextSource.range);
- if (!TextSourceRange.anyNodeMatchesSelector(nodes, this._ignoreNodes)) {
- break;
- }
- --length;
- clonedTextSource.setEndOffset(length);
- }
- }
-
- return clonedTextSource.text();
- }
-
- clearSelection(passive) {
- if (!this._canClearSelection) { return; }
- if (this._textSourceCurrent !== null) {
- if (this._textSourceCurrentSelected) {
- this._textSourceCurrent.deselect();
- }
- this._textSourceCurrent = null;
- this._textSourceCurrentSelected = false;
- }
- this.trigger('clearSelection', {passive});
- }
-
- getCurrentTextSource() {
- return this._textSourceCurrent;
- }
-
- setCurrentTextSource(textSource) {
- this._textSourceCurrent = textSource;
- if (this._options.scanning.selectText) {
- this._textSourceCurrent.select();
- this._textSourceCurrentSelected = true;
- } else {
- this._textSourceCurrentSelected = false;
- }
- }
-
_isScanningModifierPressed(scanningModifier, mouseEvent) {
switch (scanningModifier) {
case 'alt': return mouseEvent.altKey;