diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-09-27 19:07:28 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-09-27 19:07:28 -0400 | 
| commit | b0f6c41f5dc7f498f2948f846dd273bcb1bc1f0b (patch) | |
| tree | 09f17dd0c6d549528d680c6f982efeb55610858d | |
| parent | b784e5b11a596a456eb8879d394fde64bc63aaef (diff) | |
Search query offset value (#1968)
* Add type property to TextSource* classes
* Use type property rather than instanceof
* Expose a sentence offset value
* Use offset added to URL
* Improve fallback sentence for Anki note context
| -rw-r--r-- | ext/js/app/frontend.js | 3 | ||||
| -rw-r--r-- | ext/js/display/display-anki.js | 8 | ||||
| -rw-r--r-- | ext/js/display/display.js | 46 | ||||
| -rw-r--r-- | ext/js/display/query-parser.js | 36 | ||||
| -rw-r--r-- | ext/js/dom/text-source-element.js | 4 | ||||
| -rw-r--r-- | ext/js/dom/text-source-range.js | 4 | ||||
| -rw-r--r-- | ext/js/language/text-scanner.js | 3 | 
7 files changed, 81 insertions, 23 deletions
| diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index ef53cecb..0d358639 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -18,7 +18,6 @@  /* global   * DocumentUtil   * TextScanner - * TextSourceElement   * TextSourceRange   */ @@ -536,7 +535,7 @@ class Frontend {                  }              }          }; -        if (textSource instanceof TextSourceElement && textSource.fullContent !== query) { +        if (textSource.type === 'element' && textSource.fullContent !== query) {              details.params.full = textSource.fullContent;              details.params['full-visible'] = 'true';          } diff --git a/ext/js/display/display-anki.js b/ext/js/display/display-anki.js index d17a2f0f..216e3108 100644 --- a/ext/js/display/display-anki.js +++ b/ext/js/display/display-anki.js @@ -216,8 +216,8 @@ class DisplayAnki {          if (typeof url !== 'string') {              url = window.location.href;          } -        const {query, fullQuery} = this._display; -        sentence = this._getValidSentenceData(sentence, query); +        const {query, fullQuery, queryOffset} = this._display; +        sentence = this._getValidSentenceData(sentence, fullQuery, queryOffset);          return {              url,              sentence, @@ -565,11 +565,11 @@ class DisplayAnki {          return isTerms ? ['term-kanji', 'term-kana'] : ['kanji'];      } -    _getValidSentenceData(sentence, fallback) { +    _getValidSentenceData(sentence, fallback, fallbackOffset) {          let {text, offset} = (isObject(sentence) ? sentence : {});          if (typeof text !== 'string') {              text = fallback; -            offset = 0; +            offset = fallbackOffset;          } else {              if (typeof offset !== 'number') { offset = 0; }          } diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 0509e22a..0e82af54 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -73,6 +73,7 @@ class Display extends EventDispatcher {          this._titleMaxLength = 1000;          this._query = '';          this._fullQuery = ''; +        this._queryOffset = 0;          this._documentUtil = new DocumentUtil();          this._progressIndicator = document.querySelector('#progress-indicator');          this._progressIndicatorTimer = null; @@ -208,6 +209,10 @@ class Display extends EventDispatcher {          return this._fullQuery;      } +    get queryOffset() { +        return this._queryOffset; +    } +      get frameVisible() {          return this._frameVisible;      } @@ -432,7 +437,7 @@ class Display extends EventDispatcher {          const details = {              focus: false,              historyMode: 'clear', -            params: this._createSearchParams(type, query, false), +            params: this._createSearchParams(type, query, false, this._queryOffset),              state,              content: {                  dictionaryEntries: null, @@ -598,9 +603,14 @@ class Display extends EventDispatcher {                          const isTerms = (type === 'terms');                          let queryFull = urlSearchParams.get('full');                          queryFull = (queryFull !== null ? queryFull : query); +                        let queryOffset = urlSearchParams.get('offset'); +                        if (queryOffset !== null) { +                            queryOffset = Number.parseInt(queryOffset, 10); +                            if (!Number.isFinite(queryOffset)) { queryOffset = null; } +                        }                          const wildcardsEnabled = (urlSearchParams.get('wildcards') !== 'off');                          const lookup = (urlSearchParams.get('lookup') !== 'false'); -                        await this._setContentTermsOrKanji(token, isTerms, query, queryFull, lookup, wildcardsEnabled, eventArgs); +                        await this._setContentTermsOrKanji(token, isTerms, query, queryFull, queryOffset, lookup, wildcardsEnabled, eventArgs);                      }                      break;                  case 'unloaded': @@ -633,7 +643,7 @@ class Display extends EventDispatcher {          }      } -    _onQueryParserSearch({type, dictionaryEntries, sentence, inputInfo: {eventType}, textSource, optionsContext}) { +    _onQueryParserSearch({type, dictionaryEntries, sentence, inputInfo: {eventType}, textSource, optionsContext, sentenceOffset}) {          const query = textSource.text();          const historyState = this._history.state;          const historyMode = ( @@ -644,7 +654,7 @@ class Display extends EventDispatcher {          const details = {              focus: false,              historyMode, -            params: this._createSearchParams(type, query, false), +            params: this._createSearchParams(type, query, false, sentenceOffset),              state: {                  sentence,                  optionsContext, @@ -724,7 +734,7 @@ class Display extends EventDispatcher {              const details = {                  focus: false,                  historyMode: 'new', -                params: this._createSearchParams('kanji', query, false), +                params: this._createSearchParams('kanji', query, false, null),                  state: {                      focusEntry: 0,                      optionsContext, @@ -887,7 +897,7 @@ class Display extends EventDispatcher {          }      } -    async _setContentTermsOrKanji(token, isTerms, query, queryFull, lookup, wildcardsEnabled, eventArgs) { +    async _setContentTermsOrKanji(token, isTerms, query, queryFull, queryOffset, lookup, wildcardsEnabled, eventArgs) {          let {state, content} = this._history;          let changeHistory = false;          if (!isObject(content)) { @@ -912,7 +922,11 @@ class Display extends EventDispatcher {              changeHistory = true;          } -        this._setFullQuery(queryFull); +        if (queryOffset !== null) { +            queryOffset = Math.max(0, Math.min(queryFull.length - query.length, queryOffset)); +        } + +        this._setFullQuery(queryFull, queryOffset);          this._setTitleText(query);          let {dictionaryEntries} = content; @@ -1015,13 +1029,13 @@ class Display extends EventDispatcher {          this._updateNavigation(false, false);          this._setNoContentVisible(false);          this._setTitleText(''); -        this._setFullQuery(''); +        this._setFullQuery('', 0);      }      _clearContent() {          this._container.textContent = '';          this._setTitleText(''); -        this._setFullQuery(''); +        this._setFullQuery('', 0);      }      _setNoContentVisible(visible) { @@ -1032,8 +1046,9 @@ class Display extends EventDispatcher {          }      } -    _setFullQuery(text) { +    _setFullQuery(text, offset) {          this._fullQuery = text; +        this._queryOffset = offset;          this._updateQueryParser();      } @@ -1200,12 +1215,17 @@ class Display extends EventDispatcher {          }      } -    _createSearchParams(type, query, wildcards) { +    _createSearchParams(type, query, wildcards, sentenceOffset) {          const params = {}; -        if (query.length < this._fullQuery.length) { -            params.full = this._fullQuery; +        const fullQuery = this._fullQuery; +        const includeFull = (query.length < fullQuery.length); +        if (includeFull) { +            params.full = fullQuery;          }          params.query = query; +        if (includeFull && sentenceOffset !== null) { +            params.offset = `${sentenceOffset}`; +        }          if (typeof type === 'string') {              params.type = type;          } diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js index cbcf7cff..e2578fcf 100644 --- a/ext/js/display/query-parser.js +++ b/ext/js/display/query-parser.js @@ -117,6 +117,8 @@ class QueryParser extends EventDispatcher {          }          if (e.type === null) { return; } +        e.sentenceOffset = this._getSentenceOffset(e.textSource); +          this.trigger('searched', e);      } @@ -208,29 +210,33 @@ class QueryParser extends EventDispatcher {      }      _createParseResult(data) { +        let offset = 0;          const fragment = document.createDocumentFragment();          for (const term of data) {              const termNode = document.createElement('span');              termNode.className = 'query-parser-term'; +            termNode.dataset.offset = `${offset}`;              for (const {text, reading} of term) {                  if (reading.length === 0) {                      termNode.appendChild(document.createTextNode(text));                  } else {                      const reading2 = this._convertReading(text, reading); -                    termNode.appendChild(this._createSegment(text, reading2)); +                    termNode.appendChild(this._createSegment(text, reading2, offset));                  } +                offset += text.length;              }              fragment.appendChild(termNode);          }          return fragment;      } -    _createSegment(text, reading) { +    _createSegment(text, reading, offset) {          const segmentNode = document.createElement('ruby');          segmentNode.className = 'query-parser-segment';          const textNode = document.createElement('span');          textNode.className = 'query-parser-segment-text'; +        textNode.dataset.offset = `${offset}`;          const readingNode = document.createElement('rt');          readingNode.className = 'query-parser-segment-reading'; @@ -265,4 +271,30 @@ class QueryParser extends EventDispatcher {                  return reading;          }      } + +    _getSentenceOffset(textSource) { +        if (textSource.type === 'range') { +            const {range} = textSource; +            const node = this._getParentElement(range.startContainer); +            if (node !== null) { +                const {offset} = node.dataset; +                if (typeof offset === 'string') { +                    const value = Number.parseInt(offset, 10); +                    if (Number.isFinite(value)) { +                        return Math.max(0, value) + range.startOffset; +                    } +                } +            } +        } +        return null; +    } + +    _getParentElement(node) { +        const {ELEMENT_NODE} = Node; +        while (true) { +            node = node.parentNode; +            if (node === null) { return null; } +            if (node.nodeType === ELEMENT_NODE) { return node; } +        } +    }  } diff --git a/ext/js/dom/text-source-element.js b/ext/js/dom/text-source-element.js index 499ee45d..11bf7eb0 100644 --- a/ext/js/dom/text-source-element.js +++ b/ext/js/dom/text-source-element.js @@ -24,6 +24,10 @@ class TextSourceElement {          this._content = this._fullContent.substring(this._startOffset, this._endOffset);      } +    get type() { +        return 'element'; +    } +      get element() {          return this._element;      } diff --git a/ext/js/dom/text-source-range.js b/ext/js/dom/text-source-range.js index 377016da..591429da 100644 --- a/ext/js/dom/text-source-range.js +++ b/ext/js/dom/text-source-range.js @@ -29,6 +29,10 @@ class TextSourceRange {          this._imposterSourceElement = imposterSourceElement;      } +    get type() { +        return 'range'; +    } +      get range() {          return this._range;      } diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index c7cedd7e..5d37c84b 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -17,7 +17,6 @@  /* global   * DocumentUtil - * TextSourceElement   */  class TextScanner extends EventDispatcher { @@ -345,7 +344,7 @@ class TextScanner extends EventDispatcher {              if (result !== null) {                  ({dictionaryEntries, sentence, type} = result);                  valid = true; -            } else if (textSource instanceof TextSourceElement && await this._hasJapanese(textSource.fullContent)) { +            } else if (textSource !== null && textSource.type === 'element' && await this._hasJapanese(textSource.fullContent)) {                  dictionaryEntries = [];                  sentence = {sentence: '', offset: 0};                  type = 'terms'; |