aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/js/app/frontend.js51
-rw-r--r--ext/js/display/display.js29
-rw-r--r--ext/js/display/query-parser.js35
-rw-r--r--ext/js/language/text-scanner.js93
-rw-r--r--types/ext/text-scanner.d.ts33
5 files changed, 119 insertions, 122 deletions
diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js
index de1c5a46..5f412340 100644
--- a/ext/js/app/frontend.js
+++ b/ext/js/app/frontend.js
@@ -183,7 +183,9 @@ export class Frontend {
chrome.runtime.onMessage.addListener(this._onRuntimeMessage.bind(this));
this._textScanner.on('clear', this._onTextScannerClear.bind(this));
- this._textScanner.on('searched', this._onSearched.bind(this));
+ this._textScanner.on('searchSuccess', this._onSearchSuccess.bind(this));
+ this._textScanner.on('searchEmpty', this._onSearchEmpty.bind(this));
+ this._textScanner.on('searchError', this._onSearchError.bind(this));
/* eslint-disable no-multi-spaces */
this._application.crossFrame.registerHandlers([
@@ -369,31 +371,36 @@ export class Frontend {
}
/**
- * @param {import('text-scanner').SearchedEventDetails} details
+ * @param {import('text-scanner').EventArgument<'searchSuccess'>} details
*/
- _onSearched({type, dictionaryEntries, sentence, inputInfo: {eventType, passive, detail: inputInfoDetail}, textSource, optionsContext, detail, error}) {
+ _onSearchSuccess({type, dictionaryEntries, sentence, inputInfo: {eventType, detail: inputInfoDetail}, textSource, optionsContext, detail}) {
+ this._stopClearSelectionDelayed();
+ let focus = (eventType === 'mouseMove');
+ if (typeof inputInfoDetail === 'object' && inputInfoDetail !== null) {
+ const focus2 = inputInfoDetail.focus;
+ if (typeof focus2 === 'boolean') { focus = focus2; }
+ }
+ this._showContent(textSource, focus, dictionaryEntries, type, sentence, detail !== null ? detail.documentTitle : null, optionsContext);
+ }
+
+ /** */
+ _onSearchEmpty() {
const scanningOptions = /** @type {import('settings').ProfileOptions} */ (this._options).scanning;
+ if (scanningOptions.autoHideResults) {
+ this._clearSelectionDelayed(scanningOptions.hideDelay, false, false);
+ }
+ }
- if (error !== null) {
- if (this._application.webExtension.unloaded) {
- if (textSource !== null && !passive) {
- this._showExtensionUnloaded(textSource);
- }
- } else {
- log.error(error);
- }
- } if (type !== null && optionsContext !== null) {
- this._stopClearSelectionDelayed();
- let focus = (eventType === 'mouseMove');
- if (typeof inputInfoDetail === 'object' && inputInfoDetail !== null) {
- const focus2 = inputInfoDetail.focus;
- if (typeof focus2 === 'boolean') { focus = focus2; }
+ /**
+ * @param {import('text-scanner').EventArgument<'searchError'>} details
+ */
+ _onSearchError({error, textSource, inputInfo: {passive}}) {
+ if (this._application.webExtension.unloaded) {
+ if (textSource !== null && !passive) {
+ this._showExtensionUnloaded(textSource);
}
- this._showContent(textSource, focus, dictionaryEntries, type, sentence, detail !== null ? detail.documentTitle : null, optionsContext);
} else {
- if (scanningOptions.autoHideResults) {
- this._clearSelectionDelayed(scanningOptions.hideDelay, false, false);
- }
+ log.error(error);
}
}
@@ -888,7 +895,7 @@ export class Frontend {
}
/**
- * @returns {Promise<{optionsContext: import('settings').OptionsContext, detail?: import('text-scanner').SearchResultDetail}>}
+ * @returns {Promise<import('text-scanner').SearchContext>}
*/
async _getSearchContext() {
let url = window.location.href;
diff --git a/ext/js/display/display.js b/ext/js/display/display.js
index 4114cc45..676f1a4f 100644
--- a/ext/js/display/display.js
+++ b/ext/js/display/display.js
@@ -1850,7 +1850,8 @@ export class Display extends EventDispatcher {
this._contentTextScanner.excludeSelector = '.scan-disable,.scan-disable *';
this._contentTextScanner.prepare();
this._contentTextScanner.on('clear', this._onContentTextScannerClear.bind(this));
- this._contentTextScanner.on('searched', this._onContentTextScannerSearched.bind(this));
+ this._contentTextScanner.on('searchSuccess', this._onContentTextScannerSearchSuccess.bind(this));
+ this._contentTextScanner.on('searchError', this._onContentTextScannerSearchError.bind(this));
}
const {scanning: scanningOptions, sentenceParsing: sentenceParsingOptions} = options;
@@ -1895,15 +1896,9 @@ export class Display extends EventDispatcher {
}
/**
- * @param {import('text-scanner').SearchedEventDetails} details
+ * @param {import('text-scanner').EventArgument<'searchSuccess'>} details
*/
- _onContentTextScannerSearched({type, dictionaryEntries, sentence, textSource, optionsContext, error}) {
- if (error !== null && !this._application.webExtension.unloaded) {
- log.error(error);
- }
-
- if (type === null) { return; }
-
+ _onContentTextScannerSearchSuccess({type, dictionaryEntries, sentence, textSource, optionsContext}) {
const query = textSource.text();
const url = window.location.href;
const documentTitle = document.title;
@@ -1933,10 +1928,24 @@ export class Display extends EventDispatcher {
}
/**
+ * @param {import('text-scanner').EventArgument<'searchError'>} details
+ */
+ _onContentTextScannerSearchError({error}) {
+ if (!this._application.webExtension.unloaded) {
+ log.error(error);
+ }
+ }
+
+ /**
* @type {import('display').GetSearchContextCallback}
*/
_getSearchContext() {
- return {optionsContext: this.getOptionsContext()};
+ return {
+ optionsContext: this.getOptionsContext(),
+ detail: {
+ documentTitle: document.title
+ }
+ };
}
/**
diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js
index daa298d2..6ec803a0 100644
--- a/ext/js/display/query-parser.js
+++ b/ext/js/display/query-parser.js
@@ -82,7 +82,8 @@ export class QueryParser extends EventDispatcher {
prepare() {
this._textScanner.prepare();
this._textScanner.on('clear', this._onTextScannerClear.bind(this));
- this._textScanner.on('searched', this._onSearched.bind(this));
+ this._textScanner.on('searchSuccess', this._onSearchSuccess.bind(this));
+ this._textScanner.on('searchError', this._onSearchError.bind(this));
this._queryParserModeSelect.addEventListener('change', this._onParserChange.bind(this), false);
}
@@ -147,39 +148,29 @@ export class QueryParser extends EventDispatcher {
}
/**
- * @param {import('text-scanner').SearchedEventDetails} e
+ * @param {import('text-scanner').EventArgument<'searchSuccess'>} details
*/
- _onSearched(e) {
- const {error} = e;
- if (error !== null) {
- log.error(error);
- return;
- }
-
- const {
- textScanner,
- type,
- dictionaryEntries,
- sentence,
- inputInfo,
- textSource,
- optionsContext
- } = e;
- if (type === null || dictionaryEntries === null || sentence === null || optionsContext === null) { return; }
-
+ _onSearchSuccess({type, dictionaryEntries, sentence, inputInfo, textSource, optionsContext}) {
this.trigger('searched', {
- textScanner,
+ textScanner: this._textScanner,
type,
dictionaryEntries,
sentence,
inputInfo,
textSource,
optionsContext,
- sentenceOffset: this._getSentenceOffset(e.textSource)
+ sentenceOffset: this._getSentenceOffset(textSource)
});
}
/**
+ * @param {import('text-scanner').EventArgument<'searchError'>} details
+ */
+ _onSearchError({error}) {
+ log.error(error);
+ }
+
+ /**
* @param {Event} e
*/
_onParserChange(e) {
diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js
index d78c4c74..5b125063 100644
--- a/ext/js/language/text-scanner.js
+++ b/ext/js/language/text-scanner.js
@@ -395,11 +395,10 @@ export class TextScanner extends EventDispatcher {
/**
* @param {import('text-source').TextSource} textSource
* @param {import('text-scanner').InputInfoDetail} [inputDetail]
- * @returns {Promise<?import('text-scanner').SearchedEventDetails>}
*/
async search(textSource, inputDetail) {
const inputInfo = this._createInputInfo(null, 'script', 'script', true, [], [], inputDetail);
- return await this._search(textSource, this._searchTerms, this._searchKanji, inputInfo);
+ await this._search(textSource, this._searchTerms, this._searchKanji, inputInfo);
}
// Private
@@ -422,23 +421,8 @@ export class TextScanner extends EventDispatcher {
* @param {boolean} searchTerms
* @param {boolean} searchKanji
* @param {import('text-scanner').InputInfo} inputInfo
- * @returns {Promise<?import('text-scanner').SearchedEventDetails>}
*/
async _search(textSource, searchTerms, searchKanji, inputInfo) {
- /** @type {?import('dictionary').DictionaryEntry[]} */
- let dictionaryEntries = null;
- /** @type {?import('display').HistoryStateSentence} */
- let sentence = null;
- /** @type {?import('display').PageType} */
- let type = null;
- /** @type {?Error} */
- let error = null;
- let searched = false;
- /** @type {?import('settings').OptionsContext} */
- let optionsContext = null;
- /** @type {?import('text-scanner').SearchResultDetail} */
- let detail = null;
-
try {
const inputInfoDetail = inputInfo.detail;
const selectionRestoreInfo = (
@@ -448,56 +432,59 @@ export class TextScanner extends EventDispatcher {
);
if (this._textSourceCurrent !== null && this._textSourceCurrent.hasSameStart(textSource)) {
- return null;
+ return;
}
const getSearchContextPromise = this._getSearchContext();
const getSearchContextResult = getSearchContextPromise instanceof Promise ? await getSearchContextPromise : getSearchContextPromise;
- const {detail: detail2} = getSearchContextResult;
- if (typeof detail2 !== 'undefined') { detail = detail2; }
- optionsContext = this._createOptionsContextForInput(getSearchContextResult.optionsContext, inputInfo);
-
- searched = true;
-
- let valid = false;
+ const {detail} = getSearchContextResult;
+ const optionsContext = this._createOptionsContextForInput(getSearchContextResult.optionsContext, inputInfo);
+
+ /** @type {?import('dictionary').DictionaryEntry[]} */
+ let dictionaryEntries = null;
+ /** @type {?import('display').HistoryStateSentence} */
+ let sentence = null;
+ /** @type {'terms'|'kanji'} */
+ let type = 'terms';
const result = await this._findDictionaryEntries(textSource, searchTerms, searchKanji, optionsContext);
if (result !== null) {
({dictionaryEntries, sentence, type} = result);
- valid = true;
} else if (textSource !== null && textSource instanceof TextSourceElement && await this._hasJapanese(textSource.fullContent)) {
dictionaryEntries = [];
sentence = {text: '', offset: 0};
- type = 'terms';
- valid = true;
}
- if (valid) {
+ if (dictionaryEntries !== null && sentence !== null) {
this._inputInfoCurrent = inputInfo;
this.setCurrentTextSource(textSource);
- if (typeof selectionRestoreInfo !== 'undefined') {
- this._selectionRestoreInfo = selectionRestoreInfo;
- }
+ this._selectionRestoreInfo = selectionRestoreInfo;
+
+ this.trigger('searchSuccess', {
+ type,
+ dictionaryEntries,
+ sentence,
+ inputInfo,
+ textSource,
+ optionsContext,
+ detail
+ });
+ } else {
+ this._triggerSearchEmpty(inputInfo);
}
- } catch (e) {
- error = e instanceof Error ? e : new Error(`A search error occurred: ${e}`);
+ } catch (error) {
+ this.trigger('searchError', {
+ error: error instanceof Error ? error : new Error(`A search error occurred: ${error}`),
+ textSource,
+ inputInfo
+ });
}
+ }
- if (!searched) { return null; }
-
- /** @type {import('text-scanner').SearchedEventDetails} */
- const results = {
- textScanner: this,
- type,
- dictionaryEntries,
- sentence,
- inputInfo,
- textSource,
- optionsContext,
- detail,
- error
- };
- this.trigger('searched', results);
- return results;
+ /**
+ * @param {import('text-scanner').InputInfo} inputInfo
+ */
+ _triggerSearchEmpty(inputInfo) {
+ this.trigger('searchEmpty', {inputInfo});
}
/** */
@@ -1287,10 +1274,10 @@ export class TextScanner extends EventDispatcher {
try {
await this._search(textSource, searchTerms, searchKanji, inputInfo);
} finally {
- if (textSource !== null) {
- textSource.cleanup();
- }
+ textSource.cleanup();
}
+ } else {
+ this._triggerSearchEmpty(inputInfo);
}
} catch (e) {
log.error(e);
diff --git a/types/ext/text-scanner.d.ts b/types/ext/text-scanner.d.ts
index 4253d6cc..8acc780d 100644
--- a/types/ext/text-scanner.d.ts
+++ b/types/ext/text-scanner.d.ts
@@ -15,7 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import type {TextScanner} from '../../ext/js/language/text-scanner';
import type {TextSourceGenerator} from '../../ext/js/dom/text-source-generator';
import type {API} from '../../ext/js/comm/api';
import type * as Dictionary from './dictionary';
@@ -94,18 +93,6 @@ export type InputConfig = {
preventPenScrolling: boolean;
};
-export type SearchedEventDetails = {
- textScanner: TextScanner;
- type: Display.PageType | null;
- dictionaryEntries: Dictionary.DictionaryEntry[] | null;
- sentence: Display.HistoryStateSentence | null;
- inputInfo: InputInfo;
- textSource: TextSource.TextSource;
- optionsContext: Settings.OptionsContext | null;
- detail: SearchResultDetail | null;
- error: Error | null;
-};
-
export type InputInfo = {
input: InputConfig | null;
pointerType: PointerType;
@@ -122,10 +109,26 @@ export type InputInfoDetail = {
};
export type Events = {
- searched: SearchedEventDetails;
clear: {
reason: ClearReason;
};
+ searchSuccess: {
+ type: 'terms' | 'kanji';
+ dictionaryEntries: Dictionary.DictionaryEntry[];
+ sentence: Display.HistoryStateSentence;
+ inputInfo: InputInfo;
+ textSource: TextSource.TextSource;
+ optionsContext: Settings.OptionsContext;
+ detail: SearchResultDetail;
+ };
+ searchEmpty: {
+ inputInfo: InputInfo;
+ };
+ searchError: {
+ error: Error;
+ textSource: TextSource.TextSource;
+ inputInfo: InputInfo;
+ };
};
export type ClearReason = 'mousedown';
@@ -153,7 +156,7 @@ export type ConstructorDetails = {
export type SearchContext = {
optionsContext: Settings.OptionsContext;
- detail?: SearchResultDetail;
+ detail: SearchResultDetail;
};
export type SelectionRestoreInfo = {