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.
- package/README.md +39 -3
- package/dist/src/cli/main.js +124 -30
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.js +8 -1
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/module.js +10 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +24 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +6 -0
- package/dist/src/codegen/generator/tables.js +38 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/bundler/commonjs.d.ts +52 -0
- package/dist/src/driver/bundler/commonjs.js +493 -0
- package/dist/src/driver/bundler/commonjs.js.map +1 -0
- package/dist/src/driver/bundler/graph.js +14 -0
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/bundler/merge.js +14 -0
- package/dist/src/driver/bundler/merge.js.map +1 -1
- package/dist/src/driver/bundler/types.d.ts +8 -0
- package/dist/src/driver/compiler.d.ts +8 -1
- package/dist/src/driver/compiler.js +73 -5
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/config.d.ts +53 -0
- package/dist/src/driver/config.js +141 -0
- package/dist/src/driver/config.js.map +1 -0
- package/dist/src/driver/icon.d.ts +62 -0
- package/dist/src/driver/icon.js +151 -0
- package/dist/src/driver/icon.js.map +1 -0
- package/dist/src/driver/index.d.ts +4 -0
- package/dist/src/driver/index.js +4 -0
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/mac-bundle.d.ts +41 -0
- package/dist/src/driver/mac-bundle.js +123 -0
- package/dist/src/driver/mac-bundle.js.map +1 -0
- package/dist/src/driver/win-icon.d.ts +27 -0
- package/dist/src/driver/win-icon.js +80 -0
- package/dist/src/driver/win-icon.js.map +1 -0
- package/dist/src/parser/statements.js +17 -4
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/tests/cli/main.test.js +46 -1
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/config.test.d.ts +1 -0
- package/dist/tests/driver/config.test.js +124 -0
- package/dist/tests/driver/config.test.js.map +1 -0
- package/dist/tests/driver/icon.test.d.ts +1 -0
- package/dist/tests/driver/icon.test.js +421 -0
- package/dist/tests/driver/icon.test.js.map +1 -0
- package/dist/tests/driver/modules.test.js +109 -0
- package/dist/tests/driver/modules.test.js.map +1 -1
- package/dist/tests/e2e/fetch.test.d.ts +12 -0
- package/dist/tests/e2e/fetch.test.js +272 -0
- package/dist/tests/e2e/fetch.test.js.map +1 -0
- package/dist/tests/e2e/gc.test.d.ts +1 -0
- package/dist/tests/e2e/gc.test.js +168 -0
- package/dist/tests/e2e/gc.test.js.map +1 -0
- package/dist/tests/e2e/gui-dom.test.d.ts +5 -0
- package/dist/tests/e2e/gui-dom.test.js +102 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -0
- package/dist/tests/e2e/gui-events.test.d.ts +5 -0
- package/dist/tests/e2e/gui-events.test.js +60 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -0
- package/dist/tests/e2e/gui-helpers.d.ts +31 -0
- package/dist/tests/e2e/gui-helpers.js +74 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -0
- package/dist/tests/e2e/gui-layout.test.d.ts +4 -0
- package/dist/tests/e2e/gui-layout.test.js +88 -0
- package/dist/tests/e2e/gui-layout.test.js.map +1 -0
- package/dist/tests/e2e/gui-lifecycle.test.d.ts +5 -0
- package/dist/tests/e2e/gui-lifecycle.test.js +58 -0
- package/dist/tests/e2e/gui-lifecycle.test.js.map +1 -0
- package/dist/tests/e2e/gui-paint.test.d.ts +5 -0
- package/dist/tests/e2e/gui-paint.test.js +111 -0
- package/dist/tests/e2e/gui-paint.test.js.map +1 -0
- package/dist/tests/e2e/gui-script.test.d.ts +5 -0
- package/dist/tests/e2e/gui-script.test.js +78 -0
- package/dist/tests/e2e/gui-script.test.js.map +1 -0
- package/dist/tests/e2e/gui-style.test.d.ts +5 -0
- package/dist/tests/e2e/gui-style.test.js +175 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +2 -0
- package/dist/tests/e2e/harness.js +1 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/e2e/icon.test.d.ts +7 -0
- package/dist/tests/e2e/icon.test.js +80 -0
- package/dist/tests/e2e/icon.test.js.map +1 -0
- package/dist/tests/e2e/npm-modules.test.d.ts +4 -1
- package/dist/tests/e2e/npm-modules.test.js +47 -1
- package/dist/tests/e2e/npm-modules.test.js.map +1 -1
- package/dist/tests/e2e/runtime-coverage-typed-array.test.d.ts +9 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js +140 -0
- package/dist/tests/e2e/runtime-coverage-typed-array.test.js.map +1 -0
- package/dist/tests/parser/statements.test.js +11 -0
- package/dist/tests/parser/statements.test.js.map +1 -1
- package/package.json +3 -2
- package/runtime/ext_gui/css.cpp +4 -517
- package/runtime/ext_gui/css_internal.h +27 -0
- package/runtime/ext_gui/css_match.cpp +195 -0
- package/runtime/ext_gui/css_selector.cpp +207 -0
- package/runtime/ext_gui/css_sheet.cpp +129 -0
- package/runtime/ext_gui/dom.cpp +7 -403
- package/runtime/ext_gui/dom_api.cpp +63 -1146
- package/runtime/ext_gui/dom_api_document.cpp +96 -0
- package/runtime/ext_gui/dom_api_events.cpp +123 -0
- package/runtime/ext_gui/dom_api_internal.h +135 -0
- package/runtime/ext_gui/dom_api_node.cpp +329 -0
- package/runtime/ext_gui/dom_api_node_read.cpp +294 -0
- package/runtime/ext_gui/dom_api_proto.cpp +135 -0
- package/runtime/ext_gui/dom_api_style.cpp +192 -0
- package/runtime/ext_gui/dom_internal.h +39 -0
- package/runtime/ext_gui/dom_parse.cpp +348 -0
- package/runtime/ext_gui/dom_serialize.cpp +54 -0
- package/runtime/ext_gui/gui.cpp +56 -286
- package/runtime/ext_gui/gui_events.cpp +294 -0
- package/runtime/ext_gui/gui_internal.h +29 -0
- package/runtime/ext_gui/layout.cpp +6 -597
- package/runtime/ext_gui/layout_flex.cpp +200 -0
- package/runtime/ext_gui/layout_flow.cpp +191 -0
- package/runtime/ext_gui/layout_flow_internal.h +149 -0
- package/runtime/ext_gui/layout_inline.cpp +181 -0
- package/runtime/ext_gui/layout_internal.h +20 -0
- package/runtime/ext_gui/renderer.cpp +15 -679
- package/runtime/ext_gui/renderer_image.cpp +233 -0
- package/runtime/ext_gui/renderer_internal.h +112 -0
- package/runtime/ext_gui/renderer_shaders.h +108 -0
- package/runtime/ext_gui/renderer_text.cpp +342 -0
- package/runtime/ext_gui/style.cpp +12 -660
- package/runtime/ext_gui/style_internal.h +33 -0
- package/runtime/ext_gui/style_print.cpp +113 -0
- package/runtime/ext_gui/style_ua.cpp +98 -0
- package/runtime/ext_gui/style_values.cpp +468 -0
- package/runtime/ext_gui/window.cpp +1 -0
- package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
- package/runtime/ext_node/dgram/dgram.c +1 -0
- package/runtime/ext_node/events/events.c +1 -0
- package/runtime/ext_node/fs/fs_ops.c +10 -25
- package/runtime/ext_node/fs/glob.c +13 -30
- package/runtime/ext_node/fs/promises.c +1 -0
- package/runtime/ext_node/http/parts/prototypes.inc +5 -0
- package/runtime/ext_node/net/parts/prototypes.inc +2 -0
- package/runtime/ext_node/process/process.c +9 -7
- package/runtime/ext_node/stream/stream.c +1 -0
- package/runtime/ext_node/util/util.c +6 -6
- package/runtime/rt.h +16 -0
- package/runtime/rt_internal.h +67 -2
- package/runtime/xt_alloc.c +442 -5
- package/runtime/xt_builtins.c +1 -0
- package/runtime/xt_containers/access.inc +32 -1
- package/runtime/xt_fetch/buffer.inc +64 -0
- package/runtime/xt_fetch/headers.inc +312 -0
- package/runtime/xt_fetch/request.inc +386 -0
- package/runtime/xt_fetch/response.inc +107 -0
- package/runtime/xt_fetch/socket.inc +72 -0
- package/runtime/xt_fetch/url.inc +108 -0
- package/runtime/xt_fetch.c +39 -0
- package/runtime/xt_generator.c +95 -1
- package/runtime/xt_loop.c +35 -1
- package/runtime/xt_promise.c +46 -0
- package/runtime/xt_stdlib2/error.inc +1 -0
- package/runtime/xt_stdlib2/regexp-match.inc +8 -7
- package/runtime/xt_stdlib2/set.inc +1 -1
- package/runtime/xt_symbol.c +2 -0
- package/runtime/xt_typed_array/construction.inc +142 -0
- package/runtime/xt_typed_array/elements.inc +92 -0
- package/runtime/xt_typed_array/methods.inc +329 -0
- package/runtime/xt_typed_array.c +72 -0
- package/runtime/xt_values/number-format.inc +26 -0
- package/scripts/check-file-length.ts +85 -0
- package/src/cli/main.ts +137 -29
- package/src/codegen/generator/calls/access.ts +9 -1
- package/src/codegen/generator/module.ts +10 -0
- package/src/codegen/generator/state.ts +24 -0
- package/src/codegen/generator/tables.ts +40 -0
- package/src/driver/bundler/commonjs.ts +553 -0
- package/src/driver/bundler/graph.ts +14 -0
- package/src/driver/bundler/merge.ts +15 -0
- package/src/driver/bundler/types.ts +8 -0
- package/src/driver/compiler.ts +83 -5
- package/src/driver/config.ts +173 -0
- package/src/driver/icon.ts +184 -0
- package/src/driver/index.ts +30 -0
- package/src/driver/mac-bundle.ts +156 -0
- package/src/driver/win-icon.ts +96 -0
- package/src/parser/statements.ts +17 -4
- package/dist/tests/e2e/gui.test.d.ts +0 -12
- package/dist/tests/e2e/gui.test.js +0 -702
- package/dist/tests/e2e/gui.test.js.map +0 -1
package/runtime/ext_gui/dom.cpp
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* xbintsc GUI engine —
|
|
2
|
+
* xbintsc GUI engine — DOM node model.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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 <
|
|
13
|
-
#include <
|
|
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
|