Display pronunciation separation (#1833)
* Create PronunciationGenerator * Update DisplayGenerator * Update templates * Move pronunciation styles to a separate file * Simplify pitch-accents-preview.html
This commit is contained in:
parent
41fc76d6fd
commit
4a2b824371
@ -111,6 +111,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"ext/js/core.js",
|
"ext/js/core.js",
|
||||||
"ext/js/data/anki-note-data-creator.js",
|
"ext/js/data/anki-note-data-creator.js",
|
||||||
|
"ext/js/display/pronunciation-generator.js",
|
||||||
"ext/js/display/structured-content-generator.js",
|
"ext/js/display/structured-content-generator.js",
|
||||||
"ext/js/dom/css-style-applier.js",
|
"ext/js/dom/css-style-applier.js",
|
||||||
"ext/js/language/dictionary-data-util.js",
|
"ext/js/language/dictionary-data-util.js",
|
||||||
@ -126,6 +127,7 @@
|
|||||||
"excludedFiles": [
|
"excludedFiles": [
|
||||||
"ext/js/core.js",
|
"ext/js/core.js",
|
||||||
"ext/js/data/anki-note-data-creator.js",
|
"ext/js/data/anki-note-data-creator.js",
|
||||||
|
"ext/js/display/pronunciation-generator.js",
|
||||||
"ext/js/display/structured-content-generator.js",
|
"ext/js/display/structured-content-generator.js",
|
||||||
"ext/js/dom/css-style-applier.js",
|
"ext/js/dom/css-style-applier.js",
|
||||||
"ext/js/language/dictionary-data-util.js",
|
"ext/js/language/dictionary-data-util.js",
|
||||||
@ -158,6 +160,7 @@
|
|||||||
"ext/js/core.js",
|
"ext/js/core.js",
|
||||||
"ext/js/yomichan.js",
|
"ext/js/yomichan.js",
|
||||||
"ext/js/data/anki-note-data-creator.js",
|
"ext/js/data/anki-note-data-creator.js",
|
||||||
|
"ext/js/display/pronunciation-generator.js",
|
||||||
"ext/js/display/structured-content-generator.js",
|
"ext/js/display/structured-content-generator.js",
|
||||||
"ext/js/dom/css-style-applier.js",
|
"ext/js/dom/css-style-applier.js",
|
||||||
"ext/js/language/dictionary-data-util.js",
|
"ext/js/language/dictionary-data-util.js",
|
||||||
|
119
ext/css/display-pronunciation.css
Normal file
119
ext/css/display-pronunciation.css
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Yomichan Authors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the entrys of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--pitch-accent-annotation-color: #000000;
|
||||||
|
}
|
||||||
|
:root[data-theme=dark] {
|
||||||
|
--pitch-accent-annotation-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pitch-accent-character {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.pitch-accent-character::before {
|
||||||
|
border-color: var(--pitch-accent-annotation-color);
|
||||||
|
}
|
||||||
|
.pitch-accent-character[data-pitch='high']::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.1em;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top-width: 0.1em;
|
||||||
|
border-top-style: solid;
|
||||||
|
}
|
||||||
|
.pitch-accent-character[data-pitch='high'][data-pitch-next='low']::before {
|
||||||
|
right: -0.1em;
|
||||||
|
height: 0.4em;
|
||||||
|
border-right-width: 0.1em;
|
||||||
|
border-right-style: solid;
|
||||||
|
}
|
||||||
|
.pitch-accent-character[data-pitch='high'][data-pitch-next='low'] {
|
||||||
|
padding-right: 0.1em;
|
||||||
|
margin-right: 0.1em;
|
||||||
|
}
|
||||||
|
.pitch-accent-character-devoice-indicator {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
width: 1.125em;
|
||||||
|
height: 1.125em;
|
||||||
|
border: calc(1.5em / var(--font-size-no-units)) dotted var(--danger-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 1;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
.pitch-accent-character-nasal-indicator {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
right: -0.125em;
|
||||||
|
top: 0.125em;
|
||||||
|
width: 0.375em;
|
||||||
|
height: 0.375em;
|
||||||
|
border: calc(1.5em / var(--font-size-no-units)) solid var(--danger-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pitch-accent-position::before {
|
||||||
|
content: ' [';
|
||||||
|
}
|
||||||
|
.pitch-accent-position::after {
|
||||||
|
content: ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
.pitch-accent-graph {
|
||||||
|
display: block;
|
||||||
|
height: 1.5em;
|
||||||
|
transform: translateY(-0.875em);
|
||||||
|
}
|
||||||
|
.pitch-accent-graph-line,
|
||||||
|
.pitch-accent-graph-line-tail {
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--pitch-accent-annotation-color);
|
||||||
|
stroke-width: 5;
|
||||||
|
}
|
||||||
|
.pitch-accent-graph-line-tail {
|
||||||
|
stroke-dasharray: 5 5;
|
||||||
|
}
|
||||||
|
.pitch-accent-graph-dot {
|
||||||
|
fill: var(--pitch-accent-annotation-color);
|
||||||
|
stroke: var(--pitch-accent-annotation-color);
|
||||||
|
stroke-width: 5;
|
||||||
|
}
|
||||||
|
.pitch-accent-graph-dot-downstep1 {
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--pitch-accent-annotation-color);
|
||||||
|
stroke-width: 5;
|
||||||
|
}
|
||||||
|
.pitch-accent-graph-dot-downstep2 {
|
||||||
|
fill: var(--pitch-accent-annotation-color);
|
||||||
|
}
|
||||||
|
.pitch-accent-graph-triangle {
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--pitch-accent-annotation-color);
|
||||||
|
stroke-width: 5;
|
||||||
|
}
|
@ -132,8 +132,6 @@
|
|||||||
--medium-border-color: #dddddd;
|
--medium-border-color: #dddddd;
|
||||||
--dark-border-color: #777777;
|
--dark-border-color: #777777;
|
||||||
|
|
||||||
--pitch-accent-annotation-color: #000000;
|
|
||||||
|
|
||||||
--tag-text-color: #ffffff;
|
--tag-text-color: #ffffff;
|
||||||
--tag-border-color: transparent;
|
--tag-border-color: transparent;
|
||||||
--tag-default-background-color: #8a8a91;
|
--tag-default-background-color: #8a8a91;
|
||||||
@ -202,8 +200,6 @@
|
|||||||
--medium-border-color: #3f3f3f;
|
--medium-border-color: #3f3f3f;
|
||||||
--dark-border-color: #888888;
|
--dark-border-color: #888888;
|
||||||
|
|
||||||
--pitch-accent-annotation-color: #ffffff;
|
|
||||||
|
|
||||||
--tag-text-color: #f1f1f1;
|
--tag-text-color: #f1f1f1;
|
||||||
--tag-border-color: transparent;
|
--tag-border-color: transparent;
|
||||||
--tag-default-background-color: #69696e;
|
--tag-default-background-color: #69696e;
|
||||||
@ -1482,67 +1478,6 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
|
|||||||
.pitch-accent-tag-list:not([data-count='0']) {
|
.pitch-accent-tag-list:not([data-count='0']) {
|
||||||
margin-right: 0.375em;
|
margin-right: 0.375em;
|
||||||
}
|
}
|
||||||
.pitch-accent-character {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.pitch-accent-character::before {
|
|
||||||
border-color: var(--pitch-accent-annotation-color);
|
|
||||||
}
|
|
||||||
.pitch-accent-character[data-pitch='high']::before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0.1em;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 0;
|
|
||||||
border-top-width: 0.1em;
|
|
||||||
border-top-style: solid;
|
|
||||||
}
|
|
||||||
.pitch-accent-character[data-pitch='high'][data-pitch-next='low']::before {
|
|
||||||
right: -0.1em;
|
|
||||||
height: 0.4em;
|
|
||||||
border-right-width: 0.1em;
|
|
||||||
border-right-style: solid;
|
|
||||||
}
|
|
||||||
.pitch-accent-character[data-pitch='high'][data-pitch-next='low'] {
|
|
||||||
padding-right: 0.1em;
|
|
||||||
margin-right: 0.1em;
|
|
||||||
}
|
|
||||||
.pitch-accent-character-devoice-indicator {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
width: 1.125em;
|
|
||||||
height: 1.125em;
|
|
||||||
border: calc(1.5em / var(--font-size-no-units)) dotted var(--danger-color);
|
|
||||||
border-radius: 50%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 1;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
.pitch-accent-character-nasal-indicator {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
right: -0.125em;
|
|
||||||
top: 0.125em;
|
|
||||||
width: 0.375em;
|
|
||||||
height: 0.375em;
|
|
||||||
border: calc(1.5em / var(--font-size-no-units)) solid var(--danger-color);
|
|
||||||
border-radius: 50%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
.pitch-accent-position::before {
|
|
||||||
content: ' [';
|
|
||||||
}
|
|
||||||
.pitch-accent-position::after {
|
|
||||||
content: ']';
|
|
||||||
}
|
|
||||||
.pitch-accent-details {
|
.pitch-accent-details {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 0;
|
height: 0;
|
||||||
@ -1551,41 +1486,6 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Pitch accent graph styles */
|
|
||||||
.pitch-accent-graph {
|
|
||||||
display: block;
|
|
||||||
height: 1.5em;
|
|
||||||
transform: translateY(-0.875em);
|
|
||||||
}
|
|
||||||
.pitch-accent-graph-line,
|
|
||||||
.pitch-accent-graph-line-tail {
|
|
||||||
fill: none;
|
|
||||||
stroke: var(--pitch-accent-annotation-color);
|
|
||||||
stroke-width: 5;
|
|
||||||
}
|
|
||||||
.pitch-accent-graph-line-tail {
|
|
||||||
stroke-dasharray: 5 5;
|
|
||||||
}
|
|
||||||
#pitch-accent-graph-dot {
|
|
||||||
fill: var(--pitch-accent-annotation-color);
|
|
||||||
stroke: var(--pitch-accent-annotation-color);
|
|
||||||
stroke-width: 5;
|
|
||||||
}
|
|
||||||
#pitch-accent-graph-dot-downstep {
|
|
||||||
fill: none;
|
|
||||||
stroke: var(--pitch-accent-annotation-color);
|
|
||||||
stroke-width: 5;
|
|
||||||
}
|
|
||||||
#pitch-accent-graph-dot-downstep>circle:last-of-type {
|
|
||||||
fill: var(--pitch-accent-annotation-color);
|
|
||||||
}
|
|
||||||
#pitch-accent-graph-triangle {
|
|
||||||
fill: none;
|
|
||||||
stroke: var(--pitch-accent-annotation-color);
|
|
||||||
stroke-width: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Kanji */
|
/* Kanji */
|
||||||
.kanji-glyph-container {
|
.kanji-glyph-container {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -82,16 +82,9 @@
|
|||||||
</span></span></template>
|
</span></span></template>
|
||||||
|
|
||||||
<!-- Pitch accent templates -->
|
<!-- Pitch accent templates -->
|
||||||
<template id="pitch-accent-static-template"><svg xmlns="http://www.w3.org/2000/svg" style="display: none;" focusable="false">
|
|
||||||
<defs>
|
|
||||||
<g id="pitch-accent-graph-dot"><circle cx="0" cy="0" r="15" /></g>
|
|
||||||
<g id="pitch-accent-graph-dot-downstep"><circle cx="0" cy="0" r="15" /><circle cx="0" cy="0" r="5" /></g>
|
|
||||||
<g id="pitch-accent-graph-triangle"><path d="M0 13 L15 -13 L-15 -13 Z" /></g>
|
|
||||||
</defs>
|
|
||||||
</svg></template>
|
|
||||||
<template id="pitch-accent-group-template"><li class="pitch-accent-group"><span class="pitch-accent-group-tag-list tag-list"></span><ul class="pitch-accent-list"></ul></li></template>
|
<template id="pitch-accent-group-template"><li class="pitch-accent-group"><span class="pitch-accent-group-tag-list tag-list"></span><ul class="pitch-accent-list"></ul></li></template>
|
||||||
<template id="pitch-accent-disambiguation-template"><span class="pitch-accent-disambiguation"></span></template>
|
<template id="pitch-accent-disambiguation-template"><span class="pitch-accent-disambiguation"></span></template>
|
||||||
<template id="pitch-accent-template"><li class="pitch-accent"><span class="pitch-accent-tag-list tag-list"></span><span class="pitch-accent-disambiguation-list"></span><span class="pitch-accent-characters"></span><span class="pitch-accent-position"></span><span class="pitch-accent-details"><svg class="pitch-accent-graph" xmlns="http://www.w3.org/2000/svg" focusable="false"><path class="pitch-accent-graph-line" /><path class="pitch-accent-graph-line-tail" /></svg></span></li></template>
|
<template id="pitch-accent-template"><li class="pitch-accent"><span class="pitch-accent-tag-list tag-list"></span><span class="pitch-accent-disambiguation-list"></span><span class="pitch-accent-characters"></span><span class="pitch-accent-position"></span><span class="pitch-accent-details"></span></li></template>
|
||||||
<template id="pitch-accent-character-template"><span class="pitch-accent-character"><span class="pitch-accent-character-inner"></span></span></template>
|
<template id="pitch-accent-character-template"><span class="pitch-accent-character"><span class="pitch-accent-character-inner"></span></span></template>
|
||||||
|
|
||||||
<!-- Kanji entry templates -->
|
<!-- Kanji entry templates -->
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
/* global
|
/* global
|
||||||
* DictionaryDataUtil
|
* DictionaryDataUtil
|
||||||
* HtmlTemplateCollection
|
* HtmlTemplateCollection
|
||||||
|
* PronunciationGenerator
|
||||||
* StructuredContentGenerator
|
* StructuredContentGenerator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ class DisplayGenerator {
|
|||||||
this._hotkeyHelpController = hotkeyHelpController;
|
this._hotkeyHelpController = hotkeyHelpController;
|
||||||
this._templates = null;
|
this._templates = null;
|
||||||
this._structuredContentGenerator = new StructuredContentGenerator(this._mediaLoader, document);
|
this._structuredContentGenerator = new StructuredContentGenerator(this._mediaLoader, document);
|
||||||
this._termPitchAccentStaticTemplateIsSetup = false;
|
this._pronunciationGenerator = new PronunciationGenerator(japaneseUtil);
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
@ -45,13 +46,6 @@ class DisplayGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preparePitchAccents() {
|
|
||||||
if (this._termPitchAccentStaticTemplateIsSetup) { return; }
|
|
||||||
this._termPitchAccentStaticTemplateIsSetup = true;
|
|
||||||
const t = this._templates.instantiate('pitch-accent-static');
|
|
||||||
document.head.appendChild(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
createTermEntry(dictionaryEntry) {
|
createTermEntry(dictionaryEntry) {
|
||||||
const node = this._templates.instantiate('term-entry');
|
const node = this._templates.instantiate('term-entry');
|
||||||
|
|
||||||
@ -439,8 +433,6 @@ class DisplayGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_createPitches(details) {
|
_createPitches(details) {
|
||||||
this.preparePitchAccents();
|
|
||||||
|
|
||||||
const {dictionary, pitches} = details;
|
const {dictionary, pitches} = details;
|
||||||
|
|
||||||
const node = this._templates.instantiate('pitch-accent-group');
|
const node = this._templates.instantiate('pitch-accent-group');
|
||||||
@ -471,9 +463,6 @@ class DisplayGenerator {
|
|||||||
const {reading, position, nasalPositions, devoicePositions, tags, exclusiveTerms, exclusiveReadings} = details;
|
const {reading, position, nasalPositions, devoicePositions, tags, exclusiveTerms, exclusiveReadings} = details;
|
||||||
const morae = jp.getKanaMorae(reading);
|
const morae = jp.getKanaMorae(reading);
|
||||||
|
|
||||||
const nasalPositionsSet = nasalPositions.length > 0 ? new Set(nasalPositions) : null;
|
|
||||||
const devoicePositionsSet = devoicePositions.length > 0 ? new Set(devoicePositions) : null;
|
|
||||||
|
|
||||||
const node = this._templates.instantiate('pitch-accent');
|
const node = this._templates.instantiate('pitch-accent');
|
||||||
|
|
||||||
node.dataset.pitchAccentPosition = `${position}`;
|
node.dataset.pitchAccentPosition = `${position}`;
|
||||||
@ -491,46 +480,10 @@ class DisplayGenerator {
|
|||||||
this._createPitchAccentDisambiguations(n, exclusiveTerms, exclusiveReadings);
|
this._createPitchAccentDisambiguations(n, exclusiveTerms, exclusiveReadings);
|
||||||
|
|
||||||
n = node.querySelector('.pitch-accent-characters');
|
n = node.querySelector('.pitch-accent-characters');
|
||||||
for (let i = 0, ii = morae.length; i < ii; ++i) {
|
n.lang = 'ja';
|
||||||
const i1 = i + 1;
|
n.appendChild(this._pronunciationGenerator.createPitchAccentHtml(morae, position, nasalPositions, devoicePositions));
|
||||||
const mora = morae[i];
|
|
||||||
const highPitch = jp.isMoraPitchHigh(i, position);
|
|
||||||
const highPitchNext = jp.isMoraPitchHigh(i1, position);
|
|
||||||
const nasal = nasalPositionsSet !== null && nasalPositionsSet.has(i1);
|
|
||||||
const devoice = devoicePositionsSet !== null && devoicePositionsSet.has(i1);
|
|
||||||
|
|
||||||
const n1 = document.createElement('span');
|
node.querySelector('.pitch-accent-details').appendChild(this._pronunciationGenerator.createPitchGraph(morae, position));
|
||||||
n1.className = 'pitch-accent-character';
|
|
||||||
|
|
||||||
const n2 = document.createElement('span');
|
|
||||||
n2.className = 'pitch-accent-character-inner';
|
|
||||||
|
|
||||||
n1.appendChild(n2);
|
|
||||||
|
|
||||||
n1.dataset.position = `${i}`;
|
|
||||||
n1.dataset.pitch = highPitch ? 'high' : 'low';
|
|
||||||
n1.dataset.pitchNext = highPitchNext ? 'high' : 'low';
|
|
||||||
this._setTextContent(n2, mora, 'ja');
|
|
||||||
|
|
||||||
if (devoice) {
|
|
||||||
n1.dataset.devoice = 'true';
|
|
||||||
const n3 = document.createElement('span');
|
|
||||||
n3.className = 'pitch-accent-character-devoice-indicator';
|
|
||||||
n1.appendChild(n3);
|
|
||||||
}
|
|
||||||
if (nasal) {
|
|
||||||
n1.dataset.nasal = 'true';
|
|
||||||
const n3 = document.createElement('span');
|
|
||||||
n3.className = 'pitch-accent-character-nasal-indicator';
|
|
||||||
n1.appendChild(n3);
|
|
||||||
}
|
|
||||||
|
|
||||||
n.appendChild(n1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (morae.length > 0) {
|
|
||||||
this._populatePitchGraph(node.querySelector('.pitch-accent-graph'), position, morae);
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -556,47 +509,6 @@ class DisplayGenerator {
|
|||||||
container.dataset.readingCount = `${exclusiveReadings.length}`;
|
container.dataset.readingCount = `${exclusiveReadings.length}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_populatePitchGraph(svg, position, morae) {
|
|
||||||
const jp = this._japaneseUtil;
|
|
||||||
const svgns = svg.getAttribute('xmlns');
|
|
||||||
const ii = morae.length;
|
|
||||||
svg.setAttribute('viewBox', `0 0 ${50 * (ii + 1)} 100`);
|
|
||||||
|
|
||||||
const pathPoints = [];
|
|
||||||
for (let i = 0; i < ii; ++i) {
|
|
||||||
const highPitch = jp.isMoraPitchHigh(i, position);
|
|
||||||
const highPitchNext = jp.isMoraPitchHigh(i + 1, position);
|
|
||||||
const graphic = (highPitch && !highPitchNext ? '#pitch-accent-graph-dot-downstep' : '#pitch-accent-graph-dot');
|
|
||||||
const x = `${i * 50 + 25}`;
|
|
||||||
const y = highPitch ? '25' : '75';
|
|
||||||
const use = document.createElementNS(svgns, 'use');
|
|
||||||
use.setAttribute('href', graphic);
|
|
||||||
use.setAttribute('x', x);
|
|
||||||
use.setAttribute('y', y);
|
|
||||||
svg.appendChild(use);
|
|
||||||
pathPoints.push(`${x} ${y}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = svg.querySelector('.pitch-accent-graph-line');
|
|
||||||
path.setAttribute('d', `M${pathPoints.join(' L')}`);
|
|
||||||
|
|
||||||
pathPoints.splice(0, ii - 1);
|
|
||||||
{
|
|
||||||
const highPitch = jp.isMoraPitchHigh(ii, position);
|
|
||||||
const x = `${ii * 50 + 25}`;
|
|
||||||
const y = highPitch ? '25' : '75';
|
|
||||||
const use = document.createElementNS(svgns, 'use');
|
|
||||||
use.setAttribute('href', '#pitch-accent-graph-triangle');
|
|
||||||
use.setAttribute('x', x);
|
|
||||||
use.setAttribute('y', y);
|
|
||||||
svg.appendChild(use);
|
|
||||||
pathPoints.push(`${x} ${y}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
path = svg.querySelector('.pitch-accent-graph-line-tail');
|
|
||||||
path.setAttribute('d', `M${pathPoints.join(' L')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
_createFrequencyGroup(details, kanji) {
|
_createFrequencyGroup(details, kanji) {
|
||||||
const {dictionary, frequencies} = details;
|
const {dictionary, frequencies} = details;
|
||||||
|
|
||||||
|
145
ext/js/display/pronunciation-generator.js
Normal file
145
ext/js/display/pronunciation-generator.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Yomichan Authors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PronunciationGenerator {
|
||||||
|
constructor(japaneseUtil) {
|
||||||
|
this._japaneseUtil = japaneseUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
createPitchAccentHtml(morae, downstepPosition, nasalPositions, devoicePositions) {
|
||||||
|
const jp = this._japaneseUtil;
|
||||||
|
const nasalPositionsSet = nasalPositions.length > 0 ? new Set(nasalPositions) : null;
|
||||||
|
const devoicePositionsSet = devoicePositions.length > 0 ? new Set(devoicePositions) : null;
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
for (let i = 0, ii = morae.length; i < ii; ++i) {
|
||||||
|
const i1 = i + 1;
|
||||||
|
const mora = morae[i];
|
||||||
|
const highPitch = jp.isMoraPitchHigh(i, downstepPosition);
|
||||||
|
const highPitchNext = jp.isMoraPitchHigh(i1, downstepPosition);
|
||||||
|
const nasal = nasalPositionsSet !== null && nasalPositionsSet.has(i1);
|
||||||
|
const devoice = devoicePositionsSet !== null && devoicePositionsSet.has(i1);
|
||||||
|
|
||||||
|
const n1 = document.createElement('span');
|
||||||
|
n1.className = 'pitch-accent-character';
|
||||||
|
|
||||||
|
const n2 = document.createElement('span');
|
||||||
|
n2.className = 'pitch-accent-character-inner';
|
||||||
|
|
||||||
|
n1.appendChild(n2);
|
||||||
|
|
||||||
|
n1.dataset.position = `${i}`;
|
||||||
|
n1.dataset.pitch = highPitch ? 'high' : 'low';
|
||||||
|
n1.dataset.pitchNext = highPitchNext ? 'high' : 'low';
|
||||||
|
n2.textContent = mora;
|
||||||
|
|
||||||
|
if (devoice) {
|
||||||
|
n1.dataset.devoice = 'true';
|
||||||
|
const n3 = document.createElement('span');
|
||||||
|
n3.className = 'pitch-accent-character-devoice-indicator';
|
||||||
|
n1.appendChild(n3);
|
||||||
|
}
|
||||||
|
if (nasal) {
|
||||||
|
n1.dataset.nasal = 'true';
|
||||||
|
const n3 = document.createElement('span');
|
||||||
|
n3.className = 'pitch-accent-character-nasal-indicator';
|
||||||
|
n1.appendChild(n3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.appendChild(n1);
|
||||||
|
}
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
createPitchGraph(morae, downstepPosition) {
|
||||||
|
const jp = this._japaneseUtil;
|
||||||
|
const ii = morae.length;
|
||||||
|
|
||||||
|
const svgns = 'http://www.w3.org/2000/svg';
|
||||||
|
const svg = document.createElementNS(svgns, 'svg');
|
||||||
|
svg.setAttribute('xmlns', svgns);
|
||||||
|
svg.setAttribute('class', 'pitch-accent-graph');
|
||||||
|
svg.setAttribute('focusable', 'false');
|
||||||
|
svg.setAttribute('viewBox', `0 0 ${50 * (ii + 1)} 100`);
|
||||||
|
|
||||||
|
if (ii <= 0) { return svg; }
|
||||||
|
|
||||||
|
const path1 = document.createElementNS(svgns, 'path');
|
||||||
|
svg.appendChild(path1);
|
||||||
|
|
||||||
|
const path2 = document.createElementNS(svgns, 'path');
|
||||||
|
svg.appendChild(path2);
|
||||||
|
|
||||||
|
const pathPoints = [];
|
||||||
|
for (let i = 0; i < ii; ++i) {
|
||||||
|
const highPitch = jp.isMoraPitchHigh(i, downstepPosition);
|
||||||
|
const highPitchNext = jp.isMoraPitchHigh(i + 1, downstepPosition);
|
||||||
|
const x = i * 50 + 25;
|
||||||
|
const y = highPitch ? 25 : 75;
|
||||||
|
if (highPitch && !highPitchNext) {
|
||||||
|
this._addGraphDotDownstep(svg, svgns, x, y);
|
||||||
|
} else {
|
||||||
|
this._addGraphDot(svg, svgns, x, y);
|
||||||
|
}
|
||||||
|
pathPoints.push(`${x} ${y}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
path1.setAttribute('class', 'pitch-accent-graph-line');
|
||||||
|
path1.setAttribute('d', `M${pathPoints.join(' L')}`);
|
||||||
|
|
||||||
|
pathPoints.splice(0, ii - 1);
|
||||||
|
{
|
||||||
|
const highPitch = jp.isMoraPitchHigh(ii, downstepPosition);
|
||||||
|
const x = ii * 50 + 25;
|
||||||
|
const y = highPitch ? 25 : 75;
|
||||||
|
this._addGraphTriangle(svg, svgns, x, y);
|
||||||
|
pathPoints.push(`${x} ${y}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
path2.setAttribute('class', 'pitch-accent-graph-line-tail');
|
||||||
|
path2.setAttribute('d', `M${pathPoints.join(' L')}`);
|
||||||
|
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
|
_addGraphDot(container, svgns, x, y) {
|
||||||
|
container.appendChild(this._createGraphCircle(svgns, 'pitch-accent-graph-dot', x, y, '15'));
|
||||||
|
}
|
||||||
|
|
||||||
|
_addGraphDotDownstep(container, svgns, x, y) {
|
||||||
|
container.appendChild(this._createGraphCircle(svgns, 'pitch-accent-graph-dot-downstep1', x, y, '15'));
|
||||||
|
container.appendChild(this._createGraphCircle(svgns, 'pitch-accent-graph-dot-downstep2', x, y, '5'));
|
||||||
|
}
|
||||||
|
|
||||||
|
_addGraphTriangle(container, svgns, x, y) {
|
||||||
|
const node = document.createElementNS(svgns, 'path');
|
||||||
|
node.setAttribute('class', 'pitch-accent-graph-triangle');
|
||||||
|
node.setAttribute('d', 'M0 13 L15 -13 L-15 -13 Z');
|
||||||
|
node.setAttribute('transform', `translate(${x},${y})`);
|
||||||
|
container.appendChild(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createGraphCircle(svgns, className, x, y, radius) {
|
||||||
|
const node = document.createElementNS(svgns, 'circle');
|
||||||
|
node.setAttribute('class', className);
|
||||||
|
node.setAttribute('cx', `${x}`);
|
||||||
|
node.setAttribute('cy', `${y}`);
|
||||||
|
node.setAttribute('r', radius);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019-2021 Yomichan Authors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global
|
|
||||||
* DisplayGenerator
|
|
||||||
*/
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
await yomichan.prepare();
|
|
||||||
|
|
||||||
const displayGenerator = new DisplayGenerator({
|
|
||||||
japaneseUtil: null,
|
|
||||||
mediaLoader: null
|
|
||||||
});
|
|
||||||
await displayGenerator.prepare();
|
|
||||||
displayGenerator.preparePitchAccents();
|
|
||||||
} catch (e) {
|
|
||||||
log.error(e);
|
|
||||||
}
|
|
||||||
})();
|
|
@ -11,7 +11,7 @@
|
|||||||
<link rel="icon" type="image/png" href="/images/icon48.png" sizes="48x48">
|
<link rel="icon" type="image/png" href="/images/icon48.png" sizes="48x48">
|
||||||
<link rel="icon" type="image/png" href="/images/icon64.png" sizes="64x64">
|
<link rel="icon" type="image/png" href="/images/icon64.png" sizes="64x64">
|
||||||
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
|
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/display.css">
|
<link rel="stylesheet" type="text/css" href="/css/display-pronunciation.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/pitch-accents-preview.css">
|
<link rel="stylesheet" type="text/css" href="/css/pitch-accents-preview.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -35,12 +35,13 @@
|
|||||||
<strong>Graph</strong> -
|
<strong>Graph</strong> -
|
||||||
<span class="format-preview">
|
<span class="format-preview">
|
||||||
<span class="pitch-accent-details">
|
<span class="pitch-accent-details">
|
||||||
<svg class="pitch-accent-graph" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 100" focusable="false">
|
<svg xmlns="http://www.w3.org/2000/svg" class="pitch-accent-graph" focusable="false" viewBox="0 0 150 100">
|
||||||
<path class="pitch-accent-graph-line" d="M25 25 L75 75"></path>
|
<path class="pitch-accent-graph-line" d="M25 25 L75 75"></path>
|
||||||
<path class="pitch-accent-graph-line-tail" d="M75 75 L125 75"></path>
|
<path class="pitch-accent-graph-line-tail" d="M75 75 L125 75"></path>
|
||||||
<use href="#pitch-accent-graph-dot-downstep" x="25" y="25"></use>
|
<circle class="pitch-accent-graph-dot-downstep1" cx="25" cy="25" r="15"></circle>
|
||||||
<use href="#pitch-accent-graph-dot" x="75" y="75"></use>
|
<circle class="pitch-accent-graph-dot-downstep2" cx="25" cy="25" r="5"></circle>
|
||||||
<use href="#pitch-accent-graph-triangle" x="125" y="75"></use>
|
<circle class="pitch-accent-graph-dot" cx="75" cy="75" r="15"></circle>
|
||||||
|
<path class="pitch-accent-graph-triangle" d="M0 13 L15 -13 L-15 -13 Z" transform="translate(125,75)"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -49,18 +50,5 @@
|
|||||||
|
|
||||||
</div></div>
|
</div></div>
|
||||||
|
|
||||||
<!-- Scripts -->
|
|
||||||
<script src="/js/core.js"></script>
|
|
||||||
|
|
||||||
<script src="/js/yomichan.js"></script>
|
|
||||||
|
|
||||||
<script src="/js/comm/api.js"></script>
|
|
||||||
<script src="/js/comm/cross-frame-api.js"></script>
|
|
||||||
<script src="/js/display/display-generator.js"></script>
|
|
||||||
<script src="/js/display/structured-content-generator.js"></script>
|
|
||||||
<script src="/js/dom/html-template-collection.js"></script>
|
|
||||||
|
|
||||||
<script src="/js/pages/settings/pitch-accents-preview-main.js"></script>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
|
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/material.css">
|
<link rel="stylesheet" type="text/css" href="/css/material.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/display.css">
|
<link rel="stylesheet" type="text/css" href="/css/display.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/display-pronunciation.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/structured-content.css">
|
<link rel="stylesheet" type="text/css" href="/css/structured-content.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -109,6 +110,7 @@
|
|||||||
<script src="/js/display/display-resizer.js"></script>
|
<script src="/js/display/display-resizer.js"></script>
|
||||||
<script src="/js/display/element-overflow-controller.js"></script>
|
<script src="/js/display/element-overflow-controller.js"></script>
|
||||||
<script src="/js/display/option-toggle-hotkey-handler.js"></script>
|
<script src="/js/display/option-toggle-hotkey-handler.js"></script>
|
||||||
|
<script src="/js/display/pronunciation-generator.js"></script>
|
||||||
<script src="/js/display/query-parser.js"></script>
|
<script src="/js/display/query-parser.js"></script>
|
||||||
<script src="/js/display/structured-content-generator.js"></script>
|
<script src="/js/display/structured-content-generator.js"></script>
|
||||||
<script src="/js/dom/document-focus-controller.js"></script>
|
<script src="/js/dom/document-focus-controller.js"></script>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
|
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/material.css">
|
<link rel="stylesheet" type="text/css" href="/css/material.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/display.css">
|
<link rel="stylesheet" type="text/css" href="/css/display.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/display-pronunciation.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/structured-content.css">
|
<link rel="stylesheet" type="text/css" href="/css/structured-content.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/search.css">
|
<link rel="stylesheet" type="text/css" href="/css/search.css">
|
||||||
</head>
|
</head>
|
||||||
@ -93,6 +94,7 @@
|
|||||||
<script src="/js/display/display-notification.js"></script>
|
<script src="/js/display/display-notification.js"></script>
|
||||||
<script src="/js/display/element-overflow-controller.js"></script>
|
<script src="/js/display/element-overflow-controller.js"></script>
|
||||||
<script src="/js/display/option-toggle-hotkey-handler.js"></script>
|
<script src="/js/display/option-toggle-hotkey-handler.js"></script>
|
||||||
|
<script src="/js/display/pronunciation-generator.js"></script>
|
||||||
<script src="/js/display/query-parser.js"></script>
|
<script src="/js/display/query-parser.js"></script>
|
||||||
<script src="/js/display/search-action-popup-controller.js"></script>
|
<script src="/js/display/search-action-popup-controller.js"></script>
|
||||||
<script src="/js/display/search-display-controller.js"></script>
|
<script src="/js/display/search-display-controller.js"></script>
|
||||||
|
Loading…
Reference in New Issue
Block a user