Structured content auto language (#2131)
* Pass JapaneseUtil instance to StructuredContentGenerator * Move body of createStructuredContent to an internal function * Create _createStructuredContentGenericElement * Wrap structured content in a <span> * Change _createStructuredContent to _appendStructuredContent * Add public appendStructuredContent function * Add missing return * Remove unused _createDocumentFragment * Automatically assign lang=ja for content with Japanese characters without an explicit language * Add test
This commit is contained in:
parent
6a74746113
commit
5dcc2315d2
@ -28,7 +28,7 @@ class DisplayGenerator {
|
|||||||
this._contentManager = contentManager;
|
this._contentManager = contentManager;
|
||||||
this._hotkeyHelpController = hotkeyHelpController;
|
this._hotkeyHelpController = hotkeyHelpController;
|
||||||
this._templates = null;
|
this._templates = null;
|
||||||
this._structuredContentGenerator = new StructuredContentGenerator(this._contentManager, document);
|
this._structuredContentGenerator = new StructuredContentGenerator(this._contentManager, japaneseUtil, document);
|
||||||
this._pronunciationGenerator = new PronunciationGenerator(japaneseUtil);
|
this._pronunciationGenerator = new PronunciationGenerator(japaneseUtil);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,11 +347,8 @@ class DisplayGenerator {
|
|||||||
|
|
||||||
_createTermDefinitionEntryStructuredContent(content, dictionary) {
|
_createTermDefinitionEntryStructuredContent(content, dictionary) {
|
||||||
const node = this._templates.instantiate('gloss-item');
|
const node = this._templates.instantiate('gloss-item');
|
||||||
const child = this._structuredContentGenerator.createStructuredContent(content, dictionary);
|
|
||||||
if (child !== null) {
|
|
||||||
const contentContainer = node.querySelector('.gloss-content');
|
const contentContainer = node.querySelector('.gloss-content');
|
||||||
contentContainer.appendChild(child);
|
this._structuredContentGenerator.appendStructuredContent(contentContainer, content, dictionary);
|
||||||
}
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,56 +16,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class StructuredContentGenerator {
|
class StructuredContentGenerator {
|
||||||
constructor(contentManager, document) {
|
constructor(contentManager, japaneseUtil, document) {
|
||||||
this._contentManager = contentManager;
|
this._contentManager = contentManager;
|
||||||
|
this._japaneseUtil = japaneseUtil;
|
||||||
this._document = document;
|
this._document = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendStructuredContent(node, content, dictionary) {
|
||||||
|
node.classList.add('structured-content');
|
||||||
|
this._appendStructuredContent(node, content, dictionary, null);
|
||||||
|
}
|
||||||
|
|
||||||
createStructuredContent(content, dictionary) {
|
createStructuredContent(content, dictionary) {
|
||||||
if (typeof content === 'string') {
|
const node = this._createElement('span', 'structured-content');
|
||||||
return this._createTextNode(content);
|
this._appendStructuredContent(node, content, dictionary, null);
|
||||||
}
|
return node;
|
||||||
if (!(typeof content === 'object' && content !== null)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (Array.isArray(content)) {
|
|
||||||
const fragment = this._createDocumentFragment();
|
|
||||||
for (const item of content) {
|
|
||||||
const child = this.createStructuredContent(item, dictionary);
|
|
||||||
if (child !== null) { fragment.appendChild(child); }
|
|
||||||
}
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
const {tag} = content;
|
|
||||||
switch (tag) {
|
|
||||||
case 'br':
|
|
||||||
return this._createStructuredContentElement(tag, content, dictionary, 'simple', false, false);
|
|
||||||
case 'ruby':
|
|
||||||
case 'rt':
|
|
||||||
case 'rp':
|
|
||||||
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':
|
|
||||||
case 'ol':
|
|
||||||
case 'ul':
|
|
||||||
case 'li':
|
|
||||||
return this._createStructuredContentElement(tag, content, dictionary, 'simple', true, true);
|
|
||||||
case 'img':
|
|
||||||
return this.createDefinitionImage(content, dictionary);
|
|
||||||
case 'a':
|
|
||||||
return this._createLinkElement(content, dictionary);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createDefinitionImage(data, dictionary) {
|
createDefinitionImage(data, dictionary) {
|
||||||
@ -160,6 +125,31 @@ class StructuredContentGenerator {
|
|||||||
|
|
||||||
// Private
|
// Private
|
||||||
|
|
||||||
|
_appendStructuredContent(container, content, dictionary, language) {
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
if (content.length > 0) {
|
||||||
|
container.appendChild(this._createTextNode(content));
|
||||||
|
if (language === null && this._japaneseUtil.isStringPartiallyJapanese(content)) {
|
||||||
|
container.lang = 'ja';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(typeof content === 'object' && content !== null)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(content)) {
|
||||||
|
for (const item of content) {
|
||||||
|
this._appendStructuredContent(container, item, dictionary, language);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const node = this._createStructuredContentGenericElement(content, dictionary, language);
|
||||||
|
if (node !== null) {
|
||||||
|
container.appendChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_createElement(tagName, className) {
|
_createElement(tagName, className) {
|
||||||
const node = this._document.createElement(tagName);
|
const node = this._document.createElement(tagName);
|
||||||
node.className = className;
|
node.className = className;
|
||||||
@ -170,10 +160,6 @@ class StructuredContentGenerator {
|
|||||||
return this._document.createTextNode(data);
|
return this._document.createTextNode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createDocumentFragment() {
|
|
||||||
return this._document.createDocumentFragment();
|
|
||||||
}
|
|
||||||
|
|
||||||
_setElementDataset(element, data) {
|
_setElementDataset(element, data) {
|
||||||
for (let [key, value] of Object.entries(data)) {
|
for (let [key, value] of Object.entries(data)) {
|
||||||
if (key.length > 0) {
|
if (key.length > 0) {
|
||||||
@ -198,18 +184,54 @@ class StructuredContentGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_createStructuredContentTableElement(tag, content, dictionary) {
|
_createStructuredContentGenericElement(content, dictionary, language) {
|
||||||
|
const {tag} = content;
|
||||||
|
switch (tag) {
|
||||||
|
case 'br':
|
||||||
|
return this._createStructuredContentElement(tag, content, dictionary, language, 'simple', false, false);
|
||||||
|
case 'ruby':
|
||||||
|
case 'rt':
|
||||||
|
case 'rp':
|
||||||
|
return this._createStructuredContentElement(tag, content, dictionary, language, 'simple', true, false);
|
||||||
|
case 'table':
|
||||||
|
return this._createStructuredContentTableElement(tag, content, dictionary, language);
|
||||||
|
case 'thead':
|
||||||
|
case 'tbody':
|
||||||
|
case 'tfoot':
|
||||||
|
case 'tr':
|
||||||
|
return this._createStructuredContentElement(tag, content, dictionary, language, 'table', true, false);
|
||||||
|
case 'th':
|
||||||
|
case 'td':
|
||||||
|
return this._createStructuredContentElement(tag, content, dictionary, language, 'table-cell', true, true);
|
||||||
|
case 'div':
|
||||||
|
case 'span':
|
||||||
|
case 'ol':
|
||||||
|
case 'ul':
|
||||||
|
case 'li':
|
||||||
|
return this._createStructuredContentElement(tag, content, dictionary, language, 'simple', true, true);
|
||||||
|
case 'img':
|
||||||
|
return this.createDefinitionImage(content, dictionary);
|
||||||
|
case 'a':
|
||||||
|
return this._createLinkElement(content, dictionary, language);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createStructuredContentTableElement(tag, content, dictionary, language) {
|
||||||
const container = this._createElement('div', 'gloss-sc-table-container');
|
const container = this._createElement('div', 'gloss-sc-table-container');
|
||||||
const table = this._createStructuredContentElement(tag, content, dictionary, 'table', true, false);
|
const table = this._createStructuredContentElement(tag, content, dictionary, language, 'table', true, false);
|
||||||
container.appendChild(table);
|
container.appendChild(table);
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createStructuredContentElement(tag, content, dictionary, type, hasChildren, hasStyle) {
|
_createStructuredContentElement(tag, content, dictionary, language, type, hasChildren, hasStyle) {
|
||||||
const node = this._createElement(tag, `gloss-sc-${tag}`);
|
const node = this._createElement(tag, `gloss-sc-${tag}`);
|
||||||
const {data, lang} = content;
|
const {data, lang} = content;
|
||||||
if (typeof data === 'object' && data !== null) { this._setElementDataset(node, data); }
|
if (typeof data === 'object' && data !== null) { this._setElementDataset(node, data); }
|
||||||
if (typeof lang === 'string') { node.lang = lang; }
|
if (typeof lang === 'string') {
|
||||||
|
node.lang = lang;
|
||||||
|
language = lang;
|
||||||
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'table-cell':
|
case 'table-cell':
|
||||||
{
|
{
|
||||||
@ -226,8 +248,7 @@ class StructuredContentGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
const child = this.createStructuredContent(content.content, dictionary);
|
this._appendStructuredContent(node, content.content, dictionary, language);
|
||||||
if (child !== null) { node.appendChild(child); }
|
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -262,7 +283,7 @@ class StructuredContentGenerator {
|
|||||||
if (typeof listStyleType === 'string') { style.listStyleType = listStyleType; }
|
if (typeof listStyleType === 'string') { style.listStyleType = listStyleType; }
|
||||||
}
|
}
|
||||||
|
|
||||||
_createLinkElement(content, dictionary) {
|
_createLinkElement(content, dictionary, language) {
|
||||||
let {href} = content;
|
let {href} = content;
|
||||||
const internal = href.startsWith('?');
|
const internal = href.startsWith('?');
|
||||||
if (internal) {
|
if (internal) {
|
||||||
@ -276,10 +297,12 @@ class StructuredContentGenerator {
|
|||||||
node.appendChild(text);
|
node.appendChild(text);
|
||||||
|
|
||||||
const {lang} = content;
|
const {lang} = content;
|
||||||
if (typeof lang === 'string') { node.lang = lang; }
|
if (typeof lang === 'string') {
|
||||||
|
node.lang = lang;
|
||||||
|
language = lang;
|
||||||
|
}
|
||||||
|
|
||||||
const child = this.createStructuredContent(content.content, dictionary);
|
this._appendStructuredContent(text, content.content, dictionary, language);
|
||||||
if (child !== null) { text.appendChild(child); }
|
|
||||||
|
|
||||||
if (!internal) {
|
if (!internal) {
|
||||||
const icon = this._createElement('span', 'gloss-link-external-icon icon');
|
const icon = this._createElement('span', 'gloss-link-external-icon icon');
|
||||||
|
@ -536,7 +536,7 @@ class AnkiTemplateRenderer {
|
|||||||
|
|
||||||
_createStructuredContentGenerator(data) {
|
_createStructuredContentGenerator(data) {
|
||||||
const contentManager = new AnkiTemplateRendererContentManager(this._mediaProvider, data);
|
const contentManager = new AnkiTemplateRendererContentManager(this._mediaProvider, data);
|
||||||
const instance = new StructuredContentGenerator(contentManager, document);
|
const instance = new StructuredContentGenerator(contentManager, this._japaneseUtil, document);
|
||||||
this._cleanupCallbacks.push(() => contentManager.unloadAll());
|
this._cleanupCallbacks.push(() => contentManager.unloadAll());
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
@ -243,6 +243,7 @@
|
|||||||
]}
|
]}
|
||||||
]},
|
]},
|
||||||
{"type": "structured-content", "content": [
|
{"type": "structured-content", "content": [
|
||||||
|
{"tag": "div", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (auto lang)"},
|
||||||
{"tag": "div", "lang": "?????", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (invalid lang)"},
|
{"tag": "div", "lang": "?????", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (invalid lang)"},
|
||||||
{"tag": "div", "lang": "ja-JP", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (lang=ja-JP)"},
|
{"tag": "div", "lang": "ja-JP", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (lang=ja-JP)"},
|
||||||
{"tag": "div", "lang": "zh-CN", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (lang=zh-CN)"},
|
{"tag": "div", "lang": "zh-CN", "style": {"fontSize": "xxx-large"}, "content": "直次茶冷 (lang=zh-CN)"},
|
||||||
|
Loading…
Reference in New Issue
Block a user