xbintsc 0.3.25 → 0.3.46

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 (187) hide show
  1. package/README.md +39 -3
  2. package/dist/src/cli/main.js +124 -30
  3. package/dist/src/cli/main.js.map +1 -1
  4. package/dist/src/codegen/generator/calls/access.js +8 -1
  5. package/dist/src/codegen/generator/calls/access.js.map +1 -1
  6. package/dist/src/codegen/generator/module.js +10 -0
  7. package/dist/src/codegen/generator/module.js.map +1 -1
  8. package/dist/src/codegen/generator/state.js +24 -0
  9. package/dist/src/codegen/generator/state.js.map +1 -1
  10. package/dist/src/codegen/generator/tables.d.ts +6 -0
  11. package/dist/src/codegen/generator/tables.js +38 -0
  12. package/dist/src/codegen/generator/tables.js.map +1 -1
  13. package/dist/src/driver/bundler/commonjs.d.ts +52 -0
  14. package/dist/src/driver/bundler/commonjs.js +493 -0
  15. package/dist/src/driver/bundler/commonjs.js.map +1 -0
  16. package/dist/src/driver/bundler/graph.js +14 -0
  17. package/dist/src/driver/bundler/graph.js.map +1 -1
  18. package/dist/src/driver/bundler/merge.js +14 -0
  19. package/dist/src/driver/bundler/merge.js.map +1 -1
  20. package/dist/src/driver/bundler/types.d.ts +8 -0
  21. package/dist/src/driver/compiler.d.ts +8 -1
  22. package/dist/src/driver/compiler.js +73 -5
  23. package/dist/src/driver/compiler.js.map +1 -1
  24. package/dist/src/driver/config.d.ts +53 -0
  25. package/dist/src/driver/config.js +141 -0
  26. package/dist/src/driver/config.js.map +1 -0
  27. package/dist/src/driver/icon.d.ts +62 -0
  28. package/dist/src/driver/icon.js +151 -0
  29. package/dist/src/driver/icon.js.map +1 -0
  30. package/dist/src/driver/index.d.ts +4 -0
  31. package/dist/src/driver/index.js +4 -0
  32. package/dist/src/driver/index.js.map +1 -1
  33. package/dist/src/driver/mac-bundle.d.ts +41 -0
  34. package/dist/src/driver/mac-bundle.js +123 -0
  35. package/dist/src/driver/mac-bundle.js.map +1 -0
  36. package/dist/src/driver/win-icon.d.ts +27 -0
  37. package/dist/src/driver/win-icon.js +80 -0
  38. package/dist/src/driver/win-icon.js.map +1 -0
  39. package/dist/src/parser/statements.js +17 -4
  40. package/dist/src/parser/statements.js.map +1 -1
  41. package/dist/tests/cli/main.test.js +46 -1
  42. package/dist/tests/cli/main.test.js.map +1 -1
  43. package/dist/tests/driver/config.test.d.ts +1 -0
  44. package/dist/tests/driver/config.test.js +124 -0
  45. package/dist/tests/driver/config.test.js.map +1 -0
  46. package/dist/tests/driver/icon.test.d.ts +1 -0
  47. package/dist/tests/driver/icon.test.js +421 -0
  48. package/dist/tests/driver/icon.test.js.map +1 -0
  49. package/dist/tests/driver/modules.test.js +109 -0
  50. package/dist/tests/driver/modules.test.js.map +1 -1
  51. package/dist/tests/e2e/fetch.test.d.ts +12 -0
  52. package/dist/tests/e2e/fetch.test.js +272 -0
  53. package/dist/tests/e2e/fetch.test.js.map +1 -0
  54. package/dist/tests/e2e/gc.test.d.ts +1 -0
  55. package/dist/tests/e2e/gc.test.js +168 -0
  56. package/dist/tests/e2e/gc.test.js.map +1 -0
  57. package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
  58. package/dist/tests/e2e/gui-dom.test.js +102 -0
  59. package/dist/tests/e2e/gui-dom.test.js.map +1 -0
  60. package/dist/tests/e2e/gui-events.test.d.ts +5 -0
  61. package/dist/tests/e2e/gui-events.test.js +60 -0
  62. package/dist/tests/e2e/gui-events.test.js.map +1 -0
  63. package/dist/tests/e2e/gui-helpers.d.ts +31 -0
  64. package/dist/tests/e2e/gui-helpers.js +74 -0
  65. package/dist/tests/e2e/gui-helpers.js.map +1 -0
  66. package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
  67. package/dist/tests/e2e/gui-layout.test.js +88 -0
  68. package/dist/tests/e2e/gui-layout.test.js.map +1 -0
  69. package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
  70. package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
  71. package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
  72. package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
  73. package/dist/tests/e2e/gui-paint.test.js +111 -0
  74. package/dist/tests/e2e/gui-paint.test.js.map +1 -0
  75. package/dist/tests/e2e/gui-script.test.d.ts +5 -0
  76. package/dist/tests/e2e/gui-script.test.js +78 -0
  77. package/dist/tests/e2e/gui-script.test.js.map +1 -0
  78. package/dist/tests/e2e/gui-style.test.d.ts +5 -0
  79. package/dist/tests/e2e/gui-style.test.js +175 -0
  80. package/dist/tests/e2e/gui-style.test.js.map +1 -0
  81. package/dist/tests/e2e/harness.d.ts +2 -0
  82. package/dist/tests/e2e/harness.js +1 -0
  83. package/dist/tests/e2e/harness.js.map +1 -1
  84. package/dist/tests/e2e/icon.test.d.ts +7 -0
  85. package/dist/tests/e2e/icon.test.js +80 -0
  86. package/dist/tests/e2e/icon.test.js.map +1 -0
  87. package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
  88. package/dist/tests/e2e/npm-modules.test.js +47 -1
  89. package/dist/tests/e2e/npm-modules.test.js.map +1 -1
  90. package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
  91. package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
  92. package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
  93. package/dist/tests/parser/statements.test.js +11 -0
  94. package/dist/tests/parser/statements.test.js.map +1 -1
  95. package/package.json +3 -2
  96. package/runtime/ext_gui/css.cpp +4 -517
  97. package/runtime/ext_gui/css_internal.h +27 -0
  98. package/runtime/ext_gui/css_match.cpp +195 -0
  99. package/runtime/ext_gui/css_selector.cpp +207 -0
  100. package/runtime/ext_gui/css_sheet.cpp +129 -0
  101. package/runtime/ext_gui/dom.cpp +7 -403
  102. package/runtime/ext_gui/dom_api.cpp +63 -1146
  103. package/runtime/ext_gui/dom_api_document.cpp +96 -0
  104. package/runtime/ext_gui/dom_api_events.cpp +123 -0
  105. package/runtime/ext_gui/dom_api_internal.h +135 -0
  106. package/runtime/ext_gui/dom_api_node.cpp +329 -0
  107. package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
  108. package/runtime/ext_gui/dom_api_proto.cpp +135 -0
  109. package/runtime/ext_gui/dom_api_style.cpp +192 -0
  110. package/runtime/ext_gui/dom_internal.h +39 -0
  111. package/runtime/ext_gui/dom_parse.cpp +348 -0
  112. package/runtime/ext_gui/dom_serialize.cpp +54 -0
  113. package/runtime/ext_gui/gui.cpp +56 -286
  114. package/runtime/ext_gui/gui_events.cpp +294 -0
  115. package/runtime/ext_gui/gui_internal.h +29 -0
  116. package/runtime/ext_gui/layout.cpp +6 -597
  117. package/runtime/ext_gui/layout_flex.cpp +200 -0
  118. package/runtime/ext_gui/layout_flow.cpp +191 -0
  119. package/runtime/ext_gui/layout_flow_internal.h +149 -0
  120. package/runtime/ext_gui/layout_inline.cpp +181 -0
  121. package/runtime/ext_gui/layout_internal.h +20 -0
  122. package/runtime/ext_gui/renderer.cpp +15 -679
  123. package/runtime/ext_gui/renderer_image.cpp +233 -0
  124. package/runtime/ext_gui/renderer_internal.h +112 -0
  125. package/runtime/ext_gui/renderer_shaders.h +108 -0
  126. package/runtime/ext_gui/renderer_text.cpp +342 -0
  127. package/runtime/ext_gui/style.cpp +12 -660
  128. package/runtime/ext_gui/style_internal.h +33 -0
  129. package/runtime/ext_gui/style_print.cpp +113 -0
  130. package/runtime/ext_gui/style_ua.cpp +98 -0
  131. package/runtime/ext_gui/style_values.cpp +468 -0
  132. package/runtime/ext_gui/window.cpp +1 -0
  133. package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
  134. package/runtime/ext_node/dgram/dgram.c +1 -0
  135. package/runtime/ext_node/events/events.c +1 -0
  136. package/runtime/ext_node/fs/fs_ops.c +10 -25
  137. package/runtime/ext_node/fs/glob.c +13 -30
  138. package/runtime/ext_node/fs/promises.c +1 -0
  139. package/runtime/ext_node/http/parts/prototypes.inc +5 -0
  140. package/runtime/ext_node/net/parts/prototypes.inc +2 -0
  141. package/runtime/ext_node/process/process.c +9 -7
  142. package/runtime/ext_node/stream/stream.c +1 -0
  143. package/runtime/ext_node/util/util.c +6 -6
  144. package/runtime/rt.h +16 -0
  145. package/runtime/rt_internal.h +67 -2
  146. package/runtime/xt_alloc.c +442 -5
  147. package/runtime/xt_builtins.c +1 -0
  148. package/runtime/xt_containers/access.inc +32 -1
  149. package/runtime/xt_fetch/buffer.inc +64 -0
  150. package/runtime/xt_fetch/headers.inc +312 -0
  151. package/runtime/xt_fetch/request.inc +386 -0
  152. package/runtime/xt_fetch/response.inc +107 -0
  153. package/runtime/xt_fetch/socket.inc +72 -0
  154. package/runtime/xt_fetch/url.inc +108 -0
  155. package/runtime/xt_fetch.c +39 -0
  156. package/runtime/xt_generator.c +95 -1
  157. package/runtime/xt_loop.c +35 -1
  158. package/runtime/xt_promise.c +46 -0
  159. package/runtime/xt_stdlib2/error.inc +1 -0
  160. package/runtime/xt_stdlib2/regexp-match.inc +8 -7
  161. package/runtime/xt_stdlib2/set.inc +1 -1
  162. package/runtime/xt_symbol.c +2 -0
  163. package/runtime/xt_typed_array/construction.inc +142 -0
  164. package/runtime/xt_typed_array/elements.inc +92 -0
  165. package/runtime/xt_typed_array/methods.inc +329 -0
  166. package/runtime/xt_typed_array.c +72 -0
  167. package/runtime/xt_values/number-format.inc +26 -0
  168. package/scripts/check-file-length.ts +85 -0
  169. package/src/cli/main.ts +137 -29
  170. package/src/codegen/generator/calls/access.ts +9 -1
  171. package/src/codegen/generator/module.ts +10 -0
  172. package/src/codegen/generator/state.ts +24 -0
  173. package/src/codegen/generator/tables.ts +40 -0
  174. package/src/driver/bundler/commonjs.ts +553 -0
  175. package/src/driver/bundler/graph.ts +14 -0
  176. package/src/driver/bundler/merge.ts +15 -0
  177. package/src/driver/bundler/types.ts +8 -0
  178. package/src/driver/compiler.ts +83 -5
  179. package/src/driver/config.ts +173 -0
  180. package/src/driver/icon.ts +184 -0
  181. package/src/driver/index.ts +30 -0
  182. package/src/driver/mac-bundle.ts +156 -0
  183. package/src/driver/win-icon.ts +96 -0
  184. package/src/parser/statements.ts +17 -4
  185. package/dist/tests/e2e/gui.test.d.ts +0 -12
  186. package/dist/tests/e2e/gui.test.js +0 -702
  187. package/dist/tests/e2e/gui.test.js.map +0 -1
