« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/link.js54
-rw-r--r--src/util/sugar.js16
2 files changed, 69 insertions, 1 deletions
diff --git a/src/util/link.js b/src/util/link.js
index ee3579d5..8fe3c2f4 100644
--- a/src/util/link.js
+++ b/src/util/link.js
@@ -14,6 +14,17 @@
 import * as html from './html.js';
 import {getColors} from './colors.js';
 
+import {
+  Album,
+  Artist,
+  ArtTag,
+  Flash,
+  Group,
+  NewsEntry,
+  StaticPage,
+  Track,
+} from '../data/things.js';
+
 export function getLinkThemeString(color) {
   if (!color) return '';
 
@@ -80,6 +91,21 @@ const linkPathname = (key, conf) =>
 const linkIndex = (key, conf) =>
   linkHelper((_, {to}) => to('localized.' + key), conf);
 
+// Mapping of Thing constructor classes to the key for a link.x() function.
+// These represent a sensible "default" link, i.e. to the primary page for
+// the given thing based on what it's an instance of. This is used for the
+// link.anything() function.
+const linkAnythingMapping = [
+  [Album, 'album'],
+  [Artist, 'artist'],
+  [ArtTag, 'tag'],
+  [Flash, 'flash'],
+  [Group, 'groupInfo'],
+  [NewsEntry, 'newsEntry'],
+  [StaticPage, 'staticPage'],
+  [Track, 'track'],
+];
+
 const link = {
   globalOptions: {
     // This should usually only 8e used during development! It'll take any
@@ -134,6 +160,34 @@ const link = {
   root: linkPathname('shared.path', {color: false}),
   data: linkPathname('data.path', {color: false}),
   site: linkPathname('localized.path', {color: false}),
+
+  // This is NOT an arrow functions because it should be callable for other
+  // "this" objects - i.e, if we bind arguments in other functions on the same
+  // link object, link.anything() should use those bound functions, not the
+  // original ones we're exporting here.
+  //
+  // This function has been through a lot of names:
+  //   - getHrefOfAnythingMan (2020-05-25)
+  //   - toAnythingMan (2021-03-02)
+  //   - linkAnythingMan (2021-05-14)
+  //   - link.anything (2022-09-15)
+  // ...And it'll probably end up being renamed yet again one day!
+  //
+  anything(...args) {
+    if (!this) {
+      throw new Error(`Missing value for \`this\` - investigate JS call stack`);
+    }
+
+    const [thing] = args;
+
+    for (const [constructor, fnKey] of linkAnythingMapping) {
+      if (thing instanceof constructor) {
+        return Reflect.apply(this[fnKey], this, args);
+      }
+    }
+
+    throw new Error(`Unrecognized type of thing for linking: ${thing}`);
+  },
 };
 
 export default link;
diff --git a/src/util/sugar.js b/src/util/sugar.js
index 754f1991..8b59deef 100644
--- a/src/util/sugar.js
+++ b/src/util/sugar.js
@@ -107,12 +107,26 @@ export function escapeRegex(string) {
   return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
 }
 
+// Binds default values for arguments in a {key: value} type function argument
+// (typically the second argument, but may be overridden by providing a
+// [bindOpts.bindIndex] argument). Typically useful for preparing a function for
+// reuse within one or multiple other contexts, which may not be aware of
+// required or relevant values provided in the initial context.
+//
+// This function also passes the identity of `this` through (the returned value
+// is not an arrow function), though note it's not a true bound function either
+// (since Function.prototype.bind only supports positional arguments, not
+// "options" specified via key/value).
+//
 export function bindOpts(fn, bind) {
   const bindIndex = bind[bindOpts.bindIndex] ?? 1;
 
   const bound = function (...args) {
     const opts = args[bindIndex] ?? {};
-    return fn(...args.slice(0, bindIndex), {...bind, ...opts});
+    return Reflect.apply(fn, this, [
+      ...args.slice(0, bindIndex),
+      {...bind, ...opts}
+    ]);
   };
 
   Object.defineProperty(bound, 'name', {