@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
@@ -1,110 +1,268 @@
1
1
  import {
2
2
  ATTRIBUTES,
3
3
  CHILD,
4
+ CONTENT,
5
+ CREATE_ELEMENT,
4
6
  DATA,
7
+ HTML_NAMESPACE,
5
8
  NAME,
6
9
  NEXT,
10
+ NS,
11
+ SVG_NAMESPACE,
7
12
  VALUE,
8
- NodeType,
13
+ NODE_TYPE_COMMENT,
14
+ NODE_TYPE_ELEMENT,
15
+ NODE_TYPE_TEXT,
16
+ asciiLowercase,
9
17
  } from './constants.ts';
10
18
  import type {Node} from './Node.ts';
11
19
  import type {Text} from './Text.ts';
12
20
  import type {Comment} from './Comment.ts';
13
21
  import type {ParentNode} from './ParentNode.ts';
14
22
  import type {Element} from './Element.ts';
23
+ import type {HTMLTemplateElement} from './HTMLTemplateElement.ts';
15
24
 
16
- // const voidElements = {
17
- // img: true,
18
- // image: true,
19
- // };
20
- // const elementTokenizer =
21
- // /(?:<([a-z][a-z0-9-:]*)( [^<>'"\n=\s]+=(['"])[^>'"\n]*\3)*\s*(\/?)\s*>|<\/([a-z][a-z0-9-:]*)>|([^&<>]+))/gi;
22
- // const attributeTokenizer = / ([^<>'"\n=\s]+)=(['"])([^>'"\n]*)\2/g;
25
+ const CHARACTER_REFERENCES: Readonly<Record<string, string>> = {
26
+ amp: '&',
27
+ AMP: '&',
28
+ apos: "'",
29
+ gt: '>',
30
+ GT: '>',
31
+ lt: '<',
32
+ LT: '<',
33
+ quot: '"',
34
+ QUOT: '"',
35
+ };
23
36
 
24
- const elementTokenizer =
25
- /(?:<([a-z][a-z0-9-:]*)((?:[\s]+[^<>'"=\s]+(?:=(['"])[^]*?\3|=[^>'"\s]*|))*)[\s]*(\/?)\s*>|<\/([a-z][a-z0-9-:]*)>|<!--(.*?)-->|([^&<>]+))/gi;
37
+ const VOID_ELEMENTS = new Set([
38
+ 'area',
39
+ 'base',
40
+ 'br',
41
+ 'col',
42
+ 'embed',
43
+ 'hr',
44
+ 'img',
45
+ 'input',
46
+ 'link',
47
+ 'meta',
48
+ 'param',
49
+ 'source',
50
+ 'track',
51
+ 'wbr',
52
+ ]);
26
53
 
