aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2021-09-27 19:07:28 -0400
committerGitHub <noreply@github.com>2021-09-27 19:07:28 -0400
commitb0f6c41f5dc7f498f2948f846dd273bcb1bc1f0b (patch)
tree09f17dd0c6d549528d680c6f982efeb55610858d
parentb784e5b11a596a456eb8879d394fde64bc63aaef (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.js3
-rw-r--r--ext/js/display/display-anki.js8
-rw-r--r--ext/js/display/display.js46
-rw-r--r--ext/js/display/query-parser.js36
-rw-r--r--ext/js/dom/text-source-element.js4
-rw-r--r--ext/js/dom/text-source-range.js4
-rw-r--r--ext/js/language/text-scanner.js3
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';