« get me outta code hell

content, css: vertical tooltips + basic external parsing - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/content
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-11-23 09:16:23 -0400
committer(quasar) nebula <qznebula@protonmail.com>2023-11-24 13:45:10 -0400
commit0202375db8ccd03d98ed6c2ffbb800b67c026639 (patch)
treea7bdecbeeaeb7ba43333cce69ac9ecd47d768a36 /src/content
parenta1d50400b858e40471bc1bb78408d69d39907c5f (diff)
content, css: vertical tooltips + basic external parsing
Diffstat (limited to 'src/content')
-rw-r--r--src/content/dependencies/linkContribution.js14
-rw-r--r--src/content/dependencies/linkExternalAsIcon.js265
2 files changed, 235 insertions, 44 deletions
diff --git a/src/content/dependencies/linkContribution.js b/src/content/dependencies/linkContribution.js
index 5bc398de..ef61c766 100644
--- a/src/content/dependencies/linkContribution.js
+++ b/src/content/dependencies/linkContribution.js
@@ -1,15 +1,8 @@
 import {empty} from '#sugar';
 
 export default {
-  contentDependencies: [
-    'linkArtist',
-    'linkExternalAsIcon',
-  ],
-
-  extraDependencies: [
-    'html',
-    'language',
-  ],
+  contentDependencies: ['linkArtist', 'linkExternalAsIcon'],
+  extraDependencies: ['html', 'language'],
 
   relations(relation, contribution) {
     const relations = {};
@@ -85,7 +78,8 @@ export default {
               [html.joinChildren]: '',
               class: 'icons-tooltip-content',
             },
-            relations.artistIcons)),
+            relations.artistIcons
+              .map(icon => icon.slot('withText', true)))),
       ];
     }
 
