import { SearchSentenceResult } from "../search/types.ts"; import APIBase from "./base.ts"; import { JapaneseFormatter } from "./japanese.ts"; import Word from "./word.ts"; export default class Sentence extends APIBase { public words: Array = []; protected query?: SearchSentenceResult; protected original: string = ""; protected breaks: Array = []; protected frozen = false; private _resolveReady: () => void = () => {}; public ready = new Promise(res => this._resolveReady = res); constructor(input: string) { super(); this.original = input; this.update(); } first(searchValue: RegExp | string): Word | undefined { return this.at(this.original.search(searchValue)); } private async fetch() { this.query = await (await this.api)["core"].search.sentence(this.original, { breaks: this.breaks }); } private async updateWords() { this.words = []; let token = 0; let i = 0; while (i < this.original.length) { this.words.push( new Word(this.query!.words[token]) .withAPI(await this.api) .withParent(this) ); i += this.query!.words[token].source.length; if (i == this.original.length) break; token++; // continue if there are no unrecognized gaps between words if (this.query!.words[token]?.start == i) continue; var remainder = this.original.substring(i, this.query!.words[token]?.start); this.words.push( new Word(remainder) .withAPI(await this.api) .withParent(this) ); i += remainder.length; } } furigana(format: JapaneseFormatter = "HTML"): string { return this.words.reduce((out, word) => { return out + word.furigana(format); }, ""); } get reading(): string { return this.words.reduce((out, word) => { return out + word.reading; }, ""); } public async forceUpdate() { // unresolve ready this.ready = new Promise(res => this._resolveReady = res); // fetch sentence from DB await this.fetch(); // parse words into Word await this.updateWords(); // mark ready again this._resolveReady(); } public async update() { if (this.frozen) return; await this.forceUpdate(); } public indexOf(searchString: string, position: number = 0) { return this.original.indexOf(searchString, position); } public at(indentifier: number | string): Word | undefined { var index = typeof indentifier === "number" ? indentifier : this.indexOf(indentifier); if (index == -1) return; let wordIndex = 0; for (let i = 0; wordIndex < this.words.length; wordIndex++) { var length = this.words[wordIndex].length; if (i + length > index) break; i += length; } return this.words[wordIndex]; } public async break(location: number) { this.breaks.push(location); await this.update(); } public async freeze() { this.frozen = true; } public async unfreeze() { this.frozen = false; await this.forceUpdate(); } }