@linxiraos/pi-utils 1.0.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 +449 -0
- package/README.md +36 -0
- package/dist/types/abortable.d.ts +32 -0
- package/dist/types/acp/connection.d.ts +118 -0
- package/dist/types/acp/protocol.d.ts +526 -0
- package/dist/types/acp/schema.d.ts +41 -0
- package/dist/types/acp/stream.d.ts +8 -0
- package/dist/types/acp/transport.d.ts +81 -0
- package/dist/types/acp.d.ts +6 -0
- package/dist/types/async.d.ts +20 -0
- package/dist/types/binary.d.ts +17 -0
- package/dist/types/browsers.d.ts +68 -0
- package/dist/types/chalk.d.ts +125 -0
- package/dist/types/cli.d.ts +134 -0
- package/dist/types/color.d.ts +102 -0
- package/dist/types/dates.d.ts +7 -0
- package/dist/types/dirs.d.ts +282 -0
- package/dist/types/docx/converter.d.ts +46 -0
- package/dist/types/docx/xml.d.ts +26 -0
- package/dist/types/docx/zip.d.ts +6 -0
- package/dist/types/docx.d.ts +11 -0
- package/dist/types/dom/core.d.ts +431 -0
- package/dist/types/dom/parser.d.ts +7 -0
- package/dist/types/dom/selector.d.ts +5 -0
- package/dist/types/dom.d.ts +5 -0
- package/dist/types/env.d.ts +124 -0
- package/dist/types/fetch-retry.d.ts +96 -0
- package/dist/types/file-lock.d.ts +23 -0
- package/dist/types/format.d.ts +37 -0
- package/dist/types/frontmatter.d.ts +46 -0
- package/dist/types/fs-error.d.ts +31 -0
- package/dist/types/glob.d.ts +28 -0
- package/dist/types/headers.d.ts +34 -0
- package/dist/types/index.d.ts +39 -0
- package/dist/types/json-parse.d.ts +74 -0
- package/dist/types/json.d.ts +14 -0
- package/dist/types/logger/rotating-file.d.ts +18 -0
- package/dist/types/logger.d.ts +96 -0
- package/dist/types/loop-phase.d.ts +10 -0
- package/dist/types/lru.d.ts +46 -0
- package/dist/types/marked/core.d.ts +445 -0
- package/dist/types/marked.d.ts +2 -0
- package/dist/types/mermaid-ascii.d.ts +11 -0
- package/dist/types/mime.d.ts +29 -0
- package/dist/types/module-timer.d.ts +1 -0
- package/dist/types/path-tree.d.ts +76 -0
- package/dist/types/path.d.ts +2 -0
- package/dist/types/peek-file.d.ts +29 -0
- package/dist/types/postmortem.d.ts +104 -0
- package/dist/types/process-name.d.ts +7 -0
- package/dist/types/procmgr.d.ts +74 -0
- package/dist/types/prompt.d.ts +18 -0
- package/dist/types/ptree.d.ts +110 -0
- package/dist/types/readability/readability.d.ts +9 -0
- package/dist/types/readability/readerable.d.ts +10 -0
- package/dist/types/readability/types.d.ts +70 -0
- package/dist/types/readability.d.ts +4 -0
- package/dist/types/ring.d.ts +93 -0
- package/dist/types/runtime-install.d.ts +78 -0
- package/dist/types/sanitize-text.d.ts +29 -0
- package/dist/types/snowflake.d.ts +25 -0
- package/dist/types/stderr-guard.d.ts +22 -0
- package/dist/types/stream.d.ts +67 -0
- package/dist/types/tab-spacing.d.ts +24 -0
- package/dist/types/temp.d.ts +17 -0
- package/dist/types/template.d.ts +62 -0
- package/dist/types/timing-buffer.d.ts +22 -0
- package/dist/types/tls-fetch.d.ts +37 -0
- package/dist/types/turndown/gfm.d.ts +11 -0
- package/dist/types/turndown/html.d.ts +5 -0
- package/dist/types/turndown/service.d.ts +21 -0
- package/dist/types/turndown/types.d.ts +70 -0
- package/dist/types/turndown.d.ts +4 -0
- package/dist/types/type-guards.d.ts +5 -0
- package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +41 -0
- package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +89 -0
- package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +12 -0
- package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +66 -0
- package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +48 -0
- package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +43 -0
- package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +56 -0
- package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +65 -0
- package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +27 -0
- package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +17 -0
- package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +34 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +26 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +31 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +59 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +17 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +30 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +55 -0
- package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +206 -0
- package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +51 -0
- package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +2 -0
- package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/class/types.d.ts +102 -0
- package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/er/types.d.ts +76 -0
- package/dist/types/vendor/mermaid-ascii/index.d.ts +1 -0
- package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +9 -0
- package/dist/types/vendor/mermaid-ascii/parser.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +130 -0
- package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +21 -0
- package/dist/types/vendor/mermaid-ascii/types.d.ts +114 -0
- package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +25 -0
- package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +145 -0
- package/dist/types/version.d.ts +18 -0
- package/dist/types/vterm/buffer.d.ts +99 -0
- package/dist/types/vterm/terminal.d.ts +44 -0
- package/dist/types/vterm.d.ts +8 -0
- package/dist/types/which.d.ts +37 -0
- package/dist/types/worker-host.d.ts +51 -0
- package/dist/types/xml.d.ts +31 -0
- package/package.json +59 -0
- package/src/abortable.ts +109 -0
- package/src/acp/connection.ts +344 -0
- package/src/acp/protocol.ts +466 -0
- package/src/acp/schema.ts +160 -0
- package/src/acp/stream.ts +82 -0
- package/src/acp/transport.ts +213 -0
- package/src/acp.ts +6 -0
- package/src/async.ts +91 -0
- package/src/binary.ts +50 -0
- package/src/browsers.ts +501 -0
- package/src/chalk.ts +312 -0
- package/src/cli.ts +529 -0
- package/src/color.ts +302 -0
- package/src/dates.ts +194 -0
- package/src/dirs.ts +1026 -0
- package/src/docx/converter.ts +681 -0
- package/src/docx/xml.ts +166 -0
- package/src/docx/zip.ts +87 -0
- package/src/docx.ts +20 -0
- package/src/dom/core.ts +1254 -0
- package/src/dom/parser.ts +370 -0
- package/src/dom/selector.ts +290 -0
- package/src/dom.ts +33 -0
- package/src/env.ts +400 -0
- package/src/fetch-retry.ts +379 -0
- package/src/file-lock.ts +69 -0
- package/src/format.ts +113 -0
- package/src/frontmatter.ts +205 -0
- package/src/fs-error.ts +56 -0
- package/src/glob.ts +189 -0
- package/src/headers.ts +167 -0
- package/src/index.ts +59 -0
- package/src/json-parse.ts +821 -0
- package/src/json.ts +23 -0
- package/src/logger/rotating-file.ts +149 -0
- package/src/logger.ts +798 -0
- package/src/loop-phase.ts +49 -0
- package/src/lru.ts +185 -0
- package/src/marked/core.ts +1576 -0
- package/src/marked.ts +2 -0
- package/src/mermaid-ascii.ts +31 -0
- package/src/mime.ts +159 -0
- package/src/module-timer.ts +148 -0
- package/src/path-tree.ts +147 -0
- package/src/path.ts +28 -0
- package/src/peek-file.ts +188 -0
- package/src/postmortem.ts +433 -0
- package/src/process-name.ts +57 -0
- package/src/procmgr.ts +247 -0
- package/src/prompt.ts +545 -0
- package/src/ptree.ts +412 -0
- package/src/readability/readability.ts +533 -0
- package/src/readability/readerable.ts +51 -0
- package/src/readability/types.ts +72 -0
- package/src/readability.ts +11 -0
- package/src/ring.ts +169 -0
- package/src/runtime-install.ts +393 -0
- package/src/sanitize-text.ts +96 -0
- package/src/snowflake.ts +121 -0
- package/src/stderr-guard.ts +155 -0
- package/src/stream.ts +454 -0
- package/src/tab-spacing.ts +336 -0
- package/src/temp.ts +134 -0
- package/src/template.ts +586 -0
- package/src/timing-buffer.ts +47 -0
- package/src/tls-fetch.ts +178 -0
- package/src/turndown/gfm.ts +106 -0
- package/src/turndown/html.ts +257 -0
- package/src/turndown/service.ts +334 -0
- package/src/turndown/types.ts +81 -0
- package/src/turndown.ts +5 -0
- package/src/type-guards.ts +17 -0
- package/src/vendor/mermaid-ascii/NOTICE +33 -0
- package/src/vendor/mermaid-ascii/ascii/ansi.ts +409 -0
- package/src/vendor/mermaid-ascii/ascii/canvas.ts +476 -0
- package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +699 -0
- package/src/vendor/mermaid-ascii/ascii/converter.ts +271 -0
- package/src/vendor/mermaid-ascii/ascii/draw.ts +1382 -0
- package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +328 -0
- package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +297 -0
- package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +441 -0
- package/src/vendor/mermaid-ascii/ascii/grid.ts +578 -0
- package/src/vendor/mermaid-ascii/ascii/index.ts +187 -0
- package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +78 -0
- package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +277 -0
- package/src/vendor/mermaid-ascii/ascii/sequence.ts +460 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +127 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +101 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +175 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +296 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +114 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +192 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +73 -0
- package/src/vendor/mermaid-ascii/ascii/types.ts +273 -0
- package/src/vendor/mermaid-ascii/ascii/validate.ts +120 -0
- package/src/vendor/mermaid-ascii/ascii/xychart.ts +875 -0
- package/src/vendor/mermaid-ascii/class/parser.ts +290 -0
- package/src/vendor/mermaid-ascii/class/types.ts +121 -0
- package/src/vendor/mermaid-ascii/er/parser.ts +181 -0
- package/src/vendor/mermaid-ascii/er/types.ts +91 -0
- package/src/vendor/mermaid-ascii/index.ts +14 -0
- package/src/vendor/mermaid-ascii/multiline-utils.ts +30 -0
- package/src/vendor/mermaid-ascii/parser.ts +645 -0
- package/src/vendor/mermaid-ascii/sequence/parser.ts +207 -0
- package/src/vendor/mermaid-ascii/sequence/types.ts +146 -0
- package/src/vendor/mermaid-ascii/text-metrics.ts +71 -0
- package/src/vendor/mermaid-ascii/types.ts +164 -0
- package/src/vendor/mermaid-ascii/xychart/colors.ts +140 -0
- package/src/vendor/mermaid-ascii/xychart/parser.ts +115 -0
- package/src/vendor/mermaid-ascii/xychart/types.ts +150 -0
- package/src/version.ts +99 -0
- package/src/vterm/buffer.ts +218 -0
- package/src/vterm/terminal.ts +773 -0
- package/src/vterm.ts +8 -0
- package/src/which.ts +234 -0
- package/src/worker-host.ts +100 -0
- package/src/xml.ts +313 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { Document, type DocumentFragment, Element, Text } from "./core";
|
|
2
|
+
|
|
3
|
+
const RAW_TEXT_ELEMENTS: Record<string, true> = { script: true, style: true };
|
|
4
|
+
const VOID_ELEMENTS: Record<string, true> = {
|
|
5
|
+
area: true,
|
|
6
|
+
base: true,
|
|
7
|
+
br: true,
|
|
8
|
+
col: true,
|
|
9
|
+
embed: true,
|
|
10
|
+
hr: true,
|
|
11
|
+
img: true,
|
|
12
|
+
input: true,
|
|
13
|
+
link: true,
|
|
14
|
+
meta: true,
|
|
15
|
+
param: true,
|
|
16
|
+
source: true,
|
|
17
|
+
track: true,
|
|
18
|
+
wbr: true,
|
|
19
|
+
};
|
|
20
|
+
const CLOSE_ON_OPEN: Record<string, readonly string[]> = {
|
|
21
|
+
li: ["li"],
|
|
22
|
+
dt: ["dt", "dd"],
|
|
23
|
+
dd: ["dt", "dd"],
|
|
24
|
+
tr: ["tr"],
|
|
25
|
+
th: ["th", "td"],
|
|
26
|
+
td: ["th", "td"],
|
|
27
|
+
option: ["option"],
|
|
28
|
+
thead: ["thead", "tbody", "tfoot"],
|
|
29
|
+
tbody: ["thead", "tbody", "tfoot"],
|
|
30
|
+
tfoot: ["thead", "tbody", "tfoot"],
|
|
31
|
+
};
|
|
32
|
+
const P_CLOSERS: Record<string, true> = {
|
|
33
|
+
address: true,
|
|
34
|
+
article: true,
|
|
35
|
+
aside: true,
|
|
36
|
+
blockquote: true,
|
|
37
|
+
div: true,
|
|
38
|
+
dl: true,
|
|
39
|
+
fieldset: true,
|
|
40
|
+
footer: true,
|
|
41
|
+
form: true,
|
|
42
|
+
h1: true,
|
|
43
|
+
h2: true,
|
|
44
|
+
h3: true,
|
|
45
|
+
h4: true,
|
|
46
|
+
h5: true,
|
|
47
|
+
h6: true,
|
|
48
|
+
header: true,
|
|
49
|
+
hr: true,
|
|
50
|
+
menu: true,
|
|
51
|
+
nav: true,
|
|
52
|
+
ol: true,
|
|
53
|
+
p: true,
|
|
54
|
+
pre: true,
|
|
55
|
+
section: true,
|
|
56
|
+
table: true,
|
|
57
|
+
ul: true,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const NAMED_ENTITIES: Record<string, string> = {
|
|
61
|
+
AElig: "Æ",
|
|
62
|
+
Aacute: "Á",
|
|
63
|
+
Acirc: "Â",
|
|
64
|
+
Agrave: "À",
|
|
65
|
+
Aring: "Å",
|
|
66
|
+
Atilde: "Ã",
|
|
67
|
+
Auml: "Ä",
|
|
68
|
+
Ccedil: "Ç",
|
|
69
|
+
ETH: "Ð",
|
|
70
|
+
Eacute: "É",
|
|
71
|
+
Ecirc: "Ê",
|
|
72
|
+
Egrave: "È",
|
|
73
|
+
Euml: "Ë",
|
|
74
|
+
Iacute: "Í",
|
|
75
|
+
Icirc: "Î",
|
|
76
|
+
Igrave: "Ì",
|
|
77
|
+
Iuml: "Ï",
|
|
78
|
+
Ntilde: "Ñ",
|
|
79
|
+
Oacute: "Ó",
|
|
80
|
+
Ocirc: "Ô",
|
|
81
|
+
Ograve: "Ò",
|
|
82
|
+
Oslash: "Ø",
|
|
83
|
+
Otilde: "Õ",
|
|
84
|
+
Ouml: "Ö",
|
|
85
|
+
THORN: "Þ",
|
|
86
|
+
Uacute: "Ú",
|
|
87
|
+
Ucirc: "Û",
|
|
88
|
+
Ugrave: "Ù",
|
|
89
|
+
Uuml: "Ü",
|
|
90
|
+
Yacute: "Ý",
|
|
91
|
+
aacute: "á",
|
|
92
|
+
acirc: "â",
|
|
93
|
+
aelig: "æ",
|
|
94
|
+
agrave: "à",
|
|
95
|
+
aring: "å",
|
|
96
|
+
atilde: "ã",
|
|
97
|
+
auml: "ä",
|
|
98
|
+
amp: "&",
|
|
99
|
+
apos: "'",
|
|
100
|
+
bull: "•",
|
|
101
|
+
brvbar: "¦",
|
|
102
|
+
ccedil: "ç",
|
|
103
|
+
cedil: "¸",
|
|
104
|
+
cent: "¢",
|
|
105
|
+
copy: "©",
|
|
106
|
+
deg: "°",
|
|
107
|
+
curren: "¤",
|
|
108
|
+
divide: "÷",
|
|
109
|
+
emsp: " ",
|
|
110
|
+
ensp: " ",
|
|
111
|
+
euro: "€",
|
|
112
|
+
eacute: "é",
|
|
113
|
+
ecirc: "ê",
|
|
114
|
+
egrave: "è",
|
|
115
|
+
eth: "ð",
|
|
116
|
+
euml: "ë",
|
|
117
|
+
frac12: "½",
|
|
118
|
+
frac14: "¼",
|
|
119
|
+
frac34: "¾",
|
|
120
|
+
gt: ">",
|
|
121
|
+
iacute: "í",
|
|
122
|
+
icirc: "î",
|
|
123
|
+
iexcl: "¡",
|
|
124
|
+
igrave: "ì",
|
|
125
|
+
iquest: "¿",
|
|
126
|
+
iuml: "ï",
|
|
127
|
+
hellip: "…",
|
|
128
|
+
laquo: "«",
|
|
129
|
+
ldquo: "“",
|
|
130
|
+
lsquo: "‘",
|
|
131
|
+
lt: "<",
|
|
132
|
+
mdash: "—",
|
|
133
|
+
macr: "¯",
|
|
134
|
+
micro: "µ",
|
|
135
|
+
middot: "·",
|
|
136
|
+
nbsp: " ",
|
|
137
|
+
ndash: "–",
|
|
138
|
+
ntilde: "ñ",
|
|
139
|
+
oacute: "ó",
|
|
140
|
+
ocirc: "ô",
|
|
141
|
+
ograve: "ò",
|
|
142
|
+
ordf: "ª",
|
|
143
|
+
ordm: "º",
|
|
144
|
+
oslash: "ø",
|
|
145
|
+
otilde: "õ",
|
|
146
|
+
ouml: "ö",
|
|
147
|
+
para: "¶",
|
|
148
|
+
pound: "£",
|
|
149
|
+
plusmn: "±",
|
|
150
|
+
quot: '"',
|
|
151
|
+
raquo: "»",
|
|
152
|
+
rdquo: "”",
|
|
153
|
+
reg: "®",
|
|
154
|
+
rsquo: "’",
|
|
155
|
+
sect: "§",
|
|
156
|
+
shy: "",
|
|
157
|
+
szlig: "ß",
|
|
158
|
+
thorn: "þ",
|
|
159
|
+
sup1: "¹",
|
|
160
|
+
sup2: "²",
|
|
161
|
+
sup3: "³",
|
|
162
|
+
thinsp: " ",
|
|
163
|
+
times: "×",
|
|
164
|
+
trade: "™",
|
|
165
|
+
uacute: "ú",
|
|
166
|
+
ucirc: "û",
|
|
167
|
+
ugrave: "ù",
|
|
168
|
+
uml: "¨",
|
|
169
|
+
uuml: "ü",
|
|
170
|
+
yacute: "ý",
|
|
171
|
+
yuml: "ÿ",
|
|
172
|
+
yen: "¥",
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/** Decode common named entities and all valid numeric character references. */
|
|
176
|
+
export function decodeEntities(value: string): string {
|
|
177
|
+
return value.replace(/&(#(?:x[0-9a-f]+|\d+)|[a-z][a-z0-9]+);/gi, (whole, entity: string) => {
|
|
178
|
+
if (entity[0] !== "#") return NAMED_ENTITIES[entity] ?? NAMED_ENTITIES[entity.toLowerCase()] ?? whole;
|
|
179
|
+
const hexadecimal = entity[1]?.toLowerCase() === "x";
|
|
180
|
+
const codePoint = Number.parseInt(entity.slice(hexadecimal ? 2 : 1), hexadecimal ? 16 : 10);
|
|
181
|
+
if (
|
|
182
|
+
!Number.isFinite(codePoint) ||
|
|
183
|
+
codePoint <= 0 ||
|
|
184
|
+
codePoint > 0x10ffff ||
|
|
185
|
+
(codePoint >= 0xd800 && codePoint <= 0xdfff)
|
|
186
|
+
) {
|
|
187
|
+
return "�";
|
|
188
|
+
}
|
|
189
|
+
return String.fromCodePoint(codePoint);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function findTagEnd(html: string, start: number): number {
|
|
194
|
+
let quote = "";
|
|
195
|
+
for (let index = start; index < html.length; index++) {
|
|
196
|
+
const character = html[index];
|
|
197
|
+
if (quote) {
|
|
198
|
+
if (character === quote) quote = "";
|
|
199
|
+
} else if (character === '"' || character === "'") {
|
|
200
|
+
quote = character;
|
|
201
|
+
} else if (character === ">") {
|
|
202
|
+
return index;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return html.length - 1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function parseStartTag(
|
|
209
|
+
source: string,
|
|
210
|
+
): { name: string; attributes: Array<[string, string]>; selfClosing: boolean } | null {
|
|
211
|
+
let index = 0;
|
|
212
|
+
while (/\s/.test(source[index] ?? "")) index++;
|
|
213
|
+
const nameMatch = /^[^\s/>]+/.exec(source.slice(index));
|
|
214
|
+
if (!nameMatch) return null;
|
|
215
|
+
const name = nameMatch[0].toLowerCase();
|
|
216
|
+
index += nameMatch[0].length;
|
|
217
|
+
const attributes: Array<[string, string]> = [];
|
|
218
|
+
let selfClosing = false;
|
|
219
|
+
while (index < source.length) {
|
|
220
|
+
while (/\s/.test(source[index] ?? "")) index++;
|
|
221
|
+
if (source[index] === "/") {
|
|
222
|
+
selfClosing = true;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
const attributeMatch = /^[^\s=/>]+/.exec(source.slice(index));
|
|
226
|
+
if (!attributeMatch) break;
|
|
227
|
+
const attributeName = attributeMatch[0];
|
|
228
|
+
index += attributeMatch[0].length;
|
|
229
|
+
while (/\s/.test(source[index] ?? "")) index++;
|
|
230
|
+
let value = "";
|
|
231
|
+
if (source[index] === "=") {
|
|
232
|
+
index++;
|
|
233
|
+
while (/\s/.test(source[index] ?? "")) index++;
|
|
234
|
+
const quote = source[index];
|
|
235
|
+
if (quote === '"' || quote === "'") {
|
|
236
|
+
index++;
|
|
237
|
+
const end = source.indexOf(quote, index);
|
|
238
|
+
if (end < 0) {
|
|
239
|
+
value = source.slice(index);
|
|
240
|
+
index = source.length;
|
|
241
|
+
} else {
|
|
242
|
+
value = source.slice(index, end);
|
|
243
|
+
index = end + 1;
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
const valueMatch = /^[^\s>]+/.exec(source.slice(index));
|
|
247
|
+
value = valueMatch?.[0] ?? "";
|
|
248
|
+
index += value.length;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (!attributes.some(([existing]) => existing.toLowerCase() === attributeName.toLowerCase())) {
|
|
252
|
+
attributes.push([attributeName, decodeEntities(value)]);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return { name, attributes, selfClosing };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function closeOptionalElements(stack: Element[], incoming: string): void {
|
|
259
|
+
const current = stack[stack.length - 1];
|
|
260
|
+
if (!current) return;
|
|
261
|
+
if (current.localName === "p" && P_CLOSERS[incoming]) {
|
|
262
|
+
stack.pop();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const closeable = CLOSE_ON_OPEN[incoming];
|
|
266
|
+
if (closeable?.includes(current.localName)) stack.pop();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function parseInto(html: string, document: Document, root: Document | DocumentFragment, contextTag?: string): void {
|
|
270
|
+
const stack: Element[] = [];
|
|
271
|
+
const xmlLike = root instanceof Document && /^\s*(?:<\?xml[\s\S]*?\?>\s*)?<(?:feed|rss)\b/i.test(html);
|
|
272
|
+
let parent: Document | DocumentFragment | Element = root;
|
|
273
|
+
let index = 0;
|
|
274
|
+
while (index < html.length) {
|
|
275
|
+
if (html.startsWith("<!--", index)) {
|
|
276
|
+
const end = html.indexOf("-->", index + 4);
|
|
277
|
+
const contentEnd = end < 0 ? html.length : end;
|
|
278
|
+
parent.appendChild(document.createComment(html.slice(index + 4, contentEnd)));
|
|
279
|
+
index = end < 0 ? html.length : end + 3;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (html[index] !== "<") {
|
|
283
|
+
const next = html.indexOf("<", index);
|
|
284
|
+
const end = next < 0 ? html.length : next;
|
|
285
|
+
parent.appendChild(new Text(decodeEntities(html.slice(index, end)), document, xmlLike));
|
|
286
|
+
index = end;
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (/^<!doctype\b/i.test(html.slice(index))) {
|
|
290
|
+
const end = html.indexOf(">", index + 2);
|
|
291
|
+
index = end < 0 ? html.length : end + 1;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (html.startsWith("<![CDATA[", index)) {
|
|
295
|
+
const end = html.indexOf("]]>", index + 9);
|
|
296
|
+
const contentEnd = end < 0 ? html.length : end;
|
|
297
|
+
parent.appendChild(new Text(html.slice(index + 9, contentEnd), document, xmlLike));
|
|
298
|
+
index = end < 0 ? html.length : end + 3;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (html[index + 1] === "/") {
|
|
302
|
+
const end = findTagEnd(html, index + 2);
|
|
303
|
+
const closingName = html
|
|
304
|
+
.slice(index + 2, end)
|
|
305
|
+
.trim()
|
|
306
|
+
.split(/\s/, 1)[0]
|
|
307
|
+
.toLowerCase();
|
|
308
|
+
const matchIndex = stack.findLastIndex(element => element.localName === closingName);
|
|
309
|
+
if (matchIndex >= 0) stack.length = matchIndex;
|
|
310
|
+
parent = stack[stack.length - 1] ?? root;
|
|
311
|
+
index = end + 1;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (html[index + 1] === "!" || html[index + 1] === "?") {
|
|
315
|
+
const end = html.indexOf(">", index + 2);
|
|
316
|
+
index = end < 0 ? html.length : end + 1;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const end = findTagEnd(html, index + 1);
|
|
320
|
+
const parsed = parseStartTag(html.slice(index + 1, end));
|
|
321
|
+
if (!parsed) {
|
|
322
|
+
parent.appendChild(document.createTextNode("<"));
|
|
323
|
+
index++;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
closeOptionalElements(stack, parsed.name);
|
|
327
|
+
parent = stack[stack.length - 1] ?? root;
|
|
328
|
+
const namespace =
|
|
329
|
+
parent instanceof Element && parent.namespaceURI === "http://www.w3.org/2000/svg"
|
|
330
|
+
? "http://www.w3.org/2000/svg"
|
|
331
|
+
: parsed.name === "svg"
|
|
332
|
+
? "http://www.w3.org/2000/svg"
|
|
333
|
+
: "http://www.w3.org/1999/xhtml";
|
|
334
|
+
const element = document.createElementNS(namespace, parsed.name);
|
|
335
|
+
for (let attributeIndex = parsed.attributes.length - 1; attributeIndex >= 0; attributeIndex--) {
|
|
336
|
+
const [name, value] = parsed.attributes[attributeIndex];
|
|
337
|
+
element.setAttribute(name, value);
|
|
338
|
+
}
|
|
339
|
+
parent.appendChild(element);
|
|
340
|
+
index = end + 1;
|
|
341
|
+
if (RAW_TEXT_ELEMENTS[parsed.name] && !parsed.selfClosing) {
|
|
342
|
+
const closePattern = new RegExp(`</${parsed.name}\\s*>`, "ig");
|
|
343
|
+
closePattern.lastIndex = index;
|
|
344
|
+
const match = closePattern.exec(html);
|
|
345
|
+
const rawEnd = match?.index ?? html.length;
|
|
346
|
+
element.appendChild(new Text(html.slice(index, rawEnd), document));
|
|
347
|
+
index = match ? closePattern.lastIndex : html.length;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (!VOID_ELEMENTS[parsed.name] && !(namespace === "http://www.w3.org/2000/svg" && parsed.selfClosing)) {
|
|
351
|
+
stack.push(element);
|
|
352
|
+
parent = element;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
void contextTag;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** Parse markup into a document fragment. */
|
|
359
|
+
export function parseFragment(html: string, document: Document, contextTag?: string): DocumentFragment {
|
|
360
|
+
const fragment = document.createDocumentFragment();
|
|
361
|
+
parseInto(html, document, fragment, contextTag);
|
|
362
|
+
return fragment;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** Parse markup directly into a document. */
|
|
366
|
+
export function parseDocument(html: string): Document {
|
|
367
|
+
const document = new Document();
|
|
368
|
+
parseInto(html, document, document);
|
|
369
|
+
return document;
|
|
370
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { type Document, type DocumentFragment, Element, Node } from "./core";
|
|
2
|
+
|
|
3
|
+
interface ComplexSelector {
|
|
4
|
+
simples: string[];
|
|
5
|
+
combinators: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function splitTopLevel(value: string, delimiter: string): string[] {
|
|
9
|
+
const parts: string[] = [];
|
|
10
|
+
let start = 0;
|
|
11
|
+
let brackets = 0;
|
|
12
|
+
let parentheses = 0;
|
|
13
|
+
let quote = "";
|
|
14
|
+
for (let index = 0; index < value.length; index++) {
|
|
15
|
+
const character = value[index];
|
|
16
|
+
if (quote) {
|
|
17
|
+
if (character === quote && value[index - 1] !== "\\") quote = "";
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (character === '"' || character === "'") quote = character;
|
|
21
|
+
else if (character === "[") brackets++;
|
|
22
|
+
else if (character === "]") brackets--;
|
|
23
|
+
else if (character === "(") parentheses++;
|
|
24
|
+
else if (character === ")") parentheses--;
|
|
25
|
+
else if (character === delimiter && brackets === 0 && parentheses === 0) {
|
|
26
|
+
parts.push(value.slice(start, index).trim());
|
|
27
|
+
start = index + 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
parts.push(value.slice(start).trim());
|
|
31
|
+
return parts.filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseComplex(selector: string): ComplexSelector {
|
|
35
|
+
const simples: string[] = [];
|
|
36
|
+
const combinators: string[] = [];
|
|
37
|
+
let current = "";
|
|
38
|
+
let brackets = 0;
|
|
39
|
+
let parentheses = 0;
|
|
40
|
+
let quote = "";
|
|
41
|
+
for (let index = 0; index < selector.length; index++) {
|
|
42
|
+
const character = selector[index];
|
|
43
|
+
if (quote) {
|
|
44
|
+
current += character;
|
|
45
|
+
if (character === quote && selector[index - 1] !== "\\") quote = "";
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (character === '"' || character === "'") {
|
|
49
|
+
quote = character;
|
|
50
|
+
current += character;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (character === "[") brackets++;
|
|
54
|
+
else if (character === "]") brackets--;
|
|
55
|
+
else if (character === "(") parentheses++;
|
|
56
|
+
else if (character === ")") parentheses--;
|
|
57
|
+
if (brackets || parentheses) {
|
|
58
|
+
current += character;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (character === ">" || character === "+" || character === "~") {
|
|
62
|
+
if (current.trim()) simples.push(current.trim());
|
|
63
|
+
current = "";
|
|
64
|
+
combinators.push(character);
|
|
65
|
+
while (/\s/.test(selector[index + 1] ?? "")) index++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (/\s/.test(character)) {
|
|
69
|
+
while (/\s/.test(selector[index + 1] ?? "")) index++;
|
|
70
|
+
const next = selector[index + 1];
|
|
71
|
+
if (current.trim()) {
|
|
72
|
+
simples.push(current.trim());
|
|
73
|
+
current = "";
|
|
74
|
+
if (next !== ">" && next !== "+" && next !== "~" && next !== undefined) combinators.push(" ");
|
|
75
|
+
}
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
current += character;
|
|
79
|
+
}
|
|
80
|
+
if (current.trim()) simples.push(current.trim());
|
|
81
|
+
while (combinators.length >= simples.length) combinators.pop();
|
|
82
|
+
return { simples, combinators };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function readIdentifier(source: string, start: number): { value: string; end: number } {
|
|
86
|
+
let value = "";
|
|
87
|
+
let index = start;
|
|
88
|
+
while (index < source.length) {
|
|
89
|
+
const character = source[index];
|
|
90
|
+
if (character === "\\" && index + 1 < source.length) {
|
|
91
|
+
value += source[index + 1];
|
|
92
|
+
index += 2;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (!/[a-zA-Z0-9_-]/.test(character)) break;
|
|
96
|
+
value += character;
|
|
97
|
+
index++;
|
|
98
|
+
}
|
|
99
|
+
return { value, end: index };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function findClosing(source: string, start: number, opener: string, closer: string): number {
|
|
103
|
+
let depth = 1;
|
|
104
|
+
let quote = "";
|
|
105
|
+
for (let index = start + 1; index < source.length; index++) {
|
|
106
|
+
const character = source[index];
|
|
107
|
+
if (quote) {
|
|
108
|
+
if (character === quote && source[index - 1] !== "\\") quote = "";
|
|
109
|
+
} else if (character === '"' || character === "'") quote = character;
|
|
110
|
+
else if (character === opener) depth++;
|
|
111
|
+
else if (character === closer && --depth === 0) return index;
|
|
112
|
+
}
|
|
113
|
+
return source.length - 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function matchAttribute(element: Element, expression: string): boolean {
|
|
117
|
+
const match = /^\s*([^\s~|^$*!=]+)\s*(?:(\^=|\$=|\*=|~=|\|=|=)\s*(?:(["'])(.*?)\3|([^\s]+))\s*([isIS])?)?\s*$/.exec(
|
|
118
|
+
expression,
|
|
119
|
+
);
|
|
120
|
+
if (!match) return false;
|
|
121
|
+
const [, name, operator, , quotedValue, bareValue, flag] = match;
|
|
122
|
+
const actual = element.getAttribute(name);
|
|
123
|
+
if (!operator) return actual !== null;
|
|
124
|
+
if (actual === null) return false;
|
|
125
|
+
let left = actual;
|
|
126
|
+
let right = (quotedValue ?? bareValue ?? "").replace(/\\(.)/g, "$1");
|
|
127
|
+
if (flag?.toLowerCase() === "i") {
|
|
128
|
+
left = left.toLowerCase();
|
|
129
|
+
right = right.toLowerCase();
|
|
130
|
+
}
|
|
131
|
+
switch (operator) {
|
|
132
|
+
case "=":
|
|
133
|
+
return left === right;
|
|
134
|
+
case "^=":
|
|
135
|
+
return left.startsWith(right);
|
|
136
|
+
case "$=":
|
|
137
|
+
return left.endsWith(right);
|
|
138
|
+
case "*=":
|
|
139
|
+
return left.includes(right);
|
|
140
|
+
case "~=":
|
|
141
|
+
return left.split(/\s+/).includes(right);
|
|
142
|
+
case "|=":
|
|
143
|
+
return left === right || left.startsWith(`${right}-`);
|
|
144
|
+
default:
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function matchNth(element: Element, expression: string): boolean {
|
|
150
|
+
const siblings = element.parentElement?.children ?? [];
|
|
151
|
+
const index = siblings.indexOf(element) + 1;
|
|
152
|
+
const normalized = expression.trim().toLowerCase().replace(/\s+/g, "");
|
|
153
|
+
if (normalized === "odd") return index % 2 === 1;
|
|
154
|
+
if (normalized === "even") return index % 2 === 0;
|
|
155
|
+
if (/^[+-]?\d+$/.test(normalized)) return index === Number(normalized);
|
|
156
|
+
const match = /^([+-]?\d*)n([+-]\d+)?$/.exec(normalized);
|
|
157
|
+
if (!match) return false;
|
|
158
|
+
const coefficient = match[1] === "" || match[1] === "+" ? 1 : match[1] === "-" ? -1 : Number(match[1]);
|
|
159
|
+
const offset = Number(match[2] ?? 0);
|
|
160
|
+
return coefficient === 0
|
|
161
|
+
? index === offset
|
|
162
|
+
: (index - offset) / coefficient >= 0 && Number.isInteger((index - offset) / coefficient);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function matchPseudo(element: Element, name: string, argument: string | undefined): boolean {
|
|
166
|
+
switch (name) {
|
|
167
|
+
case "not":
|
|
168
|
+
return argument !== undefined && !matchesSelector(element, argument);
|
|
169
|
+
case "is":
|
|
170
|
+
case "where":
|
|
171
|
+
return argument !== undefined && matchesSelector(element, argument);
|
|
172
|
+
case "first-child":
|
|
173
|
+
return element.parentElement?.firstElementChild === element;
|
|
174
|
+
case "last-child":
|
|
175
|
+
return element.parentElement?.lastElementChild === element;
|
|
176
|
+
case "only-child":
|
|
177
|
+
return element.parentElement?.children.length === 1;
|
|
178
|
+
case "empty":
|
|
179
|
+
return element.childNodes.every(child => child.nodeType === Node.COMMENT_NODE || child.textContent === "");
|
|
180
|
+
case "root":
|
|
181
|
+
return element.ownerDocument?.documentElement === element;
|
|
182
|
+
case "nth-child":
|
|
183
|
+
return argument !== undefined && matchNth(element, argument);
|
|
184
|
+
case "first-of-type": {
|
|
185
|
+
for (let sibling = element.previousElementSibling; sibling; sibling = sibling.previousElementSibling) {
|
|
186
|
+
if (sibling.tagName === element.tagName) return false;
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
case "last-of-type": {
|
|
191
|
+
for (let sibling = element.nextElementSibling; sibling; sibling = sibling.nextElementSibling) {
|
|
192
|
+
if (sibling.tagName === element.tagName) return false;
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
default:
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function matchSimple(element: Element, selector: string): boolean {
|
|
202
|
+
let index = 0;
|
|
203
|
+
if (selector[index] === "*") index++;
|
|
204
|
+
else if (/[a-zA-Z_]/.test(selector[index] ?? "")) {
|
|
205
|
+
const tag = readIdentifier(selector, index);
|
|
206
|
+
if (element.localName !== tag.value.toLowerCase()) return false;
|
|
207
|
+
index = tag.end;
|
|
208
|
+
}
|
|
209
|
+
while (index < selector.length) {
|
|
210
|
+
const marker = selector[index];
|
|
211
|
+
if (marker === "#" || marker === ".") {
|
|
212
|
+
const identifier = readIdentifier(selector, index + 1);
|
|
213
|
+
if (!identifier.value) return false;
|
|
214
|
+
if (marker === "#" ? element.id !== identifier.value : !element.classList.contains(identifier.value))
|
|
215
|
+
return false;
|
|
216
|
+
index = identifier.end;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (marker === "[") {
|
|
220
|
+
const end = findClosing(selector, index, "[", "]");
|
|
221
|
+
if (!matchAttribute(element, selector.slice(index + 1, end))) return false;
|
|
222
|
+
index = end + 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (marker === ":") {
|
|
226
|
+
const identifier = readIdentifier(selector, index + 1);
|
|
227
|
+
let argument: string | undefined;
|
|
228
|
+
index = identifier.end;
|
|
229
|
+
if (selector[index] === "(") {
|
|
230
|
+
const end = findClosing(selector, index, "(", ")");
|
|
231
|
+
argument = selector.slice(index + 1, end);
|
|
232
|
+
index = end + 1;
|
|
233
|
+
}
|
|
234
|
+
if (!matchPseudo(element, identifier.value.toLowerCase(), argument)) return false;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function matchComplexAt(element: Element, complex: ComplexSelector, index: number): boolean {
|
|
243
|
+
if (!matchSimple(element, complex.simples[index])) return false;
|
|
244
|
+
if (index === 0) return true;
|
|
245
|
+
const combinator = complex.combinators[index - 1] ?? " ";
|
|
246
|
+
if (combinator === ">")
|
|
247
|
+
return element.parentElement !== null && matchComplexAt(element.parentElement, complex, index - 1);
|
|
248
|
+
if (combinator === "+")
|
|
249
|
+
return (
|
|
250
|
+
element.previousElementSibling !== null && matchComplexAt(element.previousElementSibling, complex, index - 1)
|
|
251
|
+
);
|
|
252
|
+
if (combinator === "~") {
|
|
253
|
+
for (let sibling = element.previousElementSibling; sibling; sibling = sibling.previousElementSibling) {
|
|
254
|
+
if (matchComplexAt(sibling, complex, index - 1)) return true;
|
|
255
|
+
}
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
259
|
+
if (matchComplexAt(ancestor, complex, index - 1)) return true;
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Whether an element matches a CSS selector list. */
|
|
265
|
+
export function matchesSelector(element: Element, selector: string): boolean {
|
|
266
|
+
for (const part of splitTopLevel(selector, ",")) {
|
|
267
|
+
const complex = parseComplex(part);
|
|
268
|
+
if (complex.simples.length && matchComplexAt(element, complex, complex.simples.length - 1)) return true;
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Query descendants of a node in document order. */
|
|
274
|
+
export function querySelectorAllFrom(
|
|
275
|
+
root: Document | DocumentFragment | Element,
|
|
276
|
+
selector: string,
|
|
277
|
+
includeRoot: boolean,
|
|
278
|
+
): Element[] {
|
|
279
|
+
const result: Element[] = [];
|
|
280
|
+
const visit = (node: Node): void => {
|
|
281
|
+
if (node instanceof Element && matchesSelector(node, selector)) result.push(node);
|
|
282
|
+
for (const child of node.childNodes) visit(child);
|
|
283
|
+
};
|
|
284
|
+
if (includeRoot) {
|
|
285
|
+
for (const child of root.childNodes) visit(child);
|
|
286
|
+
} else {
|
|
287
|
+
for (const child of root.childNodes) visit(child);
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
package/src/dom.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Behavior-compatible reimplementation of linkedom's used surface. */
|
|
2
|
+
|
|
3
|
+
import { DOMWindow } from "./dom/core";
|
|
4
|
+
import { parseDocument } from "./dom/parser";
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
Attr,
|
|
8
|
+
Comment,
|
|
9
|
+
CSSStyleDeclaration,
|
|
10
|
+
CustomEvent,
|
|
11
|
+
DOMTokenList,
|
|
12
|
+
DOMWindow,
|
|
13
|
+
Document,
|
|
14
|
+
DocumentFragment,
|
|
15
|
+
Element,
|
|
16
|
+
Event,
|
|
17
|
+
EventTarget,
|
|
18
|
+
HTMLElement,
|
|
19
|
+
HTMLIFrameElement,
|
|
20
|
+
HTMLMetaElement,
|
|
21
|
+
HTMLTemplateElement,
|
|
22
|
+
NamedNodeMap,
|
|
23
|
+
Node,
|
|
24
|
+
NodeType,
|
|
25
|
+
SVGElement,
|
|
26
|
+
serializeNode,
|
|
27
|
+
Text,
|
|
28
|
+
} from "./dom/core";
|
|
29
|
+
|
|
30
|
+
/** Parse HTML or XML-like markup into a lightweight window and document. */
|
|
31
|
+
export function parseHTML(html: string): DOMWindow {
|
|
32
|
+
return new DOMWindow(parseDocument(html));
|
|
33
|
+
}
|