From 4075254c9e38be6741527e1fb535eed444e6ad08 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Sun, 26 Jun 2022 16:41:09 -0300 Subject: initial prettier/eslint commit --- src/static/client.js | 638 ++++++++++++++++++++++++++++----------------------- 1 file changed, 346 insertions(+), 292 deletions(-) (limited to 'src/static/client.js') diff --git a/src/static/client.js b/src/static/client.js index 7397735c..72fa9cc2 100644 --- a/src/static/client.js +++ b/src/static/client.js @@ -5,13 +5,9 @@ // // Upd8: As of 04/02/2021, it's now used for info cards too! Nice. -import { - getColors -} from '../util/colors.js'; +import { getColors } from "../util/colors.js"; -import { - getArtistNumContributions -} from '../util/wiki-data.js'; +import { getArtistNumContributions } from "../util/wiki-data.js"; let albumData, artistData, flashData; let officialAlbumData, fandomAlbumData, artistNames; @@ -20,190 +16,235 @@ let ready = false; // Localiz8tion nonsense ---------------------------------- -const language = document.documentElement.getAttribute('lang'); +const language = document.documentElement.getAttribute("lang"); let list; -if ( - typeof Intl === 'object' && - typeof Intl.ListFormat === 'function' -) { - const getFormat = type => { - const formatter = new Intl.ListFormat(language, {type}); - return formatter.format.bind(formatter); - }; - - list = { - conjunction: getFormat('conjunction'), - disjunction: getFormat('disjunction'), - unit: getFormat('unit') - }; +if (typeof Intl === "object" && typeof Intl.ListFormat === "function") { + const getFormat = (type) => { + const formatter = new Intl.ListFormat(language, { type }); + return formatter.format.bind(formatter); + }; + + list = { + conjunction: getFormat("conjunction"), + disjunction: getFormat("disjunction"), + unit: getFormat("unit"), + }; } else { - // Not a gr8 mock we've got going here, 8ut it's *mostly* language-free. - // We use the same mock for every list 'cuz we don't have any of the - // necessary CLDR info to appropri8tely distinguish 8etween them. - const arbitraryMock = array => array.join(', '); - - list = { - conjunction: arbitraryMock, - disjunction: arbitraryMock, - unit: arbitraryMock - }; + // Not a gr8 mock we've got going here, 8ut it's *mostly* language-free. + // We use the same mock for every list 'cuz we don't have any of the + // necessary CLDR info to appropri8tely distinguish 8etween them. + const arbitraryMock = (array) => array.join(", "); + + list = { + conjunction: arbitraryMock, + disjunction: arbitraryMock, + unit: arbitraryMock, + }; } // Miscellaneous helpers ---------------------------------- -function rebase(href, rebaseKey = 'rebaseLocalized') { - const relative = (document.documentElement.dataset[rebaseKey] || '.') + '/'; - if (relative) { - return relative + href; - } else { - return href; - } +function rebase(href, rebaseKey = "rebaseLocalized") { + const relative = (document.documentElement.dataset[rebaseKey] || ".") + "/"; + if (relative) { + return relative + href; + } else { + return href; + } } function pick(array) { - return array[Math.floor(Math.random() * array.length)]; + return array[Math.floor(Math.random() * array.length)]; } function cssProp(el, key) { - return getComputedStyle(el).getPropertyValue(key).trim(); + return getComputedStyle(el).getPropertyValue(key).trim(); } function getRefDirectory(ref) { - return ref.split(':')[1]; + return ref.split(":")[1]; } function getAlbum(el) { - const directory = cssProp(el, '--album-directory'); - return albumData.find(album => album.directory === directory); + const directory = cssProp(el, "--album-directory"); + return albumData.find((album) => album.directory === directory); } function getFlash(el) { - const directory = cssProp(el, '--flash-directory'); - return flashData.find(flash => flash.directory === directory); + const directory = cssProp(el, "--flash-directory"); + return flashData.find((flash) => flash.directory === directory); } // TODO: These should pro8a8ly access some shared urlSpec path. We'd need to // separ8te the tooling around that into common-shared code too. const getLinkHref = (type, directory) => rebase(`${type}/${directory}`); -const openAlbum = d => rebase(`album/${d}`); -const openTrack = d => rebase(`track/${d}`); -const openArtist = d => rebase(`artist/${d}`); -const openFlash = d => rebase(`flash/${d}`); +const openAlbum = (d) => rebase(`album/${d}`); +const openTrack = (d) => rebase(`track/${d}`); +const openArtist = (d) => rebase(`artist/${d}`); +const openFlash = (d) => rebase(`flash/${d}`); function getTrackListAndIndex() { - const album = getAlbum(document.body); - const directory = cssProp(document.body, '--track-directory'); - if (!directory && !album) return {}; - if (!directory) return {list: album.tracks}; - const trackIndex = album.tracks.findIndex(track => track.directory === directory); - return {list: album.tracks, index: trackIndex}; + const album = getAlbum(document.body); + const directory = cssProp(document.body, "--track-directory"); + if (!directory && !album) return {}; + if (!directory) return { list: album.tracks }; + const trackIndex = album.tracks.findIndex( + (track) => track.directory === directory + ); + return { list: album.tracks, index: trackIndex }; } function openRandomTrack() { - const { list } = getTrackListAndIndex(); - if (!list) return; - return openTrack(pick(list)); + const { list } = getTrackListAndIndex(); + if (!list) return; + return openTrack(pick(list)); } function getFlashListAndIndex() { - const list = flashData.filter(flash => !flash.act8r8k) - const flash = getFlash(document.body); - if (!flash) return {list}; - const flashIndex = list.indexOf(flash); - return {list, index: flashIndex}; + const list = flashData.filter((flash) => !flash.act8r8k); + const flash = getFlash(document.body); + if (!flash) return { list }; + const flashIndex = list.indexOf(flash); + return { list, index: flashIndex }; } // TODO: This should also use urlSpec. function fetchData(type, directory) { - return fetch(rebase(`${type}/${directory}/data.json`, 'rebaseData')) - .then(res => res.json()); + return fetch(rebase(`${type}/${directory}/data.json`, "rebaseData")).then( + (res) => res.json() + ); } // JS-based links ----------------------------------------- -for (const a of document.body.querySelectorAll('[data-random]')) { - a.addEventListener('click', evt => { - if (!ready) { - evt.preventDefault(); - return; - } - - setTimeout(() => { - a.href = rebase('js-disabled'); - }); - switch (a.dataset.random) { - case 'album': return a.href = openAlbum(pick(albumData).directory); - case 'album-in-fandom': return a.href = openAlbum(pick(fandomAlbumData).directory); - case 'album-in-official': return a.href = openAlbum(pick(officialAlbumData).directory); - case 'track': return a.href = openTrack(getRefDirectory(pick(albumData.map(a => a.tracks).reduce((a, b) => a.concat(b), [])))); - case 'track-in-album': return a.href = openTrack(getRefDirectory(pick(getAlbum(a).tracks))); - case 'track-in-fandom': return a.href = openTrack(getRefDirectory(pick(fandomAlbumData.reduce((acc, album) => acc.concat(album.tracks), [])))); - case 'track-in-official': return a.href = openTrack(getRefDirectory(pick(officialAlbumData.reduce((acc, album) => acc.concat(album.tracks), [])))); - case 'artist': return a.href = openArtist(pick(artistData).directory); - case 'artist-more-than-one-contrib': return a.href = openArtist(pick(artistData.filter(artist => getArtistNumContributions(artist) > 1)).directory); - } +for (const a of document.body.querySelectorAll("[data-random]")) { + a.addEventListener("click", (evt) => { + if (!ready) { + evt.preventDefault(); + return; + } + + setTimeout(() => { + a.href = rebase("js-disabled"); }); + switch (a.dataset.random) { + case "album": + return (a.href = openAlbum(pick(albumData).directory)); + case "album-in-fandom": + return (a.href = openAlbum(pick(fandomAlbumData).directory)); + case "album-in-official": + return (a.href = openAlbum(pick(officialAlbumData).directory)); + case "track": + return (a.href = openTrack( + getRefDirectory( + pick( + albumData.map((a) => a.tracks).reduce((a, b) => a.concat(b), []) + ) + ) + )); + case "track-in-album": + return (a.href = openTrack(getRefDirectory(pick(getAlbum(a).tracks)))); + case "track-in-fandom": + return (a.href = openTrack( + getRefDirectory( + pick( + fandomAlbumData.reduce( + (acc, album) => acc.concat(album.tracks), + [] + ) + ) + ) + )); + case "track-in-official": + return (a.href = openTrack( + getRefDirectory( + pick( + officialAlbumData.reduce( + (acc, album) => acc.concat(album.tracks), + [] + ) + ) + ) + )); + case "artist": + return (a.href = openArtist(pick(artistData).directory)); + case "artist-more-than-one-contrib": + return (a.href = openArtist( + pick( + artistData.filter((artist) => getArtistNumContributions(artist) > 1) + ).directory + )); + } + }); } -const next = document.getElementById('next-button'); -const previous = document.getElementById('previous-button'); -const random = document.getElementById('random-button'); +const next = document.getElementById("next-button"); +const previous = document.getElementById("previous-button"); +const random = document.getElementById("random-button"); const prependTitle = (el, prepend) => { - const existing = el.getAttribute('title'); - if (existing) { - el.setAttribute('title', prepend + ' ' + existing); - } else { - el.setAttribute('title', prepend); - } + const existing = el.getAttribute("title"); + if (existing) { + el.setAttribute("title", prepend + " " + existing); + } else { + el.setAttribute("title", prepend); + } }; -if (next) prependTitle(next, '(Shift+N)'); -if (previous) prependTitle(previous, '(Shift+P)'); -if (random) prependTitle(random, '(Shift+R)'); - -document.addEventListener('keypress', event => { - if (event.shiftKey) { - if (event.charCode === 'N'.charCodeAt(0)) { - if (next) next.click(); - } else if (event.charCode === 'P'.charCodeAt(0)) { - if (previous) previous.click(); - } else if (event.charCode === 'R'.charCodeAt(0)) { - if (random && ready) random.click(); - } +if (next) prependTitle(next, "(Shift+N)"); +if (previous) prependTitle(previous, "(Shift+P)"); +if (random) prependTitle(random, "(Shift+R)"); + +document.addEventListener("keypress", (event) => { + if (event.shiftKey) { + if (event.charCode === "N".charCodeAt(0)) { + if (next) next.click(); + } else if (event.charCode === "P".charCodeAt(0)) { + if (previous) previous.click(); + } else if (event.charCode === "R".charCodeAt(0)) { + if (random && ready) random.click(); } + } }); -for (const reveal of document.querySelectorAll('.reveal')) { - reveal.addEventListener('click', event => { - if (!reveal.classList.contains('revealed')) { - reveal.classList.add('revealed'); - event.preventDefault(); - event.stopPropagation(); - } - }); +for (const reveal of document.querySelectorAll(".reveal")) { + reveal.addEventListener("click", (event) => { + if (!reveal.classList.contains("revealed")) { + reveal.classList.add("revealed"); + event.preventDefault(); + event.stopPropagation(); + } + }); } -const elements1 = document.getElementsByClassName('js-hide-once-data'); -const elements2 = document.getElementsByClassName('js-show-once-data'); +const elements1 = document.getElementsByClassName("js-hide-once-data"); +const elements2 = document.getElementsByClassName("js-show-once-data"); -for (const element of elements1) element.style.display = 'block'; +for (const element of elements1) element.style.display = "block"; -fetch(rebase('data.json', 'rebaseShared')).then(data => data.json()).then(data => { +fetch(rebase("data.json", "rebaseShared")) + .then((data) => data.json()) + .then((data) => { albumData = data.albumData; artistData = data.artistData; flashData = data.flashData; - officialAlbumData = albumData.filter(album => album.groups.includes('group:official')); - fandomAlbumData = albumData.filter(album => !album.groups.includes('group:official')); - artistNames = artistData.filter(artist => !artist.alias).map(artist => artist.name); + officialAlbumData = albumData.filter((album) => + album.groups.includes("group:official") + ); + fandomAlbumData = albumData.filter( + (album) => !album.groups.includes("group:official") + ); + artistNames = artistData + .filter((artist) => !artist.alias) + .map((artist) => artist.name); - for (const element of elements1) element.style.display = 'none'; - for (const element of elements2) element.style.display = 'block'; + for (const element of elements1) element.style.display = "none"; + for (const element of elements2) element.style.display = "block"; ready = true; -}); + }); // Data & info card --------------------------------------- @@ -216,197 +257,210 @@ let fastHover = false; let endFastHoverTimeout = null; function colorLink(a, color) { - if (color) { - const { primary, dim } = getColors(color); - a.style.setProperty('--primary-color', primary); - a.style.setProperty('--dim-color', dim); - } + if (color) { + const { primary, dim } = getColors(color); + a.style.setProperty("--primary-color", primary); + a.style.setProperty("--dim-color", dim); + } } -function link(a, type, {name, directory, color}) { - colorLink(a, color); - a.innerText = name - a.href = getLinkHref(type, directory); +function link(a, type, { name, directory, color }) { + colorLink(a, color); + a.innerText = name; + a.href = getLinkHref(type, directory); } function joinElements(type, elements) { - // We can't use the Intl APIs with elements, 8ecuase it only oper8tes on - // strings. So instead, we'll pass the element's outer HTML's (which means - // the entire HTML of that element). - // - // That does mean this function returns a string, so always 8e sure to - // set innerHTML when using it (not appendChild). - - return list[type](elements.map(el => el.outerHTML)); + // We can't use the Intl APIs with elements, 8ecuase it only oper8tes on + // strings. So instead, we'll pass the element's outer HTML's (which means + // the entire HTML of that element). + // + // That does mean this function returns a string, so always 8e sure to + // set innerHTML when using it (not appendChild). + + return list[type](elements.map((el) => el.outerHTML)); } const infoCard = (() => { - const container = document.getElementById('info-card-container'); - - let cancelShow = false; - let hideTimeout = null; - let showing = false; - - container.addEventListener('mouseenter', cancelHide); - container.addEventListener('mouseleave', readyHide); - - function show(type, target) { - cancelShow = false; - - fetchData(type, target.dataset[type]).then(data => { - // Manual DOM 'cuz we're laaaazy. - - if (cancelShow) { - return; - } - - showing = true; - - const rect = target.getBoundingClientRect(); - - container.style.setProperty('--primary-color', data.color); - - container.style.top = window.scrollY + rect.bottom + 'px'; - container.style.left = window.scrollX + rect.left + 'px'; - - // Use a short timeout to let a currently hidden (or not yet shown) - // info card teleport to the position set a8ove. (If it's currently - // shown, it'll transition to that position.) - setTimeout(() => { - container.classList.remove('hide'); - container.classList.add('show'); - }, 50); - - // 8asic details. - - const nameLink = container.querySelector('.info-card-name a'); - link(nameLink, 'track', data); - - const albumLink = container.querySelector('.info-card-album a'); - link(albumLink, 'album', data.album); - - const artistSpan = container.querySelector('.info-card-artists span'); - artistSpan.innerHTML = joinElements('conjunction', data.artists.map(({ artist }) => { - const a = document.createElement('a'); - a.href = getLinkHref('artist', artist.directory); - a.innerText = artist.name; - return a; - })); - - const coverArtistParagraph = container.querySelector('.info-card-cover-artists'); - const coverArtistSpan = coverArtistParagraph.querySelector('span'); - if (data.coverArtists.length) { - coverArtistParagraph.style.display = 'block'; - coverArtistSpan.innerHTML = joinElements('conjunction', data.coverArtists.map(({ artist }) => { - const a = document.createElement('a'); - a.href = getLinkHref('artist', artist.directory); - a.innerText = artist.name; - return a; - })); - } else { - coverArtistParagraph.style.display = 'none'; - } - - // Cover art. - - const [ containerNoReveal, containerReveal ] = [ - container.querySelector('.info-card-art-container.no-reveal'), - container.querySelector('.info-card-art-container.reveal') - ]; - - const [ containerShow, containerHide ] = (data.cover.warnings.length - ? [containerReveal, containerNoReveal] - : [containerNoReveal, containerReveal]); - - containerHide.style.display = 'none'; - containerShow.style.display = 'block'; - - const img = containerShow.querySelector('.info-card-art'); - img.src = rebase(data.cover.paths.small, 'rebaseMedia'); - - const imgLink = containerShow.querySelector('a'); - colorLink(imgLink, data.color); - imgLink.href = rebase(data.cover.paths.original, 'rebaseMedia'); - - if (containerShow === containerReveal) { - const cw = containerShow.querySelector('.info-card-art-warnings'); - cw.innerText = list.unit(data.cover.warnings); - - const reveal = containerShow.querySelector('.reveal'); - reveal.classList.remove('revealed'); - } - }); - } - - function hide() { - container.classList.remove('show'); - container.classList.add('hide'); - cancelShow = true; - showing = false; - } - - function readyHide() { - if (!hideTimeout && showing) { - hideTimeout = setTimeout(hide, HIDE_HOVER_DELAY); - } + const container = document.getElementById("info-card-container"); + + let cancelShow = false; + let hideTimeout = null; + let showing = false; + + container.addEventListener("mouseenter", cancelHide); + container.addEventListener("mouseleave", readyHide); + + function show(type, target) { + cancelShow = false; + + fetchData(type, target.dataset[type]).then((data) => { + // Manual DOM 'cuz we're laaaazy. + + if (cancelShow) { + return; + } + + showing = true; + + const rect = target.getBoundingClientRect(); + + container.style.setProperty("--primary-color", data.color); + + container.style.top = window.scrollY + rect.bottom + "px"; + container.style.left = window.scrollX + rect.left + "px"; + + // Use a short timeout to let a currently hidden (or not yet shown) + // info card teleport to the position set a8ove. (If it's currently + // shown, it'll transition to that position.) + setTimeout(() => { + container.classList.remove("hide"); + container.classList.add("show"); + }, 50); + + // 8asic details. + + const nameLink = container.querySelector(".info-card-name a"); + link(nameLink, "track", data); + + const albumLink = container.querySelector(".info-card-album a"); + link(albumLink, "album", data.album); + + const artistSpan = container.querySelector(".info-card-artists span"); + artistSpan.innerHTML = joinElements( + "conjunction", + data.artists.map(({ artist }) => { + const a = document.createElement("a"); + a.href = getLinkHref("artist", artist.directory); + a.innerText = artist.name; + return a; + }) + ); + + const coverArtistParagraph = container.querySelector( + ".info-card-cover-artists" + ); + const coverArtistSpan = coverArtistParagraph.querySelector("span"); + if (data.coverArtists.length) { + coverArtistParagraph.style.display = "block"; + coverArtistSpan.innerHTML = joinElements( + "conjunction", + data.coverArtists.map(({ artist }) => { + const a = document.createElement("a"); + a.href = getLinkHref("artist", artist.directory); + a.innerText = artist.name; + return a; + }) + ); + } else { + coverArtistParagraph.style.display = "none"; + } + + // Cover art. + + const [containerNoReveal, containerReveal] = [ + container.querySelector(".info-card-art-container.no-reveal"), + container.querySelector(".info-card-art-container.reveal"), + ]; + + const [containerShow, containerHide] = data.cover.warnings.length + ? [containerReveal, containerNoReveal] + : [containerNoReveal, containerReveal]; + + containerHide.style.display = "none"; + containerShow.style.display = "block"; + + const img = containerShow.querySelector(".info-card-art"); + img.src = rebase(data.cover.paths.small, "rebaseMedia"); + + const imgLink = containerShow.querySelector("a"); + colorLink(imgLink, data.color); + imgLink.href = rebase(data.cover.paths.original, "rebaseMedia"); + + if (containerShow === containerReveal) { + const cw = containerShow.querySelector(".info-card-art-warnings"); + cw.innerText = list.unit(data.cover.warnings); + + const reveal = containerShow.querySelector(".reveal"); + reveal.classList.remove("revealed"); + } + }); + } + + function hide() { + container.classList.remove("show"); + container.classList.add("hide"); + cancelShow = true; + showing = false; + } + + function readyHide() { + if (!hideTimeout && showing) { + hideTimeout = setTimeout(hide, HIDE_HOVER_DELAY); } + } - function cancelHide() { - if (hideTimeout) { - clearTimeout(hideTimeout); - hideTimeout = null; - } + function cancelHide() { + if (hideTimeout) { + clearTimeout(hideTimeout); + hideTimeout = null; } - - return { - show, - hide, - readyHide, - cancelHide - }; + } + + return { + show, + hide, + readyHide, + cancelHide, + }; })(); function makeInfoCardLinkHandlers(type) { - let hoverTimeout = null; - - return { - mouseenter(evt) { - hoverTimeout = setTimeout(() => { - fastHover = true; - infoCard.show(type, evt.target); - }, fastHover ? FAST_HOVER_INFO_DELAY : NORMAL_HOVER_INFO_DELAY); + let hoverTimeout = null; + + return { + mouseenter(evt) { + hoverTimeout = setTimeout( + () => { + fastHover = true; + infoCard.show(type, evt.target); + }, + fastHover ? FAST_HOVER_INFO_DELAY : NORMAL_HOVER_INFO_DELAY + ); - clearTimeout(endFastHoverTimeout); - endFastHoverTimeout = null; + clearTimeout(endFastHoverTimeout); + endFastHoverTimeout = null; - infoCard.cancelHide(); - }, + infoCard.cancelHide(); + }, - mouseleave(evt) { - clearTimeout(hoverTimeout); + mouseleave(evt) { + clearTimeout(hoverTimeout); - if (fastHover && !endFastHoverTimeout) { - endFastHoverTimeout = setTimeout(() => { - endFastHoverTimeout = null; - fastHover = false; - }, END_FAST_HOVER_DELAY); - } + if (fastHover && !endFastHoverTimeout) { + endFastHoverTimeout = setTimeout(() => { + endFastHoverTimeout = null; + fastHover = false; + }, END_FAST_HOVER_DELAY); + } - infoCard.readyHide(); - } - }; + infoCard.readyHide(); + }, + }; } const infoCardLinkHandlers = { - track: makeInfoCardLinkHandlers('track') + track: makeInfoCardLinkHandlers("track"), }; function addInfoCardLinkHandlers(type) { - for (const a of document.querySelectorAll(`a[data-${type}]`)) { - for (const [ eventName, handler ] of Object.entries(infoCardLinkHandlers[type])) { - a.addEventListener(eventName, handler); - } + for (const a of document.querySelectorAll(`a[data-${type}]`)) { + for (const [eventName, handler] of Object.entries( + infoCardLinkHandlers[type] + )) { + a.addEventListener(eventName, handler); } + } } // Info cards are disa8led for now since they aren't quite ready for release, @@ -415,5 +469,5 @@ function addInfoCardLinkHandlers(type) { // localStorage.tryInfoCards = true; // if (localStorage.tryInfoCards) { - addInfoCardLinkHandlers('track'); + addInfoCardLinkHandlers("track"); } -- cgit 1.3.0-6-gf8a5