{"_id":"dom-types","_rev":"7-bc38fe782491a06600cf5ea4470f9a89","name":"dom-types","dist-tags":{"latest":"1.1.5"},"versions":{"1.0.0":{"name":"dom-types","version":"1.0.0","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","typescript","library"],"author":{"name":"Mikael Saurén"},"license":"MIT","_id":"dom-types@1.0.0","maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"homepage":"https://github.com/koodikulma-fi/dom-types","bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"dist":{"shasum":"6fbf383a5867d2852702fe3bb97f2bca3991e0d1","tarball":"https://registry.npmjs.org/dom-types/-/dom-types-1.0.0.tgz","fileCount":6,"integrity":"sha512-Lxdl/bhGOfN06OAkcGeYr4U5ieeYHmHvn3WZ93HCj9ajMqM38GifpCRwXPnPoT0DZsxX74XSJotF0p9Rex4t0g==","signatures":[{"sig":"MEQCIEN8nQDGgsqbKvwx9aTkW0HCtDDHsKYogrBwqrtv8FakAiBk1vAT5h90pJs3RuvdkX/tFN11oSppmxaSeb5tLnLd8Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":211939},"main":"./dom-types.js","types":"./dom-types.d.ts","module":"./dom-types.module.js","exports":{".":{"import":"./dom-types.module.js","require":"./dom-types.js"},"./package.json":"./package.json"},"gitHead":"81a948ca82ff9c89235dfea25f327269cb358125","private":false,"_npmUser":{"name":"koodikulma","email":"info@koodikulma.fi"},"repository":{"url":"git+https://github.com/koodikulma-fi/dom-types.git","type":"git"},"_npmVersion":"9.6.7","description":"Typing helpers for DOM attributes (HTML, SVG, CSS).","directories":{},"_nodeVersion":"18.17.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dom-types_1.0.0_1728674406167_0.2781139721608705","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"dom-types","version":"1.1.0","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","js","typescript","javascript","library"],"author":{"name":"Mikael Saurén"},"license":"MIT","_id":"dom-types@1.1.0","maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"homepage":"https://github.com/koodikulma-fi/dom-types","bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"dist":{"shasum":"0d8494046f9523a33b9c065af5a255dc57776002","tarball":"https://registry.npmjs.org/dom-types/-/dom-types-1.1.0.tgz","fileCount":11,"integrity":"sha512-VQs1DvcOBHr9jbq6G+tvmiQPr9d1abtgTVI0v9O20rUnBk6N274EiZ59GhmGlegJvsKS0Xo3HsIX6zIP1C5jZQ==","signatures":[{"sig":"MEYCIQC8tPM5upxKWtZ4pyb69BvB1EUDXf52U3sJEg0RMK7eiQIhAKjjJC03Q8w6RtE/7LYCA/t0oAZ6KPR5MkkaEfaAZog+","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":224947},"main":"./dom-types.js","types":"./dom-types.d.ts","module":"./dom-types.module.js","exports":{".":{"types":"./dom-types.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./js":{"types":"./js.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./core":{"types":"./core.d.ts"},"./native":{"types":"./native.d.ts"},"./camelCase":{"types":"./camelCase.d.ts"},"./package.json":"./package.json"},"gitHead":"6ac29a65e73c163c5d02670dcc5f6866b64a7718","private":false,"_npmUser":{"name":"koodikulma","email":"info@koodikulma.fi"},"repository":{"url":"git+https://github.com/koodikulma-fi/dom-types.git","type":"git"},"_npmVersion":"9.6.7","description":"Typing and JavaScript helpers for DOM attributes (HTML, SVG, CSS).","directories":{},"_nodeVersion":"18.17.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dom-types_1.1.0_1729035521172_0.7058804544694932","host":"s3://npm-registry-packages"}},"1.1.1":{"name":"dom-types","version":"1.1.1","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","js","typescript","javascript","library"],"author":{"name":"Mikael Saurén"},"license":"MIT","_id":"dom-types@1.1.1","maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"homepage":"https://github.com/koodikulma-fi/dom-types","bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"dist":{"shasum":"56c15d2c91e157d06e3d1149538c7d76bbddee70","tarball":"https://registry.npmjs.org/dom-types/-/dom-types-1.1.1.tgz","fileCount":11,"integrity":"sha512-uf2/CJm1rXFmnvVSQ+IwKL/Ds3zfdykMFH85ORQq86kGElOmt1AGab+C5G4cwIyiwKU3ZbYK6TAb9stDG/bKOw==","signatures":[{"sig":"MEUCIEFO9pxCkDY9yxjC0k7zV45s3LCk1lXzWPY9f6HQTqslAiEAmaEhvXMxf8uLXI8PEO09zPgc2nQYBsVZFTOXnITNLkE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":263041},"main":"./dom-types.js","types":"./dom-types.d.ts","module":"./dom-types.module.js","exports":{".":{"types":"./dom-types.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./js":{"types":"./js.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./core":{"types":"./core.d.ts"},"./native":{"types":"./native.d.ts"},"./camelCase":{"types":"./camelCase.d.ts"},"./package.json":"./package.json"},"gitHead":"5312052579bbb150077327dc72c55fc6894ec865","private":false,"_npmUser":{"name":"koodikulma","email":"info@koodikulma.fi"},"repository":{"url":"git+https://github.com/koodikulma-fi/dom-types.git","type":"git"},"_npmVersion":"9.6.7","description":"Typing and JavaScript helpers for DOM attributes (HTML, SVG, CSS).","directories":{},"_nodeVersion":"18.17.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dom-types_1.1.1_1730388777003_0.8609448743726724","host":"s3://npm-registry-packages"}},"1.1.2":{"name":"dom-types","version":"1.1.2","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","js","typescript","javascript","library"],"author":{"name":"Mikael Saurén"},"license":"MIT","_id":"dom-types@1.1.2","maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"homepage":"https://github.com/koodikulma-fi/dom-types","bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"dist":{"shasum":"f8edf74c1aefdffb2d45d29654c99ea04fbb202b","tarball":"https://registry.npmjs.org/dom-types/-/dom-types-1.1.2.tgz","fileCount":11,"integrity":"sha512-yBe608cqVMPsjOzNnID8VdoOBvpewvp7e9Z4E+hcrDMHDlpl8Wv+HJ+xtNLMeA1X9rWtGIbscI6LDpe32H7Krw==","signatures":[{"sig":"MEUCIAuinDjejXdBD9Jb2h4Ct8mtuzoXackbfXDEY3XKeUWHAiEAo3gIDBh16NNw4JOwhvrR/GzsNx/qmsWFqDyVsxjIne0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":265938},"main":"./dom-types.js","types":"./dom-types.d.ts","module":"./dom-types.module.js","exports":{".":{"types":"./dom-types.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./js":{"types":"./js.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./core":{"types":"./core.d.ts"},"./native":{"types":"./native.d.ts"},"./camelCase":{"types":"./camelCase.d.ts"},"./package.json":"./package.json"},"gitHead":"f2bcbbaf91a17f11d7cfd824aa524606f21dd54c","private":false,"_npmUser":{"name":"koodikulma","email":"info@koodikulma.fi"},"repository":{"url":"git+https://github.com/koodikulma-fi/dom-types.git","type":"git"},"_npmVersion":"9.6.7","description":"Typing and JavaScript helpers for DOM attributes (HTML, SVG, CSS).","directories":{},"_nodeVersion":"18.17.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dom-types_1.1.2_1731248079219_0.15799385721928028","host":"s3://npm-registry-packages"}},"1.1.3":{"name":"dom-types","version":"1.1.3","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","js","typescript","javascript","library"],"author":{"name":"Mikael Saurén"},"license":"MIT","_id":"dom-types@1.1.3","maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"homepage":"https://github.com/koodikulma-fi/dom-types","bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"dist":{"shasum":"bb500c2f8dbbb04d2d3fd68791a4753a35f4be35","tarball":"https://registry.npmjs.org/dom-types/-/dom-types-1.1.3.tgz","fileCount":11,"integrity":"sha512-bMM7A1/4jVACZzqDKU/TaCIffEb2e7gMkC1DMS0CCadAVWu5bFppqifHjmCp56xm2AHqPdNeRPwtU7A9PUTlGg==","signatures":[{"sig":"MEUCIBvD13Dii3AqdmlfvMtTtqIV+ko4vNYi6vobSDS6bCdFAiEAsH4NzTRn/oxjPleDB5eaf9n1HxIZr3Z7YM1RGnutnps=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":266184},"main":"./dom-types.js","types":"./dom-types.d.ts","module":"./dom-types.module.js","exports":{".":{"types":"./dom-types.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./js":{"types":"./js.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./core":{"types":"./core.d.ts"},"./native":{"types":"./native.d.ts"},"./camelCase":{"types":"./camelCase.d.ts"},"./package.json":"./package.json"},"gitHead":"c41ba908df28b4f72d7927a1efecdb9985ed41af","private":false,"_npmUser":{"name":"koodikulma","email":"info@koodikulma.fi"},"repository":{"url":"git+https://github.com/koodikulma-fi/dom-types.git","type":"git"},"_npmVersion":"9.6.7","description":"Typing and JavaScript helpers for DOM attributes (HTML, SVG, CSS).","directories":{},"_nodeVersion":"18.17.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dom-types_1.1.3_1770125197847_0.6343695189243703","host":"s3://npm-registry-packages-npm-production"}},"1.1.4":{"name":"dom-types","version":"1.1.4","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","js","typescript","javascript","library"],"author":{"name":"Mikael Saurén"},"license":"MIT","_id":"dom-types@1.1.4","maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"homepage":"https://github.com/koodikulma-fi/dom-types","bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"dist":{"shasum":"64983ed756a0db059a106c656c6afd249e662d32","tarball":"https://registry.npmjs.org/dom-types/-/dom-types-1.1.4.tgz","fileCount":11,"integrity":"sha512-S6maYqyNZVTvzEQHE1wzK0UbD+Aa9zpnzwkvJgSRNAMehW3swnrELvC5ur6j1L8ZozfPcpc8C14i3FjIc7GpDw==","signatures":[{"sig":"MEQCIHYFQPOuUUXxj3QaAdHl4AtDqxd+xCng68gVqvX019R9AiAArwXXRuYjyri/asYyuwu4cX44mnHyhrgVs1OPJ1iuxQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":266415},"main":"./dom-types.js","types":"./dom-types.d.ts","module":"./dom-types.module.js","exports":{".":{"types":"./dom-types.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./js":{"types":"./js.d.ts","import":"./dom-types.module.js","require":"./dom-types.js"},"./core":{"types":"./core.d.ts"},"./native":{"types":"./native.d.ts"},"./camelCase":{"types":"./camelCase.d.ts"},"./package.json":"./package.json"},"gitHead":"4419725cffe037ef7e6ae621edd5c7fb3014478f","private":false,"_npmUser":{"name":"koodikulma","email":"info@koodikulma.fi"},"repository":{"url":"git+https://github.com/koodikulma-fi/dom-types.git","type":"git"},"_npmVersion":"9.6.7","description":"Typing and JavaScript helpers for DOM attributes (HTML, SVG, CSS).","directories":{},"_nodeVersion":"18.17.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dom-types_1.1.4_1779819052587_0.033474638949673174","host":"s3://npm-registry-packages-npm-production"}},"1.1.5":{"name":"dom-types","version":"1.1.5","description":"Typing and JavaScript helpers for DOM attributes (HTML, SVG, CSS).","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","js","typescript","javascript","library"],"homepage":"https://github.com/koodikulma-fi/dom-types","author":{"name":"Mikael Saurén"},"license":"MIT","private":false,"repository":{"type":"git","url":"git+https://github.com/koodikulma-fi/dom-types.git"},"module":"./dom-types.module.js","main":"./dom-types.js","types":"./dom-types.d.ts","exports":{".":{"import":"./dom-types.module.js","require":"./dom-types.js","types":"./dom-types.d.ts"},"./package.json":"./package.json","./js":{"import":"./dom-types.module.js","require":"./dom-types.js","types":"./js.d.ts"},"./core":{"types":"./core.d.ts"},"./native":{"types":"./native.d.ts"},"./camelCase":{"types":"./camelCase.d.ts"}},"gitHead":"c77f377c4d25378f40ca8b68e80ee2fbce8d00d9","bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"_id":"dom-types@1.1.5","_nodeVersion":"18.17.1","_npmVersion":"9.6.7","dist":{"integrity":"sha512-4AB09NKm6OmW3jsQ1PNROxrWYv7laNg+B83AkdqaVS8a0OM22l4zyjWJXIprY3ELF9T0WQQ4kY/2AuxWJXEcUQ==","shasum":"29ae174590a0ac33c5480c43758bc7b4928900d7","tarball":"https://registry.npmjs.org/dom-types/-/dom-types-1.1.5.tgz","fileCount":11,"unpackedSize":266719,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCif/QGqkkTKZuSUQpBLWeZTmt8IS6FGty9hTX36c4EjgIgeWW4qen8eJqHU/t/75Uh1f9+OIQWZhmbT02WH2uMfIo="}]},"_npmUser":{"name":"koodikulma","email":"info@koodikulma.fi"},"directories":{},"maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dom-types_1.1.5_1784818691892_0.5256560385822409"},"_hasShrinkwrap":false}},"time":{"created":"2024-10-11T19:20:06.166Z","modified":"2026-07-23T14:58:12.237Z","1.0.0":"2024-10-11T19:20:06.437Z","1.1.0":"2024-10-15T23:38:41.416Z","1.1.1":"2024-10-31T15:32:57.218Z","1.1.2":"2024-11-10T14:14:39.422Z","1.1.3":"2026-02-03T13:26:38.007Z","1.1.4":"2026-05-26T18:10:52.721Z","1.1.5":"2026-07-23T14:58:12.044Z"},"bugs":{"url":"https://github.com/koodikulma-fi/dom-types/issues"},"author":{"name":"Mikael Saurén"},"license":"MIT","homepage":"https://github.com/koodikulma-fi/dom-types","keywords":["DomTypes","dom-types","dom types","dom","types","tools","web","ts","js","typescript","javascript","library"],"repository":{"type":"git","url":"git+https://github.com/koodikulma-fi/dom-types.git"},"description":"Typing and JavaScript helpers for DOM attributes (HTML, SVG, CSS).","maintainers":[{"name":"koodikulma","email":"info@koodikulma.fi"}],"readme":"\r\n## WHAT\r\n\r\n`dom-types` is a light weight library containing typing for HTML & SVG attributes and CSS properties, and a few related JS methods.\r\n\r\nThe types are provided in native as well as camelCase naming, while the JS side works for both.\r\n\r\nThe npm package can be found with: [dom-types](https://www.npmjs.com/package/dom-types). Contribute in GitHub: [koodikulma-fi/dom-types.git](https://github.com/koodikulma-fi/dom-types.git)\r\n\r\n---\r\n\r\n## CONTENTS\r\n\r\nThere are 2 kinds of tools available.\r\n\r\n### [1. TS DECLARATIONS](#1-ts-declarations-doc)\r\n\r\nDeclarations for common attributes:\r\n- Tag based (and tagless) [attributes](#11-attribute-declarations) for [HTML](#111-html-examples) and [SVG](#112-svg-examples) (combined as [DOM](#113-dom-examples-and-tag-unions)) in native and camelCase naming.\r\n    * The attributes include global listeners and ARIA attributes (which are also exported separately).\r\n- The [global listeners and ARIA attributes](#12-global-listeners-and-aria-attributes) (also in HTML/SVG attributes) in native and camelCase naming.\r\n- [CSS properties](#13-css-properties) as an interface with camelCase keys. For example: `{ \"backgroundColor\": \"#000\" }`\r\n    * Some properties support `number` or `0` values, eg. `{ width: 50, opacity: .5, inset: 0 }`.\r\n    * All properties have typing suggestions while always allowing any string value.\r\n    * In addition a bunch of value names for some common CSS features, eg. `CSSBorderStyle`.\r\n- All attribute related types (except CSSProperties) are available in two spellings: native and camelCase.\r\n    * To choose the spelling use sub module \"/native\" or \"/camelCase\" in the import: eg. `import { HTMLAttributes } from \"dom-types/camelCase\";`\r\n    * Typically a rendering engine imports (JS and TS) utilities from \"dom-types\", while specific end user projects use \"/native\" or \"/camelCase\".\r\n\r\n### [2. JS TOOLS](#2-js-tools-doc)\r\n\r\nA couple of (mutable) constants for enforcing the naming:\r\n- [`domSkipAttributes`](#library---constant-domskipattributes): Always skips these attributes (eg. \"innerHTML\" and such).\r\n- [`domRenamedAttributes`](#library---constant-domrenamedattributes): To convert from clean camelCase to native attributes.\r\n- [`domListenerProps`](#library---constant-domlistenerprops): The known listener properties - applied as event listeners.\r\n\r\nA couple of helper methods for reading and applying the type suggested features into/from DOM:\r\n- [`readDOMString`](#library---method-readdomstringtag-domprops-childrencontent-readfromnode)`(tag, domProps?, childrenContent?, readFromNode?): string`\r\n- [`readDOMProps`](#library---method-readdompropsnode)`(node): DOMCleanProps`\r\n- [`cleanDOMProps`](#library---method-cleandompropsuncleanprops)`(uncleanProps): DOMCleanProps`\r\n- [`equalDOMProps`](#library---method-equaldompropsadomprops-bdomprops)`(aDomProps, bDomProps): boolean`\r\n- [`applyDOMProps`](#library---method-applydompropselement-newprops-oldprops---logwarnings--true)`(element, newProps, oldProps = {}, logWarnings = true): DOMDiffProps | null`\r\n\r\nOther general DOM helpers:\r\n- [`createDOMElement`](#library---method-createdomelementtag-checksvgbyparentnode-namespaceuri)`(tag, checkSVGByParentNode?, namespaceURI?): HTMLElement | SVGElement`\r\n- [`isNodeSVG`](#library---method-isnodesvgnode-node--null--undefined-boolean)`(node): boolean`\r\n- [`classNames`](#library---method-classnamesstrlikes)`(...namesStrArrOrDictionary)`\r\n- [`cleanNames`](#library---method-cleannamesstrlikes)`(...namesStrArrOrDictionary)`\r\n- [`parseDOMStyle`](#library---method-parsedomstylestr)`(styleString): CSSProperties`\r\n\r\nCore methods behind the scenes:\r\n- [`getDictionaryDiffs`](#library---method-getdictionarydiffsorig-updated)`(orig, updated): Record<string, any>`\r\n- [`equalSubDictionaries`](#library---method-equalsubdictionariesa-b-props)`(a, b, ...props): boolean`\r\n- [`getNameDiffs`](#library---method-getnamediffsorigname-newname-splitter---)`(origName, newName, splitter = \" \"): Record<string, boolean> | null`\r\n- [`collectKeysTo`](#library---method-collectkeystorecord-keylikes-keysplitter--)`(record, keyLikes, keySplitter = \"\"): Record<string, true>`\r\n- [`lowerCaseStr`](#library---method-lowercasestrstr-delimiter---)`(str, delimiter = \"-\"): string`\r\n- [`camelCaseStr`](#library---method-camelcasestrstr-splitter---)`(str, splitter = \"-\"): string`\r\n\r\n---\r\n\r\n## 1. TS DECLARATIONS (doc)\r\n\r\n---\r\n\r\n### 1.1. Attribute declarations\r\n\r\n- There are 3 similar collections: HTML, SVG and DOM (combining HTML & SVG).\r\n- The collections follow similar typing and naming to each other. For example:\r\n    * `HTMLAttributes<Tag extends string, Fallback = HTMLAttributesAny>`: Only HTML attributes.\r\n    * `SVGAttributes<Tag extends string, Fallback = SVGAttributesAny>`: Only SVG attributes.\r\n    * `DOMAttributes<Tag extends string, Fallback = DOMAttributesAny>`: Combines HTML & SVG.\r\n- The available forms are the following, shown with DOM prefix here:\r\n    * `DOMAttributes<Tag extends string, Fallback = DOMAttributesAny>`: Tag based attributes.\r\n    * `DOMAttributesAny`: All possible attributes for any tag. As it's DOM, includes HTML and SVG.\r\n    * `DOMAttributesBy: Record<Tag, DOMAttributes<Tag>>`: Tag based attributes as a dictionary.\r\n- All attributes are available in two alternative spellings: in native and camelCase case.\r\n    * Specify which you want to use by referring to the sub module under \"dom-types\" upon importing.\r\n        - For example, `import { HTMLAttributes } from \"dom-types/native\"`, or `from \"dom-types/camelCase\"`.\r\n    * The default main module has both available but with `_native` and `_camelCase` suffix.\r\n        - For example, `import { HTMLAttributes_native, SVGAttributes_camelCase } from \"dom-types\"`.\r\n    * The sub modules \"core\", \"native\" and \"camelCase\" only refer to typing - they have no JS side.\r\n    * Conversely, the \"js\" sub module only has typing for the JS methods and the JS side (for \"import\" and \"require\").\r\n\r\n#### 1.1.1. HTML examples\r\n\r\n```typescript\r\n\r\n// Imports - using camelCase.\r\nimport { HTMLAttributes, HTMLAttributesBy, HTMLAttributesAny } from \"dom-types/camelCase\";\r\n\r\n// HTMLAttributes for \"div\" in camelCase.\r\ntype MyDiv = HTMLAttributes<\"div\">;\r\ntype MyDivTests = [\r\n    // Correct.\r\n    MyDiv[\"onClick\"],   // ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null | undefined\r\n    MyDiv[\"style\"],     // string | CSSProperties | undefined\r\n    MyDiv[\"class\"],     // string | undefined\r\n    MyDiv[\"className\"], // string | undefined\r\n    // Failures.\r\n    MyDiv[\"onclick\"],   // Type guard says it doesn't exist - as intended.\r\n    MyDiv[\"disabled\"],  // Type guard says it doesn't exist - as intended.\r\n];\r\n\r\n// HTMLAttributes for any element in camelCase.\r\ntype MyAny = HTMLAttributesAny;\r\ntype MyAnyTests = [\r\n    // Correct.\r\n    MyInput[\"disabled\"],    // BoolOrStr | undefined  =  \"true\" | \"false\" | boolean | string & {} | undefined\r\n    // Failures.\r\n    MyAny[\"onclick\"],       // Type guard says it doesn't exist - as intended.\r\n];\r\n\r\n// HTMLAttributes for \"input\" in camelCase.\r\ntype MyInput = HTMLAttributesBy[\"input\"]; // Let's test the dictionary form.\r\ntype MyInputTests = [\r\n    // Correct.\r\n    MyInput[\"onFocus\"],     // ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null | undefined\r\n    MyInput[\"style\"],       // string | CSSProperties | undefined\r\n    MyInput[\"disabled\"],    // BoolOrStr | undefined  =  \"true\" | \"false\" | boolean | string & {} | undefined\r\n    MyInput[\"virtualKeyboardPolicy\"], // AnyString | \"auto\" | \"manual\" | undefined\r\n];\r\n\r\n```\r\n\r\n\r\n```typescript\r\n\r\n// Imports - using native.\r\nimport { HTMLAttributes } from \"dom-types/native\";\r\n\r\n// HTMLAttributes for \"input\" in native case.\r\ntype MyInput = HTMLAttributes<\"input\">;\r\ntype MyInputTests = [\r\n    // Correct.\r\n    MyInput[\"onfocus\"],  // ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null | undefined\r\n    MyInput[\"class\"],    // string | undefined\r\n    MyInput[\"style\"],    // string | CSSProperties | undefined\r\n    MyInput[\"disabled\"], // BoolOrStr | undefined  =  \"true\" | \"false\" | boolean | string & {} | undefined\r\n    MyInput[\"virtualkeyboardpolicy\"], // AnyString | \"auto\" | \"manual\" | undefined\r\n    // Failures.\r\n    MyInput[\"className\"], // string | undefined\r\n];\r\n\r\n```\r\n\r\n#### 1.1.2. SVG examples\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { SVGAttributes_camelCase, SVGAttributesBy_native } from \"dom-types\";\r\n\r\n// SVGAttributes for \"circle\" in camelCase.\r\ntype MyCircle = SVGAttributes_camelCase<\"circle\">;\r\ntype MyCircleTests = [\r\n    // Correct.\r\n    MyCircle[\"onFocus\"],    // ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null | undefined\r\n    MyCircle[\"style\"],      // string | CSSProperties | undefined\r\n    MyCircle[\"fill\"],       // CSSColorNames | undefined\r\n    MyCircle[\"strokeDashArray\"], // string | number | number[] | undefined\r\n    MyCircle[\"ariaBrailleRoleDescription\"], // string | null | undefined\r\n];\r\n\r\n// SVGAttributes for \"circle\" in native case.\r\ntype MyCircle_native = SVGAttributesBy_native[\"circle\"]; // Let's test the dictionary form.\r\ntype MyCircleTests_native = [\r\n    // Correct.\r\n    MyCircle_native[\"onfocus\"], // ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null | undefined\r\n    MyCircle_native[\"style\"],   // string | CSSProperties | undefined\r\n    MyCircle_native[\"fill\"],    // CSSColorNames | undefined\r\n    MyCircle_native[\"stroke-dasharray\"], // string | number | number[] | undefined\r\n    MyCircle_native[\"aria-brailleroledescription\"], // string | null | undefined\r\n];\r\n\r\n```\r\n\r\n#### 1.1.3. DOM examples and tag unions\r\n\r\n- You can also define a union of tags for `DOMAttributes<Tag>` (or HTML or SVG).\r\n    * However, it's typically not recommended, and just easier to use the Any type variant.\r\n- In any case, it works except for one caveat specifically for the DOM type:\r\n    * If you mix HTML and SVG the result will just be the intersection of the Any type for both sides.\r\n    * For example: `DOMAttributes<\"div\" | \"svg\">` results in `HTMLAttributesAny & SVGAttributesAny`.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { DOMAttributes, SVGAttributes } from \"dom-types/camelCase\";\r\n\r\n// DOMAttributes.\r\ntype MySVGPathOrSVG = DOMAttributes<\"path\" | \"svg\">;    // Same as `SVGAttributes<\"path\" | \"svg\">`\r\ntype MySVGPathAndSVG = DOMAttributes<\"path\"> & SVGAttributes<\"svg\">; // Manual intersection.\r\ntype MySVGPathOrGroup = DOMAttributes<\"path\" | \"group\">;// Same as `SVGAttributes<\"path\" | \"group\">`\r\ntype MyHTMLDivOrSpan = DOMAttributes<\"div\" | \"span\">;   // Same as `HTMLAttributes<\"div\" | \"span\">`\r\ntype MyMixHTMLAndSVG = DOMAttributes<\"div\" | \"svg\">;    // Same as `HTMLAttributesAny & SVGAttributesAny`\r\ntype MySpecialAnchor = DOMAttributes<\"a\">;              // Same as `HTMLAttributes<\"a\"> & SVGAttributes<\"a\">`\r\n\r\n// Test.\r\ntype MyTests = [\r\n    // SVG presentational attributes.\r\n    MySVGPathOrSVG[\"fill\"],     // Error - not found since it's a union, not intersection.\r\n    MySVGPathAndSVG[\"fill\"],    // Found, since it's an intersection.\r\n    MySVGPathOrGroup[\"fill\"],   // Found, since present in both.\r\n    // HTML basics.\r\n    MyHTMLDivOrSpan[\"tabIndex\"], // Found. Actually \"div\" and \"span\" have nothing special.\r\n    MyHTMLDivOrSpan[\"disabled\"], // Error - not found, since \"disabled\" is for \"input\", \"button\" and such.\r\n    // Mixed outcome.\r\n    MyMixHTMLAndSVG[\"strokeDashArray\"],  // Anything for HTML or SVG is allowed. Tags won't matter.\r\n    MyMixHTMLAndSVG[\"disabled\"],         // Like said, all possible allowed.\r\n    // Special case - tag belonging to both sides.\r\n    MySpecialAnchor[\"href\"],        // Found, correct.\r\n    MySpecialAnchor[\"disabled\"],    // Error - not found, correct.\r\n    MySpecialAnchor[\"xmlBase\"],     // Found, because found on the SVG side attributes.\r\n];\r\n\r\n```\r\n\r\n---\r\n\r\n### 1.2. Global listeners and ARIA attributes\r\n\r\n- The global listeners and ARIA attributes are part of the attributes, but can be used separately.\r\n- Accordingly, the global listeners are provided as native and camelCase interfaces:\r\n    * `GlobalListeners` and `GlobalListeners_native`.\r\n    * For example: `GlobalListeners[\"onAbort\"]`, or `GlobalListeners_native[\"onclick\"]`.\r\n- And likewise, the ARIA attributes are provided as native and camelCase interfaces:\r\n    * `ARIAAttributes` (copied from global `ARIAMixin`) and `ARIAAttributes_native`.\r\n    * In addition, there's a string type for `ARIARole`.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport {\r\n    GlobalListeners_camelCase, ARIAAttributes_camelCase,\r\n    GlobalListeners_native, ARIAAttributes_native\r\n} from \"dom-types\";\r\n\r\n// Just showcasing.\r\ntype MyTests = [\r\n    // Correctly found.\r\n    GlobalListeners_camelCase[\"onClick\"],\r\n    GlobalListeners_native[\"onclick\"],\r\n    ARIAAttributes_camelCase[\"ariaAtomic\"],\r\n    ARIAAttributes_camelCase[\"ariaValueMin\"],\r\n    ARIAAttributes_native[\"aria-atomic\"],\r\n    ARIAAttributes_native[\"aria-valuemin\"],\r\n    // Failures - mismatching cases.\r\n    GlobalListeners_camelCase[\"onclick\"],\r\n    GlobalListeners_native[\"onClick\"],\r\n    ARIAAttributes_camelCase[\"aria-atomic\"],\r\n    ARIAAttributes_camelCase[\"aria-valuemin\"],\r\n    ARIAAttributes_native[\"ariaAtomic\"],\r\n    ARIAAttributes_native[\"ariaValueMin\"],\r\n];\r\n\r\n```\r\n\r\n---\r\n\r\n### 1.3. CSS properties\r\n\r\n- The CSS properties are available as a camelCase interface, for example: `{ \"backgroundColor\": \"#000\" }`\r\n- This form works nicely with the Element's style, eg. `el.style[\"backgroundColor\"] = \"#000\"`.\r\n    * Actually both are supported by browsers through style: `el.style[\"background-color\"] = \"#000\"`.\r\n* All values are `string`, though some support `number` or just `0` reflecting what the major browsers support.\r\n    - For example: `{ width: 50 }` -> `el.style.width = 50` -> `50` = `50px`.\r\n    - Supporting only `0` is relatively common, eg. `{ inset: 0, gap: 0, margin: 0 }`.\r\n        * Basically, all properties that could take number with a unit support 0, eg. `{ margin: \"5px\" }`.\r\n\r\n```typescript\r\n\r\n// Imports - same for \"dom-types/core\", \"/native\" and \"/camelCase\".\r\nimport { CSSProperties, CSSColorNamesAll, CSSBorderStyle } from \"dom-types\";\r\n\r\n// All properties have some suggestions.\r\nconst myStyles: CSSProperties = {\r\n    // Some common ones.\r\n    display: \"flex\",\r\n    flexDirection: \"column-reverse\",\r\n    overflow: \"auto\",\r\n    overflowBehavior: \"contain\",\r\n    position: \"sticky\",\r\n    // For colors, type suggests some but allows any string. Suggestions are:\r\n    // \"currentcolor\" | \"currentColor\" | \"transparent\" | \"inherit\" | \"initial\"\r\n    backgroundColor: \"currentColor\",\r\n    color: \"inherit\",\r\n    textEmphasisColor: \"transparent\",\r\n    // Test numeric.\r\n    animationIterationCount: 5,\r\n    flexGrow: .1,\r\n    flexShrink: 100,\r\n    opacity: .5,\r\n    width: 100,\r\n    // Test half-numeric: only supports 0. Any other number fails.\r\n    borderBlockEndWidth: 0,\r\n    columnWidth: 0,\r\n    gap: 0,\r\n    inset: 0,\r\n    left: 0,\r\n    margin: 0,\r\n    maxInlineSize: 0,\r\n    padding: 0,\r\n    rowGap: 0,\r\n};\r\n\r\n// There are more helper types for specific values.\r\nconst myColor: CSSColorNamesAll = \"Black\"; // All (133) color names in PascalCase.\r\nconst myBorderStyle: CSSBorderStyle = \"double\";\r\n\r\n```\r\n\r\n---\r\n\r\n## 2. JS TOOLS (doc)\r\n\r\n---\r\n\r\n### 2.1. DOM constants\r\n- The usage of constants can be overridden on the methods that use them (as the last arguments):\r\n    * For example, `cleanDOMProps`, `applyDOMProps` and `readDOMString` use the constants.\r\n    * An alternative way to customize is by _mutating_ the constants - though it'll affect your whole project.\r\n- The constants are `domSkipAttributes`, `domRenamedAttrs`, and `domListenerProps`.\r\n\r\n#### library - constant: `domSkipAttributes`\r\n\r\n- Simply defines a set of attributes that will be totally ignored from the processing.\r\n- Note that this is used in applyDOMProps and readDOMString as an overrideable default.\r\n\r\n```typescript\r\n\r\nconst domSkipAttributes = {\r\n    innerHTML: true,\r\n    outerHTML: true,\r\n    textContent: true,\r\n    innerText: true,\r\n    outerText: true,\r\n};\r\n\r\n```\r\n\r\n#### library - constant: `domRenamedAttributes`\r\n\r\n- A dictionary with all the renamed attributes as keys and their native name as value.\r\n\r\n```typescript\r\n\r\n// This is just an example, not the whole dictionary.\r\nconst domRenamedAttributes = {\r\n    // HTML side has 2 non-optional ones.\r\n    acceptCharset: \"accept-charset\", // Not optional.\r\n    httpEquiv: \"http-equiv\", // Not optional.\r\n    // The rest are optional.\r\n    autoComplete: \"autocomplete\",\r\n    // ... any some more ... \r\n\r\n    // SVG side native naming is quite diverse and inherits some from HTML.\r\n    // .. The list is relatively long, so here only a couple of examples.\r\n    accentHeight: \"accent-height\",\r\n    referrerPolicy: \"referrerpolicy\", // Used for HTML and SVG.\r\n    xlinkHref: \"xlink:href\",\r\n    xmlBase: \"xml:base\",\r\n    // ... any many more ... \r\n};\r\n\r\n```\r\n\r\n#### library - constant: `domListenerProps`\r\n\r\n- A dictionary whose keys are lowercase listener attributes and values the event names.\r\n- It's used by checking if the attribute (converted to lowercase) is found in the dictionary.\r\n    * Can then use the value for adding / removing a listener: `el.addEventListener(eName, listener)`\r\n\r\n```typescript\r\n\r\n// This is just an example, not the whole dictionary.\r\nconst domListenerAttributes = {\r\n    onabort: \"abort\",\r\n    onclick: \"click\",\r\n    ondblclick: \"dblclick\",\r\n    // ... and many more ...\r\n};\r\n\r\n```\r\n\r\n---\r\n\r\n### 2.2. DOM props helpers\r\n- The helpers are designed to meet practical needs and to enforce the naming convention:\r\n    * `readDOMString(tag, domProps?, childrenContent?, readFromNode?): string`\r\n    * `readDOMProps(node): DOMCleanProps`\r\n    * `cleanDOMProps(uncleanProps): DOMCleanProps`\r\n    * `equalDOMProps(aDomProps, bDomProps): boolean`\r\n    * `applyDOMProps(element, newProps, oldProps = {}, logWarnings = true): DOMDiffProps | null`\r\n\r\n#### library - method: `readDOMString(tag, domProps?, childrenContent?, readFromNode?)`\r\n\r\n- The method converts the tag, domProps and childrenContent to a string.\r\n- Supports reading tag and domProps from the readFromNode. Skips any listener attributes.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { readDOMString } from \"dom-types\";\r\n\r\n// Returns: \"<div style='background-color: #fff'><span>some text</span></div>\"\r\nreadDOMString(\"div\", { style: { backgroundColor: \"#fff\" }, }, \"<span>some text</span>\");\r\n\r\n// Returns: \"<div></div>\", we use `true` as childrenContent to use a closing tag without content.\r\nreadDOMString(\"div\", null, true);\r\n\r\n// Returns: \"<img src='pics/my_image.jpg' class='image' />\"\r\nreadDOMString(\"img\", { className: \"image\", attributes: { src: \"pics/my_image.jpg\" }});\r\n\r\n```\r\n\r\n#### library - method: `readDOMProps(node)`\r\n\r\n- The methods reads the `DOMCleanProps` type information from the given node.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { readDOMProps } from \"dom-types\";\r\n\r\n// Create an element and set it up.\r\nconst input = document.createElement(\"input\");\r\ninput.className = \"my test\";\r\ninput.style.cssText = \"border-color: #000\"; // Might become, say, \"rgb(0,0,0)\"\r\ninput.disabled = true;\r\n\r\n// Read info.\r\nreadDOMProps(input);\r\n// Returns: {\r\n//      className: \"my test\",\r\n//      style: { borderColor: \"rgb(0,0,0)\" }, // Or whatever browser used.\r\n//      attributes: { disabled: \"\" }\r\n// }\r\n\r\n```\r\n\r\n#### library - method: `cleanDOMProps(uncleanProps)`\r\n\r\n- Helps to clean unorganized properties into `DOMCleanProps` form.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { cleanDOMProps } from \"dom-types\";\r\n\r\n// Returns: { className: \"test me\" }\r\ncleanDOMProps({ class: \"test\", className: \"me\" });\r\n\r\n// Returns: { style: { borderColor: \"#fff\" }, attributes: { src: \"pics/my-gif.gif\" } }\r\ncleanDOMProps({ style: \"border-color: #fff\", src: \"pics/my-gif.gif\" });\r\n\r\n// Returns: { listeners: { click: () => {}, abort: (e) => {} } }\r\ncleanDOMProps({ onclick: () => {}, onAbort: (e) => {} });\r\n\r\n// Returns: { attributes: { unknownThing: 5 } }\r\ncleanDOMProps({ unknownThing: 5 });\r\n\r\n\r\n```\r\n\r\n#### library - method: `equalDOMProps(aDomProps, bDomProps)`\r\n\r\n- Compares two sets of DOMCleanProps to determine whether they are equal or not.\r\n    * Technically, they allow some tolerance over the cleaniness, if the structure is in place.\r\n- The method is useful to determine whether needs to apply anything to the DOM or not.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { equalDOMProps } from \"dom-types\";\r\n\r\n// True like tests.\r\nequalDOMProps({ className: undefined }, {}); // true\r\nequalDOMProps(\r\n    { attributes: {}, className: \"\" },\r\n    { listeners: {}, data: {} }\r\n); // true\r\nequalDOMProps(\r\n    { style: { backgroundColor: \"#fff\" } },\r\n    { style: { backgroundColor: \"#fff\" } }\r\n); // true\r\n\r\n// False like tests are more obvious.\r\nequalDOMProps({ className: \"active\" }, {}); // false\r\nequalDOMProps(\r\n    { attributes: {}, className: \"\" },\r\n    { listeners: {}, data: { myName: \"me\" } }\r\n); // false\r\nequalDOMProps(\r\n    { style: { backgroundColor: \"#000\" } },\r\n    { style: { backgroundColor: \"#fff\" } }\r\n); // false\r\n\r\n\r\n```\r\n\r\n#### library - method: `applyDOMProps(element, newProps, oldProps = {}, logWarnings = true)`\r\n\r\n- Apply the cleaned DOM props to an element, optionally comparing against oldProps.\r\n- Returns info for changes (`DOMDiffProps`), or `null` if didn't apply any.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { applyDOMProps, cleanDOMProps, createDOMElement } from \"dom-types\";\r\n\r\n// Create an element and apply props.\r\nconst el = createDOMElement(\"div\");\r\nconst props1 = cleanDOMProps({ style: \"background: #fff\", class: \"test\", className: \"me\" });\r\napplyDOMProps(el, props1); // Applies style and class: <div style=\"background:#fff\" class=\"test me\" />\r\n// .. Returns: { className: { test: true, me: true }, style: { background: \"#fff\" } }\r\n\r\n// Apply another set of props, extending earlier props.\r\nconst props2 = { ...props1, data: { myName: \"me\" } };\r\napplyDOMProps(el, props2, props1); // Applies data: <div ... data-my-name=\"me\" />\r\n// .. Returns: { data: { myName: \"me\" } }\r\n\r\n// Reuse data from props2, but otherwise clean.\r\nconst props3 = { data: props2.data };\r\napplyDOMProps(el, props3, props2); // Removes style and class: <div data-my-name=\"me\" />\r\n// .. Returns: { className: { test: false, me: false }, style: { background: undefined } }\r\n\r\n// Test empty update.\r\napplyDOMProps(el, {...props3}, props3);\r\n// .. Returns: null\r\n\r\n```\r\n\r\n---\r\n\r\n### 2.3. General DOM helpers\r\n- DOM helpers that are independent from the attribute naming.\r\n    * `createDOMElement(tag, checkSVGByParentNode?, namespaceURI?): HTMLElement | SVGElement`\r\n    * `isNodeSVG(node): boolean`\r\n    * `classNames(...namesStrArrOrDictionary): string`\r\n    * `cleanNames(...namesStrArrOrDictionary): string`\r\n    * `parseDOMStyle(styleString): CSSProperties`\r\n\r\n#### library - method: `createDOMElement(tag, checkSVGByParentNode?, namespaceURI?)`\r\n- Create a new HTML or SVG element with tag suggestions.\r\n- Use lowercase tag names to stay consistent all around.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { createDOMElement } from \"dom-types\";\r\n\r\n// By default all are HTML, except \"svg\" tag.\r\nconst div = createDOMElement(\"div\");        // HTMLDivElement\r\nconst input = createDOMElement(\"input\");    // HTMLInputElement\r\nconst funky = createDOMElement(\"funky\");    // HTMLElement - doesn't suggest, but is allowed.\r\nconst a_html = createDOMElement(\"a\");       // HTMLAnchorElement\r\nconst svg = createDOMElement(\"svg\");        // SVGSVGElement - using createElementNS.\r\n\r\n// Use the parent element to _detect_ SVG vs. HTML.\r\nconst group = createDOMElement(\"group\", svg);       // SVGElement\r\nconst circle = createDOMElement(\"circle\", group);   // SVGCircleElement\r\nconst a_svg = createDOMElement(\"a\", group);         // SVGAElement\r\n\r\n// The default namespaceURI for svg is: \"http://www.w3.org/2000/svg\"\r\nconst circle_ALT = createDOMElement(\"circle\", group, \"http://www.w3.org/2000/svg\");\r\n\r\n// Don't create SVG elements without the parent check, unless \"svg\" tag or saying it's SVG.\r\nconst circle_NONO = createDOMElement(\"circle\");         // HTMLElement - in JS, too.\r\nconst circle_OKAY = createDOMElement(\"circle\", true);   // SVGCircleElement - in JS, too.\r\n\r\n// Insert accordingly.\r\ndocument.body.appendChild(div);\r\ndiv.appendChild(input);\r\ndiv.appendChild(funky);\r\ndiv.appendChild(a_html);\r\ndiv.appendChild(svg);\r\nsvg.appendChild(group);\r\ngroup.appendChild(circle);\r\ngroup.appendChild(a_svg);\r\n\r\n```\r\n\r\n#### library - method: `isNodeSVG(node: Node | null | undefined): boolean`\r\n- The same check that is used in createDOMElement using \"ownerSVGElement\" property internally.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { createDOMElement, isNodeSVG } from \"dom-types\";\r\n\r\n// Create some elements.\r\nconst div = createDOMElement(\"div\");\r\nconst svg = createDOMElement(\"svg\");\r\nconst group = createDOMElement(\"group\", svg);\r\nconst circle = createDOMElement(\"circle\", group);\r\nconst a_svg = createDOMElement(\"a\", group); // On the SVG side.\r\nconst a_html = createDOMElement(\"a\", div);  // On the HTML side.\r\n\r\n// Test for SVG.\r\nisNodeSVG(div);     // false\r\nisNodeSVG(svg);     // true\r\nisNodeSVG(group);   // true\r\nisNodeSVG(circle);  // true\r\nisNodeSVG(a_svg);   // true\r\nisNodeSVG(a_html);  // false\r\n\r\n```\r\n\r\n#### library - method: `classNames(...strLikes)`\r\n- Simply concats non-false like strings from string, array or dictionary input.\r\n- Does not remove any duplicates - to do that use `cleanNames` instead.\r\n- Note. For optimal nested processing (eg. in a component structure), it's recommended to use `classNames` on the extending layers and `cleanNames` only at the last step.\r\n\r\n```typescript\r\n\r\n// - Imports - //\r\n\r\nimport { classNames, ValidateNames } from \"dom-types\";\r\n\r\n\r\n// - Basic usage - //\r\n\r\n// Simply concats the strings with \" \" as the joiner.\r\nclassNames(true && \"a\", 1 && \"b\", false && \"c\", null, undefined, 0); // \"a b\"\r\nclassNames(\"a b\", [\"c\", \"d e\", false], {\"f g\": true, h: false});     // \"a b c d e f g\"\r\n\r\n// But doesn't remove duplicates.\r\nclassNames(\"a b\", \"a\", [\"a\", \"b\"], {a: false, \"a b\": true});         // \"a b a a b a b\"\r\n\r\n\r\n// - Simple usage with typing - //\r\n\r\n// Let's define our valid names.\r\ntype Names = \"a\" | \"b\";\r\n\r\n// Just try \"a\" and \"b\" separately.\r\nclassNames<Names>(\"a\", \"b\", [\"b\", \"a\"], new Set([\"b\"]), { a: true }); // \"a b b a b a\"\r\nclassNames<Names>(\"a\", \"b\", [\"b\", \"a\"], { a: true }, \"c\"); // Type guards against \"c\"\r\nclassNames<Names>(\"a\", \"a b\", \"b\", [\"a b\"]); // Type guards against \"a b\".\r\n// Let's allow any string, but still use suggestions.\r\nclassNames<Names | string & {}>(\"a\", \"a b\", [\"a b\"], \"c\"); // \"a a b a b c\", won't suggest \"c\" but allows it.\r\n// We could also use this pattern for some very specific cases - though, get type heavy quickly.\r\nclassNames<Names | `${Names} ${Names}`>(\"a\", \"a b\", [\"a b\"]); // \"a a b a b\", would not allow \"a b b\"\r\n\r\n\r\n// - For full concatenated validation use ValidateNames type - //\r\n\r\n// Prepare.\r\nconst validNames = classNames as ValidateNames<Names>;\r\n\r\n// Do tests. All below output \"a b\".\r\n// .. These should not produce errors in typing.\r\nvalidNames([\"a\"], { b: true });\r\nvalidNames([\"a\", \"b\", \"\"]);\r\nvalidNames([\"a\", \"b\", \"a b\", \"b a\"], new Set([\"a\", \"b a\"]));\r\nvalidNames([\"a\", false, undefined, \"b\"]);\r\nvalidNames([\"a\", false, undefined, \"b\"] as const);\r\nvalidNames({\"a\": true, \"b a\": false});\r\nvalidNames({\"a\": true, \"b a\": false} as const);\r\nvalidNames(\"a\", \"a b\", false, [\"a\"], [\"b a\", \"\"], undefined, { \"a\": true, \"b a\": false });\r\n// .. These should fail each in typing, since \"FAIL\" is not part of ValidNames.\r\nvalidNames(\"FAIL\");\r\nvalidNames([\"FAIL\"]);\r\nvalidNames({\"FAIL\": false});\r\nvalidNames(new Set([\"a\", \"b\", \"FAIL\"]));\r\nvalidNames(\"a\", \"a b\", undefined, \"FAIL\", [\"a\", false]);\r\nvalidNames(\"a\", \"a b\", undefined, [\"a\", \"FAIL\", false]);\r\nvalidNames([\"a\", \"b\", \"a b\", \"FAIL\", false]);\r\nvalidNames(\"a\", \"a b\", false, [\"a\"], [\"b a\", \"\"], undefined, {\"a\": true, \"FAIL\": true, \"b a\": false});\r\nvalidNames(\"a\", \"FAIL\", \"a b\", false, [\"a\"], [\"b a\", \"\"], undefined, {\"a\": true, \"b a\": false});\r\nvalidNames(\"a\", \"a b\", false, [\"a\", \"FAIL\"], [\"b a\", \"\"], undefined, {\"a\": true, \"b a\": false});\r\n\r\n```\r\n\r\n#### library - method: `cleanNames(...strLikes)`\r\n- Like `classNames` but removes any duplicates in the outcome.\r\n- Typically used right before applying to DOM (to keep DOM clean).\r\n\r\n```typescript\r\n\r\n// - Imports - //\r\n\r\nimport { cleanNames, ValidateNames } from \"dom-types\";\r\n\r\n\r\n// - Basic JS usage - //\r\n\r\n// Numeric and false-like are cut off (\"\", false, null, undefined).\r\ncleanNames(\"a\", \"b\", 0, undefined, [false, \"c\"], { d: true }); // \"a b c d\"\r\n// Each string is splitted by \" \" and collected to a record, so duplicates are dropped easily.\r\ncleanNames(\"a b\", \"b\", \"b b a a\", [\"b\"], new Set([\"b\"]), { a: true }); // \"a b\"\r\n// Simulate some validation.\r\ncleanNames(\"a\", 1 && \"b\", [\"b\", 0 && \"c\"], { \"b d\": true, \"e\": null }); // \"a b d\"\r\n// If you input numbers other than 0, they are type guarded - guard stops at first fail.\r\ncleanNames(0, 1, -1); // 1 nor -1 won't be allowed here.\r\n\r\n\r\n// - Simple usage with typing - //\r\n\r\n// Let's define our valid names.\r\ntype Names = \"a\" | \"b\";\r\n\r\n// Just try \"a\" and \"b\" separately.\r\ncleanNames<Names>(\"a\", \"b\", [\"b\", \"a\"], { a: true }); // \"a b\"\r\ncleanNames<Names>(\"a\", \"b\", [\"b\", \"a\"], new Set([\"b\"]), { a: true }, \"c\"); // Type guards against \"c\"\r\ncleanNames<Names>(\"a\", \"a b\", \"b\", [\"a b\"]); // Type guards against \"a b\".\r\n// Let's allow any string, but still use suggestions.\r\ncleanNames<Names | string & {}>(\"a\", \"a b\", [\"a b\"], \"c\"); // \"a b c\", won't suggest \"c\" but allows it.\r\n// We could also use this pattern for some very specific cases - though, get type heavy quickly.\r\ncleanNames<Names | `${Names} ${Names}`>(\"a\", \"a b\", [\"a b\"]); // \"a b\", would not allow \"a b b\"\r\n\r\n\r\n// - For full concatenated validation use ValidateNames type - //\r\n\r\n// Prepare.\r\nconst validNames = cleanNames as ValidateNames<Names>;\r\n\r\n// Do tests. All below output \"a b\".\r\n// .. These should not produce errors in typing.\r\nvalidNames([\"a\"], { b: true });\r\nvalidNames([\"a\", \"b\", \"\"]);\r\nvalidNames([\"a\", \"b\", \"a b\", \"b a\"], new Set([\"a\", \"b a\"]));\r\nvalidNames([\"a\", false, undefined, \"b\"]);\r\nvalidNames([\"a\", false, undefined, \"b\"] as const);\r\nvalidNames({\"a\": true, \"b a\": false});\r\nvalidNames({\"a\": true, \"b a\": false} as const);\r\nvalidNames(\"a\", \"a b\", false, [\"a\"], [\"b a\", \"\"], undefined, { \"a\": true, \"b a\": false });\r\n// .. These should fail each in typing, since \"FAIL\" is not part of ValidNames.\r\nvalidNames(\"FAIL\");\r\nvalidNames([\"FAIL\"]);\r\nvalidNames({\"FAIL\": false});\r\nvalidNames(new Set([\"a\", \"b\", \"FAIL\"]));\r\nvalidNames(\"a\", \"a b\", undefined, \"FAIL\", [\"a\", false]);\r\nvalidNames(\"a\", \"a b\", undefined, [\"a\", \"FAIL\", false]);\r\nvalidNames([\"a\", \"b\", \"a b\", \"FAIL\", false]);\r\nvalidNames(\"a\", \"a b\", false, [\"a\"], [\"b a\", \"\"], undefined, {\"a\": true, \"FAIL\": true, \"b a\": false});\r\nvalidNames(\"a\", \"FAIL\", \"a b\", false, [\"a\"], [\"b a\", \"\"], undefined, {\"a\": true, \"b a\": false});\r\nvalidNames(\"a\", \"a b\", false, [\"a\", \"FAIL\"], [\"b a\", \"\"], undefined, {\"a\": true, \"b a\": false});\r\n\r\n```\r\n\r\n#### library - method: `parseDOMStyle(str)`\r\n- Split the style string and return a dictionary with camelCase keys with respective values.\r\n- Note that both keys are compatible with `element.style[styleProp] = value` usage.\r\n    * For example, `element.style[\"border-color\"] = \"#000\"` and `element.style[\"borderColor\"] = \"#000\"` work the same.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { parseDOMStyle } from \"dom-types\";\r\n\r\n// Returns: { borderColor: \"#ccc\", width: \"50px\" }\r\nparseDOMStyle(\"border-color: #ccc; width: 50px\");\r\n\r\n// Returns: { backgroundImage: \"url('https://somedomain.com/some_picture.jpg')\" }\r\nparseDOMStyle(\"background-image: url('https://somedomain.com/some_picture.jpg')\");\r\n\r\n// Let's test parsing and applying to DOM.\r\nconst cssProps = parseDOMStyle(\"background-color: #000; font-size: 12px;\");\r\nconst el = document.createElement(\"div\");\r\nfor (const prop in cssProps)\r\n    el.style[prop] = cssProps[prop];\r\n\r\n// Test results.\r\nel                          // <div style=\"background-color: #000; font-size: 12px\" />\r\nel.style.backgroundColor    // \"rgb(0, 0, 0)\" or \"#000000\" or such.\r\nel.style.fontSize           // \"12px\"\r\nel.style[\"font-size\"]       // \"12px\"\r\n\r\n```\r\n\r\n---\r\n\r\n### 2.4. Core methods\r\n- `getDictionaryDiffs(orig, updated): Record<string, any>`\r\n- `equalSubDictionaries(a, b, ...props): boolean`\r\n- `getNameDiffs(origName, newName, splitter = \" \"): Record<string, boolean> | null`\r\n- `collectKeysTo(record, keyLikes, keySplitter = \"\"): Record<string, true>`\r\n- `lowerCaseStr(str, delimiter = \"-\"): string`\r\n- `camelCaseStr(str, splitter = \"-\"): string`\r\n\r\n#### library - method: `getDictionaryDiffs(orig, updated)`\r\n- If unchanged, returns null, otherwise a new dictionary with keys only for changed, and value of each the new value or `undefined` if removed.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { getDictionaryDiffs } from \"dom-types\";\r\n\r\n// Returns: { a: true, c: undefined }\r\nconst myDiffs1 = getDictionaryDiffs({ a: true, b: \"test\", c: 0 }, { a: false, b: \"test\" });\r\n\r\n// Returns: null.\r\nconst myDiffs2 = getDictionaryDiffs({ a: true }, { a: true });\r\n\r\n// Note that for the purposes of getDictionaryDiffs, `undefined` is the same as not found.\r\nconst myDiffs3 = getDictionaryDiffs({ a: undefined }, {}); // Returns null.\r\n\r\n```\r\n\r\n#### library - method: `equalSubDictionaries(a, b, ...props)`\r\n- Checks if sub dictionaries in both `a` and `b` are equal in shallow comparison for given properties.\r\n- In case one has an empty dictionary and the other nothing (or false-like), then regards them as equal.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { equalSubDictionaries } from \"dom-types\";\r\n\r\n// Prepare type.\r\ninterface MyObj {\r\n    set1?: { test: boolean; deep: Record<string, any>; };\r\n    set2?: { test: boolean; deep: Record<string, any>; };\r\n    other?: { more?: boolean; }\r\n    another?: Record<string, any>;\r\n}\r\n// Prepare two somewhat similar objects.\r\nconst a: MyObj = {\r\n    set1: { test: true, deep: { state: true } },\r\n    set2: { test: true, deep: { state: true } },\r\n    other: {}\r\n};\r\nconst b: MyObj = {\r\n    set1: { test: true, deep: { state: true } },\r\n    set2: { test: true, deep: a.set2!.deep },\r\n    other: { more: true },\r\n    another: {}\r\n};\r\n\r\n// Test.\r\nequalSubDictionaries(a, b, \"set1\");         // false, a.set1.deep !== a.set2.deep\r\nequalSubDictionaries(a, b, \"set2\");         // true\r\nequalSubDictionaries(a, b, \"other\");        // false, b.other.more !== a.other.more.\r\nequalSubDictionaries(a, b, \"another\");      // true, b.another is just {}, so undefined is fine.\r\nequalSubDictionaries(a, b, \"set1\", \"set2\"); // false, because \"set1\" returns false.\r\n\r\n```\r\n\r\n#### library - method: `getNameDiffs(origName, newName, splitter = \" \")`\r\n\r\n- Get name diffs as: `Record<string, boolean>`, where `true` means added and `false` removed.\r\n- The names are splitted from each given string using the splitter - defaults to \" \".\r\n    * If no splitter is defined, won't split them, but just compare the two words as such.\r\n- The method ignores order: just checks whether the names are present or not. Skips empty \"\".\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { getNameDiffs } from \"dom-types\";\r\n\r\n// Common usage with \" \" as splitter.\r\ngetNameDiffs(\"\", \"a\")           // { a: true }\r\ngetNameDiffs(\"a\", \"\")           // { a: false }\r\ngetNameDiffs(\"a b\", \"a b c\")    // { c: true }\r\ngetNameDiffs(\"a b c\", \"a b\")    // { c: false }\r\ngetNameDiffs(\"c b a a a\", \"a b b   b c e\"); // { e: true }\r\n\r\n// Testing other splitters.\r\ngetNameDiffs(\"a.b\", \"a.b.c\", \".\")   // { c: true }\r\ngetNameDiffs(\"a.b\", \"a.b c\", \".\")   // { \"b c\": true }\r\ngetNameDiffs(\"a\", \"b c\", \"\");       // { a: false, \"b c\": true }\r\n\r\n```\r\n\r\n#### library - method: `collectKeysTo(record, keyLikes, keySplitter = \"\")`\r\n- Helper to collect found keys from a string, array or dictionary, optionally using a string splitter.\r\n- This is the core method for the `cleanNames` method.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { collectKeysTo } from \"dom-types\";\r\n\r\n// Prepare a collection.\r\nconst collection: Record<string, true> = {};\r\n\r\n// Let's use it with a string splitter for \" \".\r\nconst splitter = \" \";\r\ncollectKeysTo(collection, \"a b\", splitter);     // Adds { a: true, b: true }\r\ncollectKeysTo(collection, [\"b c\"], splitter);   // Adds { c: true }\r\ncollectKeysTo(collection, {                     // Adds { d: true, e: true }\r\n    \"c d\": true,\r\n    e: true,\r\n    f: false\r\n}, splitter);\r\n// .. Testing empty. Won't add anything - regardless of what the keySplitter is.\r\ncollectKeysTo(collection, \"\");\r\ncollectKeysTo(collection, [\"\"], \" \");\r\ncollectKeysTo(collection, {\"\": true}, \"\");\r\n\r\n// Test the claims.\r\ncollection;  // { a: true, b: true, c: true, d: true, e: true }\r\n\r\n```\r\n\r\n#### library - method: `lowerCaseStr(str, delimiter = \"-\")`\r\n- Helper to convert a camelCase or PascalCase string to dash-case (by default).\r\n- This method is useful with the native Element's `dataset` feature.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { lowerCaseStr } from \"dom-types\";\r\n\r\n// Simple tests.\r\nlowerCaseStr(\"myLife\");     // \"my-life\"\r\nlowerCaseStr(\"MyLife\");     // \"-my-life\"\r\nlowerCaseStr(\"TEST\");       // \"-t-e-s-t\"\r\nlowerCaseStr(\"TEST\", \"_\");  // \"_t_e_s_t\"\r\nlowerCaseStr(\"TEST\", \"\");   // \"test\"\r\n\r\n```\r\n\r\n#### library - method: `camelCaseStr(str, splitter = \"-\")`\r\n- Helper to convert a dash-case (by default) to a camelCase or PascalCase string.\r\n- This method is useful with the native Element's `dataset` feature.\r\n\r\n```typescript\r\n\r\n// Imports.\r\nimport { camelCaseStr } from \"dom-types\";\r\n\r\n// Simple tests.\r\ncamelCaseStr(\"my-life\");        // \"myLife\"\r\ncamelCaseStr(\"-my-life\");       // \"MyLife\"\r\ncamelCaseStr(\"-t-e-s-t\");       // \"TEST\"\r\ncamelCaseStr(\"---what---\");     // \"What\"\r\ncamelCaseStr(\"__what__\", \"_\");  // \"What\"\r\n\r\n```\r\n\r\n---\r\n\r\n[Back to top](#what)\r\n","readmeFilename":"README.md"}