« get me outta code hell

content, test: image (and linkArtTag) - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-04-05 21:25:15 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-04-05 21:33:44 -0300
commit7fb97e45fb926aed5b6aceeb53abb139368a36ac (patch)
treea181caddaa83a2fc36b2ad332ebc89a45b373702
parent64885f8124106ef6e5ed400ad3bf2a813ccb0cd5 (diff)
content, test: image (and linkArtTag)
-rw-r--r--src/content/dependencies/image.js180
-rw-r--r--src/content/dependencies/linkArtTag.js8
-rw-r--r--src/misc-templates.js117
-rw-r--r--tap-snapshots/test/snapshot/image.js.test.cjs60
-rw-r--r--test/snapshot/image.js92
5 files changed, 340 insertions, 117 deletions
diff --git a/src/content/dependencies/image.js b/src/content/dependencies/image.js
new file mode 100644
index 00000000..1f904377
--- /dev/null
+++ b/src/content/dependencies/image.js
@@ -0,0 +1,180 @@
+import {empty} from '../../util/sugar.js';
+
+export default {
+  extraDependencies: [
+    'getSizeOfImageFile',
+    'html',
+    'language',
+    'thumb',
+    'to',
+  ],
+
+  data(artTags) {
+    const data = {};
+
+    if (artTags) {
+      data.contentWarnings =
+        artTags
+          .filter(tag => tag.isContentWarning)
+          .map(tag => tag.name);
+    } else {
+      data.contentWarnings = null;
+    }
+
+    return data;
+  },
+
+  generate(data, {
+    getSizeOfImageFile,
+    html,
+    language,
+    thumb,
+    to,
+  }) {
+    return html.template(slot =>
+      slot('src', ([src]) =>
+      slot('path', ([...path]) =>
+      slot('thumb', ([thumbKey = '']) =>
+      slot('link', ([link = false]) =>
+      slot('lazy', ([lazy = false]) =>
+      slot('square', ([willSquare = false]) => {
+        let originalSrc;
+
+        if (src) {
+          originalSrc = src;
+        } else if (!empty(path)) {
+          originalSrc = to(...path);
+        } else {
+          originalSrc = '';
+        }
+
+        const thumbSrc =
+          originalSrc &&
+            (thumbKey
+              ? thumb[thumbKey](originalSrc)
+              : originalSrc);
+
+        const willLink = typeof link === 'string' || link;
+        const willReveal = originalSrc && !empty(data.contentWarnings);
+
+        const idOnImg = willLink ? null : slot('id');
+        const idOnLink = willLink ? slot('id') : null;
+
+        if (!originalSrc) {
+          return prepare(
+            html.tag('div', {class: 'image-text-area'},
+              slot('missingSourceContent')));
+        }
+
+        let fileSize = null;
+        if (willLink) {
+          const mediaRoot = to('media.root');
+          if (originalSrc.startsWith(mediaRoot)) {
+            fileSize =
+              getSizeOfImageFile(
+                originalSrc
+                  .slice(mediaRoot.length)
+                  .replace(/^\//, ''));
+          }
+        }
+
+        let reveal = null;
+        if (willReveal) {
+          reveal = [
+            language.$('misc.contentWarnings', {
+              warnings: language.formatUnitList(data.contentWarnings),
+            }),
+            html.tag('br'),
+            html.tag('span', {class: 'reveal-interaction'},
+              language.$('misc.contentWarnings.reveal')),
+          ];
+        }
+
+        const className = slot('class');
+        const imgAttributes = {
+          id: idOnImg,
+          class: className,
+          alt: slot('alt'),
+          width: slot('width'),
+          height: slot('height'),
+          'data-original-size': fileSize,
+        };
+
+        const nonlazyHTML =
+          originalSrc &&
+            prepare(
+              html.tag('img', {
+                ...imgAttributes,
+                src: thumbSrc,
+              }));
+
+        if (lazy) {
+          return html.tags([
+            html.tag('noscript', nonlazyHTML),
+            prepare(
+              html.tag('img',
+                {
+                  ...imgAttributes,
+                  class: [className, 'lazy'],
+                  'data-original': thumbSrc,
+                }),
+              true),
+          ]);
+        }
+
+        return nonlazyHTML;
+
+        function prepare(content, hide = false) {
+          let wrapped = content;
+
+          wrapped =
+            html.tag('div', {class: 'image-container'},
+              wrapped);
+
+          if (willReveal) {
+            wrapped =
+              html.tag('div', {class: 'reveal'}, [
+                wrapped,
+                html.tag('span', {class: 'reveal-text-container'},
+                  html.tag('span', {class: 'reveal-text'},
+                    reveal)),
+              ]);
+          }
+
+          if (willSquare) {
+            wrapped =
+              html.tag('div',
+                {
+                  class: [
+                    'square',
+                    hide && !willLink && 'js-hide'
+                  ],
+                },
+
+                html.tag('div', {class: 'square-content'},
+                  wrapped));
+          }
+
+          if (willLink) {
+            wrapped = html.tag('a',
+              {
+                id: idOnLink,
+                class: [
+                  'box',
+                  'image-link',
+                  hide && 'js-hide',
+                ],
+
+                href:
+                  (typeof link === 'string'
+                    ? link
+                    : originalSrc),
+              },
+              wrapped);
+          }
+
+          return wrapped;
+        }
+      })))))));
+    },
+};
diff --git a/src/content/dependencies/linkArtTag.js b/src/content/dependencies/linkArtTag.js
new file mode 100644
index 00000000..7ddb7786
--- /dev/null
+++ b/src/content/dependencies/linkArtTag.js
@@ -0,0 +1,8 @@
+export default {
+  contentDependencies: ['linkThing'],
+
+  relations: (relation, artTag) =>
+    ({link: relation('linkThing', 'localized.tag', artTag)}),
+
+  generate: (relations) => relations.link,
+};
diff --git a/src/misc-templates.js b/src/misc-templates.js
index 710dbcc3..f2307724 100644
--- a/src/misc-templates.js
+++ b/src/misc-templates.js
@@ -327,123 +327,6 @@ function unbound_getFlashGridHTML({
   });
 }
 
-// Images
-
-function unbound_img({
-  getSizeOfImageFile,
-  html,
-  to,
-
-  src,
-  alt,
-  noSrcText = '',
-  thumb: thumbKey,
-  reveal,
-  id,
-  class: className,
-  width,
-  height,
-  link = false,
-  lazy = false,
-  square = false,
-}) {
-  const willSquare = square;
-  const willLink = typeof link === 'string' || link;
-
-  const originalSrc = src;
-  const thumbSrc = src && (thumbKey ? thumb[thumbKey](src) : src);
-
-  const href =
-    (willLink
-      ? (typeof link === 'string'
-          ? link
-          : originalSrc)
-      : null);
-
-  let fileSize = null;
-  const mediaRoot = to('media.root');
-  if (href?.startsWith(mediaRoot)) {
-    fileSize = getSizeOfImageFile(href.slice(mediaRoot.length).replace(/^\//, ''));
-  }
-
-  const imgAttributes = {
-    id: link ? '' : id,
-    class: className,
-    alt,
-    width,
-    height,
-    'data-original-size': fileSize,
-  };
-
-  const noSrcHTML =
-    !src &&
-      wrap(
-        html.tag('div',
-          {class: 'image-text-area'},
-          noSrcText));
-
-  const nonlazyHTML =
-    src &&
-      wrap(
-        html.tag('img', {
-          ...imgAttributes,
-          src: thumbSrc,
-        }));
-
-  const lazyHTML =
-    src &&
-    lazy &&
-      wrap(
-        html.tag('img',
-          {
-            ...imgAttributes,
-            class: [className, 'lazy'],
-            'data-original': thumbSrc,
-          }),
-        true);
-
-  if (!src) {
-    return noSrcHTML;
-  } else if (lazy) {
-    return html.tag('noscript', nonlazyHTML) + '\n' + lazyHTML;
-  } else {
-    return nonlazyHTML;
-  }
-
-  function wrap(input, hide = false) {
-    let wrapped = input;
-
-    wrapped = html.tag('div', {class: 'image-container'}, wrapped);
-
-    if (reveal) {
-      wrapped = html.tag('div', {class: 'reveal'}, [
-        wrapped,
-        html.tag('span', {class: 'reveal-text-container'},
-          html.tag('span', {class: 'reveal-text'}, reveal)),
-      ]);
-    }
-
-    if (willSquare) {
-      wrapped = html.tag('div', {class: 'square-content'}, wrapped);
-      wrapped = html.tag('div',
-        {class: ['square', hide && !willLink && 'js-hide']},
-        wrapped);
-    }
-
-    if (willLink) {
-      wrapped = html.tag('a',
-        {
-          id,
-          class: ['box', hide && 'js-hide', 'image-link'],
-          href,
-        },
-        wrapped);
-    }
-
-    return wrapped;
-  }
-}
-
 // Carousel reels
 
 // Layout constants:
diff --git a/tap-snapshots/test/snapshot/image.js.test.cjs b/tap-snapshots/test/snapshot/image.js.test.cjs
new file mode 100644
index 00000000..9f0e9948
--- /dev/null
+++ b/tap-snapshots/test/snapshot/image.js.test.cjs
@@ -0,0 +1,60 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below.  Do not ignore changes!
+ */
+'use strict'
+exports[`test/snapshot/image.js TAP image (snapshot) > content warnings via tags 1`] = `
+<div class="reveal">
+    <div class="image-container"><img src="media/album-art/beyond-canon/cover.png"></div>
+    <span class="reveal-text-container">
+        <span class="reveal-text">
+            cw: too cool for school
+            <br>
+            <span class="reveal-interaction">click to show</span>
+        </span>
+    </span>
+</div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > id with link 1`] = `
+<a id="banana" class="box image-link" href="foobar"><div class="image-container"><img src="foobar"></div></a>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > id with square 1`] = `
+<div class="square"><div class="square-content"><div class="image-container"><img id="banana" src="foobar"></div></div></div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > id without link 1`] = `
+<div class="image-container"><img id="banana" src="foobar"></div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > lazy with square 1`] = `
+<noscript><div class="square"><div class="square-content"><div class="image-container"><img src="foobar"></div></div></div></noscript>
+<div class="square js-hide"><div class="square-content"><div class="image-container"><img class=" lazy" data-original="foobar"></div></div></div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > link with file size 1`] = `
+<a class="box image-link" href="media/album-art/pingas/cover.png"><div class="image-container"><img data-original-size="1000000" src="media/album-art/pingas/cover.png"></div></a>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > source missing 1`] = `
+<div class="image-container"><div class="image-text-area">Example of missing source message.</div></div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > source via path 1`] = `
+<div class="image-container"><img src="media/album-art/beyond-canon/cover.png"></div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > source via src 1`] = `
+<div class="image-container"><img src="https://example.com/bananas.gif"></div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > square 1`] = `
+<div class="square"><div class="square-content"><div class="image-container"><img src="foobar"></div></div></div>
+`
+
+exports[`test/snapshot/image.js TAP image (snapshot) > width & height 1`] = `
+<div class="image-container"><img width="600" height="400" src="foobar"></div>
+`
diff --git a/test/snapshot/image.js b/test/snapshot/image.js
new file mode 100644
index 00000000..eeffb849
--- /dev/null
+++ b/test/snapshot/image.js
@@ -0,0 +1,92 @@
+import t from 'tap';
+import {testContentFunctions} from '../lib/content-function.js';
+
+testContentFunctions(t, 'image (snapshot)', async (t, evaluate) => {
+  await evaluate.load();
+
+  const quickSnapshot = (message, opts) =>
+    evaluate.snapshot(message, {
+      name: 'image',
+      extraDependencies: {
+        getSizeOfImageFile: () => 0,
+      },
+      ...opts,
+    });
+
+  quickSnapshot('source via path', {
+    postprocess: template => template
+      .slot('path', ['media.albumCover', 'beyond-canon', 'png']),
+  });
+
+  quickSnapshot('source via src', {
+    postprocess: template => template
+      .slot('src', 'https://example.com/bananas.gif'),
+  });
+
+  quickSnapshot('source missing', {
+    postprocess: template => template
+      .slot('missingSourceContent', 'Example of missing source message.'),
+  });
+
+  quickSnapshot('id without link', {
+    postprocess: template => template
+      .slot('src', 'foobar')
+      .slot('id', 'banana'),
+  });
+
+  quickSnapshot('id with link', {
+    postprocess: template => template
+      .slot('src', 'foobar')
+      .slot('link', true)
+      .slot('id', 'banana'),
+  });
+
+  quickSnapshot('id with square', {
+    postprocess: template => template
+      .slot('src', 'foobar')
+      .slot('square', true)
+      .slot('id', 'banana'),
+  })
+
+  quickSnapshot('width & height', {
+    postprocess: template => template
+      .slot('src', 'foobar')
+      .slot('width', 600)
+      .slot('height', 400),
+  });
+
+  quickSnapshot('square', {
+    postprocess: template => template
+      .slot('src', 'foobar')
+      .slot('square', true),
+  });
+
+  quickSnapshot('lazy with square', {
+    postprocess: template => template
+      .slot('src', 'foobar')
+      .slot('lazy', true)
+      .slot('square', true),
+  });
+
+  quickSnapshot('link with file size', {
+    extraDependencies: {
+      getSizeOfImageFile: () => 10 ** 6,
+    },
+
+    postprocess: template => template
+      .slot('path', ['media.albumCover', 'pingas', 'png'])
+      .slot('link', true),
+  });
+
+  quickSnapshot('content warnings via tags', {
+    args: [
+      [
+        {name: 'Dirk Strider', directory: 'dirk'},
+        {name: 'too cool for school', isContentWarning: true},
+      ],
+    ],
+
+    postprocess: template => template
+      .slot('path', ['media.albumCover', 'beyond-canon', 'png']),
+  })
+});