diff --git a/src/content/dependencies/linkExternalAsIcon.js b/src/content/dependencies/linkExternalAsIcon.js
index cd168992..d3ed9122 100644
--- a/src/content/dependencies/linkExternalAsIcon.js
+++ b/src/content/dependencies/linkExternalAsIcon.js
@@ -1,6 +1,202 @@
-// TODO: Define these as extra dependencies and pass them somewhere
-const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com'];
-const MASTODON_DOMAINS = ['types.pl'];
+import {stitchArrays} from '#sugar';
+
+const fallbackDescriptor = {
+  icon: 'globe',
+  string: 'external',
+
+  normal: 'domain',
+  compact: 'domain',
+};
+
+// TODO: Define all this stuff in data!
+const externalSpec = [
+  {
+    matchDomain: 'bandcamp.com',
+
+    icon: 'bandcamp',
+    string: 'bandcamp',
+
+    compact: 'handle',
+
+    handle: {domain: /^[^.]*/},
+  },
+
+  {
+    matchDomains: ['bc.s3m.us', 'music.solatrux.com'],
+
+    icon: 'bandcamp',
+    string: 'bandcamp',
+
+    normal: 'domain',
+    compact: 'domain',
+  },
+
+  {
+    matchDomains: ['types.pl'],
+
+    icon: 'mastodon',
+    string: 'mastodon',
+
+    compact: 'domain',
+  },
+
+  {
+    matchDomains: ['youtube.com', 'youtu.be'],
+
+    icon: 'youtube',
+    string: 'youtube',
+
+    compact: 'handle',
+
+    handle: {
+      pathname: /^(@.*?)\/?$/,
+    },
+  },
+
+  {
+    matchDomain: 'soundcloud.com',
+
+    icon: 'soundcloud',
+    string: 'soundcloud',
+
+    compact: 'handle',
+
+    handle: /[^/]*\/?$/,
+  },
+
+  {
+    matchDomain: 'tumblr.com',
+
+    icon: 'tumblr',
+    string: 'tumblr',
+
+    compact: 'handle',
+
+    handle: {domain: /^[^.]*/},
+  },
+
+  {
+    matchDomain: 'twitter.com',
+
+    icon: 'twitter',
+    string: 'twitter',
+
+    compact: 'handle',
+
+    handle: {
+      prefix: '@',
+      pathname: /^@?.*\/?$/,
+    },
+  },
+
+  {
+    matchDomain: 'deviantart.com',
+
+    icon: 'deviantart',
+    string: 'deviantart',
+  },
+
+  {
+    matchDomain: 'instagram.com',
+
+    icon: 'instagram',
+    string: 'instagram',
+  },
+
+  {
+    matchDomain: 'newgrounds.com',
+
+    icon: 'newgrounds',
+    string: 'newgrounds',
+  },
+];
+
+function determineLinkText(url, descriptor, {language}) {
+  const prefix = 'misc.external';
+
+  const {
+    hostname: domain,
+    pathname,
+  } = new URL(url);
+
+  let normal = null;
+  let compact = null;
+
+  const place = language.$(prefix, descriptor.string);
+
+  if (descriptor.normal === 'domain') {
+    normal = language.$(prefix, 'withDomain', {place, domain});
+  }
+
+  if (descriptor.compact === 'domain') {
+    compact = domain.replace(/^www\./, '');
+  }
+
+  let handle = null;
+
+  if (descriptor.handle) {
+    let regexen = [];
+    let tests = [];
+
+    let handlePrefix = '';
+
+    if (descriptor.handle instanceof RegExp) {
+      regexen.push(descriptor.handle);
+      tests.push(url);
+    } else {
+      for (const [key, value] of Object.entries(descriptor.handle)) {
+        switch (key) {
+          case 'prefix':
+            handlePrefix = value;
+            continue;
+
+          case 'url':
+            tests.push(url);
+            break;
+
+          case 'domain':
+          case 'hostname':
+            tests.push(domain);
+            break;
+
+          case 'path':
+          case 'pathname':
+            tests.push(pathname.slice(1));
+            break;
+
+          default:
+            tests.push('');
+            break;
+        }
+
+        regexen.push(value);
+      }
+    }
+
+    for (const {regex, test} of stitchArrays({
+      regex: regexen,
+      test: tests,
+    })) {
+      const match = test.match(regex);
+      if (match) {
+        handle = handlePrefix + (match[1] ?? match[0]);
+        break;
+      }
+    }
+  }
+
+  if (descriptor.compact === 'handle') {
+    compact = handle;
+  }
+
+  if (normal === 'handle' && handle) {
+    normal = language.$(prefix, 'withHandle', {place, handle});
+  }
+
+  normal ??= language.$(prefix, descriptor.string);
+
+  return {normal, compact};
+}
 
 export default {
   extraDependencies: ['html', 'language', 'to'],
@@ -9,38 +205,39 @@ export default {
     return {url};
   },
 
-  generate(data, {html, language, to}) {
-    const domain = new URL(data.url).hostname;
-    const [id, msg] = (
-      domain.includes('bandcamp.com')
-        ? ['bandcamp', language.$('misc.external.bandcamp')]
-      : BANDCAMP_DOMAINS.includes(domain)
-        ? ['bandcamp', language.$('misc.external.bandcamp.domain', {domain})]
-      : MASTODON_DOMAINS.includes(domain)
-        ? ['mastodon', language.$('misc.external.mastodon.domain', {domain})]
-      : domain.includes('youtu')
-        ? ['youtube', language.$('misc.external.youtube')]
-      : domain.includes('soundcloud')
-        ? ['soundcloud', language.$('misc.external.soundcloud')]
-      : domain.includes('tumblr.com')
-        ? ['tumblr', language.$('misc.external.tumblr')]
-      : domain.includes('twitter.com')
-        ? ['twitter', language.$('misc.external.twitter')]
-      : domain.includes('deviantart.com')
-        ? ['deviantart', language.$('misc.external.deviantart')]
-      : domain.includes('instagram.com')
-        ? ['instagram', language.$('misc.external.bandcamp')]
-      : domain.includes('newgrounds.com')
-        ? ['newgrounds', language.$('misc.external.newgrounds')]
-        : ['globe', language.$('misc.external.domain', {domain})]);
+  slots: {
+    withText: {type: 'boolean'},
+  },
+
+  generate(data, slots, {html, language, to}) {
+    const {hostname: domain} = new URL(data.url);
+
+    const descriptor =
+      externalSpec.find(({matchDomain, matchDomains}) => {
+        const compare = d => domain.includes(d);
+        if (matchDomain && compare(matchDomain)) return true;
+        if (matchDomains && matchDomains.some(compare)) return true;
+        return false;
+      }) ?? fallbackDescriptor;
+
+    const {normal: normalText, compact: compactText} =
+      determineLinkText(data.url, descriptor, {language});
 
     return html.tag('a',
-      {href: data.url, class: 'icon'},
-      html.tag('svg', [
-        html.tag('title', msg),
-        html.tag('use', {
-          href: to('shared.staticIcon', id),
-        }),
-      ]));
+      {href: data.url, class: ['icon', slots.withText && 'has-text']},
+      [
+        html.tag('svg', [
+          !slots.withText &&
+            html.tag('title', normalText),
+
+          html.tag('use', {
+            href: to('shared.staticIcon', descriptor.icon),
+          }),
+        ]),
+
+        slots.withText &&
+          html.tag('span', {class: 'icon-text'},
+            compactText ?? normalText),
+      ]);
   },
 };