diff options
Diffstat (limited to 'ext/mixed')
| -rw-r--r-- | ext/mixed/css/display-dark.css | 27 | ||||
| -rw-r--r-- | ext/mixed/css/display-default.css | 27 | ||||
| -rw-r--r-- | ext/mixed/css/display.css | 190 | ||||
| -rw-r--r-- | ext/mixed/display-templates.html | 17 | ||||
| -rw-r--r-- | ext/mixed/js/core.js | 24 | ||||
| -rw-r--r-- | ext/mixed/js/display-generator.js | 246 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 3 | ||||
| -rw-r--r-- | ext/mixed/js/japanese.js | 26 | 
8 files changed, 544 insertions, 16 deletions
| diff --git a/ext/mixed/css/display-dark.css b/ext/mixed/css/display-dark.css index c9cd9f90..550dff3e 100644 --- a/ext/mixed/css/display-dark.css +++ b/ext/mixed/css/display-dark.css @@ -19,6 +19,8 @@  body { background-color: #1e1e1e; color: #d4d4d4; } +h2 { border-bottom-color: #2f2f2f; } +  .navigation-header {      background-color: #1e1e1e;      border-bottom-color: #2f2f2f; @@ -39,6 +41,7 @@ body { background-color: #1e1e1e; color: #d4d4d4; }  .tag[data-category=frequency]    { background-color: #489148; }  .tag[data-category=partOfSpeech] { background-color: #565656; }  .tag[data-category=search]       { background-color: #69696e; } +.tag[data-category=pitch-accent-dictionary] { background-color: #6640be; }  .term-reasons { color: #888888; } @@ -57,12 +60,15 @@ body { background-color: #1e1e1e; color: #d4d4d4; }      color: #666666;  } -.term-definition-container, -.kanji-glossary-container { +.term-definition-list, +.term-pitch-accent-group-list, +.term-pitch-accent-disambiguation-list, +.kanji-glossary-list {      color: #888888;  }  .term-glossary, +.term-pitch-accent,  .kanji-glossary {      color: #d4d4d4;  } @@ -72,3 +78,20 @@ body { background-color: #1e1e1e; color: #d4d4d4; }      background-color: #d4d4d4;      color: #1e1e1e;  } + +.term-pitch-accent-container { border-bottom-color: #2f2f2f; } + +.term-pitch-accent-character:before { border-color: #ffffff; } + +.term-pitch-accent-graph-line, +.term-pitch-accent-graph-line-tail, +#term-pitch-accent-graph-dot, +#term-pitch-accent-graph-dot-downstep, +#term-pitch-accent-graph-triangle { +    stroke: #ffffff; +} + +#term-pitch-accent-graph-dot, +#term-pitch-accent-graph-dot-downstep>circle:last-of-type { +    fill: #ffffff; +} diff --git a/ext/mixed/css/display-default.css b/ext/mixed/css/display-default.css index 6eee43c4..487b8cb8 100644 --- a/ext/mixed/css/display-default.css +++ b/ext/mixed/css/display-default.css @@ -19,6 +19,8 @@  body { background-color: #ffffff; color: #333333; } +h2 { border-bottom-color: #eeeeee; } +  .navigation-header {      background-color: #ffffff;      border-bottom-color: #eeeeee; @@ -39,6 +41,7 @@ body { background-color: #ffffff; color: #333333; }  .tag[data-category=frequency]    { background-color: #5cb85c; }  .tag[data-category=partOfSpeech] { background-color: #565656; }  .tag[data-category=search]       { background-color: #8a8a91; } +.tag[data-category=pitch-accent-dictionary] { background-color: #6640be; }  .term-reasons { color: #777777; } @@ -57,12 +60,15 @@ body { background-color: #ffffff; color: #333333; }      color: #999999;  } -.term-definition-container, -.kanji-glossary-container { +.term-definition-list, +.term-pitch-accent-group-list, +.term-pitch-accent-disambiguation-list, +.kanji-glossary-list {      color: #777777;  }  .term-glossary, +.term-pitch-accent,  .kanji-glossary {      color: #000000;  } @@ -72,3 +78,20 @@ body { background-color: #ffffff; color: #333333; }      background-color: #333333;      color: #ffffff;  } + +.term-pitch-accent-container { border-bottom-color: #eeeeee; } + +.term-pitch-accent-character:before { border-color: #000000; } + +.term-pitch-accent-graph-line, +.term-pitch-accent-graph-line-tail, +#term-pitch-accent-graph-dot, +#term-pitch-accent-graph-dot-downstep, +#term-pitch-accent-graph-triangle { +    stroke: #000000; +} + +#term-pitch-accent-graph-dot, +#term-pitch-accent-graph-dot-downstep>circle:last-of-type { +    fill: #000000; +} diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css index 688a357c..a4432016 100644 --- a/ext/mixed/css/display.css +++ b/ext/mixed/css/display.css @@ -65,6 +65,14 @@ ol, ul {      height: 2.28571428em; /* 14px => 32px */  } +h2 { +    font-size: 1.25em; +    font-weight: normal; +    margin: 0.25em 0 0; +    border-bottom-width: 0.05714285714285714em; /* 14px * 1.25em => 1px */ +    border-bottom-style: solid; +} +  /*   * Navigation   */ @@ -302,6 +310,7 @@ button.action-button {      width: 0;      height: 0;      visibility: hidden; +    z-index: 1;  }  .term-expression-list[data-multi=true] .term-expression:hover .term-expression-details { @@ -422,6 +431,187 @@ button.action-button {      display: inline;  } +.term-entry-body[data-section-count="0"] .term-entry-body-section-header, +.term-entry-body[data-section-count="1"] .term-entry-body-section-header { +    display: none; +} + + +/* + * Pitch accent styles + */ + +.entry[data-pitch-accent-count='0'] .term-pitch-accent-container { +    display: none; +} + +.term-pitch-accent-container { +    border-bottom-width: 0.05714285714285714em; /* 14px * 1.25em => 1px */ +    border-bottom-style: solid; +    padding-bottom: 0.25em; +    margin-bottom: 0.25em; +} + +.term-pitch-accent-group-list { +    margin: 0; +    padding: 0; +    list-style-type: none; +} + +.term-pitch-accent-group-list:not([data-count="0"]):not([data-count="1"]) { +    padding-left: 1.4em; +    list-style-type: decimal; +} + +.term-pitch-accent-list { +    margin: 0; +    padding: 0; +    list-style-type: none; +    display: inline; +} + +.term-pitch-accent-list:not([data-count="0"]):not([data-count="1"]) { +    padding-left: 1.4em; +    list-style-type: circle; +    display: block; +} + +.term-pitch-accent { +    display: inline; +    line-height: 1.5em; +} + +.term-pitch-accent-list:not([data-count="0"]):not([data-count="1"])>.term-pitch-accent { +    display: list-item; +} + +.term-pitch-accent-group-tag-list { +    margin-right: 0.375em; +} + +.term-pitch-accent-disambiguation-list { +    padding-right: 0.25em; +} + +.term-pitch-accent-disambiguation-list:before { +    content: "("; +} + +.term-pitch-accent-disambiguation-list:after { +    content: " only)"; +} + +.term-pitch-accent-disambiguation+.term-pitch-accent-disambiguation:before { +    content: ", "; +} + +.term-pitch-accent-disambiguation-list[data-count="0"], +:root[data-show-pitch-accent-downstep-notation=true] .term-pitch-accent-disambiguation-list[data-expression-count="0"], +:root[data-show-pitch-accent-downstep-notation=true] .term-pitch-accent-disambiguation[data-type=reading] { +    display: none; +} + +.term-pitch-accent-tag-list:not([data-count="0"]) { +    margin-right: 0.375em; +} + +.term-special-tags>.pitches { +    display: inline; +} + +.term-pitch-accent-character { +    display: inline-block; +    position: relative; +} +.term-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; +} +.term-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; +} +.term-pitch-accent-character[data-pitch='high'][data-pitch-next='low'] { +    padding-right: 0.1em; +    margin-right: 0.1em; +} + +.term-pitch-accent-position:before { +    content: " ["; +} +.term-pitch-accent-position:after { +    content: "]"; +} + +.term-pitch-accent-details { +    display: inline-block; +    height: 0; +    padding: 0 0.25em; +    vertical-align: middle; +} + + +:root[data-show-pitch-accent-downstep-notation=false] .term-pitch-accent-characters { +    display: none; +} + +:root[data-show-pitch-accent-position-notation=false] .term-pitch-accent-position { +    display: none; +} + +:root[data-show-pitch-accent-graph=false] .term-pitch-accent-details { +    display: none; +} + + +/* + * Pitch accent graph styles + */ + +.term-pitch-accent-graph { +    display: block; +    height: 1.5em; +    transform: translateY(-0.875em); +} +.term-pitch-accent-graph-line, +.term-pitch-accent-graph-line-tail { +    fill: none; +    stroke: #000000; +    stroke-width: 5; +} +.term-pitch-accent-graph-line-tail { +    stroke-dasharray: 5 5; +} +#term-pitch-accent-graph-dot { +    fill: #000000; +    stroke: #000000; +    stroke-width: 5; +} +#term-pitch-accent-graph-dot-downstep { +    fill: none; +    stroke: #000000; +    stroke-width: 5; +} +#term-pitch-accent-graph-dot-downstep>circle:last-of-type { +    fill: #000000; +} +#term-pitch-accent-graph-triangle { +    fill: none; +    stroke: #000000; +    stroke-width: 5; +} +  /*   * Kanji diff --git a/ext/mixed/display-templates.html b/ext/mixed/display-templates.html index 7ae51a62..b8d52d15 100644 --- a/ext/mixed/display-templates.html +++ b/ext/mixed/display-templates.html @@ -17,7 +17,10 @@          </div>          <div class="term-special-tags"><div class="frequencies tag-list"></div></div>      </div> -    <div class="term-definition-container"><ol class="term-definition-list"></ol></div> +    <div class="term-entry-body"> +        <div class="term-entry-body-section term-pitch-accent-container"><ol class="term-entry-body-section-content term-pitch-accent-group-list"></ol></div> +        <div class="term-entry-body-section term-definition-container"><ol class="term-entry-body-section-content term-definition-list"></ol></div> +    </div>      <pre class="debug-info"></pre>  </div></template>  <template id="term-expression-template"><div class="term-expression"><span class="term-expression-text source-text"></span><div class="term-expression-details"> @@ -34,6 +37,18 @@  <template id="term-glossary-item-template"><li class="term-glossary-item"><span class="term-glossary-separator"> </span><span class="term-glossary"></span></li></template>  <template id="term-reason-template"><span class="term-reason"></span><span class="term-reason-separator"> </span></template> +<template id="term-pitch-accent-static-template"><svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> +    <defs> +        <g id="term-pitch-accent-graph-dot"><circle cx="0" cy="0" r="15" /></g> +        <g id="term-pitch-accent-graph-dot-downstep"><circle cx="0" cy="0" r="15" /><circle cx="0" cy="0" r="5" /></g> +        <g id="term-pitch-accent-graph-triangle"><path d="M0 13 L15 -13 L-15 -13 Z" /></g> +    </defs> +</svg></template> +<template id="term-pitch-accent-group-template"><li class="term-pitch-accent-group"><span class="term-pitch-accent-group-tag-list tag-list"></span><ul class="term-pitch-accent-list"></ul></li></template> +<template id="term-pitch-accent-disambiguation-template"><span class="term-pitch-accent-disambiguation"></span></template> +<template id="term-pitch-accent-template"><li class="term-pitch-accent"><span class="term-pitch-accent-tag-list tag-list"></span><span class="term-pitch-accent-disambiguation-list"></span><span class="term-pitch-accent-characters"></span><span class="term-pitch-accent-position"></span><span class="term-pitch-accent-details"><svg class="term-pitch-accent-graph" xmlns="http://www.w3.org/2000/svg"><path class="term-pitch-accent-graph-line" /><path class="term-pitch-accent-graph-line-tail" /></svg></span></li></template> +<template id="term-pitch-accent-character-template"><span class="term-pitch-accent-character"><span class="term-pitch-accent-character-inner"></span></span></template> +  <template id="kanji-entry-template"><div class="entry" data-type="kanji">      <div class="entry-header1">          <div class="entry-header2"> diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 0d50e915..fd762e97 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -132,6 +132,30 @@ function parseUrl(url) {      return {baseUrl, queryParams};  } +function areSetsEqual(set1, set2) { +    if (set1.size !== set2.size) { +        return false; +    } + +    for (const value of set1) { +        if (!set2.has(value)) { +            return false; +        } +    } + +    return true; +} + +function getSetIntersection(set1, set2) { +    const result = []; +    for (const value of set1) { +        if (set2.has(value)) { +            result.push(value); +        } +    } +    return result; +} +  /*   * Async utilities diff --git a/ext/mixed/js/display-generator.js b/ext/mixed/js/display-generator.js index 41f7315a..f1122e3d 100644 --- a/ext/mixed/js/display-generator.js +++ b/ext/mixed/js/display-generator.js @@ -25,6 +25,7 @@  class DisplayGenerator {      constructor() {          this._templateHandler = null; +        this._termPitchAccentStaticTemplateIsSetup = false;      }      async prepare() { @@ -37,17 +38,33 @@ 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); +        const expressionCount = expressionMulti ? details.expressions.length : 1; +        const definitionCount = definitionMulti ? details.definitions.length : 1; +        const uniqueExpressionCount = Array.isArray(details.expression) ? new Set(details.expression).size : 1;          node.dataset.expressionMulti = `${expressionMulti}`;          node.dataset.definitionMulti = `${definitionMulti}`; -        node.dataset.expressionCount = `${expressionMulti ? details.expressions.length : 1}`; -        node.dataset.definitionCount = `${definitionMulti ? details.definitions.length : 1}`; +        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) + +            (pitches.length > 0 ? 1 : 0) +        }`;          const termTags = details.termTags;          let expressions = details.expressions; @@ -56,6 +73,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) { @@ -262,6 +280,133 @@ 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: 'pitch-accent-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 {reading, position, tags, exclusiveExpressions, exclusiveReadings} = details; +        const morae = jp.getKanaMorae(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-disambiguation-list'); +        this.createPitchAccentDisambiguations(n, exclusiveExpressions, exclusiveReadings); + +        n = node.querySelector('.term-pitch-accent-characters'); +        for (let i = 0, ii = morae.length; i < ii; ++i) { +            const mora = morae[i]; +            const highPitch = jp.isMoraPitchHigh(i, position); +            const highPitchNext = jp.isMoraPitchHigh(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); +        } + +        if (morae.length > 0) { +            this.populatePitchGraph(node.querySelector('.term-pitch-accent-graph'), position, morae); +        } + +        return node; +    } + +    createPitchAccentDisambiguations(container, exclusiveExpressions, exclusiveReadings) { +        const templateName = 'term-pitch-accent-disambiguation'; +        for (const exclusiveExpression of exclusiveExpressions) { +            const node = this._templateHandler.instantiate(templateName); +            node.dataset.type = 'expression'; +            node.textContent = exclusiveExpression; +            container.appendChild(node); +        } + +        for (const exclusiveReading of exclusiveReadings) { +            const node = this._templateHandler.instantiate(templateName); +            node.dataset.type = 'reading'; +            node.textContent = exclusiveReading; +            container.appendChild(node); +        } + +        container.dataset.multi = 'true'; +        container.dataset.count = `${exclusiveExpressions.length + exclusiveReadings.length}`; +        container.dataset.expressionCount = `${exclusiveExpressions.length}`; +        container.dataset.readingCount = `${exclusiveReadings.length}`; +    } + +    populatePitchGraph(svg, position, morae) { +        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 ? '#term-pitch-accent-graph-dot-downstep' : '#term-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('.term-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', '#term-pitch-accent-graph-triangle'); +            use.setAttribute('x', x); +            use.setAttribute('y', y); +            svg.appendChild(use); +            pathPoints.push(`${x} ${y}`); +        } + +        path = svg.querySelector('.term-pitch-accent-graph-line-tail'); +        path.setAttribute('d', `M${pathPoints.join(' L')}`); +    } +      createFrequencyTag(details) {          const node = this._templateHandler.instantiate('tag-frequency'); @@ -301,22 +446,28 @@ class DisplayGenerator {          }      } -    static _appendMultiple(container, createItem, detailsArray, fallback=[]) { +    static _appendMultiple(container, createItem, detailsIterable, fallback=[]) {          if (container === null) { return 0; } -        const isArray = Array.isArray(detailsArray); -        if (!isArray) { detailsArray = fallback; } - -        container.dataset.multi = `${isArray}`; -        container.dataset.count = `${detailsArray.length}`; +        const multi = ( +            detailsIterable !== null && +            typeof detailsIterable === 'object' && +            typeof detailsIterable[Symbol.iterator] !== 'undefined' +        ); +        if (!multi) { detailsIterable = fallback; } -        for (const details of detailsArray) { +        let count = 0; +        for (const details of detailsIterable) {              const item = createItem(details);              if (item === null) { continue; }              container.appendChild(item); +            ++count;          } -        return detailsArray.length; +        container.dataset.multi = `${multi}`; +        container.dataset.count = `${count}`; + +        return count;      }      static _appendFurigana(container, segments, addText) { @@ -342,4 +493,79 @@ class DisplayGenerator {              container.appendChild(document.createTextNode(parts[i]));          }      } + +    static _getPitchInfos(definition) { +        const results = new Map(); + +        const allExpressions = new Set(); +        const allReadings = new Set(); +        const expressions = definition.expressions; +        const sources = Array.isArray(expressions) ? expressions : [definition]; +        for (const {pitches: expressionPitches, expression} of sources) { +            allExpressions.add(expression); +            for (const {reading, pitches, dictionary} of expressionPitches) { +                allReadings.add(reading); +                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); +                } +            } +        } + +        for (const dictionaryResults of results.values()) { +            for (const result of dictionaryResults) { +                const exclusiveExpressions = []; +                const exclusiveReadings = []; +                const resultExpressions = result.expressions; +                if (!areSetsEqual(resultExpressions, allExpressions)) { +                    exclusiveExpressions.push(...getSetIntersection(resultExpressions, allExpressions)); +                } +                if (allReadings.size > 1) { +                    exclusiveReadings.push(result.reading); +                } +                result.exclusiveExpressions = exclusiveExpressions; +                result.exclusiveReadings = exclusiveReadings; +            } +        } + +        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; +    }  } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 6898a6eb..4a71efe0 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -385,6 +385,9 @@ class Display {          data.audioEnabled = `${options.audio.enabled}`;          data.compactGlossaries = `${options.general.compactGlossaries}`;          data.enableSearchTags = `${options.scanning.enableSearchTags}`; +        data.showPitchAccentDownstepNotation = `${options.general.showPitchAccentDownstepNotation}`; +        data.showPitchAccentPositionNotation = `${options.general.showPitchAccentPositionNotation}`; +        data.showPitchAccentGraph = `${options.general.showPitchAccentGraph}`;          data.debug = `${options.general.debugInfo}`;      } diff --git a/ext/mixed/js/japanese.js b/ext/mixed/js/japanese.js index 61a247b2..e6b9a8a0 100644 --- a/ext/mixed/js/japanese.js +++ b/ext/mixed/js/japanese.js @@ -64,6 +64,8 @@ const jp = (() => {          [0xffe0, 0xffee]  // Currency markers      ]; +    const SMALL_KANA_SET = new Set(Array.from('ぁぃぅぇぉゃゅょゎァィゥェォャュョヮ')); +      // Character code testing functions @@ -112,6 +114,26 @@ const jp = (() => {      } +    // Mora functions + +    function isMoraPitchHigh(moraIndex, pitchAccentPosition) { +        return pitchAccentPosition === 0 ? (moraIndex > 0) : (moraIndex < pitchAccentPosition); +    } + +    function getKanaMorae(text) { +        const morae = []; +        let i; +        for (const c of text) { +            if (SMALL_KANA_SET.has(c) && (i = morae.length) > 0) { +                morae[i - 1] += c; +            } else { +                morae.push(c); +            } +        } +        return morae; +    } + +      // Exports      return { @@ -119,6 +141,8 @@ const jp = (() => {          isCodePointKana,          isCodePointJapanese,          isStringEntirelyKana, -        isStringPartiallyJapanese +        isStringPartiallyJapanese, +        isMoraPitchHigh, +        getKanaMorae      };  })(); |