{"version":3,"file":"handlers.mjs","sources":["../src/handlers/show.js","../src/handlers/className.js","../src/handlers/boolAttr.js","../src/handlers/ariaAttr.js","../src/handlers/classToggle.js","../src/handlers/stringAttr.js","../src/handlers/on.js","../src/handlers/htmlAttrs.js","../src/handlers/presets.js"],"sourcesContent":["/** data-show=\"key\" → el.hidden = !Boolean(val) */\nexport const show = {\n  attr: 'data-show',\n  apply(el, val) { el.hidden = !Boolean(val); }\n};\n","/** data-classname=\"key\" → el.className = val || '' */\nexport const className = {\n  attr: 'data-classname',\n  apply(el, val) { el.className = val || ''; }\n};\n","/**\n * Create a handler for any HTML boolean attribute.\n * Uses toggleAttribute() — works correctly with any attribute name\n * (readonly, contenteditable, etc.) without worrying about camelCase property names.\n *\n * @param {string} name - Attribute name (e.g., 'readonly', 'open', 'contenteditable')\n * @returns {{ attr: string, apply: function }}\n */\nexport function boolAttr(name) {\n  return {\n    attr: `data-${name}`,\n    apply(el, val) { el.toggleAttribute(name, Boolean(val)); }\n  };\n}\n","/**\n * Create a handler for an ARIA attribute.\n * Coerces value to \"true\"/\"false\" string — use stringAttr(\"aria-X\") for token/string ARIA attrs.\n *\n * @param {string} name - ARIA name, with or without \"aria-\" prefix\n * @returns {{ attr: string, apply: function }}\n */\nexport function ariaAttr(name) {\n  const fullName = name.startsWith('aria-') ? name : `aria-${name}`;\n  return {\n    attr: `data-${fullName}`,\n    apply(el, val) { el.setAttribute(fullName, val ? 'true' : 'false'); }\n  };\n}\n","/**\n * Create handlers for CSS class toggling.\n * Each name creates a handler: data-class-{name}=\"key\" → el.classList.toggle(name, Boolean(val))\n * Returns an array — pass directly to handlers (auto-flattened by bindDom).\n *\n * @param {...string} names - CSS class names to create handlers for\n * @returns {Array<{ attr: string, apply: function }>}\n */\nexport function classToggle(...names) {\n  return names.map(name => ({\n    attr: `data-class-${name}`,\n    apply(el, val) { el.classList.toggle(name, Boolean(val)); }\n  }));\n}\n","/**\n * Create a handler for any string attribute (href, src, title, alt, action, etc.)\n * Sets the attribute value as a string. Removes the attribute when value is null/undefined.\n *\n * @param {string} name - HTML attribute name (e.g., 'href', 'src', 'title')\n * @returns {{ attr: string, apply: function }}\n */\n\n// Matched against a normalized copy of the value (C0 controls, space, and\n// DEL stripped; lowercased) because the browser URL parser ignores those\n// characters when determining the scheme — \"java\\tscript:alert(1)\" and\n// \" javascript:alert(1)\" both execute. The colon is required so legitimate\n// relative URLs like \"javascript-tutorial.html\" are not blocked.\nconst DANGEROUS_SCHEME = /^(?:javascript:|vbscript:|data:text\\/html)/;\nconst IGNORED_URL_CHARS = /[\\u0000-\\u0020\\u007F]/g;\nconst URI_ATTRS = new Set(['href', 'src', 'action', 'srcset', 'poster', 'formaction']);\n\nexport function stringAttr(name) {\n  return {\n    attr: `data-${name}`,\n    apply(el, val) {\n      if (val == null) {\n        el.removeAttribute(name);\n        return;\n      }\n      const strVal = String(val);\n      if (URI_ATTRS.has(name) &&\n          DANGEROUS_SCHEME.test(strVal.replace(IGNORED_URL_CHARS, '').toLowerCase())) {\n        el.removeAttribute(name);\n        return;\n      }\n      el.setAttribute(name, strVal);\n    }\n  };\n}\n","/**\n * Create handlers for declarative event wiring.\n * Each type creates a handler: data-on{type}=\"key\" wires the function held\n * at that store key as a DOM event listener.\n *\n *   <button data-onclick=\"addTodo\">Add</button>\n *\n *   const store = state({\n *     addTodo: (event) => { ... }   // a plain function in state\n *   });\n *   bindDom(root, store, { handlers: [on('click')] });\n *\n * Reactive like any binding: assigning a new function to the key re-wires\n * the listener; assigning null/undefined detaches it.\n *\n * Returns an array — pass directly to handlers (auto-flattened by bindDom).\n *\n * @security Same trust model as data-bind: an injected data-on* attribute\n * can only reference functions that already exist in reachable state — no\n * expressions, no eval. Ensure your HTML is trusted or sanitized.\n *\n * Note: bindDom's cleanup stops future re-wiring but does not detach\n * listeners already attached to elements that remain in the DOM. Discard\n * the bound subtree (the normal SPA teardown) to drop them.\n *\n * @param {...string} types - DOM event types ('click', 'input', 'submit', ...)\n * @returns {Array<{ attr: string, apply: function }>}\n */\n\nimport { logWarn } from '../utils/log.js';\n\n// element → Map<eventType, listener> — tracks what we attached so a\n// re-assigned store key swaps the listener instead of stacking a second one.\nconst attached = new WeakMap();\n\nexport function on(...types) {\n  return types.map(type => ({\n    attr: `data-on${type}`,\n    apply(el, val) {\n      let byType = attached.get(el);\n      const prev = byType ? byType.get(type) : undefined;\n\n      if (prev) {\n        el.removeEventListener(type, prev);\n        byType.delete(type);\n      }\n\n      if (typeof val === 'function') {\n        el.addEventListener(type, val);\n        if (!byType) {\n          byType = new Map();\n          attached.set(el, byType);\n        }\n        byType.set(type, val);\n      } else if (val != null) {\n        logWarn(`[Lume.js] on('${type}'): bound value is not a function — listener detached`);\n      }\n    }\n  }));\n}\n","import { show }       from './show.js';\nimport { boolAttr }   from './boolAttr.js';\nimport { ariaAttr }   from './ariaAttr.js';\nimport { stringAttr } from './stringAttr.js';\n\n/** @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#boolean_attributes */\nconst BOOL_ATTRS = [\n  'readonly', 'open', 'novalidate', 'formnovalidate', 'multiple',\n  'autofocus', 'autoplay', 'controls', 'loop', 'muted', 'defer',\n  'async', 'reversed', 'selected', 'inert', 'allowfullscreen',\n];\n\n/** @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes */\nconst STRING_ATTRS = [\n  'href', 'src', 'alt', 'title', 'placeholder', 'action', 'method',\n  'target', 'rel', 'type', 'name', 'role', 'lang', 'tabindex',\n  'pattern', 'min', 'max', 'step', 'minlength', 'maxlength',\n  'width', 'height', 'for', 'form', 'accept', 'autocomplete',\n  'loading', 'decoding', 'inputmode', 'enterkeyhint', 'draggable',\n  'contenteditable', 'spellcheck', 'translate', 'dir', 'id',\n  'poster', 'preload', 'download', 'media', 'sizes', 'srcset',\n  'colspan', 'rowspan', 'scope', 'headers', 'wrap', 'sandbox',\n];\n\n/** ARIA boolean state attributes — coerced to \"true\"/\"false\" string. */\nconst ARIA_BOOL_ATTRS = [\n  'pressed', 'selected', 'disabled', 'checked', 'invalid', 'required',\n  'busy', 'modal', 'multiselectable', 'multiline', 'readonly', 'atomic',\n];\n\n/** ARIA string/token/numeric attributes — value passed through as-is. */\nconst ARIA_STRING_ATTRS = [\n  'current', 'live', 'relevant', 'haspopup',\n  'sort', 'autocomplete', 'orientation',\n  'label', 'describedby', 'labelledby', 'controls', 'owns',\n  'activedescendant', 'errormessage', 'details', 'flowto',\n  'valuenow', 'valuemin', 'valuemax', 'valuetext',\n  'colcount', 'colindex', 'colspan', 'rowcount', 'rowindex', 'rowspan',\n  'level', 'setsize', 'posinset', 'placeholder', 'roledescription',\n  'keyshortcuts', 'braillelabel', 'brailleroledescription',\n];\n\n/**\n * One-import preset that enables all standard HTML attributes as reactive handlers.\n * Returns a flat array — pass directly to handlers option.\n *\n * @returns {Array<{ attr: string, apply: function }>}\n */\nexport function htmlAttrs() {\n  return [\n    show,\n    ...BOOL_ATTRS.map(name => boolAttr(name)),\n    ...STRING_ATTRS.map(name => stringAttr(name)),\n    ...ARIA_BOOL_ATTRS.map(name => ariaAttr(name)),\n    ...ARIA_STRING_ATTRS.map(name => stringAttr(`aria-${name}`)),\n  ];\n}\n","import { boolAttr } from './boolAttr.js';\nimport { ariaAttr } from './ariaAttr.js';\n\n/** Form-related handlers (beyond built-in disabled/checked/required) */\nexport const formHandlers = [\n  boolAttr('readonly'),\n];\n\n/** Additional ARIA handlers (beyond built-in aria-expanded/aria-hidden) */\nexport const a11yHandlers = [\n  ariaAttr('pressed'),\n  ariaAttr('selected'),\n  ariaAttr('disabled'),\n];\n"],"names":[],"mappings":"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"}