@remote-dom/polyfill 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +51 -3
  3. package/build/cjs/Attr.cjs +33 -4
  4. package/build/cjs/CharacterData.cjs +10 -0
  5. package/build/cjs/ChildNode.cjs +79 -13
  6. package/build/cjs/Comment.cjs +1 -1
  7. package/build/cjs/CustomElementRegistry.cjs +27 -2
  8. package/build/cjs/CustomEvent.cjs +2 -2
  9. package/build/cjs/Document.cjs +56 -32
  10. package/build/cjs/DocumentFragment.cjs +6 -2
  11. package/build/cjs/Element.cjs +68 -8
  12. package/build/cjs/Event.cjs +29 -27
  13. package/build/cjs/EventTarget.cjs +128 -58
  14. package/build/cjs/HTMLTemplateElement.cjs +1 -0
  15. package/build/cjs/MutationObserver.cjs +167 -1
  16. package/build/cjs/NamedNodeMap.cjs +82 -45
  17. package/build/cjs/Node.cjs +47 -11
  18. package/build/cjs/NodeList.cjs +9 -1
  19. package/build/cjs/ParentNode.cjs +319 -77
  20. package/build/cjs/SVGElement.cjs +1 -1
  21. package/build/cjs/Text.cjs +1 -1
  22. package/build/cjs/Window.cjs +131 -38
  23. package/build/cjs/attribute-reactions.cjs +20 -0
  24. package/build/cjs/constants.cjs +48 -24
  25. package/build/cjs/custom-element-reactions.cjs +52 -0
  26. package/build/cjs/dom-exception.cjs +15 -0
  27. package/build/cjs/hook-effects.cjs +51 -0
  28. package/build/cjs/names.cjs +61 -0
  29. package/build/cjs/selectors.cjs +213 -122
  30. package/build/cjs/serialization.cjs +168 -65
  31. package/build/cjs/shared.cjs +177 -28
  32. package/build/esm/Attr.mjs +34 -5
  33. package/build/esm/CharacterData.mjs +10 -0
  34. package/build/esm/ChildNode.mjs +76 -15
  35. package/build/esm/Comment.mjs +2 -2
  36. package/build/esm/CustomElementRegistry.mjs +27 -2
  37. package/build/esm/CustomEvent.mjs +2 -2
  38. package/build/esm/Document.mjs +60 -35
  39. package/build/esm/DocumentFragment.mjs +7 -3
  40. package/build/esm/Element.mjs +69 -9
  41. package/build/esm/Event.mjs +27 -27
  42. package/build/esm/EventTarget.mjs +130 -60
  43. package/build/esm/HTMLTemplateElement.mjs +2 -1
  44. package/build/esm/MutationObserver.mjs +166 -2
  45. package/build/esm/NamedNodeMap.mjs +83 -46
  46. package/build/esm/Node.mjs +48 -12
  47. package/build/esm/NodeList.mjs +9 -1
  48. package/build/esm/ParentNode.mjs +322 -81
  49. package/build/esm/SVGElement.mjs +2 -2
  50. package/build/esm/Text.mjs +2 -2
  51. package/build/esm/Window.mjs +132 -39
  52. package/build/esm/attribute-reactions.mjs +16 -0
  53. package/build/esm/constants.mjs +28 -23
  54. package/build/esm/custom-element-reactions.mjs +47 -0
  55. package/build/esm/dom-exception.mjs +11 -0
  56. package/build/esm/hook-effects.mjs +46 -0
  57. package/build/esm/names.mjs +54 -0
  58. package/build/esm/selectors.mjs +202 -124
  59. package/build/esm/serialization.mjs +169 -66
  60. package/build/esm/shared.mjs +173 -30
  61. package/build/esnext/Attr.esnext +45 -5
  62. package/build/esnext/CharacterData.esnext +10 -0
  63. package/build/esnext/ChildNode.esnext +84 -15
  64. package/build/esnext/Comment.esnext +2 -2
  65. package/build/esnext/CustomElementRegistry.esnext +42 -0
  66. package/build/esnext/CustomEvent.esnext +2 -2
  67. package/build/esnext/Document.esnext +69 -32
  68. package/build/esnext/DocumentFragment.esnext +6 -2
  69. package/build/esnext/Element.esnext +78 -9
  70. package/build/esnext/Event.esnext +36 -37
  71. package/build/esnext/EventTarget.esnext +133 -61
  72. package/build/esnext/HTMLTemplateElement.esnext +3 -1
  73. package/build/esnext/MutationObserver.esnext +163 -2
  74. package/build/esnext/NamedNodeMap.esnext +110 -42
  75. package/build/esnext/Node.esnext +50 -12
  76. package/build/esnext/NodeList.esnext +1 -1
  77. package/build/esnext/ParentNode.esnext +352 -79
  78. package/build/esnext/SVGElement.esnext +2 -2
  79. package/build/esnext/Text.esnext +2 -2
  80. package/build/esnext/Window.esnext +120 -16
  81. package/build/esnext/attribute-reactions.esnext +21 -0
  82. package/build/esnext/constants.esnext +34 -21
  83. package/build/esnext/custom-element-reactions.esnext +49 -0
  84. package/build/esnext/dom-exception.esnext +11 -0
  85. package/build/esnext/hook-effects.esnext +46 -0
  86. package/build/esnext/names.esnext +61 -0
  87. package/build/esnext/selectors.esnext +202 -100
  88. package/build/esnext/serialization.esnext +171 -48
  89. package/build/esnext/shared.esnext +181 -34
  90. package/build/tsconfig.tsbuildinfo +1 -1
  91. package/build/typescript/Attr.d.ts +6 -4
  92. package/build/typescript/Attr.d.ts.map +1 -1
  93. package/build/typescript/CharacterData.d.ts.map +1 -1
  94. package/build/typescript/ChildNode.d.ts +5 -0
  95. package/build/typescript/ChildNode.d.ts.map +1 -1
  96. package/build/typescript/ClipboardEvent.d.ts.map +1 -1
  97. package/build/typescript/Comment.d.ts +2 -2
  98. package/build/typescript/Comment.d.ts.map +1 -1
  99. package/build/typescript/CustomElementRegistry.d.ts +1 -0
  100. package/build/typescript/CustomElementRegistry.d.ts.map +1 -1
  101. package/build/typescript/CustomEvent.d.ts.map +1 -1
  102. package/build/typescript/Document.d.ts +10 -5
  103. package/build/typescript/Document.d.ts.map +1 -1
  104. package/build/typescript/DocumentFragment.d.ts +2 -1
  105. package/build/typescript/DocumentFragment.d.ts.map +1 -1
  106. package/build/typescript/Element.d.ts +16 -7
  107. package/build/typescript/Element.d.ts.map +1 -1
  108. package/build/typescript/ErrorEvent.d.ts.map +1 -1
  109. package/build/typescript/Event.d.ts +9 -10
  110. package/build/typescript/Event.d.ts.map +1 -1
  111. package/build/typescript/EventTarget.d.ts +20 -3
  112. package/build/typescript/EventTarget.d.ts.map +1 -1
  113. package/build/typescript/FocusEvent.d.ts.map +1 -1
  114. package/build/typescript/HTMLTemplateElement.d.ts.map +1 -1
  115. package/build/typescript/MutationObserver.d.ts +42 -0
  116. package/build/typescript/MutationObserver.d.ts.map +1 -1
  117. package/build/typescript/NamedNodeMap.d.ts +5 -3
  118. package/build/typescript/NamedNodeMap.d.ts.map +1 -1
  119. package/build/typescript/Node.d.ts +4 -3
  120. package/build/typescript/Node.d.ts.map +1 -1
  121. package/build/typescript/NodeList.d.ts +3 -2
  122. package/build/typescript/NodeList.d.ts.map +1 -1
  123. package/build/typescript/ParentNode.d.ts +29 -10
  124. package/build/typescript/ParentNode.d.ts.map +1 -1
  125. package/build/typescript/PromiseRejectionEvent.d.ts.map +1 -1
  126. package/build/typescript/SVGElement.d.ts +1 -1
  127. package/build/typescript/SVGElement.d.ts.map +1 -1
  128. package/build/typescript/Text.d.ts +1 -1
  129. package/build/typescript/Text.d.ts.map +1 -1
  130. package/build/typescript/ToggleEvent.d.ts.map +1 -1
  131. package/build/typescript/Window.d.ts +5 -1
  132. package/build/typescript/Window.d.ts.map +1 -1
  133. package/build/typescript/attribute-reactions.d.ts +4 -0
  134. package/build/typescript/attribute-reactions.d.ts.map +1 -0
  135. package/build/typescript/constants.d.ts +27 -18
  136. package/build/typescript/constants.d.ts.map +1 -1
  137. package/build/typescript/custom-element-reactions.d.ts +6 -0
  138. package/build/typescript/custom-element-reactions.d.ts.map +1 -0
  139. package/build/typescript/dom-exception.d.ts +2 -0
  140. package/build/typescript/dom-exception.d.ts.map +1 -0
  141. package/build/typescript/extensions.d.ts +4 -0
  142. package/build/typescript/extensions.d.ts.map +1 -0
  143. package/build/typescript/hook-effects.d.ts +6 -0
  144. package/build/typescript/hook-effects.d.ts.map +1 -0
  145. package/build/typescript/hooks.d.ts +3 -3
  146. package/build/typescript/hooks.d.ts.map +1 -1
  147. package/build/typescript/index.d.ts +1 -0
  148. package/build/typescript/index.d.ts.map +1 -1
  149. package/build/typescript/names.d.ts +11 -0
  150. package/build/typescript/names.d.ts.map +1 -0
  151. package/build/typescript/selectors.d.ts +21 -22
  152. package/build/typescript/selectors.d.ts.map +1 -1
  153. package/build/typescript/serialization.d.ts.map +1 -1
  154. package/build/typescript/shared.d.ts +10 -0
  155. package/build/typescript/shared.d.ts.map +1 -1
  156. package/build/typescript/tests/ChildNode.test.d.ts +2 -0
  157. package/build/typescript/tests/ChildNode.test.d.ts.map +1 -0
  158. package/build/typescript/tests/MutationObserver.test.d.ts +2 -0
  159. package/build/typescript/tests/MutationObserver.test.d.ts.map +1 -0
  160. package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts +2 -0
  161. package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts.map +1 -0
  162. package/build/typescript/tests/attr-mutation.test.d.ts +2 -0
  163. package/build/typescript/tests/attr-mutation.test.d.ts.map +1 -0
  164. package/build/typescript/tests/child-node-before-after.test.d.ts +2 -0
  165. package/build/typescript/tests/child-node-before-after.test.d.ts.map +1 -0
  166. package/build/typescript/tests/child-node-replace-with.test.d.ts +2 -0
  167. package/build/typescript/tests/child-node-replace-with.test.d.ts.map +1 -0
  168. package/build/typescript/tests/clone-import-namespaces.test.d.ts +2 -0
  169. package/build/typescript/tests/clone-import-namespaces.test.d.ts.map +1 -0
  170. package/build/typescript/tests/constants.test.d.ts +2 -0
  171. package/build/typescript/tests/constants.test.d.ts.map +1 -0
  172. package/build/typescript/tests/create-element-namespaces.test.d.ts +2 -0
  173. package/build/typescript/tests/create-element-namespaces.test.d.ts.map +1 -0
  174. package/build/typescript/tests/create-text-node.test.d.ts +2 -0
  175. package/build/typescript/tests/create-text-node.test.d.ts.map +1 -0
  176. package/build/typescript/tests/custom-element-reactions.test.d.ts +2 -0
  177. package/build/typescript/tests/custom-element-reactions.test.d.ts.map +1 -0
  178. package/build/typescript/tests/custom-element-registry.test.d.ts +2 -0
  179. package/build/typescript/tests/custom-element-registry.test.d.ts.map +1 -0
  180. package/build/typescript/tests/custom-event.test.d.ts +2 -0
  181. package/build/typescript/tests/custom-event.test.d.ts.map +1 -0
  182. package/build/typescript/tests/document-clone-import-contract.test.d.ts +2 -0
  183. package/build/typescript/tests/document-clone-import-contract.test.d.ts.map +1 -0
  184. package/build/typescript/tests/document.test.d.ts +2 -0
  185. package/build/typescript/tests/document.test.d.ts.map +1 -0
  186. package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts +2 -0
  187. package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts.map +1 -0
  188. package/build/typescript/tests/event-dispatch.test.d.ts +2 -0
  189. package/build/typescript/tests/event-dispatch.test.d.ts.map +1 -0
  190. package/build/typescript/tests/event-target.test.d.ts +2 -0
  191. package/build/typescript/tests/event-target.test.d.ts.map +1 -0
  192. package/build/typescript/tests/event.test.d.ts +2 -0
  193. package/build/typescript/tests/event.test.d.ts.map +1 -0
  194. package/build/typescript/tests/extensions.test.d.ts +2 -0
  195. package/build/typescript/tests/extensions.test.d.ts.map +1 -0
  196. package/build/typescript/tests/get-elements-by-tag-name.test.d.ts +2 -0
  197. package/build/typescript/tests/get-elements-by-tag-name.test.d.ts.map +1 -0
  198. package/build/typescript/tests/getElementsByClassName.test.d.ts +2 -0
  199. package/build/typescript/tests/getElementsByClassName.test.d.ts.map +1 -0
  200. package/build/typescript/tests/hooks.test.d.ts +2 -0
  201. package/build/typescript/tests/hooks.test.d.ts.map +1 -0
  202. package/build/typescript/tests/inner-html-parsing.test.d.ts +2 -0
  203. package/build/typescript/tests/inner-html-parsing.test.d.ts.map +1 -0
  204. package/build/typescript/tests/is-default-namespace.test.d.ts +2 -0
  205. package/build/typescript/tests/is-default-namespace.test.d.ts.map +1 -0
  206. package/build/typescript/tests/name-and-namespace-normalization.test.d.ts +2 -0
  207. package/build/typescript/tests/name-and-namespace-normalization.test.d.ts.map +1 -0
  208. package/build/typescript/tests/named-node-map.test.d.ts +2 -0
  209. package/build/typescript/tests/named-node-map.test.d.ts.map +1 -0
  210. package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts +2 -0
  211. package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts.map +1 -0
  212. package/build/typescript/tests/node.test.d.ts +2 -0
  213. package/build/typescript/tests/node.test.d.ts.map +1 -0
  214. package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts +2 -0
  215. package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts.map +1 -0
  216. package/build/typescript/tests/parent-node-mutation-validation.test.d.ts +2 -0
  217. package/build/typescript/tests/parent-node-mutation-validation.test.d.ts.map +1 -0
  218. package/build/typescript/tests/parent-node.test.d.ts +2 -0
  219. package/build/typescript/tests/parent-node.test.d.ts.map +1 -0
  220. package/build/typescript/tests/serialization-template.test.d.ts +2 -0
  221. package/build/typescript/tests/serialization-template.test.d.ts.map +1 -0
  222. package/build/typescript/tests/slot-attribute.test.d.ts +2 -0
  223. package/build/typescript/tests/slot-attribute.test.d.ts.map +1 -0
  224. package/build/typescript/tests/text-content.test.d.ts +2 -0
  225. package/build/typescript/tests/text-content.test.d.ts.map +1 -0
  226. package/build/typescript/tests/toggle-attribute.test.d.ts +2 -0
  227. package/build/typescript/tests/toggle-attribute.test.d.ts.map +1 -0
  228. package/build/typescript/tests/wide-tree-traversal.test.d.ts +2 -0
  229. package/build/typescript/tests/wide-tree-traversal.test.d.ts.map +1 -0
  230. package/build/typescript/tests/window-set-global.test.d.ts +2 -0
  231. package/build/typescript/tests/window-set-global.test.d.ts.map +1 -0
  232. package/package.json +1 -1
  233. package/source/Attr.ts +60 -8
  234. package/source/CharacterData.ts +13 -0
  235. package/source/ChildNode.ts +107 -14
  236. package/source/Comment.ts +2 -2
  237. package/source/CustomElementRegistry.ts +56 -0
  238. package/source/CustomEvent.ts +2 -2
  239. package/source/Document.ts +104 -38
  240. package/source/DocumentFragment.ts +12 -2
  241. package/source/Element.ts +129 -14
  242. package/source/Event.ts +34 -32
  243. package/source/EventTarget.ts +203 -79
  244. package/source/HTMLTemplateElement.ts +2 -1
  245. package/source/MutationObserver.ts +279 -1
  246. package/source/NamedNodeMap.ts +141 -60
  247. package/source/Node.ts +70 -14
  248. package/source/NodeList.ts +13 -3
  249. package/source/ParentNode.ts +500 -88
  250. package/source/SVGElement.ts +2 -2
  251. package/source/Text.ts +2 -2
  252. package/source/Window.ts +156 -17
  253. package/source/attribute-reactions.ts +32 -0
  254. package/source/constants.ts +40 -17
  255. package/source/custom-element-reactions.ts +64 -0
  256. package/source/dom-exception.ts +14 -0
  257. package/source/extensions.ts +4 -0
  258. package/source/hook-effects.ts +56 -0
  259. package/source/hooks.ts +11 -3
  260. package/source/index.ts +1 -0
  261. package/source/names.ts +94 -0
  262. package/source/selectors.ts +301 -138
  263. package/source/serialization.ts +224 -66
  264. package/source/shared.ts +269 -40
  265. package/source/tests/ChildNode.test.ts +178 -0
  266. package/source/tests/MutationObserver.test.ts +303 -0
  267. package/source/tests/adopt-inserted-subtrees.test.ts +584 -0
  268. package/source/tests/attr-mutation.test.ts +112 -0
  269. package/source/tests/child-node-before-after.test.ts +421 -0
  270. package/source/tests/child-node-replace-with.test.ts +251 -0
  271. package/source/tests/clone-import-namespaces.test.ts +365 -0
  272. package/source/tests/constants.test.ts +46 -0
  273. package/source/tests/create-element-namespaces.test.ts +142 -0
  274. package/source/tests/create-text-node.test.ts +38 -0
  275. package/source/tests/custom-element-reactions.test.ts +600 -0
  276. package/source/tests/custom-element-registry.test.ts +137 -0
  277. package/source/tests/custom-event.test.ts +26 -0
  278. package/source/tests/document-clone-import-contract.test.ts +77 -0
  279. package/source/tests/document.test.ts +122 -0
  280. package/source/tests/dom-mutation-selector-errors.test.ts +484 -0
  281. package/source/tests/event-dispatch.test.ts +270 -0
  282. package/source/tests/event-target.test.ts +208 -0
  283. package/source/tests/event.test.ts +98 -0
  284. package/source/tests/extensions.test.ts +159 -0
  285. package/source/tests/get-elements-by-tag-name.test.ts +79 -0
  286. package/source/tests/getElementsByClassName.test.ts +109 -0
  287. package/source/tests/hooks.test.ts +43 -0
  288. package/source/tests/inner-html-parsing.test.ts +461 -0
  289. package/source/tests/is-default-namespace.test.ts +111 -0
  290. package/source/tests/name-and-namespace-normalization.test.ts +319 -0
  291. package/source/tests/named-node-map.test.ts +122 -0
  292. package/source/tests/namespaced-attribute-reactions.test.ts +141 -0
  293. package/source/tests/node.test.ts +56 -0
  294. package/source/tests/parent-node-mutation-return-values.test.ts +63 -0
  295. package/source/tests/parent-node-mutation-validation.test.ts +362 -0
  296. package/source/tests/parent-node.test.ts +169 -0
  297. package/source/tests/selectors.test.ts +533 -22
  298. package/source/tests/serialization-template.test.ts +232 -0
  299. package/source/tests/slot-attribute.test.ts +133 -0
  300. package/source/tests/text-content.test.ts +440 -0
  301. package/source/tests/toggle-attribute.test.ts +113 -0
  302. package/source/tests/wide-tree-traversal.test.ts +699 -0
  303. package/source/tests/window-set-global.test.ts +321 -0
  304. package/tsconfig.json +1 -1
