« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/content
diff options
context:
space:
mode:
Diffstat (limited to 'src/content')
-rw-r--r--src/content/dependencies/generateAlbumAdditionalFilesList.js10
-rw-r--r--src/content/dependencies/generateAlbumInfoPageContent.js186
-rw-r--r--src/content/dependencies/generateAlbumTrackList.js2
-rw-r--r--src/content/dependencies/generateTrackInfoPageContent.js190
-rw-r--r--src/content/dependencies/transformContent.js67
5 files changed, 332 insertions, 123 deletions
diff --git a/src/content/dependencies/generateAlbumAdditionalFilesList.js b/src/content/dependencies/generateAlbumAdditionalFilesList.js
index f8fd5499..5fd4e05b 100644
--- a/src/content/dependencies/generateAlbumAdditionalFilesList.js
+++ b/src/content/dependencies/generateAlbumAdditionalFilesList.js
@@ -9,24 +9,24 @@ export default {
     'urls',
   ],
 
-  data(album, {fileSize = true} = {}) {
+  data(album, additionalFiles, {fileSize = true} = {}) {
     return {
       albumDirectory: album.directory,
-      fileLocations: album.additionalFiles.flatMap(({files}) => files),
+      fileLocations: additionalFiles.flatMap(({files}) => files),
       showFileSizes: fileSize,
     };
   },
 
-  relations(relation, album, {fileSize = true} = {}) {
+  relations(relation, album, additionalFiles, {fileSize = true} = {}) {
     return {
       additionalFilesList:
-        relation('generateAdditionalFilesList', album.additionalFiles, {
+        relation('generateAdditionalFilesList', additionalFiles, {
           fileSize,
         }),
 
       additionalFileLinks:
         Object.fromEntries(
-          album.additionalFiles
+          additionalFiles
             .flatMap(({files}) => files)
             .map(file => [
               file,
diff --git a/src/content/dependencies/generateAlbumInfoPageContent.js b/src/content/dependencies/generateAlbumInfoPageContent.js
index 76862f9c..7b8522b7 100644
--- a/src/content/dependencies/generateAlbumInfoPageContent.js
+++ b/src/content/dependencies/generateAlbumInfoPageContent.js
@@ -13,65 +13,104 @@ export default {
     'linkExternal',
   ],
 
-  extraDependencies: [
-    'html',
-    'language',
-    'transformMultiline',
-  ],
+  extraDependencies: ['html', 'language'],
 
   relations(relation, album) {
     const relations = {};
+    const sections = relations.sections = {};
 
     const contributionLinksRelation = contribs =>
       contribs.map(contrib =>
         relation('linkContribution', contrib.who, contrib.what));
 
+    // Section: Release info
+
+    const releaseInfo = sections.releaseInfo = {};
+
+    if (!empty(album.artistContribs)) {
+      releaseInfo.artistContributionLinks =
+        contributionLinksRelation(album.artistContribs);
+    }
+
     if (album.hasCoverArt) {
       relations.cover =
         relation('generateCoverArtwork', album.artTags);
+      releaseInfo.coverArtistContributionLinks =
+        contributionLinksRelation(album.coverArtistContribs);
+    } else {
+      relations.cover = null;
     }
 
-    relations.artistLinks =
-      contributionLinksRelation(album.artistContribs);
+    if (album.hasWallpaperArt) {
+      releaseInfo.wallpaperArtistContributionLinks =
+        contributionLinksRelation(album.wallpaperArtistContribs);
+    }
+
+    if (album.hasBannerArt) {
+      releaseInfo.bannerArtistContributionLinks =
+        contributionLinksRelation(album.bannerArtistContribs);
+    }
+
+    // Section: Listen on
+
+    if (!empty(album.urls)) {
+      const listen = sections.listen = {};
 
-    relations.coverArtistLinks =
-      contributionLinksRelation(album.coverArtistContribs);
+      listen.heading =
+        relation('generateContentHeading');
+
+      listen.externalLinks =
+        album.urls.map(url =>
+          relation('linkExternal', url, {type: 'album'}));
+    }
 
-    relations.wallpaperArtistLinks =
-      contributionLinksRelation(album.wallpaperArtistContribs);
+    // Section: Extra links
 
-    relations.bannerArtistLinks =
-      contributionLinksRelation(album.bannerArtistContribs);
+    const extra = sections.extra = {};
 
     if (album.tracks.some(t => t.hasUniqueCoverArt)) {
-      relations.galleryLink =
+      extra.galleryLink =
         relation('linkAlbumGallery', album);
     }
 
     if (album.commentary || album.tracks.some(t => t.commentary)) {
-      relations.commentaryLink =
+      extra.commentaryLink =
         relation('linkAlbumCommentary', album);
     }
 
-    relations.externalLinks =
-      album.urls.map(url =>
-        relation('linkExternal', url, {type: 'album'}));
+    if (!empty(album.additionalFiles)) {
+      extra.additionalFilesShortcut =
+        relation('generateAdditionalFilesShortcut', album.additionalFiles);
+    }
+
+    // Section: Track list
+
+    relations.trackList =
+      relation('generateAlbumTrackList', album);
 
-    relations.trackList = relation('generateAlbumTrackList', album);
+    // Section: Additional files
 
     if (!empty(album.additionalFiles)) {
-      relations.additionalFilesShortcut =
-        relation('generateAdditionalFilesShortcut', album.additionalFiles);
+      const additionalFiles = sections.additionalFiles = {};
 
-      relations.additionalFilesHeading =
+      additionalFiles.heading =
         relation('generateContentHeading');
 
-      relations.additionalFilesList =
-        relation('generateAlbumAdditionalFilesList', album);
+      additionalFiles.additionalFilesList =
+        relation('generateAlbumAdditionalFilesList', album, album.additionalFiles);
     }
 
-    relations.artistCommentaryHeading =
-      relation('generateContentHeading');
+    // Section: Artist commentary
+
+    if (album.commentary) {
+      const artistCommentary = sections.artistCommentary = {};
+
+      artistCommentary.heading =
+        relation('generateContentHeading');
+
+      artistCommentary.content =
+        relation('transformContent', album.commentary);
+    }
 
     return relations;
   },
@@ -99,7 +138,6 @@ export default {
     }
 
     data.dateAddedToWiki = album.dateAddedToWiki;
-    data.artistCommentary = album.commentary;
 
     return data;
   },
@@ -107,18 +145,20 @@ export default {
   generate(data, relations, {
     html,
     language,
-    transformMultiline,
   }) {
     const content = {};
 
-    const formatContributions = contributionLinks =>
-      language.formatConjunctionList(
-        contributionLinks.map(link =>
-          link
-            .slots({
-              showContribution: true,
-              showIcons: true,
-            })));
+    const {sections: sec} = relations;
+
+    const formatContributions =
+      (stringKey, contributionLinks, {showContribution = true, showIcons = true} = {}) =>
+        contributionLinks &&
+          language.$(stringKey, {
+            artists:
+              language.formatConjunctionList(
+                contributionLinks.map(link =>
+                  link.slots({showContribution, showIcons}))),
+          });
 
     if (data.hasCoverArt) {
       content.cover = relations.cover
@@ -126,6 +166,8 @@ export default {
           path: ['media.albumCover', data.coverArtDirectory, data.coverArtFileExtension],
           alt: language.$('misc.alt.trackCover')
         });
+    } else {
+      content.cover = null;
     }
 
     content.main = {
@@ -137,25 +179,10 @@ export default {
             [html.joinChildren]: html.tag('br'),
           },
           [
-            !empty(relations.artistLinks) &&
-              language.$('releaseInfo.by', {
-                artists: formatContributions(relations.artistLinks),
-              }),
-
-            !empty(relations.coverArtistLinks) &&
-              language.$('releaseInfo.coverArtBy', {
-                artists: formatContributions(relations.coverArtistLinks),
-              }),
-
-            !empty(relations.wallpaperArtistLinks) &&
-              language.$('releaseInfo.wallpaperArtBy', {
-                artists: formatContributions(relations.wallpaperArtistLinks),
-              }),
-
-            !empty(relations.bannerArtistLinks) &&
-              language.$('releaseInfo.bannerArtBy', {
-                artists: formatContributions(relations.bannerArtistLinks),
-              }),
+            formatContributions('releaseInfo.by', sec.releaseInfo.artistContributionLinks),
+            formatContributions('releaseInfo.coverArtBy', sec.releaseInfo.coverArtistContributionLinks),
+            formatContributions('releaseInfo.wallpaperArtBy', sec.releaseInfo.wallpaperArtistContributionLinks),
+            formatContributions('releaseInfo.bannerArtBy', sec.releaseInfo.bannerArtistContributionLinks),
 
             data.date &&
               language.$('releaseInfo.released', {
@@ -176,35 +203,48 @@ export default {
               }),
           ]),
 
+        sec.listen &&
+          sec.listen.heading.slots({
+            id: 'listen-on',
+            title:
+              language.$('releaseInfo.listenOn', {
+                links: language.formatDisjunctionList(sec.listen.externalLinks),
+              }),
+          }),
+
         html.tag('p',
           {
             [html.onlyIfContent]: true,
             [html.joinChildren]: html.tag('br'),
           },
           [
-            relations.additionalFilesShortcut,
+            sec.extra.additionalFilesShortcut,
+
+            sec.extra.galleryLink && sec.extra.commentaryLink &&
+              language.$('releaseInfo.viewGalleryOrCommentary', {
+                gallery:
+                  sec.extra.galleryLink
+                    .slot('content', language.$('releaseInfo.viewGalleryOrCommentary.gallery')),
+                commentary:
+                  sec.extra.commentaryLink
+                    .slot('content', language.$('releaseInfo.viewGalleryOrCommentary.commentary')),
+              }),
 
-            relations.galleryLink &&
+            sec.extra.galleryLink && !sec.extra.commentaryLink &&
               language.$('releaseInfo.viewGallery', {
                 link:
-                  relations.galleryLink
+                  sec.extra.galleryLink
                     .slot('content', language.$('releaseInfo.viewGallery.link')),
               }),
 
-            relations.commentaryLink &&
+            !sec.extra.galleryLink && sec.extra.commentaryLink &&
               language.$('releaseInfo.viewCommentary', {
                 link:
-                  relations.commentaryLink
+                  sec.extra.commentaryLink
                     .slot('content', language.$('releaseInfo.viewCommentary.link')),
               }),
           ]),
 
-        !empty(relations.externalLinks) &&
-          html.tag('p',
-            language.$('releaseInfo.listenOn', {
-              links: language.formatDisjunctionList(relations.externalLinks),
-            })),
-
         relations.trackList,
 
         html.tag('p',
@@ -219,11 +259,10 @@ export default {
               }),
           ]),
 
-        relations.additionalFilesList && [
-          relations.additionalFilesHeading
+        sec.additionalFiles && [
+          sec.additionalFiles.heading
             .slots({
               id: 'additional-files',
-
               title:
                 language.$('releaseInfo.additionalFiles.heading', {
                   additionalFiles:
@@ -231,18 +270,19 @@ export default {
                 }),
             }),
 
-          relations.additionalFilesList,
+          sec.additionalFiles.additionalFilesList,
         ],
 
-        data.artistCommentary && [
-          relations.artistCommentaryHeading
+        sec.artistCommentary && [
+          sec.artistCommentary.heading
             .slots({
               id: 'artist-commentary',
               title: language.$('releaseInfo.artistCommentary')
             }),
 
           html.tag('blockquote',
-            transformMultiline(data.artistCommentary)),
+            sec.artistCommentary.content
+              .slot('mode', 'multiline')),
         ],
       ]),
     };
diff --git a/src/content/dependencies/generateAlbumTrackList.js b/src/content/dependencies/generateAlbumTrackList.js
index f2f2279d..ce174953 100644
--- a/src/content/dependencies/generateAlbumTrackList.js
+++ b/src/content/dependencies/generateAlbumTrackList.js
@@ -101,7 +101,7 @@ export default {
         {class: 'album-group-list'},
         data.trackSectionInfo.map((info, index) => [
           html.tag('dt',
-            {class: 'content-heading'},
+            {class: 'content-heading', tabindex: '0'},
             language.$('trackList.section.withDuration', {
               section: info.name,
               duration:
diff --git a/src/content/dependencies/generateTrackInfoPageContent.js b/src/content/dependencies/generateTrackInfoPageContent.js
index 57bdc0c2..47a9130d 100644
--- a/src/content/dependencies/generateTrackInfoPageContent.js
+++ b/src/content/dependencies/generateTrackInfoPageContent.js
@@ -2,6 +2,8 @@ import {empty} from '../../util/sugar.js';
 
 export default {
   contentDependencies: [
+    'generateAdditionalFilesShortcut',
+    'generateAlbumAdditionalFilesList',
     'generateContentHeading',
     'generateCoverArtwork',
     'generateTrackList',
@@ -12,11 +14,7 @@ export default {
     'linkTrack',
   ],
 
-  extraDependencies: [
-    'html',
-    'language',
-    'transformMultiline',
-  ],
+  extraDependencies: ['html', 'language'],
 
   relations(relation, track, {topLevelGroups}) {
     const {album} = track;
@@ -28,6 +26,11 @@ export default {
       contribs.map(contrib =>
         relation('linkContribution', contrib.who, contrib.what));
 
+    const additionalFilesSection = additionalFiles => ({
+      heading: relation('generateContentHeading'),
+      list: relation('generateAlbumAdditionalFilesList', album, additionalFiles),
+    });
+
     // Section: Release info
 
     const releaseInfo = sections.releaseInfo = {};
@@ -60,6 +63,15 @@ export default {
           relation('linkExternal', url));
     }
 
+    // Section: Extra links
+
+    const extra = sections.extra = {};
+
+    if (!empty(track.additionalFiles)) {
+      extra.additionalFilesShortcut =
+        relation('generateAdditionalFilesShortcut', track.additionalFiles);
+    }
+
     // Section: Other releases
 
     if (!empty(track.otherReleases)) {
@@ -113,6 +125,44 @@ export default {
           topLevelGroups);
     }
 
+    // Section: Lyrics
+
+    if (track.lyrics) {
+      const lyrics = sections.lyrics = {};
+
+      lyrics.heading =
+        relation('generateContentHeading');
+
+      lyrics.content =
+        relation('transformContent', track.lyrics);
+    }
+
+    // Sections: Sheet music files, MIDI/proejct files, additional files
+
+    if (!empty(track.sheetMusicFiles)) {
+      sections.sheetMusicFiles = additionalFilesSection(track.sheetMusicFiles);
+    }
+
+    if (!empty(track.midiProjectFiles)) {
+      sections.midiProjectFiles = additionalFilesSection(track.midiProjectFiles);
+    }
+
+    if (!empty(track.additionalFiles)) {
+      sections.additionalFiles = additionalFilesSection(track.additionalFiles);
+    }
+
+    // Section: Artist commentary
+
+    if (track.commentary) {
+      const artistCommentary = sections.artistCommentary = {};
+
+      artistCommentary.heading =
+        relation('generateContentHeading');
+
+      artistCommentary.content =
+        relation('transformContent', track.commentary);
+    }
+
     return relations;
   },
 
@@ -141,23 +191,27 @@ export default {
       data.coverArtFileExtension = album.coverArtFileExtension;
     }
 
+    if (!empty(track.additionalFiles)) {
+      data.numAdditionalFiles = track.additionalFiles.length;
+    }
+
     return data;
   },
 
-  generate(data, relations, {
-    html,
-    language,
-    // transformMultiline,
-  }) {
+  generate(data, relations, {html, language}) {
     const content = {};
 
     const {sections: sec} = relations;
 
     const formatContributions =
-      (contributionLinks, {showContribution = true, showIcons = true} = {}) =>
-        language.formatConjunctionList(
-          contributionLinks.map(link =>
-            link.slots({showContribution, showIcons})));
+      (stringKey, contributionLinks, {showContribution = true, showIcons = true} = {}) =>
+        contributionLinks &&
+          language.$(stringKey, {
+            artists:
+              language.formatConjunctionList(
+                contributionLinks.map(link =>
+                  link.slots({showContribution, showIcons}))),
+          });
 
     if (data.hasUniqueCoverArt) {
       content.cover = relations.cover
@@ -190,15 +244,8 @@ export default {
           [html.onlyIfContent]: true,
           [html.joinChildren]: html.tag('br'),
         }, [
-          sec.releaseInfo.artistContributionLinks &&
-            language.$('releaseInfo.by', {
-              artists: formatContributions(sec.releaseInfo.artistContributionLinks),
-            }),
-
-          sec.releaseInfo.coverArtistContributionLinks &&
-            language.$('releaseInfo.coverArtBy', {
-              artists: formatContributions(sec.releaseInfo.coverArtistContributionLinks),
-            }),
+          formatContributions('releaseInfo.by', sec.releaseInfo.artistContributionLinks),
+          formatContributions('releaseInfo.coverArtBy', sec.releaseInfo.coverArtistContributionLinks),
 
           data.date &&
             language.$('releaseInfo.released', {
@@ -216,41 +263,48 @@ export default {
             }),
         ]),
 
-        /*
+        sec.listen.heading.slots({
+          id: 'listen-on',
+          title:
+            (sec.listen.externalLinks
+              ? language.$('releaseInfo.listenOn', {
+                  links: language.formatDisjunctionList(sec.listen.externalLinks),
+                })
+              : language.$('releaseInfo.listenOn.noLinks', {
+                  name: html.tag('i', data.name),
+                })),
+        }),
+
         html.tag('p',
           {
             [html.onlyIfContent]: true,
             [html.joinChildren]: '<br>',
           },
           [
-            hasSheetMusicFiles &&
+            sec.sheetMusicFiles &&
               language.$('releaseInfo.sheetMusicFiles.shortcut', {
                 link: html.tag('a',
                   {href: '#sheet-music-files'},
                   language.$('releaseInfo.sheetMusicFiles.shortcut.link')),
               }),
 
-            hasMidiProjectFiles &&
+            sec.midiProjectFiles &&
               language.$('releaseInfo.midiProjectFiles.shortcut', {
                 link: html.tag('a',
                   {href: '#midi-project-files'},
                   language.$('releaseInfo.midiProjectFiles.shortcut.link')),
               }),
 
-            hasAdditionalFiles &&
-              generateAdditionalFilesShortcut(track.additionalFiles),
-          ]),
-        */
+            sec.additionalFiles &&
+              sec.extra.additionalFilesShortcut,
 
-        sec.listen.heading.slots({
-          id: 'listen-on',
-          title:
-            (sec.listen.externalLinks
-              ? language.$('releaseInfo.listenOn', {
-                  links: language.formatDisjunctionList(sec.listen.externalLinks),
-                })
-              : language.$('releaseInfo.listenOn.noLinks')),
-        }),
+            sec.artistCommentary &&
+              language.$('releaseInfo.readCommentary', {
+                link: html.tag('a',
+                  {href: '#artist-commentary'},
+                  language.$('releaseInfo.readCommentary.link')),
+              }),
+          ]),
 
         sec.otherReleases && [
           sec.otherReleases.heading
@@ -309,6 +363,64 @@ export default {
 
           sec.referencedBy.list,
         ],
+
+        sec.lyrics && [
+          sec.lyrics.heading
+            .slots({
+              id: 'lyrics',
+              title: language.$('releaseInfo.lyrics'),
+            }),
+
+          html.tag('blockquote',
+            sec.lyrics.content
+              .slot('mode', 'lyrics')),
+        ],
+
+        sec.sheetMusicFiles && [
+          sec.sheetMusicFiles.heading
+            .slots({
+              id: 'sheet-music-files',
+              title: language.$('releaseInfo.sheetMusicFiles.heading'),
+            }),
+
+          sec.sheetMusicFiles.list,
+        ],
+
+        sec.midiProjectFiles && [
+          sec.midiProjectFiles.heading
+            .slots({
+              id: 'midi-project-files',
+              title: language.$('releaseInfo.midiProjectFiles.heading'),
+            }),
+
+          sec.midiProjectFiles.list,
+        ],
+
+        sec.additionalFiles && [
+          sec.additionalFiles.heading
+            .slots({
+              id: 'additional-files',
+              title:
+                language.$('releaseInfo.additionalFiles.heading', {
+                  additionalFiles:
+                    language.countAdditionalFiles(data.numAdditionalFiles, {unit: true}),
+                }),
+            }),
+
+          sec.additionalFiles.list,
+        ],
+
+        sec.artistCommentary && [
+          sec.artistCommentary.heading
+            .slots({
+              id: 'artist-commentary',
+              title: language.$('releaseInfo.artistCommentary')
+            }),
+
+          html.tag('blockquote',
+            sec.artistCommentary.content
+              .slot('mode', 'multiline')),
+        ],
       ]),
     };
 
diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js
index 262c2982..c2ca548a 100644
--- a/src/content/dependencies/transformContent.js
+++ b/src/content/dependencies/transformContent.js
@@ -1,3 +1,5 @@
+import {marked} from 'marked';
+
 import {bindFind} from '../../util/find.js';
 import {parseInput} from '../../util/replacer.js';
 import {replacerSpec} from '../../util/transform-content.js';
@@ -191,7 +193,13 @@ export default {
         }
 
         if (node.type === 'link') {
-          const {link, label, hash} = relations.links[linkIndex++];
+          const linkNode = relations.links[linkIndex++];
+          if (linkNode.type === 'text') {
+            return {type: 'text', data: linkNode.data};
+          }
+
+          const {link, label, hash} = linkNode;
+
           return {
             type: 'link',
             data: link.slots({content: label, hash}),
@@ -242,16 +250,65 @@ export default {
           return html.tags(contentFromNodes.map(node => node.data));
         }
 
-        // In multiline mode...
+        // Multiline mode has a secondary processing stage where it's passed...
+        // through marked! Rolling your own Markdown only gets you so far :D
+
+        const markedOptions = {
+          headerIds: false,
+          mangle: false,
+        };
+
+        // This is separated into its own function just since we're gonna reuse
+        // it in a minute if everything goes to heck in lyrics mode.
+        const transformMultiline = () =>
+          marked.parse(
+            contentFromNodes
+              .map(node => {
+                if (node.type === 'text') {
+                  return node.data.replace(/\n+/g, '\n\n');
+                } else {
+                  return node.data.toString();
+                }
+              })
+              .join(''),
+            markedOptions);
 
         if (slots.mode === 'multiline') {
-          return html.tags(contentFromNodes.map(node => node.data));
+          // Unfortunately, we kind of have to be super evil here and stringify
+          // the links, or else parse marked's output into html tags, which is
+          // very out of scope at the moment.
+          return transformMultiline();
         }
 
-        // In lyrics mode...
+        // Lyrics mode goes through marked too, but line breaks are processed
+        // differently. Instead of having each line get its own paragraph,
+        // "adjacent" lines are joined together (with blank lines separating
+        // each verse/paragraph).
 
         if (slots.mode === 'lyrics') {
-          return html.tags(contentFromNodes.map(node => node.data));
+          // If it looks like old data, using <br> instead of bunched together
+          // lines... then oh god... just use transformMultiline. Perishes.
+          if (
+            contentFromNodes.some(node =>
+              node.type === 'text' &&
+              node.data.includes('<br'))
+          ) {
+            return transformMultiline();
+          }
+
+          // Lyrics mode is also evil for the same stringifying reasons as
+          // multiline.
+          return marked.parse(
+            contentFromNodes
+              .map(node => {
+                if (node.type === 'text') {
+                  return node.data.replace(/\b\n\b/g, '<br>\n');
+                } else {
+                  return node.data.toString();
+                }
+              })
+              .join(''),
+            markedOptions);
         }
       },
     });