diff options
Diffstat (limited to 'ext/js/display/query-parser.js')
-rw-r--r-- | ext/js/display/query-parser.js | 127 |
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..fd173bde 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 {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; } } } |