From 0e1f90cbfe17ebfdf40dff3dd5d1de30119b3445 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 28 Jan 2024 21:54:09 -0500 Subject: [PATCH 1/5] Remove return --- ext/js/language/text-scanner.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index d78c4c7403..96ea690aba 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} */ 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,7 +421,6 @@ export class TextScanner extends EventDispatcher { * @param {boolean} searchTerms * @param {boolean} searchKanji * @param {import('text-scanner').InputInfo} inputInfo - * @returns {Promise} */ async _search(textSource, searchTerms, searchKanji, inputInfo) { /** @type {?import('dictionary').DictionaryEntry[]} */ @@ -448,7 +446,7 @@ export class TextScanner extends EventDispatcher { ); if (this._textSourceCurrent !== null && this._textSourceCurrent.hasSameStart(textSource)) { - return null; + return; } const getSearchContextPromise = this._getSearchContext(); @@ -482,7 +480,7 @@ export class TextScanner extends EventDispatcher { error = e instanceof Error ? e : new Error(`A search error occurred: ${e}`); } - if (!searched) { return null; } + if (!searched) { return; } /** @type {import('text-scanner').SearchedEventDetails} */ const results = { @@ -497,7 +495,6 @@ export class TextScanner extends EventDispatcher { error }; this.trigger('searched', results); - return results; } /** */ From a8122d2fe890e79f1467695cc440ed7dd9728f1d Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 28 Jan 2024 22:16:11 -0500 Subject: [PATCH 2/5] Update searched event --- ext/js/app/frontend.js | 2 +- ext/js/display/display.js | 2 +- ext/js/display/query-parser.js | 4 ++-- ext/js/language/text-scanner.js | 20 ++++++++++++++++---- types/ext/text-scanner.d.ts | 2 +- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index de1c5a4697..317349e556 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -382,7 +382,7 @@ export class Frontend { } else { log.error(error); } - } if (type !== null && optionsContext !== null) { + } if (type !== null && optionsContext !== null && textSource !== null) { this._stopClearSelectionDelayed(); let focus = (eventType === 'mouseMove'); if (typeof inputInfoDetail === 'object' && inputInfoDetail !== null) { diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 4114cc456b..6a93715a99 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -1902,7 +1902,7 @@ export class Display extends EventDispatcher { log.error(error); } - if (type === null) { return; } + if (type === null || textSource === null) { return; } const query = textSource.text(); const url = window.location.href; diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js index daa298d230..45b0dd05f4 100644 --- a/ext/js/display/query-parser.js +++ b/ext/js/display/query-parser.js @@ -165,7 +165,7 @@ export class QueryParser extends EventDispatcher { textSource, optionsContext } = e; - if (type === null || dictionaryEntries === null || sentence === null || optionsContext === null) { return; } + if (type === null || dictionaryEntries === null || sentence === null || optionsContext === null || textSource === null) { return; } this.trigger('searched', { textScanner, @@ -175,7 +175,7 @@ export class QueryParser extends EventDispatcher { inputInfo, textSource, optionsContext, - sentenceOffset: this._getSentenceOffset(e.textSource) + sentenceOffset: this._getSentenceOffset(textSource) }); } diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index 96ea690aba..1c0d5aeba8 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -482,8 +482,21 @@ export class TextScanner extends EventDispatcher { if (!searched) { return; } - /** @type {import('text-scanner').SearchedEventDetails} */ - const results = { + this._triggerSearched(type, dictionaryEntries, sentence, inputInfo, textSource, optionsContext, detail, error); + } + + /** + * @param {?import('display').PageType} type + * @param {?import('dictionary').DictionaryEntry[]} dictionaryEntries + * @param {?import('display').HistoryStateSentence} sentence + * @param {import('text-scanner').InputInfo} inputInfo + * @param {?import('text-source').TextSource} textSource + * @param {?import('settings').OptionsContext} optionsContext + * @param {?import('text-scanner').SearchResultDetail} detail + * @param {?Error} error + */ + _triggerSearched(type, dictionaryEntries, sentence, inputInfo, textSource, optionsContext, detail, error) { + this.trigger('searched', { textScanner: this, type, dictionaryEntries, @@ -493,8 +506,7 @@ export class TextScanner extends EventDispatcher { optionsContext, detail, error - }; - this.trigger('searched', results); + }); } /** */ diff --git a/types/ext/text-scanner.d.ts b/types/ext/text-scanner.d.ts index 4253d6cc07..c08d5ba529 100644 --- a/types/ext/text-scanner.d.ts +++ b/types/ext/text-scanner.d.ts @@ -100,7 +100,7 @@ export type SearchedEventDetails = { dictionaryEntries: Dictionary.DictionaryEntry[] | null; sentence: Display.HistoryStateSentence | null; inputInfo: InputInfo; - textSource: TextSource.TextSource; + textSource: TextSource.TextSource | null; optionsContext: Settings.OptionsContext | null; detail: SearchResultDetail | null; error: Error | null; From 6f48ea2e4d0a59c50f64c88cfcb82d6503fbca99 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 28 Jan 2024 22:16:32 -0500 Subject: [PATCH 3/5] Fix auto-hide search popup event not occuring --- ext/js/language/text-scanner.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index 1c0d5aeba8..754381b5d8 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -1296,10 +1296,10 @@ export class TextScanner extends EventDispatcher { try { await this._search(textSource, searchTerms, searchKanji, inputInfo); } finally { - if (textSource !== null) { - textSource.cleanup(); - } + textSource.cleanup(); } + } else { + this._triggerSearched(null, null, null, inputInfo, null, null, null, null); } } catch (e) { log.error(e); From 73ab96ee26cad1d44e57f9fdc92ae75507d23eef Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 28 Jan 2024 22:16:37 -0500 Subject: [PATCH 4/5] Add TODO --- types/ext/text-scanner.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/ext/text-scanner.d.ts b/types/ext/text-scanner.d.ts index c08d5ba529..351540f1ea 100644 --- a/types/ext/text-scanner.d.ts +++ b/types/ext/text-scanner.d.ts @@ -94,6 +94,7 @@ export type InputConfig = { preventPenScrolling: boolean; }; +// TODO : this event should be split into 3 different types : searchSuccess, searchEmpty, searchError export type SearchedEventDetails = { textScanner: TextScanner; type: Display.PageType | null; From 8247811b0284b75c4346c80066ab89eda6839558 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 29 Jan 2024 19:32:08 -0500 Subject: [PATCH 5/5] Split into multiple events --- ext/js/app/frontend.js | 51 ++++++++++--------- ext/js/display/display.js | 29 +++++++---- ext/js/display/query-parser.js | 33 +++++-------- ext/js/language/text-scanner.js | 88 +++++++++++++-------------------- types/ext/text-scanner.d.ts | 34 +++++++------ 5 files changed, 111 insertions(+), 124 deletions(-) diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index 317349e556..5f412340e8 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 && textSource !== 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} */ async _getSearchContext() { let url = window.location.href; diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 6a93715a99..676f1a4fd4 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 || textSource === null) { return; } - + _onContentTextScannerSearchSuccess({type, dictionaryEntries, sentence, textSource, optionsContext}) { const query = textSource.text(); const url = window.location.href; const documentTitle = document.title; @@ -1932,11 +1927,25 @@ export class Display extends EventDispatcher { this.setContent(details); } + /** + * @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 45b0dd05f4..6ec803a0b3 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,28 +148,11 @@ 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 || textSource === null) { return; } - + _onSearchSuccess({type, dictionaryEntries, sentence, inputInfo, textSource, optionsContext}) { this.trigger('searched', { - textScanner, + textScanner: this._textScanner, type, dictionaryEntries, sentence, @@ -179,6 +163,13 @@ export class QueryParser extends EventDispatcher { }); } + /** + * @param {import('text-scanner').EventArgument<'searchError'>} details + */ + _onSearchError({error}) { + log.error(error); + } + /** * @param {Event} e */ diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index 754381b5d8..5b12506328 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -423,20 +423,6 @@ export class TextScanner extends EventDispatcher { * @param {import('text-scanner').InputInfo} inputInfo */ 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 = ( @@ -451,62 +437,54 @@ export class TextScanner extends EventDispatcher { 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; } - - this._triggerSearched(type, dictionaryEntries, sentence, inputInfo, textSource, optionsContext, detail, error); } /** - * @param {?import('display').PageType} type - * @param {?import('dictionary').DictionaryEntry[]} dictionaryEntries - * @param {?import('display').HistoryStateSentence} sentence * @param {import('text-scanner').InputInfo} inputInfo - * @param {?import('text-source').TextSource} textSource - * @param {?import('settings').OptionsContext} optionsContext - * @param {?import('text-scanner').SearchResultDetail} detail - * @param {?Error} error - */ - _triggerSearched(type, dictionaryEntries, sentence, inputInfo, textSource, optionsContext, detail, error) { - this.trigger('searched', { - textScanner: this, - type, - dictionaryEntries, - sentence, - inputInfo, - textSource, - optionsContext, - detail, - error - }); + */ + _triggerSearchEmpty(inputInfo) { + this.trigger('searchEmpty', {inputInfo}); } /** */ @@ -1299,7 +1277,7 @@ export class TextScanner extends EventDispatcher { textSource.cleanup(); } } else { - this._triggerSearched(null, null, null, inputInfo, null, null, null, null); + 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 351540f1ea..8acc780d11 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 . */ -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,19 +93,6 @@ export type InputConfig = { preventPenScrolling: boolean; }; -// TODO : this event should be split into 3 different types : searchSuccess, searchEmpty, searchError -export type SearchedEventDetails = { - textScanner: TextScanner; - type: Display.PageType | null; - dictionaryEntries: Dictionary.DictionaryEntry[] | null; - sentence: Display.HistoryStateSentence | null; - inputInfo: InputInfo; - textSource: TextSource.TextSource | null; - optionsContext: Settings.OptionsContext | null; - detail: SearchResultDetail | null; - error: Error | null; -}; - export type InputInfo = { input: InputConfig | null; pointerType: PointerType; @@ -123,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'; @@ -154,7 +156,7 @@ export type ConstructorDetails = { export type SearchContext = { optionsContext: Settings.OptionsContext; - detail?: SearchResultDetail; + detail: SearchResultDetail; }; export type SelectionRestoreInfo = {