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
This commit is contained in:
toasted-nutbread 2021-09-27 19:07:28 -04:00 committed by GitHub
parent b784e5b11a
commit b0f6c41f5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 81 additions and 23 deletions

View File

@ -18,7 +18,6 @@
/* global /* global
* DocumentUtil * DocumentUtil
* TextScanner * TextScanner
* TextSourceElement
* TextSourceRange * 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 = textSource.fullContent;
details.params['full-visible'] = 'true'; details.params['full-visible'] = 'true';
} }

View File

@ -216,8 +216,8 @@ class DisplayAnki {
if (typeof url !== 'string') { if (typeof url !== 'string') {
url = window.location.href; url = window.location.href;
} }
const {query, fullQuery} = this._display; const {query, fullQuery, queryOffset} = this._display;
sentence = this._getValidSentenceData(sentence, query); sentence = this._getValidSentenceData(sentence, fullQuery, queryOffset);
return { return {
url, url,
sentence, sentence,
@ -565,11 +565,11 @@ class DisplayAnki {
return isTerms ? ['term-kanji', 'term-kana'] : ['kanji']; return isTerms ? ['term-kanji', 'term-kana'] : ['kanji'];
} }
_getValidSentenceData(sentence, fallback) { _getValidSentenceData(sentence, fallback, fallbackOffset) {
let {text, offset} = (isObject(sentence) ? sentence : {}); let {text, offset} = (isObject(sentence) ? sentence : {});
if (typeof text !== 'string') { if (typeof text !== 'string') {
text = fallback; text = fallback;
offset = 0; offset = fallbackOffset;
} else { } else {
if (typeof offset !== 'number') { offset = 0; } if (typeof offset !== 'number') { offset = 0; }
} }

View File

@ -73,6 +73,7 @@ class Display extends EventDispatcher {
this._titleMaxLength = 1000; this._titleMaxLength = 1000;
this._query = ''; this._query = '';
this._fullQuery = ''; this._fullQuery = '';
this._queryOffset = 0;
this._documentUtil = new DocumentUtil(); this._documentUtil = new DocumentUtil();
this._progressIndicator = document.querySelector('#progress-indicator'); this._progressIndicator = document.querySelector('#progress-indicator');
this._progressIndicatorTimer = null; this._progressIndicatorTimer = null;
@ -208,6 +209,10 @@ class Display extends EventDispatcher {
return this._fullQuery; return this._fullQuery;
} }
get queryOffset() {
return this._queryOffset;
}
get frameVisible() { get frameVisible() {
return this._frameVisible; return this._frameVisible;
} }
@ -432,7 +437,7 @@ class Display extends EventDispatcher {
const details = { const details = {
focus: false, focus: false,
historyMode: 'clear', historyMode: 'clear',
params: this._createSearchParams(type, query, false), params: this._createSearchParams(type, query, false, this._queryOffset),
state, state,
content: { content: {
dictionaryEntries: null, dictionaryEntries: null,
@ -598,9 +603,14 @@ class Display extends EventDispatcher {
const isTerms = (type === 'terms'); const isTerms = (type === 'terms');
let queryFull = urlSearchParams.get('full'); let queryFull = urlSearchParams.get('full');
queryFull = (queryFull !== null ? queryFull : query); 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 wildcardsEnabled = (urlSearchParams.get('wildcards') !== 'off');
const lookup = (urlSearchParams.get('lookup') !== 'false'); 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; break;
case 'unloaded': 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 query = textSource.text();
const historyState = this._history.state; const historyState = this._history.state;
const historyMode = ( const historyMode = (
@ -644,7 +654,7 @@ class Display extends EventDispatcher {
const details = { const details = {
focus: false, focus: false,
historyMode, historyMode,
params: this._createSearchParams(type, query, false), params: this._createSearchParams(type, query, false, sentenceOffset),
state: { state: {
sentence, sentence,
optionsContext, optionsContext,
@ -724,7 +734,7 @@ class Display extends EventDispatcher {
const details = { const details = {
focus: false, focus: false,
historyMode: 'new', historyMode: 'new',
params: this._createSearchParams('kanji', query, false), params: this._createSearchParams('kanji', query, false, null),
state: { state: {
focusEntry: 0, focusEntry: 0,
optionsContext, 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 {state, content} = this._history;
let changeHistory = false; let changeHistory = false;
if (!isObject(content)) { if (!isObject(content)) {
@ -912,7 +922,11 @@ class Display extends EventDispatcher {
changeHistory = true; 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); this._setTitleText(query);
let {dictionaryEntries} = content; let {dictionaryEntries} = content;
@ -1015,13 +1029,13 @@ class Display extends EventDispatcher {
this._updateNavigation(false, false); this._updateNavigation(false, false);
this._setNoContentVisible(false); this._setNoContentVisible(false);
this._setTitleText(''); this._setTitleText('');
this._setFullQuery(''); this._setFullQuery('', 0);
} }
_clearContent() { _clearContent() {
this._container.textContent = ''; this._container.textContent = '';
this._setTitleText(''); this._setTitleText('');
this._setFullQuery(''); this._setFullQuery('', 0);
} }
_setNoContentVisible(visible) { _setNoContentVisible(visible) {
@ -1032,8 +1046,9 @@ class Display extends EventDispatcher {
} }
} }
_setFullQuery(text) { _setFullQuery(text, offset) {
this._fullQuery = text; this._fullQuery = text;
this._queryOffset = offset;
this._updateQueryParser(); this._updateQueryParser();
} }
@ -1200,12 +1215,17 @@ class Display extends EventDispatcher {
} }
} }
_createSearchParams(type, query, wildcards) { _createSearchParams(type, query, wildcards, sentenceOffset) {
const params = {}; const params = {};
if (query.length < this._fullQuery.length) { const fullQuery = this._fullQuery;
params.full = this._fullQuery; const includeFull = (query.length < fullQuery.length);
if (includeFull) {
params.full = fullQuery;
} }
params.query = query; params.query = query;
if (includeFull && sentenceOffset !== null) {
params.offset = `${sentenceOffset}`;
}
if (typeof type === 'string') { if (typeof type === 'string') {
params.type = type; params.type = type;
} }

View File

@ -117,6 +117,8 @@ class QueryParser extends EventDispatcher {
} }
if (e.type === null) { return; } if (e.type === null) { return; }
e.sentenceOffset = this._getSentenceOffset(e.textSource);
this.trigger('searched', e); this.trigger('searched', e);
} }
@ -208,29 +210,33 @@ class QueryParser extends EventDispatcher {
} }
_createParseResult(data) { _createParseResult(data) {
let offset = 0;
const fragment = document.createDocumentFragment(); const fragment = document.createDocumentFragment();
for (const term of data) { for (const term of data) {
const termNode = document.createElement('span'); const termNode = document.createElement('span');
termNode.className = 'query-parser-term'; termNode.className = 'query-parser-term';
termNode.dataset.offset = `${offset}`;
for (const {text, reading} of term) { for (const {text, reading} of term) {
if (reading.length === 0) { if (reading.length === 0) {
termNode.appendChild(document.createTextNode(text)); termNode.appendChild(document.createTextNode(text));
} else { } else {
const reading2 = this._convertReading(text, reading); 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); fragment.appendChild(termNode);
} }
return fragment; return fragment;
} }
_createSegment(text, reading) { _createSegment(text, reading, offset) {
const segmentNode = document.createElement('ruby'); const segmentNode = document.createElement('ruby');
segmentNode.className = 'query-parser-segment'; segmentNode.className = 'query-parser-segment';
const textNode = document.createElement('span'); const textNode = document.createElement('span');
textNode.className = 'query-parser-segment-text'; textNode.className = 'query-parser-segment-text';
textNode.dataset.offset = `${offset}`;
const readingNode = document.createElement('rt'); const readingNode = document.createElement('rt');
readingNode.className = 'query-parser-segment-reading'; readingNode.className = 'query-parser-segment-reading';
@ -265,4 +271,30 @@ class QueryParser extends EventDispatcher {
return reading; 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; }
}
}
} }

View File

@ -24,6 +24,10 @@ class TextSourceElement {
this._content = this._fullContent.substring(this._startOffset, this._endOffset); this._content = this._fullContent.substring(this._startOffset, this._endOffset);
} }
get type() {
return 'element';
}
get element() { get element() {
return this._element; return this._element;
} }

View File

@ -29,6 +29,10 @@ class TextSourceRange {
this._imposterSourceElement = imposterSourceElement; this._imposterSourceElement = imposterSourceElement;
} }
get type() {
return 'range';
}
get range() { get range() {
return this._range; return this._range;
} }

View File

@ -17,7 +17,6 @@
/* global /* global
* DocumentUtil * DocumentUtil
* TextSourceElement
*/ */
class TextScanner extends EventDispatcher { class TextScanner extends EventDispatcher {
@ -345,7 +344,7 @@ class TextScanner extends EventDispatcher {
if (result !== null) { if (result !== null) {
({dictionaryEntries, sentence, type} = result); ({dictionaryEntries, sentence, type} = result);
valid = true; 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 = []; dictionaryEntries = [];
sentence = {sentence: '', offset: 0}; sentence = {sentence: '', offset: 0};
type = 'terms'; type = 'terms';