From fafa746a632b1907d9cca262f689d7bec4e0f940 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 17 Jul 2021 17:10:25 -0400 Subject: Sandbox script folders (#1837) * Move scripts * Update paths * Fix ordering * Simplify eslint rules --- ext/js/display/pronunciation-generator.js | 199 ------------------ ext/js/display/sandbox/pronunciation-generator.js | 199 ++++++++++++++++++ .../sandbox/structured-content-generator.js | 230 +++++++++++++++++++++ ext/js/display/structured-content-generator.js | 230 --------------------- 4 files changed, 429 insertions(+), 429 deletions(-) delete mode 100644 ext/js/display/pronunciation-generator.js create mode 100644 ext/js/display/sandbox/pronunciation-generator.js create mode 100644 ext/js/display/sandbox/structured-content-generator.js delete mode 100644 ext/js/display/structured-content-generator.js (limited to 'ext/js/display') diff --git a/ext/js/display/pronunciation-generator.js b/ext/js/display/pronunciation-generator.js deleted file mode 100644 index bab36add..00000000 --- a/ext/js/display/pronunciation-generator.js +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2021 Yomichan Authors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -class PronunciationGenerator { - constructor(japaneseUtil) { - this._japaneseUtil = japaneseUtil; - } - - createPronunciationText(morae, downstepPosition, nasalPositions, devoicePositions) { - const jp = this._japaneseUtil; - const nasalPositionsSet = nasalPositions.length > 0 ? new Set(nasalPositions) : null; - const devoicePositionsSet = devoicePositions.length > 0 ? new Set(devoicePositions) : null; - const container = document.createElement('span'); - container.className = 'pronunciation-text'; - for (let i = 0, ii = morae.length; i < ii; ++i) { - const i1 = i + 1; - const mora = morae[i]; - const highPitch = jp.isMoraPitchHigh(i, downstepPosition); - const highPitchNext = jp.isMoraPitchHigh(i1, downstepPosition); - const nasal = nasalPositionsSet !== null && nasalPositionsSet.has(i1); - const devoice = devoicePositionsSet !== null && devoicePositionsSet.has(i1); - - const n1 = document.createElement('span'); - n1.className = 'pronunciation-mora'; - n1.dataset.position = `${i}`; - n1.dataset.pitch = highPitch ? 'high' : 'low'; - n1.dataset.pitchNext = highPitchNext ? 'high' : 'low'; - - const characterNodes = []; - for (const character of mora) { - const n2 = document.createElement('span'); - n2.className = 'pronunciation-character'; - n2.textContent = character; - n1.appendChild(n2); - characterNodes.push(n2); - } - - if (devoice) { - n1.dataset.devoice = 'true'; - const n3 = document.createElement('span'); - n3.className = 'pronunciation-devoice-indicator'; - n1.appendChild(n3); - } - if (nasal && characterNodes.length > 0) { - n1.dataset.nasal = 'true'; - - const group = document.createElement('span'); - group.className = 'pronunciation-character-group'; - - const n2 = characterNodes[0]; - const character = n2.textContent; - - const characterInfo = jp.getKanaDiacriticInfo(character); - if (characterInfo !== null) { - n1.dataset.originalText = mora; - n2.dataset.originalText = character; - n2.textContent = characterInfo.character; - } - - let n3 = document.createElement('span'); - n3.className = 'pronunciation-nasal-diacritic'; - n3.textContent = '\u309a'; // Combining handakuten - group.appendChild(n3); - - n3 = document.createElement('span'); - n3.className = 'pronunciation-nasal-indicator'; - group.appendChild(n3); - - n2.parentNode.replaceChild(group, n2); - group.insertBefore(n2, group.firstChild); - } - - const line = document.createElement('span'); - line.className = 'pronunciation-mora-line'; - n1.appendChild(line); - - container.appendChild(n1); - } - return container; - } - - createPronunciationGraph(morae, downstepPosition) { - const jp = this._japaneseUtil; - const ii = morae.length; - - const svgns = 'http://www.w3.org/2000/svg'; - const svg = document.createElementNS(svgns, 'svg'); - svg.setAttribute('xmlns', svgns); - svg.setAttribute('class', 'pronunciation-graph'); - svg.setAttribute('focusable', 'false'); - svg.setAttribute('viewBox', `0 0 ${50 * (ii + 1)} 100`); - - if (ii <= 0) { return svg; } - - const path1 = document.createElementNS(svgns, 'path'); - svg.appendChild(path1); - - const path2 = document.createElementNS(svgns, 'path'); - svg.appendChild(path2); - - const pathPoints = []; - for (let i = 0; i < ii; ++i) { - const highPitch = jp.isMoraPitchHigh(i, downstepPosition); - const highPitchNext = jp.isMoraPitchHigh(i + 1, downstepPosition); - const x = i * 50 + 25; - const y = highPitch ? 25 : 75; - if (highPitch && !highPitchNext) { - this._addGraphDotDownstep(svg, svgns, x, y); - } else { - this._addGraphDot(svg, svgns, x, y); - } - pathPoints.push(`${x} ${y}`); - } - - path1.setAttribute('class', 'pronunciation-graph-line'); - path1.setAttribute('d', `M${pathPoints.join(' L')}`); - - pathPoints.splice(0, ii - 1); - { - const highPitch = jp.isMoraPitchHigh(ii, downstepPosition); - const x = ii * 50 + 25; - const y = highPitch ? 25 : 75; - this._addGraphTriangle(svg, svgns, x, y); - pathPoints.push(`${x} ${y}`); - } - - path2.setAttribute('class', 'pronunciation-graph-line-tail'); - path2.setAttribute('d', `M${pathPoints.join(' L')}`); - - return svg; - } - - createPronunciationDownstepNotation(downstepPosition) { - downstepPosition = `${downstepPosition}`; - - const n1 = document.createElement('span'); - n1.className = 'pronunciation-downstep-notation'; - n1.dataset.downstepPosition = downstepPosition; - - let n2 = document.createElement('span'); - n2.className = 'pronunciation-downstep-notation-prefix'; - n2.textContent = '['; - n1.appendChild(n2); - - n2 = document.createElement('span'); - n2.className = 'pronunciation-downstep-notation-number'; - n2.textContent = downstepPosition; - n1.appendChild(n2); - - n2 = document.createElement('span'); - n2.className = 'pronunciation-downstep-notation-suffix'; - n2.textContent = ']'; - n1.appendChild(n2); - - return n1; - } - - // Private - - _addGraphDot(container, svgns, x, y) { - container.appendChild(this._createGraphCircle(svgns, 'pronunciation-graph-dot', x, y, '15')); - } - - _addGraphDotDownstep(container, svgns, x, y) { - container.appendChild(this._createGraphCircle(svgns, 'pronunciation-graph-dot-downstep1', x, y, '15')); - container.appendChild(this._createGraphCircle(svgns, 'pronunciation-graph-dot-downstep2', x, y, '5')); - } - - _addGraphTriangle(container, svgns, x, y) { - const node = document.createElementNS(svgns, 'path'); - node.setAttribute('class', 'pronunciation-graph-triangle'); - node.setAttribute('d', 'M0 13 L15 -13 L-15 -13 Z'); - node.setAttribute('transform', `translate(${x},${y})`); - container.appendChild(node); - } - - _createGraphCircle(svgns, className, x, y, radius) { - const node = document.createElementNS(svgns, 'circle'); - node.setAttribute('class', className); - node.setAttribute('cx', `${x}`); - node.setAttribute('cy', `${y}`); - node.setAttribute('r', radius); - return node; - } -} diff --git a/ext/js/display/sandbox/pronunciation-generator.js b/ext/js/display/sandbox/pronunciation-generator.js new file mode 100644 index 00000000..bab36add --- /dev/null +++ b/ext/js/display/sandbox/pronunciation-generator.js @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2021 Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class PronunciationGenerator { + constructor(japaneseUtil) { + this._japaneseUtil = japaneseUtil; + } + + createPronunciationText(morae, downstepPosition, nasalPositions, devoicePositions) { + const jp = this._japaneseUtil; + const nasalPositionsSet = nasalPositions.length > 0 ? new Set(nasalPositions) : null; + const devoicePositionsSet = devoicePositions.length > 0 ? new Set(devoicePositions) : null; + const container = document.createElement('span'); + container.className = 'pronunciation-text'; + for (let i = 0, ii = morae.length; i < ii; ++i) { + const i1 = i + 1; + const mora = morae[i]; + const highPitch = jp.isMoraPitchHigh(i, downstepPosition); + const highPitchNext = jp.isMoraPitchHigh(i1, downstepPosition); + const nasal = nasalPositionsSet !== null && nasalPositionsSet.has(i1); + const devoice = devoicePositionsSet !== null && devoicePositionsSet.has(i1); + + const n1 = document.createElement('span'); + n1.className = 'pronunciation-mora'; + n1.dataset.position = `${i}`; + n1.dataset.pitch = highPitch ? 'high' : 'low'; + n1.dataset.pitchNext = highPitchNext ? 'high' : 'low'; + + const characterNodes = []; + for (const character of mora) { + const n2 = document.createElement('span'); + n2.className = 'pronunciation-character'; + n2.textContent = character; + n1.appendChild(n2); + characterNodes.push(n2); + } + + if (devoice) { + n1.dataset.devoice = 'true'; + const n3 = document.createElement('span'); + n3.className = 'pronunciation-devoice-indicator'; + n1.appendChild(n3); + } + if (nasal && characterNodes.length > 0) { + n1.dataset.nasal = 'true'; + + const group = document.createElement('span'); + group.className = 'pronunciation-character-group'; + + const n2 = characterNodes[0]; + const character = n2.textContent; + + const characterInfo = jp.getKanaDiacriticInfo(character); + if (characterInfo !== null) { + n1.dataset.originalText = mora; + n2.dataset.originalText = character; + n2.textContent = characterInfo.character; + } + + let n3 = document.createElement('span'); + n3.className = 'pronunciation-nasal-diacritic'; + n3.textContent = '\u309a'; // Combining handakuten + group.appendChild(n3); + + n3 = document.createElement('span'); + n3.className = 'pronunciation-nasal-indicator'; + group.appendChild(n3); + + n2.parentNode.replaceChild(group, n2); + group.insertBefore(n2, group.firstChild); + } + + const line = document.createElement('span'); + line.className = 'pronunciation-mora-line'; + n1.appendChild(line); + + container.appendChild(n1); + } + return container; + } + + createPronunciationGraph(morae, downstepPosition) { + const jp = this._japaneseUtil; + const ii = morae.length; + + const svgns = 'http://www.w3.org/2000/svg'; + const svg = document.createElementNS(svgns, 'svg'); + svg.setAttribute('xmlns', svgns); + svg.setAttribute('class', 'pronunciation-graph'); + svg.setAttribute('focusable', 'false'); + svg.setAttribute('viewBox', `0 0 ${50 * (ii + 1)} 100`); + + if (ii <= 0) { return svg; } + + const path1 = document.createElementNS(svgns, 'path'); + svg.appendChild(path1); + + const path2 = document.createElementNS(svgns, 'path'); + svg.appendChild(path2); + + const pathPoints = []; + for (let i = 0; i < ii; ++i) { + const highPitch = jp.isMoraPitchHigh(i, downstepPosition); + const highPitchNext = jp.isMoraPitchHigh(i + 1, downstepPosition); + const x = i * 50 + 25; + const y = highPitch ? 25 : 75; + if (highPitch && !highPitchNext) { + this._addGraphDotDownstep(svg, svgns, x, y); + } else { + this._addGraphDot(svg, svgns, x, y); + } + pathPoints.push(`${x} ${y}`); + } + + path1.setAttribute('class', 'pronunciation-graph-line'); + path1.setAttribute('d', `M${pathPoints.join(' L')}`); + + pathPoints.splice(0, ii - 1); + { + const highPitch = jp.isMoraPitchHigh(ii, downstepPosition); + const x = ii * 50 + 25; + const y = highPitch ? 25 : 75; + this._addGraphTriangle(svg, svgns, x, y); + pathPoints.push(`${x} ${y}`); + } + + path2.setAttribute('class', 'pronunciation-graph-line-tail'); + path2.setAttribute('d', `M${pathPoints.join(' L')}`); + + return svg; + } + + createPronunciationDownstepNotation(downstepPosition) { + downstepPosition = `${downstepPosition}`; + + const n1 = document.createElement('span'); + n1.className = 'pronunciation-downstep-notation'; + n1.dataset.downstepPosition = downstepPosition; + + let n2 = document.createElement('span'); + n2.className = 'pronunciation-downstep-notation-prefix'; + n2.textContent = '['; + n1.appendChild(n2); + + n2 = document.createElement('span'); + n2.className = 'pronunciation-downstep-notation-number'; + n2.textContent = downstepPosition; + n1.appendChild(n2); + + n2 = document.createElement('span'); + n2.className = 'pronunciation-downstep-notation-suffix'; + n2.textContent = ']'; + n1.appendChild(n2); + + return n1; + } + + // Private + + _addGraphDot(container, svgns, x, y) { + container.appendChild(this._createGraphCircle(svgns, 'pronunciation-graph-dot', x, y, '15')); + } + + _addGraphDotDownstep(container, svgns, x, y) { + container.appendChild(this._createGraphCircle(svgns, 'pronunciation-graph-dot-downstep1', x, y, '15')); + container.appendChild(this._createGraphCircle(svgns, 'pronunciation-graph-dot-downstep2', x, y, '5')); + } + + _addGraphTriangle(container, svgns, x, y) { + const node = document.createElementNS(svgns, 'path'); + node.setAttribute('class', 'pronunciation-graph-triangle'); + node.setAttribute('d', 'M0 13 L15 -13 L-15 -13 Z'); + node.setAttribute('transform', `translate(${x},${y})`); + container.appendChild(node); + } + + _createGraphCircle(svgns, className, x, y, radius) { + const node = document.createElementNS(svgns, 'circle'); + node.setAttribute('class', className); + node.setAttribute('cx', `${x}`); + node.setAttribute('cy', `${y}`); + node.setAttribute('r', radius); + return node; + } +} diff --git a/ext/js/display/sandbox/structured-content-generator.js b/ext/js/display/sandbox/structured-content-generator.js new file mode 100644 index 00000000..833df6f6 --- /dev/null +++ b/ext/js/display/sandbox/structured-content-generator.js @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2021 Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +class StructuredContentGenerator { + constructor(mediaLoader, document) { + this._mediaLoader = mediaLoader; + this._document = document; + } + + createStructuredContent(content, dictionary) { + if (typeof content === 'string') { + return this._createTextNode(content); + } + 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': + return this._createStructuredContentElement(tag, content, dictionary, 'simple', true, true); + case 'img': + return this.createDefinitionImage(content, dictionary); + } + return null; + } + + createDefinitionImage(data, dictionary) { + const { + path, + width, + height, + preferredWidth, + preferredHeight, + title, + pixelated, + imageRendering, + appearance, + background, + collapsed, + collapsible, + verticalAlign, + sizeUnits + } = data; + + const hasPreferredWidth = (typeof preferredWidth === 'number'); + const hasPreferredHeight = (typeof preferredHeight === 'number'); + const invAspectRatio = ( + hasPreferredWidth && hasPreferredHeight ? + preferredHeight / preferredWidth : + height / width + ); + const usedWidth = ( + hasPreferredWidth ? + preferredWidth : + (hasPreferredHeight ? preferredHeight * invAspectRatio : width) + ); + + const node = this._createElement('a', 'gloss-image-link'); + node.target = '_blank'; + node.rel = 'noreferrer noopener'; + + const imageContainer = this._createElement('span', 'gloss-image-container'); + node.appendChild(imageContainer); + + const aspectRatioSizer = this._createElement('span', 'gloss-image-sizer'); + imageContainer.appendChild(aspectRatioSizer); + + const imageBackground = this._createElement('span', 'gloss-image-background'); + imageContainer.appendChild(imageBackground); + + const image = this._createElement('img', 'gloss-image'); + image.alt = ''; + imageContainer.appendChild(image); + + const overlay = this._createElement('span', 'gloss-image-container-overlay'); + imageContainer.appendChild(overlay); + + const linkText = this._createElement('span', 'gloss-image-link-text'); + linkText.textContent = 'Image'; + node.appendChild(linkText); + + node.dataset.path = path; + node.dataset.dictionary = dictionary; + node.dataset.imageLoadState = 'not-loaded'; + node.dataset.hasAspectRatio = 'true'; + node.dataset.imageRendering = typeof imageRendering === 'string' ? imageRendering : (pixelated ? 'pixelated' : 'auto'); + node.dataset.appearance = typeof appearance === 'string' ? appearance : 'auto'; + node.dataset.background = typeof background === 'boolean' ? `${background}` : 'true'; + node.dataset.collapsed = typeof collapsed === 'boolean' ? `${collapsed}` : 'false'; + node.dataset.collapsible = typeof collapsible === 'boolean' ? `${collapsible}` : 'true'; + if (typeof verticalAlign === 'string') { + node.dataset.verticalAlign = verticalAlign; + } + if (typeof sizeUnits === 'string' && (hasPreferredWidth || hasPreferredHeight)) { + node.dataset.sizeUnits = sizeUnits; + } + + imageContainer.style.width = `${usedWidth}em`; + if (typeof title === 'string') { + imageContainer.title = title; + } + + aspectRatioSizer.style.paddingTop = `${invAspectRatio * 100.0}%`; + + if (this._mediaLoader !== null) { + this._mediaLoader.loadMedia( + path, + dictionary, + (url) => this._setImageData(node, image, imageBackground, url, false), + () => this._setImageData(node, image, imageBackground, null, true) + ); + } + + return node; + } + + // Private + + _createElement(tagName, className) { + const node = this._document.createElement(tagName); + node.className = className; + return node; + } + + _createTextNode(data) { + return this._document.createTextNode(data); + } + + _createDocumentFragment() { + return this._document.createDocumentFragment(); + } + + _setImageData(node, image, imageBackground, url, unloaded) { + if (url !== null) { + image.src = url; + node.href = url; + node.dataset.imageLoadState = 'loaded'; + imageBackground.style.setProperty('--image', `url("${url}")`); + } else { + image.removeAttribute('src'); + node.removeAttribute('href'); + node.dataset.imageLoadState = unloaded ? 'unloaded' : 'load-error'; + imageBackground.style.removeProperty('--image'); + } + } + + _createStructuredContentTableElement(tag, content, dictionary) { + const container = this._createElement('div', '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 = this._createElement(tag, `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(' '); + } + } +} diff --git a/ext/js/display/structured-content-generator.js b/ext/js/display/structured-content-generator.js deleted file mode 100644 index 833df6f6..00000000 --- a/ext/js/display/structured-content-generator.js +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2021 Yomichan Authors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -class StructuredContentGenerator { - constructor(mediaLoader, document) { - this._mediaLoader = mediaLoader; - this._document = document; - } - - createStructuredContent(content, dictionary) { - if (typeof content === 'string') { - return this._createTextNode(content); - } - 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': - return this._createStructuredContentElement(tag, content, dictionary, 'simple', true, true); - case 'img': - return this.createDefinitionImage(content, dictionary); - } - return null; - } - - createDefinitionImage(data, dictionary) { - const { - path, - width, - height, - preferredWidth, - preferredHeight, - title, - pixelated, - imageRendering, - appearance, - background, - collapsed, - collapsible, - verticalAlign, - sizeUnits - } = data; - - const hasPreferredWidth = (typeof preferredWidth === 'number'); - const hasPreferredHeight = (typeof preferredHeight === 'number'); - const invAspectRatio = ( - hasPreferredWidth && hasPreferredHeight ? - preferredHeight / preferredWidth : - height / width - ); - const usedWidth = ( - hasPreferredWidth ? - preferredWidth : - (hasPreferredHeight ? preferredHeight * invAspectRatio : width) - ); - - const node = this._createElement('a', 'gloss-image-link'); - node.target = '_blank'; - node.rel = 'noreferrer noopener'; - - const imageContainer = this._createElement('span', 'gloss-image-container'); - node.appendChild(imageContainer); - - const aspectRatioSizer = this._createElement('span', 'gloss-image-sizer'); - imageContainer.appendChild(aspectRatioSizer); - - const imageBackground = this._createElement('span', 'gloss-image-background'); - imageContainer.appendChild(imageBackground); - - const image = this._createElement('img', 'gloss-image'); - image.alt = ''; - imageContainer.appendChild(image); - - const overlay = this._createElement('span', 'gloss-image-container-overlay'); - imageContainer.appendChild(overlay); - - const linkText = this._createElement('span', 'gloss-image-link-text'); - linkText.textContent = 'Image'; - node.appendChild(linkText); - - node.dataset.path = path; - node.dataset.dictionary = dictionary; - node.dataset.imageLoadState = 'not-loaded'; - node.dataset.hasAspectRatio = 'true'; - node.dataset.imageRendering = typeof imageRendering === 'string' ? imageRendering : (pixelated ? 'pixelated' : 'auto'); - node.dataset.appearance = typeof appearance === 'string' ? appearance : 'auto'; - node.dataset.background = typeof background === 'boolean' ? `${background}` : 'true'; - node.dataset.collapsed = typeof collapsed === 'boolean' ? `${collapsed}` : 'false'; - node.dataset.collapsible = typeof collapsible === 'boolean' ? `${collapsible}` : 'true'; - if (typeof verticalAlign === 'string') { - node.dataset.verticalAlign = verticalAlign; - } - if (typeof sizeUnits === 'string' && (hasPreferredWidth || hasPreferredHeight)) { - node.dataset.sizeUnits = sizeUnits; - } - - imageContainer.style.width = `${usedWidth}em`; - if (typeof title === 'string') { - imageContainer.title = title; - } - - aspectRatioSizer.style.paddingTop = `${invAspectRatio * 100.0}%`; - - if (this._mediaLoader !== null) { - this._mediaLoader.loadMedia( - path, - dictionary, - (url) => this._setImageData(node, image, imageBackground, url, false), - () => this._setImageData(node, image, imageBackground, null, true) - ); - } - - return node; - } - - // Private - - _createElement(tagName, className) { - const node = this._document.createElement(tagName); - node.className = className; - return node; - } - - _createTextNode(data) { - return this._document.createTextNode(data); - } - - _createDocumentFragment() { - return this._document.createDocumentFragment(); - } - - _setImageData(node, image, imageBackground, url, unloaded) { - if (url !== null) { - image.src = url; - node.href = url; - node.dataset.imageLoadState = 'loaded'; - imageBackground.style.setProperty('--image', `url("${url}")`); - } else { - image.removeAttribute('src'); - node.removeAttribute('href'); - node.dataset.imageLoadState = unloaded ? 'unloaded' : 'load-error'; - imageBackground.style.removeProperty('--image'); - } - } - - _createStructuredContentTableElement(tag, content, dictionary) { - const container = this._createElement('div', '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 = this._createElement(tag, `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(' '); - } - } -} -- cgit v1.2.3