aboutsummaryrefslogtreecommitdiff
path: root/ext/mixed/js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mixed/js')
-rw-r--r--ext/mixed/js/display.js76
-rw-r--r--ext/mixed/js/japanese.js69
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;
+}