diff options
Diffstat (limited to 'src/content/dependencies/linkExternal.js')
-rw-r--r-- | src/content/dependencies/linkExternal.js | 118 |
1 files changed, 107 insertions, 11 deletions
diff --git a/src/content/dependencies/linkExternal.js b/src/content/dependencies/linkExternal.js index ba2dbf2..f6b47db 100644 --- a/src/content/dependencies/linkExternal.js +++ b/src/content/dependencies/linkExternal.js @@ -6,12 +6,17 @@ export default { data: (url) => ({url}), slots: { + content: { + type: 'html', + mutable: false, + }, + style: { // This awkward syntax is because the slot descriptor validator can't // differentiate between a function that returns a validator (the usual // syntax) and a function that is itself a validator. validate: () => isExternalLinkStyle, - default: 'normal', + default: 'platform', }, context: { @@ -19,22 +24,113 @@ export default { default: 'generic', }, + fromContent: { + type: 'boolean', + default: false, + }, + + indicateExternal: { + type: 'boolean', + default: false, + }, + tab: { validate: v => v.is('default', 'separate'), default: 'default', }, }, - generate: (data, slots, {html, language}) => - html.tag('a', - {href: data.url}, - {class: 'nowrap'}, + generate(data, slots, {html, language}) { + let urlIsValid; + try { + new URL(data.url); + urlIsValid = true; + } catch (error) { + urlIsValid = false; + } + + let formattedLink; + if (urlIsValid) { + formattedLink = + language.formatExternalLink(data.url, { + style: slots.style, + context: slots.context, + }); + + // Fall back to platform if nothing matched the desired style. + if (html.isBlank(formattedLink) && slots.style !== 'platform') { + formattedLink = + language.formatExternalLink(data.url, { + style: 'platform', + context: slots.context, + }); + } + } else { + formattedLink = null; + } - slots.tab === 'separate' && - {target: '_blank'}, + const linkAttributes = html.attributes({ + class: 'external-link', + }); - language.formatExternalLink(data.url, { - style: slots.style, - context: slots.context, - })), + let linkContent; + if (urlIsValid) { + linkAttributes.set('href', data.url); + + if (html.isBlank(slots.content)) { + linkContent = formattedLink; + } else { + linkContent = slots.content; + } + } else { + if (html.isBlank(slots.content)) { + linkContent = + html.tag('i', + language.$('misc.external.invalidURL.annotation')); + } else { + linkContent = + language.$('misc.external.invalidURL', { + link: slots.content, + annotation: + html.tag('i', + language.$('misc.external.invalidURL.annotation')), + }); + } + } + + if (slots.fromContent) { + linkAttributes.add('class', 'from-content'); + } + + if (urlIsValid && slots.indicateExternal) { + linkAttributes.add('class', 'indicate-external'); + + let titleText; + if (slots.tab === 'separate') { + if (html.isBlank(slots.content)) { + titleText = + language.$('misc.external.opensInNewTab.annotation'); + } else { + titleText = + language.$('misc.external.opensInNewTab', { + link: formattedLink, + annotation: + language.$('misc.external.opensInNewTab.annotation'), + }); + } + } else if (!html.isBlank(slots.content)) { + titleText = formattedLink; + } + + if (titleText) { + linkAttributes.set('title', titleText.toString()); + } + } + + if (urlIsValid && slots.tab === 'separate') { + linkAttributes.set('target', '_blank'); + } + + return html.tag('a', linkAttributes, linkContent); + }, }; |