« 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/urls.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/urls.js')
-rw-r--r--src/util/urls.js135
1 files changed, 124 insertions, 11 deletions
diff --git a/src/util/urls.js b/src/util/urls.js
index 1f9cd9c..11b9b8b 100644
--- a/src/util/urls.js
+++ b/src/util/urls.js
@@ -3,14 +3,20 @@
 // is in charge of pre-gener8ting a complete network of template strings
 // which can really quickly take su8stitute parameters to link from any one
 // place to another; 8ut there are also a few other utilities, too.
-//
-// Nota8ly, everything here is string-8ased, for gener8ting and transforming
-// actual path strings. More a8stract operations using wiki data o8jects is
-// the domain of link.js.
 
-import * as path from 'path';
+import * as path from 'node:path';
+
+import {withEntries} from '#sugar';
 
-import {withEntries} from './sugar.js';
+// This export is only provided for convenience, i.e. to enable the following:
+//
+//   import {urlSpec} from '#urls';
+//
+// It's not actually defined in this module's variable scope, and functions
+// exported here require a urlSpec (whether this default one or another) to be
+// passed directly.
+//
+export {default as urlSpec} from '../url-spec.js';
 
 export function generateURLs(urlSpec) {
   const getValueForFullKey = (obj, fullKey) => {
@@ -79,16 +85,25 @@ export function generateURLs(urlSpec) {
     );
 
     const toHelper =
-      (delimiterMode) =>
+      ({device}) =>
       (key, ...args) => {
         const {
-          value: {[delimiterMode]: template},
+          value: {
+            [device ? 'device' : 'posix']: template,
+          },
         } = getValueForFullKey(relative, key);
 
         let missing = 0;
         let result = template.replaceAll(/<([0-9]+)>/g, (match, n) => {
           if (n < args.length) {
-            return args[n];
+            const value = args[n];
+            if (device) {
+              return value;
+            } else {
+              let encoded = encodeURIComponent(value);
+              encoded = encoded.replaceAll('%2F', '/');
+              return encoded;
+            }
           } else {
             missing++;
           }
@@ -106,8 +121,8 @@ export function generateURLs(urlSpec) {
       };
 
     return {
-      to: toHelper('posix'),
-      toDevice: toHelper('device'),
+      to: toHelper({device: false}),
+      toDevice: toHelper({device: true}),
     };
   };
 
@@ -133,6 +148,104 @@ const thumbnailHelper = (name) => (file) =>
   file.replace(/\.(jpg|png)$/, name + '.jpg');
 
 export const thumb = {
+  large: thumbnailHelper('.large'),
   medium: thumbnailHelper('.medium'),
   small: thumbnailHelper('.small'),
 };
+
+// Makes the generally-used and wiki-specialized "to" page utility.
+// "to" returns a relative path from the current page to the target.
+export function getURLsFrom({
+  baseDirectory,
+  pagePath,
+  urls,
+}) {
+  const pageSubKey = pagePath[0];
+  const subdirectoryPrefix = getPageSubdirectoryPrefix({pagePath});
+
+  return (targetFullKey, ...args) => {
+    const [groupKey, subKey] = targetFullKey.split('.');
+    let from, to;
+
+    // When linking to *outside* the localized area of the site, we need to
+    // make sure the result is correctly relative to the 8ase directory.
+    if (
+      groupKey !== 'localized' &&
+      groupKey !== 'localizedDefaultLanguage' &&
+      baseDirectory
+    ) {
+      from = 'localizedWithBaseDirectory.' + pageSubKey;
+      to = targetFullKey;
+    } else if (groupKey === 'localizedDefaultLanguage' && baseDirectory) {
+      // Special case for specifically linking *from* a page with base
+      // directory *to* a page without! Used for the language switcher and
+      // hopefully nothing else oh god.
+      from = 'localizedWithBaseDirectory.' + pageSubKey;
+      to = 'localized.' + subKey;
+    } else if (groupKey === 'localizedDefaultLanguage') {
+      // Linking to the default, except surprise, we're already IN the default
+      // (no baseDirectory set).
+      from = 'localized.' + pageSubKey;
+      to = 'localized.' + subKey;
+    } else {
+      // If we're linking inside the localized area (or there just is no
+      // 8ase directory), the 8ase directory doesn't matter.
+      from = 'localized.' + pageSubKey;
+      to = targetFullKey;
+    }
+
+    return (
+      subdirectoryPrefix +
+      urls.from(from).to(to, ...args));
+  };
+}
+
+// Makes the generally-used and wiki-specialized "absoluteTo" page utility.
+// "absoluteTo" returns an absolute path, starting at site root (/) leading
+// to the target.
+export function getURLsFromRoot({
+  baseDirectory,
+  urls,
+}) {
+  const {to} = urls.from('shared.root');
+
+  return (targetFullKey, ...args) => {
+    const [groupKey, subKey] = targetFullKey.split('.');
+    return (
+      '/' +
+      (groupKey === 'localized' && baseDirectory
+        ? to(
+            'localizedWithBaseDirectory.' + subKey,
+            baseDirectory,
+            ...args
+          )
+        : to(targetFullKey, ...args))
+    );
+  };
+}
+
+export function getPagePathname({
+  baseDirectory,
+  device = false,
+  pagePath,
+  urls,
+}) {
+  const {[device ? 'toDevice' : 'to']: to} = urls.from('shared.root');
+
+  return (baseDirectory
+    ? to('localizedWithBaseDirectory.' + pagePath[0], baseDirectory, ...pagePath.slice(1))
+    : to('localized.' + pagePath[0], ...pagePath.slice(1)));
+}
+
+// Needed for the rare path arguments which themselves contains one or more
+// slashes, e.g. for listings, with arguments like 'albums/by-name'.
+export function getPageSubdirectoryPrefix({
+  pagePath,
+}) {
+  const timesNestedDeeply = (pagePath
+    .slice(1) // skip URL key, only check arguments
+    .join('/')
+    .split('/')
+    .length - 1);
+  return '../'.repeat(timesNestedDeeply);
+}