« get me outta code hell

replacer, content, css: videos in content text - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2024-12-29 13:53:25 -0400
committer(quasar) nebula <qznebula@protonmail.com>2024-12-29 13:53:25 -0400
commit012e29630fa0bddfd49faf97caf84585109e07ad (patch)
tree6528fe11ea7ef133385a8a4573c8d858eae1ab4c /src
parente23d54ab78cb38f90ed9f667759f986792aceb86 (diff)
replacer, content, css: videos in content text
Diffstat (limited to 'src')
-rw-r--r--src/content/dependencies/transformContent.js56
-rw-r--r--src/static/css/site.css18
-rw-r--r--src/util/replacer.js51
3 files changed, 113 insertions, 12 deletions
diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js
index 3d9fdd60..48e20f94 100644
--- a/src/content/dependencies/transformContent.js
+++ b/src/content/dependencies/transformContent.js
@@ -362,6 +362,41 @@ export default {
             };
           }
 
+          case 'video': {
+            const src =
+              (node.src.startsWith('media/')
+                ? to('media.path', node.src.slice('media/'.length))
+                : node.src);
+
+            const {
+              width,
+              height,
+              align,
+              pixelate,
+            } = node;
+
+            const content =
+              html.tag('div', {class: 'content-video-container'},
+                html.tag('video',
+                  src && {src},
+                  width && {width},
+                  height && {height},
+
+                  {controls: true},
+
+                  align === 'center' &&
+                    {class: 'align-center'},
+
+                  pixelate &&
+                    {class: 'pixelate'}));
+
+            return {
+              type: 'processed-video',
+              data:
+                content,
+            };
+          }
+
           case 'internal-link': {
             const nodeFromRelations = relations.internalLinks[internalLinkIndex++];
             if (nodeFromRelations.type === 'text') {
@@ -541,15 +576,18 @@ export default {
 
         const attributes = html.parseAttributes(match[1]);
 
-        // Images that were all on their own line need to be removed from
-        // the surrounding <p> tag that marked generates. The HTML parser
-        // treats a <div> that starts inside a <p> as a Crocker-class
-        // misgiving, and will treat you very badly if you feed it that.
-        if (attributes.get('data-type') === 'processed-image') {
-          if (!attributes.get('data-inline')) {
-            tags[tags.length - 1] = tags[tags.length - 1].replace(/<p>$/, '');
-            deleteParagraph = true;
-          }
+        // Images (or videos) that were all on their own line need to be
+        // removed from the surrounding <p> tag that marked generates.
+        // The HTML parser treats a <div> that starts inside a <p> as a
+        // Crocker-class misgiving, and will treat you very badly if you
+        // feed it that.
+        if (
+          (attributes.get('data-type') === 'processed-image' &&
+          !attributes.get('data-inline')) ||
+          attributes.get('data-type') === 'processed-video'
+        ) {
+          tags[tags.length - 1] = tags[tags.length - 1].replace(/<p>$/, '');
+          deleteParagraph = true;
         }
 
         const nonTextNodeIndex = match[2];
diff --git a/src/static/css/site.css b/src/static/css/site.css
index 7fe6f915..56a6ad40 100644
--- a/src/static/css/site.css
+++ b/src/static/css/site.css
@@ -1399,12 +1399,14 @@ p.image-details.illustrator-details {
   display: none;
 }
 
-.content-image-container {
+.content-image-container,
+.content-video-container {
   margin-top: 1em;
   margin-bottom: 1em;
 }
 
-.content-image-container.align-center {
+.content-image-container.align-center,
+.content-video-container.align-center {
   text-align: center;
   margin-top: 1.5em;
   margin-bottom: 1.5em;
@@ -1944,6 +1946,15 @@ h1 a[href="#additional-names-box"]:hover {
   color: white;
 }
 
+/* Videos (in content) get a lite version of image-container. */
+.content-video-container {
+  width: min-content;
+  background-color: var(--dark-color);
+  border: 2px solid var(--primary-color);
+  border-radius: 2.5px 2.5px 3px 3px;
+  padding: 5px;
+}
+
 .image-text-area {
   position: absolute;
   top: 0;
@@ -2013,7 +2024,8 @@ img {
     6px -6px 2px -4px white inset;
 }
 
-img.pixelate, .pixelate img {
+img.pixelate, .pixelate img,
+video.pixelate, .pixelate video {
   image-rendering: crisp-edges;
 }
 
diff --git a/src/util/replacer.js b/src/util/replacer.js
index 78b4a895..e3f5623e 100644
--- a/src/util/replacer.js
+++ b/src/util/replacer.js
@@ -599,6 +599,56 @@ export function postprocessImages(inputNodes) {
   return outputNodes;
 }
 
+export function postprocessVideos(inputNodes) {
+  const outputNodes = [];
+
+  for (const node of inputNodes) {
+    if (node.type !== 'text') {
+      outputNodes.push(node);
+      continue;
+    }
+
+    const videoRegexp = /<video (.*?)>(<\/video>)?/g;
+
+    let match = null, parseFrom = 0;
+    while (match = videoRegexp.exec(node.data)) {
+      const previousText = node.data.slice(parseFrom, match.index);
+
+      outputNodes.push({
+        type: 'text',
+        data: previousText,
+        i: node.i + parseFrom,
+        iEnd: node.i + parseFrom + match.index,
+      });
+
+      parseFrom = match.index + match[0].length;
+
+      const videoNode = {type: 'video'};
+      const attributes = html.parseAttributes(match[1]);
+
+      videoNode.src = attributes.get('src');
+
+      if (attributes.get('width')) videoNode.width = parseInt(attributes.get('width'));
+      if (attributes.get('height')) videoNode.height = parseInt(attributes.get('height'));
+      if (attributes.get('align')) videoNode.align = attributes.get('align');
+      if (attributes.get('pixelate')) videoNode.pixelate = true;
+
+      outputNodes.push(videoNode);
+    }
+
+    if (parseFrom !== node.data.length) {
+      outputNodes.push({
+        type: 'text',
+        data: node.data.slice(parseFrom),
+        i: node.i + parseFrom,
+        iEnd: node.iEnd,
+      });
+    }
+  }
+
+  return outputNodes;
+}
+
 export function postprocessHeadings(inputNodes) {
   const outputNodes = [];
 
@@ -760,6 +810,7 @@ export function parseInput(input) {
     let output = parseNodes(input, 0);
     output = postprocessComments(output);
     output = postprocessImages(output);
+    output = postprocessVideos(output);
     output = postprocessHeadings(output);
     output = postprocessSummaries(output);
     output = postprocessExternalLinks(output);