diff options
Diffstat (limited to 'ext/js')
| -rw-r--r-- | ext/js/display/display-generator.js | 98 | ||||
| -rw-r--r-- | ext/js/display/pronunciation-generator.js | 145 | ||||
| -rw-r--r-- | ext/js/pages/settings/pitch-accents-preview-main.js | 35 | 
3 files changed, 150 insertions, 128 deletions
| diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js index d7ae3bd9..15c5787d 100644 --- a/ext/js/display/display-generator.js +++ b/ext/js/display/display-generator.js @@ -18,6 +18,7 @@  /* global   * DictionaryDataUtil   * HtmlTemplateCollection + * PronunciationGenerator   * StructuredContentGenerator   */ @@ -28,7 +29,7 @@ class DisplayGenerator {          this._hotkeyHelpController = hotkeyHelpController;          this._templates = null;          this._structuredContentGenerator = new StructuredContentGenerator(this._mediaLoader, document); -        this._termPitchAccentStaticTemplateIsSetup = false; +        this._pronunciationGenerator = new PronunciationGenerator(japaneseUtil);      }      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) {          const node = this._templates.instantiate('term-entry'); @@ -439,8 +433,6 @@ class DisplayGenerator {      }      _createPitches(details) { -        this.preparePitchAccents(); -          const {dictionary, pitches} = details;          const node = this._templates.instantiate('pitch-accent-group'); @@ -471,9 +463,6 @@ class DisplayGenerator {          const {reading, position, nasalPositions, devoicePositions, tags, exclusiveTerms, exclusiveReadings} = details;          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');          node.dataset.pitchAccentPosition = `${position}`; @@ -491,46 +480,10 @@ class DisplayGenerator {          this._createPitchAccentDisambiguations(n, exclusiveTerms, exclusiveReadings);          n = node.querySelector('.pitch-accent-characters'); -        for (let i = 0, ii = morae.length; i < ii; ++i) { -            const i1 = i + 1; -            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'); -            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); -        } +        n.lang = 'ja'; +        n.appendChild(this._pronunciationGenerator.createPitchAccentHtml(morae, position, nasalPositions, devoicePositions)); -        if (morae.length > 0) { -            this._populatePitchGraph(node.querySelector('.pitch-accent-graph'), position, morae); -        } +        node.querySelector('.pitch-accent-details').appendChild(this._pronunciationGenerator.createPitchGraph(morae, position));          return node;      } @@ -556,47 +509,6 @@ class DisplayGenerator {          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) {          const {dictionary, frequencies} = details; diff --git a/ext/js/display/pronunciation-generator.js b/ext/js/display/pronunciation-generator.js new file mode 100644 index 00000000..eb5eb035 --- /dev/null +++ b/ext/js/display/pronunciation-generator.js @@ -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; +    } +} diff --git a/ext/js/pages/settings/pitch-accents-preview-main.js b/ext/js/pages/settings/pitch-accents-preview-main.js deleted file mode 100644 index d9d56727..00000000 --- a/ext/js/pages/settings/pitch-accents-preview-main.js +++ /dev/null @@ -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); -    } -})(); |