diff options
-rw-r--r-- | src/listing-spec.js | 9 | ||||
-rw-r--r-- | src/misc-templates.js | 369 | ||||
-rw-r--r-- | src/page/album-commentary.js | 7 | ||||
-rw-r--r-- | src/page/album.js | 275 | ||||
-rw-r--r-- | src/page/flash.js | 7 | ||||
-rw-r--r-- | src/page/group.js | 32 | ||||
-rw-r--r-- | src/page/homepage.js | 5 | ||||
-rw-r--r-- | src/page/listing.js | 77 | ||||
-rw-r--r-- | src/page/tag.js | 5 | ||||
-rw-r--r-- | src/page/track.js | 7 | ||||
-rwxr-xr-x | src/upd8.js | 434 | ||||
-rw-r--r-- | src/util/colors.js | 28 | ||||
-rw-r--r-- | src/util/link.js | 9 |
13 files changed, 669 insertions, 595 deletions
diff --git a/src/listing-spec.js b/src/listing-spec.js index a5239a41..90a2de5a 100644 --- a/src/listing-spec.js +++ b/src/listing-spec.js @@ -1,10 +1,6 @@ import fixWS from 'fix-whitespace'; import { - getLinkThemeString -} from './util/colors.js'; - -import { UNRELEASED_TRACKS_DIRECTORY } from './util/magic-constants.js'; @@ -757,7 +753,10 @@ const listingSpec = [ fandomAlbumData: wikiData.fandomAlbumData }), - html: ({officialAlbumData, fandomAlbumData}, {strings}) => fixWS` + html: ({officialAlbumData, fandomAlbumData}, { + getLinkThemeString, + strings + }) => fixWS` <p>Choose a link to go to a random page in that category or album! If your browser doesn't support relatively modern JavaScript or you've disabled it, these links won't work - sorry.</p> <p class="js-hide-once-data">(Data files are downloading in the background! Please wait for data to load.)</p> <p class="js-show-once-data">(Data files have finished being downloaded. The links should work!)</p> diff --git a/src/misc-templates.js b/src/misc-templates.js new file mode 100644 index 00000000..d4e4af38 --- /dev/null +++ b/src/misc-templates.js @@ -0,0 +1,369 @@ +// Miscellaneous utility functions which are useful across page specifications. +// These are made available right on a page spec's ({wikiData, strings, ...}) +// args object! + +import fixWS from 'fix-whitespace'; + +import * as html from './util/html.js'; + +import { + getColors +} from './util/colors.js'; + +import { + UNRELEASED_TRACKS_DIRECTORY +} from './util/magic-constants.js'; + +import { + unique +} from './util/sugar.js'; + +import { + getTotalDuration, + sortByDate +} from './util/wiki-data.js'; + +// Artist strings + +export function getArtistString(artists, { + iconifyURL, link, strings, + showIcons = false, + showContrib = false +}) { + return strings.list.and(artists.map(({ who, what }) => { + const { urls, directory, name } = who; + return [ + link.artist(who), + showContrib && what && `(${what})`, + showIcons && urls.length && `<span class="icons">(${ + strings.list.unit(urls.map(url => iconifyURL(url, {strings}))) + })</span>` + ].filter(Boolean).join(' '); + })); +} + +// Chronology links + +export function generateChronologyLinks(currentThing, { + contribKey, + getThings, + headingString, + link, + linkAnythingMan, + strings, + wikiData +}) { + const { albumData } = wikiData; + + const contributions = currentThing[contribKey]; + if (!contributions) { + return ''; + } + + if (contributions.length > 8) { + return `<div class="chronology">${strings('misc.chronology.seeArtistPages')}</div>`; + } + + return contributions.map(({ who: artist }) => { + const things = sortByDate(unique(getThings(artist))); + const releasedThings = things.filter(thing => { + const album = albumData.includes(thing) ? thing : thing.album; + return !(album && album.directory === UNRELEASED_TRACKS_DIRECTORY); + }); + const index = releasedThings.indexOf(currentThing); + + if (index === -1) return ''; + + // TODO: This can pro8a8ly 8e made to use generatePreviousNextLinks? + // We'd need to make generatePreviousNextLinks use toAnythingMan tho. + const previous = releasedThings[index - 1]; + const next = releasedThings[index + 1]; + const parts = [ + previous && linkAnythingMan(previous, { + color: false, + text: strings('misc.nav.previous') + }), + next && linkAnythingMan(next, { + color: false, + text: strings('misc.nav.next') + }) + ].filter(Boolean); + + const stringOpts = { + index: strings.count.index(index + 1, {strings}), + artist: link.artist(artist) + }; + + return fixWS` + <div class="chronology"> + <span class="heading">${strings(headingString, stringOpts)}</span> + ${parts.length && `<span class="buttons">(${parts.join(', ')})</span>`} + </div> + `; + }).filter(Boolean).join('\n'); +} + +// Content warning tags + +export function getRevealStringFromWarnings(warnings, {strings}) { + return strings('misc.contentWarnings', {warnings}) + `<br><span class="reveal-interaction">${strings('misc.contentWarnings.reveal')}</span>` +} + +export function getRevealStringFromTags(tags, {strings}) { + return tags && tags.some(tag => tag.isCW) && ( + getRevealStringFromWarnings(strings.list.unit(tags.filter(tag => tag.isCW).map(tag => tag.name)), {strings})); +} + +// Cover art links + +export function generateCoverLink({ + img, link, strings, to, wikiData, + src, + path, + alt, + tags = [] +}) { + const { wikiInfo } = wikiData; + + if (!src && path) { + src = to(...path); + } + + if (!src) { + throw new Error(`Expected src or path`); + } + + return fixWS` + <div id="cover-art-container"> + ${img({ + src, + alt, + thumb: 'medium', + id: 'cover-art', + link: true, + square: true, + reveal: getRevealStringFromTags(tags, {strings}) + })} + ${wikiInfo.features.artTagUI && tags.filter(tag => !tag.isCW).length && fixWS` + <p class="tags"> + ${strings('releaseInfo.artTags')} + ${(tags + .filter(tag => !tag.isCW) + .map(link.tag) + .join(',\n'))} + </p> + `} + </div> + `; +} + +// CSS & color shenanigans + +export function getThemeString(color, additionalVariables = []) { + if (!color) return ''; + + const { primary, dim, bg } = getColors(color); + + const variables = [ + `--primary-color: ${primary}`, + `--dim-color: ${dim}`, + `--bg-color: ${bg}`, + ...additionalVariables + ].filter(Boolean); + + if (!variables.length) return ''; + + return ( + `:root {\n` + + variables.map(line => ` ` + line + ';\n').join('') + + `}` + ); +} +export function getAlbumStylesheet(album, {to}) { + return [ + album.wallpaperArtists && fixWS` + body::before { + background-image: url("${to('media.albumWallpaper', album.directory)}"); + ${album.wallpaperStyle} + } + `, + album.bannerStyle && fixWS` + #banner img { + ${album.bannerStyle} + } + ` + ].filter(Boolean).join('\n'); +} + +// Fancy lookin' links + +export function fancifyURL(url, {strings, album = false} = {}) { + const domain = new URL(url).hostname; + return fixWS`<a href="${url}" class="nowrap">${ + domain.includes('bandcamp.com') ? strings('misc.external.bandcamp') : + [ + 'music.solatrux.com' + ].includes(domain) ? strings('misc.external.bandcamp.domain', {domain}) : + [ + 'types.pl' + ].includes(domain) ? strings('misc.external.mastodon.domain', {domain}) : + domain.includes('youtu') ? (album + ? (url.includes('list=') + ? strings('misc.external.youtube.playlist') + : strings('misc.external.youtube.fullAlbum')) + : strings('misc.external.youtube')) : + domain.includes('soundcloud') ? strings('misc.external.soundcloud') : + domain.includes('tumblr.com') ? strings('misc.external.tumblr') : + domain.includes('twitter.com') ? strings('misc.external.twitter') : + domain.includes('deviantart.com') ? strings('misc.external.deviantart') : + domain.includes('wikipedia.org') ? strings('misc.external.wikipedia') : + domain.includes('poetryfoundation.org') ? strings('misc.external.poetryFoundation') : + domain.includes('instagram.com') ? strings('misc.external.instagram') : + domain.includes('patreon.com') ? strings('misc.external.patreon') : + domain + }</a>`; +} + +export function fancifyFlashURL(url, flash, {strings}) { + const link = fancifyURL(url, {strings}); + return `<span class="nowrap">${ + url.includes('homestuck.com') ? (isNaN(Number(flash.page)) + ? strings('misc.external.flash.homestuck.secret', {link}) + : strings('misc.external.flash.homestuck.page', {link, page: flash.page})) : + url.includes('bgreco.net') ? strings('misc.external.flash.bgreco', {link}) : + url.includes('youtu') ? strings('misc.external.flash.youtube', {link}) : + link + }</span>`; +} + +export function iconifyURL(url, {strings, to}) { + const domain = new URL(url).hostname; + const [ id, msg ] = ( + domain.includes('bandcamp.com') ? ['bandcamp', strings('misc.external.bandcamp')] : + ( + domain.includes('music.solatrus.com') + ) ? ['bandcamp', strings('misc.external.bandcamp.domain', {domain})] : + ( + domain.includes('types.pl') + ) ? ['mastodon', strings('misc.external.mastodon.domain', {domain})] : + domain.includes('youtu') ? ['youtube', strings('misc.external.youtube')] : + domain.includes('soundcloud') ? ['soundcloud', strings('misc.external.soundcloud')] : + domain.includes('tumblr.com') ? ['tumblr', strings('misc.external.tumblr')] : + domain.includes('twitter.com') ? ['twitter', strings('misc.external.twitter')] : + domain.includes('deviantart.com') ? ['deviantart', strings('misc.external.deviantart')] : + domain.includes('instagram.com') ? ['instagram', strings('misc.external.bandcamp')] : + ['globe', strings('misc.external.domain', {domain})] + ); + return fixWS`<a href="${url}" class="icon"><svg><title>${msg}</title><use href="${to('shared.staticFile', `icons.svg#icon-${id}`)}"></use></svg></a>`; +} + +// Grids + +export function getGridHTML({ + getLinkThemeString, + img, + strings, + + entries, + srcFn, + hrefFn, + altFn = () => '', + detailsFn = null, + lazy = true +}) { + return entries.map(({ large, item }, i) => html.tag('a', + { + class: ['grid-item', 'box', large && 'large-grid-item'], + href: hrefFn(item), + style: getLinkThemeString(item.color) + }, + fixWS` + ${img({ + src: srcFn(item), + alt: altFn(item), + thumb: 'small', + lazy: (typeof lazy === 'number' ? i >= lazy : lazy), + square: true, + reveal: getRevealStringFromTags(item.artTags, {strings}) + })} + <span>${item.name}</span> + ${detailsFn && `<span>${detailsFn(item)}</span>`} + `)).join('\n'); +} + +export function getAlbumGridHTML({ + getAlbumCover, getGridHTML, strings, to, + details = false, + ...props +}) { + return getGridHTML({ + srcFn: getAlbumCover, + hrefFn: album => to('localized.album', album.directory), + detailsFn: details && (album => strings('misc.albumGridDetails', { + tracks: strings.count.tracks(album.tracks.length, {unit: true}), + time: strings.count.duration(getTotalDuration(album.tracks)) + })), + ...props + }); +} + +export function getFlashGridHTML({ + getFlashCover, getGridHTML, to, + ...props +}) { + return getGridHTML({ + srcFn: getFlashCover, + hrefFn: flash => to('localized.flash', flash.directory), + ...props + }); +} +// Nav-bar links + +export function generateInfoGalleryLinks(currentThing, isGallery, { + link, strings, + linkKeyGallery, + linkKeyInfo +}) { + return [ + link[linkKeyInfo](currentThing, { + class: isGallery ? '' : 'current', + text: strings('misc.nav.info') + }), + link[linkKeyGallery](currentThing, { + class: isGallery ? 'current' : '', + text: strings('misc.nav.gallery') + }) + ].join(', '); +} + +export function generatePreviousNextLinks(current, { + data, + link, + linkKey, + strings +}) { + const linkFn = link[linkKey]; + + const index = data.indexOf(current); + const previous = data[index - 1]; + const next = data[index + 1]; + + return [ + previous && linkFn(previous, { + attributes: { + id: 'previous-button', + title: previous.name + }, + text: strings('misc.nav.previous'), + color: false + }), + next && linkFn(next, { + attributes: { + id: 'next-button', + title: next.name + }, + text: strings('misc.nav.next'), + color: false + }) + ].filter(Boolean).join(', '); +} diff --git a/src/page/album-commentary.js b/src/page/album-commentary.js index 77ca3ef1..c03ae3db 100644 --- a/src/page/album-commentary.js +++ b/src/page/album-commentary.js @@ -5,11 +5,6 @@ import fixWS from 'fix-whitespace'; import { - getLinkThemeString, - getThemeString -} from '../util/colors.js'; - -import { filterAlbumsByCommentary } from '../util/wiki-data.js'; @@ -34,6 +29,8 @@ export function write(album, {wikiData}) { path: ['albumCommentary', album.directory], page: ({ getAlbumStylesheet, + getLinkThemeString, + getThemeString, link, strings, to, diff --git a/src/page/album.js b/src/page/album.js index 16da6021..adcc0584 100644 --- a/src/page/album.js +++ b/src/page/album.js @@ -4,14 +4,13 @@ import fixWS from 'fix-whitespace'; -import { - getLinkThemeString, - getThemeString -} from '../util/colors.js'; - import * as html from '../util/html.js'; import { + bindOpts +} from '../util/sugar.js'; + +import { getAlbumCover, getAlbumListTag, getTotalDuration @@ -26,7 +25,12 @@ export function targets({wikiData}) { export function write(album, {wikiData}) { const { wikiInfo } = wikiData; - const trackToListItem = (track, {getArtistString, link, strings}) => { + const unbound_trackToListItem = (track, { + getArtistString, + getLinkThemeString, + link, + strings + }) => { const itemOpts = { duration: strings.count.duration(track.duration), track: link.track(track) @@ -96,140 +100,152 @@ export function write(album, {wikiData}) { generateCoverLink, getAlbumStylesheet, getArtistString, + getLinkThemeString, + getThemeString, link, strings, transformMultiline - }) => ({ - title: strings('albumPage.title', {album: album.name}), - stylesheet: getAlbumStylesheet(album), - theme: getThemeString(album.color, [ - `--album-directory: ${album.directory}` - ]), + }) => { + const trackToListItem = bindOpts(unbound_trackToListItem, { + getArtistString, + getLinkThemeString, + link, + strings + }); - banner: album.bannerArtists && { - dimensions: album.bannerDimensions, - path: ['media.albumBanner', album.directory], - alt: strings('misc.alt.albumBanner'), - position: 'top' - }, + return { + title: strings('albumPage.title', {album: album.name}), + stylesheet: getAlbumStylesheet(album), + theme: getThemeString(album.color, [ + `--album-directory: ${album.directory}` + ]), - main: { - content: fixWS` - ${generateCoverLink({ - path: ['media.albumCover', album.directory], - alt: strings('misc.alt.albumCover'), - tags: album.artTags - })} - <h1>${strings('albumPage.title', {album: album.name})}</h1> - <p> - ${[ - album.artists && strings('releaseInfo.by', { - artists: getArtistString(album.artists, { - showContrib: true, - showIcons: true - }) - }), - album.coverArtists && strings('releaseInfo.coverArtBy', { - artists: getArtistString(album.coverArtists, { - showContrib: true, - showIcons: true - }) - }), - album.wallpaperArtists && strings('releaseInfo.wallpaperArtBy', { - artists: getArtistString(album.wallpaperArtists, { - showContrib: true, - showIcons: true + banner: album.bannerArtists && { + dimensions: album.bannerDimensions, + path: ['media.albumBanner', album.directory], + alt: strings('misc.alt.albumBanner'), + position: 'top' + }, + + main: { + content: fixWS` + ${generateCoverLink({ + path: ['media.albumCover', album.directory], + alt: strings('misc.alt.albumCover'), + tags: album.artTags + })} + <h1>${strings('albumPage.title', {album: album.name})}</h1> + <p> + ${[ + album.artists && strings('releaseInfo.by', { + artists: getArtistString(album.artists, { + showContrib: true, + showIcons: true + }) + }), + album.coverArtists && strings('releaseInfo.coverArtBy', { + artists: getArtistString(album.coverArtists, { + showContrib: true, + showIcons: true + }) + }), + album.wallpaperArtists && strings('releaseInfo.wallpaperArtBy', { + artists: getArtistString(album.wallpaperArtists, { + showContrib: true, + showIcons: true + }) + }), + album.bannerArtists && strings('releaseInfo.bannerArtBy', { + artists: getArtistString(album.bannerArtists, { + showContrib: true, + showIcons: true + }) + }), + strings('releaseInfo.released', { + date: strings.count.date(album.date) + }), + +album.coverArtDate !== +album.date && strings('releaseInfo.artReleased', { + date: strings.count.date(album.coverArtDate) + }), + strings('releaseInfo.duration', { + duration: strings.count.duration(albumDuration, {approximate: album.tracks.length > 1}) }) - }), - album.bannerArtists && strings('releaseInfo.bannerArtBy', { - artists: getArtistString(album.bannerArtists, { - showContrib: true, - showIcons: true + ].filter(Boolean).join('<br>\n')} + </p> + ${commentaryEntries && `<p>${ + strings('releaseInfo.viewCommentary', { + link: link.albumCommentary(album, { + text: strings('releaseInfo.viewCommentary.link') }) - }), - strings('releaseInfo.released', { - date: strings.count.date(album.date) - }), - +album.coverArtDate !== +album.date && strings('releaseInfo.artReleased', { - date: strings.count.date(album.coverArtDate) - }), - strings('releaseInfo.duration', { - duration: strings.count.duration(albumDuration, {approximate: album.tracks.length > 1}) - }) - ].filter(Boolean).join('<br>\n')} - </p> - ${commentaryEntries && `<p>${ - strings('releaseInfo.viewCommentary', { - link: link.albumCommentary(album, { - text: strings('releaseInfo.viewCommentary.link') }) - }) - }</p>`} - ${album.urls.length && `<p>${ - strings('releaseInfo.listenOn', { - links: strings.list.or(album.urls.map(url => fancifyURL(url, {album: true}))) - }) - }</p>`} - ${album.trackGroups ? fixWS` - <dl class="album-group-list"> - ${album.trackGroups.map(({ name, color, startIndex, tracks }) => fixWS` - <dt>${ - strings('trackList.group', { - duration: strings.count.duration(getTotalDuration(tracks), {approximate: tracks.length > 1}), - group: name - }) - }</dt> - <dd><${listTag === 'ol' ? `ol start="${startIndex + 1}"` : listTag}> - ${tracks.map(t => trackToListItem(t, {getArtistString, link, strings})).join('\n')} - </${listTag}></dd> - `).join('\n')} - </dl> - ` : fixWS` - <${listTag}> - ${album.tracks.map(t => trackToListItem(t, {getArtistString, link, strings})).join('\n')} - </${listTag}> - `} - <p> - ${[ - strings('releaseInfo.addedToWiki', { - date: strings.count.date(album.dateAdded) + }</p>`} + ${album.urls.length && `<p>${ + strings('releaseInfo.listenOn', { + links: strings.list.or(album.urls.map(url => fancifyURL(url, {album: true}))) }) - ].filter(Boolean).join('<br>\n')} - </p> - ${album.commentary && fixWS` - <p>${strings('releaseInfo.artistCommentary')}</p> - <blockquote> - ${transformMultiline(album.commentary)} - </blockquote> - `} - ` - }, + }</p>`} + ${album.trackGroups ? fixWS` + <dl class="album-group-list"> + ${album.trackGroups.map(({ name, color, startIndex, tracks }) => fixWS` + <dt>${ + strings('trackList.group', { + duration: strings.count.duration(getTotalDuration(tracks), {approximate: tracks.length > 1}), + group: name + }) + }</dt> + <dd><${listTag === 'ol' ? `ol start="${startIndex + 1}"` : listTag}> + ${tracks.map(trackToListItem).join('\n')} + </${listTag}></dd> + `).join('\n')} + </dl> + ` : fixWS` + <${listTag}> + ${album.tracks.map(trackToListItem).join('\n')} + </${listTag}> + `} + <p> + ${[ + strings('releaseInfo.addedToWiki', { + date: strings.count.date(album.dateAdded) + }) + ].filter(Boolean).join('<br>\n')} + </p> + ${album.commentary && fixWS` + <p>${strings('releaseInfo.artistCommentary')}</p> + <blockquote> + ${transformMultiline(album.commentary)} + </blockquote> + `} + ` + }, - sidebarLeft: generateAlbumSidebar(album, null, { - fancifyURL, - link, - strings, - transformMultiline, - wikiData - }), + sidebarLeft: generateAlbumSidebar(album, null, { + fancifyURL, + getLinkThemeString, + link, + strings, + transformMultiline, + wikiData + }), - nav: { - links: [ - {toHome: true}, - { - html: strings('albumPage.nav.album', { - album: link.album(album, {class: 'current'}) - }) - }, - album.tracks.length > 1 && - { - divider: false, - html: generateAlbumNavLinks(album, null, {strings}) - } - ], - content: html.tag('div', generateAlbumChronologyLinks(album, null, {generateChronologyLinks})) - } - }) + nav: { + links: [ + {toHome: true}, + { + html: strings('albumPage.nav.album', { + album: link.album(album, {class: 'current'}) + }) + }, + album.tracks.length > 1 && + { + divider: false, + html: generateAlbumNavLinks(album, null, {strings}) + } + ], + content: html.tag('div', generateAlbumChronologyLinks(album, null, {generateChronologyLinks})) + } + }; + } }; return [page, data]; @@ -239,6 +255,7 @@ export function write(album, {wikiData}) { export function generateAlbumSidebar(album, currentTrack, { fancifyURL, + getLinkThemeString, link, strings, transformMultiline, diff --git a/src/page/flash.js b/src/page/flash.js index 4ffaefc6..9c59016d 100644 --- a/src/page/flash.js +++ b/src/page/flash.js @@ -4,11 +4,6 @@ import fixWS from 'fix-whitespace'; -import { - getLinkThemeString, - getThemeString -} from '../util/colors.js'; - import * as html from '../util/html.js'; import { @@ -36,6 +31,7 @@ export function write(flash, {wikiData}) { generatePreviousNextLinks, getArtistString, getFlashCover, + getThemeString, link, strings, transformInline @@ -118,6 +114,7 @@ export function writeTargetless({wikiData}) { path: ['flashIndex'], page: ({ getFlashGridHTML, + getLinkThemeString, link, strings }) => ({ diff --git a/src/page/group.js b/src/page/group.js index 5db5a8ff..698c17ec 100644 --- a/src/page/group.js +++ b/src/page/group.js @@ -8,11 +8,6 @@ import { UNRELEASED_TRACKS_DIRECTORY } from '../util/magic-constants.js'; -import { - getLinkThemeString, - getThemeString -} from '../util/colors.js'; - import * as html from '../util/html.js'; import { @@ -44,6 +39,8 @@ export function write(group, {wikiData}) { page: ({ generateInfoGalleryLinks, generatePreviousNextLinks, + getLinkThemeString, + getThemeString, fancifyURL, link, strings, @@ -92,7 +89,13 @@ export function write(group, {wikiData}) { ` }, - sidebarLeft: generateGroupSidebar(group, false, {link, strings, wikiData}), + sidebarLeft: generateGroupSidebar(group, false, { + getLinkThemeString, + link, + strings, + wikiData + }), + nav: generateGroupNav(group, false, { generateInfoGalleryLinks, generatePreviousNextLinks, @@ -110,6 +113,8 @@ export function write(group, {wikiData}) { generateInfoGalleryLinks, generatePreviousNextLinks, getAlbumGridHTML, + getLinkThemeString, + getThemeString, link, strings }) => ({ @@ -144,7 +149,13 @@ export function write(group, {wikiData}) { ` }, - sidebarLeft: generateGroupSidebar(group, true, {link, strings, wikiData}), + sidebarLeft: generateGroupSidebar(group, true, { + getLinkThemeString, + link, + strings, + wikiData + }), + nav: generateGroupNav(group, true, { generateInfoGalleryLinks, generatePreviousNextLinks, @@ -160,7 +171,12 @@ export function write(group, {wikiData}) { // Utility functions -function generateGroupSidebar(currentGroup, isGallery, {link, strings, wikiData}) { +function generateGroupSidebar(currentGroup, isGallery, { + getLinkThemeString, + link, + strings, + wikiData +}) { const { groupCategoryData, wikiInfo } = wikiData; if (!wikiInfo.features.groupUI) { diff --git a/src/page/homepage.js b/src/page/homepage.js index d1dcc680..37ec4426 100644 --- a/src/page/homepage.js +++ b/src/page/homepage.js @@ -4,10 +4,6 @@ import fixWS from 'fix-whitespace'; -import { - getLinkThemeString -} from '../util/colors.js'; - import find from '../util/find.js'; import * as html from '../util/html.js'; @@ -27,6 +23,7 @@ export function writeTargetless({wikiData}) { path: ['home'], page: ({ getAlbumGridHTML, + getLinkThemeString, link, strings, to, diff --git a/src/page/listing.js b/src/page/listing.js index b0766b28..d4021747 100644 --- a/src/page/listing.js +++ b/src/page/listing.js @@ -46,44 +46,45 @@ export function write(listing, {wikiData}) { const page = { type: 'page', path: ['listing', listing.directory], - page: ({ - link, - strings - }) => ({ - title: listing.title({strings}), - - main: { - content: fixWS` - <h1>${listing.title({strings})}</h1> - ${listing.html && (listing.data - ? listing.html(data, {link, strings}) - : listing.html({link, strings}))} - ${listing.row && fixWS` - <ul> - ${(data - .map(item => listing.row(item, {link, strings})) - .map(row => `<li>${row}</li>`) - .join('\n'))} - </ul> - `} - ` - }, - - sidebarLeft: { - content: generateSidebarForListings(listing, {link, strings, wikiData}) - }, - - nav: { - links: [ - {toHome: true}, - { - path: ['localized.listingIndex'], - title: strings('listingIndex.title') - }, - {toCurrentPage: true} - ] - } - }) + page: opts => { + const { link, strings } = opts; + + return { + title: listing.title({strings}), + + main: { + content: fixWS` + <h1>${listing.title({strings})}</h1> + ${listing.html && (listing.data + ? listing.html(data, opts) + : listing.html(opts))} + ${listing.row && fixWS` + <ul> + ${(data + .map(item => listing.row(item, opts)) + .map(row => `<li>${row}</li>`) + .join('\n'))} + </ul> + `} + ` + }, + + sidebarLeft: { + content: generateSidebarForListings(listing, {link, strings, wikiData}) + }, + + nav: { + links: [ + {toHome: true}, + { + path: ['localized.listingIndex'], + title: strings('listingIndex.title') + }, + {toCurrentPage: true} + ] + } + }; + } }; return [page]; diff --git a/src/page/tag.js b/src/page/tag.js index c6f64bfc..610f4665 100644 --- a/src/page/tag.js +++ b/src/page/tag.js @@ -4,10 +4,6 @@ import fixWS from 'fix-whitespace'; -import { - getThemeString -} from '../util/colors.js'; - // Page exports export function condition({wikiData}) { @@ -28,6 +24,7 @@ export function write(tag, {wikiData}) { page: ({ getAlbumCover, getGridHTML, + getThemeString, getTrackCover, link, strings, diff --git a/src/page/track.js b/src/page/track.js index 2dec9bd3..0941ee89 100644 --- a/src/page/track.js +++ b/src/page/track.js @@ -10,10 +10,6 @@ import { generateAlbumSidebar } from './album.js'; -import { - getThemeString -} from '../util/colors.js'; - import * as html from '../util/html.js'; import { @@ -134,6 +130,8 @@ export function write(track, {wikiData}) { generatePreviousNextLinks, getAlbumStylesheet, getArtistString, + getLinkThemeString, + getThemeString, getTrackCover, link, strings, @@ -284,6 +282,7 @@ export function write(track, {wikiData}) { sidebarLeft: generateAlbumSidebar(album, track, { fancifyURL, + getLinkThemeString, link, strings, transformMultiline, diff --git a/src/upd8.js b/src/upd8.js index 345f166b..8652f65c 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -116,7 +116,25 @@ import * as pageSpecs from './page/index.js'; import find from './util/find.js'; import * as html from './util/html.js'; -import unbound_link from './util/link.js'; +import unbound_link, {getLinkThemeString} from './util/link.js'; + +import { + fancifyFlashURL, + fancifyURL, + generateChronologyLinks, + generateCoverLink, + generateInfoGalleryLinks, + generatePreviousNextLinks, + getAlbumGridHTML, + getAlbumStylesheet, + getArtistString, + getFlashGridHTML, + getGridHTML, + getRevealStringFromTags, + getRevealStringFromWarnings, + getThemeString, + iconifyURL +} from './misc-templates.js'; import { decorateTime, @@ -128,11 +146,6 @@ import { } from './util/cli.js'; import { - getLinkThemeString, - getThemeString -} from './util/colors.js'; - -import { validateReplacerSpec, transformInline } from './util/replacer.js'; @@ -1761,51 +1774,43 @@ writePage.html = (pageFn, { const collapseSidebars = (sidebarLeft.collapse !== false) && (sidebarRight.collapse !== false); - const mainHTML = main.content && fixWS` - <main id="content" ${classes(...main.classes || [])}> - ${main.content} - </main> - `; + const mainHTML = main.content && html.tag('main', { + id: 'content', + class: main.classes + }, main.content); - const footerHTML = footer.content && fixWS` - <footer id="footer" ${classes(...footer.classes || [])}> - ${footer.content} - </footer> - `; + const footerHTML = footer.content && html.tag('footer', { + id: 'footer', + class: footer.classes + }, footer.content); const generateSidebarHTML = (id, { content, multiple, - classes: sidebarClasses = [], + classes, collapse = true, wide = false - }) => (content ? fixWS` - <div id="${id}" ${classes( - 'sidebar-column', - 'sidebar', - wide && 'wide', - !collapse && 'no-hide', - ...sidebarClasses - )}> - ${content} - </div> - ` : multiple ? fixWS` - <div id="${id}" ${classes( - 'sidebar-column', - 'sidebar-multiple', - wide && 'wide', - !collapse && 'no-hide' - )}> - ${multiple.map(content => fixWS` - <div ${classes( - 'sidebar', - ...sidebarClasses - )}> - ${content} - </div> - `).join('\n')} - </div> - ` : ''); + }) => (content + ? html.tag('div', + {id, class: [ + 'sidebar-column', + 'sidebar', + wide && 'wide', + !collapse && 'no-hide', + ...classes + ]}, + content) + : multiple ? html.tag('div', + {id, class: [ + 'sidebar-column', + 'sidebar-multiple', + wide && 'wide', + !collapse && 'no-hide' + ]}, + multiple.map(content => html.tag('div', + {class: ['sidebar', ...classes]}, + content))) + : ''); const sidebarLeftHTML = generateSidebarHTML('sidebar-left', sidebarLeft); const sidebarRightHTML = generateSidebarHTML('sidebar-right', sidebarRight); @@ -2012,58 +2017,6 @@ writePage.paths = (baseDirectory, fullKey, directory = '', { }; }; -function getGridHTML({ - strings, - entries, - srcFn, - hrefFn, - altFn = () => '', - detailsFn = null, - lazy = true -}) { - return entries.map(({ large, item }, i) => fixWS` - <a ${classes('grid-item', 'box', large && 'large-grid-item')} href="${hrefFn(item)}" style="${getLinkThemeString(item.color)}"> - ${img({ - src: srcFn(item), - alt: altFn(item), - thumb: 'small', - lazy: (typeof lazy === 'number' ? i >= lazy : lazy), - square: true, - reveal: getRevealStringFromTags(item.artTags, {strings}) - })} - <span>${item.name}</span> - ${detailsFn && `<span>${detailsFn(item)}</span>`} - </a> - `).join('\n'); -} - -function getAlbumGridHTML({ - getAlbumCover, getGridHTML, strings, to, - details = false, - ...props -}) { - return getGridHTML({ - srcFn: getAlbumCover, - hrefFn: album => to('localized.album', album.directory), - detailsFn: details && (album => strings('misc.albumGridDetails', { - tracks: strings.count.tracks(album.tracks.length, {unit: true}), - time: strings.count.duration(getTotalDuration(album.tracks)) - })), - ...props - }); -} - -function getFlashGridHTML({ - getFlashCover, getGridHTML, to, - ...props -}) { - return getGridHTML({ - srcFn: getFlashCover, - hrefFn: flash => to('localized.flash', flash.directory), - ...props - }); -} - function writeSymlinks() { return progressPromiseAll('Writing site symlinks.', [ link(path.join(__dirname, UTILITY_DIRECTORY), 'shared.utilityRoot'), @@ -2115,72 +2068,6 @@ function writeSharedFilesAndPages({strings, wikiData}) { ].filter(Boolean)); } -function getRevealStringFromWarnings(warnings, {strings}) { - return strings('misc.contentWarnings', {warnings}) + `<br><span class="reveal-interaction">${strings('misc.contentWarnings.reveal')}</span>` -} - -function getRevealStringFromTags(tags, {strings}) { - return tags && tags.some(tag => tag.isCW) && ( - getRevealStringFromWarnings(strings.list.unit(tags.filter(tag => tag.isCW).map(tag => tag.name)), {strings})); -} - -function generateCoverLink({ - link, strings, to, wikiData, - src, - path, - alt, - tags = [] -}) { - const { wikiInfo } = wikiData; - - if (!src && path) { - src = to(...path); - } - - if (!src) { - throw new Error(`Expected src or path`); - } - - return fixWS` - <div id="cover-art-container"> - ${img({ - src, - alt, - thumb: 'medium', - id: 'cover-art', - link: true, - square: true, - reveal: getRevealStringFromTags(tags, {strings}) - })} - ${wikiInfo.features.artTagUI && tags.filter(tag => !tag.isCW).length && fixWS` - <p class="tags"> - ${strings('releaseInfo.artTags')} - ${(tags - .filter(tag => !tag.isCW) - .map(link.tag) - .join(',\n'))} - </p> - `} - </div> - `; -} - -function getAlbumStylesheet(album, {to}) { - return [ - album.wallpaperArtists && fixWS` - body::before { - background-image: url("${to('media.albumWallpaper', album.directory)}"); - ${album.wallpaperStyle} - } - `, - album.bannerStyle && fixWS` - #banner img { - ${album.bannerStyle} - } - ` - ].filter(Boolean).join('\n'); -} - function generateRedirectPage(title, target, {strings}) { return fixWS` <!DOCTYPE html> @@ -2204,203 +2091,6 @@ function generateRedirectPage(title, target, {strings}) { `; } -function getArtistString(artists, { - iconifyURL, link, strings, - showIcons = false, - showContrib = false -}) { - return strings.list.and(artists.map(({ who, what }) => { - const { urls, directory, name } = who; - return [ - link.artist(who), - showContrib && what && `(${what})`, - showIcons && urls.length && `<span class="icons">(${ - strings.list.unit(urls.map(url => iconifyURL(url, {strings}))) - })</span>` - ].filter(Boolean).join(' '); - })); -} - -function getFlashDirectory(flash) { - // const kebab = getKebabCase(flash.name.replace('[S] ', '')); - // return flash.page + (kebab ? '-' + kebab : ''); - // return '' + flash.page; - return '' + flash.directory; -} - -function getTagDirectory({name}) { - return getKebabCase(name); -} - -function fancifyURL(url, {strings, album = false} = {}) { - const domain = new URL(url).hostname; - return fixWS`<a href="${url}" class="nowrap">${ - domain.includes('bandcamp.com') ? strings('misc.external.bandcamp') : - [ - 'music.solatrux.com' - ].includes(domain) ? strings('misc.external.bandcamp.domain', {domain}) : - [ - 'types.pl' - ].includes(domain) ? strings('misc.external.mastodon.domain', {domain}) : - domain.includes('youtu') ? (album - ? (url.includes('list=') - ? strings('misc.external.youtube.playlist') - : strings('misc.external.youtube.fullAlbum')) - : strings('misc.external.youtube')) : - domain.includes('soundcloud') ? strings('misc.external.soundcloud') : - domain.includes('tumblr.com') ? strings('misc.external.tumblr') : - domain.includes('twitter.com') ? strings('misc.external.twitter') : - domain.includes('deviantart.com') ? strings('misc.external.deviantart') : - domain.includes('wikipedia.org') ? strings('misc.external.wikipedia') : - domain.includes('poetryfoundation.org') ? strings('misc.external.poetryFoundation') : - domain.includes('instagram.com') ? strings('misc.external.instagram') : - domain.includes('patreon.com') ? strings('misc.external.patreon') : - domain - }</a>`; -} - -function fancifyFlashURL(url, flash, {strings}) { - const link = fancifyURL(url, {strings}); - return `<span class="nowrap">${ - url.includes('homestuck.com') ? (isNaN(Number(flash.page)) - ? strings('misc.external.flash.homestuck.secret', {link}) - : strings('misc.external.flash.homestuck.page', {link, page: flash.page})) : - url.includes('bgreco.net') ? strings('misc.external.flash.bgreco', {link}) : - url.includes('youtu') ? strings('misc.external.flash.youtube', {link}) : - link - }</span>`; -} - -function iconifyURL(url, {strings, to}) { - const domain = new URL(url).hostname; - const [ id, msg ] = ( - domain.includes('bandcamp.com') ? ['bandcamp', strings('misc.external.bandcamp')] : - ( - domain.includes('music.solatrus.com') - ) ? ['bandcamp', strings('misc.external.bandcamp.domain', {domain})] : - ( - domain.includes('types.pl') - ) ? ['mastodon', strings('misc.external.mastodon.domain', {domain})] : - domain.includes('youtu') ? ['youtube', strings('misc.external.youtube')] : - domain.includes('soundcloud') ? ['soundcloud', strings('misc.external.soundcloud')] : - domain.includes('tumblr.com') ? ['tumblr', strings('misc.external.tumblr')] : - domain.includes('twitter.com') ? ['twitter', strings('misc.external.twitter')] : - domain.includes('deviantart.com') ? ['deviantart', strings('misc.external.deviantart')] : - domain.includes('instagram.com') ? ['instagram', strings('misc.external.bandcamp')] : - ['globe', strings('misc.external.domain', {domain})] - ); - return fixWS`<a href="${url}" class="icon"><svg><title>${msg}</title><use href="${to('shared.staticFile', `icons.svg#icon-${id}`)}"></use></svg></a>`; -} - -function generateChronologyLinks(currentThing, { - contribKey, - getThings, - headingString, - link, - strings, - wikiData -}) { - const { albumData } = wikiData; - - const contributions = currentThing[contribKey]; - if (!contributions) { - return ''; - } - - if (contributions.length > 8) { - return `<div class="chronology">${strings('misc.chronology.seeArtistPages')}</div>`; - } - - return contributions.map(({ who: artist }) => { - const things = sortByDate(unique(getThings(artist))); - const releasedThings = things.filter(thing => { - const album = albumData.includes(thing) ? thing : thing.album; - return !(album && album.directory === UNRELEASED_TRACKS_DIRECTORY); - }); - const index = releasedThings.indexOf(currentThing); - - if (index === -1) return ''; - - // TODO: This can pro8a8ly 8e made to use generatePreviousNextLinks? - // We'd need to make generatePreviousNextLinks use toAnythingMan tho. - const previous = releasedThings[index - 1]; - const next = releasedThings[index + 1]; - const parts = [ - previous && linkAnythingMan(previous, { - link, wikiData, - color: false, - text: strings('misc.nav.previous') - }), - next && linkAnythingMan(next, { - link, wikiData, - color: false, - text: strings('misc.nav.next') - }) - ].filter(Boolean); - - const stringOpts = { - index: strings.count.index(index + 1, {strings}), - artist: link.artist(artist) - }; - - return fixWS` - <div class="chronology"> - <span class="heading">${strings(headingString, stringOpts)}</span> - ${parts.length && `<span class="buttons">(${parts.join(', ')})</span>`} - </div> - `; - }).filter(Boolean).join('\n'); -} - -function generateInfoGalleryLinks(currentThing, isGallery, { - link, strings, - linkKeyGallery, - linkKeyInfo -}) { - return [ - link[linkKeyInfo](currentThing, { - class: isGallery ? '' : 'current', - text: strings('misc.nav.info') - }), - link[linkKeyGallery](currentThing, { - class: isGallery ? 'current' : '', - text: strings('misc.nav.gallery') - }) - ].join(', '); -} - -function generatePreviousNextLinks(current, { - data, - link, - linkKey, - strings -}) { - const linkFn = link[linkKey]; - - const index = data.indexOf(current); - const previous = data[index - 1]; - const next = data[index + 1]; - - return [ - previous && linkFn(previous, { - attributes: { - id: 'previous-button', - title: previous.name - }, - text: strings('misc.nav.previous'), - color: false - }), - next && linkFn(next, { - attributes: { - id: 'next-button', - title: next.name - }, - text: strings('misc.nav.next'), - color: false - }) - ].filter(Boolean).join(', '); -} - // RIP toAnythingMan (previously getHrefOfAnythingMan), 2020-05-25<>2021-05-14. // ........Yet the function 8reathes life anew as linkAnythingMan! ::::) function linkAnythingMan(anythingMan, {link, wikiData, ...opts}) { @@ -2412,11 +2102,6 @@ function linkAnythingMan(anythingMan, {link, wikiData, ...opts}) { ) } -function classes(...args) { - const values = args.filter(Boolean); - return `class="${values.join(' ')}"`; -} - async function processLanguageFile(file, defaultStrings = null) { let contents; try { @@ -3255,6 +2940,11 @@ async function main() { bound.link = withEntries(unbound_link, entries => entries .map(([ key, fn ]) => [key, bindOpts(fn, {to})])); + bound.linkAnythingMan = bindOpts(linkAnythingMan, { + link: bound.link, + wikiData + }); + bound.parseAttributes = bindOpts(parseAttributes, { to }); @@ -3291,6 +2981,10 @@ async function main() { strings }); + bound.getLinkThemeString = getLinkThemeString; + + bound.getThemeString = getThemeString; + bound.getArtistString = bindOpts(getArtistString, { iconifyURL: bound.iconifyURL, link: bound.link, @@ -3311,12 +3005,14 @@ async function main() { bound.generateChronologyLinks = bindOpts(generateChronologyLinks, { link: bound.link, + linkAnythingMan: bound.linkAnythingMan, strings, wikiData }); bound.generateCoverLink = bindOpts(generateCoverLink, { [bindOpts.bindIndex]: 0, + img, link: bound.link, strings, to, @@ -3336,6 +3032,8 @@ async function main() { bound.getGridHTML = bindOpts(getGridHTML, { [bindOpts.bindIndex]: 0, + getLinkThemeString, + img, strings }); @@ -3354,6 +3052,14 @@ async function main() { to }); + bound.getRevealStringFromTags = bindOpts(getRevealStringFromTags, { + strings + }); + + bound.getRevealStringFromWarnings = bindOpts(getRevealStringFromWarnings, { + strings + }); + bound.getAlbumStylesheet = bindOpts(getAlbumStylesheet, { to }); diff --git a/src/util/colors.js b/src/util/colors.js index 01c55024..3a7ce8f3 100644 --- a/src/util/colors.js +++ b/src/util/colors.js @@ -19,31 +19,3 @@ export function getColors(primary) { return {primary, dim, bg}; } - -export function getLinkThemeString(color) { - if (!color) return ''; - - const { primary, dim } = getColors(color); - return `--primary-color: ${primary}; --dim-color: ${dim}`; -} - -export function getThemeString(color, additionalVariables = []) { - if (!color) return ''; - - const { primary, dim, bg } = getColors(color); - - const variables = [ - `--primary-color: ${primary}`, - `--dim-color: ${dim}`, - `--bg-color: ${bg}`, - ...additionalVariables - ].filter(Boolean); - - if (!variables.length) return ''; - - return ( - `:root {\n` + - variables.map(line => ` ` + line + ';\n').join('') + - `}` - ); -} diff --git a/src/util/link.js b/src/util/link.js index 107b35ff..7ed5fd8e 100644 --- a/src/util/link.js +++ b/src/util/link.js @@ -10,7 +10,14 @@ // gener8ting just a8out any link on the site. import * as html from './html.js' -import { getLinkThemeString } from './colors.js' +import { getColors } from './colors.js' + +export function getLinkThemeString(color) { + if (!color) return ''; + + const { primary, dim } = getColors(color); + return `--primary-color: ${primary}; --dim-color: ${dim}`; +} const linkHelper = (hrefFn, {color = true, attr = null} = {}) => (thing, { |