@remote-dom/polyfill 0.0.1

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 (168) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +45 -0
  3. package/build/cjs/Attr.cjs +54 -0
  4. package/build/cjs/CharacterData.cjs +29 -0
  5. package/build/cjs/ChildNode.cjs +45 -0
  6. package/build/cjs/Comment.cjs +11 -0
  7. package/build/cjs/CustomElementRegistry.cjs +35 -0
  8. package/build/cjs/CustomEvent.cjs +28 -0
  9. package/build/cjs/Document.cjs +94 -0
  10. package/build/cjs/DocumentFragment.cjs +12 -0
  11. package/build/cjs/Element.cjs +81 -0
  12. package/build/cjs/Event.cjs +113 -0
  13. package/build/cjs/EventTarget.cjs +101 -0
  14. package/build/cjs/HTMLElement.cjs +7 -0
  15. package/build/cjs/HTMLTemplateElement.cjs +26 -0
  16. package/build/cjs/MutationObserver.cjs +7 -0
  17. package/build/cjs/NamedNodeMap.cjs +115 -0
  18. package/build/cjs/Node.cjs +102 -0
  19. package/build/cjs/NodeList.cjs +9 -0
  20. package/build/cjs/ParentNode.cjs +130 -0
  21. package/build/cjs/SVGElement.cjs +19 -0
  22. package/build/cjs/Text.cjs +11 -0
  23. package/build/cjs/Window.cjs +69 -0
  24. package/build/cjs/constants.cjs +57 -0
  25. package/build/cjs/hooks.cjs +5 -0
  26. package/build/cjs/index.cjs +10 -0
  27. package/build/cjs/selectors.cjs +203 -0
  28. package/build/cjs/serialization.cjs +96 -0
  29. package/build/cjs/shared.cjs +63 -0
  30. package/build/esm/Attr.mjs +52 -0
  31. package/build/esm/CharacterData.mjs +27 -0
  32. package/build/esm/ChildNode.mjs +42 -0
  33. package/build/esm/Comment.mjs +9 -0
  34. package/build/esm/CustomElementRegistry.mjs +33 -0
  35. package/build/esm/CustomEvent.mjs +26 -0
  36. package/build/esm/Document.mjs +89 -0
  37. package/build/esm/DocumentFragment.mjs +10 -0
  38. package/build/esm/Element.mjs +95 -0
  39. package/build/esm/Event.mjs +108 -0
  40. package/build/esm/EventTarget.mjs +99 -0
  41. package/build/esm/HTMLElement.mjs +5 -0
  42. package/build/esm/HTMLTemplateElement.mjs +24 -0
  43. package/build/esm/MutationObserver.mjs +5 -0
  44. package/build/esm/NamedNodeMap.mjs +121 -0
  45. package/build/esm/Node.mjs +100 -0
  46. package/build/esm/NodeList.mjs +7 -0
  47. package/build/esm/ParentNode.mjs +128 -0
  48. package/build/esm/SVGElement.mjs +17 -0
  49. package/build/esm/Text.mjs +9 -0
  50. package/build/esm/Window.mjs +66 -0
  51. package/build/esm/constants.mjs +39 -0
  52. package/build/esm/hooks.mjs +3 -0
  53. package/build/esm/index.mjs +2 -0
  54. package/build/esm/selectors.mjs +197 -0
  55. package/build/esm/serialization.mjs +92 -0
  56. package/build/esm/shared.mjs +55 -0
  57. package/build/esnext/Attr.esnext +57 -0
  58. package/build/esnext/CharacterData.esnext +27 -0
  59. package/build/esnext/ChildNode.esnext +47 -0
  60. package/build/esnext/Comment.esnext +9 -0
  61. package/build/esnext/CustomElementRegistry.esnext +34 -0
  62. package/build/esnext/CustomEvent.esnext +25 -0
  63. package/build/esnext/Document.esnext +86 -0
  64. package/build/esnext/DocumentFragment.esnext +10 -0
  65. package/build/esnext/Element.esnext +99 -0
  66. package/build/esnext/Event.esnext +109 -0
  67. package/build/esnext/EventTarget.esnext +99 -0
  68. package/build/esnext/HTMLElement.esnext +6 -0
  69. package/build/esnext/HTMLTemplateElement.esnext +26 -0
  70. package/build/esnext/MutationObserver.esnext +5 -0
  71. package/build/esnext/NamedNodeMap.esnext +131 -0
  72. package/build/esnext/Node.esnext +116 -0
  73. package/build/esnext/NodeList.esnext +7 -0
  74. package/build/esnext/ParentNode.esnext +138 -0
  75. package/build/esnext/SVGElement.esnext +17 -0
  76. package/build/esnext/Text.esnext +9 -0
  77. package/build/esnext/Window.esnext +59 -0
  78. package/build/esnext/constants.esnext +37 -0
  79. package/build/esnext/hooks.esnext +3 -0
  80. package/build/esnext/index.esnext +2 -0
  81. package/build/esnext/selectors.esnext +169 -0
  82. package/build/esnext/serialization.esnext +76 -0
  83. package/build/esnext/shared.esnext +59 -0
  84. package/build/tsconfig.tsbuildinfo +1 -0
  85. package/build/typescript/Attr.d.ts +24 -0
  86. package/build/typescript/Attr.d.ts.map +1 -0
  87. package/build/typescript/CharacterData.d.ts +10 -0
  88. package/build/typescript/CharacterData.d.ts.map +1 -0
  89. package/build/typescript/ChildNode.d.ts +10 -0
  90. package/build/typescript/ChildNode.d.ts.map +1 -0
  91. package/build/typescript/Comment.d.ts +7 -0
  92. package/build/typescript/Comment.d.ts.map +1 -0
  93. package/build/typescript/CustomElementRegistry.d.ts +9 -0
  94. package/build/typescript/CustomElementRegistry.d.ts.map +1 -0
  95. package/build/typescript/CustomEvent.d.ts +17 -0
  96. package/build/typescript/CustomEvent.d.ts.map +1 -0
  97. package/build/typescript/Document.d.ts +27 -0
  98. package/build/typescript/Document.d.ts.map +1 -0
  99. package/build/typescript/DocumentFragment.d.ts +8 -0
  100. package/build/typescript/DocumentFragment.d.ts.map +1 -0
  101. package/build/typescript/Element.d.ts +32 -0
  102. package/build/typescript/Element.d.ts.map +1 -0
  103. package/build/typescript/Event.d.ts +47 -0
  104. package/build/typescript/Event.d.ts.map +1 -0
  105. package/build/typescript/EventTarget.d.ts +12 -0
  106. package/build/typescript/EventTarget.d.ts.map +1 -0
  107. package/build/typescript/HTMLElement.d.ts +4 -0
  108. package/build/typescript/HTMLElement.d.ts.map +1 -0
  109. package/build/typescript/HTMLTemplateElement.d.ts +11 -0
  110. package/build/typescript/HTMLTemplateElement.d.ts.map +1 -0
  111. package/build/typescript/MutationObserver.d.ts +3 -0
  112. package/build/typescript/MutationObserver.d.ts.map +1 -0
  113. package/build/typescript/NamedNodeMap.d.ts +18 -0
  114. package/build/typescript/NamedNodeMap.d.ts.map +1 -0
  115. package/build/typescript/Node.d.ts +37 -0
  116. package/build/typescript/Node.d.ts.map +1 -0
  117. package/build/typescript/NodeList.d.ts +4 -0
  118. package/build/typescript/NodeList.d.ts.map +1 -0
  119. package/build/typescript/ParentNode.d.ts +18 -0
  120. package/build/typescript/ParentNode.d.ts.map +1 -0
  121. package/build/typescript/SVGElement.d.ts +7 -0
  122. package/build/typescript/SVGElement.d.ts.map +1 -0
  123. package/build/typescript/Text.d.ts +7 -0
  124. package/build/typescript/Text.d.ts.map +1 -0
  125. package/build/typescript/Window.d.ts +45 -0
  126. package/build/typescript/Window.d.ts.map +1 -0
  127. package/build/typescript/constants.d.ts +35 -0
  128. package/build/typescript/constants.d.ts.map +1 -0
  129. package/build/typescript/hooks.d.ts +13 -0
  130. package/build/typescript/hooks.d.ts.map +1 -0
  131. package/build/typescript/index.d.ts +3 -0
  132. package/build/typescript/index.d.ts.map +1 -0
  133. package/build/typescript/selectors.d.ts +32 -0
  134. package/build/typescript/selectors.d.ts.map +1 -0
  135. package/build/typescript/serialization.d.ts +6 -0
  136. package/build/typescript/serialization.d.ts.map +1 -0
  137. package/build/typescript/shared.d.ts +16 -0
  138. package/build/typescript/shared.d.ts.map +1 -0
  139. package/package.json +35 -0
  140. package/rollup.config.js +3 -0
  141. package/source/Attr.ts +70 -0
  142. package/source/CharacterData.ts +29 -0
  143. package/source/ChildNode.ts +45 -0
  144. package/source/Comment.ts +7 -0
  145. package/source/CustomElementRegistry.ts +52 -0
  146. package/source/CustomEvent.ts +30 -0
  147. package/source/Document.ts +112 -0
  148. package/source/DocumentFragment.ts +8 -0
  149. package/source/Element.ts +119 -0
  150. package/source/Event.ts +142 -0
  151. package/source/EventTarget.ts +139 -0
  152. package/source/HTMLElement.ts +3 -0
  153. package/source/HTMLTemplateElement.ts +28 -0
  154. package/source/MutationObserver.ts +3 -0
  155. package/source/NamedNodeMap.ts +156 -0
  156. package/source/Node.ts +139 -0
  157. package/source/NodeList.ts +5 -0
  158. package/source/ParentNode.ts +158 -0
  159. package/source/SVGElement.ts +16 -0
  160. package/source/Text.ts +7 -0
  161. package/source/Window.ts +64 -0
  162. package/source/constants.ts +37 -0
  163. package/source/hooks.ts +28 -0
  164. package/source/index.ts +2 -0
  165. package/source/selectors.ts +230 -0
  166. package/source/serialization.ts +110 -0
  167. package/source/shared.ts +80 -0
  168. package/tsconfig.json +10 -0
