diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2024-03-04 07:43:31 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-04 12:43:31 +0000 |
commit | 4fe881d68d4c1182bee2e78a559c2064aaf48b0d (patch) | |
tree | 00e8b209b90cae66ca289f5ea3e0c466e17d6fd3 /ext/js/display/sandbox | |
parent | 81fc2bd6d063db92f90171722e8129875bdb56cd (diff) |
Move sandbox files (#731)
* Move sandbox files
* Update order
Diffstat (limited to 'ext/js/display/sandbox')
-rw-r--r-- | ext/js/display/sandbox/pronunciation-generator.js | 236 | ||||
-rw-r--r-- | ext/js/display/sandbox/structured-content-generator.js | 464 |
2 files changed, 0 insertions, 700 deletions
diff --git a/ext/js/display/sandbox/pronunciation-generator.js b/ext/js/display/sandbox/pronunciation-generator.js deleted file mode 100644 index f28520be..00000000 --- a/ext/js/display/sandbox/pronunciation-generator.js +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2023-2024 Yomitan Authors - * Copyright (C) 2021-2022 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 <https://www.gnu.org/licenses/>. - */ - -import {getKanaDiacriticInfo, isMoraPitchHigh} from '../../language/ja/japanese.js'; - -/** - * @param {string[]} morae - * @param {number} downstepPosition - * @param {number[]} nasalPositions - * @param {number[]} devoicePositions - * @returns {HTMLSpanElement} - */ -export function createPronunciationText(morae, downstepPosition, nasalPositions, devoicePositions) { - 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 = isMoraPitchHigh(i, downstepPosition); - const highPitchNext = 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 = /** @type {string} */ (n2.textContent); - - const characterInfo = 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); - - /** @type {ParentNode} */ (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; -} - -/** - * @param {string[]} morae - * @param {number} downstepPosition - * @returns {SVGSVGElement} - */ -export function createPronunciationGraph(morae, downstepPosition) { - 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 = isMoraPitchHigh(i, downstepPosition); - const highPitchNext = isMoraPitchHigh(i + 1, downstepPosition); - const x = i * 50 + 25; - const y = highPitch ? 25 : 75; - if (highPitch && !highPitchNext) { - addGraphDotDownstep(svg, svgns, x, y); - } else { - 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 = isMoraPitchHigh(ii, downstepPosition); - const x = ii * 50 + 25; - const y = highPitch ? 25 : 75; - 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; -} - -/** - * @param {number} downstepPosition - * @returns {HTMLSpanElement} - */ -export function createPronunciationDownstepPosition(downstepPosition) { - const downstepPositionString = `${downstepPosition}`; - - const n1 = document.createElement('span'); - n1.className = 'pronunciation-downstep-notation'; - n1.dataset.downstepPosition = downstepPositionString; - - 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 = downstepPositionString; - n1.appendChild(n2); - - n2 = document.createElement('span'); - n2.className = 'pronunciation-downstep-notation-suffix'; - n2.textContent = ']'; - n1.appendChild(n2); - - return n1; -} - -// Private - -/** - * @param {Element} container - * @param {string} svgns - * @param {number} x - * @param {number} y - */ -function addGraphDot(container, svgns, x, y) { - container.appendChild(createGraphCircle(svgns, 'pronunciation-graph-dot', x, y, '15')); -} - -/** - * @param {Element} container - * @param {string} svgns - * @param {number} x - * @param {number} y - */ -function addGraphDotDownstep(container, svgns, x, y) { - container.appendChild(createGraphCircle(svgns, 'pronunciation-graph-dot-downstep1', x, y, '15')); - container.appendChild(createGraphCircle(svgns, 'pronunciation-graph-dot-downstep2', x, y, '5')); -} - -/** - * @param {Element} container - * @param {string} svgns - * @param {number} x - * @param {number} y - */ -function 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); -} - -/** - * @param {string} svgns - * @param {string} className - * @param {number} x - * @param {number} y - * @param {string} radius - * @returns {Element} - */ -function 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 deleted file mode 100644 index 90a47158..00000000 --- a/ext/js/display/sandbox/structured-content-generator.js +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright (C) 2023-2024 Yomitan Authors - * Copyright (C) 2021-2022 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 <https://www.gnu.org/licenses/>. - */ - -import {getLanguageFromText} from '../../language/text-utilities.js'; - -export class StructuredContentGenerator { - /** - * @param {import('../../display/display-content-manager.js').DisplayContentManager|import('../../templates/sandbox/anki-template-renderer-content-manager.js').AnkiTemplateRendererContentManager} contentManager - * @param {Document} document - */ - constructor(contentManager, document) { - /** @type {import('../../display/display-content-manager.js').DisplayContentManager|import('../../templates/sandbox/anki-template-renderer-content-manager.js').AnkiTemplateRendererContentManager} */ - this._contentManager = contentManager; - /** @type {Document} */ - this._document = document; - } - - /** - * @param {HTMLElement} node - * @param {import('structured-content').Content} content - * @param {string} dictionary - */ - appendStructuredContent(node, content, dictionary) { - node.classList.add('structured-content'); - this._appendStructuredContent(node, content, dictionary, null); - } - - /** - * @param {import('structured-content').Content} content - * @param {string} dictionary - * @returns {HTMLElement} - */ - createStructuredContent(content, dictionary) { - const node = this._createElement('span', 'structured-content'); - this._appendStructuredContent(node, content, dictionary, null); - return node; - } - - /** - * @param {import('structured-content').ImageElement|import('dictionary-data').TermGlossaryImage} data - * @param {string} dictionary - * @returns {HTMLAnchorElement} - */ - createDefinitionImage(data, dictionary) { - const { - path, - width = 100, - height = 100, - preferredWidth, - preferredHeight, - title, - alt, - pixelated, - imageRendering, - appearance, - background, - collapsed, - collapsible, - verticalAlign, - border, - borderRadius, - 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 = /** @type {HTMLAnchorElement} */ (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 = /** @type {HTMLImageElement} */ (this._createElement('img', 'gloss-image')); - image.alt = typeof alt === 'string' ? 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 border === 'string') { imageContainer.style.border = border; } - if (typeof borderRadius === 'string') { imageContainer.style.borderRadius = borderRadius; } - if (typeof title === 'string') { - imageContainer.title = title; - } - - aspectRatioSizer.style.paddingTop = `${invAspectRatio * 100}%`; - - if (this._contentManager !== null) { - this._contentManager.loadMedia( - path, - dictionary, - (url) => this._setImageData(node, image, imageBackground, url, false), - () => this._setImageData(node, image, imageBackground, null, true) - ); - } - - return node; - } - - // Private - - /** - * @param {HTMLElement} container - * @param {import('structured-content').Content|undefined} content - * @param {string} dictionary - * @param {?string} language - */ - _appendStructuredContent(container, content, dictionary, language) { - if (typeof content === 'string') { - if (content.length > 0) { - container.appendChild(this._createTextNode(content)); - if (language === null) { - const language2 = getLanguageFromText(content); - if (language2 !== null) { - container.lang = language2; - } - } - } - 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); - } - } - - /** - * @param {string} tagName - * @param {string} className - * @returns {HTMLElement} - */ - _createElement(tagName, className) { - const node = this._document.createElement(tagName); - node.className = className; - return node; - } - - /** - * @param {string} data - * @returns {Text} - */ - _createTextNode(data) { - return this._document.createTextNode(data); - } - - /** - * @param {HTMLElement} element - * @param {import('structured-content').Data} data - */ - _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}`; - try { - element.dataset[key] = value; - } catch (e) { - // DOMException if key is malformed - } - } - } - - /** - * @param {HTMLAnchorElement} node - * @param {HTMLImageElement} image - * @param {HTMLElement} imageBackground - * @param {?string} url - * @param {boolean} unloaded - */ - _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'); - } - } - - /** - * @param {import('structured-content').Element} content - * @param {string} dictionary - * @param {?string} language - * @returns {?HTMLElement} - */ - _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; - } - - /** - * @param {string} tag - * @param {import('structured-content').UnstyledElement} content - * @param {string} dictionary - * @param {?string} language - * @returns {HTMLElement} - */ - _createStructuredContentTableElement(tag, content, dictionary, language) { - const container = this._createElement('div', 'gloss-sc-table-container'); - const table = this._createStructuredContentElement(tag, content, dictionary, language, 'table', true, false); - container.appendChild(table); - return container; - } - - /** - * @param {string} tag - * @param {import('structured-content').StyledElement|import('structured-content').UnstyledElement|import('structured-content').TableElement|import('structured-content').LineBreak} content - * @param {string} dictionary - * @param {?string} language - * @param {'simple'|'table'|'table-cell'} type - * @param {boolean} hasChildren - * @param {boolean} hasStyle - * @returns {HTMLElement} - */ - _createStructuredContentElement(tag, content, dictionary, language, type, hasChildren, hasStyle) { - const node = this._createElement(tag, `gloss-sc-${tag}`); - const {data, lang} = content; - if (typeof data === 'object' && data !== null) { this._setElementDataset(node, data); } - if (typeof lang === 'string') { - node.lang = lang; - language = lang; - } - switch (type) { - case 'table-cell': - { - const cell = /** @type {HTMLTableCellElement} */ (node); - const {colSpan, rowSpan} = /** @type {import('structured-content').TableElement} */ (content); - if (typeof colSpan === 'number') { cell.colSpan = colSpan; } - if (typeof rowSpan === 'number') { cell.rowSpan = rowSpan; } - } - break; - } - if (hasStyle) { - const {style, title} = /** @type {import('structured-content').StyledElement} */ (content); - if (typeof style === 'object' && style !== null) { - this._setStructuredContentElementStyle(node, style); - } - if (typeof title === 'string') { node.title = title; } - } - if (hasChildren) { - this._appendStructuredContent(node, content.content, dictionary, language); - } - return node; - } - - /** - * @param {HTMLElement} node - * @param {import('structured-content').StructuredContentStyle} contentStyle - */ - _setStructuredContentElementStyle(node, contentStyle) { - const {style} = node; - const { - fontStyle, - fontWeight, - fontSize, - color, - background, - backgroundColor, - textDecorationLine, - textDecorationStyle, - textDecorationColor, - borderColor, - borderStyle, - borderRadius, - borderWidth, - clipPath, - verticalAlign, - textAlign, - textEmphasis, - textShadow, - margin, - marginTop, - marginLeft, - marginRight, - marginBottom, - padding, - paddingTop, - paddingLeft, - paddingRight, - paddingBottom, - wordBreak, - whiteSpace, - cursor, - listStyleType - } = contentStyle; - if (typeof fontStyle === 'string') { style.fontStyle = fontStyle; } - if (typeof fontWeight === 'string') { style.fontWeight = fontWeight; } - if (typeof fontSize === 'string') { style.fontSize = fontSize; } - if (typeof color === 'string') { style.color = color; } - if (typeof background === 'string') { style.background = background; } - if (typeof backgroundColor === 'string') { style.backgroundColor = backgroundColor; } - if (typeof verticalAlign === 'string') { style.verticalAlign = verticalAlign; } - if (typeof textAlign === 'string') { style.textAlign = textAlign; } - if (typeof textEmphasis === 'string') { style.textEmphasis = textEmphasis; } - if (typeof textShadow === 'string') { style.textShadow = textShadow; } - if (typeof textDecorationLine === 'string') { - style.textDecoration = textDecorationLine; - } else if (Array.isArray(textDecorationLine)) { - style.textDecoration = textDecorationLine.join(' '); - } - if (typeof textDecorationStyle === 'string') { - style.textDecorationStyle = textDecorationStyle; - } - if (typeof textDecorationColor === 'string') { - style.textDecorationColor = textDecorationColor; - } - if (typeof borderColor === 'string') { style.borderColor = borderColor; } - if (typeof borderStyle === 'string') { style.borderStyle = borderStyle; } - if (typeof borderRadius === 'string') { style.borderRadius = borderRadius; } - if (typeof borderWidth === 'string') { style.borderWidth = borderWidth; } - if (typeof clipPath === 'string') { style.clipPath = clipPath; } - if (typeof margin === 'string') { style.margin = margin; } - if (typeof marginTop === 'number') { style.marginTop = `${marginTop}em`; } - if (typeof marginTop === 'string') { style.marginTop = marginTop; } - if (typeof marginLeft === 'number') { style.marginLeft = `${marginLeft}em`; } - if (typeof marginLeft === 'string') { style.marginLeft = marginLeft; } - if (typeof marginRight === 'number') { style.marginRight = `${marginRight}em`; } - if (typeof marginRight === 'string') { style.marginRight = marginRight; } - if (typeof marginBottom === 'number') { style.marginBottom = `${marginBottom}em`; } - if (typeof marginBottom === 'string') { style.marginBottom = marginBottom; } - if (typeof padding === 'string') { style.padding = padding; } - if (typeof paddingTop === 'string') { style.paddingTop = paddingTop; } - if (typeof paddingLeft === 'string') { style.paddingLeft = paddingLeft; } - if (typeof paddingRight === 'string') { style.paddingRight = paddingRight; } - if (typeof paddingBottom === 'string') { style.paddingBottom = paddingBottom; } - if (typeof wordBreak === 'string') { style.wordBreak = wordBreak; } - if (typeof whiteSpace === 'string') { style.whiteSpace = whiteSpace; } - if (typeof cursor === 'string') { style.cursor = cursor; } - if (typeof listStyleType === 'string') { style.listStyleType = listStyleType; } - } - - /** - * @param {import('structured-content').LinkElement} content - * @param {string} dictionary - * @param {?string} language - * @returns {HTMLAnchorElement} - */ - _createLinkElement(content, dictionary, language) { - let {href} = content; - const internal = href.startsWith('?'); - if (internal) { - href = `${location.protocol}//${location.host}/search.html${href.length > 1 ? href : ''}`; - } - - const node = /** @type {HTMLAnchorElement} */ (this._createElement('a', 'gloss-link')); - node.dataset.external = `${!internal}`; - - const text = this._createElement('span', 'gloss-link-text'); - node.appendChild(text); - - const {lang} = content; - if (typeof lang === 'string') { - node.lang = lang; - language = lang; - } - - this._appendStructuredContent(text, content.content, dictionary, language); - - if (!internal) { - const icon = this._createElement('span', 'gloss-link-external-icon icon'); - icon.dataset.icon = 'external-link'; - node.appendChild(icon); - } - - this._contentManager.prepareLink(node, href, internal); - return node; - } -} |