From f68ad1f843607d4ba1ad216fe16305c420cee8d6 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 4 Sep 2021 12:43:56 -0400 Subject: [PATCH] Structured content data (#1930) * Add support for structured-content data attributes * Assign dataset properties * Don't remove data-sc attributes * Use helper functions * Update test data --- .../dictionary-term-bank-v3-schema.json | 22 ++++++++++++ .../sandbox/structured-content-generator.js | 12 +++++++ .../sandbox/anki-template-renderer.js | 35 ++++++++++--------- .../valid-dictionary1/term_bank_1.json | 15 ++++++++ 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/ext/data/schemas/dictionary-term-bank-v3-schema.json b/ext/data/schemas/dictionary-term-bank-v3-schema.json index abb4e58f..9898a15e 100644 --- a/ext/data/schemas/dictionary-term-bank-v3-schema.json +++ b/ext/data/schemas/dictionary-term-bank-v3-schema.json @@ -28,6 +28,9 @@ "tag": { "type": "string", "enum": ["br"] + }, + "data": { + "$ref": "#/definitions/structuredContentData" } } }, @@ -45,6 +48,9 @@ }, "content": { "$ref": "#/definitions/structuredContent" + }, + "data": { + "$ref": "#/definitions/structuredContentData" } } }, @@ -63,6 +69,9 @@ "content": { "$ref": "#/definitions/structuredContent" }, + "data": { + "$ref": "#/definitions/structuredContentData" + }, "colSpan": { "type": "integer", "minimum": 1 @@ -91,6 +100,9 @@ "content": { "$ref": "#/definitions/structuredContent" }, + "data": { + "$ref": "#/definitions/structuredContentData" + }, "style": { "$ref": "#/definitions/structuredContentStyle" } @@ -109,6 +121,9 @@ "type": "string", "const": "img" }, + "data": { + "$ref": "#/definitions/structuredContentData" + }, "path": { "type": "string", "description": "Path to the image file in the archive." @@ -175,6 +190,13 @@ } ] }, + "structuredContentData": { + "type": "object", + "description": "Generic data attributes that should be added to the element.", + "additionalProperties": { + "type": "string" + } + }, "structuredContentStyle": { "type": "object", "additionalProperties": false, diff --git a/ext/js/display/sandbox/structured-content-generator.js b/ext/js/display/sandbox/structured-content-generator.js index 82bf0421..8aa34bb2 100644 --- a/ext/js/display/sandbox/structured-content-generator.js +++ b/ext/js/display/sandbox/structured-content-generator.js @@ -169,6 +169,16 @@ class StructuredContentGenerator { return this._document.createDocumentFragment(); } + _setElementDataset(element, data) { + for (let [key, value] of Object.entries(data)) { + if (key.length > 0) { + key = `${key[0].toUpperCase()}${key.substring(1)}`; + } + key = `sc${key}`; + element.dataset[key] = value; + } + } + _setImageData(node, image, imageBackground, url, unloaded) { if (url !== null) { image.src = url; @@ -192,6 +202,8 @@ class StructuredContentGenerator { _createStructuredContentElement(tag, content, dictionary, type, hasChildren, hasStyle) { const node = this._createElement(tag, `gloss-sc-${tag}`); + const {data} = content; + if (typeof data === 'object' && data !== null) { this._setElementDataset(node, data); } switch (type) { case 'table-cell': { diff --git a/ext/js/templates/sandbox/anki-template-renderer.js b/ext/js/templates/sandbox/anki-template-renderer.js index 8a257bcc..10283761 100644 --- a/ext/js/templates/sandbox/anki-template-renderer.js +++ b/ext/js/templates/sandbox/anki-template-renderer.js @@ -38,6 +38,7 @@ class AnkiTemplateRenderer { constructor() { this._structuredContentStyleApplier = new CssStyleApplier('/data/structured-content-style.json'); this._pronunciationStyleApplier = new CssStyleApplier('/data/pronunciation-style.json'); + this._structuredContentDatasetKeyIgnorePattern = /^sc([^a-z]|$)/; this._japaneseUtil = new JapaneseUtil(null); this._templateRenderer = new TemplateRenderer(); this._ankiNoteDataCreator = new AnkiNoteDataCreator(this._japaneseUtil); @@ -462,16 +463,24 @@ class AnkiTemplateRenderer { return element; } - _getHtml(node, styleApplier) { + _getStructuredContentHtml(node) { + return this._getHtml(node, this._structuredContentStyleApplier, this._structuredContentDatasetKeyIgnorePattern); + } + + _getPronunciationHtml(node) { + return this._getHtml(node, this._pronunciationStyleApplier, null); + } + + _getHtml(node, styleApplier, datasetKeyIgnorePattern) { const container = this._getTemporaryElement(); container.appendChild(node); - this._normalizeHtml(container, styleApplier); + this._normalizeHtml(container, styleApplier, datasetKeyIgnorePattern); const result = container.innerHTML; container.textContent = ''; return result; } - _normalizeHtml(root, styleApplier) { + _normalizeHtml(root, styleApplier, datasetKeyIgnorePattern) { const {ELEMENT_NODE, TEXT_NODE} = Node; const treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT); const elements = []; @@ -492,6 +501,7 @@ class AnkiTemplateRenderer { for (const element of elements) { const {dataset} = element; for (const key of Object.keys(dataset)) { + if (datasetKeyIgnorePattern !== null && datasetKeyIgnorePattern.test(key)) { continue; } delete dataset[key]; } } @@ -541,13 +551,13 @@ class AnkiTemplateRenderer { _formatGlossaryImage(content, dictionary, data) { const structuredContentGenerator = this._createStructuredContentGenerator(data); const node = structuredContentGenerator.createDefinitionImage(content, dictionary); - return this._getHtml(node, this._structuredContentStyleApplier); + return this._getStructuredContentHtml(node); } _formatStructuredContent(content, dictionary, data) { const structuredContentGenerator = this._createStructuredContentGenerator(data); const node = structuredContentGenerator.createStructuredContent(content.content, dictionary); - return node !== null ? this._getHtml(node, this._structuredContentStyleApplier) : ''; + return node !== null ? this._getStructuredContentHtml(node) : ''; } _hasMedia(context, ...args) { @@ -575,20 +585,11 @@ class AnkiTemplateRenderer { switch (format) { case 'text': - return this._getHtml( - this._pronunciationGenerator.createPronunciationText(morae, downstepPosition, nasalPositions, devoicePositions), - this._pronunciationStyleApplier - ); + return this._getPronunciationHtml(this._pronunciationGenerator.createPronunciationText(morae, downstepPosition, nasalPositions, devoicePositions)); case 'graph': - return this._getHtml( - this._pronunciationGenerator.createPronunciationGraph(morae, downstepPosition), - this._pronunciationStyleApplier - ); + return this._getPronunciationHtml(this._pronunciationGenerator.createPronunciationGraph(morae, downstepPosition)); case 'position': - return this._getHtml( - this._pronunciationGenerator.createPronunciationDownstepPosition(downstepPosition), - this._pronunciationStyleApplier - ); + return this._getPronunciationHtml(this._pronunciationGenerator.createPronunciationDownstepPosition(downstepPosition)); default: return ''; } diff --git a/test/data/dictionaries/valid-dictionary1/term_bank_1.json b/test/data/dictionaries/valid-dictionary1/term_bank_1.json index 58136d79..0d94d6d1 100644 --- a/test/data/dictionaries/valid-dictionary1/term_bank_1.json +++ b/test/data/dictionaries/valid-dictionary1/term_bank_1.json @@ -164,6 +164,21 @@ ] }, "margin" + ]}, + {"type": "structured-content", "content": [ + "text 1 ", + { + "tag": "span", + "data": { + "": "empty", + "a": "b", + "c": "d" + }, + "content": [ + "text 2" + ] + }, + " text 3" ]} ], 100, "P E1"