summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/css/display.css26
-rw-r--r--ext/css/material.css2
-rw-r--r--ext/data/schemas/dictionary-term-bank-v3-schema.json107
-rw-r--r--ext/js/display/display-generator.js71
-rw-r--r--test/data/dictionaries/valid-dictionary1/term_bank_1.json73
5 files changed, 270 insertions, 9 deletions
diff --git a/ext/css/display.css b/ext/css/display.css
index 45658f19..e1efb223 100644
--- a/ext/css/display.css
+++ b/ext/css/display.css
@@ -1726,6 +1726,32 @@ button.definition-item-expansion-button:focus:focus-visible+.definition-item-con
}
+/* Structured content glossary styles */
+.gloss-sc-table-container {
+ display: block;
+}
+.gloss-sc-table {
+ table-layout: auto;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+.gloss-sc-tbody {
+ background-color: transparent;
+}
+.gloss-sc-thead,
+.gloss-sc-tfoot,
+.gloss-sc-th {
+ font-weight: bold;
+ background-color: var(--background-color-dark1);
+}
+.gloss-sc-th,
+.gloss-sc-td {
+ border: calc(1em / var(--font-size-no-units)) solid var(--text-color-light2);
+ padding: 0.25em;
+ vertical-align: top;
+}
+
+
/* Kanji */
.kanji-glyph-container {
display: block;
diff --git a/ext/css/material.css b/ext/css/material.css
index dd135065..b19b0b0f 100644
--- a/ext/css/material.css
+++ b/ext/css/material.css
@@ -69,6 +69,7 @@
--text-color-light4: #888888;
--background-color: #f8f9fa;
--background-color-light: #ffffff;
+ --background-color-dark1: #eeeeee;
--shadow-color: rgba(0, 0, 0, 0.185);
--shadow-color-off: rgba(0, 0, 0, 0);
@@ -128,6 +129,7 @@
--text-color-light4: #777777;
--background-color: #1e1e1e;
--background-color-light: #0a0a0a;
+ --background-color-dark1: #333333;
--shadow-color: rgba(255, 255, 255, 0.185);
--shadow-color-off: rgba(255, 255, 255, 0);
diff --git a/ext/data/schemas/dictionary-term-bank-v3-schema.json b/ext/data/schemas/dictionary-term-bank-v3-schema.json
index fd3f3844..af4494ff 100644
--- a/ext/data/schemas/dictionary-term-bank-v3-schema.json
+++ b/ext/data/schemas/dictionary-term-bank-v3-schema.json
@@ -19,6 +19,20 @@
"oneOf": [
{
"type": "object",
+ "description": "Empty tags.",
+ "required": [
+ "tag"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "tag": {
+ "type": "string",
+ "enum": ["br"]
+ }
+ }
+ },
+ {
+ "type": "object",
"description": "Generic container tags.",
"required": [
"tag"
@@ -27,10 +41,58 @@
"properties": {
"tag": {
"type": "string",
- "enum": ["ruby", "rt", "rp"]
+ "enum": ["ruby", "rt", "rp", "table", "thead", "tbody", "tfoot", "tr"]
+ },
+ "content": {
+ "$ref": "#/definitions/structuredContent"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "description": "Table tags.",
+ "required": [
+ "tag"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "tag": {
+ "type": "string",
+ "enum": ["td", "th"]
+ },
+ "content": {
+ "$ref": "#/definitions/structuredContent"
+ },
+ "colSpan": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "rowSpan": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "style": {
+ "$ref": "#/definitions/structuredContentStyle"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "description": "Container tags supporting configurable styles.",
+ "required": [
+ "tag"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "tag": {
+ "type": "string",
+ "enum": ["span", "div"]
},
"content": {
"$ref": "#/definitions/structuredContent"
+ },
+ "style": {
+ "$ref": "#/definitions/structuredContentStyle"
}
}
},
@@ -112,6 +174,49 @@
]
}
]
+ },
+ "structuredContentStyle": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "fontStyle": {
+ "type": "string",
+ "enum": ["normal", "italic"],
+ "default": "normal"
+ },
+ "fontWeight": {
+ "type": "string",
+ "enum": ["normal", "bold"],
+ "default": "normal"
+ },
+ "fontSize": {
+ "type": "string",
+ "enum": ["xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "xxx-large"],
+ "default": "medium"
+ },
+ "textDecorationLine": {
+ "oneOf": [
+ {
+ "type": "string",
+ "enum": ["none", "underline", "overline", "line-through"],
+ "default": "none"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["underline", "overline", "line-through"],
+ "default": "none"
+ }
+ }
+ ]
+ },
+ "verticalAlign": {
+ "type": "string",
+ "enum": ["baseline", "sub", "super", "text-top", "text-bottom", "middle", "top", "bottom"],
+ "default": "baseline"
+ }
+ }
}
},
"type": "array",
diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js
index 6a5b26f1..7de65f78 100644
--- a/ext/js/display/display-generator.js
+++ b/ext/js/display/display-generator.js
@@ -442,23 +442,78 @@ class DisplayGenerator {
}
const {tag} = content;
switch (tag) {
+ case 'br':
+ return this._createStructuredContentElement(tag, content, dictionary, 'simple', false, false);
case 'ruby':
case 'rt':
case 'rp':
- {
- const node = document.createElement(tag);
- const child = this._createStructuredContent(content.content, dictionary);
- if (child !== null) {
- node.appendChild(child);
- }
- return node;
- }
+ return this._createStructuredContentElement(tag, content, dictionary, 'simple', true, false);
+ case 'table':
+ return this._createStructuredContentTableElement(tag, content, dictionary);
+ case 'thead':
+ case 'tbody':
+ case 'tfoot':
+ case 'tr':
+ return this._createStructuredContentElement(tag, content, dictionary, 'table', true, false);
+ case 'th':
+ case 'td':
+ return this._createStructuredContentElement(tag, content, dictionary, 'table-cell', true, true);
+ case 'div':
+ case 'span':
+ return this._createStructuredContentElement(tag, content, dictionary, 'simple', true, true);
case 'img':
return this._createDefinitionImage(content, dictionary);
}
return null;
}
+ _createStructuredContentTableElement(tag, content, dictionary) {
+ const container = document.createElement('div');
+ container.classList = 'gloss-sc-table-container';
+ const table = this._createStructuredContentElement(tag, content, dictionary, 'table', true, false);
+ container.appendChild(table);
+ return container;
+ }
+
+ _createStructuredContentElement(tag, content, dictionary, type, hasChildren, hasStyle) {
+ const node = document.createElement(tag);
+ node.className = `gloss-sc-${tag}`;
+ switch (type) {
+ case 'table-cell':
+ {
+ const {colSpan, rowSpan} = content;
+ if (typeof colSpan === 'number') { node.colSpan = colSpan; }
+ if (typeof rowSpan === 'number') { node.rowSpan = rowSpan; }
+ }
+ break;
+ }
+ if (hasStyle) {
+ const {style} = content;
+ if (typeof style === 'object' && style !== null) {
+ this._setStructuredContentElementStyle(node, style);
+ }
+ }
+ if (hasChildren) {
+ const child = this._createStructuredContent(content.content, dictionary);
+ if (child !== null) { node.appendChild(child); }
+ }
+ return node;
+ }
+
+ _setStructuredContentElementStyle(node, contentStyle) {
+ const {style} = node;
+ const {fontStyle, fontWeight, fontSize, textDecorationLine, verticalAlign} = contentStyle;
+ if (typeof fontStyle === 'string') { style.fontStyle = fontStyle; }
+ if (typeof fontWeight === 'string') { style.fontWeight = fontWeight; }
+ if (typeof fontSize === 'string') { style.fontSize = fontSize; }
+ if (typeof verticalAlign === 'string') { style.verticalAlign = verticalAlign; }
+ if (typeof textDecorationLine === 'string') {
+ style.textDecoration = textDecorationLine;
+ } else if (Array.isArray(textDecorationLine)) {
+ style.textDecoration = textDecorationLine.join(' ');
+ }
+ }
+
_createTermDisambiguation(disambiguation) {
const node = this._templates.instantiate('definition-disambiguation');
node.dataset.term = disambiguation;
diff --git a/test/data/dictionaries/valid-dictionary1/term_bank_1.json b/test/data/dictionaries/valid-dictionary1/term_bank_1.json
index 1ba70c26..c58a17bf 100644
--- a/test/data/dictionaries/valid-dictionary1/term_bank_1.json
+++ b/test/data/dictionaries/valid-dictionary1/term_bank_1.json
@@ -59,6 +59,79 @@
"莢 ",
{"tag": "img", "path": "character.gif", "width": 1, "height": 1, "imageRendering": "crisp-edges", "background": false, "appearance": "monochrome", "collapsible": false, "collapsed": false, "sizeUnits": "em"},
"莢\n"
+ ]},
+ {"type": "structured-content", "content": [
+ {"tag": "div", "style": {"fontStyle": "normal"}, "content": "fontStyle:normal"},
+ {"tag": "div", "style": {"fontStyle": "italic"}, "content": "fontStyle:italic"},
+ {"tag": "div", "style": {"fontWeight": "normal"}, "content": "fontWeight:normal"},
+ {"tag": "div", "style": {"fontWeight": "bold"}, "content": "fontWeight:bold"},
+ {"tag": "div", "style": {"fontSize": "xx-small"}, "content": "fontSize:xx-small"},
+ {"tag": "div", "style": {"fontSize": "x-small"}, "content": "fontSize:x-small"},
+ {"tag": "div", "style": {"fontSize": "small"}, "content": "fontSize:small"},
+ {"tag": "div", "style": {"fontSize": "medium"}, "content": "fontSize:medium"},
+ {"tag": "div", "style": {"fontSize": "large"}, "content": "fontSize:large"},
+ {"tag": "div", "style": {"fontSize": "x-large"}, "content": "fontSize:x-large"},
+ {"tag": "div", "style": {"fontSize": "xx-large"}, "content": "fontSize:xx-large"},
+ {"tag": "div", "style": {"fontSize": "xxx-large"}, "content": "fontSize:xxx-large"},
+ {"tag": "div", "style": {"textDecorationLine": "none"}, "content": "textDecorationLine:none "},
+ {"tag": "div", "style": {"textDecorationLine": "underline"}, "content": "textDecorationLine:underline "},
+ {"tag": "div", "style": {"textDecorationLine": "overline"}, "content": "textDecorationLine:overline "},
+ {"tag": "div", "style": {"textDecorationLine": "line-through"}, "content": "textDecorationLine:line-through "},
+ {"tag": "div", "style": {"textDecorationLine": ["underline", "overline", "line-through"]}, "content": "textDecorationLine:[underline,overline,line-through] "},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "baseline"}, "content": "verticalAlign:baseline "}]},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "sub"}, "content": "verticalAlign:sub "}]},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "super"}, "content": "verticalAlign:super "}]},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "text-top"}, "content": "verticalAlign:text-top "}]},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "text-bottom"}, "content": "verticalAlign:text-bottom "}]},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "middle"}, "content": "verticalAlign:middle "}]},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "top"}, "content": "verticalAlign:top "}]},
+ {"tag": "div", "content": ["baseline ", {"tag": "span", "style": {"verticalAlign": "bottom"}, "content": "verticalAlign:bottom "}]}
+ ]},
+ {"type": "structured-content", "content": [
+ "br",
+ {"tag": "br"},
+ "br"
+ ]},
+ {"type": "structured-content", "content": [
+ {"tag": "table", "content": [
+ {"tag": "thead", "content": [
+ {"tag": "tr", "content": [
+ {"tag": "th", "content": "Header 1"},
+ {"tag": "th", "content": "Header 2"},
+ {"tag": "th", "content": "Header 3"},
+ {"tag": "th", "content": "Header 4"}
+ ]}
+ ]},
+ {"tag": "tbody", "content": [
+ {"tag": "tr", "content": [
+ {"tag": "td", "content": "Cell A1"},
+ {"tag": "td", "content": "Cell B1"},
+ {"tag": "td", "content": "Cell C1"},
+ {"tag": "td", "content": "Cell D1"}
+ ]},
+ {"tag": "tr", "content": [
+ {"tag": "td", "content": "Cell A2"},
+ {"tag": "td", "content": "Cell B2"},
+ {"tag": "td", "content": "Cell C2:D2", "colSpan": 2}
+ ]},
+ {"tag": "tr", "content": [
+ {"tag": "td", "content": "Cell A3"},
+ {"tag": "td", "content": "Cell B3\nCell B4", "rowSpan": 2},
+ {"tag": "td", "content": "Cell C3:D3\nCell C4:D4", "rowSpan": 2, "colSpan": 2}
+ ]},
+ {"tag": "tr", "content": [
+ {"tag": "td", "content": "Cell A4"}
+ ]}
+ ]},
+ {"tag": "tfoot", "content": [
+ {"tag": "tr", "content": [
+ {"tag": "th", "content": "Footer 1"},
+ {"tag": "th", "content": "Footer 2"},
+ {"tag": "th", "content": "Footer 3"},
+ {"tag": "th", "content": "Footer 4"}
+ ]}
+ ]}
+ ]}
]}
],
9, "P E1"