27
- const attributeTokenizer =
28
- /\s([^<>'"=\n\s]+)(?:=(["'])([\s\S]*?)\2|=([^>'"\n\s]*)|)/g;
54
+ const ELEMENT_TOKENIZER =
55
+ /(?:<([a-z][a-z0-9-:]*)((?:[\s]+[^<>'"=/\s]+(?:=(['"])[^]*?\3|=[^>'"\s]*|))*)[\s]*(\/?)\s*>|<\/([a-z][a-z0-9-:]*)>|<!--(.*?)-->|([^<>]+))/gi;
56
+ const ATTRIBUTE_TOKENIZER =
57
+ /\s([^<>'"=/\n\s]+)(?:=(["'])([\s\S]*?)\2|=([^>'"\n\s]*)|)/g;
58
+ const SVG_HTML_BREAKOUTS =
59
+ '|b|big|blockquote|body|br|center|code|dd|div|dl|dt|em|embed|h1|h2|h3|h4|h5|h6|head|hr|i|img|li|listing|menu|meta|nobr|ol|p|pre|ruby|s|small|span|strong|strike|sub|sup|table|tt|u|ul|var|';
60
+ const SVG_HTML_FONT_ATTRIBUTE = /^(?:color|face|size)$/i;
61
+
62
+ function decodeCharacterReferences(value: string) {
63
+ return value.replace(
64
+ /&(?:(amp|AMP|apos|gt|GT|lt|LT|quot|QUOT)|#(\d+)|#[xX]([\da-fA-F]+));/g,
65
+ (reference, name: string | undefined, decimal, hexadecimal) => {
66
+ if (name) return CHARACTER_REFERENCES[name]!;
67
+
68
+ const codePoint = Number.parseInt(
69
+ decimal ?? hexadecimal,
70
+ decimal ? 10 : 16,
71
+ );
72
+
73
+ try {
74
+ return String.fromCodePoint(codePoint);
75
+ } catch {
76
+ return reference;
77
+ }
78
+ },
79
+ );
80
+ }
81
+
82
+ function isVoidElement(element: Element) {
83
+ return (
84
+ element.namespaceURI === HTML_NAMESPACE &&
85
+ VOID_ELEMENTS.has(element.localName)
86
+ );
87
+ }
88
+
89
+ function isHtmlIntegrationPoint(element: Element) {
90
+ const name = element.localName;
91
+ return (
92
+ element[NS] === SVG_NAMESPACE &&
93
+ (name === 'foreignObject' || name === 'desc' || name === 'title')
94
+ );
95
+ }
96
+
97
+ function isSvgHtmlBreakout(tag: string, attributes: string) {
98
+ if (SVG_HTML_BREAKOUTS.includes(`|${tag}|`)) return true;
99
+ if (tag !== 'font') return false;
100
+
101
+ for (const attribute of attributes.matchAll(ATTRIBUTE_TOKENIZER)) {
102
+ if (SVG_HTML_FONT_ATTRIBUTE.test(attribute[1]!)) return true;
103
+ }
104
+ return false;
105
+ }
29
106
 
30
107
  export function parseHtml(html: string, contextNode: Node) {
31
108
  const document = contextNode.ownerDocument;
32
109
  const root = document.createDocumentFragment();
33
- const stack: Node[] = [root];
110
+ const stack: {element: Element; target: ParentNode}[] = [];
34
111
  let parent: ParentNode = root;
35
- let token: RegExpExecArray | null;
36
- elementTokenizer.lastIndex = 0;
37
- while ((token = elementTokenizer.exec(html))) {
112
+ for (const token of html.matchAll(ELEMENT_TOKENIZER)) {
38
113
  const tag = token[1];
39
114
  if (tag) {
40
- const node = document.createElement(tag);
41
115
  const attrs = token[2]!;
42
- attributeTokenizer.lastIndex = 0;
43
- let t: RegExpExecArray | null;
44
- while ((t = attributeTokenizer.exec(attrs))) {
45
- node.setAttribute(t[1]!, t[3] || t[4] || '');
116
+ const normalizedTag = asciiLowercase(tag);
117
+ const openElement =
118
+ stack[stack.length - 1]?.element ?? (contextNode as Element);
119
+ let namespace =
120
+ openElement[NS] === SVG_NAMESPACE ? SVG_NAMESPACE : HTML_NAMESPACE;
121
+
122
+ if (normalizedTag === 'svg') {
123
+ namespace = SVG_NAMESPACE;
124
+ } else if (namespace === SVG_NAMESPACE) {
125
+ if (isHtmlIntegrationPoint(openElement)) {
126
+ namespace = HTML_NAMESPACE;
127
+ } else if (isSvgHtmlBreakout(normalizedTag, attrs)) {
128
+ while (stack.length > 0) {
129
+ const frame = stack[stack.length - 1]!;
130
+ if (
131
+ frame.element[NS] !== SVG_NAMESPACE ||
132
+ isHtmlIntegrationPoint(frame.element)
133
+ ) {
134
+ break;
135
+ }
136
+
137
+ stack.pop();
138
+ parent = frame.target;
139
+ }
140
+ namespace = HTML_NAMESPACE;
141
+ }
142
+ }
143
+
144
+ const name = namespace === HTML_NAMESPACE ? normalizedTag : tag;
145
+ const node = document[CREATE_ELEMENT](name, namespace, null, name);
146
+ for (const attribute of attrs.matchAll(ATTRIBUTE_TOKENIZER)) {
147
+ node.setAttribute(
148
+ attribute[1]!,
149
+ decodeCharacterReferences(attribute[3] || attribute[4] || ''),
150
+ );
46
151
  }
47
152
  parent.append(node);
48
- // if (voidElements[tag] === true) continue;
49
- stack.push(parent);
50
- parent = node;
153
+ if (isVoidElement(node) || (namespace !== HTML_NAMESPACE && token[4])) {
154
+ continue;
155
+ }
156
+ stack.push({element: node, target: parent});
157
+ parent =
158
+ namespace === HTML_NAMESPACE && normalizedTag === 'template'
159
+ ? (node as HTMLTemplateElement).content
160
+ : node;
51
161
  } else if (token[5]) {
52
- parent = (stack.pop() as ParentNode) || root;
162
+ const closingTag = asciiLowercase(token[5]);
163
+ for (let index = stack.length - 1; index >= 0; index--) {
164
+ const frame = stack[index]!;
165
+ const frameName = asciiLowercase(frame.element[NAME]);
166
+ if (frameName === closingTag) {
167
+ parent = frame.target;
168
+ stack.length = index;
169
+ break;
170
+ }
171
+ if (frame.element[NS] === HTML_NAMESPACE && frameName === 'template') {
172
+ break;
173
+ }
174
+ }
53
175
  } else if (token[6]) {
54
176
  parent.append(document.createComment(token[6]!));
55
177
  } else {
56
- parent.append(token[7]!);
178
+ parent.append(decodeCharacterReferences(token[7]!));
57
179
  }
58
180
  }
59
181
  return root;
60
182
  }
61
183
 
184
+ type SerializationWorkItem =
185
+ | {type: 'opening'; node: Node}
186
+ | {type: 'content'; child: Node | null}
187
+ | {type: 'closing'; name: string};
188
+
62
189
  export function serializeChildren(parentNode: ParentNode) {
63
- let out = '';
64
- let child = parentNode[CHILD];
65
- while (child) {
66
- out += serializeNode(child);
67
- child = child[NEXT];
68
- }
69
- return out;
190
+ return serialize([{type: 'content', child: parentNode[CHILD]}]);
70
191
  }
71
192
 
72
193
  export function serializeNode(node: Node) {
73
- switch (node.nodeType) {
74
- case NodeType.ELEMENT_NODE: {
75
- const el = node as Element;
76
- let out = `<${el[NAME]}`;
77
- let attr = el[ATTRIBUTES]?.[CHILD];
78
- while (attr) {
79
- out += ` ${attr[NAME]}`;
80
- let value = attr[VALUE];
81
- if (value !== '') {
82
- value = String(value).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
83
- out += `="${value}"`;
194
+ return serialize([{type: 'opening', node}]);
195
+ }
196
+
197
+ function serialize(workItems: SerializationWorkItem[]) {
198
+ const chunks: string[] = [];
199
+ let workItem: SerializationWorkItem | undefined;
200
+
201
+ while ((workItem = workItems.pop())) {
202
+ switch (workItem.type) {
203
+ case 'opening': {
204
+ const node = workItem.node;
205
+ switch (node.nodeType) {
206
+ case NODE_TYPE_ELEMENT: {
207
+ const el = node as Element;
208
+ chunks.push(`<${el[NAME]}`);
209
+ let attr = el[ATTRIBUTES]?.[CHILD];
210
+ while (attr) {
211
+ chunks.push(` ${attr[NAME]}`);
212
+ let value = attr[VALUE];
213
+ if (value !== '') {
214
+ value = String(value)
215
+ .replace(/&/g, '&amp;')
216
+ .replace(/"/g, '&quot;');
217
+ chunks.push(`="${value}"`);
218
+ }
219
+ attr = attr[NEXT];
220
+ }
221
+ chunks.push('>');
222
+ if (isVoidElement(el)) break;
223
+
224
+ const content =
225
+ el.namespaceURI === HTML_NAMESPACE && el.localName === 'template'
226
+ ? (el as {[CONTENT]?: ParentNode})[CONTENT]
227
+ : el;
228
+ workItems.push({type: 'closing', name: el[NAME]});
229
+ workItems.push({
230
+ type: 'content',
231
+ child: content?.[CHILD] ?? null,
232
+ });
233
+ break;
234
+ }
235
+ case NODE_TYPE_TEXT: {
236
+ const text = node as Text;
237
+ chunks.push(
238
+ text[DATA].replace(/&/g, '&amp;')
239
+ .replace(/"/g, '&quot;')
240
+ .replace(/</g, '&lt;')
241
+ .replace(/>/g, '&gt;'),
242
+ );
243
+ break;
244
+ }
245
+ case NODE_TYPE_COMMENT: {
246
+ const text = node as Comment;
247
+ chunks.push(`<!--${text[DATA]}-->`);
248
+ break;
249
+ }
84
250
  }
85
- attr = attr[NEXT];
251
+ break;
86
252
  }
87
- out += '>';
88
- out += serializeChildren(el);
89
- // let child = el[CHILD];
90
- // while (child) {
91
- // out += serialize(child);
92
- // child = child[NEXT];
93
- // }
94
- out += `</${el[NAME]}>`;
95
- return out;
96
- }
97
- case NodeType.TEXT_NODE: {
98
- const text = node as Text;
99
- return text[DATA].replace(/&/g, '&amp;')
100
- .replace(/"/g, '&quot;')
101
- .replace(/</g, '&lt;')
102
- .replace(/>/g, '&gt;');
103
- }
104
- case NodeType.COMMENT_NODE: {
105
- const text = node as Comment;
106
- return `<!--${text[DATA]}-->`;
253
+ case 'content': {
254
+ const child = workItem.child;
255
+ if (child) {
256
+ workItems.push({type: 'content', child: child[NEXT]});
257
+ workItems.push({type: 'opening', node: child});
258
+ }
259
+ break;
260
+ }
261
+ case 'closing':
262
+ chunks.push(`</${workItem.name}>`);
263
+ break;
107
264
  }
108
265
  }
109
- return '';
266
+
267
+ return chunks.join('');
110
268
  }
package/source/shared.ts CHANGED
@@ -2,9 +2,21 @@ import {
2
2
  DATA,
3
3
  OWNER_DOCUMENT,
4
4
  ATTRIBUTES,
5
- NodeType,
5
+ NODE_TYPE_ATTRIBUTE,
6
+ NODE_TYPE_COMMENT,
7
+ NODE_TYPE_DOCUMENT_FRAGMENT,
8
+ NODE_TYPE_ELEMENT,
9
+ NODE_TYPE_TEXT,
10
+ NODE_TYPE_DOCUMENT,
6
11
  CHILD,
7
12
  NEXT,
13
+ NAME,
14
+ PREFIX,
15
+ CONTENT,
16
+ HTML_NAMESPACE,
17
+ CREATE_ELEMENT,
18
+ asciiLowercase,
19
+ splitOnASCIIWhitespace,
8
20
  } from './constants.ts';
9
21
  import type {Document} from './Document.ts';
10
22
  import type {DocumentFragment} from './DocumentFragment.ts';
@@ -12,73 +24,133 @@ import type {Node} from './Node.ts';
12
24
  import type {Comment} from './Comment.ts';
13
25
  import type {ParentNode} from './ParentNode.ts';
14
26
  import type {Element} from './Element.ts';
27
+ import type {Attr} from './Attr.ts';
28
+ import type {HTMLTemplateElement} from './HTMLTemplateElement.ts';
15
29
  import type {CharacterData} from './CharacterData.ts';
16
30
  import type {Text} from './Text.ts';
31
+ import {
32
+ MATCHER_CLASS,
33
+ MATCHER_ID,
34
+ querySelector,
35
+ querySelectorAll,
36
+ } from './selectors.ts';
37
+ import {createDOMException} from './dom-exception.ts';
38
+
39
+ export function isAttributeNode(node: Node): node is Attr {
40
+ return node.nodeType === NODE_TYPE_ATTRIBUTE;
41
+ }
17
42
 
18
43
  export function isCharacterData(node: Node): node is CharacterData {
19
44
  return DATA in node;
20
45
  }
21
46
 
22
47
  export function isTextNode(node: Node): node is Text {
23
- return node.nodeType === NodeType.TEXT_NODE;
48
+ return node.nodeType === NODE_TYPE_TEXT;
24
49
  }
25
50
 
26
51
  export function isCommentNode(node: Node): node is Comment {
27
- return node.nodeType === NodeType.COMMENT_NODE;
52
+ return node.nodeType === NODE_TYPE_COMMENT;
28
53
  }
29
54
 
30
55
  export function isElementNode(node: Node): node is Element {
31
- return node.nodeType === NodeType.ELEMENT_NODE;
56
+ return node.nodeType === NODE_TYPE_ELEMENT;
32
57
  }
33
58
 
34
59
  export function isDocumentFragmentNode(node: Node): node is DocumentFragment {
35
- return node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE;
60
+ return node.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT;
36
61
  }
37
62
 
38
63
  export function isParentNode(node: Node): node is ParentNode {
39
64
  return 'appendChild' in node;
40
65
  }
41
66
 
42
- export function cloneNode(
43
- node: Node,
44
- deep?: boolean,
45
- document: Document = node.ownerDocument,
46
- ): Node {
67
+ export function collectAdoptionSnapshot(root: Node) {
68
+ const nodes: Node[] = [];
69
+ const pendingRoots = [root];
70
+ const pending = new Set<Node>(pendingRoots);
71
+ const visited = new Set<Node>();
72
+ let treeNodes: Node[] | undefined;
73
+
74
+ while (pendingRoots.length > 0) {
75
+ const currentRoot = pendingRoots.pop()!;
76
+ pending.delete(currentRoot);
77
+ if (visited.has(currentRoot)) continue;
78
+
79
+ const currentTreeNodes: Node[] = [];
80
+ for (const node of selfAndDescendants(currentRoot)) {
81
+ if (visited.has(node)) continue;
82
+
83
+ visited.add(node);
84
+ nodes.push(node);
85
+ currentTreeNodes.push(node);
86
+ if (!isElementNode(node)) continue;
87
+
88
+ const attributes = node[ATTRIBUTES];
89
+ if (attributes) {
90
+ for (const attribute of attributes) {
91
+ if (visited.has(attribute)) continue;
92
+ visited.add(attribute);
93
+ nodes.push(attribute);
94
+ }
95
+ }
96
+
97
+ const content = (node as HTMLTemplateElement)[CONTENT];
98
+ if (content && !visited.has(content) && !pending.has(content)) {
99
+ pending.add(content);
100
+ pendingRoots.push(content);
101
+ }
102
+ }
103
+
104
+ treeNodes ??= currentTreeNodes;
105
+ }
106
+
107
+ return {nodes, treeNodes: treeNodes!};
108
+ }
109
+
110
+ export function adoptNodes(nodes: Node[], document: Document) {
111
+ for (const node of nodes) node[OWNER_DOCUMENT] = document;
112
+ }
113
+
114
+ function cloneAttribute(attribute: Attr, document: Document) {
115
+ const Attribute = attribute.constructor as new (
116
+ name: string,
117
+ value: string,
118
+ namespace?: string | null,
119
+ ) => Attr;
120
+ const cloned = new Attribute(
121
+ attribute.name,
122
+ attribute.value,
123
+ attribute.namespaceURI,
124
+ );
125
+ cloned[OWNER_DOCUMENT] = document;
126
+ return cloned;
127
+ }
128
+
129
+ function cloneNodeShallow(node: Node, document: Document): Node {
47
130
  if (isTextNode(node)) {
48
131
  return document.createTextNode(node.data);
49
132
  } else if (isCommentNode(node)) {
50
133
  return document.createComment(node.data);
134
+ } else if (isAttributeNode(node)) {
135
+ return cloneAttribute(node, document);
51
136
  } else if (isElementNode(node)) {
52
- const cloned = document.createElement(node.localName);
137
+ const cloned = document[CREATE_ELEMENT](
138
+ node[NAME],
139
+ node.namespaceURI,
140
+ node[PREFIX],
141
+ node.localName,
142
+ );
53
143
 
54
144
  if (node[ATTRIBUTES]) {
55
145
  for (let i = 0; i < node[ATTRIBUTES].length; i++) {
56
146
  const attribute = node[ATTRIBUTES].item(i)!;
57
- cloned.setAttributeNS(
58
- attribute.namespaceURI,
59
- attribute.name,
60
- attribute.value,
61
- );
62
- }
63
- }
64
-
65
- if (deep) {
66
- for (const child of node.childNodes) {
67
- cloned.appendChild(cloneNode(child, true, document));
147
+ cloned.attributes.setNamedItem(cloneAttribute(attribute, document));
68
148
  }
69
149
  }
70
150
 
71
151
  return cloned;
72
152
  } else if (isDocumentFragmentNode(node)) {
73
- const fragment = document.createDocumentFragment();
74
-
75
- if (deep) {
76
- for (const child of (node as DocumentFragment).childNodes) {
77
- fragment.appendChild(cloneNode(child, true, document));
78
- }
79
- }
80
-
81
- return fragment;
153
+ return document.createDocumentFragment();
82
154
  } else {
83
155
  const cloned = new (node.constructor as any)();
84
156
  cloned[OWNER_DOCUMENT] = document;
@@ -86,17 +158,174 @@ export function cloneNode(
86
158
  }
87
159
  }
88
160
 
161
+ type CloneParent = Element | DocumentFragment;
162
+
163
+ interface CloneFrame {
164
+ source: CloneParent;
165
+ destination: CloneParent;
166
+ appendTo?: ParentNode;
167
+ childIndex: number;
168
+ cloningContent: boolean;
169
+ contentSource?: DocumentFragment;
170
+ contentDestination?: DocumentFragment;
171
+ }
172
+
173
+ export function cloneNode(
174
+ node: Node,
175
+ deep?: boolean,
176
+ document: Document = node.ownerDocument,
177
+ ): Node {
178
+ if (node.nodeType === NODE_TYPE_DOCUMENT) {
179
+ throw createDOMException(
180
+ 'Cannot clone a document node',
181
+ 'NotSupportedError',
182
+ );
183
+ }
184
+
185
+ const cloned = cloneNodeShallow(node, document);
186
+
187
+ if (!deep || (!isElementNode(node) && !isDocumentFragmentNode(node))) {
188
+ return cloned;
189
+ }
190
+
191
+ const visited = new Set<Node>([node]);
192
+ const frames: CloneFrame[] = [
193
+ {
194
+ source: node,
195
+ destination: cloned as CloneParent,
196
+ childIndex: 0,
197
+ cloningContent: false,
198
+ },
199
+ ];
200
+
201
+ while (frames.length > 0) {
202
+ const frame = frames[frames.length - 1]!;
203
+ let sourceChild: Node | undefined;
204
+ let destinationParent: ParentNode;
205
+
206
+ if (!frame.cloningContent) {
207
+ sourceChild = frame.source.childNodes[frame.childIndex++];
208
+ destinationParent = frame.destination;
209
+
210
+ if (!sourceChild) {
211
+ frame.cloningContent = true;
212
+ frame.childIndex = 0;
213
+
214
+ if (
215
+ isElementNode(frame.source) &&
216
+ frame.source.namespaceURI === HTML_NAMESPACE &&
217
+ frame.source.localName === 'template'
218
+ ) {
219
+ const content = (frame.source as HTMLTemplateElement)[CONTENT];
220
+ if (content) {
221
+ if (visited.has(content)) throwCloneGraphError();
222
+ visited.add(content);
223
+ frame.contentSource = content;
224
+ frame.contentDestination = (
225
+ frame.destination as HTMLTemplateElement
226
+ ).content;
227
+ }
228
+ }
229
+
230
+ continue;
231
+ }
232
+ } else if (frame.contentSource && frame.contentDestination) {
233
+ sourceChild = frame.contentSource.childNodes[frame.childIndex++];
234
+ destinationParent = frame.contentDestination;
235
+ } else {
236
+ frames.pop();
237
+ if (frame.appendTo) frame.appendTo.appendChild(frame.destination);
238
+ continue;
239
+ }
240
+
241
+ if (!sourceChild) {
242
+ frames.pop();
243
+ if (frame.appendTo) frame.appendTo.appendChild(frame.destination);
244
+ continue;
245
+ }
246
+
247
+ if (visited.has(sourceChild)) throwCloneGraphError();
248
+ visited.add(sourceChild);
249
+ const clonedChild = cloneNodeShallow(sourceChild, document);
250
+
251
+ if (isElementNode(sourceChild) || isDocumentFragmentNode(sourceChild)) {
252
+ frames.push({
253
+ source: sourceChild,
254
+ destination: clonedChild as CloneParent,
255
+ appendTo: destinationParent,
256
+ childIndex: 0,
257
+ cloningContent: false,
258
+ });
259
+ } else {
260
+ destinationParent.appendChild(clonedChild);
261
+ }
262
+ }
263
+
264
+ return cloned;
265
+ }
266
+
267
+ function throwCloneGraphError(): never {
268
+ throw new Error('Cannot clone a cyclic or repeated node graph');
269
+ }
270
+
271
+ export function getElementById(within: ParentNode, elementId: string) {
272
+ const id = String(elementId);
273
+ if (id === '') return null;
274
+
275
+ return querySelector(within, [{type: MATCHER_ID, name: id}]);
276
+ }
277
+
278
+ export function getElementsByClassName(within: ParentNode, classNames: string) {
279
+ const names = [...new Set(splitOnASCIIWhitespace(String(classNames)))];
280
+
281
+ return querySelectorAll(
282
+ within,
283
+ names.map((name) => ({type: MATCHER_CLASS, name})),
284
+ );
285
+ }
286
+
287
+ export function getElementsByTagName(
288
+ within: ParentNode,
289
+ qualifiedName: string,
290
+ ) {
291
+ const name = String(qualifiedName);
292
+ const normalizedHtmlName = asciiLowercase(name);
293
+ const elements: Element[] = [];
294
+
295
+ for (const node of descendants(within)) {
296
+ if (!isElementNode(node)) continue;
297
+
298
+ if (
299
+ name === '*' ||
300
+ (node.namespaceURI === HTML_NAMESPACE
301
+ ? node[NAME] === normalizedHtmlName
302
+ : node[NAME] === name)
303
+ ) {
304
+ elements.push(node);
305
+ }
306
+ }
307
+
308
+ return elements;
309
+ }
310
+
89
311
  export function descendants(node: Node) {
90
312
  const nodes: Node[] = [];
91
- const walk = (node: Node) => {
92
- nodes.push(node);
93
- const child = node[CHILD];
94
- if (child) walk(child);
95
- const sibling = node[NEXT];
96
- if (sibling) walk(sibling);
97
- };
98
- const child = node[CHILD];
99
- if (child) walk(child);
313
+ const pendingSiblings: Node[] = [];
314
+ let current = node[CHILD];
315
+
316
+ while (current) {
317
+ nodes.push(current);
318
+
319
+ const child = current[CHILD];
320
+ const sibling = current[NEXT];
321
+ if (child) {
322
+ if (sibling) pendingSiblings.push(sibling);
323
+ current = child;
324
+ } else {
325
+ current = sibling ?? pendingSiblings.pop() ?? null;
326
+ }
327
+ }
328
+
100
329
  return nodes;
101
330
  }
102
331