aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorm-edlund <me@fwegmann.com>2024-04-09 10:53:00 +0200
committerGitHub <noreply@github.com>2024-04-09 08:53:00 +0000
commit1d52f94379730ef587de87ce3ca8f1a30ac0a44a (patch)
treeb812a0aed52e415184e058d5f38e1f234ddaef76
parent11e58d616cffbadc5a0c6ab72e5fc803f8c4e70b (diff)
feature: add handlebar for jidoujisho pitch graph (#773)
* feat: add handlebar for jidoujisho pitch graph * fix: update handlebar upgrade test --------- Co-authored-by: StefanVukovic99 <stefanvukovic44@gmail.com>
-rw-r--r--ext/data/templates/anki-field-templates-upgrade-v29.handlebars13
-rw-r--r--ext/data/templates/default-anki-field-templates.handlebars4
-rw-r--r--ext/js/data/options-util.js12
-rw-r--r--ext/js/display/pronunciation-generator.js191
-rw-r--r--ext/js/templates/anki-template-renderer.js4
-rw-r--r--ext/settings.html4
-rw-r--r--test/options-util.test.js2
7 files changed, 227 insertions, 3 deletions
diff --git a/ext/data/templates/anki-field-templates-upgrade-v29.handlebars b/ext/data/templates/anki-field-templates-upgrade-v29.handlebars
new file mode 100644
index 00000000..e19d66c4
--- /dev/null
+++ b/ext/data/templates/anki-field-templates-upgrade-v29.handlebars
@@ -0,0 +1,13 @@
+{{<<<<<<<}}
+{{#*inline "pitch-accent-graphs"}}
+ {{~> pitch-accent-list format='graph'~}}
+{{/inline}}
+{{=======}}
+{{#*inline "pitch-accent-graphs"}}
+ {{~> pitch-accent-list format='graph'~}}
+{{/inline}}
+
+{{#*inline "pitch-accent-graphs-jj"}}
+ {{~> pitch-accent-list format='graph-jj'~}}
+{{/inline}}
+{{>>>>>>>}} \ No newline at end of file
diff --git a/ext/data/templates/default-anki-field-templates.handlebars b/ext/data/templates/default-anki-field-templates.handlebars
index 0495af34..46d5c889 100644
--- a/ext/data/templates/default-anki-field-templates.handlebars
+++ b/ext/data/templates/default-anki-field-templates.handlebars
@@ -228,6 +228,10 @@
{{~> pitch-accent-list format='graph'~}}
{{/inline}}
+{{#*inline "pitch-accent-graphs-jj"}}
+ {{~> pitch-accent-list format='graph-jj'~}}
+{{/inline}}
+
{{#*inline "pitch-accent-positions"}}
{{~> pitch-accent-list format='position'~}}
{{/inline}}
diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js
index d89b3370..af81f29e 100644
--- a/ext/js/data/options-util.js
+++ b/ext/js/data/options-util.js
@@ -534,7 +534,8 @@ export class OptionsUtil {
this._updateVersion25,
this._updateVersion26,
this._updateVersion27,
- this._updateVersion28
+ this._updateVersion28,
+ this._updateVersion29
];
/* eslint-enable @typescript-eslint/unbound-method */
if (typeof targetVersion === 'number' && targetVersion < result.length) {
@@ -1209,6 +1210,15 @@ export class OptionsUtil {
}
/**
+ * - Added new handlebar for different pitch accent graph style.
+ * @type {import('options-util').UpdateFunction}
+ */
+ async _updateVersion29(options) {
+ await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v29.handlebars');
+ }
+
+
+ /**
* @param {string} url
* @returns {Promise<chrome.tabs.Tab>}
*/
diff --git a/ext/js/display/pronunciation-generator.js b/ext/js/display/pronunciation-generator.js
index 2c03eb94..e66df23c 100644
--- a/ext/js/display/pronunciation-generator.js
+++ b/ext/js/display/pronunciation-generator.js
@@ -234,3 +234,194 @@ function createGraphCircle(svgns, className, x, y, radius) {
node.setAttribute('r', radius);
return node;
}
+
+// The following Jidoujisho pitch graph code is based on code from
+// https://github.com/lrorpilla/jidoujisho licensed under the
+// GNU General Public License v3.0
+
+/**
+ * Create a pronounciation graph in the style of Jidoujisho
+ * @param {string[]} mora
+ * @param {number} downstepPosition
+ * @returns {SVGSVGElement}
+ */
+export function createPronunciationGraphJJ(mora, downstepPosition) {
+ const patt = pitchValueToPattJJ(mora.length, downstepPosition);
+
+ const positions = Math.max(mora.length, patt.length);
+ const stepWidth = 35;
+ const marginLr = 16;
+ const svgWidth = Math.max(0, ((positions - 1) * stepWidth) + (marginLr * 2));
+
+ const svgns = 'http://www.w3.org/2000/svg';
+ const svg = document.createElementNS(svgns, 'svg');
+ svg.setAttribute('xmlns', svgns);
+ svg.setAttribute('width', `${(svgWidth * (3 / 5))}px`);
+ svg.setAttribute('height', '45px');
+ svg.setAttribute('viewBox', `0 0 ${svgWidth} 75`);
+
+
+ if (mora.length <= 0) { return svg; }
+
+ for (let i = 0; i < mora.length; i++) {
+ const xCenter = marginLr + (i * stepWidth);
+ textJJ(xCenter - 11, mora[i], svgns, svg);
+ }
+
+
+ let pathType = '';
+
+ const circles = [];
+ const paths = [];
+
+ let prevCenter = [-1, -1];
+ for (let i = 0; i < patt.length; i++) {
+ const xCenter = marginLr + (i * stepWidth);
+ const accent = patt[i];
+ let yCenter = 0;
+ if (accent === 'H') {
+ yCenter = 5;
+ } else if (accent === 'L') {
+ yCenter = 30;
+ }
+ circles.push(circleJJ(xCenter, yCenter, i >= mora.length, svgns));
+
+
+ if (i > 0) {
+ if (prevCenter[1] === yCenter) {
+ pathType = 's';
+ } else if (prevCenter[1] < yCenter) {
+ pathType = 'd';
+ } else if (prevCenter[1] > yCenter) {
+ pathType = 'u';
+ }
+ paths.push(pathJJ(prevCenter[0], prevCenter[1], pathType, stepWidth, svgns));
+ }
+ prevCenter = [xCenter, yCenter];
+ }
+
+ for (const path of paths) {
+ svg.appendChild(path);
+ }
+
+ for (const circle of circles) {
+ svg.appendChild(circle);
+ }
+
+ return svg;
+}
+
+/**
+ * Get H&L pattern
+ * @param {number} numberOfMora
+ * @param {number} pitchValue
+ * @returns {string}
+ */
+function pitchValueToPattJJ(numberOfMora, pitchValue) {
+ if (numberOfMora >= 1) {
+ if (pitchValue === 0) {
+ // Heiban
+ return `L${'H'.repeat(numberOfMora)}`;
+ } else if (pitchValue === 1) {
+ // Atamadaka
+ return `H${'L'.repeat(numberOfMora)}`;
+ } else if (pitchValue >= 2) {
+ const stepdown = pitchValue - 2;
+ return `LH${'H'.repeat(stepdown)}${'L'.repeat(numberOfMora - pitchValue + 1)}`;
+ }
+ }
+ return '';
+}
+
+/**
+ * @param {number} x
+ * @param {number} y
+ * @param {boolean} o
+ * @param {string} svgns
+ * @returns {Element}
+ */
+function circleJJ(x, y, o, svgns) {
+ if (o) {
+ const node = document.createElementNS(svgns, 'circle');
+
+ node.setAttribute('r', '4');
+ node.setAttribute('cx', `${(x + 4)}`);
+ node.setAttribute('cy', `${y}`);
+ node.setAttribute('stroke', 'currentColor');
+ node.setAttribute('stroke-width', '2');
+ node.setAttribute('fill', 'none');
+
+ return node;
+ } else {
+ const node = document.createElementNS(svgns, 'circle');
+
+ node.setAttribute('r', '5');
+ node.setAttribute('cx', `${x}`);
+ node.setAttribute('cy', `${y}`);
+ node.setAttribute('style', 'opacity:1;fill:currentColor;');
+
+ return node;
+ }
+}
+
+/**
+ * @param {number} x
+ * @param {string} mora
+ * @param {string} svgns
+ * @param {SVGSVGElement} svg
+ * @returns {void}
+ */
+function textJJ(x, mora, svgns, svg) {
+ if (mora.length === 1) {
+ const path = document.createElementNS(svgns, 'text');
+ path.setAttribute('x', `${x}`);
+ path.setAttribute('y', '67.5');
+ path.setAttribute('style', 'font-size:20px;font-family:sans-serif;fill:currentColor;');
+ path.textContent = mora;
+ svg.appendChild(path);
+ } else {
+ const path1 = document.createElementNS(svgns, 'text');
+ path1.setAttribute('x', `${x - 5}`);
+ path1.setAttribute('y', '67.5');
+ path1.setAttribute('style', 'font-size:20px;font-family:sans-serif;fill:currentColor;');
+ path1.textContent = mora[0];
+ svg.appendChild(path1);
+
+
+ const path2 = document.createElementNS(svgns, 'text');
+ path2.setAttribute('x', `${x + 12}`);
+ path2.setAttribute('y', '67.5');
+ path2.setAttribute('style', 'font-size:14px;font-family:sans-serif;fill:currentColor;');
+ path2.textContent = mora[1];
+ svg.appendChild(path2);
+ }
+}
+
+/**
+ * @param {number} x
+ * @param {number} y
+ * @param {string} type
+ * @param {number} stepWidth
+ * @param {string} svgns
+ * @returns {Element}
+ */
+function pathJJ(x, y, type, stepWidth, svgns) {
+ let delta = '';
+ switch (type) {
+ case 's':
+ delta = stepWidth + ',0';
+ break;
+ case 'u':
+ delta = stepWidth + ',-25';
+ break;
+ case 'd':
+ delta = stepWidth + ',25';
+ break;
+ }
+
+ const path = document.createElementNS(svgns, 'path');
+ path.setAttribute('d', `m ${x},${y} ${delta}`);
+ path.setAttribute('style', 'fill:none;stroke:currentColor;stroke-width:1.5;');
+
+ return path;
+}
diff --git a/ext/js/templates/anki-template-renderer.js b/ext/js/templates/anki-template-renderer.js
index 4bb56a4b..ae3e7a36 100644
--- a/ext/js/templates/anki-template-renderer.js
+++ b/ext/js/templates/anki-template-renderer.js
@@ -19,7 +19,7 @@
import {Handlebars} from '../../lib/handlebars.js';
import {createAnkiNoteData} from '../data/anki-note-data-creator.js';
import {getPronunciationsOfType, isNonNounVerbOrAdjective} from '../dictionary/dictionary-data-util.js';
-import {createPronunciationDownstepPosition, createPronunciationGraph, createPronunciationText} from '../display/pronunciation-generator.js';
+import {createPronunciationDownstepPosition, createPronunciationGraph, createPronunciationGraphJJ, createPronunciationText} from '../display/pronunciation-generator.js';
import {StructuredContentGenerator} from '../display/structured-content-generator.js';
import {CssStyleApplier} from '../dom/css-style-applier.js';
import {convertHiraganaToKatakana, convertKatakanaToHiragana, distributeFurigana, getKanaMorae, getPitchCategory, isMoraPitchHigh} from '../language/ja/japanese.js';
@@ -741,6 +741,8 @@ export class AnkiTemplateRenderer {
}
case 'graph':
return this._getPronunciationHtml(createPronunciationGraph(morae, downstepPosition));
+ case 'graph-jj':
+ return this._getPronunciationHtml(createPronunciationGraphJJ(morae, downstepPosition));
case 'position':
return this._getPronunciationHtml(createPronunciationDownstepPosition(downstepPosition));
default:
diff --git a/ext/settings.html b/ext/settings.html
index 441e26df..773d7cb2 100644
--- a/ext/settings.html
+++ b/ext/settings.html
@@ -2979,6 +2979,10 @@
<td>List of pitch accent graphs for the term.</td>
</tr>
<tr>
+ <td><code class="anki-field-marker">{pitch-accent-graphs-jj}</code></td>
+ <td>List of pitch accent graphs for the term (styled after Jidoujisho).</td>
+ </tr>
+ <tr>
<td><code class="anki-field-marker">{pitch-accent-positions}</code></td>
<td>List of accent downstep positions for the term as a number.</td>
</tr>
diff --git a/test/options-util.test.js b/test/options-util.test.js
index be881d15..ebee0765 100644
--- a/test/options-util.test.js
+++ b/test/options-util.test.js
@@ -599,7 +599,7 @@ function createOptionsUpdatedTestData1() {
}
],
profileCurrent: 0,
- version: 28,
+ version: 29,
global: {
database: {
prefixWildcardsSupported: false