xbintsc 0.3.18 → 0.3.35
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 +33 -0
- 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/state.js +20 -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.d.ts +2 -1
- package/dist/src/driver/bundler/graph.js +32 -3
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/bundler/merge.d.ts +2 -1
- package/dist/src/driver/bundler/merge.js +16 -2
- 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 +75 -7
- 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/extensions/gui/html.d.ts +25 -0
- package/dist/src/extensions/gui/html.js +203 -0
- package/dist/src/extensions/gui/html.js.map +1 -0
- package/dist/src/extensions/gui/index.js +11 -0
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/registry.d.ts +29 -0
- package/dist/src/extensions/registry.js +14 -0
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lexer/scanner/identifiers.js +4 -12
- package/dist/src/lexer/scanner/identifiers.js.map +1 -1
- 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/bundler-resolve.test.d.ts +1 -0
- package/dist/tests/driver/bundler-resolve.test.js +230 -0
- package/dist/tests/driver/bundler-resolve.test.js.map +1 -0
- 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 +177 -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/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/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/extensions/gui-index.test.d.ts +1 -0
- package/dist/tests/extensions/gui-index.test.js +49 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.d.ts +1 -0
- package/dist/tests/extensions/gui.test.js +177 -0
- package/dist/tests/extensions/gui.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.js +36 -0
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/dist/tests/parser/statements.test.js +11 -0
- package/dist/tests/parser/statements.test.js.map +1 -1
- package/dist/tests/public-api.test.d.ts +1 -0
- package/dist/tests/public-api.test.js +31 -0
- package/dist/tests/public-api.test.js.map +1 -0
- package/package.json +1 -1
- 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/document.cpp +90 -0
- package/runtime/ext_gui/document.h +23 -0
- package/runtime/ext_gui/dom.cpp +43 -403
- package/runtime/ext_gui/dom.h +7 -0
- package/runtime/ext_gui/dom_api.cpp +164 -0
- package/runtime/ext_gui/dom_api.h +12 -0
- 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 +130 -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 +61 -245
- package/runtime/ext_gui/gui_engine.h +54 -0
- 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/script.cpp +63 -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 +60 -0
- package/runtime/rt.h +6 -0
- package/runtime/rt_internal.h +4 -0
- 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_stdlib2/set.inc +1 -1
- package/runtime/xt_typed_array.c +614 -0
- package/src/cli/main.ts +137 -29
- package/src/codegen/generator/calls/access.ts +9 -1
- package/src/codegen/generator/state.ts +20 -0
- package/src/codegen/generator/tables.ts +40 -0
- package/src/driver/bundler/commonjs.ts +553 -0
- package/src/driver/bundler/graph.ts +35 -2
- package/src/driver/bundler/merge.ts +18 -1
- package/src/driver/bundler/types.ts +8 -0
- package/src/driver/compiler.ts +85 -7
- 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/extensions/gui/html.ts +236 -0
- package/src/extensions/gui/index.ts +11 -0
- package/src/extensions/registry.ts +39 -0
- package/src/index.ts +1 -1
- package/src/lexer/scanner/identifiers.ts +4 -11
- 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 -493
- 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
|
|
|
@@ -37,6 +34,42 @@ Node *Node::append(std::unique_ptr<Node> child) {
|
|
|
37
34
|
return raw;
|
|
38
35
|
}
|
|
39
36
|
|
|
37
|
+
std::unique_ptr<Node> Node::detachChild(Node *child) {
|
|
38
|
+
for (auto it = children.begin(); it != children.end(); ++it) {
|
|
39
|
+
if (it->get() == child) {
|
|
40
|
+
std::unique_ptr<Node> owned = std::move(*it);
|
|
41
|
+
children.erase(it);
|
|
42
|
+
owned->parent = nullptr;
|
|
43
|
+
return owned;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return nullptr;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Node *Node::insertChild(std::unique_ptr<Node> child, Node *reference) {
|
|
50
|
+
child->parent = this;
|
|
51
|
+
Node *raw = child.get();
|
|
52
|
+
if (reference == nullptr) {
|
|
53
|
+
children.push_back(std::move(child));
|
|
54
|
+
return raw;
|
|
55
|
+
}
|
|
56
|
+
for (auto it = children.begin(); it != children.end(); ++it) {
|
|
57
|
+
if (it->get() == reference) {
|
|
58
|
+
children.insert(it, std::move(child));
|
|
59
|
+
return raw;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
children.push_back(std::move(child));
|
|
63
|
+
return raw;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
bool Node::contains(const Node *node) const {
|
|
67
|
+
for (const Node *current = node; current != nullptr; current = current->parent) {
|
|
68
|
+
if (current == this) return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
40
73
|
Node *Node::addElement(const std::string &tagName) {
|
|
41
74
|
auto node = std::make_unique<Node>(NodeType::Element);
|
|
42
75
|
node->tag = tagName;
|
|
@@ -76,397 +109,4 @@ int Node::elementSiblingCount() const {
|
|
|
76
109
|
return count;
|
|
77
110
|
}
|
|
78
111
|
|
|
79
|
-
/* -- helpers -------------------------------------------------------------- */
|
|
80
|
-
|
|
81
|
-
static bool isAsciiAlpha(char c) {
|
|
82
|
-
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
static bool isWhitespace(char c) {
|
|
86
|
-
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
static std::string toLower(const std::string &text) {
|
|
90
|
-
std::string out = text;
|
|
91
|
-
for (char &c : out) c = (char)std::tolower((unsigned char)c);
|
|
92
|
-
return out;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
static std::string trim(const std::string &text) {
|
|
96
|
-
size_t begin = 0;
|
|
97
|
-
size_t end = text.size();
|
|
98
|
-
while (begin < end && isWhitespace(text[begin])) begin++;
|
|
99
|
-
while (end > begin && isWhitespace(text[end - 1])) end--;
|
|
100
|
-
return text.substr(begin, end - begin);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Decode the handful of entities that show up in UI text. */
|
|
104
|
-
static std::string decodeEntities(const std::string &text) {
|
|
105
|
-
if (text.find('&') == std::string::npos) return text;
|
|
106
|
-
std::string out;
|
|
107
|
-
out.reserve(text.size());
|
|
108
|
-
for (size_t i = 0; i < text.size(); i++) {
|
|
109
|
-
if (text[i] != '&') {
|
|
110
|
-
out.push_back(text[i]);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
size_t semi = text.find(';', i + 1);
|
|
114
|
-
if (semi == std::string::npos || semi - i > 12) {
|
|
115
|
-
out.push_back('&');
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
std::string name = text.substr(i + 1, semi - i - 1);
|
|
119
|
-
if (name == "amp") out.push_back('&');
|
|
120
|
-
else if (name == "lt") out.push_back('<');
|
|
121
|
-
else if (name == "gt") out.push_back('>');
|
|
122
|
-
else if (name == "quot") out.push_back('"');
|
|
123
|
-
else if (name == "apos") out.push_back('\'');
|
|
124
|
-
else if (name == "nbsp") out.push_back(' ');
|
|
125
|
-
else if (!name.empty() && name[0] == '#') {
|
|
126
|
-
long code = 0;
|
|
127
|
-
if (name.size() > 1 && (name[1] == 'x' || name[1] == 'X')) {
|
|
128
|
-
for (size_t k = 2; k < name.size(); k++) {
|
|
129
|
-
char digit = (char)std::tolower((unsigned char)name[k]);
|
|
130
|
-
int value = (digit >= '0' && digit <= '9') ? digit - '0'
|
|
131
|
-
: (digit >= 'a' && digit <= 'f') ? digit - 'a' + 10
|
|
132
|
-
: -1;
|
|
133
|
-
if (value < 0) break;
|
|
134
|
-
code = code * 16 + value;
|
|
135
|
-
}
|
|
136
|
-
} else {
|
|
137
|
-
code = strtol(name.c_str() + 1, nullptr, 10);
|
|
138
|
-
}
|
|
139
|
-
if (code > 0 && code < 128) out.push_back((char)code);
|
|
140
|
-
else out.push_back('?');
|
|
141
|
-
} else {
|
|
142
|
-
out.append(text, i, semi - i + 1);
|
|
143
|
-
i = semi;
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
i = semi;
|
|
147
|
-
}
|
|
148
|
-
return out;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
static const std::unordered_map<std::string, bool> &voidElements() {
|
|
152
|
-
static const std::unordered_map<std::string, bool> set = {
|
|
153
|
-
{"area", true}, {"base", true}, {"br", true}, {"col", true},
|
|
154
|
-
{"embed", true}, {"hr", true}, {"img", true}, {"input", true},
|
|
155
|
-
{"link", true}, {"meta", true}, {"param", true}, {"source", true},
|
|
156
|
-
{"track", true}, {"wbr", true},
|
|
157
|
-
};
|
|
158
|
-
return set;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** Elements whose content is treated as raw text. `script` is discarded. */
|
|
162
|
-
static bool isRawText(const std::string &tag, bool *keepText) {
|
|
163
|
-
if (tag == "style" || tag == "title" || tag == "textarea") {
|
|
164
|
-
*keepText = true;
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
if (tag == "script" || tag == "xmp" || tag == "iframe" || tag == "noembed" ||
|
|
168
|
-
tag == "noframes" || tag == "plaintext") {
|
|
169
|
-
*keepText = false;
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/* -- tokenizer ------------------------------------------------------------ */
|
|
176
|
-
|
|
177
|
-
enum class TokenKind { Start, End, Text, EndOfFile };
|
|
178
|
-
|
|
179
|
-
struct Token {
|
|
180
|
-
TokenKind kind = TokenKind::EndOfFile;
|
|
181
|
-
std::string name;
|
|
182
|
-
std::vector<Attr> attrs;
|
|
183
|
-
bool selfClosing = false;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
class Tokenizer {
|
|
187
|
-
public:
|
|
188
|
-
explicit Tokenizer(const std::string &html) : html_(html) {}
|
|
189
|
-
|
|
190
|
-
std::vector<Token> run() {
|
|
191
|
-
std::vector<Token> tokens;
|
|
192
|
-
while (position_ < html_.size()) {
|
|
193
|
-
if (html_[position_] == '<' && position_ + 1 < html_.size()) {
|
|
194
|
-
char next = html_[position_ + 1];
|
|
195
|
-
if (next == '!') {
|
|
196
|
-
skipBogusComment();
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
if (next == '/') {
|
|
200
|
-
Token token = readEndTag();
|
|
201
|
-
if (!token.name.empty()) tokens.push_back(std::move(token));
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
if (isAsciiAlpha(next)) {
|
|
205
|
-
Token token = readStartTag();
|
|
206
|
-
tokens.push_back(token);
|
|
207
|
-
bool keepText = false;
|
|
208
|
-
if (isRawText(token.name, &keepText) && !token.selfClosing) {
|
|
209
|
-
readRawText(token.name, keepText, tokens);
|
|
210
|
-
}
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
std::string text = readText();
|
|
215
|
-
if (!text.empty()) {
|
|
216
|
-
Token token;
|
|
217
|
-
token.kind = TokenKind::Text;
|
|
218
|
-
token.name = decodeEntities(text);
|
|
219
|
-
tokens.push_back(std::move(token));
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return tokens;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
private:
|
|
226
|
-
const std::string &html_;
|
|
227
|
-
size_t position_ = 0;
|
|
228
|
-
|
|
229
|
-
void skipBogusComment() {
|
|
230
|
-
if (html_.compare(position_, 4, "<!--") == 0) {
|
|
231
|
-
size_t end = html_.find("-->", position_ + 4);
|
|
232
|
-
position_ = (end == std::string::npos) ? html_.size() : end + 3;
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
size_t end = html_.find('>', position_);
|
|
236
|
-
position_ = (end == std::string::npos) ? html_.size() : end + 1;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
std::string readText() {
|
|
240
|
-
size_t start = position_;
|
|
241
|
-
while (position_ < html_.size() && html_[position_] != '<') position_++;
|
|
242
|
-
return html_.substr(start, position_ - start);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
std::string readName() {
|
|
246
|
-
size_t start = position_;
|
|
247
|
-
while (position_ < html_.size()) {
|
|
248
|
-
char c = html_[position_];
|
|
249
|
-
if (isWhitespace(c) || c == '/' || c == '>') break;
|
|
250
|
-
position_++;
|
|
251
|
-
}
|
|
252
|
-
return toLower(html_.substr(start, position_ - start));
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
void skipWhitespace() {
|
|
256
|
-
while (position_ < html_.size() && isWhitespace(html_[position_])) position_++;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
Token readStartTag() {
|
|
260
|
-
Token token;
|
|
261
|
-
token.kind = TokenKind::Start;
|
|
262
|
-
position_++; // consume '<'
|
|
263
|
-
token.name = readName();
|
|
264
|
-
while (position_ < html_.size()) {
|
|
265
|
-
skipWhitespace();
|
|
266
|
-
if (position_ >= html_.size()) break;
|
|
267
|
-
char c = html_[position_];
|
|
268
|
-
if (c == '>') {
|
|
269
|
-
position_++;
|
|
270
|
-
break;
|
|
271
|
-
}
|
|
272
|
-
if (c == '/') {
|
|
273
|
-
token.selfClosing = true;
|
|
274
|
-
position_++;
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
readAttribute(token);
|
|
278
|
-
}
|
|
279
|
-
return token;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
void readAttribute(Token &token) {
|
|
283
|
-
size_t start = position_;
|
|
284
|
-
while (position_ < html_.size()) {
|
|
285
|
-
char c = html_[position_];
|
|
286
|
-
if (isWhitespace(c) || c == '=' || c == '>' || c == '/') break;
|
|
287
|
-
position_++;
|
|
288
|
-
}
|
|
289
|
-
std::string name = toLower(html_.substr(start, position_ - start));
|
|
290
|
-
if (name.empty()) {
|
|
291
|
-
position_++;
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
skipWhitespace();
|
|
295
|
-
std::string value;
|
|
296
|
-
if (position_ < html_.size() && html_[position_] == '=') {
|
|
297
|
-
position_++;
|
|
298
|
-
skipWhitespace();
|
|
299
|
-
if (position_ < html_.size() && (html_[position_] == '"' || html_[position_] == '\'')) {
|
|
300
|
-
char quote = html_[position_++];
|
|
301
|
-
size_t begin = position_;
|
|
302
|
-
while (position_ < html_.size() && html_[position_] != quote) position_++;
|
|
303
|
-
value = html_.substr(begin, position_ - begin);
|
|
304
|
-
if (position_ < html_.size()) position_++;
|
|
305
|
-
} else {
|
|
306
|
-
size_t begin = position_;
|
|
307
|
-
while (position_ < html_.size() && !isWhitespace(html_[position_]) && html_[position_] != '>') position_++;
|
|
308
|
-
value = html_.substr(begin, position_ - begin);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
token.attrs.push_back({name, decodeEntities(value)});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
Token readEndTag() {
|
|
315
|
-
Token token;
|
|
316
|
-
token.kind = TokenKind::End;
|
|
317
|
-
position_ += 2; // consume '</'
|
|
318
|
-
token.name = readName();
|
|
319
|
-
size_t end = html_.find('>', position_);
|
|
320
|
-
position_ = (end == std::string::npos) ? html_.size() : end + 1;
|
|
321
|
-
return token;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
void readRawText(const std::string &tag, bool keepText, std::vector<Token> &tokens) {
|
|
325
|
-
size_t start = position_;
|
|
326
|
-
std::string close = "</" + tag;
|
|
327
|
-
while (position_ < html_.size()) {
|
|
328
|
-
if (html_[position_] == '<' && toLower(html_.substr(position_, close.size())) == close) {
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
position_++;
|
|
332
|
-
}
|
|
333
|
-
std::string raw = html_.substr(start, position_ - start);
|
|
334
|
-
if (keepText && !raw.empty()) {
|
|
335
|
-
Token text;
|
|
336
|
-
text.kind = TokenKind::Text;
|
|
337
|
-
text.name = raw;
|
|
338
|
-
tokens.push_back(std::move(text));
|
|
339
|
-
}
|
|
340
|
-
if (position_ < html_.size()) {
|
|
341
|
-
Token end = readEndTag();
|
|
342
|
-
tokens.push_back(std::move(end));
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
/* -- tree builder --------------------------------------------------------- */
|
|
348
|
-
|
|
349
|
-
namespace {
|
|
350
|
-
|
|
351
|
-
bool isBlockTag(const std::string &tag) {
|
|
352
|
-
static const std::unordered_map<std::string, bool> set = {
|
|
353
|
-
{"address", true}, {"article", true}, {"aside", true}, {"blockquote", true},
|
|
354
|
-
{"div", true}, {"dl", true}, {"fieldset", true}, {"figure", true},
|
|
355
|
-
{"footer", true}, {"form", true}, {"h1", true}, {"h2", true},
|
|
356
|
-
{"h3", true}, {"h4", true}, {"h5", true}, {"h6", true},
|
|
357
|
-
{"header", true}, {"hr", true}, {"main", true}, {"nav", true},
|
|
358
|
-
{"ol", true}, {"p", true}, {"pre", true}, {"section", true},
|
|
359
|
-
{"table", true}, {"ul", true},
|
|
360
|
-
};
|
|
361
|
-
return set.count(tag) != 0;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
bool isHeading(const std::string &tag) {
|
|
365
|
-
return tag.size() == 2 && tag[0] == 'h' && tag[1] >= '1' && tag[1] <= '6';
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/** Whether a start tag implicitly closes the currently open element. */
|
|
369
|
-
bool closesOpenElement(const std::string &open, const std::string &next) {
|
|
370
|
-
if (open.empty()) return false;
|
|
371
|
-
if (next == "li" && open == "li") return true;
|
|
372
|
-
if ((next == "dt" || next == "dd") && (open == "dt" || open == "dd")) return true;
|
|
373
|
-
if (next == "option" && open == "option") return true;
|
|
374
|
-
if (next == "p" && open == "p") return true;
|
|
375
|
-
if (isHeading(next) && (isHeading(open) || open == "p")) return true;
|
|
376
|
-
if (isBlockTag(next) && open == "p") return true;
|
|
377
|
-
if ((next == "td" || next == "th") && (open == "td" || open == "th")) return true;
|
|
378
|
-
if (next == "tr" && (open == "td" || open == "th" || open == "tr")) return true;
|
|
379
|
-
if ((next == "thead" || next == "tbody" || next == "tfoot") &&
|
|
380
|
-
(open == "td" || open == "th" || open == "tr" || open == "thead" ||
|
|
381
|
-
open == "tbody" || open == "tfoot")) {
|
|
382
|
-
return true;
|
|
383
|
-
}
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
} // namespace
|
|
388
|
-
|
|
389
|
-
std::unique_ptr<Node> xt_html_parse(const std::string &html) {
|
|
390
|
-
Tokenizer tokenizer(html);
|
|
391
|
-
std::vector<Token> tokens = tokenizer.run();
|
|
392
|
-
|
|
393
|
-
auto document = std::make_unique<Node>(NodeType::Document);
|
|
394
|
-
std::vector<Node *> stack;
|
|
395
|
-
stack.push_back(document.get());
|
|
396
|
-
|
|
397
|
-
for (Token &token : tokens) {
|
|
398
|
-
if (token.kind == TokenKind::Text) {
|
|
399
|
-
Node *current = stack.back();
|
|
400
|
-
/* Text inside <style>/<title> is kept verbatim; elsewhere keep it too so
|
|
401
|
-
* layout can collapse whitespace later. */
|
|
402
|
-
current->addText(token.name);
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
if (token.kind == TokenKind::Start) {
|
|
406
|
-
while (stack.size() > 1 && closesOpenElement(stack.back()->tag, token.name)) {
|
|
407
|
-
stack.pop_back();
|
|
408
|
-
}
|
|
409
|
-
Node *element = stack.back()->addElement(token.name);
|
|
410
|
-
element->attrs = std::move(token.attrs);
|
|
411
|
-
bool isVoid = voidElements().count(token.name) != 0;
|
|
412
|
-
if (!isVoid && !token.selfClosing) stack.push_back(element);
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
415
|
-
if (token.kind == TokenKind::End) {
|
|
416
|
-
for (size_t i = stack.size(); i-- > 1;) {
|
|
417
|
-
if (stack[i]->tag == token.name) {
|
|
418
|
-
stack.resize(i);
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return document;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/* -- serialization (diagnostics) ------------------------------------------ */
|
|
429
|
-
|
|
430
|
-
static void serialize(const Node *node, std::string &out) {
|
|
431
|
-
if (node->type == NodeType::Document) {
|
|
432
|
-
for (const std::unique_ptr<Node> &child : node->children) serialize(child.get(), out);
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
if (node->type == NodeType::Text) {
|
|
436
|
-
out += trim(node->text);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
out += "<" + node->tag;
|
|
440
|
-
for (const Attr &attr : node->attrs) out += " " + attr.name + "=\"" + attr.value + "\"";
|
|
441
|
-
out += ">";
|
|
442
|
-
for (const std::unique_ptr<Node> &child : node->children) serialize(child.get(), out);
|
|
443
|
-
out += "</" + node->tag + ">";
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
std::string xt_dom_to_string(const Node *node) {
|
|
447
|
-
std::string out;
|
|
448
|
-
if (node != nullptr) serialize(node, out);
|
|
449
|
-
return out;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
std::string xt_dom_describe(const Node *node) {
|
|
453
|
-
if (node == nullptr) return "";
|
|
454
|
-
if (node->isText()) return "#text";
|
|
455
|
-
if (!node->isElement()) return "#document";
|
|
456
|
-
std::string out = node->tag;
|
|
457
|
-
const std::string *id = node->attr("id");
|
|
458
|
-
if (id != nullptr && !id->empty()) out += "#" + *id;
|
|
459
|
-
const std::string *classes = node->attr("class");
|
|
460
|
-
if (classes != nullptr) {
|
|
461
|
-
size_t index = 0;
|
|
462
|
-
while (index < classes->size()) {
|
|
463
|
-
while (index < classes->size() && std::isspace((unsigned char)(*classes)[index])) index++;
|
|
464
|
-
size_t start = index;
|
|
465
|
-
while (index < classes->size() && !std::isspace((unsigned char)(*classes)[index])) index++;
|
|
466
|
-
if (index > start) out += "." + classes->substr(start, index - start);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return out;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
112
|
} // namespace xtgui
|
package/runtime/ext_gui/dom.h
CHANGED
|
@@ -49,6 +49,13 @@ struct Node {
|
|
|
49
49
|
Node *addText(const std::string &data);
|
|
50
50
|
Node *append(std::unique_ptr<Node> child);
|
|
51
51
|
|
|
52
|
+
/** Detach a direct child, transferring ownership to the caller. */
|
|
53
|
+
std::unique_ptr<Node> detachChild(Node *child);
|
|
54
|
+
/** Insert `child` before `reference` (append when `reference` is nullptr). */
|
|
55
|
+
Node *insertChild(std::unique_ptr<Node> child, Node *reference);
|
|
56
|
+
/** True when `node` is this node or a descendant of it. */
|
|
57
|
+
bool contains(const Node *node) const;
|
|
58
|
+
|
|
52
59
|
/** First element child (skips text), or NULL. */
|
|
53
60
|
Node *firstElementChild() const;
|
|
54
61
|
/** Total number of element children with the same tag, and this node's index. */
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — the DOM object model exposed to TypeScript.
|
|
3
|
+
*
|
|
4
|
+
* Node/document handles are plain runtime objects with a shared prototype,
|
|
5
|
+
* exactly like the window handles in `window.cpp`. Each handle carries hidden
|
|
6
|
+
* properties:
|
|
7
|
+
*
|
|
8
|
+
* __xt_gui_win the owning window object (resolved with xt_gui_window_from_this)
|
|
9
|
+
* __xt_gui_gen the document generation it was created for
|
|
10
|
+
* __xt_gui_node an index into the window's node table (element handles)
|
|
11
|
+
* __xt_gui_doc true for the singleton `document` handle
|
|
12
|
+
*
|
|
13
|
+
* A DOM mutation marks the document dirty; the frame loop restyles/relayouts
|
|
14
|
+
* before the next paint.
|
|
15
|
+
*
|
|
16
|
+
* This unit owns the shared prototypes, the generic helpers and the public
|
|
17
|
+
* handle entry points. Event machinery lives in `dom_api_events.cpp`, the
|
|
18
|
+
* prototype table in `dom_api_proto.cpp`, and the element methods in the
|
|
19
|
+
* remaining `dom_api_*.cpp` units.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
#include "dom_api_internal.h"
|
|
23
|
+
|
|
24
|
+
#include <cctype>
|
|
25
|
+
#include <string>
|
|
26
|
+
#include <vector>
|
|
27
|
+
|
|
28
|
+
namespace domapi {
|
|
29
|
+
|
|
30
|
+
xt_value g_node_proto = XT_UNDEFINED;
|
|
31
|
+
xt_value g_document_proto = XT_UNDEFINED;
|
|
32
|
+
xt_value g_event_proto = XT_UNDEFINED;
|
|
33
|
+
xt_value g_class_list_proto = XT_UNDEFINED;
|
|
34
|
+
xt_value g_style_proto = XT_UNDEFINED;
|
|
35
|
+
|
|
36
|
+
/* -- small helpers -------------------------------------------------------- */
|
|
37
|
+
|
|
38
|
+
static const char *to_cstr(xt_value value, const char *fallback = "") {
|
|
39
|
+
const char *data = xt_string_data(xt_to_string(value));
|
|
40
|
+
return data != nullptr ? data : fallback;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
std::string to_string_value(xt_value value) { return std::string(to_cstr(value)); }
|
|
44
|
+
|
|
45
|
+
bool value_truthy(xt_value value) { return xt_truthy(value) != 0; }
|
|
46
|
+
|
|
47
|
+
/* -- handle plumbing ------------------------------------------------------ */
|
|
48
|
+
|
|
49
|
+
XtGuiWindow *resolve_window(xt_value self) {
|
|
50
|
+
return xt_gui_window_from_this(xt_object_get_cstr(self, "__xt_gui_win"));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Resolve a node handle to its node, or NULL when it is stale/foreign. */
|
|
54
|
+
xtgui::Node *resolve_node(xt_value self, XtGuiWindow **winOut) {
|
|
55
|
+
XtGuiWindow *win = resolve_window(self);
|
|
56
|
+
if (winOut != nullptr) *winOut = win;
|
|
57
|
+
if (win == nullptr || win->document == nullptr) return nullptr;
|
|
58
|
+
xt_value gen = xt_object_get_cstr(self, "__xt_gui_gen");
|
|
59
|
+
if (!XT_IS_NUMBER(gen) || xt_to_number(gen) != win->doc_generation) return nullptr;
|
|
60
|
+
if (value_truthy(xt_object_get_cstr(self, "__xt_gui_doc"))) {
|
|
61
|
+
return const_cast<xtgui::Node *>(win->document->root());
|
|
62
|
+
}
|
|
63
|
+
xt_value index = xt_object_get_cstr(self, "__xt_gui_node");
|
|
64
|
+
if (!XT_IS_NUMBER(index)) return nullptr;
|
|
65
|
+
int slot = (int)xt_to_number(index);
|
|
66
|
+
if (slot < 0 || slot >= (int)win->node_order.size()) return nullptr;
|
|
67
|
+
return win->node_order[(size_t)slot];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void mark_dirty(XtGuiWindow *win) {
|
|
71
|
+
if (win != nullptr && win->document != nullptr) win->document->invalidate();
|
|
72
|
+
if (win != nullptr) win->struct_dirty = 1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* -- attribute / class helpers -------------------------------------------- */
|
|
76
|
+
|
|
77
|
+
void set_attribute_raw(xtgui::Node *node, const std::string &name, const std::string &value) {
|
|
78
|
+
for (xtgui::Attr &attr : node->attrs) {
|
|
79
|
+
if (attr.name == name) {
|
|
80
|
+
attr.value = value;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
node->attrs.push_back({name, value});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
void remove_attribute_raw(xtgui::Node *node, const std::string &name) {
|
|
88
|
+
for (auto it = node->attrs.begin(); it != node->attrs.end(); ++it) {
|
|
89
|
+
if (it->name == name) {
|
|
90
|
+
node->attrs.erase(it);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void set_attribute(XtGuiWindow *win, xtgui::Node *node, const std::string &name,
|
|
97
|
+
const std::string &value) {
|
|
98
|
+
set_attribute_raw(node, name, value);
|
|
99
|
+
mark_dirty(win);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
std::string class_name_of(const xtgui::Node *node) {
|
|
103
|
+
const std::string *value = node->attr("class");
|
|
104
|
+
return value != nullptr ? *value : std::string();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void set_class_name(XtGuiWindow *win, xtgui::Node *node, const std::string &value) {
|
|
108
|
+
if (value.empty()) {
|
|
109
|
+
remove_attribute_raw(node, "class");
|
|
110
|
+
mark_dirty(win);
|
|
111
|
+
} else {
|
|
112
|
+
set_attribute(win, node, "class", value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
} // namespace domapi
|
|
117
|
+
|
|
118
|
+
xt_value xt_gui_node_handle(XtGuiWindow *win, const xtgui::Node *node) {
|
|
119
|
+
if (win == nullptr || node == nullptr) return XT_UNDEFINED;
|
|
120
|
+
auto cached = win->node_handles.find(node);
|
|
121
|
+
if (cached != win->node_handles.end()) return cached->second;
|
|
122
|
+
int slot;
|
|
123
|
+
auto found = win->node_index.find(node);
|
|
124
|
+
if (found != win->node_index.end()) {
|
|
125
|
+
slot = found->second;
|
|
126
|
+
} else {
|
|
127
|
+
slot = (int)win->node_order.size();
|
|
128
|
+
win->node_order.push_back(const_cast<xtgui::Node *>(node));
|
|
129
|
+
win->node_index[node] = slot;
|
|
130
|
+
}
|
|
131
|
+
xt_value handle = xt_object_new_with_proto(domapi::node_proto());
|
|
132
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_win"), win->object);
|
|
133
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_gen"), xt_number(win->doc_generation));
|
|
134
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_node"), xt_number((double)slot));
|
|
135
|
+
win->node_handles[node] = handle;
|
|
136
|
+
return handle;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
xt_value xt_gui_document_handle(XtGuiWindow *win) {
|
|
140
|
+
if (win == nullptr) return XT_UNDEFINED;
|
|
141
|
+
if (!XT_IS_OBJECT(win->document_object)) {
|
|
142
|
+
xt_value handle = xt_object_new_with_proto(domapi::document_proto());
|
|
143
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_win"), win->object);
|
|
144
|
+
xt_object_set(handle, xt_string_from_cstr("__xt_gui_doc"), XT_TRUE);
|
|
145
|
+
win->document_object = handle;
|
|
146
|
+
}
|
|
147
|
+
xt_object_set(win->document_object, xt_string_from_cstr("__xt_gui_gen"),
|
|
148
|
+
xt_number(win->doc_generation));
|
|
149
|
+
return win->document_object;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void xt_gui_handles_reset(XtGuiWindow *win) {
|
|
153
|
+
if (win == nullptr) return;
|
|
154
|
+
win->doc_generation += 1.0;
|
|
155
|
+
win->node_order.clear();
|
|
156
|
+
win->node_index.clear();
|
|
157
|
+
win->node_handles.clear();
|
|
158
|
+
win->node_listeners.clear();
|
|
159
|
+
win->struct_dirty = 0;
|
|
160
|
+
if (XT_IS_OBJECT(win->document_object)) {
|
|
161
|
+
xt_object_set(win->document_object, xt_string_from_cstr("__xt_gui_gen"),
|
|
162
|
+
xt_number(win->doc_generation));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — DOM object model entry points.
|
|
3
|
+
*
|
|
4
|
+
* The implementation lives in `dom_api.cpp`; this header only pulls in the
|
|
5
|
+
* shared engine definitions so translation units can include either one.
|
|
6
|
+
*/
|
|
7
|
+
#ifndef XT_GUI_DOM_API_H
|
|
8
|
+
#define XT_GUI_DOM_API_H
|
|
9
|
+
|
|
10
|
+
#include "gui_engine.h"
|
|
11
|
+
|
|
12
|
+
#endif /* XT_GUI_DOM_API_H */
|