// @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 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); };