@yuneta/gobj-ui 7.23.29 → 7.23.31
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 +175 -2
- package/dist/gobj-ui.cjs.js +1008 -197
- package/dist/gobj-ui.es.js +1009 -198
- package/package.json +1 -1
- package/src/c_yui_gobj_tree_js.js +465 -249
- package/src/c_yui_json_graph.js +30 -4
- package/src/c_yui_treedb_graph.js +20 -6
- package/src/gclass_describe.js +234 -0
- package/src/gclass_describe.test.js +125 -0
- package/src/gobj_tree_model.js +436 -0
- package/src/gobj_tree_model.test.js +184 -0
- package/src/yui_gclass_view.js +290 -0
package/dist/gobj-ui.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
|
|
1
|
+
import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_bottom_gobj, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, gobj_yuno_name, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
|
|
2
2
|
import i18next, { t } from "i18next";
|
|
3
3
|
import TomSelect from "tom-select";
|
|
4
4
|
import { createJSONEditor } from "vanilla-jsoneditor";
|
|
@@ -3513,7 +3513,7 @@ function composed_title(gobj) {
|
|
|
3513
3513
|
* Below the mobile breakpoint the window is a full-screen
|
|
3514
3514
|
* sheet (no float / drag / resize).
|
|
3515
3515
|
************************************************************/
|
|
3516
|
-
function is_mobile$
|
|
3516
|
+
function is_mobile$4() {
|
|
3517
3517
|
return (window.innerWidth || document.documentElement.offsetWidth) <= MOBILE_MAX;
|
|
3518
3518
|
}
|
|
3519
3519
|
/************************************************************
|
|
@@ -3546,7 +3546,7 @@ function do_minimize(gobj) {
|
|
|
3546
3546
|
************************************************************/
|
|
3547
3547
|
function build_ui$15(gobj) {
|
|
3548
3548
|
ensure_window_style();
|
|
3549
|
-
let mobile = is_mobile$
|
|
3549
|
+
let mobile = is_mobile$4();
|
|
3550
3550
|
let header = gobj_read_attr(gobj, "header");
|
|
3551
3551
|
if (is_gobj(header)) header = gobj_read_attr(header, "$container");
|
|
3552
3552
|
if (!header) header = build_default_header(gobj);
|
|
@@ -3884,7 +3884,7 @@ function min$1(gobj) {
|
|
|
3884
3884
|
* handlers moving
|
|
3885
3885
|
************************************************************/
|
|
3886
3886
|
function mvStart(gobj, evt) {
|
|
3887
|
-
if (is_mobile$
|
|
3887
|
+
if (is_mobile$4()) return;
|
|
3888
3888
|
let $container = gobj_read_attr(gobj, "$container");
|
|
3889
3889
|
let window_rect = $container.getBoundingClientRect();
|
|
3890
3890
|
let x = evt.screenX;
|
|
@@ -4043,7 +4043,7 @@ function do_center(gobj, x, y, width, height) {
|
|
|
4043
4043
|
function handleResize(gobj) {
|
|
4044
4044
|
let $container = gobj_read_attr(gobj, "$container");
|
|
4045
4045
|
if (!$container) return;
|
|
4046
|
-
if (is_mobile$
|
|
4046
|
+
if (is_mobile$4()) {
|
|
4047
4047
|
$container.classList.add("is-mobile-sheet");
|
|
4048
4048
|
let vw = window.innerWidth || document.documentElement.offsetWidth;
|
|
4049
4049
|
let vh = window.innerHeight || document.documentElement.offsetHeight;
|
|
@@ -10978,6 +10978,26 @@ function make_toolbar$3(gobj) {
|
|
|
10978
10978
|
return $toolbar;
|
|
10979
10979
|
}
|
|
10980
10980
|
/************************************************************
|
|
10981
|
+
* The element G6 mounts its canvas in.
|
|
10982
|
+
*
|
|
10983
|
+
* Read from THIS gclass's own $container, never with
|
|
10984
|
+
* `document.getElementById()`: a view is not always in the
|
|
10985
|
+
* document when it is started -- a host that builds the
|
|
10986
|
+
* viewer and mounts it into a window afterwards starts it
|
|
10987
|
+
* DETACHED -- and a global lookup answers null there. It cost
|
|
10988
|
+
* a resize that never worked: the ResizeObserver below was
|
|
10989
|
+
* simply never attached, silently, and the canvas kept the
|
|
10990
|
+
* size it was born with for the life of the window.
|
|
10991
|
+
*
|
|
10992
|
+
* Null only if the UI was never built; that is a bug, and it
|
|
10993
|
+
* is logged where it matters rather than skipped.
|
|
10994
|
+
************************************************************/
|
|
10995
|
+
function canvas_mount$1(gobj) {
|
|
10996
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
10997
|
+
if (!$container) return null;
|
|
10998
|
+
return $container.querySelector("#" + gobj.priv.canvas_id);
|
|
10999
|
+
}
|
|
11000
|
+
/************************************************************
|
|
10981
11001
|
* Build a G6 graph instance
|
|
10982
11002
|
************************************************************/
|
|
10983
11003
|
function build_graph$3(gobj) {
|
|
@@ -11021,8 +11041,9 @@ function build_graph$3(gobj) {
|
|
|
11021
11041
|
});
|
|
11022
11042
|
graph.on(CanvasEvent.CLICK, (evt) => {});
|
|
11023
11043
|
if (typeof ResizeObserver !== "undefined") {
|
|
11024
|
-
let $canvas =
|
|
11025
|
-
if (
|
|
11044
|
+
let $canvas = canvas_mount$1(gobj);
|
|
11045
|
+
if (!$canvas) log_error(`${gobj_short_name(gobj)}: no canvas mount, the graph will never resize`);
|
|
11046
|
+
else {
|
|
11026
11047
|
let ro = new ResizeObserver(() => {
|
|
11027
11048
|
if (priv._resize_raf) cancelAnimationFrame(priv._resize_raf);
|
|
11028
11049
|
priv._resize_raf = requestAnimationFrame(() => {
|
|
@@ -11959,10 +11980,9 @@ function ac_node_click$4(gobj, event, kw, src) {
|
|
|
11959
11980
|
*
|
|
11960
11981
|
************************************************************/
|
|
11961
11982
|
function ac_show$8(gobj, event, kw, src) {
|
|
11962
|
-
let
|
|
11963
|
-
let graph = priv.graph;
|
|
11983
|
+
let graph = gobj.priv.graph;
|
|
11964
11984
|
if (graph) {
|
|
11965
|
-
let $canvas =
|
|
11985
|
+
let $canvas = canvas_mount$1(gobj);
|
|
11966
11986
|
if ($canvas) {
|
|
11967
11987
|
let cw = $canvas.clientWidth;
|
|
11968
11988
|
let ch = $canvas.clientHeight;
|
|
@@ -11981,10 +12001,9 @@ function ac_show$8(gobj, event, kw, src) {
|
|
|
11981
12001
|
* No fitView: preserve the user's current pan/zoom.
|
|
11982
12002
|
************************************************************/
|
|
11983
12003
|
function ac_resize$2(gobj, event, kw, src) {
|
|
11984
|
-
let
|
|
11985
|
-
let graph = priv.graph;
|
|
12004
|
+
let graph = gobj.priv.graph;
|
|
11986
12005
|
if (graph) {
|
|
11987
|
-
let $canvas =
|
|
12006
|
+
let $canvas = canvas_mount$1(gobj);
|
|
11988
12007
|
if ($canvas) {
|
|
11989
12008
|
let cw = $canvas.clientWidth;
|
|
11990
12009
|
let ch = $canvas.clientHeight;
|
|
@@ -13200,7 +13219,7 @@ function ac_json_item_clicked(gobj, event, kw, src) {
|
|
|
13200
13219
|
/************************************************************
|
|
13201
13220
|
* EV_LANGUAGE_CHANGED — re-translate chrome + re-render
|
|
13202
13221
|
************************************************************/
|
|
13203
|
-
function ac_language_changed$
|
|
13222
|
+
function ac_language_changed$4(gobj, event, kw, src) {
|
|
13204
13223
|
let priv = gobj.priv;
|
|
13205
13224
|
let $container = gobj_read_attr(gobj, "$container");
|
|
13206
13225
|
if ($container) {
|
|
@@ -13261,7 +13280,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13261
13280
|
],
|
|
13262
13281
|
[
|
|
13263
13282
|
"EV_LANGUAGE_CHANGED",
|
|
13264
|
-
ac_language_changed$
|
|
13283
|
+
ac_language_changed$4,
|
|
13265
13284
|
null
|
|
13266
13285
|
],
|
|
13267
13286
|
[
|
|
@@ -13337,7 +13356,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13337
13356
|
],
|
|
13338
13357
|
[
|
|
13339
13358
|
"EV_LANGUAGE_CHANGED",
|
|
13340
|
-
ac_language_changed$
|
|
13359
|
+
ac_language_changed$4,
|
|
13341
13360
|
null
|
|
13342
13361
|
],
|
|
13343
13362
|
[
|
|
@@ -13387,6 +13406,568 @@ function register_c_yui_json() {
|
|
|
13387
13406
|
return create_gclass$10(GCLASS_NAME$10);
|
|
13388
13407
|
}
|
|
13389
13408
|
//#endregion
|
|
13409
|
+
//#region src/gobj_tree_model.js
|
|
13410
|
+
/***********************************************************************
|
|
13411
|
+
* gobj_tree_model.js
|
|
13412
|
+
*
|
|
13413
|
+
* The gobj tree of a yuno, as plain data.
|
|
13414
|
+
*
|
|
13415
|
+
* The view (C_YUI_GOBJ_TREE_JS) draws NODE DESCRIPTORS, never
|
|
13416
|
+
* gobjs. There are two producers of a descriptor, because the
|
|
13417
|
+
* view has to show the gobjs of the BACKEND too:
|
|
13418
|
+
*
|
|
13419
|
+
* - describe_js_gobj() the gobj tree of THIS browser yuno
|
|
13420
|
+
* - describe_backend_tree() the answer of the backend command
|
|
13421
|
+
* `view-gobj-tree` of another yuno
|
|
13422
|
+
*
|
|
13423
|
+
* Both answer the SAME shape, and that shape is the backend's:
|
|
13424
|
+
* the field names are the ones `gobj2json()` writes in
|
|
13425
|
+
* `kernel/c/gobj-c/src/gobj.c` (fullname, shortname, gclass,
|
|
13426
|
+
* name, parent, state, running, playing, service, disabled,
|
|
13427
|
+
* volatil, bottom_gobj, gobj_flags, commands, gobj_trace_level).
|
|
13428
|
+
* Anything the browser runtime cannot answer is simply absent,
|
|
13429
|
+
* and every reader here treats absent as "not known", never as
|
|
13430
|
+
* false.
|
|
13431
|
+
*
|
|
13432
|
+
* Copyright (c) 2026, ArtGins.
|
|
13433
|
+
* All Rights Reserved.
|
|
13434
|
+
***********************************************************************/
|
|
13435
|
+
/************************************************************
|
|
13436
|
+
* The role of a node: "yuno" | "service" | "volatil" | "pure" | "child"
|
|
13437
|
+
************************************************************/
|
|
13438
|
+
function node_role(d) {
|
|
13439
|
+
if (!d) return "child";
|
|
13440
|
+
if (d.is_yuno) return "yuno";
|
|
13441
|
+
if (d.service) return "service";
|
|
13442
|
+
if (d.volatil) return "volatil";
|
|
13443
|
+
if (d.pure_child) return "pure";
|
|
13444
|
+
return "child";
|
|
13445
|
+
}
|
|
13446
|
+
/************************************************************
|
|
13447
|
+
* What the node is doing: "stopped" | "running" | "playing"
|
|
13448
|
+
************************************************************/
|
|
13449
|
+
function node_status(d) {
|
|
13450
|
+
if (!d || !d.running) return "stopped";
|
|
13451
|
+
return d.playing ? "playing" : "running";
|
|
13452
|
+
}
|
|
13453
|
+
/************************************************************
|
|
13454
|
+
* The i18n key of the status, which is NOT the status itself:
|
|
13455
|
+
* a gobj that runs and does not play is PAUSED to the reader,
|
|
13456
|
+
* and "running" alone never said which of the two it was.
|
|
13457
|
+
************************************************************/
|
|
13458
|
+
function node_status_key(d) {
|
|
13459
|
+
switch (node_status(d)) {
|
|
13460
|
+
case "playing": return "playing";
|
|
13461
|
+
case "running": return "running (paused)";
|
|
13462
|
+
default: return "stopped";
|
|
13463
|
+
}
|
|
13464
|
+
}
|
|
13465
|
+
/******************************
|
|
13466
|
+
* Producers
|
|
13467
|
+
******************************/
|
|
13468
|
+
/************************************************************
|
|
13469
|
+
* A descriptor of a LIVE gobj of this browser yuno, children
|
|
13470
|
+
* included (the whole subtree, depth-first).
|
|
13471
|
+
*
|
|
13472
|
+
* `is_root` marks the yuno itself: the JS runtime has no
|
|
13473
|
+
* gobj_flag_yuno test of its own.
|
|
13474
|
+
************************************************************/
|
|
13475
|
+
function describe_js_gobj(target_gobj, is_root) {
|
|
13476
|
+
if (!target_gobj) return null;
|
|
13477
|
+
let parent_gobj = gobj_parent(target_gobj);
|
|
13478
|
+
let bottom = gobj_bottom_gobj(target_gobj);
|
|
13479
|
+
let d = {
|
|
13480
|
+
gclass: gobj_gclass_name(target_gobj) || "",
|
|
13481
|
+
name: gobj_name(target_gobj) || "",
|
|
13482
|
+
shortname: gobj_short_name(target_gobj) || "",
|
|
13483
|
+
fullname: gobj_full_name(target_gobj) || "",
|
|
13484
|
+
parent: parent_gobj ? gobj_short_name(parent_gobj) || "" : "",
|
|
13485
|
+
state: gobj_current_state(target_gobj) || "",
|
|
13486
|
+
running: !!gobj_is_running(target_gobj),
|
|
13487
|
+
playing: !!gobj_is_playing(target_gobj),
|
|
13488
|
+
service: !!gobj_is_service(target_gobj),
|
|
13489
|
+
volatil: !!gobj_is_volatil(target_gobj),
|
|
13490
|
+
pure_child: !!gobj_is_pure_child(target_gobj),
|
|
13491
|
+
disabled: !!target_gobj.disabled,
|
|
13492
|
+
bottom_gobj: bottom ? gobj_short_name(bottom) || "" : "",
|
|
13493
|
+
commands: !!(target_gobj.gclass && target_gobj.gclass.command_table),
|
|
13494
|
+
is_yuno: !!is_root,
|
|
13495
|
+
children: []
|
|
13496
|
+
};
|
|
13497
|
+
let children = target_gobj.dl_children || [];
|
|
13498
|
+
for (let child of children) {
|
|
13499
|
+
let cd = describe_js_gobj(child, false);
|
|
13500
|
+
if (cd) d.children.push(cd);
|
|
13501
|
+
}
|
|
13502
|
+
return d;
|
|
13503
|
+
}
|
|
13504
|
+
/******************************
|
|
13505
|
+
* Readouts
|
|
13506
|
+
******************************/
|
|
13507
|
+
/************************************************************
|
|
13508
|
+
* The i18n key of a role. Two of the five roles are not the
|
|
13509
|
+
* word the reader wants ("pure" and "volatil" alone say
|
|
13510
|
+
* nothing), so the map is explicit.
|
|
13511
|
+
************************************************************/
|
|
13512
|
+
function role_i18n_key(role) {
|
|
13513
|
+
switch (role) {
|
|
13514
|
+
case "yuno": return "yuno";
|
|
13515
|
+
case "service": return "service";
|
|
13516
|
+
case "volatil": return "volatil child";
|
|
13517
|
+
case "pure": return "pure child";
|
|
13518
|
+
default: return "child";
|
|
13519
|
+
}
|
|
13520
|
+
}
|
|
13521
|
+
/************************************************************
|
|
13522
|
+
* The marks a node carries besides its role: what the reader
|
|
13523
|
+
* would otherwise have to open the popover to learn.
|
|
13524
|
+
*
|
|
13525
|
+
* Returns i18n KEYS, in a fixed order, so the card and the
|
|
13526
|
+
* popover cannot disagree about what a node is.
|
|
13527
|
+
************************************************************/
|
|
13528
|
+
function node_badge_keys(d) {
|
|
13529
|
+
let badges = [];
|
|
13530
|
+
if (!d) return badges;
|
|
13531
|
+
let role = node_role(d);
|
|
13532
|
+
if (role !== "child") badges.push(role_i18n_key(role));
|
|
13533
|
+
if (d.disabled) badges.push("disabled");
|
|
13534
|
+
if (d.bottom_gobj) badges.push("bottom");
|
|
13535
|
+
if (d.commands) badges.push("commands");
|
|
13536
|
+
return badges;
|
|
13537
|
+
}
|
|
13538
|
+
/************************************************************
|
|
13539
|
+
* The vocabulary of this view, translated.
|
|
13540
|
+
*
|
|
13541
|
+
* Written as literal t("...") calls and not as t(key): a key
|
|
13542
|
+
* reached through a variable is invisible to the locale
|
|
13543
|
+
* validator, which is how a fixed vocabulary ships in one
|
|
13544
|
+
* language and never changes it.
|
|
13545
|
+
************************************************************/
|
|
13546
|
+
function node_labels(t) {
|
|
13547
|
+
if (typeof t !== "function") t = (k) => k;
|
|
13548
|
+
return {
|
|
13549
|
+
role: {
|
|
13550
|
+
"yuno": t("yuno"),
|
|
13551
|
+
"service": t("service"),
|
|
13552
|
+
"volatil child": t("volatil child"),
|
|
13553
|
+
"pure child": t("pure child"),
|
|
13554
|
+
"child": t("child")
|
|
13555
|
+
},
|
|
13556
|
+
status: {
|
|
13557
|
+
"playing": t("playing"),
|
|
13558
|
+
"running (paused)": t("running (paused)"),
|
|
13559
|
+
"stopped": t("stopped")
|
|
13560
|
+
},
|
|
13561
|
+
status_short: {
|
|
13562
|
+
"playing": t("playing"),
|
|
13563
|
+
"running": t("running"),
|
|
13564
|
+
"stopped": t("stopped")
|
|
13565
|
+
},
|
|
13566
|
+
badge: {
|
|
13567
|
+
"yuno": t("yuno"),
|
|
13568
|
+
"service": t("service"),
|
|
13569
|
+
"volatil child": t("volatil child"),
|
|
13570
|
+
"pure child": t("pure child"),
|
|
13571
|
+
"disabled": t("disabled"),
|
|
13572
|
+
"bottom": t("bottom"),
|
|
13573
|
+
"commands": t("commands")
|
|
13574
|
+
},
|
|
13575
|
+
field: {
|
|
13576
|
+
gclass: t("gclass"),
|
|
13577
|
+
name: t("name"),
|
|
13578
|
+
full_name: t("full name"),
|
|
13579
|
+
role: t("role"),
|
|
13580
|
+
status: t("status"),
|
|
13581
|
+
state: t("state"),
|
|
13582
|
+
disabled: t("disabled"),
|
|
13583
|
+
parent: t("parent"),
|
|
13584
|
+
bottom_gobj: t("bottom gobj"),
|
|
13585
|
+
commands: t("commands"),
|
|
13586
|
+
traces: t("traces"),
|
|
13587
|
+
flags: t("flags"),
|
|
13588
|
+
children: t("children"),
|
|
13589
|
+
yes: t("yes"),
|
|
13590
|
+
collapsed: t("(collapsed)")
|
|
13591
|
+
}
|
|
13592
|
+
};
|
|
13593
|
+
}
|
|
13594
|
+
/************************************************************
|
|
13595
|
+
* The rows of the popover: [label, value], both translated.
|
|
13596
|
+
*
|
|
13597
|
+
* `t` is the app's translator -- the library has none of its
|
|
13598
|
+
* own. Values that are a fixed vocabulary (the role, the
|
|
13599
|
+
* status, yes) go through it too; free text does not.
|
|
13600
|
+
************************************************************/
|
|
13601
|
+
function node_info_rows(d, t) {
|
|
13602
|
+
let rows = [];
|
|
13603
|
+
if (!d) return rows;
|
|
13604
|
+
let L = node_labels(t);
|
|
13605
|
+
let f = L.field;
|
|
13606
|
+
rows.push([f.gclass, d.gclass || "—"]);
|
|
13607
|
+
rows.push([f.name, d.name || "—"]);
|
|
13608
|
+
if (d.fullname && d.fullname !== d.shortname) rows.push([f.full_name, d.fullname]);
|
|
13609
|
+
rows.push([f.role, L.role[role_i18n_key(node_role(d))]]);
|
|
13610
|
+
rows.push([f.status, L.status[node_status_key(d)]]);
|
|
13611
|
+
rows.push([f.state, d.state || "—"]);
|
|
13612
|
+
if (d.disabled) rows.push([f.disabled, f.yes]);
|
|
13613
|
+
if (d.parent) rows.push([f.parent, d.parent]);
|
|
13614
|
+
if (d.bottom_gobj) rows.push([f.bottom_gobj, d.bottom_gobj]);
|
|
13615
|
+
if (d.commands) rows.push([f.commands, f.yes]);
|
|
13616
|
+
if (Array.isArray(d.gobj_trace_level) && d.gobj_trace_level.length > 0) rows.push([f.traces, d.gobj_trace_level.join(", ")]);
|
|
13617
|
+
if (Array.isArray(d.gobj_flags) && d.gobj_flags.length > 0) rows.push([f.flags, d.gobj_flags.join(", ")]);
|
|
13618
|
+
let kids = Array.isArray(d.children) ? d.children.length : 0;
|
|
13619
|
+
if (kids > 0) rows.push([f.children, d.is_collapsed ? kids + " " + f.collapsed : String(kids)]);
|
|
13620
|
+
return rows;
|
|
13621
|
+
}
|
|
13622
|
+
//#endregion
|
|
13623
|
+
//#region src/gclass_describe.js
|
|
13624
|
+
/***********************************************************************
|
|
13625
|
+
* gclass_describe.js
|
|
13626
|
+
*
|
|
13627
|
+
* A gclass, as plain JSON, in the shape the BACKEND answers.
|
|
13628
|
+
*
|
|
13629
|
+
* There is no gclass viewer in the framework: the C kernel
|
|
13630
|
+
* answers `view-gclass` with `gclass2json()`
|
|
13631
|
+
* (`kernel/c/gobj-c/src/gobj.c`) and nothing draws it. This
|
|
13632
|
+
* module builds the same document for a gclass of the BROWSER
|
|
13633
|
+
* yuno, out of the gobj-js registry, so one viewer can show a
|
|
13634
|
+
* gclass of either side:
|
|
13635
|
+
*
|
|
13636
|
+
* {id, gcflag, attrs, commands, gclass_methods,
|
|
13637
|
+
* internal_methods, FSM: {events, states}}
|
|
13638
|
+
*
|
|
13639
|
+
* Two keys the backend does not carry are added at the end,
|
|
13640
|
+
* because the browser can answer them for free and the reader
|
|
13641
|
+
* asks them first: `instances` and `trace_levels`.
|
|
13642
|
+
*
|
|
13643
|
+
* Copyright (c) 2026, ArtGins.
|
|
13644
|
+
* All Rights Reserved.
|
|
13645
|
+
***********************************************************************/
|
|
13646
|
+
/***************************************************************
|
|
13647
|
+
* Bit tables
|
|
13648
|
+
***************************************************************/
|
|
13649
|
+
var DATA_TYPE_NAMES = {
|
|
13650
|
+
[data_type_t.DTP_STRING]: "DTP_STRING",
|
|
13651
|
+
[data_type_t.DTP_BOOLEAN]: "DTP_BOOLEAN",
|
|
13652
|
+
[data_type_t.DTP_INTEGER]: "DTP_INTEGER",
|
|
13653
|
+
[data_type_t.DTP_REAL]: "DTP_REAL",
|
|
13654
|
+
[data_type_t.DTP_LIST]: "DTP_LIST",
|
|
13655
|
+
[data_type_t.DTP_DICT]: "DTP_DICT",
|
|
13656
|
+
[data_type_t.DTP_JSON]: "DTP_JSON",
|
|
13657
|
+
[data_type_t.DTP_POINTER]: "DTP_POINTER"
|
|
13658
|
+
};
|
|
13659
|
+
/************************************************************
|
|
13660
|
+
* The names of the bits set in `bits`, in table order.
|
|
13661
|
+
************************************************************/
|
|
13662
|
+
function bits2names(table, bits) {
|
|
13663
|
+
let names = [];
|
|
13664
|
+
let value = Number(bits) || 0;
|
|
13665
|
+
for (let name of Object.keys(table)) if ((value & table[name]) !== 0) names.push(name);
|
|
13666
|
+
return names;
|
|
13667
|
+
}
|
|
13668
|
+
/************************************************************
|
|
13669
|
+
* The name of a data type. An unknown type is reported as
|
|
13670
|
+
* the number it is -- silently drawing "DTP_STRING" for a
|
|
13671
|
+
* type nobody registered would be worse than saying "17".
|
|
13672
|
+
************************************************************/
|
|
13673
|
+
function data_type_name(type) {
|
|
13674
|
+
return DATA_TYPE_NAMES[type] || String(type);
|
|
13675
|
+
}
|
|
13676
|
+
/************************************************************
|
|
13677
|
+
* One SDATA descriptor -> one row.
|
|
13678
|
+
*
|
|
13679
|
+
* A DTP_POINTER default is a live object (a DOM node, a
|
|
13680
|
+
* gobj): it is never printed, only named, because a viewer
|
|
13681
|
+
* that serializes it would either throw or dump the page.
|
|
13682
|
+
************************************************************/
|
|
13683
|
+
function sdatadesc2json(it) {
|
|
13684
|
+
let row = {
|
|
13685
|
+
id: it.name,
|
|
13686
|
+
type: data_type_name(it.type),
|
|
13687
|
+
flag: bits2names(sdata_flag_t, it.flag),
|
|
13688
|
+
description: it.description || ""
|
|
13689
|
+
};
|
|
13690
|
+
if (it.type === data_type_t.DTP_POINTER) row.default_value = it.default_value === null || it.default_value === void 0 ? null : "<pointer>";
|
|
13691
|
+
else row.default_value = it.default_value === void 0 ? null : it.default_value;
|
|
13692
|
+
return row;
|
|
13693
|
+
}
|
|
13694
|
+
/************************************************************
|
|
13695
|
+
* The attrs table of a gclass.
|
|
13696
|
+
************************************************************/
|
|
13697
|
+
function attrs2json(attrs_table) {
|
|
13698
|
+
let rows = [];
|
|
13699
|
+
if (!Array.isArray(attrs_table)) return rows;
|
|
13700
|
+
for (let it of attrs_table) {
|
|
13701
|
+
if (!it || !it.name) continue;
|
|
13702
|
+
rows.push(sdatadesc2json(it));
|
|
13703
|
+
}
|
|
13704
|
+
return rows;
|
|
13705
|
+
}
|
|
13706
|
+
/************************************************************
|
|
13707
|
+
* The command table of a gclass, with the parameters of
|
|
13708
|
+
* each command (the `schema` slot of SDATACM).
|
|
13709
|
+
************************************************************/
|
|
13710
|
+
function commands2json(command_table) {
|
|
13711
|
+
let rows = [];
|
|
13712
|
+
if (!Array.isArray(command_table)) return rows;
|
|
13713
|
+
for (let it of command_table) {
|
|
13714
|
+
if (!it || !it.name) continue;
|
|
13715
|
+
let row = {
|
|
13716
|
+
id: it.name,
|
|
13717
|
+
alias: it.alias || [],
|
|
13718
|
+
description: it.description || ""
|
|
13719
|
+
};
|
|
13720
|
+
if (Array.isArray(it.schema)) row.parameters = attrs2json(it.schema);
|
|
13721
|
+
rows.push(row);
|
|
13722
|
+
}
|
|
13723
|
+
return rows;
|
|
13724
|
+
}
|
|
13725
|
+
/************************************************************
|
|
13726
|
+
* The methods the gclass actually implements. A table of
|
|
13727
|
+
* every possible one, mostly empty, tells the reader nothing.
|
|
13728
|
+
************************************************************/
|
|
13729
|
+
function methods2json(mt) {
|
|
13730
|
+
let names = [];
|
|
13731
|
+
if (!mt || typeof mt !== "object") return names;
|
|
13732
|
+
for (let key of Object.keys(mt)) if (typeof mt[key] === "function") names.push(key);
|
|
13733
|
+
return names;
|
|
13734
|
+
}
|
|
13735
|
+
/************************************************************
|
|
13736
|
+
* The FSM: the events the gclass declares, and what each
|
|
13737
|
+
* state does with them.
|
|
13738
|
+
************************************************************/
|
|
13739
|
+
function fsm2json(gclass) {
|
|
13740
|
+
let events = [];
|
|
13741
|
+
for (let ev of gclass.dl_events || []) events.push({
|
|
13742
|
+
event_name: ev.event_name,
|
|
13743
|
+
event_flag: bits2names(event_flag_t, ev.event_flag)
|
|
13744
|
+
});
|
|
13745
|
+
let states = {};
|
|
13746
|
+
for (let st of gclass.dl_states || []) {
|
|
13747
|
+
let actions = [];
|
|
13748
|
+
for (let ac of st.dl_actions || []) actions.push({
|
|
13749
|
+
event_name: ac.event_name,
|
|
13750
|
+
action: ac.action ? ac.action.name || "action" : "",
|
|
13751
|
+
next_state: ac.next_state || ""
|
|
13752
|
+
});
|
|
13753
|
+
states[st.state_name] = actions;
|
|
13754
|
+
}
|
|
13755
|
+
return {
|
|
13756
|
+
events,
|
|
13757
|
+
states
|
|
13758
|
+
};
|
|
13759
|
+
}
|
|
13760
|
+
/************************************************************
|
|
13761
|
+
* The description of a gclass REGISTERED IN THIS BROWSER YUNO,
|
|
13762
|
+
* or null when the registry does not know that name.
|
|
13763
|
+
*
|
|
13764
|
+
* Null is the answer for a gclass of a BACKEND yuno too: the
|
|
13765
|
+
* browser registry carries the GUI gclasses and nothing else,
|
|
13766
|
+
* so the caller asks the backend `view-gclass` for those and
|
|
13767
|
+
* shows whatever it answers -- this same shape.
|
|
13768
|
+
************************************************************/
|
|
13769
|
+
function describe_local_gclass(gclass_name) {
|
|
13770
|
+
if (!gclass_name) return null;
|
|
13771
|
+
let gclass = gclass_find_by_name(gclass_name, false);
|
|
13772
|
+
if (!gclass) return null;
|
|
13773
|
+
return {
|
|
13774
|
+
id: gclass.gclass_name,
|
|
13775
|
+
gcflag: bits2names(gclass_flag_t, gclass.gclass_flag),
|
|
13776
|
+
attrs: attrs2json(gclass.attrs_table),
|
|
13777
|
+
commands: commands2json(gclass.command_table),
|
|
13778
|
+
gclass_methods: methods2json(gclass.gmt),
|
|
13779
|
+
internal_methods: methods2json(gclass.lmt),
|
|
13780
|
+
FSM: fsm2json(gclass),
|
|
13781
|
+
instances: Number(gclass.instances) || 0,
|
|
13782
|
+
trace_levels: Array.isArray(gclass.s_user_trace_level) ? gclass.s_user_trace_level : []
|
|
13783
|
+
};
|
|
13784
|
+
}
|
|
13785
|
+
//#endregion
|
|
13786
|
+
//#region src/yui_gclass_view.js
|
|
13787
|
+
/***********************************************************************
|
|
13788
|
+
* yui_gclass_view.js
|
|
13789
|
+
*
|
|
13790
|
+
* The gclass viewer: what a GClass IS, in a window.
|
|
13791
|
+
*
|
|
13792
|
+
* The framework had no such viewer. The C kernel answers
|
|
13793
|
+
* `view-gclass` with a full description (attrs, commands,
|
|
13794
|
+
* methods, FSM) and the only reader of it was a terminal.
|
|
13795
|
+
* This one draws that same document with C_YUI_JSON, so the
|
|
13796
|
+
* FSM of a gclass is a graph and its attrs table is a tree,
|
|
13797
|
+
* and it takes the document from either side:
|
|
13798
|
+
*
|
|
13799
|
+
* - a gclass of the browser yuno -> describe_local_gclass()
|
|
13800
|
+
* - a gclass of a backend yuno -> `opts.description`, the
|
|
13801
|
+
* answer of `view-gclass`
|
|
13802
|
+
*
|
|
13803
|
+
* One window at a time per host: the caller keeps the handle
|
|
13804
|
+
* and closes it, the way the treedb graph keeps its raw-JSON
|
|
13805
|
+
* viewer.
|
|
13806
|
+
*
|
|
13807
|
+
* Copyright (c) 2026, ArtGins.
|
|
13808
|
+
* All Rights Reserved.
|
|
13809
|
+
***********************************************************************/
|
|
13810
|
+
/************************************************************
|
|
13811
|
+
* Can this app show a gclass at all?
|
|
13812
|
+
*
|
|
13813
|
+
* The viewer is C_YUI_JSON, and an app registers only the
|
|
13814
|
+
* gclasses it mounts. The caller asks this BEFORE drawing a
|
|
13815
|
+
* button, so an app that does not carry the viewer shows no
|
|
13816
|
+
* control instead of a control that fails.
|
|
13817
|
+
************************************************************/
|
|
13818
|
+
function gclass_view_available() {
|
|
13819
|
+
return !!gclass_find_by_name("C_YUI_JSON", false);
|
|
13820
|
+
}
|
|
13821
|
+
/************************************************************
|
|
13822
|
+
* Open the viewer on `gclass_name`.
|
|
13823
|
+
*
|
|
13824
|
+
* opts:
|
|
13825
|
+
* description the document to draw. Omitted, it is
|
|
13826
|
+
* built from the browser registry.
|
|
13827
|
+
* title_prefix what the window says before the gclass
|
|
13828
|
+
* name (the yuno it belongs to).
|
|
13829
|
+
* on_close called when the reader dismisses it.
|
|
13830
|
+
*
|
|
13831
|
+
* Returns a handle for close_gclass_view(), or null (and a
|
|
13832
|
+
* logged reason) when there is nothing to show.
|
|
13833
|
+
************************************************************/
|
|
13834
|
+
function open_gclass_view(host, gclass_name, opts) {
|
|
13835
|
+
opts = opts || {};
|
|
13836
|
+
if (!host || !gclass_name) {
|
|
13837
|
+
log_error(`yui_gclass_view: open without a host or a gclass name`);
|
|
13838
|
+
return null;
|
|
13839
|
+
}
|
|
13840
|
+
let shell = yui_shell_of(host);
|
|
13841
|
+
if (!gclass_view_available()) {
|
|
13842
|
+
log_error(`${gobj_short_name(host)}: C_YUI_JSON not registered by the app`);
|
|
13843
|
+
yui_shell_show_error(shell, "gclass viewer unavailable", { t });
|
|
13844
|
+
return null;
|
|
13845
|
+
}
|
|
13846
|
+
let description = opts.description || describe_local_gclass(gclass_name);
|
|
13847
|
+
if (!description) {
|
|
13848
|
+
log_warning(`${gobj_short_name(host)}: gclass not registered here: ${gclass_name}`);
|
|
13849
|
+
yui_shell_show_error(shell, "gclass not registered in this yuno", { t });
|
|
13850
|
+
return null;
|
|
13851
|
+
}
|
|
13852
|
+
let jv = gobj_create_service(`gclass-view-${clean_name(gobj_name(host))}`, "C_YUI_JSON", {
|
|
13853
|
+
json_data: description,
|
|
13854
|
+
subscriber: null
|
|
13855
|
+
}, host);
|
|
13856
|
+
if (!jv) {
|
|
13857
|
+
log_error(`${gobj_short_name(host)}: cannot create the gclass viewer`);
|
|
13858
|
+
return null;
|
|
13859
|
+
}
|
|
13860
|
+
let handle = {
|
|
13861
|
+
json_gobj: jv,
|
|
13862
|
+
win: null,
|
|
13863
|
+
modal: null
|
|
13864
|
+
};
|
|
13865
|
+
let $box = gobj_read_pointer_attr(jv, "$container");
|
|
13866
|
+
let on_close = () => {
|
|
13867
|
+
if (typeof opts.on_close === "function") opts.on_close();
|
|
13868
|
+
};
|
|
13869
|
+
if (is_mobile$3()) {
|
|
13870
|
+
if (!shell) {
|
|
13871
|
+
log_error(`${gobj_short_name(host)}: no shell, cannot open the gclass sheet`);
|
|
13872
|
+
close_gclass_view(handle);
|
|
13873
|
+
return null;
|
|
13874
|
+
}
|
|
13875
|
+
handle.modal = yui_shell_show_modal(shell, $box, {
|
|
13876
|
+
dialog: true,
|
|
13877
|
+
logical_class: "GCLASS_VIEW_SHEET",
|
|
13878
|
+
title_prefix: opts.title_prefix || "",
|
|
13879
|
+
title: gclass_name,
|
|
13880
|
+
t,
|
|
13881
|
+
on_close
|
|
13882
|
+
});
|
|
13883
|
+
gobj_start(jv);
|
|
13884
|
+
return handle;
|
|
13885
|
+
}
|
|
13886
|
+
let $win_parent = window_layer_of(host, shell);
|
|
13887
|
+
handle.win = gobj_create_service(`gclass-win-${clean_name(gobj_name(host))}`, "C_YUI_WINDOW", {
|
|
13888
|
+
$parent: $win_parent,
|
|
13889
|
+
subscriber: null,
|
|
13890
|
+
modal: false,
|
|
13891
|
+
showMax: true,
|
|
13892
|
+
showFooter: false,
|
|
13893
|
+
resizable: true,
|
|
13894
|
+
center: true,
|
|
13895
|
+
auto_save_size_and_position: true,
|
|
13896
|
+
width: 720,
|
|
13897
|
+
height: 640,
|
|
13898
|
+
logical_class: "GCLASS_VIEW_WINDOW",
|
|
13899
|
+
title_prefix: opts.title_prefix || "",
|
|
13900
|
+
title: gclass_name,
|
|
13901
|
+
icon: "yi-code",
|
|
13902
|
+
body: $box,
|
|
13903
|
+
manager: gobj_find_service("__window_manager__", false) || null,
|
|
13904
|
+
on_close
|
|
13905
|
+
}, host);
|
|
13906
|
+
if (!handle.win) {
|
|
13907
|
+
log_error(`${gobj_short_name(host)}: cannot create the gclass window`);
|
|
13908
|
+
close_gclass_view(handle);
|
|
13909
|
+
return null;
|
|
13910
|
+
}
|
|
13911
|
+
gobj_start(handle.win);
|
|
13912
|
+
gobj_start(jv);
|
|
13913
|
+
return handle;
|
|
13914
|
+
}
|
|
13915
|
+
/************************************************************
|
|
13916
|
+
* Close whatever open_gclass_view() opened. Safe on null and
|
|
13917
|
+
* on a handle whose window the reader already dismissed.
|
|
13918
|
+
************************************************************/
|
|
13919
|
+
function close_gclass_view(handle) {
|
|
13920
|
+
if (!handle) return;
|
|
13921
|
+
let jv = handle.json_gobj;
|
|
13922
|
+
let win = handle.win;
|
|
13923
|
+
let modal = handle.modal;
|
|
13924
|
+
handle.json_gobj = null;
|
|
13925
|
+
handle.win = null;
|
|
13926
|
+
handle.modal = null;
|
|
13927
|
+
if (win && is_gobj(win)) try {
|
|
13928
|
+
if (gobj_is_running(win)) gobj_stop(win);
|
|
13929
|
+
gobj_destroy(win);
|
|
13930
|
+
} catch (e) {
|
|
13931
|
+
log_warning(`yui_gclass_view: window already gone: ${e}`);
|
|
13932
|
+
}
|
|
13933
|
+
if (modal && typeof modal.close === "function") try {
|
|
13934
|
+
modal.close();
|
|
13935
|
+
} catch (e) {
|
|
13936
|
+
log_warning(`yui_gclass_view: modal already gone: ${e}`);
|
|
13937
|
+
}
|
|
13938
|
+
if (jv && is_gobj(jv)) try {
|
|
13939
|
+
if (gobj_is_running(jv)) gobj_stop(jv);
|
|
13940
|
+
gobj_destroy(jv);
|
|
13941
|
+
} catch (e) {
|
|
13942
|
+
log_warning(`yui_gclass_view: viewer already gone: ${e}`);
|
|
13943
|
+
}
|
|
13944
|
+
}
|
|
13945
|
+
/************************************************************
|
|
13946
|
+
* Where to mount the window: the SAME element the caller's
|
|
13947
|
+
* own window hangs from.
|
|
13948
|
+
*
|
|
13949
|
+
* Not a preference -- a requirement. z-index only orders
|
|
13950
|
+
* siblings of one stacking context, so a viewer opened from a
|
|
13951
|
+
* window on `body` and mounted in the shell's popup layer
|
|
13952
|
+
* lands BEHIND its opener whatever number it is given, and
|
|
13953
|
+
* the button reads as one that did nothing.
|
|
13954
|
+
************************************************************/
|
|
13955
|
+
function window_layer_of(host, shell) {
|
|
13956
|
+
let $c = gobj_read_pointer_attr(host, "$container");
|
|
13957
|
+
if ($c && typeof $c.closest === "function") {
|
|
13958
|
+
let $win = $c.closest(".C_YUI_WINDOW");
|
|
13959
|
+
if ($win && $win.parentNode) return $win.parentNode;
|
|
13960
|
+
}
|
|
13961
|
+
return typeof document !== "undefined" && document.getElementById("top-layer") || shell && yui_shell_popup_layer(shell) || null;
|
|
13962
|
+
}
|
|
13963
|
+
/************************************************************
|
|
13964
|
+
* A window is not a sheet: below this width there is no room
|
|
13965
|
+
* for a title bar the reader can drag.
|
|
13966
|
+
************************************************************/
|
|
13967
|
+
function is_mobile$3() {
|
|
13968
|
+
return typeof window !== "undefined" && window.innerWidth <= 768;
|
|
13969
|
+
}
|
|
13970
|
+
//#endregion
|
|
13390
13971
|
//#region src/c_yui_gobj_tree_js.js
|
|
13391
13972
|
/***********************************************************************
|
|
13392
13973
|
* c_yui_gobj_tree_js.js
|
|
@@ -13497,6 +14078,9 @@ var PRIVATE_DATA$9 = {
|
|
|
13497
14078
|
$popover: null,
|
|
13498
14079
|
$popover_title: null,
|
|
13499
14080
|
$popover_body: null,
|
|
14081
|
+
$popover_gclass: null,
|
|
14082
|
+
popover_node: null,
|
|
14083
|
+
gclass_view: null,
|
|
13500
14084
|
node_by_id: null,
|
|
13501
14085
|
collapse_state: null,
|
|
13502
14086
|
resize_observer: null,
|
|
@@ -13511,34 +14095,92 @@ var PRIVATE_DATA$9 = {
|
|
|
13511
14095
|
var __gclass__$9 = null;
|
|
13512
14096
|
var __layout_registered__ = false;
|
|
13513
14097
|
/***************************************************************
|
|
13514
|
-
* Node
|
|
14098
|
+
* Node colours by role.
|
|
14099
|
+
*
|
|
14100
|
+
* Saturated hues, one family apart from the next, and the same
|
|
14101
|
+
* five everywhere the reader meets a gobj. `stroke` draws the
|
|
14102
|
+
* border and the edge; `accent` is the brighter sibling used
|
|
14103
|
+
* for the bar down the left of the card and the toggle badge,
|
|
14104
|
+
* so a card carries its colour twice at two intensities and is
|
|
14105
|
+
* readable at the zoom where the text is not.
|
|
13515
14106
|
***************************************************************/
|
|
13516
14107
|
var ROLE_COLORS = {
|
|
13517
14108
|
yuno: {
|
|
13518
|
-
|
|
13519
|
-
|
|
14109
|
+
stroke: "#0284c7",
|
|
14110
|
+
accent: "#38bdf8"
|
|
13520
14111
|
},
|
|
13521
14112
|
service: {
|
|
13522
|
-
|
|
13523
|
-
|
|
14113
|
+
stroke: "#059669",
|
|
14114
|
+
accent: "#34d399"
|
|
13524
14115
|
},
|
|
13525
14116
|
pure: {
|
|
13526
|
-
|
|
13527
|
-
|
|
14117
|
+
stroke: "#d97706",
|
|
14118
|
+
accent: "#fbbf24"
|
|
13528
14119
|
},
|
|
13529
14120
|
volatil: {
|
|
13530
|
-
|
|
13531
|
-
|
|
14121
|
+
stroke: "#db2777",
|
|
14122
|
+
accent: "#f472b6"
|
|
13532
14123
|
},
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
14124
|
+
child: {
|
|
14125
|
+
stroke: "#7c3aed",
|
|
14126
|
+
accent: "#a78bfa"
|
|
13536
14127
|
}
|
|
13537
14128
|
};
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
14129
|
+
/***************************************************************
|
|
14130
|
+
* Status colours: what the gobj is DOING.
|
|
14131
|
+
*
|
|
14132
|
+
* A pill and not a word: three states of one gobj are told
|
|
14133
|
+
* apart across a whole tree by colour long before anybody
|
|
14134
|
+
* reads them, and the dot alone never said which was which.
|
|
14135
|
+
* `disabled` is a fourth entry and not a status -- a disabled
|
|
14136
|
+
* gobj is still stopped or running -- but it takes the pill,
|
|
14137
|
+
* because it is the fact that explains the other one.
|
|
14138
|
+
***************************************************************/
|
|
14139
|
+
var STATUS_COLORS = {
|
|
14140
|
+
light: {
|
|
14141
|
+
playing: {
|
|
14142
|
+
fg: "#065f46",
|
|
14143
|
+
bg: "#a7f3d0",
|
|
14144
|
+
dot: "#10b981"
|
|
14145
|
+
},
|
|
14146
|
+
running: {
|
|
14147
|
+
fg: "#92400e",
|
|
14148
|
+
bg: "#fde68a",
|
|
14149
|
+
dot: "#f59e0b"
|
|
14150
|
+
},
|
|
14151
|
+
stopped: {
|
|
14152
|
+
fg: "#991b1b",
|
|
14153
|
+
bg: "#fecaca",
|
|
14154
|
+
dot: "#ef4444"
|
|
14155
|
+
},
|
|
14156
|
+
disabled: {
|
|
14157
|
+
fg: "#334155",
|
|
14158
|
+
bg: "#e2e8f0",
|
|
14159
|
+
dot: "#94a3b8"
|
|
14160
|
+
}
|
|
14161
|
+
},
|
|
14162
|
+
dark: {
|
|
14163
|
+
playing: {
|
|
14164
|
+
fg: "#6ee7b7",
|
|
14165
|
+
bg: "rgba(16,185,129,0.28)",
|
|
14166
|
+
dot: "#34d399"
|
|
14167
|
+
},
|
|
14168
|
+
running: {
|
|
14169
|
+
fg: "#fcd34d",
|
|
14170
|
+
bg: "rgba(245,158,11,0.28)",
|
|
14171
|
+
dot: "#fbbf24"
|
|
14172
|
+
},
|
|
14173
|
+
stopped: {
|
|
14174
|
+
fg: "#fca5a5",
|
|
14175
|
+
bg: "rgba(239,68,68,0.28)",
|
|
14176
|
+
dot: "#f87171"
|
|
14177
|
+
},
|
|
14178
|
+
disabled: {
|
|
14179
|
+
fg: "#cbd5e1",
|
|
14180
|
+
bg: "rgba(148,163,184,0.28)",
|
|
14181
|
+
dot: "#94a3b8"
|
|
14182
|
+
}
|
|
14183
|
+
}
|
|
13542
14184
|
};
|
|
13543
14185
|
/***************************************************************
|
|
13544
14186
|
* Fixed node sizes per role (px).
|
|
@@ -13548,16 +14190,16 @@ var STATE_COLORS = {
|
|
|
13548
14190
|
var NODE_SIZES = {
|
|
13549
14191
|
full: {
|
|
13550
14192
|
yuno: {
|
|
13551
|
-
w:
|
|
13552
|
-
h:
|
|
14193
|
+
w: 260,
|
|
14194
|
+
h: 96
|
|
13553
14195
|
},
|
|
13554
14196
|
service: {
|
|
13555
|
-
w:
|
|
13556
|
-
h:
|
|
14197
|
+
w: 232,
|
|
14198
|
+
h: 92
|
|
13557
14199
|
},
|
|
13558
14200
|
child: {
|
|
13559
|
-
w:
|
|
13560
|
-
h:
|
|
14201
|
+
w: 196,
|
|
14202
|
+
h: 86
|
|
13561
14203
|
}
|
|
13562
14204
|
},
|
|
13563
14205
|
compact: {
|
|
@@ -13580,27 +14222,47 @@ var NODE_SIZES = {
|
|
|
13580
14222
|
***************************************************************/
|
|
13581
14223
|
var GT_FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
|
|
13582
14224
|
/***************************************************************
|
|
13583
|
-
*
|
|
13584
|
-
*
|
|
13585
|
-
*
|
|
14225
|
+
* Theme-aware card palette derived from the role colour.
|
|
14226
|
+
*
|
|
14227
|
+
* Brighter than a tint: at 9% of the role colour every card
|
|
14228
|
+
* was the same near-white rectangle and the tree read as a
|
|
14229
|
+
* wireframe. The fill carries enough of the hue to be named
|
|
14230
|
+
* from across the canvas, and the border and the accent bar
|
|
14231
|
+
* carry the rest.
|
|
13586
14232
|
***************************************************************/
|
|
13587
|
-
function role_card_style(
|
|
14233
|
+
function role_card_style(colors, dark) {
|
|
14234
|
+
let stroke = colors.stroke;
|
|
14235
|
+
let accent = colors.accent;
|
|
13588
14236
|
return {
|
|
13589
|
-
bg: dark ? `color-mix(in srgb, ${stroke}
|
|
13590
|
-
border: dark ?
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
|
|
14237
|
+
bg: dark ? `color-mix(in srgb, ${stroke} 34%, #253044)` : `color-mix(in srgb, ${stroke} 13%, #ffffff)`,
|
|
14238
|
+
border: dark ? accent : stroke,
|
|
14239
|
+
accent: dark ? accent : stroke,
|
|
14240
|
+
title: dark ? "#f1f5f9" : "#0f172a",
|
|
14241
|
+
sub: dark ? "#a8b3c2" : "#475569",
|
|
14242
|
+
tagbg: dark ? `color-mix(in srgb, ${stroke} 48%, #253044)` : `color-mix(in srgb, ${stroke} 20%, #ffffff)`,
|
|
14243
|
+
tagfg: dark ? "#f1f5f9" : stroke,
|
|
14244
|
+
shadow: dark ? "0 2px 6px rgba(0,0,0,0.50)" : "0 2px 5px rgba(15,23,42,0.16)"
|
|
13595
14245
|
};
|
|
13596
14246
|
}
|
|
13597
14247
|
/***************************************************************
|
|
13598
|
-
*
|
|
14248
|
+
* The pill colours of a node, for the theme in force.
|
|
13599
14249
|
***************************************************************/
|
|
13600
|
-
function
|
|
13601
|
-
|
|
13602
|
-
if (
|
|
13603
|
-
return
|
|
14250
|
+
function status_card_style(d, dark) {
|
|
14251
|
+
let table = dark ? STATUS_COLORS.dark : STATUS_COLORS.light;
|
|
14252
|
+
if (d.disabled) return table.disabled;
|
|
14253
|
+
return table[node_status(d)] || table.stopped;
|
|
14254
|
+
}
|
|
14255
|
+
/***************************************************************
|
|
14256
|
+
* Map a node to its visual size class. Services are drawn
|
|
14257
|
+
* bigger than plain children so the service boundary of the
|
|
14258
|
+
* hierarchy is visible before anything is read.
|
|
14259
|
+
***************************************************************/
|
|
14260
|
+
function get_node_category(d) {
|
|
14261
|
+
switch (node_role(d)) {
|
|
14262
|
+
case "yuno": return "yuno";
|
|
14263
|
+
case "service": return "service";
|
|
14264
|
+
default: return "child";
|
|
14265
|
+
}
|
|
13604
14266
|
}
|
|
13605
14267
|
/***************************************************************
|
|
13606
14268
|
* Layouts registry.
|
|
@@ -13713,13 +14375,21 @@ function mt_create$9(gobj) {
|
|
|
13713
14375
|
***************************************************************/
|
|
13714
14376
|
function mt_start$9(gobj) {
|
|
13715
14377
|
restore_view_shape(gobj);
|
|
14378
|
+
let shell = yui_shell_of(gobj);
|
|
14379
|
+
if (shell) gobj_subscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
13716
14380
|
build_graph$2(gobj);
|
|
13717
14381
|
load_tree(gobj);
|
|
13718
14382
|
}
|
|
13719
14383
|
/***************************************************************
|
|
13720
14384
|
* Framework Method: Stop
|
|
13721
14385
|
***************************************************************/
|
|
13722
|
-
function mt_stop$9(gobj) {
|
|
14386
|
+
function mt_stop$9(gobj) {
|
|
14387
|
+
let priv = gobj.priv;
|
|
14388
|
+
let shell = yui_shell_of(gobj);
|
|
14389
|
+
if (shell) gobj_unsubscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
14390
|
+
close_gclass_view(priv.gclass_view);
|
|
14391
|
+
priv.gclass_view = null;
|
|
14392
|
+
}
|
|
13723
14393
|
/***************************************************************
|
|
13724
14394
|
* Framework Method: Destroy
|
|
13725
14395
|
***************************************************************/
|
|
@@ -13745,6 +14415,8 @@ function mt_destroy$9(gobj) {
|
|
|
13745
14415
|
priv.$popover = null;
|
|
13746
14416
|
priv.$popover_title = null;
|
|
13747
14417
|
priv.$popover_body = null;
|
|
14418
|
+
priv.$popover_gclass = null;
|
|
14419
|
+
priv.popover_node = null;
|
|
13748
14420
|
priv.$layout_select = null;
|
|
13749
14421
|
priv.node_by_id = null;
|
|
13750
14422
|
priv.collapse_state = null;
|
|
@@ -13848,6 +14520,30 @@ function build_popover(gobj, $container) {
|
|
|
13848
14520
|
text-overflow: ellipsis;
|
|
13849
14521
|
`;
|
|
13850
14522
|
$header.appendChild($title);
|
|
14523
|
+
if (gclass_view_available()) {
|
|
14524
|
+
let $gclass = document.createElement("button");
|
|
14525
|
+
$gclass.type = "button";
|
|
14526
|
+
$gclass.className = "GOBJ_TREE_POPOVER_GCLASS button is-small";
|
|
14527
|
+
$gclass.style.cssText = `
|
|
14528
|
+
margin-right: 6px;
|
|
14529
|
+
padding: 0 8px;
|
|
14530
|
+
height: 22px;
|
|
14531
|
+
font-size: 11px;
|
|
14532
|
+
white-space: nowrap;
|
|
14533
|
+
`;
|
|
14534
|
+
$gclass.textContent = t("gclass");
|
|
14535
|
+
$gclass.setAttribute("i18n", "gclass");
|
|
14536
|
+
$gclass.title = t("view gclass");
|
|
14537
|
+
$gclass.setAttribute("data-i18n-title", "view gclass");
|
|
14538
|
+
$gclass.setAttribute("aria-label", t("view gclass"));
|
|
14539
|
+
$gclass.setAttribute("data-i18n-aria-label", "view gclass");
|
|
14540
|
+
$gclass.addEventListener("click", (evt) => {
|
|
14541
|
+
evt.stopPropagation();
|
|
14542
|
+
gobj_send_event(gobj, "EV_OPEN_GCLASS", {}, gobj);
|
|
14543
|
+
});
|
|
14544
|
+
$header.appendChild($gclass);
|
|
14545
|
+
priv.$popover_gclass = $gclass;
|
|
14546
|
+
} else log_warning(`${gobj_short_name(gobj)}: no C_YUI_JSON registered, the gclass viewer is not offered`);
|
|
13851
14547
|
let $close = document.createElement("button");
|
|
13852
14548
|
$close.type = "button";
|
|
13853
14549
|
$close.className = "GOBJ_TREE_POPOVER_CLOSE";
|
|
@@ -13955,6 +14651,26 @@ function make_toolbar$1(gobj) {
|
|
|
13955
14651
|
return $toolbar;
|
|
13956
14652
|
}
|
|
13957
14653
|
/************************************************************
|
|
14654
|
+
* The element G6 mounts its canvas in.
|
|
14655
|
+
*
|
|
14656
|
+
* Read from THIS gclass's own $container, never with
|
|
14657
|
+
* `document.getElementById()`: a view is not always in the
|
|
14658
|
+
* document when it is started -- a host that builds the
|
|
14659
|
+
* viewer and mounts it into a window afterwards starts it
|
|
14660
|
+
* DETACHED -- and a global lookup answers null there. It cost
|
|
14661
|
+
* a resize that never worked: the ResizeObserver below was
|
|
14662
|
+
* simply never attached, silently, and the canvas kept the
|
|
14663
|
+
* size it was born with for the life of the window.
|
|
14664
|
+
*
|
|
14665
|
+
* Null only if the UI was never built; that is a bug, and it
|
|
14666
|
+
* is logged where it matters rather than skipped.
|
|
14667
|
+
************************************************************/
|
|
14668
|
+
function canvas_mount(gobj) {
|
|
14669
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
14670
|
+
if (!$container) return null;
|
|
14671
|
+
return $container.querySelector("#" + gobj.priv.canvas_id);
|
|
14672
|
+
}
|
|
14673
|
+
/************************************************************
|
|
13958
14674
|
* Build a G6 graph instance
|
|
13959
14675
|
************************************************************/
|
|
13960
14676
|
function build_graph$2(gobj) {
|
|
@@ -13998,8 +14714,9 @@ function build_graph$2(gobj) {
|
|
|
13998
14714
|
hide_popover(gobj);
|
|
13999
14715
|
});
|
|
14000
14716
|
if (typeof ResizeObserver !== "undefined") {
|
|
14001
|
-
let $canvas =
|
|
14002
|
-
if (
|
|
14717
|
+
let $canvas = canvas_mount(gobj);
|
|
14718
|
+
if (!$canvas) log_error(`${gobj_short_name(gobj)}: no canvas mount, the graph will never resize`);
|
|
14719
|
+
else {
|
|
14003
14720
|
let ro = new ResizeObserver(() => {
|
|
14004
14721
|
if (priv._resize_raf) cancelAnimationFrame(priv._resize_raf);
|
|
14005
14722
|
priv._resize_raf = requestAnimationFrame(() => {
|
|
@@ -14032,22 +14749,20 @@ function apply_layout(gobj) {
|
|
|
14032
14749
|
});
|
|
14033
14750
|
}
|
|
14034
14751
|
/************************************************************
|
|
14035
|
-
*
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14040
|
-
if (gobj_is_service(target_gobj)) return ROLE_COLORS.service;
|
|
14041
|
-
if (gobj_is_pure_child(target_gobj)) return ROLE_COLORS.pure;
|
|
14042
|
-
return ROLE_COLORS.default;
|
|
14043
|
-
}
|
|
14044
|
-
/************************************************************
|
|
14045
|
-
* Resolve state color
|
|
14752
|
+
* The role colours of a node.
|
|
14753
|
+
*
|
|
14754
|
+
* Note the ORDER: volatil before service. A volatil gobj that
|
|
14755
|
+
* is also a service is drawn volatil, because that is the fact
|
|
14756
|
+
* that decides how long it lives.
|
|
14046
14757
|
************************************************************/
|
|
14047
|
-
function
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
14758
|
+
function get_role_colors(d) {
|
|
14759
|
+
switch (node_role(d)) {
|
|
14760
|
+
case "yuno": return ROLE_COLORS.yuno;
|
|
14761
|
+
case "volatil": return ROLE_COLORS.volatil;
|
|
14762
|
+
case "service": return ROLE_COLORS.service;
|
|
14763
|
+
case "pure": return ROLE_COLORS.pure;
|
|
14764
|
+
default: return ROLE_COLORS.child;
|
|
14765
|
+
}
|
|
14051
14766
|
}
|
|
14052
14767
|
/************************************************************
|
|
14053
14768
|
* Generate a unique node id
|
|
@@ -14079,72 +14794,135 @@ function is_node_collapsed(gobj, full_name, num_children) {
|
|
|
14079
14794
|
************************************************************/
|
|
14080
14795
|
function render_toggle_html(node_id, collapsed, num_children, cs) {
|
|
14081
14796
|
let label = collapsed ? "+" + num_children : "−";
|
|
14797
|
+
let title = collapsed ? t("expand children") : t("collapse children");
|
|
14082
14798
|
return `<span
|
|
14083
14799
|
data-gobj-toggle="true"
|
|
14084
14800
|
data-node-id="${node_id}"
|
|
14085
|
-
title="${
|
|
14801
|
+
title="${escapeHtml(title)}"
|
|
14086
14802
|
style="
|
|
14087
14803
|
flex: 0 0 auto;
|
|
14088
14804
|
display: inline-flex;
|
|
14089
14805
|
align-items: center;
|
|
14090
14806
|
justify-content: center;
|
|
14091
|
-
min-width:
|
|
14092
|
-
height:
|
|
14093
|
-
padding: 0
|
|
14807
|
+
min-width: 24px;
|
|
14808
|
+
height: 18px;
|
|
14809
|
+
padding: 0 6px;
|
|
14094
14810
|
margin-left: 4px;
|
|
14095
|
-
background: ${cs.
|
|
14096
|
-
color:
|
|
14097
|
-
border: 1px solid ${cs.
|
|
14098
|
-
border-radius:
|
|
14811
|
+
background: ${cs.accent};
|
|
14812
|
+
color: #ffffff;
|
|
14813
|
+
border: 1px solid ${cs.accent};
|
|
14814
|
+
border-radius: 9px;
|
|
14099
14815
|
font-size: 10px;
|
|
14100
|
-
font-weight:
|
|
14816
|
+
font-weight: 700;
|
|
14101
14817
|
line-height: 1;
|
|
14102
14818
|
cursor: pointer;
|
|
14103
14819
|
user-select: none;
|
|
14104
14820
|
">${label}</span>`;
|
|
14105
14821
|
}
|
|
14106
14822
|
/************************************************************
|
|
14107
|
-
*
|
|
14823
|
+
* HTML for the status pill: what the gobj is DOING.
|
|
14824
|
+
*
|
|
14825
|
+
* A dot plus a word, and the word is dropped in the compact
|
|
14826
|
+
* card where there is no room for it -- the colour still
|
|
14827
|
+
* carries it, and the `title` says it in full either way.
|
|
14108
14828
|
************************************************************/
|
|
14109
|
-
function
|
|
14829
|
+
function render_status_html(d, ss, labels, with_label) {
|
|
14830
|
+
let full = d.disabled ? labels.field.disabled : labels.status[node_status_key(d)] || node_status_key(d);
|
|
14831
|
+
let label = d.disabled ? labels.field.disabled : labels.status_short[node_status(d)] || node_status(d);
|
|
14832
|
+
if (!with_label) return `<span title="${escapeHtml(full)}" style="
|
|
14833
|
+
flex: 0 0 auto;
|
|
14834
|
+
width: 10px; height: 10px; border-radius: 50%;
|
|
14835
|
+
background: ${ss.dot};
|
|
14836
|
+
box-shadow: 0 0 0 2px ${ss.bg};
|
|
14837
|
+
margin-left: 6px;
|
|
14838
|
+
"></span>`;
|
|
14839
|
+
return `<span title="${escapeHtml(full)}" style="
|
|
14840
|
+
flex: 0 0 auto;
|
|
14841
|
+
display: inline-flex;
|
|
14842
|
+
align-items: center;
|
|
14843
|
+
gap: 4px;
|
|
14844
|
+
padding: 1px 7px 1px 5px;
|
|
14845
|
+
background: ${ss.bg};
|
|
14846
|
+
color: ${ss.fg};
|
|
14847
|
+
border-radius: 9px;
|
|
14848
|
+
font-size: 10px;
|
|
14849
|
+
font-weight: 700;
|
|
14850
|
+
line-height: 1.45;
|
|
14851
|
+
white-space: nowrap;
|
|
14852
|
+
"><span style="width:7px; height:7px; border-radius:50%; background:${ss.dot};"></span>${escapeHtml(label)}</span>`;
|
|
14853
|
+
}
|
|
14854
|
+
/************************************************************
|
|
14855
|
+
* HTML for the badges: what the gobj IS, besides its colour.
|
|
14856
|
+
************************************************************/
|
|
14857
|
+
function render_badges_html(d, cs, labels) {
|
|
14858
|
+
let html = "";
|
|
14859
|
+
for (let key of node_badge_keys(d)) {
|
|
14860
|
+
let label = labels.badge[key] || key;
|
|
14861
|
+
html += `<span style="
|
|
14862
|
+
font-size: 10px;
|
|
14863
|
+
font-weight: 600;
|
|
14864
|
+
padding: 0 6px;
|
|
14865
|
+
margin-right: 4px;
|
|
14866
|
+
background: ${cs.tagbg};
|
|
14867
|
+
color: ${cs.tagfg};
|
|
14868
|
+
border: 1px solid ${cs.accent};
|
|
14869
|
+
border-radius: 9px;
|
|
14870
|
+
white-space: nowrap;
|
|
14871
|
+
">${escapeHtml(label)}</span>`;
|
|
14872
|
+
}
|
|
14873
|
+
return html;
|
|
14874
|
+
}
|
|
14875
|
+
/************************************************************
|
|
14876
|
+
* Recursively build nodes and edges from a NODE DESCRIPTOR
|
|
14877
|
+
* tree (gobj_tree_model.js), never from gobjs: the same
|
|
14878
|
+
* builder draws the browser yuno and a backend one.
|
|
14879
|
+
*
|
|
14880
|
+
* The descriptor is also what the popover reads, so the view
|
|
14881
|
+
* fields it needs are written onto it here (`full_name`,
|
|
14882
|
+
* `short_name`, `num_children`, `is_collapsed`,
|
|
14883
|
+
* `direct_children`) instead of copied into a second object
|
|
14884
|
+
* that can then disagree with the first.
|
|
14885
|
+
************************************************************/
|
|
14886
|
+
function build_gobj_nodes(gobj, d, nodes, edges, parent_id, compact, labels) {
|
|
14110
14887
|
let priv = gobj.priv;
|
|
14111
14888
|
let node_id = gen_node_id(gobj);
|
|
14112
|
-
let gclass_name = gobj_gclass_name(target_gobj) || "";
|
|
14113
|
-
let instance_name = gobj_name(target_gobj) || "";
|
|
14114
|
-
let state = gobj_current_state(target_gobj) || "";
|
|
14115
|
-
let full_name = gobj_full_name(target_gobj);
|
|
14116
|
-
let colors = get_role_colors(target_gobj, is_root);
|
|
14117
14889
|
let dark = yui_is_dark();
|
|
14118
|
-
let
|
|
14119
|
-
let
|
|
14120
|
-
let
|
|
14121
|
-
let
|
|
14122
|
-
let status_text = !running ? "stopped" : playing ? "playing" : "running";
|
|
14123
|
-
let category = get_node_category(target_gobj, is_root);
|
|
14890
|
+
let colors = get_role_colors(d);
|
|
14891
|
+
let cs = role_card_style(colors, dark);
|
|
14892
|
+
let ss = status_card_style(d, dark);
|
|
14893
|
+
let category = get_node_category(d);
|
|
14124
14894
|
let size = NODE_SIZES[compact ? "compact" : "full"][category];
|
|
14125
14895
|
let width = size.w;
|
|
14126
14896
|
let height = size.h;
|
|
14127
14897
|
let border_width = category === "yuno" || category === "service" ? 2 : 1;
|
|
14128
|
-
let
|
|
14898
|
+
let border_style = d.disabled ? "dashed" : "solid";
|
|
14899
|
+
let children = d.children || [];
|
|
14129
14900
|
let num_children = children.length;
|
|
14130
14901
|
let has_children = num_children > 0;
|
|
14131
|
-
let collapsed = has_children && is_node_collapsed(gobj,
|
|
14902
|
+
let collapsed = has_children && is_node_collapsed(gobj, d.fullname, num_children);
|
|
14132
14903
|
let direct_children = [];
|
|
14133
14904
|
for (let child of children) direct_children.push({
|
|
14134
|
-
full_name:
|
|
14135
|
-
has_grandchildren: (child.
|
|
14905
|
+
full_name: child.fullname,
|
|
14906
|
+
has_grandchildren: (child.children || []).length > 0
|
|
14136
14907
|
});
|
|
14908
|
+
d.full_name = d.fullname;
|
|
14909
|
+
d.short_name = d.shortname;
|
|
14910
|
+
d.num_children = num_children;
|
|
14911
|
+
d.is_collapsed = collapsed;
|
|
14912
|
+
d.direct_children = direct_children;
|
|
14913
|
+
d.node_id = node_id;
|
|
14137
14914
|
let toggle_html = has_children ? render_toggle_html(node_id, collapsed, num_children, cs) : "";
|
|
14915
|
+
let card_title = d.gclass + (d.name ? "^" + d.name : "");
|
|
14138
14916
|
let node_html;
|
|
14139
14917
|
if (compact) {
|
|
14140
|
-
let compact_label = escapeHtml(
|
|
14918
|
+
let compact_label = escapeHtml(card_title);
|
|
14141
14919
|
node_html = `
|
|
14142
|
-
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(
|
|
14920
|
+
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(d.fullname)}" style="
|
|
14143
14921
|
width: ${width}px;
|
|
14144
14922
|
height: ${height}px;
|
|
14145
14923
|
background: ${cs.bg};
|
|
14146
|
-
border: ${border_width}px
|
|
14147
|
-
border-left:
|
|
14924
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14925
|
+
border-left: 6px solid ${cs.accent};
|
|
14148
14926
|
border-radius: 7px;
|
|
14149
14927
|
box-shadow: ${cs.shadow};
|
|
14150
14928
|
font-family: ${GT_FONT};
|
|
@@ -14153,34 +14931,24 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14153
14931
|
padding: 0 8px;
|
|
14154
14932
|
box-sizing: border-box;
|
|
14155
14933
|
overflow: hidden;
|
|
14934
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
14156
14935
|
cursor: pointer;
|
|
14157
14936
|
" title="${compact_label}">
|
|
14158
14937
|
<span style="flex:1 1 auto; min-width:0; font-size:11px; font-weight:600; color:${cs.title}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${compact_label}</span>
|
|
14159
|
-
|
|
14160
|
-
flex: 0 0 auto;
|
|
14161
|
-
width: 8px; height: 8px; border-radius: 50%;
|
|
14162
|
-
background: ${state_color}; margin-left: 6px;
|
|
14163
|
-
"></span>
|
|
14938
|
+
${render_status_html(d, ss, labels, false)}
|
|
14164
14939
|
${toggle_html}
|
|
14165
14940
|
</div>`;
|
|
14166
14941
|
} else {
|
|
14167
|
-
let header_label = escapeHtml(
|
|
14168
|
-
let name_label = escapeHtml(
|
|
14169
|
-
let state_label = escapeHtml(state);
|
|
14170
|
-
let badges = [];
|
|
14171
|
-
if (is_root) badges.push("yuno");
|
|
14172
|
-
else {
|
|
14173
|
-
if (gobj_is_service(target_gobj)) badges.push("service");
|
|
14174
|
-
if (gobj_is_pure_child(target_gobj)) badges.push("pure");
|
|
14175
|
-
if (gobj_is_volatil(target_gobj)) badges.push("volatil");
|
|
14176
|
-
}
|
|
14177
|
-
let badges_html = badges.map((b) => `<span style="font-size:10px; padding:0 5px; margin-right:4px; background:${cs.tagbg}; color:${cs.border}; border:1px solid ${cs.border}; border-radius:6px; white-space:nowrap;">${b}</span>`).join("");
|
|
14942
|
+
let header_label = escapeHtml(d.gclass);
|
|
14943
|
+
let name_label = escapeHtml(d.name);
|
|
14944
|
+
let state_label = escapeHtml(d.state);
|
|
14178
14945
|
node_html = `
|
|
14179
|
-
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(
|
|
14946
|
+
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(d.fullname)}" style="
|
|
14180
14947
|
width: ${width}px;
|
|
14181
14948
|
height: ${height}px;
|
|
14182
14949
|
background: ${cs.bg};
|
|
14183
|
-
border: ${border_width}px
|
|
14950
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14951
|
+
border-left: 6px solid ${cs.accent};
|
|
14184
14952
|
border-radius: 9px;
|
|
14185
14953
|
box-shadow: ${cs.shadow};
|
|
14186
14954
|
overflow: hidden;
|
|
@@ -14189,48 +14957,29 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14189
14957
|
display: flex;
|
|
14190
14958
|
flex-direction: column;
|
|
14191
14959
|
justify-content: center;
|
|
14192
|
-
padding:
|
|
14193
|
-
gap:
|
|
14960
|
+
padding: 6px 9px;
|
|
14961
|
+
gap: 2px;
|
|
14962
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
14194
14963
|
cursor: pointer;
|
|
14195
|
-
" title="${escapeHtml(
|
|
14964
|
+
" title="${escapeHtml(card_title)}">
|
|
14196
14965
|
<div style="flex:0 0 auto; display:flex; align-items:center; gap:6px; overflow:hidden;">
|
|
14197
|
-
<span style="flex:1 1 auto; min-width:0; font-size:12px; line-height:1.25; font-weight:
|
|
14966
|
+
<span style="flex:1 1 auto; min-width:0; font-size:12px; line-height:1.25; font-weight:700; color:${cs.title}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${header_label}</span>
|
|
14198
14967
|
${toggle_html}
|
|
14199
14968
|
</div>
|
|
14200
14969
|
<div style="flex:0 0 auto; font-size:12px; line-height:1.25; color:${cs.sub}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${name_label || " "}</div>
|
|
14201
14970
|
<div style="flex:0 0 auto; display:flex; justify-content:space-between; align-items:center; gap:6px; overflow:hidden;">
|
|
14202
|
-
<span style="flex:1 1 auto; min-width:0;
|
|
14203
|
-
|
|
14204
|
-
<span style="width:7px; height:7px; border-radius:50%; background:${state_color};"></span>${state_label || status_text}
|
|
14205
|
-
</span>
|
|
14971
|
+
<span style="flex:1 1 auto; min-width:0; font-size:10px; line-height:1.25; color:${cs.sub}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${state_label || " "}</span>
|
|
14972
|
+
${render_status_html(d, ss, labels, true)}
|
|
14206
14973
|
</div>
|
|
14974
|
+
<div style="flex:0 0 auto; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;">${render_badges_html(d, cs, labels)}</div>
|
|
14207
14975
|
</div>`;
|
|
14208
14976
|
}
|
|
14209
|
-
let parent_gobj = gobj_parent(target_gobj);
|
|
14210
|
-
let node_data = {
|
|
14211
|
-
gclass: gclass_name,
|
|
14212
|
-
name: instance_name,
|
|
14213
|
-
short_name: gobj_short_name(target_gobj),
|
|
14214
|
-
full_name,
|
|
14215
|
-
parent_short_name: parent_gobj ? gobj_short_name(parent_gobj) : "",
|
|
14216
|
-
state,
|
|
14217
|
-
category,
|
|
14218
|
-
is_yuno: is_root,
|
|
14219
|
-
is_service: gobj_is_service(target_gobj),
|
|
14220
|
-
is_pure_child: gobj_is_pure_child(target_gobj),
|
|
14221
|
-
is_volatil: gobj_is_volatil(target_gobj),
|
|
14222
|
-
is_running: running,
|
|
14223
|
-
is_playing: playing,
|
|
14224
|
-
num_children,
|
|
14225
|
-
is_collapsed: collapsed,
|
|
14226
|
-
direct_children
|
|
14227
|
-
};
|
|
14228
14977
|
if (!priv.node_by_id) priv.node_by_id = {};
|
|
14229
|
-
priv.node_by_id[node_id] =
|
|
14978
|
+
priv.node_by_id[node_id] = d;
|
|
14230
14979
|
let node = {
|
|
14231
14980
|
id: node_id,
|
|
14232
14981
|
type: "html",
|
|
14233
|
-
data:
|
|
14982
|
+
data: d,
|
|
14234
14983
|
style: {
|
|
14235
14984
|
innerHTML: node_html,
|
|
14236
14985
|
size: [width, height],
|
|
@@ -14243,11 +14992,11 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14243
14992
|
source: parent_id,
|
|
14244
14993
|
target: node_id,
|
|
14245
14994
|
style: {
|
|
14246
|
-
stroke: colors.
|
|
14247
|
-
lineWidth: 1
|
|
14995
|
+
stroke: colors.accent,
|
|
14996
|
+
lineWidth: 1.4
|
|
14248
14997
|
}
|
|
14249
14998
|
});
|
|
14250
|
-
if (!collapsed) for (let child of children) build_gobj_nodes(gobj, child, nodes, edges, node_id,
|
|
14999
|
+
if (!collapsed) for (let child of children) build_gobj_nodes(gobj, child, nodes, edges, node_id, compact, labels);
|
|
14251
15000
|
return node_id;
|
|
14252
15001
|
}
|
|
14253
15002
|
/************************************************************
|
|
@@ -14264,9 +15013,13 @@ function load_tree(gobj) {
|
|
|
14264
15013
|
let priv = gobj.priv;
|
|
14265
15014
|
let graph = priv.graph;
|
|
14266
15015
|
if (!graph) return;
|
|
14267
|
-
let
|
|
14268
|
-
if (!
|
|
15016
|
+
let root = describe_js_gobj(gobj_yuno(), true);
|
|
15017
|
+
if (!root) {
|
|
15018
|
+
log_error(`${gobj_short_name(gobj)}: no yuno to draw`);
|
|
15019
|
+
return;
|
|
15020
|
+
}
|
|
14269
15021
|
let layout_cfg = get_layout_cfg(gobj_read_str_attr(gobj, "layout"));
|
|
15022
|
+
let labels = node_labels(t);
|
|
14270
15023
|
let anchor = priv.pending_anchor || null;
|
|
14271
15024
|
let preserve_view = !!priv.pending_preserve_view;
|
|
14272
15025
|
priv.pending_anchor = null;
|
|
@@ -14275,7 +15028,7 @@ function load_tree(gobj) {
|
|
|
14275
15028
|
priv.node_by_id = {};
|
|
14276
15029
|
let nodes = [];
|
|
14277
15030
|
let edges = [];
|
|
14278
|
-
build_gobj_nodes(gobj,
|
|
15031
|
+
build_gobj_nodes(gobj, root, nodes, edges, null, layout_cfg.compact, labels);
|
|
14279
15032
|
if (nodes.length > 0) {
|
|
14280
15033
|
let root_id = nodes[0].id;
|
|
14281
15034
|
graph.setData({
|
|
@@ -14376,46 +15129,17 @@ function refresh_tree(gobj, opts) {
|
|
|
14376
15129
|
}
|
|
14377
15130
|
}
|
|
14378
15131
|
/************************************************************
|
|
14379
|
-
* Rows to show in the popover for a given node data.
|
|
14380
|
-
*
|
|
14381
|
-
* This is the extension point: to surface a new attribute,
|
|
14382
|
-
* collect it into `node_data` in build_gobj_nodes(), then
|
|
14383
|
-
* append a `[label, value]` pair here.
|
|
14384
|
-
************************************************************/
|
|
14385
|
-
function get_node_info_rows(node_data) {
|
|
14386
|
-
let rows = [];
|
|
14387
|
-
rows.push([t("gclass"), node_data.gclass || "—"]);
|
|
14388
|
-
rows.push([t("name"), node_data.name || "—"]);
|
|
14389
|
-
if (node_data.full_name && node_data.full_name !== node_data.short_name) rows.push([t("full name"), node_data.full_name]);
|
|
14390
|
-
let role;
|
|
14391
|
-
if (node_data.is_yuno) role = "yuno";
|
|
14392
|
-
else if (node_data.is_service) role = "service";
|
|
14393
|
-
else if (node_data.is_volatil) role = "volatil child";
|
|
14394
|
-
else if (node_data.is_pure_child) role = "pure child";
|
|
14395
|
-
else role = "child";
|
|
14396
|
-
rows.push([t("role"), role]);
|
|
14397
|
-
let status;
|
|
14398
|
-
if (!node_data.is_running) status = "stopped";
|
|
14399
|
-
else if (node_data.is_playing) status = "running + playing";
|
|
14400
|
-
else status = "running";
|
|
14401
|
-
rows.push([t("status"), status]);
|
|
14402
|
-
rows.push([t("state"), node_data.state || "—"]);
|
|
14403
|
-
if (node_data.parent_short_name) rows.push([t("parent"), node_data.parent_short_name]);
|
|
14404
|
-
if (typeof node_data.num_children === "number") {
|
|
14405
|
-
let kids = node_data.num_children;
|
|
14406
|
-
if (kids > 0 && node_data.is_collapsed) rows.push([t("children"), kids + " " + t("(collapsed)")]);
|
|
14407
|
-
else if (kids > 0) rows.push([t("children"), String(kids)]);
|
|
14408
|
-
}
|
|
14409
|
-
return rows;
|
|
14410
|
-
}
|
|
14411
|
-
/************************************************************
|
|
14412
15132
|
* Render popover body from the info rows of a node.
|
|
15133
|
+
*
|
|
15134
|
+
* The rows come from gobj_tree_model.js, so a node of the
|
|
15135
|
+
* browser yuno and a node of a backend one are read out with
|
|
15136
|
+
* the same words in the same order.
|
|
14413
15137
|
************************************************************/
|
|
14414
15138
|
function render_popover_body(gobj, node_data) {
|
|
14415
15139
|
let $body = gobj.priv.$popover_body;
|
|
14416
15140
|
if (!$body) return;
|
|
14417
|
-
$body.innerHTML =
|
|
14418
|
-
<div style="display:grid; grid-template-columns:
|
|
15141
|
+
$body.innerHTML = node_info_rows(node_data, t).map(([label, value]) => `
|
|
15142
|
+
<div style="display:grid; grid-template-columns: 96px 1fr; column-gap:10px; padding: 2px 0;">
|
|
14419
15143
|
<span style="color:var(--bulma-text-weak, #6B7280); font-weight:500;">${escapeHtml(String(label))}</span>
|
|
14420
15144
|
<span style="color:var(--bulma-text-strong, #1A1A1A); word-break:break-all;">${escapeHtml(String(value))}</span>
|
|
14421
15145
|
</div>
|
|
@@ -14432,6 +15156,7 @@ function show_popover(gobj, node_data, client_x, client_y) {
|
|
|
14432
15156
|
let $title = priv.$popover_title;
|
|
14433
15157
|
let $container = gobj_read_attr(gobj, "$container");
|
|
14434
15158
|
if (!$popover || !$title || !$container) return;
|
|
15159
|
+
priv.popover_node = node_data;
|
|
14435
15160
|
$title.textContent = node_data.short_name || node_data.gclass + (node_data.name ? "^" + node_data.name : "");
|
|
14436
15161
|
$title.title = $title.textContent;
|
|
14437
15162
|
render_popover_body(gobj, node_data);
|
|
@@ -14462,6 +15187,7 @@ function hide_popover(gobj) {
|
|
|
14462
15187
|
if (priv.$popover) priv.$popover.style.display = "none";
|
|
14463
15188
|
if (priv.$popover_body) priv.$popover_body.innerHTML = "";
|
|
14464
15189
|
if (priv.$popover_title) priv.$popover_title.textContent = "";
|
|
15190
|
+
priv.popover_node = null;
|
|
14465
15191
|
}
|
|
14466
15192
|
/************************************************************
|
|
14467
15193
|
* Generic tree layout engine.
|
|
@@ -14988,12 +15714,11 @@ function ac_node_click$3(gobj, event, kw, src) {
|
|
|
14988
15714
|
*
|
|
14989
15715
|
************************************************************/
|
|
14990
15716
|
function ac_show$6(gobj, event, kw, src) {
|
|
14991
|
-
let
|
|
14992
|
-
let graph = priv.graph;
|
|
15717
|
+
let graph = gobj.priv.graph;
|
|
14993
15718
|
save_view_state(gobj);
|
|
14994
15719
|
refresh_tree(gobj);
|
|
14995
15720
|
if (graph) {
|
|
14996
|
-
let $canvas =
|
|
15721
|
+
let $canvas = canvas_mount(gobj);
|
|
14997
15722
|
if ($canvas) {
|
|
14998
15723
|
let cw = $canvas.clientWidth;
|
|
14999
15724
|
let ch = $canvas.clientHeight;
|
|
@@ -15012,10 +15737,9 @@ function ac_show$6(gobj, event, kw, src) {
|
|
|
15012
15737
|
* No fitView: preserve the user's current pan/zoom.
|
|
15013
15738
|
************************************************************/
|
|
15014
15739
|
function ac_resize$1(gobj, event, kw, src) {
|
|
15015
|
-
let
|
|
15016
|
-
let graph = priv.graph;
|
|
15740
|
+
let graph = gobj.priv.graph;
|
|
15017
15741
|
if (graph) {
|
|
15018
|
-
let $canvas =
|
|
15742
|
+
let $canvas = canvas_mount(gobj);
|
|
15019
15743
|
if ($canvas) {
|
|
15020
15744
|
let cw = $canvas.clientWidth;
|
|
15021
15745
|
let ch = $canvas.clientHeight;
|
|
@@ -15031,6 +15755,68 @@ function ac_hide$5(gobj, event, kw, src) {
|
|
|
15031
15755
|
hide_popover(gobj);
|
|
15032
15756
|
return 0;
|
|
15033
15757
|
}
|
|
15758
|
+
/************************************************************
|
|
15759
|
+
* Open the gclass of the node the popover is showing.
|
|
15760
|
+
*
|
|
15761
|
+
* One window at a time: opening a second gclass replaces the
|
|
15762
|
+
* first rather than stacking windows nobody closes.
|
|
15763
|
+
************************************************************/
|
|
15764
|
+
function ac_open_gclass(gobj, event, kw, src) {
|
|
15765
|
+
let priv = gobj.priv;
|
|
15766
|
+
let node_data = priv.popover_node;
|
|
15767
|
+
if (!node_data || !node_data.gclass) {
|
|
15768
|
+
log_error(`${gobj_short_name(gobj)}: EV_OPEN_GCLASS with no node`);
|
|
15769
|
+
return -1;
|
|
15770
|
+
}
|
|
15771
|
+
close_gclass_view(priv.gclass_view);
|
|
15772
|
+
priv.gclass_view = null;
|
|
15773
|
+
priv.gclass_view = open_gclass_view(gobj, node_data.gclass, {
|
|
15774
|
+
title_prefix: gobj_yuno_name() || "",
|
|
15775
|
+
on_close: () => {
|
|
15776
|
+
if (gobj_is_destroying(gobj)) return;
|
|
15777
|
+
gobj_send_event(gobj, "EV_GCLASS_CLOSED", {}, gobj);
|
|
15778
|
+
}
|
|
15779
|
+
});
|
|
15780
|
+
return 0;
|
|
15781
|
+
}
|
|
15782
|
+
/************************************************************
|
|
15783
|
+
* The reader dismissed the gclass window (its own ✕).
|
|
15784
|
+
*
|
|
15785
|
+
* C_YUI_WINDOW.close_window() calls on_close and THEN destroys
|
|
15786
|
+
* itself, so here we only drop our reference -- destroying it
|
|
15787
|
+
* again would destroy it twice.
|
|
15788
|
+
************************************************************/
|
|
15789
|
+
function ac_gclass_closed(gobj, event, kw, src) {
|
|
15790
|
+
gobj.priv.gclass_view = null;
|
|
15791
|
+
return 0;
|
|
15792
|
+
}
|
|
15793
|
+
/************************************************************
|
|
15794
|
+
* The gclass viewer asked for a subtree.
|
|
15795
|
+
*
|
|
15796
|
+
* It cannot: the gclass description is a COMPLETE document,
|
|
15797
|
+
* it carries no `__collapsed__` stub, so nothing in it is
|
|
15798
|
+
* fetchable. The event is declared because the viewer is
|
|
15799
|
+
* hosted as a child and subscribes its host to everything it
|
|
15800
|
+
* publishes -- and it is logged, because arriving here means
|
|
15801
|
+
* the document was not the one we built.
|
|
15802
|
+
************************************************************/
|
|
15803
|
+
function ac_gclass_expand_path(gobj, event, kw, src) {
|
|
15804
|
+
log_error(`${gobj_short_name(gobj)}: EV_EXPAND_PATH on a complete document: ${kw && kw.path}`);
|
|
15805
|
+
return -1;
|
|
15806
|
+
}
|
|
15807
|
+
/************************************************************
|
|
15808
|
+
* The app switched language.
|
|
15809
|
+
*
|
|
15810
|
+
* A rebuild, not a re-translation: the words live inside the
|
|
15811
|
+
* innerHTML of G6 nodes, where refresh_language() cannot go.
|
|
15812
|
+
************************************************************/
|
|
15813
|
+
function ac_language_changed$3(gobj, event, kw, src) {
|
|
15814
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
15815
|
+
if ($container) refresh_language($container, t);
|
|
15816
|
+
hide_popover(gobj);
|
|
15817
|
+
refresh_tree(gobj);
|
|
15818
|
+
return 0;
|
|
15819
|
+
}
|
|
15034
15820
|
/***************************
|
|
15035
15821
|
* FSM
|
|
15036
15822
|
***************************/
|
|
@@ -15064,7 +15850,11 @@ function create_gclass$9(gclass_name) {
|
|
|
15064
15850
|
["EV_CENTER_ANCHOR", 0],
|
|
15065
15851
|
["EV_RESIZE", 0],
|
|
15066
15852
|
["EV_SHOW", 0],
|
|
15067
|
-
["EV_HIDE", 0]
|
|
15853
|
+
["EV_HIDE", 0],
|
|
15854
|
+
["EV_OPEN_GCLASS", 0],
|
|
15855
|
+
["EV_GCLASS_CLOSED", 0],
|
|
15856
|
+
["EV_EXPAND_PATH", 0],
|
|
15857
|
+
["EV_LANGUAGE_CHANGED", 0]
|
|
15068
15858
|
], [["ST_IDLE", [
|
|
15069
15859
|
[
|
|
15070
15860
|
"EV_REFRESH",
|
|
@@ -15145,6 +15935,26 @@ function create_gclass$9(gclass_name) {
|
|
|
15145
15935
|
"EV_HIDE",
|
|
15146
15936
|
ac_hide$5,
|
|
15147
15937
|
null
|
|
15938
|
+
],
|
|
15939
|
+
[
|
|
15940
|
+
"EV_OPEN_GCLASS",
|
|
15941
|
+
ac_open_gclass,
|
|
15942
|
+
null
|
|
15943
|
+
],
|
|
15944
|
+
[
|
|
15945
|
+
"EV_GCLASS_CLOSED",
|
|
15946
|
+
ac_gclass_closed,
|
|
15947
|
+
null
|
|
15948
|
+
],
|
|
15949
|
+
[
|
|
15950
|
+
"EV_EXPAND_PATH",
|
|
15951
|
+
ac_gclass_expand_path,
|
|
15952
|
+
null
|
|
15953
|
+
],
|
|
15954
|
+
[
|
|
15955
|
+
"EV_LANGUAGE_CHANGED",
|
|
15956
|
+
ac_language_changed$3,
|
|
15957
|
+
null
|
|
15148
15958
|
]
|
|
15149
15959
|
]]], gmt$9, 0, attrs_table$9, PRIVATE_DATA$9, 0, 0, 0, 0);
|
|
15150
15960
|
if (!__gclass__$9) return -1;
|
|
@@ -54590,7 +55400,6 @@ function open_json_viewer(gobj) {
|
|
|
54590
55400
|
return;
|
|
54591
55401
|
}
|
|
54592
55402
|
priv.json_gobj = jv;
|
|
54593
|
-
gobj_start(jv);
|
|
54594
55403
|
let $box = gobj_read_pointer_attr(jv, "$container");
|
|
54595
55404
|
if (mobile) {
|
|
54596
55405
|
if (!shell) {
|
|
@@ -54609,6 +55418,7 @@ function open_json_viewer(gobj) {
|
|
|
54609
55418
|
gobj_send_event(gobj, "EV_JSON_CLOSED", {}, gobj);
|
|
54610
55419
|
}
|
|
54611
55420
|
});
|
|
55421
|
+
gobj_start(jv);
|
|
54612
55422
|
} else {
|
|
54613
55423
|
let $win_parent = shell && yui_shell_popup_layer(shell) || typeof document !== "undefined" && document.getElementById("top-layer") || null;
|
|
54614
55424
|
priv.json_win = gobj_create_service(`treedb-jsonwin-${clean_name(gobj_name(gobj))}`, "C_YUI_WINDOW", {
|
|
@@ -54639,6 +55449,7 @@ function open_json_viewer(gobj) {
|
|
|
54639
55449
|
return;
|
|
54640
55450
|
}
|
|
54641
55451
|
gobj_start(priv.json_win);
|
|
55452
|
+
gobj_start(jv);
|
|
54642
55453
|
}
|
|
54643
55454
|
request_print_tranger(gobj, "");
|
|
54644
55455
|
}
|
|
@@ -54666,7 +55477,7 @@ function close_json_viewer(gobj) {
|
|
|
54666
55477
|
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
54667
55478
|
}
|
|
54668
55479
|
if (jv && is_gobj(jv)) try {
|
|
54669
|
-
gobj_stop(jv);
|
|
55480
|
+
if (gobj_is_running(jv)) gobj_stop(jv);
|
|
54670
55481
|
gobj_destroy(jv);
|
|
54671
55482
|
} catch (e) {
|
|
54672
55483
|
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|