diff options
Diffstat (limited to 'src/content/dependencies/transformContent.js')
-rw-r--r-- | src/content/dependencies/transformContent.js | 168 |
1 files changed, 116 insertions, 52 deletions
diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js index faae35a..0904cde 100644 --- a/src/content/dependencies/transformContent.js +++ b/src/content/dependencies/transformContent.js @@ -37,6 +37,7 @@ export default { .map(description => description.link) .filter(Boolean)), 'image', + 'linkExternal', ], extraDependencies: ['html', 'language', 'to', 'wikiData'], @@ -114,7 +115,7 @@ export default { data.hash = enteredHash ?? null; - return {i: node.i, iEnd: node.iEnd, type: 'link', data}; + return {i: node.i, iEnd: node.iEnd, type: 'internal-link', data}; } // This will be another {type: 'tag'} node which gets processed in @@ -140,10 +141,15 @@ export default { sprawl.nodes .map(node => { switch (node.type) { - // Replace link nodes with a stub. It'll be replaced (by position) - // with an item from relations. - case 'link': - return {type: 'link'}; + // Replace internal link nodes with a stub. It'll be replaced + // (by position) with an item from relations. + // + // TODO: This should be where label and hash get passed through, + // rather than in relations... (in which case there's no need to + // handle it specially here, and we can really just return + // data.nodes = sprawl.nodes) + case 'internal-link': + return {type: 'internal-link'}; // Other nodes will get processed in generate. default: @@ -167,9 +173,9 @@ export default { : getPlaceholder(node, content)); return { - links: + internalLinks: nodes - .filter(({type}) => type === 'link') + .filter(({type}) => type === 'internal-link') .map(node => { const {link, thing, value} = node.data; @@ -182,6 +188,15 @@ export default { } }), + externalLinks: + nodes + .filter(({type}) => type === 'external-link') + .map(node => { + const {href} = node.data; + + return relation('linkExternal', href); + }), + images: nodes .filter(({type}) => type === 'image') @@ -201,6 +216,11 @@ export default { default: false, }, + indicateExternalLinks: { + type: 'boolean', + default: true, + }, + thumb: { validate: v => v.is('small', 'medium', 'large'), default: 'large', @@ -208,12 +228,10 @@ export default { }, generate(data, relations, slots, {html, language, to}) { - let linkIndex = 0; let imageIndex = 0; + let internalLinkIndex = 0; + let externalLinkIndex = 0; - // This array contains only straight text and link nodes, which are directly - // representable in html (so no further processing is needed on the level of - // individual nodes). const contentFromNodes = data.nodes.map(node => { switch (node.type) { @@ -237,57 +255,83 @@ export default { } = node; if (node.inline) { + let content = + html.tag('img', + src && {src}, + width && {width}, + height && {height}, + style && {style}, + + pixelate && + {class: 'pixelate'}); + + if (link) { + content = + html.tag('a', + {href: link}, + {target: '_blank'}, + + {title: + language.$('misc.external.opensInNewTab', { + link: + language.formatExternalLink(link, { + style: 'platform', + }), + + annotation: + language.$('misc.external.opensInNewTab.annotation'), + }).toString()}, + + content); + } + return { - type: 'image', + type: 'processed-image', inline: true, - data: - html.tag('img', - src && {src}, - width && {width}, - height && {height}, - style && {style}, - - pixelate && - {class: 'pixelate'}), + data: content, }; } const image = relations.images[imageIndex++]; + image.setSlots({ + src, + + link: link ?? true, + warnings: warnings ?? null, + thumb: slots.thumb, + }); + + if (width || height) { + image.setSlot('dimensions', [width ?? null, height ?? null]); + } + + image.setSlot('attributes', [ + {class: 'content-image'}, + + pixelate && + {class: 'pixelate'}, + ]); + return { - type: 'image', + type: 'processed-image', inline: false, data: html.tag('div', {class: 'content-image-container'}, align === 'center' && {class: 'align-center'}, - image.slots({ - src, - - link: link ?? true, - width: width ?? null, - height: height ?? null, - warnings: warnings ?? null, - thumb: slots.thumb, - - attributes: [ - {class: 'content-image'}, - - pixelate && - {class: 'pixelate'}, - ], - })), + image), }; } - case 'link': { - const linkNode = relations.links[linkIndex++]; - if (linkNode.type === 'text') { - return {type: 'text', data: linkNode.data}; + case 'internal-link': { + const nodeFromRelations = relations.internalLinks[internalLinkIndex++]; + if (nodeFromRelations.type === 'text') { + return {type: 'text', data: nodeFromRelations.data}; } - const {link, label, hash} = linkNode; + const {link, label, hash} = nodeFromRelations; // These are removed from the typical combined slots({})-style // because we don't want to override slots that were already set @@ -322,7 +366,27 @@ export default { link.setSlot('tooltipStyle', 'none'); } - return {type: 'link', data: link}; + return {type: 'processed-internal-link', data: link}; + } + + case 'external-link': { + const {label} = node.data; + const externalLink = relations.externalLinks[externalLinkIndex++]; + + externalLink.setSlots({ + content: label, + fromContent: true, + }); + + if (slots.indicateExternalLinks) { + externalLink.setSlots({ + indicateExternal: true, + tab: 'separate', + style: 'platform', + }); + } + + return {type: 'processed-external-link', data: externalLink}; } case 'tag': { @@ -358,7 +422,10 @@ export default { // access to its slots. if (slots.mode === 'single-link') { - const link = contentFromNodes.find(node => node.type === 'link'); + const link = + contentFromNodes.find(node => + node.type === 'processed-internal-link' || + node.type === 'processed-external-link'); if (!link) { return html.blank(); @@ -385,13 +452,10 @@ export default { return getTextNodeContents(node, index); } - const attributes = html.attributes({ - class: 'INSERT-NON-TEXT', - 'data-type': node.type, - }); + let attributes = `class="INSERT-NON-TEXT" data-type="${node.type}"`; - if (node.type === 'image') { - attributes.set('data-inline', node.inline); + if (node.type === 'processed-image' && node.inline) { + attributes += ` data-inline`; } return `<span ${attributes}>${index}</span>`; @@ -426,7 +490,7 @@ export default { // the surrounding <p> tag that marked generates. The HTML parser // treats a <div> that starts inside a <p> as a Crocker-class // misgiving, and will treat you very badly if you feed it that. - if (attributes.get('data-type') === 'image') { + if (attributes.get('data-type') === 'processed-image') { if (!attributes.get('data-inline')) { tags[tags.length - 1] = tags[tags.length - 1].replace(/<p>$/, ''); deleteParagraph = true; |