formats.exposed/public/gif/getNodeUi.js

71 lines
1.9 KiB
JavaScript

// @ts-check
import { GifNodeType } from "./constants.js";
import crel, { fragment } from "../common/crel.js";
import getOwn from "../common/getOwn.js";
import { areBytesEqual } from "../common/bytes.js";
/** @typedef {import("../../types/gif.d.ts").GifNode} GifNode */
const textEncoder = new TextEncoder();
const VERSION_87 = textEncoder.encode("87a");
const VERSION_89 = textEncoder.encode("89a");
/**
* @param {(string | Node)[]} children
*/
const p = (...children) => crel("p", {}, ...children);
/**
* @typedef {object} NodeUi
* @prop {string} title
* @prop {HTMLElement | DocumentFragment} description
*/
/** @type {Record<string, (node: GifNode) => NodeUi>} */
const NODE_UI_FNS = {
[GifNodeType.root]: () => ({
title: "GIF file",
description: fragment(),
}),
// Header
[GifNodeType.header]: () => ({
title: "GIF header",
description: fragment(
p('GIFs start with a 6-byte header. This is typically the string "GIF87a" or "GIF89a", encoded as ASCII.'),
),
}),
[GifNodeType.headerSignature]: () => ({
title: "GIF signature",
description: p('GIFs start with the string "GIF", encoded as ASCII.'),
}),
[GifNodeType.headerVersion]: ({ bytes }) => {
/** @type {string} */ let end;
if (areBytesEqual(bytes, VERSION_87)) {
end = "87a";
} else if (areBytesEqual(bytes, VERSION_89)) {
end = "89a";
} else {
end = "something unexpected! This might not be a valid GIF file";
}
return {
title: "GIF version",
description: p(
`The version of the GIF format. This is typically "87a" or "89a". In this case, the version is ${end}.`,
),
};
},
};
/**
* @param {GifNode} node
* @returns {NodeUi}
*/
export default (node) => {
const uiFn = getOwn(NODE_UI_FNS, node.type);
if (!uiFn) throw new Error("Found a node with no matching UI function");
return uiFn(node);
};