package/source/Element.ts CHANGED
@@ -1,19 +1,56 @@
1
- import {NS, ATTRIBUTES, NamespaceURI, NodeType} from './constants.ts';
1
+ import {
2
+ NS,
3
+ NAME,
4
+ PREFIX,
5
+ ATTRIBUTES,
6
+ HTML_NAMESPACE,
7
+ NODE_TYPE_ELEMENT,
8
+ type NamespaceURI,
9
+ type NodeType,
10
+ asciiLowercase,
11
+ asciiUppercase,
12
+ } from './constants.ts';
13
+ import {
14
+ normalizeNamespace,
15
+ validateAndExtractQualifiedName,
16
+ validateAttributeLocalName,
17
+ } from './names.ts';
2
18
  import {ParentNode} from './ParentNode.ts';
3
19
  import {NamedNodeMap} from './NamedNodeMap.ts';
4
20
  import {Attr} from './Attr.ts';
5
21
  import {serializeNode, serializeChildren, parseHtml} from './serialization.ts';
22
+ import {
23
+ getElementsByClassName as findElementsByClassName,
24
+ getElementsByTagName as findElementsByTagName,
25
+ } from './shared.ts';
6
26
 
7
27
  export class Element extends ParentNode {
8
28
  static readonly observedAttributes?: string[];
9
29
 
10
- nodeType = NodeType.ELEMENT_NODE;
30
+ nodeType: NodeType = NODE_TYPE_ELEMENT;
31
+
32
+ [NS]: NamespaceURI = HTML_NAMESPACE;
33
+ [PREFIX]: string | null = null;
11
34
 
12
- [NS] = NamespaceURI.XHTML;
13
35
  get namespaceURI() {
14
36
  return this[NS];
15
37
  }
16
38
 
39
+ get prefix() {
40
+ return this[PREFIX];
41
+ }
42
+
43
+ get localName() {
44
+ const prefix = this[PREFIX];
45
+ return prefix == null ? this[NAME] : this[NAME].slice(prefix.length + 1);
46
+ }
47
+
48
+ get nodeName() {
49
+ return this[NS] === HTML_NAMESPACE
50
+ ? asciiUppercase(this[NAME])
51
+ : this[NAME];
52
+ }
53
+
17
54
  get tagName() {
18
55
  return this.nodeName;
19
56
  }
@@ -22,14 +59,22 @@ export class Element extends ParentNode {
22
59
 
23
60
  [anyProperty: string]: any;
24
61
 
62
+ get id() {
63
+ return this.getAttributeNS(null, 'id') ?? '';
64
+ }
65
+
66
+ set id(id: string) {
67
+ this.setAttribute('id', String(id));
68
+ }
69
+
25
70
  get slot() {
26
- return this.getAttribute('slot') ?? '';
71
+ return this.getAttributeNS(null, 'slot') ?? '';
27
72
  }
28
73
 
29
74
  set slot(slot: string) {
30
75
  const finalSlot = String(slot);
31
76
 
32
- if (this.getAttribute('slot') !== finalSlot) {
77
+ if (this.getAttributeNS(null, 'slot') !== finalSlot) {
33
78
  this.attributes.setNamedItem(new Attr('slot', finalSlot));
34
79
  }
35
80
  }
@@ -47,11 +92,19 @@ export class Element extends ParentNode {
47
92
  return [...this.attributes].map((attr) => attr.name);
48
93
  }
49
94
 
50
- get firstElementChild() {
95
+ getElementsByTagName(qualifiedName: string) {
96
+ return findElementsByTagName(this, qualifiedName);
97
+ }
98
+
99
+ get firstElementChild(): Element | null {
51
100
  return this.children[0] ?? null;
52
101
  }
53
102
 
54
- get lastElementChild() {
103
+ getElementsByClassName(classNames: string) {
104
+ return findElementsByClassName(this, classNames);
105
+ }
106
+
107
+ get lastElementChild(): Element | null {
55
108
  return this.children[this.children.length - 1] ?? null;
56
109
  }
57
110
 
@@ -68,11 +121,68 @@ export class Element extends ParentNode {
68
121
  }
69
122
 
70
123
  setAttribute(name: string, value: string) {
71
- this.attributes.setNamedItem(new Attr(name, String(value)));
124
+ const qualifiedName = String(name);
125
+ const normalizedValue = String(value);
126
+ validateAttributeLocalName(qualifiedName);
127
+ const normalizedName =
128
+ this[NS] === HTML_NAMESPACE
129
+ ? asciiLowercase(qualifiedName)
130
+ : qualifiedName;
131
+ const attribute = this.attributes.getNamedItemNS(null, normalizedName);
132
+
133
+ if (attribute) {
134
+ attribute.value = normalizedValue;
135
+ } else {
136
+ this.attributes.setNamedItem(new Attr(normalizedName, normalizedValue));
137
+ }
72
138
  }
73
139
 
74
- setAttributeNS(namespace: NamespaceURI | null, name: string, value: string) {
75
- this.attributes.setNamedItemNS(new Attr(name, String(value), namespace));
140
+ setAttributeNS(
141
+ namespace: NamespaceURI,
142
+ qualifiedName: string,
143
+ value: string,
144
+ ) {
145
+ const normalizedNamespace = normalizeNamespace(namespace);
146
+ const normalizedQualifiedName = String(qualifiedName);
147
+ const normalizedValue = String(value);
148
+ const name = validateAndExtractQualifiedName(
149
+ normalizedNamespace,
150
+ normalizedQualifiedName,
151
+ 'attribute',
152
+ );
153
+ const attribute = this.attributes.getNamedItemNS(
154
+ name.namespace,
155
+ name.localName,
156
+ );
157
+
158
+ if (attribute) {
159
+ attribute.value = normalizedValue;
160
+ } else {
161
+ this.attributes.setNamedItemNS(
162
+ new Attr(name.qualifiedName, normalizedValue, name.namespace),
163
+ );
164
+ }
165
+ }
166
+
167
+ toggleAttribute(name: string, force?: boolean) {
168
+ const qualifiedName = String(name);
169
+ validateAttributeLocalName(qualifiedName);
170
+ const normalizedName =
171
+ this[NS] === HTML_NAMESPACE
172
+ ? asciiLowercase(qualifiedName)
173
+ : qualifiedName;
174
+ const attribute = this.attributes.getNamedItemNS(null, normalizedName);
175
+ const normalizedForce = force === undefined ? undefined : Boolean(force);
176
+
177
+ if (attribute == null) {
178
+ if (normalizedForce === false) return false;
179
+ this.attributes.setNamedItem(new Attr(normalizedName, ''));
180
+ return true;
181
+ }
182
+
183
+ if (normalizedForce === true) return true;
184
+ this.attributes.removeNamedItemNS(null, normalizedName);
185
+ return false;
76
186
  }
77
187
 
78
188
  getAttribute(name: string) {
@@ -80,7 +190,7 @@ export class Element extends ParentNode {
80
190
  return attr && attr.value;
81
191
  }
82
192
 
83
- getAttributeNS(namespace: NamespaceURI | null, name: string) {
193
+ getAttributeNS(namespace: NamespaceURI, name: string) {
84
194
  const attr = this.attributes.getNamedItemNS(namespace, name);
85
195
  return attr && attr.value;
86
196
  }
@@ -90,16 +200,21 @@ export class Element extends ParentNode {
90
200
  return attr != null;
91
201
  }
92
202
 
93
- hasAttributeNS(namespace: NamespaceURI | null, name: string) {
203
+ hasAttributeNS(namespace: NamespaceURI, name: string) {
94
204
  const attr = this.attributes.getNamedItemNS(namespace, name);
95
205
  return attr != null;
96
206
  }
97
207
 
98
208
  removeAttribute(name: string) {
99
- this.attributes.removeNamedItem(name);
209
+ const qualifiedName = String(name);
210
+ const normalizedName =
211
+ this[NS] === HTML_NAMESPACE
212
+ ? asciiLowercase(qualifiedName)
213
+ : qualifiedName;
214
+ this.attributes.removeNamedItemNS(null, normalizedName);
100
215
  }
101
216
 
102
- removeAttributeNS(namespace: NamespaceURI | null, name: string) {
217
+ removeAttributeNS(namespace: NamespaceURI, name: string) {
103
218
  this.attributes.removeNamedItemNS(namespace, name);
104
219
  }
105
220
 
package/source/Event.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ DISPATCHING,
2
3
  PATH,
3
4
  IS_TRUSTED,
4
5
  LISTENERS,
@@ -6,14 +7,12 @@ import {
6
7
  } from './constants.ts';
7
8
  import type {EventTarget} from './EventTarget.ts';
8
9
 
9
- export const enum EventPhase {
10
- NONE = 0,
11
- CAPTURING_PHASE = 1,
12
- AT_TARGET = 2,
13
- BUBBLING_PHASE = 3,
14
- }
10
+ export const EVENT_PHASE_NONE = 0;
11
+ export const EVENT_PHASE_CAPTURING = 1;
12
+ export const EVENT_PHASE_AT_TARGET = 2;
13
+ export const EVENT_PHASE_BUBBLING = 3;
15
14
 
16
- export const CAPTURE_MARKER = '@';
15
+ export type EventPhase = number;
17
16
 
18
17
  export interface EventInit {
19
18
  bubbles?: boolean;
@@ -27,16 +26,17 @@ const now =
27
26
  : performance.now.bind(performance);
28
27
 
29
28
  export class Event {
30
- static NONE = EventPhase.NONE;
31
- static CAPTURING_PHASE = EventPhase.CAPTURING_PHASE;
32
- static AT_TARGET = EventPhase.AT_TARGET;
33
- static BUBBLING_PHASE = EventPhase.BUBBLING_PHASE;
29
+ static NONE: EventPhase = EVENT_PHASE_NONE;
30
+ static CAPTURING_PHASE: EventPhase = EVENT_PHASE_CAPTURING;
31
+ static AT_TARGET: EventPhase = EVENT_PHASE_AT_TARGET;
32
+ static BUBBLING_PHASE: EventPhase = EVENT_PHASE_BUBBLING;
34
33
 
35
- // NONE = EventPhase.NONE;
36
- // CAPTURING_PHASE = EventPhase.CAPTURING_PHASE;
37
- // AT_TARGET = EventPhase.AT_TARGET;
38
- // BUBBLING_PHASE = EventPhase.BUBBLING_PHASE;
34
+ // NONE = EVENT_PHASE_NONE;
35
+ // CAPTURING_PHASE = EVENT_PHASE_CAPTURING;
36
+ // AT_TARGET = EVENT_PHASE_AT_TARGET;
37
+ // BUBBLING_PHASE = EVENT_PHASE_BUBBLING;
39
38
 
39
+ type: string;
40
40
  timeStamp = now();
41
41
  target: EventTarget | null = null;
42
42
  currentTarget: EventTarget | null = null;
@@ -46,17 +46,16 @@ export class Event {
46
46
  composed = false;
47
47
  defaultPrevented = false;
48
48
  cancelBubble = false;
49
- eventPhase: EventPhase = 0;
49
+ eventPhase: EventPhase = EVENT_PHASE_NONE;
50
50
  // private inPassiveListener = false;
51
51
  data?: any;
52
52
  [PATH]: EventTarget[] = [];
53
53
  [IS_TRUSTED]!: boolean;
54
54
  [STOP_IMMEDIATE_PROPAGATION] = false;
55
+ [DISPATCHING] = false;
55
56
 
56
- constructor(
57
- public type: string,
58
- options?: EventInit,
59
- ) {
57
+ constructor(type: string, options?: EventInit) {
58
+ this.type = type;
60
59
  Object.defineProperty(this, IS_TRUSTED, {writable: true, value: false});
61
60
  if (options) {
62
61
  if (options.bubbles) this.bubbles = options.bubbles;
@@ -70,7 +69,7 @@ export class Event {
70
69
  }
71
70
 
72
71
  composedPath() {
73
- return this[PATH];
72
+ return [...this[PATH]];
74
73
  }
75
74
 
76
75
  stopPropagation() {
@@ -83,15 +82,15 @@ export class Event {
83
82
  }
84
83
 
85
84
  preventDefault() {
86
- this.defaultPrevented = true;
85
+ if (this.cancelable) this.defaultPrevented = true;
87
86
  }
88
87
 
89
88
  set returnValue(value) {
90
- this.defaultPrevented = value;
89
+ if (!value) this.preventDefault();
91
90
  }
92
91
 
93
92
  get returnValue() {
94
- return this.defaultPrevented;
93
+ return !this.defaultPrevented;
95
94
  }
96
95
 
97
96
  /** @deprecated */
@@ -105,22 +104,25 @@ export class Event {
105
104
  export function fireEvent(
106
105
  event: Event,
107
106
  currentTarget: EventTarget,
108
- phase: EventPhase.BUBBLING_PHASE | EventPhase.CAPTURING_PHASE,
107
+ phase: typeof EVENT_PHASE_BUBBLING | typeof EVENT_PHASE_CAPTURING,
109
108
  ): void {
109
+ if (event.cancelBubble) return;
110
+
110
111
  const listeners = currentTarget[LISTENERS];
111
- const list = listeners?.get(
112
- `${event.type}${
113
- phase === EventPhase.CAPTURING_PHASE ? CAPTURE_MARKER : ''
114
- }`,
115
- );
112
+ const list = listeners?.get(event.type)?.[
113
+ phase === EVENT_PHASE_CAPTURING ? 'capture' : 'bubble'
114
+ ];
116
115
 
117
116
  if (!list) return;
118
117
 
119
- for (const listener of list) {
118
+ for (const registration of [...list]) {
119
+ if (!list.has(registration) || registration.signal?.aborted) continue;
120
+
120
121
  event.eventPhase =
121
- event.target === currentTarget ? EventPhase.AT_TARGET : phase;
122
+ event.target === currentTarget ? EVENT_PHASE_AT_TARGET : phase;
122
123
  event.currentTarget = currentTarget;
123
124
 
125
+ const listener = registration.normalizedListener;
124
126
  try {
125
127
  if (typeof listener === 'object') {
126
128
  listener.handleEvent(event as any);
@@ -1,18 +1,51 @@
1
- import {HOOKS, PATH, LISTENERS, OWNER_DOCUMENT} from './constants.ts';
2
- import {fireEvent, EventPhase} from './Event.ts';
3
- import {CAPTURE_MARKER, type Event} from './Event.ts';
1
+ import {
2
+ DISPATCHING,
3
+ HOOKS_DISPATCH,
4
+ PATH,
5
+ LISTENERS,
6
+ OWNER_DOCUMENT,
7
+ STOP_IMMEDIATE_PROPAGATION,
8
+ } from './constants.ts';
9
+ import {createDOMException} from './dom-exception.ts';
10
+ import {
11
+ EVENT_PHASE_NONE,
12
+ EVENT_PHASE_BUBBLING,
13
+ EVENT_PHASE_CAPTURING,
14
+ fireEvent,
15
+ } from './Event.ts';
16
+ import {type Event} from './Event.ts';
4
17
  import type {ChildNode} from './ChildNode.ts';
5
18
  import type {Document} from './Document.ts';
6
19
 
7
- const ONCE_LISTENERS = Symbol('onceListeners');
20
+ const LISTENER_REGISTRATIONS = Symbol('listenerRegistrations');
21
+
22
+ type ListenerPhase = 'bubble' | 'capture';
23
+
24
+ interface EventListenersForType {
25
+ bubble?: Set<ListenerRegistration>;
26
+ capture?: Set<ListenerRegistration>;
27
+ }
28
+
29
+ interface ListenerRegistrationsForType {
30
+ bubble?: Map<EventListenerOrEventListenerObject, ListenerRegistration>;
31
+ capture?: Map<EventListenerOrEventListenerObject, ListenerRegistration>;
32
+ }
33
+
34
+ interface ListenerRegistration {
35
+ type: string;
36
+ listener: EventListenerOrEventListenerObject;
37
+ capture: boolean;
38
+ once: boolean;
39
+ normalizedListener: EventListenerOrEventListenerObject;
40
+ signal?: AbortSignal;
41
+ abortListener?: EventListener;
42
+ }
8
43
 
9
44
  export class EventTarget {
10
- [LISTENERS]:
11
- | Map<string, Set<EventListenerOrEventListenerObject>>
12
- | undefined = undefined;
45
+ [LISTENERS]: Map<string, EventListenersForType> | undefined = undefined;
13
46
 
14
- [ONCE_LISTENERS]:
15
- | WeakMap<EventListenerOrEventListenerObject, EventListener>
47
+ [LISTENER_REGISTRATIONS]:
48
+ | Map<string, ListenerRegistrationsForType>
16
49
  | undefined = undefined;
17
50
 
18
51
  /**
@@ -28,31 +61,59 @@ export class EventTarget {
28
61
  ) {
29
62
  if (listener == null) return;
30
63
 
31
- const capture = options === true || (options && options.capture === true);
64
+ const capture =
65
+ options === true ||
66
+ (typeof options === 'object' && options.capture === true);
32
67
  const once = typeof options === 'object' && options.once === true;
33
68
  const signal = typeof options === 'object' ? options.signal : undefined;
34
- const key = `${type}${capture ? CAPTURE_MARKER : ''}`;
35
- let normalizedListener = listener;
69
+ if (signal?.aborted) return;
70
+
71
+ const phase = listenerPhase(capture);
72
+ let registrations = this[LISTENER_REGISTRATIONS];
73
+ if (!registrations) {
74
+ registrations = new Map();
75
+ this[LISTENER_REGISTRATIONS] = registrations;
76
+ }
77
+
78
+ let registrationsForType = registrations.get(type);
79
+ if (!registrationsForType) {
80
+ registrationsForType = {};
81
+ registrations.set(type, registrationsForType);
82
+ }
83
+
84
+ let registrationsForPhase = registrationsForType[phase];
85
+ if (!registrationsForPhase) {
86
+ registrationsForPhase = new Map();
87
+ registrationsForType[phase] = registrationsForPhase;
88
+ }
89
+
90
+ if (registrationsForPhase.has(listener)) return;
36
91
 
37
- if (once) {
38
- normalizedListener = function normalizedListener(
92
+ const target = this;
93
+ const registration: ListenerRegistration = {
94
+ type,
95
+ listener,
96
+ capture,
97
+ once,
98
+ normalizedListener: listener,
99
+ signal,
100
+ };
101
+
102
+ if (once || signal) {
103
+ registration.normalizedListener = function normalizedListener(
39
104
  this: EventTarget,
40
105
  ...args: Parameters<EventListener>
41
106
  ) {
42
- this.removeEventListener(type, listener, options);
107
+ if (registration.signal?.aborted) {
108
+ removeListenerRegistration(target, registration);
109
+ return;
110
+ }
111
+ if (registration.once) removeListenerRegistration(target, registration);
43
112
 
44
- return typeof listener === 'object'
45
- ? listener.handleEvent(...args)
46
- : listener.call(this, ...args);
113
+ return typeof registration.listener === 'object'
114
+ ? registration.listener.handleEvent(...args)
115
+ : registration.listener.call(this, ...args);
47
116
  };
48
-
49
- let onceListeners = this[ONCE_LISTENERS];
50
- if (!onceListeners) {
51
- onceListeners = new WeakMap();
52
- this[ONCE_LISTENERS] = onceListeners;
53
- }
54
-
55
- onceListeners.set(listener, normalizedListener);
56
117
  }
57
118
 
58
119
  let listeners = this[LISTENERS];
@@ -61,24 +122,29 @@ export class EventTarget {
61
122
  this[LISTENERS] = listeners;
62
123
  }
63
124
 
64
- let list = listeners.get(key);
125
+ let listenersForType = listeners.get(type);
126
+ if (!listenersForType) {
127
+ listenersForType = {};
128
+ listeners.set(type, listenersForType);
129
+ }
130
+
131
+ let list = listenersForType[phase];
65
132
  if (!list) {
66
133
  list = new Set();
67
- listeners.set(key, list);
134
+ listenersForType[phase] = list;
68
135
  }
69
136
 
70
- if (list.has(normalizedListener)) return;
71
-
72
- signal?.addEventListener(
73
- 'abort',
74
- () => {
75
- removeEventListener.call(this, type, listener, options);
76
- },
77
- {once: true},
78
- );
137
+ registrationsForPhase.set(listener, registration);
138
+ list.add(registration);
79
139
 
80
- list.add(normalizedListener);
81
- this[OWNER_DOCUMENT]?.defaultView[HOOKS].addEventListener?.(
140
+ if (signal) {
141
+ const abortListener = () => {
142
+ removeListenerRegistration(target, registration);
143
+ };
144
+ registration.abortListener = abortListener;
145
+ signal.addEventListener('abort', abortListener, {once: true});
146
+ }
147
+ this[OWNER_DOCUMENT]?.defaultView[HOOKS_DISPATCH].addEventListener?.(
82
148
  this as any,
83
149
  type,
84
150
  listener,
@@ -99,33 +165,51 @@ export class EventTarget {
99
165
  // }
100
166
 
101
167
  dispatchEvent(event: Event) {
102
- const path: EventTarget[] = [];
103
- // instanceof here is just to keep TypeScript happy
104
- let target = this as unknown as ChildNode | null;
105
- while (target != null) {
106
- path.push(target);
107
- target = target.parentNode;
108
- }
109
- // while (target instanceof Node && (target = target.parentNode)) {
110
- // path.push(target);
111
- // }
112
- event.target = this;
113
- event.srcElement = this;
114
- event[PATH] = path;
115
-
116
- for (let i = path.length; i--; ) {
117
- fireEvent(event, path[i]!, EventPhase.CAPTURING_PHASE);
118
- if (event.cancelBubble) return event.defaultPrevented;
168
+ if (event[DISPATCHING]) {
169
+ throw createDOMException(
170
+ `Failed to execute 'dispatchEvent' on 'EventTarget': The event is already being dispatched.`,
171
+ 'InvalidStateError',
172
+ );
119
173
  }
120
174
 
121
- const bubblePath = event.bubbles ? path : path.slice(0, 1);
175
+ event[DISPATCHING] = true;
122
176
 
123
- for (let i = 0; i < bubblePath.length; i++) {
124
- fireEvent(event, bubblePath[i]!, EventPhase.BUBBLING_PHASE);
125
- if (event.cancelBubble) return event.defaultPrevented;
126
- }
177
+ try {
178
+ const path: EventTarget[] = [];
179
+ // instanceof here is just to keep TypeScript happy
180
+ let target = this as unknown as ChildNode | null;
181
+ while (target != null) {
182
+ path.push(target);
183
+ target = target.parentNode;
184
+ }
185
+ // while (target instanceof Node && (target = target.parentNode)) {
186
+ // path.push(target);
187
+ // }
188
+ event.target = this;
189
+ event.srcElement = this;
190
+ event[PATH] = path;
191
+
192
+ for (let i = path.length; i--; ) {
193
+ fireEvent(event, path[i]!, EVENT_PHASE_CAPTURING);
194
+ if (event.cancelBubble) return !event.defaultPrevented;
195
+ }
196
+
197
+ const bubblePath = event.bubbles ? path : path.slice(0, 1);
198
+
199
+ for (let i = 0; i < bubblePath.length; i++) {
200
+ fireEvent(event, bubblePath[i]!, EVENT_PHASE_BUBBLING);
201
+ if (event.cancelBubble) return !event.defaultPrevented;
202
+ }
127
203
 
128
- return event.defaultPrevented;
204
+ return !event.defaultPrevented;
205
+ } finally {
206
+ event[DISPATCHING] = false;
207
+ event.cancelBubble = false;
208
+ event[STOP_IMMEDIATE_PROPAGATION] = false;
209
+ event.eventPhase = EVENT_PHASE_NONE;
210
+ event.currentTarget = null;
211
+ event[PATH] = [];
212
+ }
129
213
  }
130
214
  }
131
215
 
@@ -137,24 +221,64 @@ function removeEventListener(
137
221
  ) {
138
222
  if (listener == null) return;
139
223
 
140
- const onceListeners = this[ONCE_LISTENERS];
141
- const normalizedListener = onceListeners?.get(listener) ?? listener;
224
+ const capture = options === true || (options && options.capture === true);
225
+ const phase = listenerPhase(Boolean(capture));
226
+ const registration =
227
+ this[LISTENER_REGISTRATIONS]?.get(type)?.[phase]?.get(listener);
228
+ if (!registration) return;
142
229
 
143
- onceListeners?.delete(listener);
230
+ removeListenerRegistration(this, registration);
231
+ }
144
232
 
145
- const capture = options === true || (options && options.capture === true);
146
- const key = `${type}${capture ? CAPTURE_MARKER : ''}`;
147
- const list = this[LISTENERS]?.get(key);
148
-
149
- if (list) {
150
- const deleted = list.delete(normalizedListener);
151
- if (deleted) {
152
- this[OWNER_DOCUMENT]?.defaultView[HOOKS].removeEventListener?.(
153
- this as any,
154
- type,
155
- listener,
156
- options,
157
- );
158
- }
233
+ function listenerPhase(capture: boolean): ListenerPhase {
234
+ return capture ? 'capture' : 'bubble';
235
+ }
236
+
237
+ function removeListenerRegistration(
238
+ target: EventTarget,
239
+ registration: ListenerRegistration,
240
+ ) {
241
+ const phase = listenerPhase(registration.capture);
242
+ const registrations = target[LISTENER_REGISTRATIONS];
243
+ const registrationsForType = registrations?.get(registration.type);
244
+ if (!registrationsForType) return;
245
+
246
+ const registrationsForPhase = registrationsForType[phase];
247
+ if (
248
+ !registrationsForPhase ||
249
+ registrationsForPhase.get(registration.listener) !== registration
250
+ ) {
251
+ return;
252
+ }
253
+
254
+ registrationsForPhase.delete(registration.listener);
255
+ if (registrationsForPhase.size === 0) delete registrationsForType[phase];
256
+ if (!registrationsForType.bubble && !registrationsForType.capture) {
257
+ registrations?.delete(registration.type);
258
+ }
259
+
260
+ if (registration.signal && registration.abortListener) {
261
+ registration.signal.removeEventListener(
262
+ 'abort',
263
+ registration.abortListener,
264
+ );
159
265
  }
266
+
267
+ const listeners = target[LISTENERS];
268
+ const listenersForType = listeners?.get(registration.type);
269
+ if (!listenersForType) return;
270
+
271
+ const list = listenersForType[phase];
272
+ if (!list?.delete(registration)) return;
273
+ if (list.size === 0) delete listenersForType[phase];
274
+ if (!listenersForType.bubble && !listenersForType.capture) {
275
+ listeners?.delete(registration.type);
276
+ }
277
+
278
+ target[OWNER_DOCUMENT]?.defaultView[HOOKS_DISPATCH].removeEventListener?.(
279
+ target as any,
280
+ registration.type,
281
+ registration.listener,
282
+ registration.capture,
283
+ );
160
284
  }
@@ -1,4 +1,4 @@
1
- import {CONTENT} from './constants.ts';
1
+ import {CONTENT, HOST} from './constants.ts';
2
2
  import {DocumentFragment} from './DocumentFragment.ts';
3
3
  import {Element} from './Element.ts';
4
4
  import {parseHtml, serializeChildren} from './serialization.ts';
@@ -10,6 +10,7 @@ export class HTMLTemplateElement extends Element {
10
10
  let content = this[CONTENT];
11
11
  if (!content) {
12
12
  content = this.ownerDocument.createDocumentFragment();
13
+ content[HOST] = this;
13
14
  this[CONTENT] = content;
14
15
  }
15
16
  return content;