@@ -1,19 +1,16 @@
1
1
  /*
2
- * xbintsc GUI engine — HTML tokenizer + tree builder.
2
+ * xbintsc GUI engine — DOM node model.
3
3
  *
4
- * The tokenizer is a single pass that also swallows the contents of raw-text
5
- * elements (<style>, <script>, ...). The tree builder keeps a stack of open
6
- * elements and applies a small set of HTML "implied end tag" rules, which is
7
- * all a UI document needs.
4
+ * Defines the `Node` helpers shared by the HTML tree builder (`dom_parse.cpp`)
5
+ * and the serializer (`dom_serialize.cpp`). The tree is deliberately simple:
6
+ * document/element/text nodes are one type so the rest of the engine can walk
7
+ * it without casts.
8
8
  */
9
9
 
10
10
  #include "dom.h"
11
11
 
12
- #include <algorithm>
13
- #include <cctype>
14
- #include <cstdlib>
15
- #include <cstring>
16
- #include <unordered_map>
12
+ #include <memory>
13
+ #include <utility>
17
14
 
18
15
  namespace xtgui {
19
16
 
@@ -112,397 +109,4 @@ int Node::elementSiblingCount() const {
112
109
  return count;
113
110
  }
114
111
 
115
- /* -- helpers -------------------------------------------------------------- */
116
-
117
- static bool isAsciiAlpha(char c) {
118
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
119
- }
120
-
121
- static bool isWhitespace(char c) {
122
- return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
123
- }
124
-
125
- static std::string toLower(const std::string &text) {
126
- std::string out = text;
127
- for (char &c : out) c = (char)std::tolower((unsigned char)c);
128
- return out;
129
- }
130
-
131
- static std::string trim(const std::string &text) {
132
- size_t begin = 0;
133
- size_t end = text.size();
134
- while (begin < end && isWhitespace(text[begin])) begin++;
135
- while (end > begin && isWhitespace(text[end - 1])) end--;
136
- return text.substr(begin, end - begin);
137
- }
138
-
139
- /** Decode the handful of entities that show up in UI text. */
140
- static std::string decodeEntities(const std::string &text) {
141
- if (text.find('&') == std::string::npos) return text;
142
- std::string out;
143
- out.reserve(text.size());
144
- for (size_t i = 0; i < text.size(); i++) {
145
- if (text[i] != '&') {
146
- out.push_back(text[i]);
147
- continue;
148
- }
149
- size_t semi = text.find(';', i + 1);
150
- if (semi == std::string::npos || semi - i > 12) {
151
- out.push_back('&');
152
- continue;
153
- }
154
- std::string name = text.substr(i + 1, semi - i - 1);
155
- if (name == "amp") out.push_back('&');
156
- else if (name == "lt") out.push_back('<');
157
- else if (name == "gt") out.push_back('>');
158
- else if (name == "quot") out.push_back('"');
159
- else if (name == "apos") out.push_back('\'');
160
- else if (name == "nbsp") out.push_back(' ');
161
- else if (!name.empty() && name[0] == '#') {
162
- long code = 0;
163
- if (name.size() > 1 && (name[1] == 'x' || name[1] == 'X')) {
164
- for (size_t k = 2; k < name.size(); k++) {
165
- char digit = (char)std::tolower((unsigned char)name[k]);
166
- int value = (digit >= '0' && digit <= '9') ? digit - '0'
167
- : (digit >= 'a' && digit <= 'f') ? digit - 'a' + 10
168
- : -1;
169
- if (value < 0) break;
170
- code = code * 16 + value;
171
- }
172
- } else {
173
- code = strtol(name.c_str() + 1, nullptr, 10);
174
- }
175
- if (code > 0 && code < 128) out.push_back((char)code);
176
- else out.push_back('?');
177
- } else {
178
- out.append(text, i, semi - i + 1);
179
- i = semi;
180
- continue;
181
- }
182
- i = semi;
183
- }
184
- return out;
185
- }
186
-
187
- static const std::unordered_map<std::string, bool> &voidElements() {
188
- static const std::unordered_map<std::string, bool> set = {
189
- {"area", true}, {"base", true}, {"br", true}, {"col", true},
190
- {"embed", true}, {"hr", true}, {"img", true}, {"input", true},
191
- {"link", true}, {"meta", true}, {"param", true}, {"source", true},
192
- {"track", true}, {"wbr", true},
193
- };
194
- return set;
195
- }
196
-
197
- /** Elements whose content is treated as raw text. `script` is discarded. */
198
- static bool isRawText(const std::string &tag, bool *keepText) {
199
- if (tag == "style" || tag == "title" || tag == "textarea") {
200
- *keepText = true;
201
- return true;
202
- }
203
- if (tag == "script" || tag == "xmp" || tag == "iframe" || tag == "noembed" ||
204
- tag == "noframes" || tag == "plaintext") {
205
- *keepText = false;
206
- return true;
207
- }
208
- return false;
209
- }
210
-
211
- /* -- tokenizer ------------------------------------------------------------ */
212
-
213
- enum class TokenKind { Start, End, Text, EndOfFile };
214
-
215
- struct Token {
216
- TokenKind kind = TokenKind::EndOfFile;
217
- std::string name;
218
- std::vector<Attr> attrs;
219
- bool selfClosing = false;
220
- };
221
-
222
- class Tokenizer {
223
- public:
224
- explicit Tokenizer(const std::string &html) : html_(html) {}
225
-
226
- std::vector<Token> run() {
227
- std::vector<Token> tokens;
228
- while (position_ < html_.size()) {
229
- if (html_[position_] == '<' && position_ + 1 < html_.size()) {
230
- char next = html_[position_ + 1];
231
- if (next == '!') {
232
- skipBogusComment();
233
- continue;
234
- }
235
- if (next == '/') {
236
- Token token = readEndTag();
237
- if (!token.name.empty()) tokens.push_back(std::move(token));
238
- continue;
239
- }
240
- if (isAsciiAlpha(next)) {
241
- Token token = readStartTag();
242
- tokens.push_back(token);
243
- bool keepText = false;
244
- if (isRawText(token.name, &keepText) && !token.selfClosing) {
245
- readRawText(token.name, keepText, tokens);
246
- }
247
- continue;
248
- }
249
- }
250
- std::string text = readText();
251
- if (!text.empty()) {
252
- Token token;
253
- token.kind = TokenKind::Text;
254
- token.name = decodeEntities(text);
255
- tokens.push_back(std::move(token));
256
- }
257
- }
258
- return tokens;
259
- }
260
-
261
- private:
262
- const std::string &html_;
263
- size_t position_ = 0;
264
-
265
- void skipBogusComment() {
266
- if (html_.compare(position_, 4, "<!--") == 0) {
267
- size_t end = html_.find("-->", position_ + 4);
268
- position_ = (end == std::string::npos) ? html_.size() : end + 3;
269
- return;
270
- }
271
- size_t end = html_.find('>', position_);
272
- position_ = (end == std::string::npos) ? html_.size() : end + 1;
273
- }
274
-
275
- std::string readText() {
276
- size_t start = position_;
277
- while (position_ < html_.size() && html_[position_] != '<') position_++;
278
- return html_.substr(start, position_ - start);
279
- }
280
-
281
- std::string readName() {
282
- size_t start = position_;
283
- while (position_ < html_.size()) {
284
- char c = html_[position_];
285
- if (isWhitespace(c) || c == '/' || c == '>') break;
286
- position_++;
287
- }
288
- return toLower(html_.substr(start, position_ - start));
289
- }
290
-
291
- void skipWhitespace() {
292
- while (position_ < html_.size() && isWhitespace(html_[position_])) position_++;
293
- }
294
-
295
- Token readStartTag() {
296
- Token token;
297
- token.kind = TokenKind::Start;
298
- position_++; // consume '<'
299
- token.name = readName();
300
- while (position_ < html_.size()) {
301
- skipWhitespace();
302
- if (position_ >= html_.size()) break;
303
- char c = html_[position_];
304
- if (c == '>') {
305
- position_++;
306
- break;
307
- }
308
- if (c == '/') {
309
- token.selfClosing = true;
310
- position_++;
311
- continue;
312
- }
313
- readAttribute(token);
314
- }
315
- return token;
316
- }
317
-
318
- void readAttribute(Token &token) {
319
- size_t start = position_;
320
- while (position_ < html_.size()) {
321
- char c = html_[position_];
322
- if (isWhitespace(c) || c == '=' || c == '>' || c == '/') break;
323
- position_++;
324
- }
325
- std::string name = toLower(html_.substr(start, position_ - start));
326
- if (name.empty()) {
327
- position_++;
328
- return;
329
- }
330
- skipWhitespace();
331
- std::string value;
332
- if (position_ < html_.size() && html_[position_] == '=') {
333
- position_++;
334
- skipWhitespace();
335
- if (position_ < html_.size() && (html_[position_] == '"' || html_[position_] == '\'')) {
336
- char quote = html_[position_++];
337
- size_t begin = position_;
338
- while (position_ < html_.size() && html_[position_] != quote) position_++;
339
- value = html_.substr(begin, position_ - begin);
340
- if (position_ < html_.size()) position_++;
341
- } else {
342
- size_t begin = position_;
343
- while (position_ < html_.size() && !isWhitespace(html_[position_]) && html_[position_] != '>') position_++;
344
- value = html_.substr(begin, position_ - begin);
345
- }
346
- }
347
- token.attrs.push_back({name, decodeEntities(value)});
348
- }
349
-
350
- Token readEndTag() {
351
- Token token;
352
- token.kind = TokenKind::End;
353
- position_ += 2; // consume '</'
354
- token.name = readName();
355
- size_t end = html_.find('>', position_);
356
- position_ = (end == std::string::npos) ? html_.size() : end + 1;
357
- return token;
358
- }
359
-
360
- void readRawText(const std::string &tag, bool keepText, std::vector<Token> &tokens) {
361
- size_t start = position_;
362
- std::string close = "</" + tag;
363
- while (position_ < html_.size()) {
364
- if (html_[position_] == '<' && toLower(html_.substr(position_, close.size())) == close) {
365
- break;
366
- }
367
- position_++;
368
- }
369
- std::string raw = html_.substr(start, position_ - start);
370
- if (keepText && !raw.empty()) {
371
- Token text;
372
- text.kind = TokenKind::Text;
373
- text.name = raw;
374
- tokens.push_back(std::move(text));
375
- }
376
- if (position_ < html_.size()) {
377
- Token end = readEndTag();
378
- tokens.push_back(std::move(end));
379
- }
380
- }
381
- };
382
-
383
- /* -- tree builder --------------------------------------------------------- */
384
-
385
- namespace {
386
-
387
- bool isBlockTag(const std::string &tag) {
388
- static const std::unordered_map<std::string, bool> set = {
389
- {"address", true}, {"article", true}, {"aside", true}, {"blockquote", true},
390
- {"div", true}, {"dl", true}, {"fieldset", true}, {"figure", true},
391
- {"footer", true}, {"form", true}, {"h1", true}, {"h2", true},
392
- {"h3", true}, {"h4", true}, {"h5", true}, {"h6", true},
393
- {"header", true}, {"hr", true}, {"main", true}, {"nav", true},
394
- {"ol", true}, {"p", true}, {"pre", true}, {"section", true},
395
- {"table", true}, {"ul", true},
396
- };
397
- return set.count(tag) != 0;
398
- }
399
-
400
- bool isHeading(const std::string &tag) {
401
- return tag.size() == 2 && tag[0] == 'h' && tag[1] >= '1' && tag[1] <= '6';
402
- }
403
-
404
- /** Whether a start tag implicitly closes the currently open element. */
405
- bool closesOpenElement(const std::string &open, const std::string &next) {
406
- if (open.empty()) return false;
407
- if (next == "li" && open == "li") return true;
408
- if ((next == "dt" || next == "dd") && (open == "dt" || open == "dd")) return true;
409
- if (next == "option" && open == "option") return true;
410
- if (next == "p" && open == "p") return true;
411
- if (isHeading(next) && (isHeading(open) || open == "p")) return true;
412
- if (isBlockTag(next) && open == "p") return true;
413
- if ((next == "td" || next == "th") && (open == "td" || open == "th")) return true;
414
- if (next == "tr" && (open == "td" || open == "th" || open == "tr")) return true;
415
- if ((next == "thead" || next == "tbody" || next == "tfoot") &&
416
- (open == "td" || open == "th" || open == "tr" || open == "thead" ||
417
- open == "tbody" || open == "tfoot")) {
418
- return true;
419
- }
420
- return false;
421
- }
422
-
423
- } // namespace
424
-
425
- std::unique_ptr<Node> xt_html_parse(const std::string &html) {
426
- Tokenizer tokenizer(html);
427
- std::vector<Token> tokens = tokenizer.run();
428
-
429
- auto document = std::make_unique<Node>(NodeType::Document);
430
- std::vector<Node *> stack;
431
- stack.push_back(document.get());
432
-
433
- for (Token &token : tokens) {
434
- if (token.kind == TokenKind::Text) {
435
- Node *current = stack.back();
436
- /* Text inside <style>/<title> is kept verbatim; elsewhere keep it too so
437
- * layout can collapse whitespace later. */
438
- current->addText(token.name);
439
- continue;
440
- }
441
- if (token.kind == TokenKind::Start) {
442
- while (stack.size() > 1 && closesOpenElement(stack.back()->tag, token.name)) {
443
- stack.pop_back();
444
- }
445
- Node *element = stack.back()->addElement(token.name);
446
- element->attrs = std::move(token.attrs);
447
- bool isVoid = voidElements().count(token.name) != 0;
448
- if (!isVoid && !token.selfClosing) stack.push_back(element);
449
- continue;
450
- }
451
- if (token.kind == TokenKind::End) {
452
- for (size_t i = stack.size(); i-- > 1;) {
453
- if (stack[i]->tag == token.name) {
454
- stack.resize(i);
455
- break;
456
- }
457
- }
458
- }
459
- }
460
-
461
- return document;
462
- }
463
-
464
- /* -- serialization (diagnostics) ------------------------------------------ */
465
-
466
- static void serialize(const Node *node, std::string &out) {
467
- if (node->type == NodeType::Document) {
468
- for (const std::unique_ptr<Node> &child : node->children) serialize(child.get(), out);
469
- return;
470
- }
471
- if (node->type == NodeType::Text) {
472
- out += trim(node->text);
473
- return;
474
- }
475
- out += "<" + node->tag;
476
- for (const Attr &attr : node->attrs) out += " " + attr.name + "=\"" + attr.value + "\"";
477
- out += ">";
478
- for (const std::unique_ptr<Node> &child : node->children) serialize(child.get(), out);
479
- out += "</" + node->tag + ">";
480
- }
481
-
482
- std::string xt_dom_to_string(const Node *node) {
483
- std::string out;
484
- if (node != nullptr) serialize(node, out);
485
- return out;
486
- }
487
-
488
- std::string xt_dom_describe(const Node *node) {
489
- if (node == nullptr) return "";
490
- if (node->isText()) return "#text";
491
- if (!node->isElement()) return "#document";
492
- std::string out = node->tag;
493
- const std::string *id = node->attr("id");
494
- if (id != nullptr && !id->empty()) out += "#" + *id;
495
- const std::string *classes = node->attr("class");
496
- if (classes != nullptr) {
497
- size_t index = 0;
498
- while (index < classes->size()) {
499
- while (index < classes->size() && std::isspace((unsigned char)(*classes)[index])) index++;
500
- size_t start = index;
501
- while (index < classes->size() && !std::isspace((unsigned char)(*classes)[index])) index++;
502
- if (index > start) out += "." + classes->substr(start, index - start);
503
- }
504
- }
505
- return out;
506
- }
507
-
508
112
  } // namespace xtgui