]> Lady’s Gitweb - GitWikiWeb/commitdiff
Support “timeline” definition lists
authorLady <redacted>
Sat, 23 Sep 2023 23:19:59 +0000 (19:19 -0400)
committerLady <redacted>
Sat, 23 Sep 2023 23:19:59 +0000 (19:19 -0400)
These make it easier to construct timelines by just doing

    {.timeline}
    : 1972-01-01

      - Here is a thing which happened on Jan 01 1972

    : 1972-12-31

      - Here is a thing which happened on Dec 31 1972

There’s a lot of work which could be done to style these, but it’s at
least semantically appropriate.

build.js

index fa0b836190c555699758a895b6d62faf6c5e1cee..2381a51c426a552ca7d4620385c2678098d08a54 100644 (file)
--- a/build.js
+++ b/build.js
@@ -42,7 +42,7 @@ import {
   JSON_SCHEMA,
   parse as parseYaml,
 } from "https://deno.land/std@0.196.0/yaml/mod.ts";
   JSON_SCHEMA,
   parse as parseYaml,
 } from "https://deno.land/std@0.196.0/yaml/mod.ts";
-import djot from "npm:@djot/djot@0.2.3";
+import djot from "npm:@djot/djot@0.2.4";
 import { Parser } from "npm:htmlparser2@9.0.0";
 import { DomHandler, Element, Text } from "npm:domhandler@5.0.3";
 import * as domutils from "npm:domutils@3.1.0";
 import { Parser } from "npm:htmlparser2@9.0.0";
 import { DomHandler, Element, Text } from "npm:domhandler@5.0.3";
 import * as domutils from "npm:domutils@3.1.0";
@@ -229,6 +229,115 @@ class GitWikiWebPage {
     djot.applyFilter(ast, () => {
       let titleSoFar = null; // used to collect strs from headings
       return {
     djot.applyFilter(ast, () => {
       let titleSoFar = null; // used to collect strs from headings
       return {
+        definition_list: {
+          enter: (e) => {
+            const attributes = e.attributes ?? {};
+            if (
+              (attributes.class ?? "").split(/\s/gu).includes(
+                "timeline",
+              )
+            ) {
+              const years = new Map();
+              for (
+                const { children: [{ children: termChildren }, defn] }
+                  of e.children
+              ) {
+                const { label, year } = (() => {
+                  if (termChildren.length != 1) {
+                    return { label: termChildren, year: termChildren };
+                  } else {
+                    const str = termChildren[0];
+                    if (str.tag != "str") {
+                      return {
+                        label: termChildren,
+                        year: termChildren,
+                      };
+                    } else {
+                      const { text } = str;
+                      return {
+                        label: text,
+                        year:
+                          /^([0-9X]{4})(?:-[0-9X]{2}(?:-[0-9X]{2})?)?$/u
+                            .exec(text)?.[1] ?? text,
+                      };
+                    }
+                  }
+                })();
+                const yearList = (() => {
+                  const result = {
+                    tag: "bullet_list",
+                    tight: false,
+                    style: "-",
+                    children: [],
+                  };
+                  if (years.has(year)) {
+                    const yearMap = years.get(year);
+                    if (yearMap.has(label)) {
+                      return yearMap.get(label);
+                    } else {
+                      yearMap.set(label, result);
+                      return result;
+                    }
+                  } else {
+                    years.set(year, new Map([[label, result]]));
+                    return result;
+                  }
+                })();
+                const misc = { tag: "list_item", children: [] };
+                for (const child of defn.children) {
+                  if (child.tag == "bullet_list") {
+                    yearList.children.push(...child.children);
+                  } else {
+                    misc.children.push(child);
+                  }
+                }
+                if (misc.children.length > 0) {
+                  yearList.children.unshift(misc);
+                } else {
+                  /* do nothing */
+                }
+              }
+              const sorted = [...years].sort(([a], [b]) =>
+                typeof a != "string" || isNaN(a) ||
+                  typeof b != "string" || isNaN(b) || +a == +b
+                  ? 0
+                  : 1 - 2 * (+a < b)
+              );
+              sorted.forEach((pair) =>
+                pair[1] = [...pair[1]].sort(([a], [b]) =>
+                  1 - 2 * (a < b)
+                )
+              );
+              return {
+                tag: "div",
+                attributes,
+                children: sorted.flatMap(([year, yearDef]) => [
+                  rawBlock`<details open="">`,
+                  rawBlock`<summary>`,
+                  ...(Array.isArray(year) ? year : [str`${year}`]),
+                  rawBlock`</summary>`,
+                  rawBlock`<dl>`,
+                  ...yearDef.map(([label, list]) => ({
+                      tag: "div",
+                      children: [
+                        rawBlock`<dt>`,
+                        ...(Array.isArray(label) ? label : [str`${label}`]),
+                        rawBlock`</dt>`,
+                        rawBlock`<dd>`,
+                        list,
+                        rawBlock`</dd>`,
+                      ],
+                    })),
+                  rawBlock`</dl>`,
+                  rawBlock`</details>`,
+                ]),
+              };
+            } else {
+              /* do nothing */
+            }
+          },
+          exit: (_) => {},
+        },
         hard_break: {
           enter: (_) => {
             if (titleSoFar != null) {
         hard_break: {
           enter: (_) => {
             if (titleSoFar != null) {
This page took 0.023745 seconds and 4 git commands to generate.