aboutsummaryrefslogtreecommitdiff
path: root/ext/js/display/query-parser.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/display/query-parser.js')
-rw-r--r--ext/js/display/query-parser.js127
1 files changed, 114 insertions, 13 deletions
diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js
index 85ec3031..03b54fd5 100644
--- a/ext/js/display/query-parser.js
+++ b/ext/js/display/query-parser.js
@@ -20,22 +20,42 @@ import {EventDispatcher, log} from '../core.js';
import {TextScanner} from '../language/text-scanner.js';
import {yomitan} from '../yomitan.js';
+/**
+ * @augments EventDispatcher<import('display').QueryParserEventType>
+ */
export class QueryParser extends EventDispatcher {
+ /**
+ * @param {import('display').QueryParserConstructorDetails} details
+ */
constructor({getSearchContext, japaneseUtil}) {
super();
+ /** @type {import('display').GetSearchContextCallback} */
this._getSearchContext = getSearchContext;
+ /** @type {import('../language/sandbox/japanese-util.js').JapaneseUtil} */
this._japaneseUtil = japaneseUtil;
+ /** @type {string} */
this._text = '';
+ /** @type {?import('core').TokenObject} */
this._setTextToken = null;
+ /** @type {?string} */
this._selectedParser = null;
+ /** @type {import('settings').ParsingReadingMode} */
this._readingMode = 'none';
+ /** @type {number} */
this._scanLength = 1;
+ /** @type {boolean} */
this._useInternalParser = true;
+ /** @type {boolean} */
this._useMecabParser = false;
+ /** @type {import('api').ParseTextResult} */
this._parseResults = [];
- this._queryParser = document.querySelector('#query-parser-content');
- this._queryParserModeContainer = document.querySelector('#query-parser-mode-container');
- this._queryParserModeSelect = document.querySelector('#query-parser-mode-select');
+ /** @type {HTMLElement} */
+ this._queryParser = /** @type {HTMLElement} */ (document.querySelector('#query-parser-content'));
+ /** @type {HTMLElement} */
+ this._queryParserModeContainer = /** @type {HTMLElement} */ (document.querySelector('#query-parser-mode-container'));
+ /** @type {HTMLSelectElement} */
+ this._queryParserModeSelect = /** @type {HTMLSelectElement} */ (document.querySelector('#query-parser-mode-select'));
+ /** @type {TextScanner} */
this._textScanner = new TextScanner({
node: this._queryParser,
getSearchContext,
@@ -45,10 +65,12 @@ export class QueryParser extends EventDispatcher {
});
}
+ /** @type {string} */
get text() {
return this._text;
}
+ /** */
prepare() {
this._textScanner.prepare();
this._textScanner.on('clear', this._onTextScannerClear.bind(this));
@@ -56,6 +78,9 @@ export class QueryParser extends EventDispatcher {
this._queryParserModeSelect.addEventListener('change', this._onParserChange.bind(this), false);
}
+ /**
+ * @param {import('display').QueryParserOptions} display
+ */
setOptions({selectedParser, termSpacing, readingMode, useInternalParser, useMecabParser, scanning}) {
let selectedParserChanged = false;
if (selectedParser === null || typeof selectedParser === 'string') {
@@ -87,10 +112,14 @@ export class QueryParser extends EventDispatcher {
}
}
+ /**
+ * @param {string} text
+ */
async setText(text) {
this._text = text;
this._setPreview(text);
+ /** @type {?import('core').TokenObject} */
const token = {};
this._setTextToken = token;
this._parseResults = await yomitan.api.parseText(text, this._getOptionsContext(), this._scanLength, this._useInternalParser, this._useMecabParser);
@@ -104,32 +133,63 @@ export class QueryParser extends EventDispatcher {
// Private
+ /** */
_onTextScannerClear() {
this._textScanner.clearSelection();
}
+ /**
+ * @param {import('text-scanner').SearchedEventDetails} e
+ */
_onSearched(e) {
const {error} = e;
if (error !== null) {
log.error(error);
return;
}
- if (e.type === null) { return; }
-
- e.sentenceOffset = this._getSentenceOffset(e.textSource);
- this.trigger('searched', e);
+ const {
+ textScanner,
+ type,
+ dictionaryEntries,
+ sentence,
+ inputInfo,
+ textSource,
+ optionsContext
+ } = e;
+ if (type === null || dictionaryEntries === null || sentence === null || optionsContext === null) { return; }
+
+ /** @type {import('display').QueryParserSearchedEvent} */
+ const event2 = {
+ textScanner,
+ type,
+ dictionaryEntries,
+ sentence,
+ inputInfo,
+ textSource,
+ optionsContext,
+ sentenceOffset: this._getSentenceOffset(e.textSource)
+ };
+ this.trigger('searched', event2);
}
+ /**
+ * @param {Event} e
+ */
_onParserChange(e) {
- const value = e.currentTarget.value;
+ const element = /** @type {HTMLInputElement} */ (e.currentTarget);
+ const value = element.value;
this._setSelectedParser(value);
}
+ /**
+ * @returns {import('settings').OptionsContext}
+ */
_getOptionsContext() {
return this._getSearchContext().optionsContext;
}
+ /** */
_refreshSelectedParser() {
if (this._parseResults.length > 0 && !this._getParseResult()) {
const value = this._parseResults[0].id;
@@ -137,22 +197,33 @@ export class QueryParser extends EventDispatcher {
}
}
+ /**
+ * @param {string} value
+ */
_setSelectedParser(value) {
const optionsContext = this._getOptionsContext();
- yomitan.api.modifySettings([{
+ /** @type {import('settings-modifications').ScopedModificationSet} */
+ const modification = {
action: 'set',
path: 'parsing.selectedParser',
value,
scope: 'profile',
optionsContext
- }], 'search');
+ };
+ yomitan.api.modifySettings([modification], 'search');
}
+ /**
+ * @returns {import('api').ParseTextResultItem|undefined}
+ */
_getParseResult() {
const selectedParser = this._selectedParser;
return this._parseResults.find((r) => r.id === selectedParser);
}
+ /**
+ * @param {string} text
+ */
_setPreview(text) {
const terms = [[{text, reading: ''}]];
this._queryParser.textContent = '';
@@ -160,6 +231,7 @@ export class QueryParser extends EventDispatcher {
this._queryParser.appendChild(this._createParseResult(terms));
}
+ /** */
_renderParserSelect() {
const visible = (this._parseResults.length > 1);
if (visible) {
@@ -168,6 +240,7 @@ export class QueryParser extends EventDispatcher {
this._queryParserModeContainer.hidden = !visible;
}
+ /** */
_renderParseResult() {
const parseResult = this._getParseResult();
this._queryParser.textContent = '';
@@ -176,6 +249,11 @@ export class QueryParser extends EventDispatcher {
this._queryParser.appendChild(this._createParseResult(parseResult.content));
}
+ /**
+ * @param {HTMLSelectElement} select
+ * @param {import('api').ParseTextResult} parseResults
+ * @param {?string} selectedParser
+ */
_updateParserModeSelect(select, parseResults, selectedParser) {
const fragment = document.createDocumentFragment();
@@ -208,6 +286,10 @@ export class QueryParser extends EventDispatcher {
select.selectedIndex = selectedIndex;
}
+ /**
+ * @param {import('api').ParseTextLine[]} data
+ * @returns {DocumentFragment}
+ */
_createParseResult(data) {
let offset = 0;
const fragment = document.createDocumentFragment();
@@ -229,6 +311,12 @@ export class QueryParser extends EventDispatcher {
return fragment;
}
+ /**
+ * @param {string} text
+ * @param {string} reading
+ * @param {number} offset
+ * @returns {HTMLElement}
+ */
_createSegment(text, reading, offset) {
const segmentNode = document.createElement('ruby');
segmentNode.className = 'query-parser-segment';
@@ -249,6 +337,11 @@ export class QueryParser extends EventDispatcher {
return segmentNode;
}
+ /**
+ * @param {string} term
+ * @param {string} reading
+ * @returns {string}
+ */
_convertReading(term, reading) {
switch (this._readingMode) {
case 'hiragana':
@@ -271,11 +364,15 @@ export class QueryParser extends EventDispatcher {
}
}
+ /**
+ * @param {import('text-source').TextSource} textSource
+ * @returns {?number}
+ */
_getSentenceOffset(textSource) {
if (textSource.type === 'range') {
const {range} = textSource;
const node = this._getParentElement(range.startContainer);
- if (node !== null) {
+ if (node !== null && node instanceof HTMLElement) {
const {offset} = node.dataset;
if (typeof offset === 'string') {
const value = Number.parseInt(offset, 10);
@@ -288,12 +385,16 @@ export class QueryParser extends EventDispatcher {
return null;
}
+ /**
+ * @param {?Node} node
+ * @returns {?Element}
+ */
_getParentElement(node) {
const {ELEMENT_NODE} = Node;
while (true) {
- node = node.parentNode;
if (node === null) { return null; }
- if (node.nodeType === ELEMENT_NODE) { return node; }
+ if (node.nodeType === ELEMENT_NODE) { return /** @type {Element} */ (node); }
+ node = node.parentNode;
}
}
}