@@ -0,0 +1,158 @@
1
+ import {hooks} from './hooks.ts';
2
+ import {
3
+ CHILD,
4
+ NEXT,
5
+ PREV,
6
+ PARENT,
7
+ OWNER_DOCUMENT,
8
+ NodeType,
9
+ } from './constants.ts';
10
+ import type {Node} from './Node.ts';
11
+ import {ChildNode, toNode} from './ChildNode.ts';
12
+ import {NodeList} from './NodeList.ts';
13
+ import {querySelectorAll, querySelector} from './selectors.ts';
14
+
15
+ export class ParentNode extends ChildNode {
16
+ readonly childNodes = new NodeList();
17
+ readonly children = new NodeList();
18
+
19
+ appendChild(child: Node) {
20
+ this.insertInto(child, null);
21
+ }
22
+
23
+ insertBefore(child: Node, ref?: Node | null) {
24
+ this.insertInto(child, ref || null);
25
+ }
26
+
27
+ append(...nodes: (Node | string)[]) {
28
+ for (const child of nodes) {
29
+ if (child == null) continue;
30
+ this.appendChild(toNode(this, child));
31
+ }
32
+ }
33
+
34
+ prepend(...nodes: (Node | string)[]) {
35
+ const before = this.firstChild;
36
+ for (const child of nodes) {
37
+ if (child == null) continue;
38
+ this.insertBefore(toNode(this, child), before);
39
+ }
40
+ }
41
+
42
+ replaceChildren(...nodes: (Node | string)[]) {
43
+ let child;
44
+ while ((child = this.firstChild)) {
45
+ this.removeChild(child);
46
+ }
47
+ this.append(...nodes);
48
+ }
49
+
50
+ removeChild(child: Node) {
51
+ if (child.parentNode !== this) throw Error(`not a child of this node`);
52
+ const prev = child[PREV];
53
+ const next = child[NEXT];
54
+ if (prev) prev[NEXT] = next;
55
+ else this[CHILD] = next;
56
+ if (next) next[PREV] = prev;
57
+
58
+ const childNodes = this.childNodes;
59
+
60
+ const childNodesIndex = childNodes.indexOf(child);
61
+
62
+ childNodes.splice(childNodesIndex, 1);
63
+
64
+ if (child.nodeType === 1) {
65
+ const children = this.children;
66
+ children.splice(children.indexOf(child), 1);
67
+ }
68
+
69
+ hooks.removeChild?.(this as any, child as any, childNodesIndex);
70
+ }
71
+
72
+ replaceChild(newChild: Node, oldChild: Node) {
73
+ if (oldChild.parentNode !== this) {
74
+ throw Error('reference node is not a child of this parent');
75
+ }
76
+ const next = oldChild[NEXT];
77
+ this.removeChild(oldChild);
78
+ this.insertInto(newChild, next);
79
+ }
80
+
81
+ querySelectorAll(selector: string) {
82
+ return querySelectorAll(this, selector);
83
+ }
84
+
85
+ querySelector(selector: string) {
86
+ return querySelector(this, selector);
87
+ }
88
+
89
+ private insertInto(child: Node, before: Node | null) {
90
+ // append the children of a DocumentFragment:
91
+ if (child.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE) {
92
+ let node = child[CHILD];
93
+ while (node) {
94
+ const next = node[NEXT];
95
+ this.insertInto(node, before);
96
+ node = next;
97
+ }
98
+ return;
99
+ }
100
+
101
+ if (child.parentNode !== null) {
102
+ child.parentNode.removeChild(child);
103
+ }
104
+
105
+ if (before) {
106
+ if (before.parentNode !== this) {
107
+ throw Error('reference node is not a child of this parent');
108
+ }
109
+ child[NEXT] = before;
110
+ child[PREV] = before[PREV];
111
+ if (before[PREV] === null) this[CHILD] = child;
112
+ before[PREV] = child;
113
+ } else {
114
+ child[NEXT] = null;
115
+ let last = this[CHILD];
116
+ if (last) {
117
+ let next;
118
+ while ((next = last[NEXT])) last = next;
119
+ last[NEXT] = child;
120
+ child[PREV] = last;
121
+ } else {
122
+ this[CHILD] = child;
123
+ child[PREV] = null;
124
+ }
125
+ }
126
+
127
+ const ownerDocument = this[OWNER_DOCUMENT];
128
+ const isElement = child.nodeType === NodeType.ELEMENT_NODE;
129
+
130
+ child[PARENT] = this;
131
+ child[OWNER_DOCUMENT] = ownerDocument;
132
+
133
+ const childNodes = this.childNodes;
134
+ let insertIndex: number;
135
+
136
+ if (before) {
137
+ insertIndex = childNodes.indexOf(before);
138
+ childNodes.splice(insertIndex, 0, child);
139
+
140
+ if (isElement) {
141
+ const children = this.children;
142
+ let ref: Node | null = before;
143
+ while (ref && ref.nodeType !== 1) ref = ref[NEXT];
144
+ if (ref) {
145
+ children.splice(children.indexOf(ref), 0, child);
146
+ } else {
147
+ children.push(child);
148
+ }
149
+ }
150
+ } else {
151
+ insertIndex = childNodes.length;
152
+ childNodes.push(child);
153
+ if (isElement) this.children.push(child);
154
+ }
155
+
156
+ hooks.insertChild?.(this as any, child as any, insertIndex);
157
+ }
158
+ }
@@ -0,0 +1,16 @@
1
+ import {NS, NamespaceURI} from './constants.ts';
2
+ import {Element} from './Element.ts';
3
+
4
+ export class SVGElement extends Element {
5
+ [NS] = NamespaceURI.SVG;
6
+
7
+ get ownerSVGElement() {
8
+ let root: SVGElement | null = null;
9
+ let parent = this.parentNode;
10
+ while (parent instanceof SVGElement) {
11
+ root = parent;
12
+ parent = parent.parentNode;
13
+ }
14
+ return root;
15
+ }
16
+ }
package/source/Text.ts ADDED
@@ -0,0 +1,7 @@
1
+ import {NAME, NodeType} from './constants.ts';
2
+ import {CharacterData} from './CharacterData.ts';
3
+
4
+ export class Text extends CharacterData {
5
+ nodeType = NodeType.TEXT_NODE;
6
+ [NAME] = '#text';
7
+ }
@@ -0,0 +1,64 @@
1
+ import {Document} from './Document.ts';
2
+ import {Event} from './Event.ts';
3
+ import {EventTarget} from './EventTarget.ts';
4
+ import {CustomEvent} from './CustomEvent.ts';
5
+ import {Node} from './Node.ts';
6
+ import {ParentNode} from './ParentNode.ts';
7
+ import {ChildNode} from './ChildNode.ts';
8
+ import {Element} from './Element.ts';
9
+ import {HTMLElement} from './HTMLElement.ts';
10
+ import {SVGElement} from './SVGElement.ts';
11
+ import {CharacterData} from './CharacterData.ts';
12
+ import {Text} from './Text.ts';
13
+ import {Comment} from './Comment.ts';
14
+ import {DocumentFragment} from './DocumentFragment.ts';
15
+ import {HTMLTemplateElement} from './HTMLTemplateElement.ts';
16
+ import {CustomElementRegistryImplementation} from './CustomElementRegistry.ts';
17
+ import {MutationObserver} from './MutationObserver.ts';
18
+
19
+ export class Window extends EventTarget {
20
+ name = '';
21
+ parent = this;
22
+ self = this;
23
+ top = this;
24
+ document = new Document(this);
25
+ customElements = new CustomElementRegistryImplementation();
26
+ location = globalThis.location;
27
+ navigator = globalThis.navigator;
28
+ Event = Event;
29
+ EventTarget = EventTarget;
30
+ CustomEvent = CustomEvent;
31
+ Node = Node;
32
+ ParentNode = ParentNode;
33
+ ChildNode = ChildNode;
34
+ DocumentFragment = DocumentFragment;
35
+ Document = Document;
36
+ CharacterData = CharacterData;
37
+ Comment = Comment;
38
+ Text = Text;
39
+ Element = Element;
40
+ HTMLElement = HTMLElement;
41
+ SVGElement = SVGElement;
42
+ HTMLTemplateElement = HTMLTemplateElement;
43
+ MutationObserver = MutationObserver;
44
+
45
+ static setGlobal(window: Window) {
46
+ const properties = Object.getOwnPropertyDescriptors(window);
47
+
48
+ delete (properties as any).self;
49
+
50
+ Object.defineProperties(globalThis, {
51
+ ...properties,
52
+ window: {value: window},
53
+ });
54
+
55
+ if (typeof self === 'undefined') {
56
+ Object.defineProperty(globalThis, 'self', {value: window});
57
+ } else {
58
+ // There can already be a `self`, like when polyfilling the DOM
59
+ // in a Web Worker. In those cases, just mirror all the `Window`
60
+ // properties onto `self`, rather than wholly redefining it.
61
+ Object.defineProperties(self, properties);
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,37 @@
1
+ export const NAME = Symbol('name');
2
+ export const VALUE = Symbol('value');
3
+ export const NS = Symbol('ns');
4
+ export const OWNER_ELEMENT = Symbol('owner');
5
+ export const OWNER_DOCUMENT = Symbol('owner_document');
6
+ export const ATTRIBUTES = Symbol('attributes');
7
+ export const PREV = Symbol('prev');
8
+ export const NEXT = Symbol('next');
9
+ export const CHILD = Symbol('child');
10
+ export const PARENT = Symbol('parent');
11
+ export const DATA = Symbol('data');
12
+ export const USER_PROPERTIES = Symbol('user_properties');
13
+ export const LISTENERS = Symbol('listeners');
14
+ export const IS_TRUSTED = Symbol('isTrusted');
15
+ export const CONTENT = Symbol('content');
16
+ export const SLOT = Symbol('slot');
17
+
18
+ // @TODO remove explicit values
19
+ export const enum NodeType {
20
+ NODE = 0,
21
+ ELEMENT_NODE = 1,
22
+ ATTRIBUTE_NODE = 2,
23
+ TEXT_NODE = 3,
24
+ CDATA_SECTION_NODE = 4,
25
+ ENTITY_REFERENCE_NODE = 5,
26
+ ENTITY_NODE = 6,
27
+ PROCESSING_INSTRUCTION_NODE = 7,
28
+ COMMENT_NODE = 8,
29
+ DOCUMENT_NODE = 9,
30
+ DOCUMENT_TYPE_NODE = 10,
31
+ DOCUMENT_FRAGMENT_NODE = 11,
32
+ }
33
+
34
+ export const enum NamespaceURI {
35
+ XHTML = 'http://www.w3.org/1999/xhtml',
36
+ SVG = 'http://www.w3.org/2000/svg',
37
+ }
@@ -0,0 +1,28 @@
1
+ export interface Hooks {
2
+ createElement(element: Element, ns?: string | null): void;
3
+ setAttribute(
4
+ element: Element,
5
+ name: string,
6
+ value: string,
7
+ ns?: string | null,
8
+ ): void;
9
+ removeAttribute(element: Element, name: string, ns?: string | null): void;
10
+ createText(text: Text, data: string): void;
11
+ setText(text: Text, data: string): void;
12
+ insertChild(parent: Element, node: Element | Text, index: number): void;
13
+ removeChild(parent: Element, node: Element | Text, index: number): void;
14
+ addEventListener(
15
+ element: EventTarget,
16
+ type: string,
17
+ listener: EventListenerOrEventListenerObject | null,
18
+ options?: boolean | AddEventListenerOptions,
19
+ ): void;
20
+ removeEventListener(
21
+ element: EventTarget,
22
+ type: string,
23
+ listener: EventListenerOrEventListenerObject | null,
24
+ options?: boolean | EventListenerOptions,
25
+ ): void;
26
+ }
27
+
28
+ export const hooks: Partial<Hooks> = {};
@@ -0,0 +1,2 @@
1
+ export {hooks, type Hooks} from './hooks.ts';
2
+ export {Window} from './Window.ts';
@@ -0,0 +1,230 @@
1
+ import {CHILD, NEXT, PARENT, PREV} from './constants.ts';
2
+ import {isElementNode} from './shared.ts';
3
+
4
+ import type {Node} from './Node.ts';
5
+ import type {Element} from './Element.ts';
6
+ import type {ParentNode} from './ParentNode.ts';
7
+
8
+ const enum Combinator {
9
+ Descendant,
10
+ Child,
11
+ Sibling,
12
+ Adjacent,
13
+ Inner,
14
+ }
15
+
16
+ const enum MatcherType {
17
+ Unknown,
18
+ Element,
19
+ Id,
20
+ Class,
21
+ Attribute,
22
+ Pseudo,
23
+ Function,
24
+ }
25
+
26
+ interface Part {
27
+ combinator: Combinator;
28
+ matchers: Matcher[];
29
+ }
30
+
31
+ interface Matcher {
32
+ type: MatcherType;
33
+ name: string;
34
+ value?: string;
35
+ }
36
+
37
+ const ELEMENT_SELECTOR_TEST = /[a-z]/;
38
+
39
+ export function querySelector(within: ParentNode, selector: string) {
40
+ const parts = parseSelector(selector);
41
+ let result: Element | null = null;
42
+
43
+ const child = within[CHILD];
44
+ if (child && parts[0]!.matchers.length) {
45
+ walkNodesForSelector(child, parts, (node) => {
46
+ result = node;
47
+ return false;
48
+ });
49
+ }
50
+ return result;
51
+ }
52
+
53
+ export function querySelectorAll(within: ParentNode, selector: string) {
54
+ const parts = parseSelector(selector);
55
+ const results: Element[] = [];
56
+
57
+ const child = within[CHILD];
58
+ if (child && parts[0]!.matchers.length) {
59
+ walkNodesForSelector(child, parts, (node) => {
60
+ results.push(node);
61
+ });
62
+ }
63
+ return results;
64
+ }
65
+
66
+ export function parseSelector(selector: string) {
67
+ let part: Part = {combinator: Combinator.Inner, matchers: []};
68
+ const parts = [part];
69
+ const tokenizer =
70
+ /\s*?([>\s+~]?)\s*?(?:(?:\[\s*([^\]=]+)(?:=(['"])(.*?)\3)?\s*\])|([#.]?)([^\s#.[>:]+)|:(\w+)(?:\((.*?)\))?)/gi;
71
+ let token;
72
+ while ((token = tokenizer.exec(selector))) {
73
+ // [1]: ancestor/parent/sibling/adjacent
74
+ // [2]: attribute name
75
+ // [4]: attribute value
76
+ // [5]: id/class sigil
77
+ // [6]: id/class name
78
+ // [7]: :pseudo/:function() name
79
+ // [8]: :function(argument) value
80
+ if (token[1]) {
81
+ // Update the combinator on the (now parent) Part:
82
+ if (token[1] === '>') part.combinator = Combinator.Child;
83
+ else if (token[1] === '+') part.combinator = Combinator.Adjacent;
84
+ else if (token[1] === '~') part.combinator = Combinator.Sibling;
85
+ else part.combinator = Combinator.Descendant;
86
+ // Add a new Part for the next selector parts:
87
+ part = {combinator: Combinator.Inner, matchers: []};
88
+ parts.push(part);
89
+ }
90
+
91
+ let type = MatcherType.Unknown;
92
+ if (token[2]) {
93
+ type = MatcherType.Attribute;
94
+ } else if (token[5]) {
95
+ type = token[5] === '#' ? MatcherType.Id : MatcherType.Class;
96
+ } else if (token[7]) {
97
+ type = token[8] == null ? MatcherType.Pseudo : MatcherType.Function;
98
+ } else if (token[6] && ELEMENT_SELECTOR_TEST.test(token[6])) {
99
+ type = MatcherType.Element;
100
+ }
101
+ part.matchers.push({
102
+ type,
103
+ name: (token[2] || token[6] || token[7])!,
104
+ value: token[4] ?? token[6] ?? token[8],
105
+ });
106
+ }
107
+ return parts;
108
+ }
109
+
110
+ function matchesSelector(element: Element, selector: string) {
111
+ const parsed = parseSelector(selector);
112
+ let part: Part | undefined;
113
+ while ((part = parsed.pop())) {
114
+ if (!matchesSelectorPart(element, part)) return false;
115
+ }
116
+ return true;
117
+ }
118
+
119
+ function walkNodesForSelector(
120
+ node: Node,
121
+ parts: Part[],
122
+ callback: (node: Element) => boolean | void,
123
+ ) {
124
+ if (isElementNode(node)) {
125
+ if (matchesSelectorRecursive(node, parts)) {
126
+ if (callback(node) === false) return false;
127
+ }
128
+ const child = node[CHILD];
129
+ if (child && walkNodesForSelector(child, parts, callback) === false) {
130
+ return false;
131
+ }
132
+ }
133
+ const next = node[NEXT];
134
+ if (next && walkNodesForSelector(next, parts, callback) === false) {
135
+ return false;
136
+ }
137
+ return true;
138
+ }
139
+
140
+ function matchesSelectorRecursive(element: Element, parts: Part[]): boolean {
141
+ const {combinator, matchers} = parts[parts.length - 1]!;
142
+ if (combinator === Combinator.Inner) {
143
+ if (!matchesSelectorMatcher(element, matchers)) return false;
144
+ const pp = parts.slice(0, -1);
145
+ return pp.length === 0 || matchesSelectorRecursive(element, pp);
146
+ }
147
+ const link =
148
+ combinator === Combinator.Child || combinator === Combinator.Descendant
149
+ ? PARENT
150
+ : PREV;
151
+ let ref = element[link];
152
+ if (!ref || !matchesSelectorMatcher(ref as Element, matchers)) {
153
+ return false;
154
+ }
155
+ if (
156
+ combinator === Combinator.Descendant ||
157
+ combinator === Combinator.Sibling
158
+ ) {
159
+ while ((ref = ref[link])) {
160
+ if (matchesSelectorMatcher(ref as Element, matchers)) {
161
+ const pp = parts.slice(0, -1);
162
+ if (pp.length === 0) return true;
163
+ if (matchesSelectorRecursive(element, pp)) return true;
164
+ }
165
+ }
166
+ }
167
+ return false;
168
+ }
169
+
170
+ function matchesSelectorPart(element: Element, {combinator, matchers}: Part) {
171
+ if (combinator === Combinator.Inner) {
172
+ return matchesSelectorMatcher(element, matchers);
173
+ }
174
+ const link =
175
+ combinator === Combinator.Child || combinator === Combinator.Descendant
176
+ ? PARENT
177
+ : PREV;
178
+ let ref = element[link];
179
+ if (!ref || !matchesSelectorMatcher(ref as Element, matchers)) {
180
+ return false;
181
+ }
182
+ if (
183
+ combinator === Combinator.Descendant ||
184
+ combinator === Combinator.Sibling
185
+ ) {
186
+ while ((ref = ref[link])) {
187
+ if (matchesSelectorMatcher(ref as Element, matchers)) return true;
188
+ }
189
+ }
190
+ return false;
191
+ }
192
+
193
+ function matchesSelectorMatcher(
194
+ element: Element | null,
195
+ matcher: Matcher | Matcher[],
196
+ ) {
197
+ if (!element) return false;
198
+ if (Array.isArray(matcher)) {
199
+ for (const single of matcher) {
200
+ if (matchesSelectorMatcher(element, single) === false) return false;
201
+ }
202
+ return true;
203
+ }
204
+ const {type, name, value} = matcher;
205
+ switch (type) {
206
+ case MatcherType.Element:
207
+ return element.localName === name;
208
+ case MatcherType.Id:
209
+ return element.getAttribute('id') === name;
210
+ case MatcherType.Class:
211
+ return element.getAttribute('class') === name;
212
+ case MatcherType.Attribute:
213
+ return value == null
214
+ ? element.hasAttribute(name)
215
+ : element.getAttribute(name) === value;
216
+ case MatcherType.Pseudo:
217
+ switch (name) {
218
+ default:
219
+ throw Error(`Pseudo :${name} not implemented`);
220
+ }
221
+ case MatcherType.Function:
222
+ switch (name) {
223
+ case 'has':
224
+ return matchesSelector(element, value || '');
225
+ default:
226
+ throw Error(`Function :${name}(${value}) not implemented`);
227
+ }
228
+ }
229
+ return false;
230
+ }
@@ -0,0 +1,110 @@
1
+ import {
2
+ ATTRIBUTES,
3
+ CHILD,
4
+ DATA,
5
+ NAME,
6
+ NEXT,
7
+ VALUE,
8
+ NodeType,
9
+ } from './constants.ts';
10
+ import type {Node} from './Node.ts';
11
+ import type {Text} from './Text.ts';
12
+ import type {Comment} from './Comment.ts';
13
+ import type {ParentNode} from './ParentNode.ts';
14
+ import type {Element} from './Element.ts';
15
+
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;
23
+
24
+ const elementTokenizer =
25
+ /(?:<([a-z][a-z0-9-:]*)((?:\s[^<>'"=\n\s]+(?:=(['"])[^\n]*?\3|=[^>'"\n\s]*|))*)\s*(\/?)\s*>|<\/([a-z][a-z0-9-:]*)>|<!--(.*?)-->|([^&<>]+))/gi;
26
+
27
+ const attributeTokenizer =
28
+ /\s([^<>'"=\n\s]+)(?:=(['"])([^\n]*?)\2|=([^>'"\n\s]*)|)/g;
29
+
30
+ export function parseHtml(html: string, contextNode: Node) {
31
+ const document = contextNode.ownerDocument;
32
+ const root = document.createDocumentFragment();
33
+ const stack: Node[] = [root];
34
+ let parent: ParentNode = root;
35
+ let token: RegExpExecArray | null;
36
+ elementTokenizer.lastIndex = 0;
37
+ while ((token = elementTokenizer.exec(html))) {
38
+ const tag = token[1];
39
+ if (tag) {
40
+ const node = document.createElement(tag);
41
+ 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] || '');
46
+ }
47
+ parent.append(node);
48
+ // if (voidElements[tag] === true) continue;
49
+ stack.push(parent);
50
+ parent = node;
51
+ } else if (token[5]) {
52
+ parent = (stack.pop() as ParentNode) || root;
53
+ } else if (token[6]) {
54
+ parent.append(document.createComment(token[6]!));
55
+ } else {
56
+ parent.append(token[7]!);
57
+ }
58
+ }
59
+ return root;
60
+ }
61
+
62
+ 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;
70
+ }
71
+
72
+ 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}"`;
84
+ }
85
+ attr = attr[NEXT];
86
+ }
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]}-->`;
107
+ }
108
+ }
109
+ return '';
110
+ }