diff options
| -rw-r--r-- | ext/js/app/frontend.js | 51 | ||||
| -rw-r--r-- | ext/js/display/display.js | 29 | ||||
| -rw-r--r-- | ext/js/display/query-parser.js | 35 | ||||
| -rw-r--r-- | ext/js/language/text-scanner.js | 93 | ||||
| -rw-r--r-- | types/ext/text-scanner.d.ts | 33 | 
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 = { |