diff options
-rw-r--r-- | src/data/things/thing.js | 4 | ||||
-rw-r--r-- | src/data/things/track.js | 2 | ||||
-rw-r--r-- | src/data/yaml.js | 4 | ||||
-rw-r--r-- | src/listing-spec.js | 68 | ||||
-rw-r--r-- | src/misc-templates.js | 81 | ||||
-rw-r--r-- | src/page/album.js | 57 | ||||
-rw-r--r-- | src/page/group.js | 7 | ||||
-rw-r--r-- | src/page/track.js | 157 | ||||
-rw-r--r-- | src/static/client.js | 77 | ||||
-rw-r--r-- | src/static/site3.css | 58 | ||||
-rw-r--r-- | src/strings-default.json | 39 | ||||
-rwxr-xr-x | src/upd8.js | 6 | ||||
-rw-r--r-- | src/write/bind-utilities.js | 6 | ||||
-rw-r--r-- | src/write/page-template.js | 106 |
14 files changed, 552 insertions, 120 deletions
diff --git a/src/data/things/thing.js b/src/data/things/thing.js index b9fa60c6..5ab15c0e 100644 --- a/src/data/things/thing.js +++ b/src/data/things/thing.js @@ -148,6 +148,10 @@ export default class Thing extends CacheableObject { additionalFiles: () => ({ flags: {update: true, expose: true}, update: {validate: isAdditionalFileList}, + expose: { + transform: (additionalFiles) => + additionalFiles ?? [], + }, }), // A reference list! Keep in mind this is for general references to wiki diff --git a/src/data/things/track.js b/src/data/things/track.js index 1778ed27..0751b2d0 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -96,6 +96,8 @@ export class Track extends Thing { commentary: Thing.common.commentary(), lyrics: Thing.common.simpleString(), additionalFiles: Thing.common.additionalFiles(), + sheetMusicFiles: Thing.common.additionalFiles(), + midiProjectFiles: Thing.common.additionalFiles(), // Update only diff --git a/src/data/yaml.js b/src/data/yaml.js index 6350588d..7cd23cfc 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -248,6 +248,8 @@ export const processTrackDocument = makeProcessDocument(T.Track, { 'Cover Artists': parseContributors, 'Additional Files': parseAdditionalFiles, + 'Sheet Music Files': parseAdditionalFiles, + 'MIDI Project Files': parseAdditionalFiles, }, propertyFieldMapping: { @@ -264,6 +266,8 @@ export const processTrackDocument = makeProcessDocument(T.Track, { lyrics: 'Lyrics', commentary: 'Commentary', additionalFiles: 'Additional Files', + sheetMusicFiles: 'Sheet Music Files', + midiProjectFiles: 'MIDI Project Files', originalReleaseTrackByRef: 'Originally Released As', referencedTracksByRef: 'Referenced Tracks', diff --git a/src/listing-spec.js b/src/listing-spec.js index ef51fe90..29b7645c 100644 --- a/src/listing-spec.js +++ b/src/listing-spec.js @@ -826,6 +826,74 @@ const listingSpec = [ }, { + directory: 'tracks/with-sheet-music-files', + stringsKey: 'listTracks.withSheetMusicFiles', + + data: ({wikiData: {albumData}}) => + albumData + .map(album => ({ + album, + tracks: album.tracks.filter(t => !empty(t.sheetMusicFiles)), + })) + .filter(({tracks}) => !empty(tracks)), + + html: (data, {html, language, link}) => + html.tag('dl', + data.flatMap(({album, tracks}) => [ + html.tag('dt', + {class: 'content-heading'}, + language.$('listingPage.listTracks.withSheetMusicFiles.album', { + album: link.album(album), + date: language.formatDate(album.date), + })), + + html.tag('dd', + html.tag('ul', + tracks.map(track => + html.tag('li', + language.$('listingPage.listTracks.withSheetMusicFiles.track', { + track: link.track(track, { + hash: 'sheet-music-files', + }), + }))))), + ])), + }, + + { + directory: 'tracks/with-midi-project-files', + stringsKey: 'listTracks.withMidiProjectFiles', + + data: ({wikiData: {albumData}}) => + albumData + .map(album => ({ + album, + tracks: album.tracks.filter(t => !empty(t.midiProjectFiles)), + })) + .filter(({tracks}) => !empty(tracks)), + + html: (data, {html, language, link}) => + html.tag('dl', + data.flatMap(({album, tracks}) => [ + html.tag('dt', + {class: 'content-heading'}, + language.$('listingPage.listTracks.withMidiProjectFiles.album', { + album: link.album(album), + date: language.formatDate(album.date), + })), + + html.tag('dd', + html.tag('ul', + tracks.map(track => + html.tag('li', + language.$('listingPage.listTracks.withMidiProjectFiles.track', { + track: link.track(track, { + hash: 'midi-project-files', + }), + }))))), + ])), + }, + + { directory: 'tags/by-name', stringsKey: 'listTags.byName', diff --git a/src/misc-templates.js b/src/misc-templates.js index 21dca90e..171b4825 100644 --- a/src/misc-templates.js +++ b/src/misc-templates.js @@ -49,46 +49,31 @@ function unbound_generateAdditionalFilesList(additionalFiles, { }) { if (empty(additionalFiles)) return []; - const fileCount = additionalFiles.flatMap((g) => g.files).length; - - return html.fragment([ - html.tag('p', - { - id: 'additional-files', - class: ['content-heading'], - }, - language.$('releaseInfo.additionalFiles.heading', { - additionalFiles: language.countAdditionalFiles(fileCount, { - unit: true, - }), - })), - - 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(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), - }))); - }))), - ])), - ]); + 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 @@ -933,6 +918,21 @@ function unbound_generateNavigationLinks(current, { // Sticky heading, ooooo +function unbound_generateContentHeading({ + html, + + id, + title, +}) { + return html.tag('p', + { + class: 'content-heading', + id, + tabindex: '0', + }, + title); +} + function unbound_generateStickyHeadingContainer({ getRevealStringFromArtTags, html, @@ -1042,6 +1042,7 @@ export { unbound_generateInfoGalleryLinks as generateInfoGalleryLinks, unbound_generateNavigationLinks as generateNavigationLinks, + unbound_generateContentHeading as generateContentHeading, unbound_generateStickyHeadingContainer as generateStickyHeadingContainer, unbound_getFooterLocalizationLinks as getFooterLocalizationLinks, diff --git a/src/page/album.js b/src/page/album.js index 897e5110..24033b1d 100644 --- a/src/page/album.js +++ b/src/page/album.js @@ -50,6 +50,8 @@ export function write(album, {wikiData}) { }; const hasAdditionalFiles = !empty(album.additionalFiles); + const numAdditionalFiles = album.additionalFiles.flatMap((g) => g.files).length; + const albumDuration = getTotalDuration(album.tracks); const displayTrackSections = @@ -336,18 +338,22 @@ export function write(album, {wikiData}) { ]), ...html.fragment( - hasAdditionalFiles && - generateAdditionalFilesList(album.additionalFiles, { - // TODO: Kinda near the metal here... - getFileSize: (file) => - getSizeOfAdditionalFile( - urls.from('media.root').to( - 'media.albumAdditionalFile', - album.directory, - file)), - linkFile: (file) => - link.albumAdditionalFile({album, file}), - })), + hasAdditionalFiles && [ + html.tag('p', + {id: 'additional-files', class: ['content-heading']}, + language.$('releaseInfo.additionalFiles.heading', { + additionalFiles: language.countAdditionalFiles(numAdditionalFiles, { + unit: true, + }), + })), + + generateAlbumAdditionalFilesList(album, album.additionalFiles, { + generateAdditionalFilesList, + getSizeOfAdditionalFile, + link, + urls, + }), + ]), ...html.fragment( album.commentary && [ @@ -543,7 +549,7 @@ export function generateAlbumSidebar(album, currentTrack, { html.tag( 'summary', {style: getLinkThemeString(color)}, - [ + html.tag('span', [ listTag === 'ol' && language.$('albumSidebar.trackList.group.withRange', { group: groupName, @@ -555,7 +561,7 @@ export function generateAlbumSidebar(album, currentTrack, { language.$('albumSidebar.trackList.group', { group: groupName, }), - ]), + ])), html.tag(listTag, listTag === 'ol' ? {start: startIndex + 1} : {}, tracks.map(trackToListItem)), @@ -834,3 +840,26 @@ export function generateAlbumChronologyLinks(album, currentTrack, { })), ]); } + +export function generateAlbumAdditionalFilesList(album, additionalFiles, { + fileSize = true, + + generateAdditionalFilesList, + getSizeOfAdditionalFile, + link, + urls, +}) { + return generateAdditionalFilesList(additionalFiles, { + getFileSize: + (fileSize + ? (file) => + // TODO: Kinda near the metal here... + getSizeOfAdditionalFile( + urls + .from('media.root') + .to('media.albumAdditionalFile', album.directory, file)) + : () => null), + linkFile: (file) => + link.albumAdditionalFile({album, file}), + }); +} diff --git a/src/page/group.js b/src/page/group.js index 9a48c1d8..81e1728d 100644 --- a/src/page/group.js +++ b/src/page/group.js @@ -247,9 +247,10 @@ function generateGroupSidebar(currentGroup, isGallery, { [ html.tag('summary', {style: getLinkThemeString(category.color)}, - language.$('groupSidebar.groupList.category', { - category: `<span class="group-name">${category.name}</span>`, - })), + html.tag('span', + language.$('groupSidebar.groupList.category', { + category: `<span class="group-name">${category.name}</span>`, + }))), html.tag('ul', category.groups.map((group) => { const linkKey = ( diff --git a/src/page/track.js b/src/page/track.js index caba3668..7f0d1cf2 100644 --- a/src/page/track.js +++ b/src/page/track.js @@ -5,6 +5,7 @@ import { generateAlbumNavLinks, generateAlbumSecondaryNav, generateAlbumSidebar, + generateAlbumAdditionalFilesList as unbound_generateAlbumAdditionalFilesList, } from './album.js'; import { @@ -73,6 +74,11 @@ export function write(track, {wikiData}) { const hasCommentary = track.commentary || otherReleases.some((t) => t.commentary); + const hasAdditionalFiles = !empty(track.additionalFiles); + const hasSheetMusicFiles = !empty(track.sheetMusicFiles); + const hasMidiProjectFiles = !empty(track.midiProjectFiles); + const numAdditionalFiles = album.additionalFiles.flatMap((g) => g.files).length; + const generateCommentary = ({language, link, transformMultiline}) => transformMultiline([ track.commentary, @@ -161,12 +167,16 @@ export function write(track, {wikiData}) { page: ({ absoluteTo, fancifyURL, + generateAdditionalFilesList, + generateAdditionalFilesShortcut, generateChronologyLinks, + generateContentHeading, generateNavigationLinks, generateTrackListDividedByGroups, getAlbumStylesheet, getArtistString, getLinkThemeString, + getSizeOfAdditionalFile, getThemeString, getTrackCover, html, @@ -184,6 +194,14 @@ export function write(track, {wikiData}) { link, }); + const generateAlbumAdditionalFilesList = bindOpts(unbound_generateAlbumAdditionalFilesList, { + [bindOpts.bindIndex]: 2, + generateAdditionalFilesList, + getSizeOfAdditionalFile, + link, + urls, + }); + return { title: language.$('trackPage.title', {track: track.name}), stylesheet: getAlbumStylesheet(album, {to}), @@ -274,6 +292,30 @@ export function write(track, {wikiData}) { ]), html.tag('p', + { + [html.onlyIfContent]: true, + [html.joinChildren]: '<br>', + }, + [ + hasSheetMusicFiles && + language.$('releaseInfo.sheetMusicFiles.shortcut', { + link: html.tag('a', + {href: '#sheet-music-files'}, + language.$('releaseInfo.sheetMusicFiles.shortcut.link')), + }), + + hasMidiProjectFiles && + language.$('releaseInfo.midiProjectFiles.shortcut', { + link: html.tag('a', + {href: '#midi-project-files'}, + language.$('releaseInfo.midiProjectFiles.shortcut.link')), + }), + + hasAdditionalFiles && + generateAdditionalFilesShortcut(track.additionalFiles), + ]), + + html.tag('p', (empty(track.urls) ? language.$('releaseInfo.listenOn.noLinks') : language.$('releaseInfo.listenOn', { @@ -283,8 +325,10 @@ export function write(track, {wikiData}) { ...html.fragment( !empty(otherReleases) && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.alsoReleasedAs')), + generateContentHeading({ + id: 'also-released-as', + title: language.$('releaseInfo.alsoReleasedAs'), + }), html.tag('ul', otherReleases.map(track => html.tag('li', language.$('releaseInfo.alsoReleasedAs.item', { @@ -295,8 +339,10 @@ export function write(track, {wikiData}) { ...html.fragment( !empty(contributorContribs) && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.contributors')), + generateContentHeading({ + id: 'contributors', + title: language.$('releaseInfo.contributors'), + }), html.tag('ul', contributorContribs.map(contrib => html.tag('li', getArtistString([contrib], { @@ -307,20 +353,26 @@ export function write(track, {wikiData}) { ...html.fragment( !empty(referencedTracks) && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.tracksReferenced', { - track: html.tag('i', track.name), - })), + generateContentHeading({ + id: 'references', + title: + language.$('releaseInfo.tracksReferenced', { + track: html.tag('i', track.name), + }), + }), html.tag('ul', referencedTracks.map(getTrackItem)), ]), ...html.fragment( !empty(referencedByTracks) && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.tracksThatReference', { - track: html.tag('i', track.name), - })), + generateContentHeading({ + id: 'referenced-by', + title: + language.$('releaseInfo.tracksThatReference', { + track: html.tag('i', track.name), + }), + }), generateTrackListDividedByGroups(referencedByTracks, { getTrackItem, @@ -330,20 +382,26 @@ export function write(track, {wikiData}) { ...html.fragment( !empty(sampledTracks) && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.tracksSampled', { - track: html.tag('i', track.name), - })), + generateContentHeading({ + id: 'samples', + title: + language.$('releaseInfo.tracksSampled', { + track: html.tag('i', track.name), + }), + }), html.tag('ul', sampledTracks.map(getTrackItem)), ]), ...html.fragment( !empty(sampledByTracks) && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.tracksThatSample', { - track: html.tag('i', track.name), - })), + generateContentHeading({ + id: 'sampled-by', + title: + language.$('releaseInfo.tracksThatSample', { + track: html.tag('i', track.name), + }) + }), html.tag('ul', sampledByTracks.map(getTrackItem)), ]), @@ -351,10 +409,13 @@ export function write(track, {wikiData}) { ...html.fragment( wikiInfo.enableFlashesAndGames && !empty(flashesThatFeature) && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.flashesThatFeature', { - track: html.tag('i', track.name), - })), + generateContentHeading({ + id: 'featured-in', + title: + language.$('releaseInfo.flashesThatFeature', { + track: html.tag('i', track.name), + }), + }), html.tag('ul', flashesThatFeature.map(({flash, as}) => html.tag('li', @@ -371,16 +432,56 @@ export function write(track, {wikiData}) { ...html.fragment( track.lyrics && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.lyrics')), + generateContentHeading({ + id: 'lyrics', + title: language.$('releaseInfo.lyrics'), + }), html.tag('blockquote', transformLyrics(track.lyrics)), ]), ...html.fragment( + hasSheetMusicFiles && [ + generateContentHeading({ + id: 'sheet-music-files', + title: language.$('releaseInfo.sheetMusicFiles.heading'), + }), + + generateAlbumAdditionalFilesList(album, track.sheetMusicFiles, { + fileSize: false, + }), + ]), + + ...html.fragment( + hasMidiProjectFiles && [ + generateContentHeading({ + id: 'midi-project-files', + title: language.$('releaseInfo.midiProjectFiles.heading'), + }), + + generateAlbumAdditionalFilesList(album, track.midiProjectFiles), + ]), + + ...html.fragment( + hasAdditionalFiles && [ + generateContentHeading({ + id: 'additional-files', + title: language.$('releaseInfo.additionalFiles.heading', { + additionalFiles: language.countAdditionalFiles(numAdditionalFiles, { + unit: true, + }), + }) + }), + + generateAlbumAdditionalFilesList(album, track.additionalFiles), + ]), + + ...html.fragment( hasCommentary && [ - html.tag('p', {class: ['content-heading']}, - language.$('releaseInfo.artistCommentary')), + generateContentHeading({ + id: 'artist-commentary', + title: language.$('releaseInfo.artistCommentary'), + }), html.tag('blockquote', generateCommentary({ link, diff --git a/src/static/client.js b/src/static/client.js index 9ae5510a..47936d82 100644 --- a/src/static/client.js +++ b/src/static/client.js @@ -444,6 +444,81 @@ if (localStorage.tryInfoCards) { addInfoCardLinkHandlers('track'); } +// Custom hash links -------------------------------------- + +function addHashLinkHandlers() { + // Instead of defining a scroll offset (to account for the sticky heading) + // in JavaScript, we interface with the CSS property 'scroll-margin-top'. + // This lets the scroll offset be consolidated where it makes sense, and + // sets an appropriate offset when (re)loading a page with hash for free! + + let wasHighlighted; + + for (const a of document.links) { + const href = a.getAttribute('href'); + if (!href || !href.startsWith('#')) { + continue; + } + + a.addEventListener('click', handleHashLinkClicked); + } + + function handleHashLinkClicked(evt) { + if (evt.metaKey || evt.shiftKey || evt.ctrlKey || evt.altKey) { + return; + } + + const href = evt.target.getAttribute('href'); + const id = href.slice(1); + const linked = document.getElementById(id); + + if (!linked) { + return; + } + + // Hide skipper box right away, so the layout is updated on time for the + // math operations coming up next. + const skipper = document.getElementById('skippers'); + skipper.style.display = 'none'; + setTimeout(() => skipper.style.display = ''); + + const box = linked.getBoundingClientRect(); + const style = window.getComputedStyle(linked); + + const scrollY = + window.scrollY + + box.top + - style['scroll-margin-top'].replace('px', ''); + + evt.preventDefault(); + history.pushState({}, '', href); + window.scrollTo({top: scrollY, behavior: 'smooth'}); + linked.focus({preventScroll: true}); + + const maxScroll = + document.body.scrollHeight + - window.innerHeight; + + if (scrollY > maxScroll && linked.classList.contains('content-heading')) { + if (wasHighlighted) { + wasHighlighted.classList.remove('highlight-hash-link'); + } + + wasHighlighted = linked; + linked.classList.add('highlight-hash-link'); + linked.addEventListener('animationend', function handle(evt) { + if (evt.animationName === 'highlight-hash-link') { + linked.removeEventListener('animationend', handle); + linked.classList.remove('highlight-hash-link'); + wasHighlighted = null; + } + }); + } + } +} + +addHashLinkHandlers(); + // Sticky content heading --------------------------------- const stickyHeadingInfo = Array.from(document.querySelectorAll('.content-sticky-heading-container')) @@ -510,7 +585,7 @@ function updateStickyHeading() { for (let i = contentHeadings.length - 1; i >= 0; i--) { const heading = contentHeadings[i]; const headingRect = heading.getBoundingClientRect(); - if (headingRect.y + headingRect.height / 1.5 < stickyBottom) { + if (headingRect.y + headingRect.height / 1.5 < stickyBottom + 20) { closestHeading = heading; break; } diff --git a/src/static/site3.css b/src/static/site3.css index 7abb5351..449e6fad 100644 --- a/src/static/site3.css +++ b/src/static/site3.css @@ -208,7 +208,19 @@ body::before { box-shadow: 0 0 40px rgba(0, 0, 0, 0.5); } -#skippers > .skipper:not(:last-child)::after { +#skippers > * { + display: inline-block; +} + +#skippers > .skipper-list:not(:last-child)::after { + display: inline-block; + content: "\00a0"; + margin-left: 2px; + margin-right: -2px; + border-left: 1px dotted; +} + +#skippers .skipper-list > .skipper:not(:last-child)::after { content: " \00b7 "; font-weight: 800; } @@ -342,14 +354,13 @@ body::before { .sidebar > details summary { margin-top: 0.5em; padding-left: 5px; - user-select: none; } .sidebar > details summary .group-name { color: var(--primary-color); } -.sidebar > details summary:hover { +.sidebar > details summary > span:hover { cursor: pointer; text-decoration: underline; text-decoration-color: var(--primary-color); @@ -1138,8 +1149,49 @@ html[data-url-key="localized.home"] .carousel-container { margin-bottom: 0; } +/* Custom hash links */ + +.content-heading { + border-bottom: 3px double transparent; + margin-bottom: -3px; +} + +.content-heading.highlight-hash-link { + animation: highlight-hash-link 4s; + animation-delay: 125ms; +} + +/* This animation's name is referenced in JavaScript */ +@keyframes highlight-hash-link { + 0% { + border-bottom-color: transparent; + } + + 10% { + border-bottom-color: white; + } + + 25% { + border-bottom-color: white; + } + + 100% { + border-bottom-color: transparent; + } +} + /* Sticky heading */ +#content [id] { + /* Adjust scroll margin. */ + scroll-margin-top: calc( + 74px /* Sticky heading */ + + 33px /* Sticky subheading */ + - 1em /* One line of text (align bottom) */ + - 12px /* Padding for hanging letters & focus ring */ + ); +} + .content-sticky-heading-container { position: sticky; top: 0; diff --git a/src/strings-default.json b/src/strings-default.json index 37e5fbad..bfe358e4 100644 --- a/src/strings-default.json +++ b/src/strings-default.json @@ -120,11 +120,17 @@ "releaseInfo.artTags.inline": "Tags: {TAGS}", "releaseInfo.additionalFiles.shortcut": "{ANCHOR_LINK} {TITLES}", "releaseInfo.additionalFiles.shortcut.anchorLink": "Additional files:", - "releaseInfo.additionalFiles.heading": "Has {ADDITIONAL_FILES}:", + "releaseInfo.additionalFiles.heading": "View or download {ADDITIONAL_FILES}:", "releaseInfo.additionalFiles.entry": "{TITLE}", "releaseInfo.additionalFiles.entry.withDescription": "{TITLE}: {DESCRIPTION}", "releaseInfo.additionalFiles.file": "{FILE}", "releaseInfo.additionalFiles.file.withSize": "{FILE} ({SIZE})", + "releaseInfo.sheetMusicFiles.shortcut": "Download {LINK}.", + "releaseInfo.sheetMusicFiles.shortcut.link": "sheet music files", + "releaseInfo.sheetMusicFiles.heading": "Print or download sheet music files:", + "releaseInfo.midiProjectFiles.shortcut": "Download {LINK}.", + "releaseInfo.midiProjectFiles.shortcut.link": "MIDI/project files", + "releaseInfo.midiProjectFiles.heading": "Download MIDI/project files:", "releaseInfo.note": "Note:", "trackList.section.withDuration": "{SECTION} ({DURATION}):", "trackList.group": "{GROUP}:", @@ -177,11 +183,24 @@ "misc.nav.gallery": "Gallery", "misc.pageTitle": "{TITLE}", "misc.pageTitle.withWikiName": "{TITLE} | {WIKI_NAME}", - "misc.skippers.skipToContent": "Skip to content", - "misc.skippers.skipToSidebar": "Skip to sidebar", - "misc.skippers.skipToSidebar.left": "Skip to sidebar (left)", - "misc.skippers.skipToSidebar.right": "Skip to sidebar (right)", - "misc.skippers.skipToFooter": "Skip to footer", + "misc.skippers.skipTo": "Skip to:", + "misc.skippers.content": "Content", + "misc.skippers.sidebar": "Sidebar", + "misc.skippers.sidebar.left": "Sidebar (left)", + "misc.skippers.sidebar.right": "Sidebar (right)", + "misc.skippers.header": "Header", + "misc.skippers.footer": "Footer", + "misc.skippers.contributors": "Contributors", + "misc.skippers.references": "References...", + "misc.skippers.referencedBy": "Referenced by...", + "misc.skippers.samples": "Samples...", + "misc.skippers.sampledBy": "Sampled by...", + "misc.skippers.featuredIn": "Featured in...", + "misc.skippers.lyrics": "Lyrics", + "misc.skippers.sheetMusicFiles": "Sheet music files", + "misc.skippers.midiProjectFiles": "MIDI/project files", + "misc.skippers.additionalFiles": "Additional files", + "misc.skippers.artistCommentary": "Commentary", "misc.socialEmbed.heading": "{WIKI_NAME} | {HEADING}", "misc.jumpTo": "Jump to:", "misc.jumpTo.withLinks": "Jump to: {LINKS}.", @@ -373,6 +392,14 @@ "listingPage.listTracks.withLyrics.title.short": "...with Lyrics", "listingPage.listTracks.withLyrics.album": "{ALBUM} ({DATE})", "listingPage.listTracks.withLyrics.track": "{TRACK}", + "listingPage.listTracks.withSheetMusicFiles.title": "Tracks - with Sheet Music Files", + "listingPage.listTracks.withSheetMusicFiles.title.short": "...with Sheet Music Files", + "listingPage.listTracks.withSheetMusicFiles.album": "{ALBUM} ({DATE})", + "listingPage.listTracks.withSheetMusicFiles.track": "{TRACK}", + "listingPage.listTracks.withMidiProjectFiles.title": "Tracks - with MIDI & Project Files", + "listingPage.listTracks.withMidiProjectFiles.title.short": "...with MIDI & Project Files", + "listingPage.listTracks.withMidiProjectFiles.album": "{ALBUM} ({DATE})", + "listingPage.listTracks.withMidiProjectFiles.track": "{TRACK}", "listingPage.listTags.byName.title": "Tags - by Name", "listingPage.listTags.byName.title.short": "...by Name", "listingPage.listTags.byName.item": "{TAG} ({TIMES_USED})", diff --git a/src/upd8.js b/src/upd8.js index 39372833..fd565228 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -627,7 +627,11 @@ async function main() { ...wikiData.albumData.flatMap((album) => [ ...(album.additionalFiles ?? []), - ...album.tracks.flatMap((track) => track.additionalFiles ?? []), + ...album.tracks.flatMap((track) => [ + ...(track.additionalFiles ?? []), + ...(track.sheetMusicFiles ?? []), + ...(track.midiProjectFiles ?? []), + ]), ] .flatMap((fileGroup) => fileGroup.files) .map((file) => ({ diff --git a/src/write/bind-utilities.js b/src/write/bind-utilities.js index 127afe2c..427111b4 100644 --- a/src/write/bind-utilities.js +++ b/src/write/bind-utilities.js @@ -19,6 +19,7 @@ import { generateAdditionalFilesList, generateAdditionalFilesShortcut, generateChronologyLinks, + generateContentHeading, generateCoverLink, generateInfoGalleryLinks, generateTrackListDividedByGroups, @@ -192,6 +193,11 @@ export function bindUtilities({ language, }); + bound.generateContentHeading = bindOpts(generateContentHeading, { + [bindOpts.bindIndex]: 0, + html, + }); + bound.generateStickyHeadingContainer = bindOpts(generateStickyHeadingContainer, { [bindOpts.bindIndex]: 0, getRevealStringFromArtTags: bound.getRevealStringFromArtTags, diff --git a/src/write/page-template.js b/src/write/page-template.js index f47d3f0d..bd52c456 100644 --- a/src/write/page-template.js +++ b/src/write/page-template.js @@ -401,6 +401,87 @@ export function generateDocumentHTML(pageInfo, { footerHTML, ].filter(Boolean).join('\n'); + const processSkippers = skipperList => + skipperList + .filter(Boolean) + .map(([href, stringSubkey]) => + html.tag('span', {class: 'skipper'}, + html.tag('a', + {href}, + language.$(`misc.skippers.${stringSubkey}`)))); + + // Hilariously jank. Sorry! + const hasID = id => mainHTML.includes(`id="${id}"`); + const hasContributors = hasID('contributors'); + const hasReferences = hasID('references'); + const hasReferencedBy = hasID('referenced-by'); + const hasSamples = hasID('samples'); + const hasSampledBy = hasID('sampled-by'); + const hasFeaturedIn = hasID('featured-in'); + const hasLyrics = hasID('lyrics'); + const hasSheetMusicFiles = hasID('sheet-music-files'); + const hasMidiProjectFiles = hasID('midi-project-files'); + const hasAdditionalFiles = hasID('additional-files'); + const hasArtistCommentary = hasID('artist-commentary'); + + const skippersHTML = + mainHTML && + html.tag('div', {id: 'skippers'}, [ + html.tag('span', language.$('misc.skippers.skipTo')), + html.tag('div', {class: 'skipper-list'}, + processSkippers([ + ['#content', 'content'], + sidebarLeftHTML && + [ + '#sidebar-left', + sidebarRightHTML + ? 'sidebar.left' + : 'sidebar', + ], + sidebarRightHTML && + [ + '#sidebar-right', + sidebarLeftHTML + ? 'sidebar.right' + : 'sidebar', + ], + navHTML && + ['#header', 'header'], + footerHTML && + ['#footer', 'footer'], + ])), + + html.tag('div', + { + [html.onlyIfContent]: true, + class: 'skipper-list' + }, + processSkippers([ + hasContributors && + ['#contributors', 'contributors'], + hasReferences && + ['#references', 'references'], + hasReferencedBy && + ['#referenced-by', 'referencedBy'], + hasSamples && + ['#samples', 'samples'], + hasSampledBy && + ['#sampled-by', 'sampledBy'], + hasFeaturedIn && + ['#featured-in', 'featuredIn'], + hasLyrics && + ['#lyrics', 'lyrics'], + hasSheetMusicFiles && + ['#sheet-music-files', 'sheetMusicFiles'], + hasMidiProjectFiles && + ['#midi-project-files', 'midiProjectFiles'], + hasAdditionalFiles && + ['#additional-files', 'additionalFiles'], + hasArtistCommentary && + ['#artist-commentary', 'artistCommentary'], + ])), + ]); + const infoCardHTML = html.tag('div', {id: 'info-card-container'}, html.tag('div', {id: 'info-card-decor'}, html.tag('div', {id: 'info-card'}, [ @@ -566,30 +647,7 @@ export function generateDocumentHTML(pageInfo, { [ html.tag('div', {id: 'page-container'}, [ mainHTML && - html.tag('div', {id: 'skippers'}, - [ - ['#content', language.$('misc.skippers.skipToContent')], - sidebarLeftHTML && - [ - '#sidebar-left', - sidebarRightHTML - ? language.$('misc.skippers.skipToSidebar.left') - : language.$('misc.skippers.skipToSidebar'), - ], - sidebarRightHTML && - [ - '#sidebar-right', - sidebarLeftHTML - ? language.$('misc.skippers.skipToSidebar.right') - : language.$('misc.skippers.skipToSidebar'), - ], - footerHTML && - ['#footer', language.$('misc.skippers.skipToFooter')], - ] - .filter(Boolean) - .map(([href, title]) => - html.tag('span', {class: 'skipper'}, - html.tag('a', {href}, title)))), + skippersHTML, layoutHTML, ]), |