diff options
Diffstat (limited to 'src/content/dependencies/transformContent.js')
-rw-r--r-- | src/content/dependencies/transformContent.js | 200 |
1 files changed, 163 insertions, 37 deletions
diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js index 1bbd45e2..e9a75744 100644 --- a/src/content/dependencies/transformContent.js +++ b/src/content/dependencies/transformContent.js @@ -1,5 +1,8 @@ +import {basename} from 'node:path'; + +import {logWarn} from '#cli'; import {bindFind} from '#find'; -import {replacerSpec, parseInput} from '#replacer'; +import {replacerSpec, parseContentNodes} from '#replacer'; import {Marked} from 'marked'; import striptags from 'striptags'; @@ -46,24 +49,44 @@ function getPlaceholder(node, content) { return {type: 'text', data: content.slice(node.i, node.iEnd)}; } +function getArg(node, argKey) { + return ( + node.data.args + ?.find(({key}) => key.data === argKey) + ?.value ?? + null); +} + export default { contentDependencies: [ ...( Object.values(replacerSpec) .map(description => description.link) .filter(Boolean)), + 'image', + 'generateTextWithTooltip', + 'generateTooltip', 'linkExternal', ], - extraDependencies: ['html', 'language', 'to', 'wikiData'], + extraDependencies: [ + 'html', + 'language', + 'niceShowAggregate', + 'to', + 'wikiData', + ], sprawl(wikiData, content) { - const find = bindFind(wikiData); + const find = bindFind(wikiData, {mode: 'quiet'}); - const parsedNodes = parseInput(content ?? ''); + const {result: parsedNodes, error} = + parseContentNodes(content ?? '', {errorMode: 'return'}); return { + error, + nodes: parsedNodes .map(node => { if (node.type !== 'tag') { @@ -134,6 +157,30 @@ export default { return {i: node.i, iEnd: node.iEnd, type: 'internal-link', data}; } + if (replacerKey === 'tooltip') { + // TODO: Again, no recursive nodes. Sorry! + // const enteredLabel = node.data.label && transformNode(node.data.label, opts); + const enteredLabel = node.data.label?.data; + + return { + i: node.i, + iEnd: node.iEnd, + type: 'tooltip', + data: { + tooltip: + replacerValue ?? '(empty tooltip...)', + + label: + enteredLabel ?? '(tooltip without label)', + + link: + (getArg(node, 'link') + ? getArg(node, 'link')[0].data + : null), + }, + }; + } + // This will be another {type: 'tag'} node which gets processed in // generate. Extract replacerKey and replacerValue now, since it'd // be a pain to deal with later. @@ -153,6 +200,9 @@ export default { return { content, + error: + sprawl.error, + nodes: sprawl.nodes .map(node => { @@ -191,6 +241,12 @@ export default { : getPlaceholder(node, content)); return { + textWithTooltip: + relation('generateTextWithTooltip'), + + tooltip: + relation('generateTooltip'), + internalLinks: nodes .filter(({type}) => type === 'internal-link') @@ -209,11 +265,15 @@ export default { externalLinks: nodes .filter(({type}) => type === 'external-link') - .map(node => { - const {href} = node.data; + .map(({data: {href}}) => + relation('linkExternal', href)), - return relation('linkExternal', href); - }), + externalLinksForTooltipNodes: + nodes + .filter(({type}) => type === 'tooltip') + .filter(({data}) => data.link) + .map(({data: {link: href}}) => + relation('linkExternal', href)), images: nodes @@ -255,10 +315,16 @@ export default { }, }, - generate(data, relations, slots, {html, language, to}) { + generate(data, relations, slots, {html, language, niceShowAggregate, to}) { + if (data.error) { + logWarn`Error in content text.`; + niceShowAggregate(data.error); + } + let imageIndex = 0; let internalLinkIndex = 0; let externalLinkIndex = 0; + let externalLinkForTooltipNodeIndex = 0; let offsetTextNode = 0; @@ -313,9 +379,8 @@ export default { height && {height}, style && {style}, - align === 'center' && - !link && - {class: 'align-center'}, + align && !link && + {class: 'align-' + align}, pixelate && {class: 'pixelate'}); @@ -326,8 +391,8 @@ export default { {href: link}, {target: '_blank'}, - align === 'center' && - {class: 'align-center'}, + align && + {class: 'align-' + align}, {title: language.encapsulate('misc.external.opensInNewTab', capsule => @@ -377,8 +442,8 @@ export default { inline: false, data: html.tag('div', {class: 'content-image-container'}, - align === 'center' && - {class: 'align-center'}, + align && + {class: 'align-' + align}, image), }; @@ -390,22 +455,31 @@ export default { ? to('media.path', node.src.slice('media/'.length)) : node.src); - const {width, height, align, pixelate} = node; + const {width, height, align, inline, pixelate} = node; - const content = - html.tag('div', {class: 'content-video-container'}, - align === 'center' && - {class: 'align-center'}, + const video = + html.tag('video', + src && {src}, + width && {width}, + height && {height}, - html.tag('video', - src && {src}, - width && {width}, - height && {height}, + {controls: true}, - {controls: true}, + align && inline && + {class: 'align-' + align}, + + pixelate && + {class: 'pixelate'}); + + const content = + (inline + ? video + : html.tag('div', {class: 'content-video-container'}, + align && + {class: 'align-' + align}, + + video)); - pixelate && - {class: 'pixelate'})); return { type: 'processed-video', @@ -419,15 +493,14 @@ export default { ? to('media.path', node.src.slice('media/'.length)) : node.src); - const {align, inline} = node; + const {align, inline, nameless} = node; const audio = html.tag('audio', src && {src}, - align === 'center' && - inline && - {class: 'align-center'}, + align && inline && + {class: 'align-' + align}, {controls: true}); @@ -435,10 +508,17 @@ export default { (inline ? audio : html.tag('div', {class: 'content-audio-container'}, - align === 'center' && - {class: 'align-center'}, + align && + {class: 'align-' + align}, + + [ + !nameless && + html.tag('a', {class: 'filename'}, + src && {href: src}, + language.sanitize(basename(node.src))), - audio)); + audio, + ])); return { type: 'processed-audio', @@ -484,7 +564,7 @@ export default { try { link.getSlotDescription('preferShortName'); hasPreferShortNameSlot = true; - } catch (error) { + } catch { hasPreferShortNameSlot = false; } @@ -497,7 +577,7 @@ export default { try { link.getSlotDescription('tooltipStyle'); hasTooltipStyleSlot = true; - } catch (error) { + } catch { hasTooltipStyleSlot = false; } @@ -548,6 +628,52 @@ export default { return {type: 'processed-external-link', data: externalLink}; } + case 'tooltip': { + const {label, link, tooltip: tooltipContent} = node.data; + + const externalLink = + (link + ? relations.externalLinksForTooltipNodes + .at(externalLinkForTooltipNodeIndex++) + : null); + + if (externalLink) { + externalLink.setSlots({ + content: label, + fromContent: true, + }); + + if (slots.indicateExternalLinks) { + externalLink.setSlots({ + indicateExternal: true, + disableBrowserTooltip: true, + tab: 'separate', + style: 'platform', + }); + } + } + + const textWithTooltip = relations.textWithTooltip.clone(); + const tooltip = relations.tooltip.clone(); + + tooltip.setSlots({ + attributes: {class: 'content-tooltip'}, + content: tooltipContent, // Not sanitized! + }); + + textWithTooltip.setSlots({ + attributes: [ + {class: 'content-tooltip-guy'}, + externalLink && {class: 'has-link'}, + ], + + text: externalLink ?? label, + tooltip, + }); + + return {type: 'processed-tooltip', data: textWithTooltip}; + } + case 'tag': { const {replacerKey, replacerValue} = node.data; |