(o: string, f: (e: T) => void): (e: T) => void {\n return e => { halt(o, e); f(e); };\n}\n\n/**\n * Dispatch a {@link CustomEvent}.\n * @param el - the event target\n * @param type - the event type, e.g. `\"myapp:clear-cart\"`\n * @param [detail] - Event.detail\n */\nexport function dispatch(el: EventTarget, type: string, detail?: any) {\n return el.dispatchEvent(new CustomEvent(type, { detail }));\n}\n\n/**\n * Get, remove or set an attribute.\n * \n * - attr(el, \"name\") Get the attribute \"name\"\n * - attr(el, \"name\", \"value\") Set the attribute \"name\" to \"value\"\n * - attr(el, \"name\", null) Remove the attribute \"name\"\n * - attr(el, [ nameA: \"valueA\", nameB: \"valueB\" ]) Set the attributes name-a to \"valueA\", name-b to \"valueB\"\n * \n * @param el \n * @param name - The attribute name **or** a map of names to values.\n * If an object is passed, camelCase attribute names will be converted to kebab-case.\n * @param value - The value of the attribute, when setting. Pass `null` to remove an attribute.\n */\nexport function attr(el: Element, name: string | object, value: any = undefined): string | null {\n if (typeof name === \"object\") {\n for (const at in name) el.setAttribute(camelToKebab(at), name[at]);\n return null;\n }\n const curValue = el.getAttribute(name);\n if (value === undefined)\n return el.getAttribute(name);\n else if (value === null)\n return el.removeAttribute(name), curValue;\n else\n return el.setAttribute(name, value), value;\n}\n\n/**\n * Convert a node to equivalent HTML.\n */\nexport function stringifyNode(node: Node): string {\n const tmp = document.createElement(\"div\");\n tmp.append(node);\n return tmp.innerHTML;\n}\n\n/**\n * HTML-escape a string.\n * If given a DOM node, it will return **unescaped** HTML for it.\n * Returns empty string when given null or undefined.\n */\nexport function htmlescape(s: any): string {\n if (s === null || s === undefined)\n return \"\";\n if (s instanceof Node)\n return stringifyNode(s);\n return String(s)\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll(\"'\", \"'\")\n .replaceAll(\"\\\"\", \""\");\n}\n\n\n/**\n * Template literal that escapes HTML in interpolated values and returns a DocumentFragment.\n * Can also be called with a string to parse it as HTML.\n * To let trusted HTML through escaping, parse it first:\n * \n * html`My trusted markup: ${html(trustedMarkup)}
`\n */\nexport function html(str: TemplateStringsArray | string, ...values: any[]): DocumentFragment {\n const tmpl = document.createElement(\"template\");\n if (typeof str === \"object\" && \"raw\" in str)\n str = String.raw(str, ...values.map(htmlescape));\n tmpl.innerHTML = str;\n return tmpl.content;\n}\n\n/**\n * 'Type \"Element\" cannot be assigned to type \"HTMLElement\"' SHUT UP\n * @param {*} [el] \n * @returns {HTMLElement | null}\n */\nexport function asHtml(el: any): HTMLElement | null {\n return el instanceof HTMLElement ? el : null;\n}\n\n/**\n * Find the next element matching a given selector, searching deeply throughout the DOM.\n * @see traverse\n * @param root - The element within which to look for the next element, e.g. a menu.\n * @param selector - The selector to look for, e.g. `\"[role=menuitem]\"`.\n * @param [current] - The element to start the search from, e.g. the currently selected menu item.\n * If missing, the first or last matching element will be returned (depending on search direction).\n * @param [options]\n * @param [options.wrap] Whether to wrap around when the end/start of {@link root} is reached.\n */\nexport function next(\n root: ParentNode,\n selector: string, \n current: Element | null, \n options: { wrap?: boolean } = {}\n) {\n return traverse(\"next\", root, selector, current, options);\n}\n\n/**\n * Find the previous element matching a given selector, searching deeply throughout the DOM.\n * @see traverse\n * @param root - The element within which to look for the next element, e.g. a menu.\n * @param selector - The selector to look for, e.g. `\"[role=menuitem]\"`.\n * @param [current] - The element to start the search from, e.g. the currently selected menu item.\n * If missing, the first or last matching element will be returned (depending on search direction).\n * @param [options]\n * @param [options.wrap] Whether to wrap around when the end/start of {@link root} is reached.\n */\n export function prev(\n root: ParentNode,\n selector: string,\n current: Element | null,\n options: { wrap?: boolean } = {}\n) {\n return traverse(\"previous\", root, selector, current, options);\n}\n\ntype KeyboardEventListener = (e: KeyboardEvent) => void;\n\n/**\n * Create a handler for keyboard events using a keyboard shortcut DSL.\n * \n * - \"ArrowLeft\"\n * - \"Ctrl+Alt+3\"\n */\nexport function hotkey(hotkeys: Record): KeyboardEventListener {\n const alt = 0b1, ctrl = 0b10, meta = 0b100, shift = 0b1000;\n const handlers = {}; // handlers[key][modifiers as bitfields]\n const modifiersOf = (e: KeyboardEvent) => ~~(e.altKey && alt) | ~~(e.ctrlKey && ctrl) | ~~(e.metaKey && meta) | ~~(e.shiftKey && shift);\n const parse = (hotkeySpec: string) => {\n const\n tokens = hotkeySpec.split(\"+\"), key = tokens.pop()!;\n let modifiers = 0 | 0;\n for (const token in tokens)\n switch (token.toLowerCase()) {\n case \"alt\": modifiers |= alt; break;\n case \"ctrl\": modifiers |= ctrl; break;\n case \"meta\": modifiers |= meta; break;\n case \"shift\": modifiers |= shift; break;\n }\n return [key, modifiers];\n };\n\n for (const [hotkeySpec, handler] of Object.entries(hotkeys)) {\n const [key, modifiers] = parse(hotkeySpec);\n (handlers[key] ??= new Array(8))[modifiers] = handler;\n }\n\n return e => handlers[e.key]?.[modifiersOf(e)]?.(e);\n}\n\n/**\n * Debounce a function.\n * \n * @param t - The debounce time.\n * @param f - The function.\n * @param [options.mode] - Leading or trailing debounce.\n */\nexport function debounce(t: number, f: (...args: TArgs) => void, { mode = \"trailing\" } = {}): typeof f {\n let timeout: number | null = null;\n return (...args: TArgs) => {\n if (timeout)\n clearTimeout(timeout);\n else if (mode === \"leading\")\n f(...args);\n timeout = setTimeout(() => {\n if (mode === \"trailing\")\n f(...args);\n timeout = null;\n }, t);\n };\n}\n\n/**\n * Create a behavior that applies to elements matching the given selector.\n * @returns A function that can be called to apply the behavior to new elements within a subtree.\n */\nexport function behavior(selector: string, init: BehaviorInit): Behavior {\n const initialized = new WeakSet;\n return (subtree = document, options = {}) => {\n const root = subtree.getRootNode() as Root;\n $$(subtree, selector).forEach(el => {\n if (initialized.has(el))\n return;\n initialized.add(el);\n init(el, { options, root });\n });\n };\n}\n\ntype Repeater = {\n /**\n * Returns the HTML id for a given data value.\n * @param datum The data value.\n */\n idOf(datum: TData): string;\n\n /**\n * Createsa an element for a data value.\n * @param data The data value\n * @param ctx.id The id the returned element should have.\n */\n create(data: TData, ctx: { id: string }): Node;\n\n /**\n * \n * @param el The current element.\n * @param data The new data value.\n */\n update?(el: Element, data: TData): void;\n}\n\n/**\n * Repeat an element such that the list can be updated when data changes.\n * \n * @param {object} context\n * @param {(data: TData) => string} context.idOf\n * @param {(data: TData, ctx: { id: string }) => Node} context.create\n * @param {(el: Element, data: TData) => void} [context.update]\n * @returns \n */\nexport function repeater(container: ParentNode, rep: Repeater) {\n return (dataset: any) => {\n let cursor: ChildNode | null = null;\n\n const append = (...nodes: any[]) => {\n const oldcursor = cursor;\n cursor = nodes.at(-1);\n if (cursor instanceof DocumentFragment)\n cursor = cursor.lastChild;\n if (oldcursor)\n oldcursor.after(...nodes);\n else\n container.prepend(...nodes);\n };\n const clearAfter = (cursor: Node | null) => {\n if (cursor)\n while (cursor.nextSibling)\n cursor.nextSibling.remove();\n else\n container.replaceChildren();\n };\n\n const root = container.getRootNode() as Root;\n\n // TODO: use an actual morphing algo\n for (const datum of dataset) {\n const\n id = rep.idOf(datum), existing = root.getElementById(id);\n if (existing)\n append(rep.update?.(existing, datum) ?? existing);\n else\n append(rep.create(datum, { id }));\n }\n clearAfter(cursor);\n };\n}\n"],"mappings":"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","names":["makelogger","scope","args","ilog","camelToKebab","s","traverse","direction","root","selector","current","options","wrap","advance","wrapIt","$","$$","cursor","found","sel","on","target","type","listener","listenerWrapper","e","off","halt","o","t","halts","f","dispatch","el","detail","attr","name","value","at","curValue","stringifyNode","node","tmp","htmlescape","html","str","values","tmpl","asHtml","next","prev","hotkey","hotkeys","handlers","modifiersOf","parse","hotkeySpec","tokens","key","modifiers","token","handler","debounce","mode","timeout","behavior","init","initialized","subtree","repeater","container","rep","dataset","append","nodes","oldcursor","clearAfter","datum","id","existing"],"sourceRoot":"file:///home/dz4k/Documents/Projects/bigsky.software/missing.css","file":"/missing-js/19.js.map"}