« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/static/client.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/static/client.js')
-rw-r--r--src/static/client.js638
1 files changed, 346 insertions, 292 deletions
diff --git a/src/static/client.js b/src/static/client.js
index 7397735..72fa9cc 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");
 }