diff --git a/.eslintrc.json b/.eslintrc.json index 9bd6514a..8882cb42 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -87,6 +87,8 @@ "stringReverse": "readonly", "promiseTimeout": "readonly", "parseUrl": "readonly", + "areSetsEqual": "readonly", + "getSetIntersection": "readonly", "EventDispatcher": "readonly", "EventListenerCollection": "readonly", "EXTENSION_IS_BROWSER_EDGE": "readonly" diff --git a/ext/bg/data/dictionary-term-meta-bank-v3-schema.json b/ext/bg/data/dictionary-term-meta-bank-v3-schema.json index 1cc0557f..8475db81 100644 --- a/ext/bg/data/dictionary-term-meta-bank-v3-schema.json +++ b/ext/bg/data/dictionary-term-meta-bank-v3-schema.json @@ -13,13 +13,71 @@ }, { "type": "string", - "enum": ["freq"], - "description": "Type of data. \"freq\" corresponds to frequency information." + "enum": ["freq", "pitch"], + "description": "Type of data. \"freq\" corresponds to frequency information; \"pitch\" corresponds to pitch information." }, { - "type": ["string", "number"], "description": "Data for the term/expression." } + ], + "oneOf": [ + { + "items": [ + {}, + {"enum": ["freq"]}, + { + "type": ["string", "number"], + "description": "Frequency information for the term or expression." + } + ] + }, + { + "items": [ + {}, + {"enum": ["pitch"]}, + { + "type": ["object"], + "description": "Pitch accent information for the term or expression.", + "required": [ + "reading", + "pitches" + ], + "additionalProperties": false, + "properties": { + "reading": { + "type": "string", + "description": "Reading for the term or expression." + }, + "pitches": { + "type": "array", + "description": "List of different pitch accent information for the term and reading combination.", + "additionalItems": { + "type": "object", + "required": [ + "position" + ], + "additionalProperties": false, + "properties": { + "position": { + "type": "integer", + "description": "Mora position of the pitch accent downstep. A value of 0 indicates that the word does not have a downstep (heiban).", + "minimum": 0 + }, + "tags": { + "type": "array", + "description": "List of tags for this pitch accent.", + "items": { + "type": "string", + "description": "Tag for this pitch accent. This typically corresponds to a certain type of part of speech." + } + } + } + } + } + } + } + ] + } ] } } \ No newline at end of file diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json index d6207952..cb759b72 100644 --- a/ext/bg/data/options-schema.json +++ b/ext/bg/data/options-schema.json @@ -105,7 +105,10 @@ "customPopupCss", "customPopupOuterCss", "enableWanakana", - "enableClipboardMonitor" + "enableClipboardMonitor", + "showPitchAccentDownstepNotation", + "showPitchAccentPositionNotation", + "showPitchAccentGraph" ], "properties": { "enable": { @@ -227,6 +230,18 @@ "enableClipboardMonitor": { "type": "boolean", "default": false + }, + "showPitchAccentDownstepNotation": { + "type": "boolean", + "default": true + }, + "showPitchAccentPositionNotation": { + "type": "boolean", + "default": true + }, + "showPitchAccentGraph": { + "type": "boolean", + "default": false } } }, diff --git a/ext/bg/js/dictionary.js b/ext/bg/js/dictionary.js index 3dd1d0c1..74bd5a64 100644 --- a/ext/bg/js/dictionary.js +++ b/ext/bg/js/dictionary.js @@ -137,30 +137,6 @@ function dictTermsGroup(definitions, dictionaries) { return dictTermsSort(results); } -function dictAreSetsEqual(set1, set2) { - if (set1.size !== set2.size) { - return false; - } - - for (const value of set1) { - if (!set2.has(value)) { - return false; - } - } - - return true; -} - -function dictGetSetIntersection(set1, set2) { - const result = []; - for (const value of set1) { - if (set2.has(value)) { - result.push(value); - } - } - return result; -} - function dictTermsMergeBySequence(definitions, mainDictionary) { const sequencedDefinitions = new Map(); const nonSequencedDefinitions = []; @@ -281,11 +257,11 @@ function dictTermsMergeByGloss(result, definitions, appendTo=null, mergedIndices const only = []; const expressionSet = definition.expression; const readingSet = definition.reading; - if (!dictAreSetsEqual(expressionSet, resultExpressionSet)) { - only.push(...dictGetSetIntersection(expressionSet, resultExpressionSet)); + if (!areSetsEqual(expressionSet, resultExpressionSet)) { + only.push(...getSetIntersection(expressionSet, resultExpressionSet)); } - if (!dictAreSetsEqual(readingSet, resultReadingSet)) { - only.push(...dictGetSetIntersection(readingSet, resultReadingSet)); + if (!areSetsEqual(readingSet, resultReadingSet)) { + only.push(...getSetIntersection(readingSet, resultReadingSet)); } definition.only = only; } diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index bd0bbe0e..b36fe812 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -124,7 +124,10 @@ function profileOptionsCreateDefaults() { customPopupCss: '', customPopupOuterCss: '', enableWanakana: true, - enableClipboardMonitor: false + enableClipboardMonitor: false, + showPitchAccentDownstepNotation: true, + showPitchAccentPositionNotation: true, + showPitchAccentGraph: false }, audio: { diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index ebc443df..7caeaea0 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -84,6 +84,9 @@ async function formRead(options) { options.general.popupScalingFactor = parseFloat($('#popup-scaling-factor').val()); options.general.popupScaleRelativeToPageZoom = $('#popup-scale-relative-to-page-zoom').prop('checked'); options.general.popupScaleRelativeToVisualViewport = $('#popup-scale-relative-to-visual-viewport').prop('checked'); + options.general.showPitchAccentDownstepNotation = $('#show-pitch-accent-downstep-notation').prop('checked'); + options.general.showPitchAccentPositionNotation = $('#show-pitch-accent-position-notation').prop('checked'); + options.general.showPitchAccentGraph = $('#show-pitch-accent-graph').prop('checked'); options.general.popupTheme = $('#popup-theme').val(); options.general.popupOuterTheme = $('#popup-outer-theme').val(); options.general.customPopupCss = $('#custom-popup-css').val(); @@ -161,6 +164,9 @@ async function formWrite(options) { $('#popup-scaling-factor').val(options.general.popupScalingFactor); $('#popup-scale-relative-to-page-zoom').prop('checked', options.general.popupScaleRelativeToPageZoom); $('#popup-scale-relative-to-visual-viewport').prop('checked', options.general.popupScaleRelativeToVisualViewport); + $('#show-pitch-accent-downstep-notation').prop('checked', options.general.showPitchAccentDownstepNotation); + $('#show-pitch-accent-position-notation').prop('checked', options.general.showPitchAccentPositionNotation); + $('#show-pitch-accent-graph').prop('checked', options.general.showPitchAccentGraph); $('#popup-theme').val(options.general.popupTheme); $('#popup-outer-theme').val(options.general.popupOuterTheme); $('#custom-popup-css').val(options.general.customPopupCss); diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js index 6f43f7b0..f16889ce 100644 --- a/ext/bg/js/translator.js +++ b/ext/bg/js/translator.js @@ -490,6 +490,7 @@ class Translator { // New data term.frequencies = []; + term.pitches = []; } const metas = await this.database.findTermMetaBulk(expressionsUnique, dictionaries); @@ -500,6 +501,13 @@ class Translator { term.frequencies.push({expression, frequency: data, dictionary}); } break; + case 'pitch': + for (const term of termsUnique[index]) { + const pitchData = await this.getPitchData(expression, data, dictionary, term); + if (pitchData === null) { continue; } + term.pitches.push(pitchData); + } + break; } } } @@ -583,6 +591,20 @@ class Translator { return tagMetaList; } + async getPitchData(expression, data, dictionary, term) { + const reading = data.reading; + const termReading = term.reading || expression; + if (reading !== termReading) { return null; } + + const pitches = []; + for (let {position, tags} of data.pitches) { + tags = Array.isArray(tags) ? await this.getTagMetaList(tags, dictionary) : []; + pitches.push({position, tags}); + } + + return {reading, pitches, dictionary}; + } + static createExpression(expression, reading, termTags=null, termFrequency=null) { const furiganaSegments = jp.distributeFurigana(expression, reading); return { diff --git a/ext/bg/settings.html b/ext/bg/settings.html index cfe20be4..0b2e4f9c 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -162,6 +162,18 @@ +
+ +
+ +
+ +
+ +
+ +
+
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 @@
-
    +
    +
      +
        +