diff options
Diffstat (limited to 'src/misc-templates.js')
-rw-r--r-- | src/misc-templates.js | 873 |
1 files changed, 0 insertions, 873 deletions
diff --git a/src/misc-templates.js b/src/misc-templates.js index 8f3f0166..dfff4d88 100644 --- a/src/misc-templates.js +++ b/src/misc-templates.js @@ -16,547 +16,8 @@ import { getTotalDuration, sortAlbumsTracksChronologically, sortChronologically, - sortFlashesChronologically, } from './util/wiki-data.js'; -const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com']; - -const MASTODON_DOMAINS = ['types.pl']; - -// "Additional Files" listing - -function unbound_generateAdditionalFilesShortcut(additionalFiles, { - html, - language, -}) { - if (empty(additionalFiles)) return ''; - - return language.$('releaseInfo.additionalFiles.shortcut', { - anchorLink: - html.tag('a', - {href: '#additional-files'}, - language.$('releaseInfo.additionalFiles.shortcut.anchorLink')), - titles: language.formatUnitList( - additionalFiles.map(g => g.title)), - }); -} - -function unbound_generateAdditionalFilesList(additionalFiles, { - html, - language, - - getFileSize, - linkFile, -}) { - if (empty(additionalFiles)) return []; - - return html.tag('dl', - additionalFiles.flatMap(({title, description, files}) => [ - html.tag('dt', - (description - ? language.$('releaseInfo.additionalFiles.entry.withDescription', { - title, - description, - }) - : language.$('releaseInfo.additionalFiles.entry', {title}))), - - html.tag('dd', - html.tag('ul', - files.map((file) => { - const size = (getFileSize && getFileSize(file)); - return html.tag('li', - (size - ? language.$('releaseInfo.additionalFiles.file.withSize', { - file: linkFile(file), - size: language.formatFileSize(size), - }) - : language.$('releaseInfo.additionalFiles.file', { - file: linkFile(file), - }))) - }))), - ])); -} - -// Artist strings - -function unbound_getArtistString(artists, { - html, - language, - link, - - iconifyURL, - - showIcons = false, - showContrib = false, -}) { - return language.formatConjunctionList( - artists.map(({who, what}) => { - const {urls} = who; - - const hasContribPart = !!(showContrib && what); - const hasExternalPart = !!(showIcons && !empty(urls)); - - const artistLink = link.artist(who); - - const externalLinks = hasExternalPart && - html.tag('span', - { - [html.noEdgeWhitespace]: true, - class: 'icons' - }, - language.formatUnitList( - urls.slice(0, 4).map(url => iconifyURL(url, {language})))); - - return html.tag('span', {class: 'nowrap'}, - (hasContribPart - ? (hasExternalPart - ? language.$('misc.artistLink.withContribution.withExternalLinks', { - artist: artistLink, - contrib: what, - links: externalLinks, - }) - : language.$('misc.artistLink.withContribution', { - artist: artistLink, - contrib: what, - })) - : (hasExternalPart - ? language.$('misc.artistLink.withExternalLinks', { - artist: artistLink, - links: externalLinks, - }) - : language.$('misc.artistLink', { - artist: artistLink, - })))); - })); -} - -// Chronology links - -function unbound_generateChronologyLinks(currentThing, { - html, - language, - link, - - generateNavigationLinks, - - dateKey = 'date', - contribKey, - getThings, - headingString, -}) { - const contributions = currentThing[contribKey]; - - if (empty(contributions)) { - return []; - } - - if (contributions.length > 8) { - return html.tag('div', {class: 'chronology'}, - language.$('misc.chronology.seeArtistPages')); - } - - return contributions - .map(({who: artist}) => { - const thingsUnsorted = unique(getThings(artist)) - .filter((t) => t[dateKey]); - - // Kinda a hack, but we automatically detect which is (probably) the - // right function to use here. - const args = [thingsUnsorted, {getDate: (t) => t[dateKey]}]; - const things = ( - thingsUnsorted.every(t => t instanceof T.Album || t instanceof T.Track) - ? sortAlbumsTracksChronologically(...args) - : thingsUnsorted.every(t => t instanceof T.Flash) - ? sortFlashesChronologically(...args) - : sortChronologically(...args)); - - if (things.length === 0) return ''; - - const index = things.indexOf(currentThing); - - if (index === -1) return ''; - - const heading = ( - html.tag('span', {class: 'heading'}, - language.$(headingString, { - index: language.formatIndex(index + 1, {language}), - artist: link.artist(artist), - }))); - - const navigation = things.length > 1 && - html.tag('span', - { - [html.onlyIfContent]: true, - class: 'buttons', - }, - generateNavigationLinks(currentThing, { - data: things, - isMain: false, - })); - - return ( - html.tag('div', {class: 'chronology'}, - (navigation - ? language.$('misc.chronology.withNavigation', { - heading, - navigation, - }) - : heading))); - }); -} - -// Content warning tags - -function unbound_getRevealStringFromContentWarningMessage(warnings, { - html, - language, -}) { - return ( - language.$('misc.contentWarnings', {warnings}) + - html.tag('br') + - html.tag('span', {class: 'reveal-interaction'}, - language.$('misc.contentWarnings.reveal')) - ); -} - -function unbound_getRevealStringFromArtTags(tags, { - getRevealStringFromContentWarningMessage, - language, -}) { - return ( - tags?.some(tag => tag.isContentWarning) && - getRevealStringFromContentWarningMessage( - language.formatUnitList( - tags - .filter(tag => tag.isContentWarning) - .map(tag => tag.name))) - ); -} - -// Cover art links - -function unbound_generateCoverLink({ - html, - img, - language, - link, - - getRevealStringFromArtTags, - - alt, - path, - src, - tags = [], - to, - wikiData, -}) { - const {wikiInfo} = wikiData; - - if (!src && path) { - src = to(...path); - } - - if (!src) { - throw new Error(`Expected src or path`); - } - - const linkedTags = tags.filter(tag => !tag.isContentWarning); - - return html.tag('div', {id: 'cover-art-container'}, [ - img({ - src, - alt, - thumb: 'medium', - id: 'cover-art', - link: true, - square: true, - reveal: getRevealStringFromArtTags(tags), - }), - - wikiInfo.enableArtTagUI && - linkedTags.length && - html.tag('p', {class: 'tags'}, - language.$('releaseInfo.artTags.inline', { - tags: language.formatUnitList( - linkedTags.map(tag => link.tag(tag))), - })), - ]); -} - -// CSS & color shenanigans - -function unbound_getThemeString(color, { - getColors, - - additionalVariables = [], -} = {}) { - if (!color) return ''; - - const { - primary, - dark, - dim, - dimGhost, - bg, - bgBlack, - shadow, - } = getColors(color); - - const variables = [ - `--primary-color: ${primary}`, - `--dark-color: ${dark}`, - `--dim-color: ${dim}`, - `--dim-ghost-color: ${dimGhost}`, - `--bg-color: ${bg}`, - `--bg-black-color: ${bgBlack}`, - `--shadow-color: ${shadow}`, - ...additionalVariables, - ].filter(Boolean); - - if (!variables.length) return ''; - - return [ - `:root {`, - ...variables.map((line) => ` ${line};`), - `}` - ].join('\n'); -} - -function unbound_getAlbumStylesheet(album, { - to, -}) { - const hasWallpaper = album.wallpaperArtistContribs.length >= 1; - const hasWallpaperStyle = !!album.wallpaperStyle; - const hasBannerStyle = !!album.bannerStyle; - - const wallpaperSource = - (hasWallpaper && - to( - 'media.albumWallpaper', - album.directory, - album.wallpaperFileExtension)); - - const wallpaperPart = - (hasWallpaper - ? [ - `body::before {`, - ` background-image: url("${wallpaperSource}");`, - ...(hasWallpaperStyle - ? album.wallpaperStyle - .split('\n') - .map(line => ` ${line}`) - : []), - `}`, - ] - : []); - - const bannerPart = - (hasBannerStyle - ? [ - `#banner img {`, - ...album.bannerStyle - .split('\n') - .map(line => ` ${line}`), - `}`, - ] - : []); - - return [ - ...wallpaperPart, - ...bannerPart, - ] - .filter(Boolean) - .join('\n'); -} - -// Divided track lists - -function unbound_generateTrackListDividedByGroups(tracks, { - html, - language, - - getTrackItem, - wikiData, -}) { - const {divideTrackListsByGroups: groups} = wikiData.wikiInfo; - - if (empty(groups)) { - return html.tag('ul', - tracks.map(t => getTrackItem(t))); - } - - const lists = Object.fromEntries( - groups.map((group) => [ - group.directory, - {group, tracks: []} - ])); - - const other = []; - - for (const track of tracks) { - const {album} = track; - const group = groups.find((g) => g.albums.includes(album)); - if (group) { - lists[group.directory].tracks.push(track); - } else { - other.push(track); - } - } - - const dt = name => - html.tag('dt', - language.$('trackList.group', { - group: name, - })); - - const ddul = tracks => - html.tag('dd', - html.tag('ul', - tracks.map(t => getTrackItem(t)))); - - return html.tag('dl', [ - ...Object.values(lists) - .filter(({tracks}) => tracks.length) - .flatMap(({group, tracks}) => [ - dt(group.name), - ddul(tracks), - ]), - - ...html.fragment( - other.length && [ - dt(language.$('trackList.group.other')), - ddul(other), - ]), - ]); -} - -// Fancy lookin' links - -function unbound_fancifyURL(url, { - html, - language, - - album = false, -} = {}) { - let local = Symbol(); - let domain; - try { - domain = new URL(url).hostname; - } catch (error) { - // No support for relative local URLs yet, sorry! (I.e, local URLs must - // be absolute relative to the domain name in order to work.) - domain = local; - } - - return html.tag('a', - { - href: url, - class: 'nowrap', - }, - - // truly unhinged indentation here - domain === local - ? language.$('misc.external.local') - : domain.includes('bandcamp.com') - ? language.$('misc.external.bandcamp') - : BANDCAMP_DOMAINS.includes(domain) - ? language.$('misc.external.bandcamp.domain', {domain}) - : MASTODON_DOMAINS.includes(domain) - ? language.$('misc.external.mastodon.domain', {domain}) - : domain.includes('youtu') - ? album - ? url.includes('list=') - ? language.$('misc.external.youtube.playlist') - : language.$('misc.external.youtube.fullAlbum') - : language.$('misc.external.youtube') - : domain.includes('soundcloud') - ? language.$('misc.external.soundcloud') - : domain.includes('tumblr.com') - ? language.$('misc.external.tumblr') - : domain.includes('twitter.com') - ? language.$('misc.external.twitter') - : domain.includes('deviantart.com') - ? language.$('misc.external.deviantart') - : domain.includes('wikipedia.org') - ? language.$('misc.external.wikipedia') - : domain.includes('poetryfoundation.org') - ? language.$('misc.external.poetryFoundation') - : domain.includes('instagram.com') - ? language.$('misc.external.instagram') - : domain.includes('patreon.com') - ? language.$('misc.external.patreon') - : domain.includes('spotify.com') - ? language.$('misc.external.spotify') - : domain.includes('newgrounds.com') - ? language.$('misc.external.newgrounds') - : domain); -} - -function unbound_fancifyFlashURL(url, flash, { - html, - language, - - fancifyURL, -}) { - const link = fancifyURL(url); - return html.tag('span', - {class: 'nowrap'}, - url.includes('homestuck.com') - ? isNaN(Number(flash.page)) - ? language.$('misc.external.flash.homestuck.secret', {link}) - : language.$('misc.external.flash.homestuck.page', { - link, - page: flash.page, - }) - : url.includes('bgreco.net') - ? language.$('misc.external.flash.bgreco', {link}) - : url.includes('youtu') - ? language.$('misc.external.flash.youtube', {link}) - : link); -} - -function unbound_iconifyURL(url, { - html, - language, - to, -}) { - const domain = new URL(url).hostname; - const [id, msg] = ( - domain.includes('bandcamp.com') - ? ['bandcamp', language.$('misc.external.bandcamp')] - : BANDCAMP_DOMAINS.includes(domain) - ? ['bandcamp', language.$('misc.external.bandcamp.domain', {domain})] - : MASTODON_DOMAINS.includes(domain) - ? ['mastodon', language.$('misc.external.mastodon.domain', {domain})] - : domain.includes('youtu') - ? ['youtube', language.$('misc.external.youtube')] - : domain.includes('soundcloud') - ? ['soundcloud', language.$('misc.external.soundcloud')] - : domain.includes('tumblr.com') - ? ['tumblr', language.$('misc.external.tumblr')] - : domain.includes('twitter.com') - ? ['twitter', language.$('misc.external.twitter')] - : domain.includes('deviantart.com') - ? ['deviantart', language.$('misc.external.deviantart')] - : domain.includes('instagram.com') - ? ['instagram', language.$('misc.external.bandcamp')] - : domain.includes('newgrounds.com') - ? ['newgrounds', language.$('misc.external.newgrounds')] - : ['globe', language.$('misc.external.domain', {domain})]); - - return html.tag('a', - { - href: url, - class: 'icon', - }, - html.tag('svg', [ - html.tag('title', msg), - html.tag('use', { - href: to('shared.staticFile', `icons.svg#icon-${id}`), - }), - ])); -} - // Grids function unbound_getGridHTML({ @@ -636,123 +97,6 @@ function unbound_getFlashGridHTML({ }); } -// Images - -function unbound_img({ - getSizeOfImageFile, - html, - to, - - src, - alt, - noSrcText = '', - thumb: thumbKey, - reveal, - id, - class: className, - width, - height, - link = false, - lazy = false, - square = false, -}) { - const willSquare = square; - const willLink = typeof link === 'string' || link; - - const originalSrc = src; - const thumbSrc = src && (thumbKey ? thumb[thumbKey](src) : src); - - const href = - (willLink - ? (typeof link === 'string' - ? link - : originalSrc) - : null); - - let fileSize = null; - const mediaRoot = to('media.root'); - if (href?.startsWith(mediaRoot)) { - fileSize = getSizeOfImageFile(href.slice(mediaRoot.length).replace(/^\//, '')); - } - - const imgAttributes = { - id: link ? '' : id, - class: className, - alt, - width, - height, - 'data-original-size': fileSize, - }; - - const noSrcHTML = - !src && - wrap( - html.tag('div', - {class: 'image-text-area'}, - noSrcText)); - - const nonlazyHTML = - src && - wrap( - html.tag('img', { - ...imgAttributes, - src: thumbSrc, - })); - - const lazyHTML = - src && - lazy && - wrap( - html.tag('img', - { - ...imgAttributes, - class: [className, 'lazy'], - 'data-original': thumbSrc, - }), - true); - - if (!src) { - return noSrcHTML; - } else if (lazy) { - return html.tag('noscript', nonlazyHTML) + '\n' + lazyHTML; - } else { - return nonlazyHTML; - } - - function wrap(input, hide = false) { - let wrapped = input; - - wrapped = html.tag('div', {class: 'image-container'}, wrapped); - - if (reveal) { - wrapped = html.tag('div', {class: 'reveal'}, [ - wrapped, - html.tag('span', {class: 'reveal-text-container'}, - html.tag('span', {class: 'reveal-text'}, reveal)), - ]); - } - - if (willSquare) { - wrapped = html.tag('div', {class: 'square-content'}, wrapped); - wrapped = html.tag('div', - {class: ['square', hide && !willLink && 'js-hide']}, - wrapped); - } - - if (willLink) { - wrapped = html.tag('a', - { - id, - class: ['box', hide && 'js-hide', 'image-link'], - href, - }, - wrapped); - } - - return wrapped; - } -} - // Carousel reels // Layout constants: @@ -851,228 +195,11 @@ function unbound_getCarouselHTML({ })))))); } -// Nav-bar links - -function unbound_generateInfoGalleryLinks(currentThing, isGallery, { - link, - language, - - linkKeyGallery, - linkKeyInfo, -}) { - return [ - link[linkKeyInfo](currentThing, { - class: isGallery ? '' : 'current', - text: language.$('misc.nav.info'), - }), - link[linkKeyGallery](currentThing, { - class: isGallery ? 'current' : '', - text: language.$('misc.nav.gallery'), - }), - ].join(', '); -} - -// Generate "previous" and "next" links relative to a given current thing and a -// data set (array of things) which includes it, optionally including additional -// provided links like "random". This is for use in navigation bars and other -// inline areas. -// -// By default, generated links include ID attributes which enable client-side -// keyboard shortcuts. Provide isMain: false to disable this (if the generated -// links aren't the for the page's primary navigation). -function unbound_generateNavigationLinks(current, { - language, - link, - - additionalLinks = [], - data, - isMain = true, - linkKey = 'anything', - returnAsArray = false, -}) { - let previousLink, nextLink; - - if (current) { - const linkFn = link[linkKey].bind(link); - - const index = data.indexOf(current); - const previousThing = data[index - 1]; - const nextThing = data[index + 1]; - - previousLink = previousThing && - linkFn(previousThing, { - attributes: { - id: isMain && 'previous-button', - title: previousThing.name, - }, - text: language.$('misc.nav.previous'), - color: false, - }); - - nextLink = nextThing && - linkFn(nextThing, { - attributes: { - id: isMain && 'next-button', - title: nextThing.name, - }, - text: language.$('misc.nav.next'), - color: false, - }); - } - - const links = [ - previousLink, - nextLink, - ...additionalLinks, - ].filter(Boolean); - - if (returnAsArray) { - return links; - } else if (empty(links)) { - return ''; - } else { - return language.formatUnitList(links); - } -} - -// Sticky heading, ooooo - -function unbound_generateContentHeading({ - html, - - id, - title, -}) { - return html.tag('p', - { - class: 'content-heading', - id, - tabindex: '0', - }, - title); -} - -function unbound_generateStickyHeadingContainer({ - html, - img, - - class: classes, - coverSrc, - coverAlt, - coverArtTags, - title, -}) { - return html.tag('div', - {class: [ - 'content-sticky-heading-container', - coverSrc && 'has-cover', - ].concat(classes)}, - [ - html.tag('div', {class: 'content-sticky-heading-row'}, [ - html.tag('h1', title), - - // Cover art in the sticky heading never uses the 'reveal' setting - // because it's too small to effectively display content warnings. - // Instead, if art has content warnings, it's hidden from the sticky - // heading by default, and will be enabled once the main cover art - // is revealed. - coverSrc && - html.tag('div', {class: 'content-sticky-heading-cover-container'}, - html.tag('div', - { - class: [ - 'content-sticky-heading-cover', - coverArtTags.some(tag => tag.isContentWarning) && - 'content-sticky-heading-cover-needs-reveal', - ], - }, - img({ - src: coverSrc, - alt: coverAlt, - thumb: 'small', - link: false, - square: true, - }))), - ]), - - html.tag('div', {class: 'content-sticky-subheading-row'}, - html.tag('h2', {class: 'content-sticky-subheading'})), - ]); -} - -// Footer stuff - -function unbound_getFooterLocalizationLinks({ - html, - defaultLanguage, - language, - languages, - pagePath, - to, -}) { - const links = Object.entries(languages) - .filter(([code, language]) => code !== 'default' && !language.hidden) - .map(([code, language]) => language) - .sort(({name: a}, {name: b}) => (a < b ? -1 : a > b ? 1 : 0)) - .map((language) => - html.tag('span', - html.tag('a', - { - href: - language === defaultLanguage - ? to( - 'localizedDefaultLanguage.' + pagePath[0], - ...pagePath.slice(1)) - : to( - 'localizedWithBaseDirectory.' + pagePath[0], - language.code, - ...pagePath.slice(1)), - }, - language.name))); - - return html.tag('div', {class: 'footer-localization-links'}, - language.$('misc.uiLanguage', { - languages: links.join('\n'), - })); -} - // Exports export { - unbound_generateAdditionalFilesList as generateAdditionalFilesList, - unbound_generateAdditionalFilesShortcut as generateAdditionalFilesShortcut, - - unbound_getArtistString as getArtistString, - - unbound_generateChronologyLinks as generateChronologyLinks, - - unbound_getRevealStringFromContentWarningMessage as getRevealStringFromContentWarningMessage, - unbound_getRevealStringFromArtTags as getRevealStringFromArtTags, - - unbound_generateCoverLink as generateCoverLink, - - unbound_getThemeString as getThemeString, - unbound_getAlbumStylesheet as getAlbumStylesheet, - - unbound_generateTrackListDividedByGroups as generateTrackListDividedByGroups, - - unbound_fancifyURL as fancifyURL, - unbound_fancifyFlashURL as fancifyFlashURL, - unbound_iconifyURL as iconifyURL, - unbound_getGridHTML as getGridHTML, unbound_getAlbumGridHTML as getAlbumGridHTML, unbound_getFlashGridHTML as getFlashGridHTML, - unbound_getCarouselHTML as getCarouselHTML, - - unbound_img as img, - - unbound_generateInfoGalleryLinks as generateInfoGalleryLinks, - unbound_generateNavigationLinks as generateNavigationLinks, - - unbound_generateContentHeading as generateContentHeading, - unbound_generateStickyHeadingContainer as generateStickyHeadingContainer, - - unbound_getFooterLocalizationLinks as getFooterLocalizationLinks, } |