diff options
| -rw-r--r-- | ext/bg/js/search.js | 58 | ||||
| -rw-r--r-- | ext/bg/search.html | 2 | ||||
| -rw-r--r-- | ext/fg/float.html | 4 | ||||
| -rw-r--r-- | ext/mixed/css/display.css | 1 | ||||
| -rw-r--r-- | ext/mixed/js/display-generator.js | 1 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 172 | ||||
| -rw-r--r-- | ext/mixed/js/yomichan.js | 9 | 
7 files changed, 153 insertions, 94 deletions
| diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index fc857368..d95fd5e4 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -63,11 +63,14 @@ class DisplaySearch extends Display {          this.on('contentUpdating', this._onContentUpdating.bind(this)); +        this.queryParserVisible = true;          this.setHistorySettings({useBrowserHistory: true});          const options = this.getOptions(); -        const {queryParams: {query='', mode=''}} = parseUrl(window.location.href); +        const urlSearchParams = new URLSearchParams(location.search); +        let mode = urlSearchParams.get('mode'); +        if (mode === null) { mode = ''; }          document.documentElement.dataset.searchMode = mode; @@ -78,8 +81,6 @@ class DisplaySearch extends Display {              this._wanakanaEnable.checked = false;          } -        this._setQuery(query); -          if (mode !== 'popup') {              if (options.general.enableClipboardMonitor === true) {                  this._clipboardMonitorEnable.checked = true; @@ -147,14 +148,24 @@ class DisplaySearch extends Display {          if (!this._isPrepared) { return; }          const query = this._query.value;          if (query) { -            this._setQuery(query);              this._onSearchQueryUpdated(query, false);          }      } +    postProcessQuery(query) { +        if (this._isWanakanaEnabled()) { +            try { +                query = wanakana.toKana(query); +            } catch (e) { +                // NOP +            } +        } +        return query; +    } +      // Private -    _onContentUpdating({type, source, content, urlSearchParams}) { +    _onContentUpdating({type, content, source}) {          let animate = false;          let valid = false;          switch (type) { @@ -173,13 +184,8 @@ class DisplaySearch extends Display {          if (typeof source !== 'string') { source = ''; } -        let full = urlSearchParams.get('full'); -        if (full === null) { full = source; } - -        this._closePopups(); -        this._setQuery(full); +        this._query.value = source;          this._setIntroVisible(!valid, animate); -        this._setTitleText(source);          this._updateSearchButton();      } @@ -289,19 +295,6 @@ class DisplaySearch extends Display {          return this._wanakanaEnable !== null && this._wanakanaEnable.checked;      } -    _setQuery(query) { -        let interpretedQuery = query; -        if (this._isWanakanaEnabled()) { -            try { -                interpretedQuery = wanakana.toKana(query); -            } catch (e) { -                // NOP -            } -        } -        this._query.value = interpretedQuery; -        this.setQueryParserText(interpretedQuery); -    } -      _setIntroVisible(visible, animate) {          if (this._introVisible === visible) {              return; @@ -362,19 +355,6 @@ class DisplaySearch extends Display {          this._search.disabled = this._introVisible && (this._query === null || this._query.value.length === 0);      } -    _setTitleText(text) { -        // Chrome limits title to 1024 characters -        if (text.length > 1000) { -            text = text.substring(0, 1000) + '...'; -        } - -        if (text.length === 0) { -            document.title = 'Yomichan Search'; -        } else { -            document.title = `${text} - Yomichan Search`; -        } -    } -      async _prepareNestedPopups() {          let complete = false; @@ -401,8 +381,4 @@ class DisplaySearch extends Display {          await onOptionsUpdated();      } - -    _closePopups() { -        yomichan.trigger('closePopups'); -    }  } diff --git a/ext/bg/search.html b/ext/bg/search.html index 653a708f..eb85e368 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -46,7 +46,7 @@              <div id="spinner" hidden><img src="/mixed/img/spinner.gif"></div> -            <div class="scan-disable"> +            <div class="scan-disable" id="query-parser-container">                  <div id="query-parser-select-container" class="input-group"></div>                  <div id="query-parser-content"></div>              </div> diff --git a/ext/fg/float.html b/ext/fg/float.html index 427a7e57..17378713 100644 --- a/ext/fg/float.html +++ b/ext/fg/float.html @@ -3,7 +3,7 @@      <head>          <meta charset="UTF-8">          <meta name="viewport" content="width=device-width,initial-scale=1" /> -        <title></title> +        <title>Yomichan Search</title>          <link rel="icon" type="image/png" href="/mixed/img/icon16.png" sizes="16x16">          <link rel="icon" type="image/png" href="/mixed/img/icon19.png" sizes="19x19">          <link rel="icon" type="image/png" href="/mixed/img/icon32.png" sizes="32x32"> @@ -21,7 +21,7 @@              <button class="action-button action-next" data-icon="source-term" title="Next term (Alt + F)"></button>          </div></div><div class="navigation-header-spacer"></div> -        <div class="scan-disable" hidden> +        <div class="scan-disable" id="query-parser-container" hidden>              <div id="query-parser-select-container" class="input-group"></div>              <div id="query-parser-content"></div>          </div> diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css index 703cef1c..f5d9403a 100644 --- a/ext/mixed/css/display.css +++ b/ext/mixed/css/display.css @@ -305,6 +305,7 @@ button.action-button {      border-bottom: 0.03571428em dashed var(--dark-border-color); /* 28px => 1px */      color: var(--default-text-color);      text-decoration: none; +    cursor: pointer;  }  .term-expression[data-frequency=popular]>.term-expression-text, diff --git a/ext/mixed/js/display-generator.js b/ext/mixed/js/display-generator.js index 276b37de..2b0b5b1c 100644 --- a/ext/mixed/js/display-generator.js +++ b/ext/mixed/js/display-generator.js @@ -281,7 +281,6 @@ class DisplayGenerator {      _createKanjiLink(character) {          const node = document.createElement('a'); -        node.href = '#';          node.className = 'kanji-link';          node.textContent = character;          return node; diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index a9f217b4..40febadc 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -69,7 +69,12 @@ class Display extends EventDispatcher {          this._historyChangeIgnore = false;          this._historyHasChanged = false;          this._navigationHeader = document.querySelector('#navigation-header'); +        this._defaultTitle = 'Yomichan Search'; +        this._defaultTitleMaxLength = 1000;          this._fullQuery = ''; +        this._queryParserVisible = false; +        this._queryParserVisibleOverride = null; +        this._queryParserContainer = document.querySelector('#query-parser-container');          this._queryParser = new QueryParser({              getOptionsContext: this.getOptionsContext.bind(this),              setSpinnerVisible: this.setSpinnerVisible.bind(this) @@ -123,6 +128,15 @@ class Display extends EventDispatcher {          this._autoPlayAudioDelay = value;      } +    get queryParserVisible() { +        return this._queryParserVisible; +    } + +    set queryParserVisible(value) { +        this._queryParserVisible = value; +        this._updateQueryParserVisibility(); +    } +      async prepare() {          this._setInteractive(true);          await this._displayGenerator.prepare(); @@ -322,10 +336,8 @@ class Display extends EventDispatcher {          return data;      } -    setQueryParserText(text) { -        if (this._fullQuery === text) { return; } -        this._fullQuery = text; -        this._queryParser.setText(text); +    postProcessQuery(query) { +        return query;      }      // Message handlers @@ -371,6 +383,13 @@ class Display extends EventDispatcher {              let type = urlSearchParams.get('type');              if (type === null) { type = 'terms'; } +            const fullVisible = urlSearchParams.get('full-visible'); +            this._queryParserVisibleOverride = (fullVisible === null ? null : (fullVisible !== 'false')); +            this._updateQueryParserVisibility(); + +            this._closePopups(); +            this._setEventListenersActive(false); +              let asigned = false;              const eventArgs = {type, urlSearchParams, token};              this._historyHasChanged = true; @@ -379,38 +398,8 @@ class Display extends EventDispatcher {                  case 'terms':                  case 'kanji':                      { -                        const source = urlSearchParams.get('query'); -                        if (!source) { break; } -                          const isTerms = (type === 'terms'); -                        let {state, content} = this._history; -                        let changeHistory = false; -                        if (!isObject(content)) { -                            content = {}; -                            changeHistory = true; -                        } -                        if (!isObject(state)) { -                            state = {}; -                            changeHistory = true; -                        } - -                        let {definitions} = content; -                        if (!Array.isArray(definitions)) { -                            definitions = await this._findDefinitions(isTerms, source, urlSearchParams); -                            if (this._setContentToken !== token) { return; } -                            content.definitions = definitions; -                            changeHistory = true; -                        } - -                        if (changeHistory) { -                            this._historyStateUpdate(state, content); -                        } - -                        asigned = true; -                        eventArgs.source = source; -                        eventArgs.content = content; -                        this.trigger('contentUpdating', eventArgs); -                        await this._setContentTermsOrKanji(token, isTerms, definitions, state); +                        asigned = await this._setContentTermsOrKanji(token, isTerms, urlSearchParams, eventArgs);                      }                      break;                  case 'unloaded': @@ -419,19 +408,25 @@ class Display extends EventDispatcher {                          eventArgs.content = content;                          this.trigger('contentUpdating', eventArgs);                          this._setContentExtensionUnloaded(); +                        asigned = true;                      }                      break;              } -            if (!asigned) { -                const {content} = this._history; -                eventArgs.type = 'clear'; -                eventArgs.content = content; -                this.trigger('contentUpdating', eventArgs); -                this._clearContent(); +            const stale = (this._setContentToken !== token); +            if (!stale) { +                if (!asigned) { +                    const {content} = this._history; +                    eventArgs.type = 'clear'; +                    eventArgs.content = content; +                    this.trigger('contentUpdating', eventArgs); +                    this._clearContent(); +                } + +                this._setEventListenersActive(true);              } -            eventArgs.stale = (this._setContentToken !== token); +            eventArgs.stale = stale;              this.trigger('contentUpdated', eventArgs);          } catch (e) {              this.onError(e); @@ -739,12 +734,47 @@ class Display extends EventDispatcher {          }      } -    async _setContentTermsOrKanji(token, isTerms, definitions, {sentence=null, url=null, focusEntry=null, scrollX=null, scrollY=null}) { +    async _setContentTermsOrKanji(token, isTerms, urlSearchParams, eventArgs) { +        let source = urlSearchParams.get('query'); +        if (!source) { return false; } + +        let {state, content} = this._history; +        let changeHistory = false; +        if (!isObject(content)) { +            content = {}; +            changeHistory = true; +        } +        if (!isObject(state)) { +            state = {}; +            changeHistory = true; +        } + +        source = this.postProcessQuery(source); +        let full = urlSearchParams.get('full'); +        full = (full === null ? source : this.postProcessQuery(full)); +        this._setQueryParserText(full); +        this._setTitleText(source); + +        let {definitions} = content; +        if (!Array.isArray(definitions)) { +            definitions = await this._findDefinitions(isTerms, source, urlSearchParams); +            if (this._setContentToken !== token) { return true; } +            content.definitions = definitions; +            changeHistory = true; +        } + +        if (changeHistory) { +            this._historyStateUpdate(state, content); +        } + +        eventArgs.source = source; +        eventArgs.content = content; +        this.trigger('contentUpdating', eventArgs); + +        let {sentence=null, url=null, focusEntry=null, scrollX=null, scrollY=null} = state;          if (typeof url !== 'string') { url = window.location.href; }          sentence = this._getValidSentenceData(sentence); -        this._setEventListenersActive(false); -          this._definitions = definitions;          for (const definition of definitions) { @@ -761,7 +791,7 @@ class Display extends EventDispatcher {          for (let i = 0, ii = definitions.length; i < ii; ++i) {              if (i > 0) {                  await promiseTimeout(1); -                if (this._setContentToken !== token) { return; } +                if (this._setContentToken !== token) { return true; }              }              const entry = ( @@ -791,8 +821,12 @@ class Display extends EventDispatcher {              this.autoPlayAudio();          } -        this._setEventListenersActive(true); +        this._setContentTermsOrKanjiUpdateAdderButtons(token, isTerms, definitions); +        return true; +    } + +    async _setContentTermsOrKanjiUpdateAdderButtons(token, isTerms, definitions) {          const modes = isTerms ? ['term-kanji', 'term-kana'] : ['kanji'];          const states = await this._getDefinitionsAddable(definitions, modes);          if (this._setContentToken !== token) { return; } @@ -813,11 +847,12 @@ class Display extends EventDispatcher {          this._updateNavigation(null, null);          this._setNoContentVisible(false); +        this._setTitleText('');      }      _clearContent() { -        this._setEventListenersActive(false);          this._container.textContent = ''; +        this._setTitleText('');      }      _setNoContentVisible(visible) { @@ -828,6 +863,28 @@ class Display extends EventDispatcher {          }      } +    _setQueryParserText(text) { +        if (this._fullQuery === text) { return; } +        this._fullQuery = text; +        if (!this._isQueryParserVisible()) { return; } +        this._queryParser.setText(text); +    } + +    _setTitleText(text) { +        // Chrome limits title to 1024 characters +        const ellipsis = '...'; +        const maxLength = this._defaultTitleMaxLength - this._defaultTitle.length; +        if (text.length > maxLength) { +            text = `${text.substring(0, Math.max(0, maxLength - maxLength))}${ellipsis}`; +        } + +        document.title = ( +            text.length === 0 ? +            this._defaultTitle : +            `${text} - ${this._defaultTitle}` +        ); +    } +      _updateNavigation(previous, next) {          if (this._navigationHeader === null) { return; }          this._navigationHeader.hidden = !(previous || next); @@ -1177,6 +1234,25 @@ class Display extends EventDispatcher {          if (!wildcards) {              params.wildcards = 'off';          } +        if (this._queryParserVisibleOverride !== null) { +            params['full-visible'] = `${this._queryParserVisibleOverride}`; +        }          return params;      } + +    _isQueryParserVisible() { +        return ( +            this._queryParserVisibleOverride !== null ? +            this._queryParserVisibleOverride : +            this._queryParserVisible +        ); +    } + +    _updateQueryParserVisibility() { +        this._queryParserContainer.hidden = !this._isQueryParserVisible(); +    } + +    _closePopups() { +        yomichan.trigger('closePopups'); +    }  } diff --git a/ext/mixed/js/yomichan.js b/ext/mixed/js/yomichan.js index 6eba68b2..2de82274 100644 --- a/ext/mixed/js/yomichan.js +++ b/ext/mixed/js/yomichan.js @@ -48,6 +48,7 @@ const yomichan = (() => {              }              this._isExtensionUnloaded = false; +            this._isTriggeringExtensionUnloaded = false;              this._isReady = false;              const {promise, resolve} = deferPromise(); @@ -256,7 +257,13 @@ const yomichan = (() => {          triggerExtensionUnloaded() {              this._isExtensionUnloaded = true; -            this.trigger('extensionUnloaded'); +            if (this._isTriggeringExtensionUnloaded) { return; } +            try { +                this._isTriggeringExtensionUnloaded = true; +                this.trigger('extensionUnloaded'); +            } finally { +                this._isTriggeringExtensionUnloaded = false; +            }          }          // Private |