diff options
| -rw-r--r-- | src/common-util/sort.js | 11 | ||||
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPageArtworksChunkItem.js | 5 | ||||
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPageChunkItem.js | 34 | ||||
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js | 4 | ||||
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPageOtherArtistLinks.js | 28 | ||||
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPageTracksChunk.js | 92 | ||||
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPageTracksChunkItem.js | 49 | ||||
| -rw-r--r-- | src/content/dependencies/generateTrackInfoPageOtherReleaseTooltip.js | 13 | ||||
| -rw-r--r-- | src/data/composite/things/contribution/inheritFromContributionPresets.js | 6 | ||||
| -rw-r--r-- | src/data/things/artist.js | 23 | ||||
| -rw-r--r-- | src/data/things/contribution.js | 8 | ||||
| -rw-r--r-- | src/data/things/track.js | 21 | ||||
| -rw-r--r-- | src/replacer.js | 9 | ||||
| -rw-r--r-- | src/static/css/site.css | 25 | ||||
| -rw-r--r-- | src/strings-default.yaml | 32 |
15 files changed, 226 insertions, 134 deletions
diff --git a/src/common-util/sort.js b/src/common-util/sort.js index bbe4e551..b87ef500 100644 --- a/src/common-util/sort.js +++ b/src/common-util/sort.js @@ -45,11 +45,14 @@ export function normalizeName(s) { ) .trim(); - // Discard anything that isn't a letter, number, or space. - s = s.replace(/[^\p{Letter}\p{Number} ]/gu, '').trim(); + // Discard anything that isn't a letter, number, space, or apostrophe. + s = s.replace(/[^\p{Letter}\p{Number} ']/gu, '').trim(); - // Remove common English (only, for now) prefixes. - s = s.replace(/^(?:an?|the) /i, ''); + // Remove common articles. + s = s.replace(/^(?:an?|the|le|la|l') /i, ''); + + // Discard apostrophes. + s = s.replace(/'/g, '').trim(); return s; } diff --git a/src/content/dependencies/generateArtistInfoPageArtworksChunkItem.js b/src/content/dependencies/generateArtistInfoPageArtworksChunkItem.js index e3ba5342..993ef706 100644 --- a/src/content/dependencies/generateArtistInfoPageArtworksChunkItem.js +++ b/src/content/dependencies/generateArtistInfoPageArtworksChunkItem.js @@ -21,9 +21,6 @@ export default { ? relation('linkTrack', contrib.thing.thing) : null), - otherArtistLinks: - relation('generateArtistInfoPageOtherArtistLinks', [contrib]), - originDetails: relation('transformContent', contrib.thing.originDetails), }), @@ -48,8 +45,6 @@ export default { generate: (data, relations, slots, {html, language}) => relations.template.slots({ - otherArtistLinks: relations.otherArtistLinks, - annotation: language.encapsulate('artistPage.creditList.entry.artwork.accent', workingCapsule => { const workingOptions = {}; diff --git a/src/content/dependencies/generateArtistInfoPageChunkItem.js b/src/content/dependencies/generateArtistInfoPageChunkItem.js index 8117ca9a..92af420b 100644 --- a/src/content/dependencies/generateArtistInfoPageChunkItem.js +++ b/src/content/dependencies/generateArtistInfoPageChunkItem.js @@ -1,5 +1,3 @@ -import {empty} from '#sugar'; - export default { relations: (relation) => ({ textWithTooltip: @@ -17,8 +15,9 @@ export default { mutable: false, }, - otherArtistLinks: { - validate: v => v.strictArrayOf(v.isHTML), + citation: { + type: 'html', + mutable: false, }, rereleaseTooltip: { @@ -54,11 +53,7 @@ export default { text: language.$(entryCapsule, 'rerelease.term'), tooltip: slots.rereleaseTooltip, }); - - return language.$(workingCapsule, workingOptions); - } - - if (!html.isBlank(slots.firstReleaseTooltip)) { + } else if (!html.isBlank(slots.firstReleaseTooltip)) { workingCapsule += '.firstRelease'; workingOptions.firstRelease = relations.textWithTooltip.slots({ @@ -66,29 +61,20 @@ export default { text: language.$(entryCapsule, 'firstRelease.term'), tooltip: slots.firstReleaseTooltip, }); - - return language.$(workingCapsule, workingOptions); - } - - let anyAccent = false; - - if (!empty(slots.otherArtistLinks)) { - anyAccent = true; - workingCapsule += '.withArtists'; - workingOptions.artists = - language.formatConjunctionList(slots.otherArtistLinks); } if (!html.isBlank(slots.annotation)) { - anyAccent = true; workingCapsule += '.withAnnotation'; workingOptions.annotation = slots.annotation; + } else if (!html.isBlank(slots.citation)) { + workingCapsule += '.withCitation'; + workingOptions.citation = slots.citation; } - if (anyAccent) { - return language.$(workingCapsule, workingOptions); - } else { + if (workingCapsule === entryCapsule) { return slots.content; + } else { + return language.$(workingCapsule, workingOptions); } }), diff --git a/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js b/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js index 08446a2e..572eb982 100644 --- a/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js +++ b/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js @@ -231,7 +231,9 @@ export default { type: itemTypes, }).map(({item, link, annotation, type}) => item.slots({ - annotation: + // The citation slot, instead of annotation, gives commentary + // a specially custom look. + citation: annotation.slots({ mode: 'inline', absorbPunctuationFollowingExternalLinks: false, diff --git a/src/content/dependencies/generateArtistInfoPageOtherArtistLinks.js b/src/content/dependencies/generateArtistInfoPageOtherArtistLinks.js deleted file mode 100644 index afb61c33..00000000 --- a/src/content/dependencies/generateArtistInfoPageOtherArtistLinks.js +++ /dev/null @@ -1,28 +0,0 @@ -import {unique} from '#sugar'; - -export default { - query(contribs) { - const associatedContributionsByOtherArtists = - contribs - .flatMap(ownContrib => - ownContrib.associatedContributions - .filter(associatedContrib => - associatedContrib.artist !== ownContrib.artist)); - - const otherArtists = - unique( - associatedContributionsByOtherArtists - .map(contrib => contrib.artist)); - - return {otherArtists}; - }, - - relations: (relation, query) => ({ - artistLinks: - query.otherArtists - .map(artist => relation('linkArtist', artist)), - }), - - generate: (relations) => - relations.artistLinks, -}; diff --git a/src/content/dependencies/generateArtistInfoPageTracksChunk.js b/src/content/dependencies/generateArtistInfoPageTracksChunk.js index 7d00fdd6..50278271 100644 --- a/src/content/dependencies/generateArtistInfoPageTracksChunk.js +++ b/src/content/dependencies/generateArtistInfoPageTracksChunk.js @@ -2,24 +2,70 @@ import {sortAlbumsTracksChronologically} from '#sort'; import {empty, unique} from '#sugar'; import {getTotalDuration} from '#wiki-data'; +function countTowardTotals(contribs) { + const track = contribs[0].thing; + + if (track.isSecondaryRelease) { + const all = + Object.fromEntries( + unique(contribs.map(contrib => contrib.thingProperty)) + .map(prop => [ + prop, + track.mainReleaseTrack[prop].slice(), + ])); + + contribs = contribs.flatMap(a => { + const array = all[a.thingProperty]; + const index = + array.findIndex(b => + b.artist === a.artist && + b.annotation === a.annotation); + + if (index === -1) return []; + return array.splice(index, 1); + }).filter(Boolean); + } + + return contribs.some(contrib => + contrib.countInContributionTotals || + contrib.countInDurationTotals); +} + export default { - relations: (relation, artist, album, trackContribLists) => ({ + query: (_artist, _album, trackContribLists) => ({ + contribListsCountingTowardTotals: + trackContribLists + .filter(contribs => countTowardTotals(contribs)), + + contribListsNotCountingTowardTotals: + trackContribLists + .filter(contribs => !countTowardTotals(contribs)), + }), + + relations: (relation, query, artist, album, _trackContribLists) => ({ template: relation('generateArtistInfoPageChunk'), albumLink: relation('linkAlbum', album), - // Intentional mapping here: each item may be associated with - // more than one contribution. - items: - trackContribLists.map(trackContribs => + albumArtistCredit: + relation('generateArtistCredit', album.artistContribs, []), + + itemsCountingTowardTotals: + query.contribListsCountingTowardTotals.map(trackContribs => + relation('generateArtistInfoPageTracksChunkItem', + artist, + trackContribs)), + + itemsNotCountingTowardTotals: + query.contribListsNotCountingTowardTotals.map(trackContribs => relation('generateArtistInfoPageTracksChunkItem', artist, trackContribs)), }), - data(artist, album, trackContribLists) { + data(artist, _query, album, trackContribLists) { const data = {}; const contribs = @@ -71,10 +117,28 @@ export default { return data; }, - generate: (data, relations, {html}) => + generate: (data, relations, {html, language}) => relations.template.slots({ mode: 'album', - link: relations.albumLink, + + link: + language.encapsulate('artistPage.creditList.album', workingCapsule => { + const creditCapsule = workingCapsule + '.credit'; + const workingOptions = {album: relations.albumLink}; + + relations.albumArtistCredit.setSlots({ + normalStringKey: creditCapsule + '.by', + }); + + if (!html.isBlank(relations.albumArtistCredit)) { + workingCapsule += '.withCredit'; + workingOptions.credit = + html.tag('span', {class: 'by'}, + relations.albumArtistCredit); + } + + return language.$(workingCapsule, workingOptions); + }), dates: data.dates, duration: data.duration, @@ -85,6 +149,16 @@ export default { data.numLinkingOtherReleases > 1 && {class: 'offset-tooltips'}, - relations.items), + [ + relations.itemsCountingTowardTotals, + + !empty(relations.itemsCountingTowardTotals) && + !empty(relations.itemsNotCountingTowardTotals) && + html.tag('li', {class: 'divider'}, + html.tag('hr')), + + relations.itemsNotCountingTowardTotals + .map(item => item.slot('showDuration', false)), + ]), }), }; diff --git a/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js b/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js index e976c57f..f53e0f81 100644 --- a/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js +++ b/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js @@ -10,9 +10,11 @@ export default { query.track = contribs[0].thing; - const creditedAsArtist = + const creditedAsNormalArtist = contribs - .some(contrib => contrib.thingProperty === 'artistContribs'); + .some(contrib => + contrib.thingProperty === 'artistContribs' && + contrib.annotation !== 'featuring'); const creditedAsContributor = contribs @@ -20,7 +22,9 @@ export default { const annotatedContribs = contribs - .filter(contrib => contrib.annotation); + .filter(contrib => + contrib.annotation && + contrib.annotation !== 'featuring'); const annotatedArtistContribs = annotatedContribs @@ -39,7 +43,7 @@ export default { // Return seemingly only for "bass clarinet" when they're also // the one who composed and arranged Renewed Return! if ( - creditedAsArtist && + creditedAsNormalArtist && creditedAsContributor && empty(annotatedArtistContribs) ) { @@ -84,15 +88,19 @@ export default { return query; }, - relations: (relation, query, artist, contribs) => ({ + relations: (relation, query, artist, _contribs) => ({ template: relation('generateArtistInfoPageChunkItem'), trackLink: relation('linkTrack', query.track), - otherArtistLinks: - relation('generateArtistInfoPageOtherArtistLinks', contribs), + trackListItem: + relation('generateTrackListItem', + query.track, + (empty(query.track.album.artistContribs) + ? [artist.mockSimpleContribution] + : query.track.album.artistContribs)), rereleaseTooltip: (query.isLaterRelease @@ -116,9 +124,15 @@ export default { : null), }), - generate: (data, relations, {html, language}) => + slots: { + showDuration: { + type: 'boolean', + default: true, + }, + }, + + generate: (data, relations, slots, {html, language}) => relations.template.slots({ - otherArtistLinks: relations.otherArtistLinks, rereleaseTooltip: relations.rereleaseTooltip, firstReleaseTooltip: relations.firstReleaseTooltip, @@ -128,16 +142,13 @@ export default { : html.blank()), content: - language.encapsulate('artistPage.creditList.entry.track', workingCapsule => { - const workingOptions = {track: relations.trackLink}; - - if (data.duration) { - workingCapsule += '.withDuration'; - workingOptions.duration = - language.formatDuration(data.duration); - } - - return language.$(workingCapsule, workingOptions); + language.$('artistPage.creditList.entry.track', { + track: + html.inside( + relations.trackListItem.slots({ + showArtists: 'auto', + showDuration: slots.showDuration, + })), }), }), }; diff --git a/src/content/dependencies/generateTrackInfoPageOtherReleaseTooltip.js b/src/content/dependencies/generateTrackInfoPageOtherReleaseTooltip.js index 92ab52ba..fcb2e2fa 100644 --- a/src/content/dependencies/generateTrackInfoPageOtherReleaseTooltip.js +++ b/src/content/dependencies/generateTrackInfoPageOtherReleaseTooltip.js @@ -43,11 +43,12 @@ export default { }), data.otherDate && data.currentDate && - language.formatRelativeDate(data.otherDate, data.currentDate, { - considerRoundingDays: true, - approximate: true, - absolute: false, - }), + html.tag('span', {class: 'when'}, + language.formatRelativeDate(data.otherDate, data.currentDate, { + considerRoundingDays: true, + approximate: true, + absolute: false, + })), ], })), -}; \ No newline at end of file +}; diff --git a/src/data/composite/things/contribution/inheritFromContributionPresets.js b/src/data/composite/things/contribution/inheritFromContributionPresets.js index 17387404..1cefae1b 100644 --- a/src/data/composite/things/contribution/inheritFromContributionPresets.js +++ b/src/data/composite/things/contribution/inheritFromContributionPresets.js @@ -41,10 +41,8 @@ export default templateCompositeFrom({ compute: (continuation, { ['#values']: values, ['#index']: index, - }) => continuation({ - ['#value']: - values[index], - }), + }) => + continuation.exit(values[index]), }, ], }); diff --git a/src/data/things/artist.js b/src/data/things/artist.js index a2ed0b74..01eb2172 100644 --- a/src/data/things/artist.js +++ b/src/data/things/artist.js @@ -43,7 +43,7 @@ export class Artist extends Thing { 'avatarArtwork', // from inline fields ]; - static [Thing.getPropertyDescriptors] = () => ({ + static [Thing.getPropertyDescriptors] = ({Contribution}) => ({ // Update & expose name: name(V('Unnamed Artist')), @@ -78,6 +78,27 @@ export class Artist extends Thing { isArtist: exposeConstant(V(true)), + mockSimpleContribution: { + flags: {expose: true}, + expose: { + dependencies: ['directory', '_find'], + compute: ({directory, _find: find}) => + Object.assign(new Contribution, { + artist: 'artist:' + directory, + + // These nulls have no effect, they're only included + // here for clarity. + date: null, + thing: null, + annotation: null, + artistProperty: null, + thingProperty: null, + + find, + }), + }, + }, + trackArtistContributions: reverseReferenceList({ reverse: soupyReverse.input('trackArtistContributionsBy'), }), diff --git a/src/data/things/contribution.js b/src/data/things/contribution.js index 393a60b4..778bc566 100644 --- a/src/data/things/contribution.js +++ b/src/data/things/contribution.js @@ -62,12 +62,12 @@ export class Contribution extends Thing { }, countInContributionTotals: [ - inheritFromContributionPresets(), - exposeUpdateValueOrContinue({ validate: input.value(isBoolean), }), + inheritFromContributionPresets(), + { dependencies: ['thing', input.myself()], compute: (continuation, { @@ -85,12 +85,12 @@ export class Contribution extends Thing { ], countInDurationTotals: [ - inheritFromContributionPresets(), - exposeUpdateValueOrContinue({ validate: input.value(isBoolean), }), + inheritFromContributionPresets(), + withPropertyFromObject('thing', V('duration')), exitWithoutDependency('#thing.duration', { value: input.value(false), diff --git a/src/data/things/track.js b/src/data/things/track.js index f77cfa41..3c4b5409 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -289,12 +289,20 @@ export class Track extends Thing { ], contributorContribs: [ - inheritContributionListFromMainRelease(), - - contributionList({ + withResolvedContribs({ + from: input.updateValue({validate: isContributionList}), date: 'date', - artistProperty: input.value('trackContributorContributions'), + thingProperty: input.thisProperty(), + artistProperty: input.value('trackArtistContributions'), + }).outputs({ + '#resolvedContribs': '#contributorContribs', }), + + exposeDependencyOrContinue('#contributorContribs', V('empty')), + + inheritContributionListFromMainRelease(), + + exposeConstant(V([])), ], // > Update & expose - General configuration @@ -1047,11 +1055,6 @@ export class Track extends Thing { 'Sampled Tracks', ]}, - {message: `Secondary releases inherit contributors from the main one`, fields: [ - 'Main Release', - 'Contributors', - ]}, - { message: ({'Has Cover Art': hasCoverArt}) => (hasCoverArt diff --git a/src/replacer.js b/src/replacer.js index 8a929444..9be14755 100644 --- a/src/replacer.js +++ b/src/replacer.js @@ -934,7 +934,7 @@ export function postprocessExternalLinks(inputNodes) { let parseFrom = 0; for (const match of matchInlineLinks(node.data)) { - const {href, index, length} = match; + let {href, index, length} = match; textNode.data += node.data.slice(parseFrom, index); @@ -950,6 +950,13 @@ export function postprocessExternalLinks(inputNodes) { }; } + try { + const url = new URL(href); + if (url.pathname === '/' && !url.search && !url.hash) { + href = href.replace(/\/$/, ''); + } + } catch {} + outputNodes.push({ i: node.i + index, iEnd: node.i + index + length, diff --git a/src/static/css/site.css b/src/static/css/site.css index 9cfa4417..1e3a781a 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -1518,6 +1518,14 @@ label > input[type=checkbox]:not(:checked) + span { font-size: 0.9em; } +.other-release-tooltip .tooltip-content .when { + /* technically just putting this in a <span> was enough + * to keep it from wrapping all tight-like, for some + * reason, but im not taking any chances... + */ + white-space: nowrap; +} + .rerelease-tooltip .not-credited-on-first-release { opacity: 0.9; } @@ -2088,6 +2096,19 @@ h1 { white-space: nowrap; } +#content li.divider { + list-style-type: none; + max-width: 220px; + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +#content li.divider hr { + color: #888; + border: none; + border-bottom: 1px solid; +} + #content details { margin-top: 0.25em; margin-bottom: 0.25em; @@ -2263,11 +2284,11 @@ ul.quick-info li:not(:last-child)::after { margin: 0 6px; } -li .by { +dt .by, li .by { font-style: oblique; } -li .by a { +dt .by a, li .by a { display: inline-block; } diff --git a/src/strings-default.yaml b/src/strings-default.yaml index 909ae043..b79dd67f 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -1285,6 +1285,11 @@ artistPage: album: _: "{ALBUM}" + + withCredit: "{ALBUM} {CREDIT}" + credit: + by: "by {ARTISTS}" + withDate: "{ALBUM} ({DATE})" withDuration: "{ALBUM} ({DURATION})" withDate.withDuration: "{ALBUM} ({DATE}; {DURATION})" @@ -1313,15 +1318,13 @@ artistPage: # thing may be described with a word or two, and that's shown # in the list. - withAnnotation: "{ENTRY} ({ANNOTATION})" + withAnnotation: "{ENTRY} — {ANNOTATION}" - # withArtists: - # This lists co-artists or co-contributors, depending on how - # the artist themselves was credited. + # withCitation: + # For commentary entries and the like. These are usually longer + # than typical annotations. - withArtists: "{ENTRY} (with {ARTISTS})" - - withArtists.withAnnotation: "{ENTRY} ({ANNOTATION}; with {ARTISTS})" + withCitation: "{ENTRY} ({CITATION})" # rerelease: # Tracks which aren't the original release don't display co- @@ -1330,28 +1333,24 @@ artistPage: rerelease: _: "{ENTRY} ({RERELEASE})" + withAnnotation: "{ENTRY} ({RERELEASE}) — {ANNOTATION}" term: "rerelease" firstRelease: >- First released on {ALBUM} notCreditedOnFirstRelease: >- - Note that {ARTIST} is not credited on this track's first release. + {ARTIST} isn't credited on this track's first release. firstRelease: _: "{ENTRY} ({FIRST_RELEASE})" + withAnnotation: "{ENTRY} ({FIRST_RELEASE}) — {ANNOTATION}" term: "first release" rerelease: >- Also released on {ALBUM} - # track: - # The string without duration is used in both the artist's - # track credits list as well as their commentary list. - - track: - _: "{TRACK}" - withDuration: "({DURATION}) {TRACK}" + track: "{TRACK}" # album: # The artist info page doesn't display if the artist is @@ -1366,8 +1365,7 @@ artistPage: bannerArt: "(banner art)" commentary: "(album commentary)" - flash: - _: "{FLASH}" + flash: "{FLASH}" artwork.accent: withLabel: >- |