xbintsc 0.5.32 → 0.5.37
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 +10 -7
- package/README.zh-CN.md +10 -8
- package/dist/scripts/check-version.d.ts +27 -0
- package/dist/scripts/check-version.js +71 -0
- package/dist/scripts/check-version.js.map +1 -0
- package/dist/scripts/package-platform.d.ts +58 -0
- package/dist/scripts/package-platform.js +142 -0
- package/dist/scripts/package-platform.js.map +1 -0
- package/dist/scripts/verify-platform-package.d.ts +34 -0
- package/dist/scripts/verify-platform-package.js +161 -0
- package/dist/scripts/verify-platform-package.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/main.js +3 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/runtime-lib.d.ts +29 -4
- package/dist/src/driver/runtime-lib.js +68 -10
- package/dist/src/driver/runtime-lib.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/extensions/gui/index.js +18 -5
- package/dist/src/extensions/gui/index.js.map +1 -1
- package/dist/src/extensions/media/index.js +4 -3
- package/dist/src/extensions/media/index.js.map +1 -1
- package/dist/src/parser/context.d.ts +41 -0
- package/dist/src/parser/context.js +89 -4
- package/dist/src/parser/context.js.map +1 -1
- package/dist/src/parser/literals.js +6 -12
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/tests/driver/platform-package.test.d.ts +1 -0
- package/dist/tests/driver/platform-package.test.js +49 -0
- package/dist/tests/driver/platform-package.test.js.map +1 -0
- package/dist/tests/driver/runtime-lib.test.js +51 -4
- package/dist/tests/driver/runtime-lib.test.js.map +1 -1
- package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
- package/dist/tests/e2e/gui-audio.test.js +287 -0
- package/dist/tests/e2e/gui-audio.test.js.map +1 -0
- package/dist/tests/e2e/gui-browser.test.js +65 -0
- package/dist/tests/e2e/gui-browser.test.js.map +1 -1
- package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
- package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
- package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
- package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
- package/dist/tests/e2e/gui-dom.test.js +215 -0
- package/dist/tests/e2e/gui-dom.test.js.map +1 -1
- package/dist/tests/e2e/gui-events.test.js +112 -0
- package/dist/tests/e2e/gui-events.test.js.map +1 -1
- package/dist/tests/e2e/gui-helpers.d.ts +7 -0
- package/dist/tests/e2e/gui-helpers.js +1 -0
- package/dist/tests/e2e/gui-helpers.js.map +1 -1
- package/dist/tests/e2e/gui-media.test.js +78 -0
- package/dist/tests/e2e/gui-media.test.js.map +1 -1
- package/dist/tests/e2e/gui-style.test.js +55 -0
- package/dist/tests/e2e/gui-style.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl.test.js +258 -0
- package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
- package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
- package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
- package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
- package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
- package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
- package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
- package/dist/tests/e2e/gui-webgl2.test.js +45 -1
- package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
- package/dist/tests/extensions/gui-index.test.js +1 -0
- package/dist/tests/extensions/gui-index.test.js.map +1 -1
- package/dist/tests/extensions/media.test.js +1 -0
- package/dist/tests/extensions/media.test.js.map +1 -1
- package/dist/tests/parser/expressions.test.js +30 -0
- package/dist/tests/parser/expressions.test.js.map +1 -1
- package/dist/tests/platform-tarball.test.d.ts +1 -0
- package/dist/tests/platform-tarball.test.js +111 -0
- package/dist/tests/platform-tarball.test.js.map +1 -0
- package/dist/tests/version.test.js +6 -0
- package/dist/tests/version.test.js.map +1 -1
- package/doc/ai/contributing.md +11 -0
- package/doc/ai/extensions.md +7 -4
- package/doc/ai/troubleshooting.md +7 -5
- package/doc/ai/zh-CN/contributing.md +9 -0
- package/doc/ai/zh-CN/extensions.md +4 -2
- package/doc/ai/zh-CN/troubleshooting.md +5 -3
- package/doc/gui/api.md +99 -22
- package/doc/gui/build.md +11 -4
- package/doc/gui/canvas.md +49 -18
- package/doc/gui/dom.md +57 -7
- package/doc/gui/media.md +4 -0
- package/doc/gui/webgl.md +105 -46
- package/doc/gui.md +7 -3
- package/doc/npm-package.md +55 -27
- package/doc/requirements.md +6 -2
- package/doc/zh-CN/gui/api.md +84 -14
- package/doc/zh-CN/gui/build.md +7 -2
- package/doc/zh-CN/gui/canvas.md +44 -19
- package/doc/zh-CN/gui/dom.md +48 -4
- package/doc/zh-CN/gui/media.md +3 -0
- package/doc/zh-CN/gui/webgl.md +22 -16
- package/doc/zh-CN/gui.md +3 -2
- package/doc/zh-CN/npm-package.md +44 -23
- package/doc/zh-CN/requirements.md +4 -1
- package/package.json +17 -1
- package/runtime/ext_gui/audio.cpp +365 -153
- package/runtime/ext_gui/audio_buffer.cpp +166 -0
- package/runtime/ext_gui/audio_buffer.h +37 -0
- package/runtime/ext_gui/audio_decode.cpp +219 -0
- package/runtime/ext_gui/audio_decode.h +39 -0
- package/runtime/ext_gui/audio_device.cpp +87 -0
- package/runtime/ext_gui/audio_effects.cpp +163 -0
- package/runtime/ext_gui/audio_effects.h +25 -0
- package/runtime/ext_gui/audio_internal.h +30 -0
- package/runtime/ext_gui/audio_mixer.cpp +494 -0
- package/runtime/ext_gui/audio_mixer.h +238 -0
- package/runtime/ext_gui/audio_param.cpp +104 -0
- package/runtime/ext_gui/audio_param_object.cpp +155 -0
- package/runtime/ext_gui/browser.cpp +45 -50
- package/runtime/ext_gui/browser_abort.cpp +249 -0
- package/runtime/ext_gui/browser_dialogs.cpp +76 -0
- package/runtime/ext_gui/browser_media.cpp +176 -0
- package/runtime/ext_gui/browser_navigation.cpp +130 -0
- package/runtime/ext_gui/canvas.cpp +88 -30
- package/runtime/ext_gui/canvas.h +74 -9
- package/runtime/ext_gui/canvas_image_data.cpp +143 -0
- package/runtime/ext_gui/canvas_internal.h +8 -5
- package/runtime/ext_gui/canvas_pattern.cpp +90 -0
- package/runtime/ext_gui/canvas_raster.cpp +31 -9
- package/runtime/ext_gui/canvas_style.cpp +4 -2
- package/runtime/ext_gui/css_selector.cpp +21 -3
- package/runtime/ext_gui/dom.h +4 -0
- package/runtime/ext_gui/dom_api.cpp +1 -0
- package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
- package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
- package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
- package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
- package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
- package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
- package/runtime/ext_gui/dom_api_document.cpp +58 -5
- package/runtime/ext_gui/dom_api_events.cpp +93 -17
- package/runtime/ext_gui/dom_api_internal.h +71 -2
- package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
- package/runtime/ext_gui/dom_api_node.cpp +40 -19
- package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
- package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
- package/runtime/ext_gui/dom_api_proto.cpp +35 -0
- package/runtime/ext_gui/dom_api_style.cpp +223 -13
- package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
- package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
- package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
- package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
- package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
- package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
- package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
- package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
- package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
- package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
- package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
- package/runtime/ext_gui/gui.cpp +103 -14
- package/runtime/ext_gui/gui_engine.h +56 -4
- package/runtime/ext_gui/gui_events.cpp +80 -9
- package/runtime/ext_gui/image_bitmap.cpp +28 -0
- package/runtime/ext_gui/image_bitmap.h +26 -0
- package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
- package/runtime/ext_gui/listener_options.h +43 -0
- package/runtime/ext_gui/media.cpp +32 -0
- package/runtime/ext_gui/media.h +13 -0
- package/runtime/ext_gui/media_api.h +23 -0
- package/runtime/ext_gui/paint.cpp +28 -0
- package/runtime/ext_gui/paint.h +4 -0
- package/runtime/ext_gui/webgl.h +152 -18
- package/runtime/ext_gui/webgl_blit.cpp +95 -0
- package/runtime/ext_gui/webgl_buffer.cpp +178 -263
- package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
- package/runtime/ext_gui/webgl_clear.cpp +142 -0
- package/runtime/ext_gui/webgl_compressed.cpp +180 -0
- package/runtime/ext_gui/webgl_context.cpp +241 -156
- package/runtime/ext_gui/webgl_draw.cpp +68 -3
- package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
- package/runtime/ext_gui/webgl_glsl.h +40 -0
- package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
- package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
- package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
- package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
- package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
- package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
- package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
- package/runtime/ext_gui/webgl_program_internal.h +6 -0
- package/runtime/ext_gui/webgl_shader.cpp +13 -1
- package/runtime/ext_gui/webgl_state.cpp +228 -0
- package/runtime/ext_gui/webgl_texture.cpp +144 -17
- package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
- package/runtime/ext_gui/webgl_uniform_block.h +31 -0
- package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
- package/runtime/ext_gui/window.cpp +14 -65
- package/runtime/ext_gui/window_events.cpp +125 -0
- package/runtime/ext_media/decode_audio.cpp +308 -0
- package/runtime/ext_media/media_bridge.c +2 -0
- package/runtime/rt.h +43 -0
- package/runtime/rt_internal.h +4 -0
- package/runtime/xt_array_buffer.c +30 -0
- package/runtime/xt_builtins.c +0 -21
- package/runtime/xt_containers/access.inc +94 -2
- package/runtime/xt_containers/objects.inc +29 -31
- package/runtime/xt_typed_array/elements.inc +13 -0
- package/scripts/build-media.ts +14 -6
- package/scripts/build-runtime.ts +21 -16
- package/scripts/check-version.ts +39 -9
- package/scripts/package-platform.ts +183 -0
- package/scripts/set-platform-version.ts +62 -0
- package/scripts/verify-platform-package.ts +182 -0
- package/src/cli/main.ts +5 -1
- package/src/driver/runtime-lib.ts +73 -9
- package/src/driver/version.ts +1 -1
- package/src/extensions/gui/index.ts +18 -5
- package/src/extensions/media/index.ts +4 -3
- package/src/parser/context.ts +99 -4
- package/src/parser/literals.ts +6 -12
|
@@ -93,17 +93,31 @@ METHOD(doc_create_element) {
|
|
|
93
93
|
return xt_gui_node_handle(win, win->document->createElement(tag));
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/** `createElementNS(ns, name)`: the
|
|
97
|
-
*
|
|
96
|
+
/** `createElementNS(ns, name)`: the namespace is recorded (so `namespaceURI`
|
|
97
|
+
* answers it) and the qualified name's prefix is preserved. HTML names are
|
|
98
|
+
* lowercased, but names in another namespace keep their case — SVG is
|
|
99
|
+
* case-sensitive (`linearGradient`, `clipPath`). */
|
|
98
100
|
METHOD(doc_create_element_ns) {
|
|
99
101
|
(void)env;
|
|
100
102
|
XtGuiWindow *win = resolve_window(self);
|
|
101
103
|
if (win == nullptr || win->document == nullptr || argc < 2) return XT_UNDEFINED;
|
|
104
|
+
std::string ns = to_string_value(xt_arg(argc, argv, 0));
|
|
102
105
|
std::string name = to_string_value(xt_arg(argc, argv, 1));
|
|
106
|
+
std::string prefix;
|
|
103
107
|
size_t colon = name.rfind(':');
|
|
104
|
-
if (colon != std::string::npos)
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
if (colon != std::string::npos) {
|
|
109
|
+
prefix = name.substr(0, colon);
|
|
110
|
+
name = name.substr(colon + 1);
|
|
111
|
+
}
|
|
112
|
+
const bool html = ns.empty() || ns == "http://www.w3.org/1999/xhtml";
|
|
113
|
+
if (html) {
|
|
114
|
+
for (char &c : name) c = (char)std::tolower((unsigned char)c);
|
|
115
|
+
}
|
|
116
|
+
xtgui::Node *node = win->document->createElement(name);
|
|
117
|
+
if (node == nullptr) return XT_UNDEFINED;
|
|
118
|
+
node->ns = ns;
|
|
119
|
+
node->ns_prefix = prefix;
|
|
120
|
+
return xt_gui_node_handle(win, node);
|
|
107
121
|
}
|
|
108
122
|
|
|
109
123
|
/** `document.hidden` / `visibilityState`: a window in the main loop is always
|
|
@@ -124,6 +138,15 @@ METHOD(doc_visibility_state_get) {
|
|
|
124
138
|
return xt_string_from_cstr("visible");
|
|
125
139
|
}
|
|
126
140
|
|
|
141
|
+
/** `document.hasFocus()`: true after `window.focus()` until `window.blur()`. */
|
|
142
|
+
METHOD(doc_has_focus) {
|
|
143
|
+
(void)env;
|
|
144
|
+
(void)argc;
|
|
145
|
+
(void)argv;
|
|
146
|
+
XtGuiWindow *win = resolve_window(self);
|
|
147
|
+
return win != nullptr && win->focused ? XT_TRUE : XT_FALSE;
|
|
148
|
+
}
|
|
149
|
+
|
|
127
150
|
/** `document.readyState`: the page is fully parsed before any script runs. */
|
|
128
151
|
METHOD(doc_ready_state_get) {
|
|
129
152
|
(void)self;
|
|
@@ -149,6 +172,36 @@ METHOD(doc_title_get) {
|
|
|
149
172
|
return xt_string_from_cstr(text.c_str());
|
|
150
173
|
}
|
|
151
174
|
|
|
175
|
+
/** `document.title = text`: replace the text of the document's `<title>`.
|
|
176
|
+
* A page without one gets a `<title>` (and a `<head>`, if the parser did not
|
|
177
|
+
* create one), so the assignment is not silently lost. */
|
|
178
|
+
METHOD(doc_title_set) {
|
|
179
|
+
(void)env;
|
|
180
|
+
XtGuiWindow *win = resolve_window(self);
|
|
181
|
+
if (win == nullptr || win->document == nullptr) return XT_UNDEFINED;
|
|
182
|
+
std::string text;
|
|
183
|
+
if (argc >= 1) text = to_string_value(xt_arg(argc, argv, 0));
|
|
184
|
+
xtgui::Node *root = const_cast<xtgui::Node *>(win->document->root());
|
|
185
|
+
xtgui::Node *title = const_cast<xtgui::Node *>(find_by_tag(root, "title"));
|
|
186
|
+
if (title == nullptr) {
|
|
187
|
+
xtgui::Node *head = const_cast<xtgui::Node *>(find_by_tag(root, "head"));
|
|
188
|
+
if (head == nullptr) {
|
|
189
|
+
head = win->document->createElement("head");
|
|
190
|
+
if (head == nullptr) return XT_UNDEFINED;
|
|
191
|
+
/* `<head>` belongs before the body, not after it. */
|
|
192
|
+
xtgui::Node *body = const_cast<xtgui::Node *>(find_by_tag(root, "body"));
|
|
193
|
+
win->document->insertBefore(root, head, body);
|
|
194
|
+
}
|
|
195
|
+
title = win->document->createElement("title");
|
|
196
|
+
if (title == nullptr) return XT_UNDEFINED;
|
|
197
|
+
win->document->appendChild(head, title);
|
|
198
|
+
}
|
|
199
|
+
title->children.clear();
|
|
200
|
+
if (!text.empty()) title->addText(text);
|
|
201
|
+
mark_dirty(win);
|
|
202
|
+
return XT_UNDEFINED;
|
|
203
|
+
}
|
|
204
|
+
|
|
152
205
|
METHOD(doc_default_view_get) {
|
|
153
206
|
(void)env;
|
|
154
207
|
(void)argc;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
#include "dom_api_internal.h"
|
|
7
|
+
#include "listener_options.h"
|
|
7
8
|
|
|
8
9
|
#include <string>
|
|
9
10
|
#include <vector>
|
|
@@ -15,8 +16,14 @@ static xt_value make_dom_event(XtGuiWindow *win, const char *type, const xtgui::
|
|
|
15
16
|
xt_value event = xt_object_new_with_proto(event_proto());
|
|
16
17
|
xt_object_set(event, xt_string_from_cstr("type"), xt_string_from_cstr(type));
|
|
17
18
|
xt_object_set(event, xt_string_from_cstr("target"), xt_gui_node_handle(win, target));
|
|
19
|
+
xt_object_set(event, xt_string_from_cstr("currentTarget"), XT_NULL);
|
|
18
20
|
xt_object_set(event, xt_string_from_cstr("defaultPrevented"), XT_FALSE);
|
|
19
21
|
xt_object_set(event, xt_string_from_cstr("bubbles"), XT_TRUE);
|
|
22
|
+
xt_object_set(event, xt_string_from_cstr("cancelable"), XT_TRUE);
|
|
23
|
+
xt_object_set(event, xt_string_from_cstr("composed"), XT_TRUE);
|
|
24
|
+
xt_object_set(event, xt_string_from_cstr("isTrusted"), XT_TRUE);
|
|
25
|
+
xt_object_set(event, xt_string_from_cstr("eventPhase"), xt_number(0));
|
|
26
|
+
xt_object_set(event, xt_string_from_cstr("timeStamp"), xt_number((double)SDL_GetTicks()));
|
|
20
27
|
if (XT_IS_OBJECT(legacy)) {
|
|
21
28
|
static const char *const fields[] = {
|
|
22
29
|
"x", "y", "clientX", "clientY", "button", "clicks",
|
|
@@ -31,23 +38,43 @@ static xt_value make_dom_event(XtGuiWindow *win, const char *type, const xtgui::
|
|
|
31
38
|
return event;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
/**
|
|
35
|
-
|
|
41
|
+
/** Set `field` to `value` unless `init` already supplied one. */
|
|
42
|
+
static void default_field(xt_value event, const char *field, xt_value value) {
|
|
43
|
+
if (XT_IS_UNDEFINED(xt_object_get_cstr(event, field))) {
|
|
44
|
+
xt_object_set(event, xt_string_from_cstr(field), value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A detached event object, as `new Event(type, init)` /
|
|
49
|
+
* `new CustomEvent(type, init)` (and the other `*Event` constructors) builds:
|
|
50
|
+
* no target yet, the standard fields, and every member of `init` — so
|
|
51
|
+
* `composed`, `detail` and the mouse/keyboard fields all survive construction
|
|
52
|
+
* instead of being silently dropped. */
|
|
36
53
|
xt_value event_construct(xt_value type, xt_value init) {
|
|
37
54
|
std::string name = to_string_value(type);
|
|
38
55
|
xt_value event = xt_object_new_with_proto(event_proto());
|
|
56
|
+
if (XT_IS_OBJECT(init)) {
|
|
57
|
+
xt_value keys = xt_object_keys(init);
|
|
58
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(keys));
|
|
59
|
+
for (int32_t i = 0; i < count; i++) {
|
|
60
|
+
xt_value key = xt_array_get(keys, xt_number((double)i));
|
|
61
|
+
xt_object_set(event, key, xt_object_get(init, key));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
39
64
|
xt_object_set(event, xt_string_from_cstr("type"), xt_string_from_cstr(name.c_str()));
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
default_field(event, "target", XT_NULL);
|
|
66
|
+
default_field(event, "currentTarget", XT_NULL);
|
|
67
|
+
default_field(event, "defaultPrevented", XT_FALSE);
|
|
68
|
+
default_field(event, "bubbles", XT_FALSE);
|
|
69
|
+
default_field(event, "cancelable", XT_FALSE);
|
|
70
|
+
default_field(event, "composed", XT_FALSE);
|
|
71
|
+
default_field(event, "isTrusted", XT_FALSE);
|
|
72
|
+
default_field(event, "eventPhase", xt_number(0));
|
|
73
|
+
default_field(event, "detail", XT_NULL);
|
|
74
|
+
default_field(event, "timeStamp", xt_number((double)SDL_GetTicks()));
|
|
75
|
+
/* Marks an event object built here, so `dispatchEvent(ev)` can reuse it (an
|
|
76
|
+
* event keeps its identity through dispatch, as in the DOM). */
|
|
77
|
+
xt_object_set(event, xt_string_from_cstr("__xt_gui_event"), XT_TRUE);
|
|
51
78
|
return event;
|
|
52
79
|
}
|
|
53
80
|
|
|
@@ -64,18 +91,32 @@ void remove_listener(XtGuiWindow *win, xtgui::Node *node, const std::string &typ
|
|
|
64
91
|
}
|
|
65
92
|
}
|
|
66
93
|
|
|
94
|
+
/** Call one listener batch. `phase` mirrors the DOM constants (1 capture,
|
|
95
|
+
* 2 at target, 3 bubble) so `event.eventPhase` is observable, and a passive
|
|
96
|
+
* listener runs with `preventDefault()` disabled. */
|
|
67
97
|
static bool call_node_listeners(XtGuiWindow *win, xtgui::Node *node, const char *type,
|
|
68
|
-
xt_value event, bool capture) {
|
|
98
|
+
xt_value event, bool capture, int32_t phase) {
|
|
69
99
|
auto found = win->node_listeners.find(node);
|
|
70
100
|
if (found == win->node_listeners.end()) return false;
|
|
71
101
|
std::vector<XtGuiNodeListener> snapshot = found->second;
|
|
102
|
+
xt_value previousPassive = xt_object_get_cstr(event, "__xt_gui_passive");
|
|
72
103
|
for (const XtGuiNodeListener &listener : snapshot) {
|
|
73
104
|
if (listener.type != type || listener.capture != capture) continue;
|
|
105
|
+
/* An aborted `AbortSignal` removes the listener. Drop it here so it also
|
|
106
|
+
* stops consuming the slot for future dispatches. */
|
|
107
|
+
if (xt_gui_signal_aborted(listener.signal)) {
|
|
108
|
+
remove_listener(win, node, listener.type, listener.fn, listener.capture);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
74
111
|
if (listener.once) remove_listener(win, node, listener.type, listener.fn, listener.capture);
|
|
75
112
|
xt_value self = xt_gui_node_handle(win, node);
|
|
76
113
|
xt_object_set(event, xt_string_from_cstr("currentTarget"), self);
|
|
114
|
+
xt_object_set(event, xt_string_from_cstr("eventPhase"), xt_number((double)phase));
|
|
115
|
+
xt_object_set(event, xt_string_from_cstr("__xt_gui_passive"),
|
|
116
|
+
listener.passive ? XT_TRUE : XT_FALSE);
|
|
77
117
|
xt_value args[1] = {event};
|
|
78
118
|
xt_call_with_this(listener.fn, self, 1, args);
|
|
119
|
+
xt_object_set(event, xt_string_from_cstr("__xt_gui_passive"), previousPassive);
|
|
79
120
|
if (value_truthy(xt_object_get_cstr(event, "__stopImmediate"))) return true;
|
|
80
121
|
if (value_truthy(xt_object_get_cstr(event, "__stop"))) return true;
|
|
81
122
|
}
|
|
@@ -88,6 +129,9 @@ METHOD(event_prevent_default) {
|
|
|
88
129
|
(void)env;
|
|
89
130
|
(void)argc;
|
|
90
131
|
(void)argv;
|
|
132
|
+
/* A listener registered with `{ passive: true }` promised not to cancel: the
|
|
133
|
+
* browser ignores the call (and warns), so the engine ignores it too. */
|
|
134
|
+
if (value_truthy(xt_object_get_cstr(self, "__xt_gui_passive"))) return XT_UNDEFINED;
|
|
91
135
|
xt_object_set(self, xt_string_from_cstr("defaultPrevented"), XT_TRUE);
|
|
92
136
|
return XT_UNDEFINED;
|
|
93
137
|
}
|
|
@@ -109,6 +153,22 @@ METHOD(event_stop_immediate_propagation) {
|
|
|
109
153
|
return XT_UNDEFINED;
|
|
110
154
|
}
|
|
111
155
|
|
|
156
|
+
/** `event.composedPath()`: the propagation path recorded by the dispatcher,
|
|
157
|
+
* target first, as element handles. A detached event has an empty path. */
|
|
158
|
+
METHOD(event_composed_path) {
|
|
159
|
+
(void)env;
|
|
160
|
+
(void)argc;
|
|
161
|
+
(void)argv;
|
|
162
|
+
xt_value result = xt_array_new(0, nullptr);
|
|
163
|
+
xt_value path = xt_object_get_cstr(self, "__xt_gui_path");
|
|
164
|
+
if (!XT_IS_ARRAY(path)) return result;
|
|
165
|
+
int32_t count = (int32_t)xt_to_number(xt_array_length(path));
|
|
166
|
+
for (int32_t i = 0; i < count; i++) {
|
|
167
|
+
xt_array_push(result, xt_array_get(path, xt_number((double)i)));
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
112
172
|
#undef METHOD
|
|
113
173
|
|
|
114
174
|
} // namespace domapi
|
|
@@ -123,13 +183,29 @@ void xt_gui_emit_dom_event(XtGuiWindow *win, const char *type, const xtgui::Node
|
|
|
123
183
|
path.push_back(node);
|
|
124
184
|
}
|
|
125
185
|
xt_value event = make_dom_event(win, type, target, legacy);
|
|
186
|
+
/* `element.dispatchEvent(ev)` hands over a real event object: dispatch that
|
|
187
|
+
* same object (identity is observable), only rebinding its target/path. */
|
|
188
|
+
if (XT_IS_OBJECT(legacy) && value_truthy(xt_object_get_cstr(legacy, "__xt_gui_event"))) {
|
|
189
|
+
event = legacy;
|
|
190
|
+
xt_object_set(event, xt_string_from_cstr("target"), xt_gui_node_handle(win, target));
|
|
191
|
+
xt_object_set(event, xt_string_from_cstr("currentTarget"), XT_NULL);
|
|
192
|
+
xt_object_set(event, xt_string_from_cstr("eventPhase"), xt_number(0));
|
|
193
|
+
}
|
|
194
|
+
xt_value handlePath = xt_array_new(0, nullptr);
|
|
195
|
+
for (xtgui::Node *node : path) xt_array_push(handlePath, xt_gui_node_handle(win, node));
|
|
196
|
+
xt_object_set(event, xt_string_from_cstr("__xt_gui_path"), handlePath);
|
|
197
|
+
bool bubbles = value_truthy(xt_object_get_cstr(event, "bubbles"));
|
|
126
198
|
/* Capture phase: root -> target. */
|
|
127
199
|
for (auto it = path.rbegin(); it != path.rend(); ++it) {
|
|
128
|
-
|
|
200
|
+
int32_t phase = *it == target ? 2 : 1;
|
|
201
|
+
if (call_node_listeners(win, *it, type, event, true, phase)) return;
|
|
129
202
|
}
|
|
130
|
-
/* Bubble phase: target -> root
|
|
203
|
+
/* Bubble phase: target -> root; a non-bubbling event stops after the target
|
|
204
|
+
* (its non-capture listeners still run there). */
|
|
131
205
|
for (auto it = path.begin(); it != path.end(); ++it) {
|
|
132
|
-
if (
|
|
206
|
+
if (!bubbles && *it != target) break;
|
|
207
|
+
int32_t phase = *it == target ? 2 : 3;
|
|
208
|
+
if (call_node_listeners(win, *it, type, event, false, phase)) return;
|
|
133
209
|
}
|
|
134
210
|
}
|
|
135
211
|
|
|
@@ -27,6 +27,7 @@ extern xt_value g_document_proto;
|
|
|
27
27
|
extern xt_value g_event_proto;
|
|
28
28
|
extern xt_value g_class_list_proto;
|
|
29
29
|
extern xt_value g_style_proto;
|
|
30
|
+
extern xt_value g_computed_style_proto;
|
|
30
31
|
extern xt_value g_canvas_proto;
|
|
31
32
|
extern xt_value g_canvas_gradient_proto;
|
|
32
33
|
extern xt_value g_webgl_proto;
|
|
@@ -49,10 +50,16 @@ xt_value document_proto();
|
|
|
49
50
|
xt_value event_proto();
|
|
50
51
|
xt_value class_list_proto();
|
|
51
52
|
xt_value style_proto();
|
|
53
|
+
xt_value computed_style_proto();
|
|
52
54
|
xt_value canvas_proto();
|
|
53
55
|
xt_value canvas_gradient_proto();
|
|
54
56
|
xt_value webgl_proto();
|
|
55
57
|
xt_value webgl2_proto();
|
|
58
|
+
/** `AbortController` / `AbortSignal` prototypes and constructors. */
|
|
59
|
+
xt_value abort_controller_proto();
|
|
60
|
+
xt_value abort_signal_proto();
|
|
61
|
+
xt_value abort_controller_new();
|
|
62
|
+
xt_value abort_signal_new(bool aborted);
|
|
56
63
|
/** The prototype a WebGL object of `kind` inherits (`buffer`, `texture`, …),
|
|
57
64
|
* so `buffer instanceof WebGLBuffer` holds. */
|
|
58
65
|
xt_value webgl_object_proto(const std::string &kind);
|
|
@@ -60,6 +67,16 @@ xt_value webgl_object_proto(const std::string &kind);
|
|
|
60
67
|
/** Create/return the 2D context for a pending `<canvas>` handle (before load). */
|
|
61
68
|
xt_value xt_canvas_pending_context(xt_value self, XtGuiWindow *win);
|
|
62
69
|
|
|
70
|
+
/* -- Path2D --------------------------------------------------------------- */
|
|
71
|
+
|
|
72
|
+
/** True when `value` is a `Path2D` handle. */
|
|
73
|
+
bool path2d_is(xt_value value);
|
|
74
|
+
/** The user-space subpaths a `Path2D` handle holds. */
|
|
75
|
+
std::vector<xtgui::CanvasSubpath> path2d_subpaths(xt_value value);
|
|
76
|
+
/** `new Path2D()` / `new Path2D(other)`. */
|
|
77
|
+
xt_value path2d_create(xt_value source);
|
|
78
|
+
xt_value path2d_proto();
|
|
79
|
+
|
|
63
80
|
/* -- canvas helpers shared by `dom_api_canvas*.cpp` ----------------------- */
|
|
64
81
|
|
|
65
82
|
/** The engine canvas behind a 2D context handle, or null when stale/foreign. */
|
|
@@ -77,6 +94,9 @@ uint64_t canvas_gradient_id(xt_value handle, const xtgui::Canvas2D *canvas);
|
|
|
77
94
|
|
|
78
95
|
/** The engine WebGL context behind a context handle, or null. */
|
|
79
96
|
xtgui::WebGLContext *webgl_from_context(xt_value self);
|
|
97
|
+
/** Like `webgl_from_context`, but null while the context is lost: every GL
|
|
98
|
+
* entry point except `getError`/`isContextLost` is a no-op in that state. */
|
|
99
|
+
xtgui::WebGLContext *webgl_live_context(xt_value self);
|
|
80
100
|
/** The numeric context id stored on a context handle (0 when absent). */
|
|
81
101
|
uint64_t webgl_context_id(xt_value self);
|
|
82
102
|
/** Create the JavaScript handle for a canvas's WebGL context. */
|
|
@@ -95,9 +115,19 @@ std::vector<double> webgl_numbers(xt_value value);
|
|
|
95
115
|
std::vector<uint8_t> webgl_bytes(xt_value value);
|
|
96
116
|
/** Store `count` bytes into a typed array / array handle. */
|
|
97
117
|
void webgl_write_bytes(xt_value target, const uint8_t *bytes, size_t count);
|
|
118
|
+
/** Convert a source pixel array (RGBA/RGB/LUMINANCE/…) to RGBA8. */
|
|
119
|
+
void webgl_convert_pixels(const uint8_t *source, int count, int format, bool premultiply,
|
|
120
|
+
std::vector<uint8_t> *out);
|
|
121
|
+
/** Decode a `texImage2D` source (`<img>`, `<canvas>`, `<video>`, `ImageData`,
|
|
122
|
+
* `ImageBitmap`) into top-down RGBA8. */
|
|
123
|
+
bool webgl_read_source(xt_value source, int *width, int *height,
|
|
124
|
+
std::vector<uint8_t> *rgba, bool premultiply);
|
|
98
125
|
/** Read the `UNPACK_FLIP_Y_WEBGL` / `UNPACK_PREMULTIPLY_ALPHA_WEBGL` flags set
|
|
99
126
|
* through `pixelStorei`. */
|
|
100
127
|
void webgl_unpack_flags(xt_value context, bool *flip_y, bool *premultiply);
|
|
128
|
+
/** Extension object for a compressed-texture family (null when unsupported,
|
|
129
|
+
* undefined when the name is not one of those extensions). */
|
|
130
|
+
xt_value webgl_compressed_extension(const std::string &name, xtgui::WebGLContext *context);
|
|
101
131
|
|
|
102
132
|
/** How a pending handle names the node it will bind to. */
|
|
103
133
|
enum PendingKind { pending_by_id = 1, pending_by_selector = 2 };
|
|
@@ -144,7 +174,9 @@ METHOD(doc_create_element_ns);
|
|
|
144
174
|
METHOD(doc_hidden_get);
|
|
145
175
|
METHOD(doc_visibility_state_get);
|
|
146
176
|
METHOD(doc_ready_state_get);
|
|
177
|
+
METHOD(doc_has_focus);
|
|
147
178
|
METHOD(doc_title_get);
|
|
179
|
+
METHOD(doc_title_set);
|
|
148
180
|
METHOD(doc_default_view_get);
|
|
149
181
|
METHOD(doc_create_text_node);
|
|
150
182
|
METHOD(doc_body_get);
|
|
@@ -153,6 +185,9 @@ METHOD(doc_document_element_get);
|
|
|
153
185
|
/* node getters */
|
|
154
186
|
METHOD(node_tag_name_get);
|
|
155
187
|
METHOD(node_node_type_get);
|
|
188
|
+
METHOD(node_namespace_uri_get);
|
|
189
|
+
METHOD(node_local_name_get);
|
|
190
|
+
METHOD(node_prefix_get);
|
|
156
191
|
METHOD(node_id_get);
|
|
157
192
|
METHOD(node_id_set);
|
|
158
193
|
METHOD(node_class_name_get);
|
|
@@ -183,6 +218,10 @@ METHOD(node_query_selector);
|
|
|
183
218
|
METHOD(node_query_selector_all);
|
|
184
219
|
METHOD(node_matches);
|
|
185
220
|
METHOD(node_append_child);
|
|
221
|
+
METHOD(node_append);
|
|
222
|
+
METHOD(node_prepend);
|
|
223
|
+
METHOD(node_replace_children);
|
|
224
|
+
METHOD(node_insert_adjacent_html);
|
|
186
225
|
METHOD(node_insert_before);
|
|
187
226
|
METHOD(node_remove_child);
|
|
188
227
|
METHOD(node_replace_child);
|
|
@@ -206,6 +245,7 @@ xt_value event_construct(xt_value type, xt_value init);
|
|
|
206
245
|
METHOD(event_prevent_default);
|
|
207
246
|
METHOD(event_stop_propagation);
|
|
208
247
|
METHOD(event_stop_immediate_propagation);
|
|
248
|
+
METHOD(event_composed_path);
|
|
209
249
|
|
|
210
250
|
/* media (`<video>`/`<audio>`) */
|
|
211
251
|
METHOD(node_play);
|
|
@@ -272,11 +312,21 @@ METHOD(canvas_stroke);
|
|
|
272
312
|
METHOD(canvas_clear_rect);
|
|
273
313
|
METHOD(canvas_fill_rect);
|
|
274
314
|
METHOD(canvas_stroke_rect);
|
|
275
|
-
METHOD(canvas_measure_text);
|
|
276
|
-
METHOD(canvas_fill_text);
|
|
315
|
+
METHOD(canvas_measure_text);METHOD(canvas_fill_text);
|
|
277
316
|
METHOD(canvas_stroke_text);
|
|
278
317
|
METHOD(canvas_draw_image);
|
|
279
318
|
METHOD(canvas_get_image_data);
|
|
319
|
+
METHOD(canvas_get_context_attributes);
|
|
320
|
+
METHOD(canvas_put_image_data);
|
|
321
|
+
METHOD(canvas_create_image_data);
|
|
322
|
+
METHOD(canvas_create_pattern);
|
|
323
|
+
METHOD(canvas_to_data_url);
|
|
324
|
+
METHOD(canvas_to_blob);
|
|
325
|
+
/** The cached `CanvasPattern` handle for `id` on `canvas`. */
|
|
326
|
+
xt_value canvas_pattern_handle(xt_value context, const xtgui::Canvas2D *canvas, uint64_t id);
|
|
327
|
+
/** The pattern id stored on a handle, or 0 when it is not a pattern for
|
|
328
|
+
* `canvas` (stale or created by another canvas). */
|
|
329
|
+
uint64_t canvas_pattern_id(xt_value handle, const xtgui::Canvas2D *canvas);
|
|
280
330
|
METHOD(canvas_ellipse);
|
|
281
331
|
METHOD(canvas_round_rect);
|
|
282
332
|
METHOD(canvas_line_cap_get);
|
|
@@ -303,7 +353,23 @@ METHOD(class_list_contains);
|
|
|
303
353
|
/* style methods */
|
|
304
354
|
METHOD(style_set_property);
|
|
305
355
|
METHOD(style_get_property_value);
|
|
356
|
+
METHOD(style_get_property_priority);
|
|
306
357
|
METHOD(style_remove_property);
|
|
358
|
+
METHOD(style_css_text_get);
|
|
359
|
+
METHOD(style_css_text_set);
|
|
360
|
+
METHOD(style_length_get);
|
|
361
|
+
METHOD(style_item);
|
|
362
|
+
/** Install the dynamic named-property hooks on a `style` handle
|
|
363
|
+
* (`style.width = "50%"`). */
|
|
364
|
+
void install_style_hooks(xt_value style);
|
|
365
|
+
|
|
366
|
+
/* `dataset` (a DOMStringMap over `data-*` attributes) and computed style */
|
|
367
|
+
METHOD(node_dataset_get);
|
|
368
|
+
METHOD(computed_style_get_property_value);
|
|
369
|
+
METHOD(computed_style_get_property_priority);
|
|
370
|
+
/** Build the computed-style handle for an element (and optional pseudo). */
|
|
371
|
+
xt_value computed_style_handle(xt_value element, xt_value pseudo);
|
|
372
|
+
xt_value computed_style_proto();
|
|
307
373
|
|
|
308
374
|
/* WebGL context: state and queries */
|
|
309
375
|
METHOD(webgl_canvas_get);
|
|
@@ -426,6 +492,8 @@ METHOD(webgl_tex_parameterf);
|
|
|
426
492
|
METHOD(webgl_get_tex_parameter);
|
|
427
493
|
METHOD(webgl_tex_image_2d);
|
|
428
494
|
METHOD(webgl_tex_sub_image_2d);
|
|
495
|
+
METHOD(webgl_compressed_tex_image_2d);
|
|
496
|
+
METHOD(webgl_compressed_tex_sub_image_2d);
|
|
429
497
|
METHOD(webgl_generate_mipmap);
|
|
430
498
|
METHOD(webgl_create_framebuffer);
|
|
431
499
|
METHOD(webgl_delete_framebuffer);
|
|
@@ -495,6 +563,7 @@ METHOD(webgl2_get_uniform_block_index);
|
|
|
495
563
|
METHOD(webgl2_get_active_uniform_block_parameter);
|
|
496
564
|
METHOD(webgl2_get_active_uniform_block_name);
|
|
497
565
|
METHOD(webgl2_uniform_block_binding);
|
|
566
|
+
METHOD(webgl2_drawing_buffer_storage);
|
|
498
567
|
METHOD(webgl2_tex_storage_2d);
|
|
499
568
|
METHOD(webgl2_tex_storage_3d);
|
|
500
569
|
METHOD(webgl2_tex_image_3d);
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — the parent/child convenience mutators.
|
|
3
|
+
*
|
|
4
|
+
* `append`, `prepend`, `replaceChildren` and `insertAdjacentHTML` are the APIs
|
|
5
|
+
* modern pages actually use to build DOM (they accept nodes and plain strings
|
|
6
|
+
* in one call); the engine previously only had `appendChild`/`innerHTML`.
|
|
7
|
+
* Strings become text nodes, as in the DOM.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "dom_api_internal.h"
|
|
11
|
+
|
|
12
|
+
#include <memory>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
namespace domapi {
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
|
|
20
|
+
/** A node handle, or a new text node for a string argument. */
|
|
21
|
+
xtgui::Node *argument_node(XtGuiWindow *win, xt_value value) {
|
|
22
|
+
xtgui::Node *node = resolve_node(value, nullptr);
|
|
23
|
+
if (node != nullptr) return node;
|
|
24
|
+
if (XT_IS_UNDEFINED(value) || value == XT_NULL) return nullptr;
|
|
25
|
+
return win->document->createTextNode(to_string_value(value));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void insert_argument(XtGuiWindow *win, xtgui::Node *parent, xt_value value, xtgui::Node *before) {
|
|
29
|
+
xtgui::Node *node = argument_node(win, value);
|
|
30
|
+
if (node == nullptr) return;
|
|
31
|
+
if (before != nullptr) {
|
|
32
|
+
win->document->insertBefore(parent, node, before);
|
|
33
|
+
} else {
|
|
34
|
+
win->document->appendChild(parent, node);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Copy a parsed fragment's children into `dest`, inserting before `before`
|
|
39
|
+
* (nullptr appends). The parser's nodes belong to a standalone tree, so they are
|
|
40
|
+
* recreated through the document rather than adopted. */
|
|
41
|
+
void import_children(XtGuiWindow *win, xtgui::Node *dest, const xtgui::Node *parsed,
|
|
42
|
+
xtgui::Node *before) {
|
|
43
|
+
for (const std::unique_ptr<xtgui::Node> &child : parsed->children) {
|
|
44
|
+
xtgui::Node *copy = nullptr;
|
|
45
|
+
if (child->isText()) {
|
|
46
|
+
copy = win->document->createTextNode(child->text);
|
|
47
|
+
} else {
|
|
48
|
+
copy = win->document->createElement(child->tag);
|
|
49
|
+
if (copy == nullptr) continue;
|
|
50
|
+
for (const xtgui::Attr &attr : child->attrs) copy->attrs.push_back(attr);
|
|
51
|
+
import_children(win, copy, child.get(), nullptr);
|
|
52
|
+
}
|
|
53
|
+
if (copy == nullptr) continue;
|
|
54
|
+
if (before != nullptr) {
|
|
55
|
+
win->document->insertBefore(dest, copy, before);
|
|
56
|
+
} else {
|
|
57
|
+
win->document->appendChild(dest, copy);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
xtgui::Node *next_sibling(xtgui::Node *parent, xtgui::Node *node) {
|
|
63
|
+
if (parent == nullptr) return nullptr;
|
|
64
|
+
for (size_t i = 0; i + 1 < parent->children.size(); i++) {
|
|
65
|
+
if (parent->children[i].get() == node) return parent->children[i + 1].get();
|
|
66
|
+
}
|
|
67
|
+
return nullptr;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
xtgui::Node *first_child(xtgui::Node *node) {
|
|
71
|
+
return node->children.empty() ? nullptr : node->children.front().get();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
} // namespace
|
|
75
|
+
|
|
76
|
+
#define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
|
|
77
|
+
|
|
78
|
+
METHOD(node_append) {
|
|
79
|
+
(void)env;
|
|
80
|
+
XtGuiWindow *win = nullptr;
|
|
81
|
+
xtgui::Node *node = resolve_node(self, &win);
|
|
82
|
+
if (node == nullptr || win == nullptr) return XT_UNDEFINED;
|
|
83
|
+
for (int32_t i = 0; i < argc; i++) insert_argument(win, node, xt_arg(argc, argv, i), nullptr);
|
|
84
|
+
mark_dirty(win);
|
|
85
|
+
return XT_UNDEFINED;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
METHOD(node_prepend) {
|
|
89
|
+
(void)env;
|
|
90
|
+
XtGuiWindow *win = nullptr;
|
|
91
|
+
xtgui::Node *node = resolve_node(self, &win);
|
|
92
|
+
if (node == nullptr || win == nullptr) return XT_UNDEFINED;
|
|
93
|
+
/* The reference stays the original first child; inserting each argument before
|
|
94
|
+
* it keeps the argument order. */
|
|
95
|
+
xtgui::Node *reference = first_child(node);
|
|
96
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
97
|
+
insert_argument(win, node, xt_arg(argc, argv, i), reference);
|
|
98
|
+
}
|
|
99
|
+
mark_dirty(win);
|
|
100
|
+
return XT_UNDEFINED;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
METHOD(node_replace_children) {
|
|
104
|
+
(void)env;
|
|
105
|
+
XtGuiWindow *win = nullptr;
|
|
106
|
+
xtgui::Node *node = resolve_node(self, &win);
|
|
107
|
+
if (node == nullptr || win == nullptr || win->document == nullptr) return XT_UNDEFINED;
|
|
108
|
+
/* Detach node arguments first: one of them may be a current child that the
|
|
109
|
+
* replacement keeps. */
|
|
110
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
111
|
+
xtgui::Node *child = resolve_node(xt_arg(argc, argv, i), nullptr);
|
|
112
|
+
if (child == nullptr) continue;
|
|
113
|
+
if (child->parent == node) win->document->removeChild(node, child);
|
|
114
|
+
}
|
|
115
|
+
std::vector<xtgui::Node *> existing;
|
|
116
|
+
for (const std::unique_ptr<xtgui::Node> &child : node->children) {
|
|
117
|
+
existing.push_back(child.get());
|
|
118
|
+
}
|
|
119
|
+
for (xtgui::Node *child : existing) {
|
|
120
|
+
win->document->removeChild(node, child);
|
|
121
|
+
discard_subtree(win, child);
|
|
122
|
+
}
|
|
123
|
+
for (int32_t i = 0; i < argc; i++) insert_argument(win, node, xt_arg(argc, argv, i), nullptr);
|
|
124
|
+
mark_dirty(win);
|
|
125
|
+
return XT_UNDEFINED;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** `insertAdjacentHTML(position, html)`; `position` is beforebegin,
|
|
129
|
+
* afterbegin, beforeend or afterend. Scripts in the fragment do not run. */
|
|
130
|
+
METHOD(node_insert_adjacent_html) {
|
|
131
|
+
(void)env;
|
|
132
|
+
XtGuiWindow *win = nullptr;
|
|
133
|
+
xtgui::Node *node = resolve_node(self, &win);
|
|
134
|
+
if (node == nullptr || win == nullptr || win->document == nullptr || argc < 2) {
|
|
135
|
+
return XT_UNDEFINED;
|
|
136
|
+
}
|
|
137
|
+
std::string position = to_string_value(xt_arg(argc, argv, 0));
|
|
138
|
+
std::string html = to_string_value(xt_arg(argc, argv, 1));
|
|
139
|
+
std::unique_ptr<xtgui::Node> parsed = xtgui::xt_html_parse(html);
|
|
140
|
+
if (!parsed) return XT_UNDEFINED;
|
|
141
|
+
if (position == "beforebegin" || position == "afterend") {
|
|
142
|
+
xtgui::Node *parent = node->parent;
|
|
143
|
+
if (parent == nullptr || parent->isDocument()) return XT_UNDEFINED;
|
|
144
|
+
xtgui::Node *reference = position == "beforebegin" ? node : next_sibling(parent, node);
|
|
145
|
+
import_children(win, parent, parsed.get(), reference);
|
|
146
|
+
} else if (position == "afterbegin") {
|
|
147
|
+
import_children(win, node, parsed.get(), first_child(node));
|
|
148
|
+
} else if (position == "beforeend") {
|
|
149
|
+
import_children(win, node, parsed.get(), nullptr);
|
|
150
|
+
} else {
|
|
151
|
+
return XT_UNDEFINED;
|
|
152
|
+
}
|
|
153
|
+
mark_dirty(win);
|
|
154
|
+
return XT_UNDEFINED;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#undef METHOD
|
|
158
|
+
|
|
159
|
+
} // namespace domapi
|