From 3fb98fcbeab5173acec5222bec7a4adf597e88bb Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Sat, 7 Jan 2023 09:53:29 -0400 Subject: extract utility binds, content transform fns towards basic dynamics pt. 1 (#124) --- src/util/transform-content.js | 447 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 src/util/transform-content.js (limited to 'src/util') diff --git a/src/util/transform-content.js b/src/util/transform-content.js new file mode 100644 index 00000000..2a7d859b --- /dev/null +++ b/src/util/transform-content.js @@ -0,0 +1,447 @@ +// See also replacer.js, which covers the actual syntax parser and node +// interpreter. This file works with replacer.js to provide higher-level +// interfaces for converting various content found in wiki data to HTML for +// display on the site. + +import * as html from './html.js'; +export {transformInline} from './replacer.js'; + +export const replacerSpec = { + album: { + find: 'album', + link: 'album', + }, + 'album-commentary': { + find: 'album', + link: 'albumCommentary', + }, + 'album-gallery': { + find: 'album', + link: 'albumGallery', + }, + artist: { + find: 'artist', + link: 'artist', + }, + 'artist-gallery': { + find: 'artist', + link: 'artistGallery', + }, + 'commentary-index': { + find: null, + link: 'commentaryIndex', + }, + date: { + find: null, + value: (ref) => new Date(ref), + html: (date, {language}) => + html.tag('time', + {datetime: date.toString()}, + language.formatDate(date)), + }, + 'flash-index': { + find: null, + link: 'flashIndex', + }, + flash: { + find: 'flash', + link: 'flash', + transformName(name, node, input) { + const nextCharacter = input[node.iEnd]; + const lastCharacter = name[name.length - 1]; + if (![' ', '\n', '<'].includes(nextCharacter) && lastCharacter === '.') { + return name.slice(0, -1); + } else { + return name; + } + }, + }, + group: { + find: 'group', + link: 'groupInfo', + }, + 'group-gallery': { + find: 'group', + link: 'groupGallery', + }, + home: { + find: null, + link: 'home', + }, + 'listing-index': { + find: null, + link: 'listingIndex', + }, + listing: { + find: 'listing', + link: 'listing', + }, + media: { + find: null, + link: 'media', + }, + 'news-index': { + find: null, + link: 'newsIndex', + }, + 'news-entry': { + find: 'newsEntry', + link: 'newsEntry', + }, + root: { + find: null, + link: 'root', + }, + site: { + find: null, + link: 'site', + }, + static: { + find: 'staticPage', + link: 'staticPage', + }, + string: { + find: null, + value: (ref) => ref, + html: (ref, {language, args}) => language.$(ref, args), + }, + tag: { + find: 'artTag', + link: 'tag', + }, + track: { + find: 'track', + link: 'track', + }, +}; + +function splitLines(text) { + return text.split(/\r\n|\r|\n/); +} + +function joinLineBreaks(sourceLines) { + const outLines = []; + + let lineSoFar = ''; + for (let i = 0; i < sourceLines.length; i++) { + const line = sourceLines[i]; + lineSoFar += line; + if (!line.endsWith('
')) { + outLines.push(lineSoFar); + lineSoFar = ''; + } + } + + if (lineSoFar) { + outLines.push(lineSoFar); + } + + return outLines; +} + +function parseAttributes(string, {to}) { + const attributes = Object.create(null); + const skipWhitespace = (i) => { + const ws = /\s/; + if (ws.test(string[i])) { + const match = string.slice(i).match(/[^\s]/); + if (match) { + return i + match.index; + } else { + return string.length; + } + } else { + return i; + } + }; + + for (let i = 0; i < string.length; ) { + i = skipWhitespace(i); + const aStart = i; + const aEnd = i + string.slice(i).match(/[\s=]|$/).index; + const attribute = string.slice(aStart, aEnd); + i = skipWhitespace(aEnd); + if (string[i] === '=') { + i = skipWhitespace(i + 1); + let end, endOffset; + if (string[i] === '"' || string[i] === "'") { + end = string[i]; + endOffset = 1; + i++; + } else { + end = '\\s'; + endOffset = 0; + } + const vStart = i; + const vEnd = i + string.slice(i).match(new RegExp(`${end}|$`)).index; + const value = string.slice(vStart, vEnd); + i = vEnd + endOffset; + if (attribute === 'src' && value.startsWith('media/')) { + attributes[attribute] = to('media.path', value.slice('media/'.length)); + } else { + attributes[attribute] = value; + } + } else { + attributes[attribute] = attribute; + } + } + return Object.fromEntries( + Object.entries(attributes).map(([key, val]) => [ + key, + val === 'true' + ? true + : val === 'false' + ? false + : val === key + ? true + : val, + ]) + ); +} + +function unbound_transformMultiline(text, { + img, + to, + transformInline, + + thumb = null, +}) { + // Heck yes, HTML magics. + + text = transformInline(text.trim()); + + const outLines = []; + + const indentString = ' '.repeat(4); + + let levelIndents = []; + const openLevel = (indent) => { + // opening a sublist is a pain: to be semantically *and* visually + // correct, we have to append the