diff options
Diffstat (limited to 'ext/mixed/js')
-rw-r--r-- | ext/mixed/js/display.js | 76 | ||||
-rw-r--r-- | ext/mixed/js/japanese.js | 69 |
2 files changed, 119 insertions, 26 deletions
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 8ad3ee1b..4c698ecf 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -98,17 +98,62 @@ class Display { } } - async onTermLookup(e) { + async onTermLookup(e, {disableScroll, selectText, disableHistory}={}) { + const termLookupResults = await this.termLookup(e); + if (!termLookupResults) { + return false; + } + + try { + const {textSource, definitions} = termLookupResults; + + const scannedElement = e.target; + const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + + if (!disableScroll) { + this.windowScroll.toY(0); + } + let context; + if (disableHistory) { + const {url, source} = this.context || {}; + context = {sentence, url, source, disableScroll}; + } else { + context = { + disableScroll, + source: { + definitions: this.definitions, + index: this.entryIndexFind(scannedElement), + scroll: this.windowScroll.y + } + }; + + if (this.context) { + context.sentence = sentence; + context.url = this.context.url; + context.source.source = this.context.source; + } + } + + this.setContentTerms(definitions, context); + + if (selectText) { + textSource.select(); + } + } catch (error) { + this.onError(error); + } + } + + async termLookup(e) { try { e.preventDefault(); - const clickedElement = e.target; const textSource = docRangeFromPoint(e.clientX, e.clientY, this.options); if (textSource === null) { return false; } - let definitions, length, sentence; + let definitions, length; try { textSource.setEndOffset(this.options.scanning.length); @@ -118,28 +163,11 @@ class Display { } textSource.setEndOffset(length); - - sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); } finally { textSource.cleanup(); } - this.windowScroll.toY(0); - const context = { - source: { - definitions: this.definitions, - index: this.entryIndexFind(clickedElement), - scroll: this.windowScroll.y - } - }; - - if (this.context) { - context.sentence = sentence; - context.url = this.context.url; - context.source.source = this.context.source; - } - - this.setContentTerms(definitions, context); + return {textSource, definitions}; } catch (error) { this.onError(error); } @@ -336,8 +364,10 @@ class Display { const content = await apiTemplateRender('terms.html', params); this.container.innerHTML = content; - const {index, scroll} = context || {}; - this.entryScrollIntoView(index || 0, scroll); + const {index, scroll, disableScroll} = context || {}; + if (!disableScroll) { + this.entryScrollIntoView(index || 0, scroll); + } if (options.audio.enabled && options.audio.autoPlay) { this.autoPlayAudio(); diff --git a/ext/mixed/js/japanese.js b/ext/mixed/js/japanese.js index d24f56a6..a7cd0452 100644 --- a/ext/mixed/js/japanese.js +++ b/ext/mixed/js/japanese.js @@ -48,6 +48,43 @@ function jpKatakanaToHiragana(text) { return result; } +function jpHiraganaToKatakana(text) { + let result = ''; + for (const c of text) { + if (wanakana.isHiragana(c)) { + result += wanakana.toKatakana(c); + } else { + result += c; + } + } + + return result; +} + +function jpToRomaji(text) { + return wanakana.toRomaji(text); +} + +function jpConvertReading(expressionFragment, readingFragment, readingMode) { + switch (readingMode) { + case 'hiragana': + return jpKatakanaToHiragana(readingFragment || ''); + case 'katakana': + return jpHiraganaToKatakana(readingFragment || ''); + case 'romaji': + if (readingFragment) { + return jpToRomaji(readingFragment); + } else { + if (jpIsKana(expressionFragment)) { + return jpToRomaji(expressionFragment); + } + } + return readingFragment; + default: + return readingFragment; + } +} + function jpDistributeFurigana(expression, reading) { const fallback = [{furigana: reading, text: expression}]; if (!reading) { @@ -61,12 +98,11 @@ function jpDistributeFurigana(expression, reading) { const group = groups[0]; if (group.mode === 'kana') { - if (reading.startsWith(group.text)) { - const readingUsed = reading.substring(0, group.text.length); + if (jpKatakanaToHiragana(reading).startsWith(jpKatakanaToHiragana(group.text))) { const readingLeft = reading.substring(group.text.length); const segs = segmentize(readingLeft, groups.splice(1)); if (segs) { - return [{text: readingUsed}].concat(segs); + return [{text: group.text}].concat(segs); } } } else { @@ -95,3 +131,30 @@ function jpDistributeFurigana(expression, reading) { return segmentize(reading, groups) || fallback; } + +function jpDistributeFuriganaInflected(expression, reading, source) { + const output = []; + + let stemLength = 0; + const shortest = Math.min(source.length, expression.length); + const sourceHiragana = jpKatakanaToHiragana(source); + const expressionHiragana = jpKatakanaToHiragana(expression); + while (stemLength < shortest && sourceHiragana[stemLength] === expressionHiragana[stemLength]) { + ++stemLength; + } + const offset = source.length - stemLength; + + const stemExpression = source.slice(0, source.length - offset); + const stemReading = reading.slice( + 0, offset === 0 ? reading.length : reading.length - expression.length + stemLength + ); + for (const segment of jpDistributeFurigana(stemExpression, stemReading)) { + output.push(segment); + } + + if (stemLength !== source.length) { + output.push({text: source.slice(stemLength)}); + } + + return output; +} |