diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-03-01 14:15:28 -0500 | 
|---|---|---|
| committer | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-03-28 10:25:57 -0400 | 
| commit | 97a520cc1595369dc18ddcf74ab7f0ba4e03f55b (patch) | |
| tree | 9ee58cba18c0e4f60571ab6266a91b3458633a3e /ext/mixed/js | |
| parent | 2d7214ce60ef55b0d3d6e98b86f41b4e9fce5c48 (diff) | |
Add support for displaying pitch accents
Diffstat (limited to 'ext/mixed/js')
| -rw-r--r-- | ext/mixed/js/display-generator.js | 159 | 
1 files changed, 158 insertions, 1 deletions
| diff --git a/ext/mixed/js/display-generator.js b/ext/mixed/js/display-generator.js index fd7c5c1f..449bac1d 100644 --- a/ext/mixed/js/display-generator.js +++ b/ext/mixed/js/display-generator.js @@ -37,9 +37,14 @@ class DisplayGenerator {          const expressionsContainer = node.querySelector('.term-expression-list');          const reasonsContainer = node.querySelector('.term-reasons'); +        const pitchesContainer = node.querySelector('.term-pitch-accent-group-list');          const frequenciesContainer = node.querySelector('.frequencies');          const definitionsContainer = node.querySelector('.term-definition-list');          const debugInfoContainer = node.querySelector('.debug-info'); +        const bodyContainer = node.querySelector('.term-entry-body'); + +        const pitches = DisplayGenerator._getPitchInfos(details); +        const pitchCount = pitches.reduce((i, v) => i + v[1].length, 0);          const expressionMulti = Array.isArray(details.expressions);          const definitionMulti = Array.isArray(details.definitions); @@ -52,9 +57,12 @@ class DisplayGenerator {          node.dataset.expressionCount = `${expressionCount}`;          node.dataset.definitionCount = `${definitionCount}`;          node.dataset.uniqueExpressionCount = `${uniqueExpressionCount}`; +        node.dataset.pitchAccentDictionaryCount = `${pitches.length}`; +        node.dataset.pitchAccentCount = `${pitchCount}`;          bodyContainer.dataset.sectionCount = `${ -            (definitionCount > 0 ? 1 : 0) +            (definitionCount > 0 ? 1 : 0) + +            (pitches.length > 0 ? 1 : 0)          }`;          const termTags = details.termTags; @@ -64,6 +72,7 @@ class DisplayGenerator {          DisplayGenerator._appendMultiple(expressionsContainer, this.createTermExpression.bind(this), expressions, [[details, termTags]]);          DisplayGenerator._appendMultiple(reasonsContainer, this.createTermReason.bind(this), details.reasons);          DisplayGenerator._appendMultiple(frequenciesContainer, this.createFrequencyTag.bind(this), details.frequencies); +        DisplayGenerator._appendMultiple(pitchesContainer, this.createPitches.bind(this), pitches);          DisplayGenerator._appendMultiple(definitionsContainer, this.createTermDefinitionItem.bind(this), details.definitions, [details]);          if (debugInfoContainer !== null) { @@ -270,6 +279,73 @@ class DisplayGenerator {          return node;      } +    createPitches(details) { +        if (!this._termPitchAccentStaticTemplateIsSetup) { +            this._termPitchAccentStaticTemplateIsSetup = true; +            const t = this._templateHandler.instantiate('term-pitch-accent-static'); +            document.head.appendChild(t); +        } + +        const [dictionary, dictionaryPitches] = details; + +        const node = this._templateHandler.instantiate('term-pitch-accent-group'); +        node.dataset.dictionary = dictionary; +        node.dataset.pitchesMulti = 'true'; +        node.dataset.pitchesCount = `${dictionaryPitches.length}`; + +        const tag = this.createTag({notes: '', name: dictionary, category: 'dictionary'}); +        node.querySelector('.term-pitch-accent-group-tag-list').appendChild(tag); + +        const n = node.querySelector('.term-pitch-accent-list'); +        DisplayGenerator._appendMultiple(n, this.createPitch.bind(this), dictionaryPitches); + +        return node; +    } + +    createPitch(details) { +        const {expressions, reading, position, tags} = details; +        const morae = DisplayGenerator._jpGetKanaMorae(reading); + +        const node = this._templateHandler.instantiate('term-pitch-accent'); + +        node.dataset.pitchAccentPosition = `${position}`; +        node.dataset.tagCount = `${tags.length}`; + +        let n = node.querySelector('.term-pitch-accent-position'); +        n.textContent = `${position}`; + +        n = node.querySelector('.term-pitch-accent-tag-list'); +        DisplayGenerator._appendMultiple(n, this.createTag.bind(this), tags); + +        n = node.querySelector('.term-pitch-accent-expression-list'); +        DisplayGenerator._appendMultiple(n, this.createPitchExpression.bind(this), expressions); + +        n = node.querySelector('.term-pitch-accent-characters'); +        for (let i = 0, ii = morae.length; i < ii; ++i) { +            const mora = morae[i]; +            const highPitch = DisplayGenerator._jpIsMoraPitchHigh(i, position); +            const highPitchNext = DisplayGenerator._jpIsMoraPitchHigh(i + 1, position); + +            const n1 = this._templateHandler.instantiate('term-pitch-accent-character'); +            const n2 = n1.querySelector('.term-pitch-accent-character-inner'); + +            n1.dataset.position = `${i}`; +            n1.dataset.pitch = highPitch ? 'high' : 'low'; +            n1.dataset.pitchNext = highPitchNext ? 'high' : 'low'; +            n2.textContent = mora; + +            n.appendChild(n1); +        } + +        return node; +    } + +    createPitchExpression(expression) { +        const node = this._templateHandler.instantiate('term-pitch-accent-expression'); +        node.textContent = expression; +        return node; +    } +      createFrequencyTag(details) {          const node = this._templateHandler.instantiate('tag-frequency'); @@ -356,4 +432,85 @@ class DisplayGenerator {              container.appendChild(document.createTextNode(parts[i]));          }      } + +    static _getPitchInfos(definition) { +        const results = new Map(); + +        const expressions = definition.expressions; +        const sources = Array.isArray(expressions) ? expressions : [definition]; +        for (const {pitches: expressionPitches, expression} of sources) { +            for (const {reading, pitches, dictionary} of expressionPitches) { +                let dictionaryResults = results.get(dictionary); +                if (typeof dictionaryResults === 'undefined') { +                    dictionaryResults = []; +                    results.set(dictionary, dictionaryResults); +                } + +                for (const {position, tags} of pitches) { +                    let pitchInfo = DisplayGenerator._findExistingPitchInfo(reading, position, tags, dictionaryResults); +                    if (pitchInfo === null) { +                        pitchInfo = {expressions: new Set(), reading, position, tags}; +                        dictionaryResults.push(pitchInfo); +                    } +                    pitchInfo.expressions.add(expression); +                } +            } +        } + +        return [...results.entries()]; +    } + +    static _findExistingPitchInfo(reading, position, tags, pitchInfoList) { +        for (const pitchInfo of pitchInfoList) { +            if ( +                pitchInfo.reading === reading && +                pitchInfo.position === position && +                DisplayGenerator._areTagListsEqual(pitchInfo.tags, tags) +            ) { +                return pitchInfo; +            } +        } +        return null; +    } + +    static _areTagListsEqual(tagList1, tagList2) { +        const ii = tagList1.length; +        if (tagList2.length !== ii) { return false; } + +        for (let i = 0; i < ii; ++i) { +            const tag1 = tagList1[i]; +            const tag2 = tagList2[i]; +            if (tag1.name !== tag2.name || tag1.dictionary !== tag2.dictionary) { +                return false; +            } +        } + +        return true; +    } + +    static _jpGetKanaMorae(text) { +        // This function splits Japanese kana reading into its individual mora +        // components. It is assumed that the text is well-formed. +        const smallKanaSet = DisplayGenerator._smallKanaSet; +        const morae = []; +        let i; +        for (const c of text) { +            if (smallKanaSet.has(c) && (i = morae.length) > 0) { +                morae[i - 1] += c; +            } else { +                morae.push(c); +            } +        } +        return morae; +    } + +    static _jpCreateSmallKanaSet() { +        return new Set(Array.from('ぁぃぅぇぉゃゅょゎァィゥェォャュョヮ')); +    } + +    static _jpIsMoraPitchHigh(moraIndex, pitchAccentPosition) { +        return pitchAccentPosition === 0 ? (moraIndex > 0) : (moraIndex < pitchAccentPosition); +    }  } + +DisplayGenerator._smallKanaSet = DisplayGenerator._jpCreateSmallKanaSet(); |