@remote-dom/polyfill 1.5.0 → 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.
- package/CHANGELOG.md +94 -0
- package/README.md +51 -3
- package/build/cjs/Attr.cjs +33 -4
- package/build/cjs/CharacterData.cjs +10 -0
- package/build/cjs/ChildNode.cjs +79 -13
- package/build/cjs/Comment.cjs +1 -1
- package/build/cjs/CustomElementRegistry.cjs +27 -2
- package/build/cjs/CustomEvent.cjs +2 -2
- package/build/cjs/Document.cjs +56 -32
- package/build/cjs/DocumentFragment.cjs +6 -2
- package/build/cjs/Element.cjs +68 -8
- package/build/cjs/Event.cjs +29 -27
- package/build/cjs/EventTarget.cjs +128 -58
- package/build/cjs/HTMLTemplateElement.cjs +1 -0
- package/build/cjs/MutationObserver.cjs +167 -1
- package/build/cjs/NamedNodeMap.cjs +82 -45
- package/build/cjs/Node.cjs +47 -11
- package/build/cjs/NodeList.cjs +9 -1
- package/build/cjs/ParentNode.cjs +319 -77
- package/build/cjs/SVGElement.cjs +1 -1
- package/build/cjs/Text.cjs +1 -1
- package/build/cjs/Window.cjs +131 -38
- package/build/cjs/attribute-reactions.cjs +20 -0
- package/build/cjs/constants.cjs +48 -24
- package/build/cjs/custom-element-reactions.cjs +52 -0
- package/build/cjs/dom-exception.cjs +15 -0
- package/build/cjs/hook-effects.cjs +51 -0
- package/build/cjs/names.cjs +61 -0
- package/build/cjs/selectors.cjs +213 -122
- package/build/cjs/serialization.cjs +168 -65
- package/build/cjs/shared.cjs +177 -28
- package/build/esm/Attr.mjs +34 -5
- package/build/esm/CharacterData.mjs +10 -0
- package/build/esm/ChildNode.mjs +76 -15
- package/build/esm/Comment.mjs +2 -2
- package/build/esm/CustomElementRegistry.mjs +27 -2
- package/build/esm/CustomEvent.mjs +2 -2
- package/build/esm/Document.mjs +60 -35
- package/build/esm/DocumentFragment.mjs +7 -3
- package/build/esm/Element.mjs +69 -9
- package/build/esm/Event.mjs +27 -27
- package/build/esm/EventTarget.mjs +130 -60
- package/build/esm/HTMLTemplateElement.mjs +2 -1
- package/build/esm/MutationObserver.mjs +166 -2
- package/build/esm/NamedNodeMap.mjs +83 -46
- package/build/esm/Node.mjs +48 -12
- package/build/esm/NodeList.mjs +9 -1
- package/build/esm/ParentNode.mjs +322 -81
- package/build/esm/SVGElement.mjs +2 -2
- package/build/esm/Text.mjs +2 -2
- package/build/esm/Window.mjs +132 -39
- package/build/esm/attribute-reactions.mjs +16 -0
- package/build/esm/constants.mjs +28 -23
- package/build/esm/custom-element-reactions.mjs +47 -0
- package/build/esm/dom-exception.mjs +11 -0
- package/build/esm/hook-effects.mjs +46 -0
- package/build/esm/names.mjs +54 -0
- package/build/esm/selectors.mjs +202 -124
- package/build/esm/serialization.mjs +169 -66
- package/build/esm/shared.mjs +173 -30
- package/build/esnext/Attr.esnext +45 -5
- package/build/esnext/CharacterData.esnext +10 -0
- package/build/esnext/ChildNode.esnext +84 -15
- package/build/esnext/Comment.esnext +2 -2
- package/build/esnext/CustomElementRegistry.esnext +42 -0
- package/build/esnext/CustomEvent.esnext +2 -2
- package/build/esnext/Document.esnext +69 -32
- package/build/esnext/DocumentFragment.esnext +7 -3
- package/build/esnext/Element.esnext +78 -9
- package/build/esnext/Event.esnext +36 -37
- package/build/esnext/EventTarget.esnext +133 -61
- package/build/esnext/HTMLTemplateElement.esnext +3 -1
- package/build/esnext/MutationObserver.esnext +163 -2
- package/build/esnext/NamedNodeMap.esnext +110 -42
- package/build/esnext/Node.esnext +50 -12
- package/build/esnext/NodeList.esnext +1 -1
- package/build/esnext/ParentNode.esnext +352 -79
- package/build/esnext/SVGElement.esnext +2 -2
- package/build/esnext/Text.esnext +2 -2
- package/build/esnext/Window.esnext +120 -16
- package/build/esnext/attribute-reactions.esnext +21 -0
- package/build/esnext/constants.esnext +34 -21
- package/build/esnext/custom-element-reactions.esnext +49 -0
- package/build/esnext/dom-exception.esnext +11 -0
- package/build/esnext/hook-effects.esnext +46 -0
- package/build/esnext/names.esnext +61 -0
- package/build/esnext/selectors.esnext +202 -100
- package/build/esnext/serialization.esnext +171 -48
- package/build/esnext/shared.esnext +181 -34
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/typescript/Attr.d.ts +6 -4
- package/build/typescript/Attr.d.ts.map +1 -1
- package/build/typescript/CharacterData.d.ts.map +1 -1
- package/build/typescript/ChildNode.d.ts +5 -0
- package/build/typescript/ChildNode.d.ts.map +1 -1
- package/build/typescript/ClipboardEvent.d.ts.map +1 -1
- package/build/typescript/Comment.d.ts +2 -2
- package/build/typescript/Comment.d.ts.map +1 -1
- package/build/typescript/CustomElementRegistry.d.ts +1 -0
- package/build/typescript/CustomElementRegistry.d.ts.map +1 -1
- package/build/typescript/CustomEvent.d.ts.map +1 -1
- package/build/typescript/Document.d.ts +10 -5
- package/build/typescript/Document.d.ts.map +1 -1
- package/build/typescript/DocumentFragment.d.ts +2 -1
- package/build/typescript/DocumentFragment.d.ts.map +1 -1
- package/build/typescript/Element.d.ts +16 -7
- package/build/typescript/Element.d.ts.map +1 -1
- package/build/typescript/ErrorEvent.d.ts.map +1 -1
- package/build/typescript/Event.d.ts +9 -10
- package/build/typescript/Event.d.ts.map +1 -1
- package/build/typescript/EventTarget.d.ts +20 -3
- package/build/typescript/EventTarget.d.ts.map +1 -1
- package/build/typescript/FocusEvent.d.ts.map +1 -1
- package/build/typescript/HTMLTemplateElement.d.ts.map +1 -1
- package/build/typescript/MutationObserver.d.ts +42 -0
- package/build/typescript/MutationObserver.d.ts.map +1 -1
- package/build/typescript/NamedNodeMap.d.ts +5 -3
- package/build/typescript/NamedNodeMap.d.ts.map +1 -1
- package/build/typescript/Node.d.ts +4 -3
- package/build/typescript/Node.d.ts.map +1 -1
- package/build/typescript/NodeList.d.ts +3 -2
- package/build/typescript/NodeList.d.ts.map +1 -1
- package/build/typescript/ParentNode.d.ts +29 -10
- package/build/typescript/ParentNode.d.ts.map +1 -1
- package/build/typescript/PromiseRejectionEvent.d.ts.map +1 -1
- package/build/typescript/SVGElement.d.ts +1 -1
- package/build/typescript/SVGElement.d.ts.map +1 -1
- package/build/typescript/Text.d.ts +1 -1
- package/build/typescript/Text.d.ts.map +1 -1
- package/build/typescript/ToggleEvent.d.ts.map +1 -1
- package/build/typescript/Window.d.ts +5 -1
- package/build/typescript/Window.d.ts.map +1 -1
- package/build/typescript/attribute-reactions.d.ts +4 -0
- package/build/typescript/attribute-reactions.d.ts.map +1 -0
- package/build/typescript/constants.d.ts +27 -18
- package/build/typescript/constants.d.ts.map +1 -1
- package/build/typescript/custom-element-reactions.d.ts +6 -0
- package/build/typescript/custom-element-reactions.d.ts.map +1 -0
- package/build/typescript/dom-exception.d.ts +2 -0
- package/build/typescript/dom-exception.d.ts.map +1 -0
- package/build/typescript/extensions.d.ts +4 -0
- package/build/typescript/extensions.d.ts.map +1 -0
- package/build/typescript/hook-effects.d.ts +6 -0
- package/build/typescript/hook-effects.d.ts.map +1 -0
- package/build/typescript/hooks.d.ts +3 -3
- package/build/typescript/hooks.d.ts.map +1 -1
- package/build/typescript/index.d.ts +1 -0
- package/build/typescript/index.d.ts.map +1 -1
- package/build/typescript/names.d.ts +11 -0
- package/build/typescript/names.d.ts.map +1 -0
- package/build/typescript/selectors.d.ts +21 -22
- package/build/typescript/selectors.d.ts.map +1 -1
- package/build/typescript/serialization.d.ts.map +1 -1
- package/build/typescript/shared.d.ts +10 -0
- package/build/typescript/shared.d.ts.map +1 -1
- package/build/typescript/tests/ChildNode.test.d.ts +2 -0
- package/build/typescript/tests/ChildNode.test.d.ts.map +1 -0
- package/build/typescript/tests/MutationObserver.test.d.ts +2 -0
- package/build/typescript/tests/MutationObserver.test.d.ts.map +1 -0
- package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts +2 -0
- package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts.map +1 -0
- package/build/typescript/tests/attr-mutation.test.d.ts +2 -0
- package/build/typescript/tests/attr-mutation.test.d.ts.map +1 -0
- package/build/typescript/tests/child-node-before-after.test.d.ts +2 -0
- package/build/typescript/tests/child-node-before-after.test.d.ts.map +1 -0
- package/build/typescript/tests/child-node-replace-with.test.d.ts +2 -0
- package/build/typescript/tests/child-node-replace-with.test.d.ts.map +1 -0
- package/build/typescript/tests/clone-import-namespaces.test.d.ts +2 -0
- package/build/typescript/tests/clone-import-namespaces.test.d.ts.map +1 -0
- package/build/typescript/tests/constants.test.d.ts +2 -0
- package/build/typescript/tests/constants.test.d.ts.map +1 -0
- package/build/typescript/tests/create-element-namespaces.test.d.ts +2 -0
- package/build/typescript/tests/create-element-namespaces.test.d.ts.map +1 -0
- package/build/typescript/tests/create-text-node.test.d.ts +2 -0
- package/build/typescript/tests/create-text-node.test.d.ts.map +1 -0
- package/build/typescript/tests/custom-element-reactions.test.d.ts +2 -0
- package/build/typescript/tests/custom-element-reactions.test.d.ts.map +1 -0
- package/build/typescript/tests/custom-element-registry.test.d.ts +2 -0
- package/build/typescript/tests/custom-element-registry.test.d.ts.map +1 -0
- package/build/typescript/tests/custom-event.test.d.ts +2 -0
- package/build/typescript/tests/custom-event.test.d.ts.map +1 -0
- package/build/typescript/tests/document-clone-import-contract.test.d.ts +2 -0
- package/build/typescript/tests/document-clone-import-contract.test.d.ts.map +1 -0
- package/build/typescript/tests/document.test.d.ts +2 -0
- package/build/typescript/tests/document.test.d.ts.map +1 -0
- package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts +2 -0
- package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts.map +1 -0
- package/build/typescript/tests/event-dispatch.test.d.ts +2 -0
- package/build/typescript/tests/event-dispatch.test.d.ts.map +1 -0
- package/build/typescript/tests/event-target.test.d.ts +2 -0
- package/build/typescript/tests/event-target.test.d.ts.map +1 -0
- package/build/typescript/tests/event.test.d.ts +2 -0
- package/build/typescript/tests/event.test.d.ts.map +1 -0
- package/build/typescript/tests/extensions.test.d.ts +2 -0
- package/build/typescript/tests/extensions.test.d.ts.map +1 -0
- package/build/typescript/tests/get-elements-by-tag-name.test.d.ts +2 -0
- package/build/typescript/tests/get-elements-by-tag-name.test.d.ts.map +1 -0
- package/build/typescript/tests/getElementsByClassName.test.d.ts +2 -0
- package/build/typescript/tests/getElementsByClassName.test.d.ts.map +1 -0
- package/build/typescript/tests/hooks.test.d.ts +2 -0
- package/build/typescript/tests/hooks.test.d.ts.map +1 -0
- package/build/typescript/tests/inner-html-parsing.test.d.ts +2 -0
- package/build/typescript/tests/inner-html-parsing.test.d.ts.map +1 -0
- package/build/typescript/tests/is-default-namespace.test.d.ts +2 -0
- package/build/typescript/tests/is-default-namespace.test.d.ts.map +1 -0
- package/build/typescript/tests/name-and-namespace-normalization.test.d.ts +2 -0
- package/build/typescript/tests/name-and-namespace-normalization.test.d.ts.map +1 -0
- package/build/typescript/tests/named-node-map.test.d.ts +2 -0
- package/build/typescript/tests/named-node-map.test.d.ts.map +1 -0
- package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts +2 -0
- package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts.map +1 -0
- package/build/typescript/tests/node.test.d.ts +2 -0
- package/build/typescript/tests/node.test.d.ts.map +1 -0
- package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts +2 -0
- package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts.map +1 -0
- package/build/typescript/tests/parent-node-mutation-validation.test.d.ts +2 -0
- package/build/typescript/tests/parent-node-mutation-validation.test.d.ts.map +1 -0
- package/build/typescript/tests/parent-node.test.d.ts +2 -0
- package/build/typescript/tests/parent-node.test.d.ts.map +1 -0
- package/build/typescript/tests/serialization-template.test.d.ts +2 -0
- package/build/typescript/tests/serialization-template.test.d.ts.map +1 -0
- package/build/typescript/tests/slot-attribute.test.d.ts +2 -0
- package/build/typescript/tests/slot-attribute.test.d.ts.map +1 -0
- package/build/typescript/tests/text-content.test.d.ts +2 -0
- package/build/typescript/tests/text-content.test.d.ts.map +1 -0
- package/build/typescript/tests/toggle-attribute.test.d.ts +2 -0
- package/build/typescript/tests/toggle-attribute.test.d.ts.map +1 -0
- package/build/typescript/tests/wide-tree-traversal.test.d.ts +2 -0
- package/build/typescript/tests/wide-tree-traversal.test.d.ts.map +1 -0
- package/build/typescript/tests/window-set-global.test.d.ts +2 -0
- package/build/typescript/tests/window-set-global.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/source/Attr.ts +60 -8
- package/source/CharacterData.ts +13 -0
- package/source/ChildNode.ts +107 -14
- package/source/Comment.ts +2 -2
- package/source/CustomElementRegistry.ts +56 -0
- package/source/CustomEvent.ts +2 -2
- package/source/Document.ts +104 -38
- package/source/DocumentFragment.ts +15 -3
- package/source/Element.ts +129 -14
- package/source/Event.ts +34 -32
- package/source/EventTarget.ts +203 -79
- package/source/HTMLTemplateElement.ts +2 -1
- package/source/MutationObserver.ts +279 -1
- package/source/NamedNodeMap.ts +141 -60
- package/source/Node.ts +70 -14
- package/source/NodeList.ts +13 -3
- package/source/ParentNode.ts +500 -88
- package/source/SVGElement.ts +2 -2
- package/source/Text.ts +2 -2
- package/source/Window.ts +156 -17
- package/source/attribute-reactions.ts +32 -0
- package/source/constants.ts +40 -17
- package/source/custom-element-reactions.ts +64 -0
- package/source/dom-exception.ts +14 -0
- package/source/extensions.ts +4 -0
- package/source/hook-effects.ts +56 -0
- package/source/hooks.ts +11 -3
- package/source/index.ts +1 -0
- package/source/names.ts +94 -0
- package/source/selectors.ts +301 -138
- package/source/serialization.ts +224 -66
- package/source/shared.ts +269 -40
- package/source/tests/ChildNode.test.ts +178 -0
- package/source/tests/MutationObserver.test.ts +303 -0
- package/source/tests/adopt-inserted-subtrees.test.ts +584 -0
- package/source/tests/attr-mutation.test.ts +112 -0
- package/source/tests/child-node-before-after.test.ts +421 -0
- package/source/tests/child-node-replace-with.test.ts +251 -0
- package/source/tests/clone-import-namespaces.test.ts +365 -0
- package/source/tests/constants.test.ts +46 -0
- package/source/tests/create-element-namespaces.test.ts +142 -0
- package/source/tests/create-text-node.test.ts +38 -0
- package/source/tests/custom-element-reactions.test.ts +600 -0
- package/source/tests/custom-element-registry.test.ts +137 -0
- package/source/tests/custom-event.test.ts +26 -0
- package/source/tests/document-clone-import-contract.test.ts +77 -0
- package/source/tests/document.test.ts +122 -0
- package/source/tests/dom-mutation-selector-errors.test.ts +484 -0
- package/source/tests/event-dispatch.test.ts +270 -0
- package/source/tests/event-target.test.ts +208 -0
- package/source/tests/event.test.ts +98 -0
- package/source/tests/extensions.test.ts +159 -0
- package/source/tests/get-elements-by-tag-name.test.ts +79 -0
- package/source/tests/getElementsByClassName.test.ts +109 -0
- package/source/tests/hooks.test.ts +43 -0
- package/source/tests/inner-html-parsing.test.ts +461 -0
- package/source/tests/is-default-namespace.test.ts +111 -0
- package/source/tests/name-and-namespace-normalization.test.ts +319 -0
- package/source/tests/named-node-map.test.ts +122 -0
- package/source/tests/namespaced-attribute-reactions.test.ts +141 -0
- package/source/tests/node.test.ts +56 -0
- package/source/tests/parent-node-mutation-return-values.test.ts +63 -0
- package/source/tests/parent-node-mutation-validation.test.ts +362 -0
- package/source/tests/parent-node.test.ts +169 -0
- package/source/tests/selectors.test.ts +533 -22
- package/source/tests/serialization-template.test.ts +232 -0
- package/source/tests/slot-attribute.test.ts +133 -0
- package/source/tests/text-content.test.ts +440 -0
- package/source/tests/toggle-attribute.test.ts +113 -0
- package/source/tests/wide-tree-traversal.test.ts +699 -0
- package/source/tests/window-set-global.test.ts +321 -0
- package/tsconfig.json +1 -1
package/source/serialization.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
25
|
-
|
|
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
|
|
28
|
-
|
|
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:
|
|
110
|
+
const stack: {element: Element; target: ParentNode}[] = [];
|
|
34
111
|
let parent: ParentNode = root;
|
|
35
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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, '&')
|
|
216
|
+
.replace(/"/g, '"');
|
|
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, '&')
|
|
239
|
+
.replace(/"/g, '"')
|
|
240
|
+
.replace(/</g, '<')
|
|
241
|
+
.replace(/>/g, '>'),
|
|
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
|
-
|
|
251
|
+
break;
|
|
86
252
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const text = node as Text;
|
|
99
|
-
return text[DATA].replace(/&/g, '&')
|
|
100
|
-
.replace(/"/g, '"')
|
|
101
|
-
.replace(/</g, '<')
|
|
102
|
-
.replace(/>/g, '>');
|
|
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
|
-
|
|
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
|
-
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|