From dfeb496e8593e28049617af32742a967e94cddff Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 4 May 2021 21:38:23 -0300 Subject: relatively acceptable HTML utility shenanigans --- upd8-util.js | 5 ++ upd8.js | 217 +++++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 150 insertions(+), 72 deletions(-) diff --git a/upd8-util.js b/upd8-util.js index abeed6c..4c4186f 100644 --- a/upd8-util.js +++ b/upd8-util.js @@ -416,3 +416,8 @@ module.exports.promisifyProcess = function(proc, showLogging = true) { // Stolen from jq! Which pro8a8ly stole the concept from other places. Nice. module.exports.withEntries = (obj, fn) => Object.fromEntries(fn(Object.entries(obj))); + +// Nothin' more to it than what it says. Runs a function in-place. Provides an +// altern8tive syntax to the usual IIFEs (e.g. (() => {})()) when you want to +// open a scope and run some statements while inside an existing expression. +module.exports.call = fn => fn(); diff --git a/upd8.js b/upd8.js index 68d95ba..e17005d 100755 --- a/upd8.js +++ b/upd8.js @@ -107,6 +107,7 @@ const unlink = util.promisify(fs.unlink); const { cacheOneArg, + call, chunkByConditions, chunkByProperties, curry, @@ -183,6 +184,99 @@ let queueSize; let languages; +const html = { + // Non-comprehensive. ::::P + selfClosingTags: ['br', 'img'], + + // Pass to tag() as an attri8utes key to make tag() return a 8lank string + // if the provided content is empty. Useful for when you'll only 8e showing + // an element according to the presence of content that would 8elong there. + onlyIfContent: Symbol(), + + tag(tagName, ...args) { + const selfClosing = html.selfClosingTags.includes(tagName); + + let openTag; + let content; + let attrs; + + if (typeof args[0] === 'object' && !Array.isArray(args[0])) { + attrs = args[0]; + content = args[1]; + } else { + content = args[0]; + } + + if (selfClosing && content) { + throw new Error(`Tag <${tagName}> is self-closing but got content!`); + } + + if (attrs?.[html.onlyIfContent] && !content) { + return ''; + } + + if (attrs) { + const attrString = html.attributes(args[0]); + if (attrString) { + openTag = `${tagName} ${attrString}`; + } + } + + if (!openTag) { + openTag = tagName; + } + + if (Array.isArray(content)) { + content = content.filter(Boolean).join('\n'); + } + + if (content) { + if (content.includes('\n')) { + return fixWS` + <${openTag}> + ${content} + + `; + } else { + return `<${openTag}>${content}`; + } + } else { + if (selfClosing) { + return `<${openTag}>`; + } else { + return `<${openTag}>`; + } + } + }, + + escapeAttributeValue(value) { + return value + .replaceAll('"', '"') + .replaceAll("'", '''); + }, + + attributes(attribs) { + return Object.entries(attribs) + .map(([ key, val ]) => { + if (!val) + return [key, val]; + else if (typeof val === 'string' || typeof val === 'boolean') + return [key, val]; + else if (typeof val === 'number') + return [key, val.toString()]; + else if (Array.isArray(val)) + return [key, val.join(' ')]; + else + throw new Error(`Attribute value for ${key} should be primitive or array, got ${typeof val}`); + }) + .filter(([ key, val ]) => val) + .map(([ key, val ]) => (typeof val === 'boolean' + ? `${key}` + : `${key}="${html.escapeAttributeValue(val)}"`)) + .join(' '); + } +}; + const urlSpec = { data: { prefix: 'data/', @@ -276,11 +370,12 @@ const linkHelper = (hrefFn, {color = true, attr = null} = {}) => class: className = '', hash = '' }) => ( - `${text || thing.name}` + }, text || thing.name) ); const linkDirectory = (key, {expose = null, attr = null, ...conf} = {}) => @@ -2031,19 +2126,6 @@ function stringifyArtistData() { }, stringifyIndent); } -function escapeAttributeValue(value) { - return value - .replaceAll('"', '"') - .replaceAll("'", '''); -} - -function attributes(attribs) { - return Object.entries(attribs) - .filter(([ key, val ]) => val !== '') - .map(([ key, val ]) => `${key}="${escapeAttributeValue(val)}"`) - .join(' '); -} - function img({ src = '', alt = '', @@ -2063,7 +2145,7 @@ function img({ const originalSrc = src; const thumbSrc = thumbKey ? thumb[thumbKey](src) : src; - const imgAttributes = attributes({ + const imgAttributes = html.attributes({ id: link ? '' : id, class: className, alt, @@ -2083,36 +2165,35 @@ function img({ return nonlazyHTML; } - function wrap(html, hide = false) { - html = fixWS` -
${html}
- `; + function wrap(input, hide = false) { + let wrapped = input; - html = fixWS` -
${html}
- `; + wrapped = `
${wrapped}
`; + wrapped = `
${wrapped}
`; if (reveal) { - html = fixWS` + wrapped = fixWS`
- ${html} + ${wrapped} ${reveal}
`; } if (willSquare) { - html = fixWS`
${html}
`; + wrapped = html.tag('div', {class: 'square-content'}, wrapped); + wrapped = html.tag('div', {class: ['square', hide && !willLink && 'js-hide']}, wrapped); } if (willLink) { - html = `${html}`; + }, wrapped); } - return html; + return wrapped; } } @@ -2420,29 +2501,27 @@ writePage.html = (pageFn, {paths, strings, to}) => { navLinkParts.push(part); } - const navContentHTML = [ - links.length && fixWS` - - `, + const navHTML = html.tag('nav', { + [html.onlyIfContent]: true, + id: 'header', + class: nav.classes + }, [ + links.length && html.tag('h2', {class: 'highlight-last-link'}, navLinkParts), nav.content - ].filter(Boolean).join('\n'); - - const navHTML = navContentHTML && fixWS` - - `; + ]); - const bannerHTML = banner.position && banner.src && fixWS` - - `; + const bannerHTML = banner.position && banner.src && html.tag('div', + { + id: 'banner', + class: banner.classes + }, + html.tag('img', { + src: banner.src, + alt: banner.alt, + width: 1100, + height: 200 + }) + ); const layoutHTML = [ navHTML, @@ -2490,7 +2569,7 @@ writePage.html = (pageFn, {paths, strings, to}) => { return filterEmptyLines(fixWS` - { ${title} - ${Object.entries(meta).filter(([ key, value ]) => value).map(([ key, value ]) => ``).join('\n')} + ${Object.entries(meta).filter(([ key, value ]) => value).map(([ key, value ]) => ``).join('\n')} ${canonical && ``} ${(theme || stylesheet) && fixWS` @@ -2512,7 +2591,7 @@ writePage.html = (pageFn, {paths, strings, to}) => { `} - +
${mainHTML && fixWS`
@@ -3271,25 +3350,19 @@ function writeTrackPage(track) { .flatMap(track => track.flashes.map(flash => ({flash, as: track})))); } - const generateTrackList = (tracks, {strings, to}) => fixWS` -
    - ${tracks.map(track => - // vim doesnt like this code much lol - (({ - line = strings('trackList.item.withArtists', { - track: strings.link.track(track, {to}), - by: `${strings('trackList.item.withArtists.by', { - artists: getArtistString(track.artists, {strings, to}) - })}` - }) - }) => ( - (track.aka - ? `
  • ${strings('trackList.item.rerelease', {track: line})}
  • ` - : `
  • ${line}
  • `) - ))({}) - ).join('\n')} -
- `; + const generateTrackList = (tracks, {strings, to}) => html.tag('ul', + tracks.map(track => { + const line = strings('trackList.item.withArtists', { + track: strings.link.track(track, {to}), + by: `${strings('trackList.item.withArtists.by', { + artists: getArtistString(track.artists, {strings, to}) + })}` + }); + return (track.aka + ? `
  • ${strings('trackList.item.rerelease', {track: line})}
  • ` + : `
  • ${line}
  • `); + }) + ); const hasCommentary = track.commentary || otherReleases.some(t => t.commentary); const generateCommentary = ({strings, to}) => transformMultiline( -- cgit 1.3.0-6-gf8a5