@yuneta/gobj-ui 3.0.0 → 4.0.0
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 +324 -6
- package/dist/gobj-ui.cjs.js +10891 -5006
- package/dist/gobj-ui.es.js +10915 -5064
- package/index.js +45 -1
- package/package.json +4 -4
- package/src/c_g6_nodes_tree.js +128 -40
- package/src/c_yui_form.js +112 -53
- package/src/c_yui_gobj_tree_js.js +58 -36
- package/src/c_yui_json.css +139 -0
- package/src/c_yui_json.js +928 -0
- package/src/c_yui_json_graph.js +110 -20
- package/src/c_yui_map.js +18 -36
- package/src/c_yui_nav.js +6 -9
- package/src/c_yui_period.css +184 -0
- package/src/c_yui_period.js +1441 -0
- package/src/c_yui_shell.css +33 -0
- package/src/c_yui_shell.js +672 -111
- package/src/c_yui_treedb_graph.js +639 -37
- package/src/c_yui_treedb_schema.js +478 -0
- package/src/c_yui_treedb_topic_with_form.css +7 -42
- package/src/c_yui_treedb_topic_with_form.js +152 -103
- package/src/c_yui_treedb_topics.css +73 -0
- package/src/c_yui_treedb_topics.js +1070 -29
- package/src/c_yui_window.js +180 -97
- package/src/c_yui_window_manager.js +38 -4
- package/src/json_view_helpers.js +214 -0
- package/src/json_view_helpers.test.js +135 -0
- package/src/route_map_model.js +317 -0
- package/src/route_map_model.test.js +209 -0
- package/src/route_resolver.js +24 -1
- package/src/route_resolver.test.js +40 -1
- package/src/shell_modals.js +117 -39
- package/src/shell_route_map.css +225 -0
- package/src/shell_route_map.js +363 -0
- package/src/tabulator.css +67 -0
- package/src/yui_dev.js +130 -8
- package/src/yui_frontend_view.js +106 -0
- package/src/yui_icons.css +62 -0
- package/src/yui_inputs.css +8 -3
- package/src/yui_inputs.js +51 -8
- package/src/yui_tabulator_i18n.js +128 -0
- package/src/yui_theme.js +147 -0
- package/src/yui_time.js +666 -0
- package/src/yui_time.test.js +330 -0
- package/src/yui_toolbar.js +86 -43
|
@@ -57,6 +57,11 @@ import {
|
|
|
57
57
|
trace_json,
|
|
58
58
|
json_object_update_missing,
|
|
59
59
|
gobj_start,
|
|
60
|
+
gobj_stop,
|
|
61
|
+
gobj_is_running,
|
|
62
|
+
gobj_current_state,
|
|
63
|
+
is_gobj,
|
|
64
|
+
gobj_is_destroying,
|
|
60
65
|
log_info,
|
|
61
66
|
trace_msg,
|
|
62
67
|
refresh_language,
|
|
@@ -80,8 +85,8 @@ import {
|
|
|
80
85
|
set_submit_state,
|
|
81
86
|
set_active_state,
|
|
82
87
|
} from "./lib_graph.js";
|
|
83
|
-
import {yui_shell_show_error} from "./shell_modals.js";
|
|
84
|
-
import {yui_shell_of} from "./c_yui_shell.js";
|
|
88
|
+
import {yui_shell_show_error, yui_shell_show_modal, yui_shell_popup_layer} from "./shell_modals.js";
|
|
89
|
+
import {yui_shell_of, yui_shell_set_sub_routes} from "./c_yui_shell.js";
|
|
85
90
|
|
|
86
91
|
import {t} from "i18next";
|
|
87
92
|
|
|
@@ -110,6 +115,8 @@ SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to
|
|
|
110
115
|
SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
|
|
111
116
|
SDATA(data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
|
|
112
117
|
SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
118
|
+
SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "Optional hash route for a '← topics' button back to the topics grid (host-supplied; empty = no button)"),
|
|
119
|
+
SDATA(data_type_t.DTP_STRING, "base_route", 0, "", "This view's base route (host-supplied); used to declare its per-topic focus sub-routes to the site map (ROUTING.md contributor)."),
|
|
113
120
|
SDATA(data_type_t.DTP_DICT, "records", 0, "{}", "Data of topics"),
|
|
114
121
|
SDATA(data_type_t.DTP_LIST, "topics", 0, "[]", "List of topic objects"),
|
|
115
122
|
|
|
@@ -141,11 +148,15 @@ let PRIVATE_DATA = {
|
|
|
141
148
|
gobj_nodes_tree: null,
|
|
142
149
|
gobj_treedb_tables: null,
|
|
143
150
|
hook_data_viewer: null,
|
|
151
|
+
json_gobj: null, /* C_YUI_JSON viewer of the raw tranger (or null) */
|
|
152
|
+
json_win: null, /* C_YUI_WINDOW hosting it, desktop (or null) */
|
|
153
|
+
json_modal: null, /* shell modal hosting it, mobile (or null) */
|
|
144
154
|
with_treedb_tables: false,
|
|
145
155
|
canvas_id: null,
|
|
146
156
|
operation_mode: null,
|
|
147
157
|
layout: null,
|
|
148
158
|
_topics_subscribed: {},
|
|
159
|
+
_links_subscribed: false, /* EV_TREEDB_NODE_LINKED/UNLINKED (treedb-wide) */
|
|
149
160
|
|
|
150
161
|
is_pinhold_window: false, // inherited of v6, todo review
|
|
151
162
|
};
|
|
@@ -182,15 +193,6 @@ function mt_create(gobj)
|
|
|
182
193
|
log_error(`${gobj_name(gobj)} -> treedb_name not configured`);
|
|
183
194
|
}
|
|
184
195
|
|
|
185
|
-
/* Optional legacy integration: EV_RESIZE from C_YUI_MAIN
|
|
186
|
-
* (old shell). Under the new C_YUI_SHELL there is no
|
|
187
|
-
* __yui_main__ — look it up SILENTLY (no verbose) so its
|
|
188
|
-
* absence is not logged as an error every mount. */
|
|
189
|
-
let __yui_main__ = gobj_find_service("__yui_main__");
|
|
190
|
-
if(__yui_main__) {
|
|
191
|
-
gobj_subscribe_event(__yui_main__, "EV_RESIZE", {}, gobj);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
196
|
/*
|
|
195
197
|
* set canvas_id, before build_ui()
|
|
196
198
|
*/
|
|
@@ -284,6 +286,11 @@ function mt_start(gobj)
|
|
|
284
286
|
***************************************************************/
|
|
285
287
|
function mt_stop(gobj)
|
|
286
288
|
{
|
|
289
|
+
let shell = yui_shell_of(gobj);
|
|
290
|
+
if(shell) {
|
|
291
|
+
yui_shell_set_sub_routes(shell, gobj_read_str_attr(gobj, "base_route"), null);
|
|
292
|
+
}
|
|
293
|
+
close_json_viewer(gobj);
|
|
287
294
|
gobj_stop_children(gobj);
|
|
288
295
|
}
|
|
289
296
|
|
|
@@ -318,12 +325,17 @@ function build_ui(gobj)
|
|
|
318
325
|
let padding = gobj_read_attr(gobj, "padding");
|
|
319
326
|
let $toolbar = make_toolbar(gobj);
|
|
320
327
|
|
|
328
|
+
/* The back link is pinned in the row, BEFORE the toolbar, so the
|
|
329
|
+
* toolbar's horizontal scroll never carries it off-screen. */
|
|
330
|
+
let $back = make_back_to_topics(gobj);
|
|
331
|
+
let row_items = $back ? [$back, $toolbar] : [$toolbar];
|
|
332
|
+
|
|
321
333
|
let $container = createElement2(
|
|
322
334
|
// Don't use is-flex, don't work well with is-hidden
|
|
323
335
|
['div', {class: 'C_YUI_TREEDB_GRAPH', style: `height:100%; display:flex; flex-direction:column;`}, [
|
|
324
|
-
['div', {class: 'is-flex-grow-0 is-flex is-align-items-center
|
|
325
|
-
['div', {class: `is-flex-grow-1 ${padding}`, style: 'height:100%; min-height:0; overflow:hidden;'}, [
|
|
326
|
-
['div', {id: priv.canvas_id, class: `graph-container`, style: 'height:100%; min-height:0;border: 1px solid var(--bulma-border-weak);border-radius:0.2rem;'}, [
|
|
336
|
+
['div', {class: 'GRAPH_TOOLBAR_ROW is-flex-grow-0 is-flex is-align-items-center'}, row_items],
|
|
337
|
+
['div', {class: `GRAPH_BODY is-flex-grow-1 ${padding}`, style: 'height:100%; min-height:0; overflow:hidden;'}, [
|
|
338
|
+
['div', {id: priv.canvas_id, class: `GRAPH_CANVAS graph-container`, style: 'height:100%; min-height:0;border: 1px solid var(--bulma-border-weak);border-radius:0.2rem;'}, [
|
|
327
339
|
]]
|
|
328
340
|
]]
|
|
329
341
|
]]
|
|
@@ -331,9 +343,49 @@ function build_ui(gobj)
|
|
|
331
343
|
|
|
332
344
|
gobj_write_attr(gobj, "$container", $container);
|
|
333
345
|
refresh_language($container, t);
|
|
346
|
+
refresh_raw_json_button(gobj); /* disabled until the backend session is up */
|
|
334
347
|
return $container;
|
|
335
348
|
}
|
|
336
349
|
|
|
350
|
+
/************************************************************
|
|
351
|
+
* The "Raw JSON" viewer issues a remote print-tranger, so it only makes
|
|
352
|
+
* sense with a live backend session — the remote (C_IEVENT_CLI) is in
|
|
353
|
+
* ST_SESSION exactly while connected. Enable the button only then; native
|
|
354
|
+
* `disabled` dims it (Bulma) and blocks its click.
|
|
355
|
+
************************************************************/
|
|
356
|
+
function is_connected(gobj)
|
|
357
|
+
{
|
|
358
|
+
let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
359
|
+
return !!remote && gobj_current_state(remote) === "ST_SESSION";
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function refresh_raw_json_button(gobj, connected)
|
|
363
|
+
{
|
|
364
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
365
|
+
if(!$container) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if(typeof connected !== "boolean") {
|
|
369
|
+
connected = is_connected(gobj);
|
|
370
|
+
}
|
|
371
|
+
let $raw = $container.querySelector(".TREEDB_JSON_BTN");
|
|
372
|
+
if($raw) {
|
|
373
|
+
$raw.disabled = !connected;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/************************************************************
|
|
378
|
+
* The host (C_TREEDB_VIEW) forwards the backend transport edges here so the
|
|
379
|
+
* "Raw JSON" button disables the moment the session drops and re-enables on
|
|
380
|
+
* reconnect. The library view must not subscribe to the C_IEVENT_CLI itself
|
|
381
|
+
* (that forwards the subscription upstream and breaks the session).
|
|
382
|
+
************************************************************/
|
|
383
|
+
function ac_transport_state(gobj, event, kw, src)
|
|
384
|
+
{
|
|
385
|
+
refresh_raw_json_button(gobj, !!(kw && kw.connected));
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
337
389
|
/************************************************************
|
|
338
390
|
* Show a non-blocking inline error banner at the top of the view
|
|
339
391
|
* (used when the treedb schema `descs` cannot load). Reuses a single
|
|
@@ -345,10 +397,10 @@ function show_load_error(gobj, message)
|
|
|
345
397
|
if(!$container) {
|
|
346
398
|
return;
|
|
347
399
|
}
|
|
348
|
-
let $err = $container.querySelector(".
|
|
400
|
+
let $err = $container.querySelector(".GRAPH_LOAD_ERROR");
|
|
349
401
|
if(!$err) {
|
|
350
402
|
$err = createElement2(
|
|
351
|
-
["div", {class: "notification is-danger is-light m-3
|
|
403
|
+
["div", {class: "GRAPH_LOAD_ERROR notification is-danger is-light m-3"}, []]
|
|
352
404
|
);
|
|
353
405
|
$container.insertBefore($err, $container.firstChild);
|
|
354
406
|
}
|
|
@@ -389,9 +441,9 @@ function make_toolbar(gobj)
|
|
|
389
441
|
* via populate_nodes_tree_options()
|
|
390
442
|
*/
|
|
391
443
|
let left_items = [
|
|
392
|
-
['span', {class: 'is-hidden-mobile', style: 'padding-right:5px;', i18n: 'layout'}, 'layout'],
|
|
444
|
+
['span', {class: 'GRAPH_LAYOUT_LABEL is-hidden-mobile', style: 'padding-right:5px;', i18n: 'layout'}, 'layout'],
|
|
393
445
|
['div', {class: 'select'}, [
|
|
394
|
-
['select', {class: '
|
|
446
|
+
['select', {class: 'GRAPH_LAYOUT_SELECT'}]
|
|
395
447
|
], {
|
|
396
448
|
change: (evt) => {
|
|
397
449
|
evt.stopPropagation();
|
|
@@ -399,9 +451,9 @@ function make_toolbar(gobj)
|
|
|
399
451
|
}
|
|
400
452
|
}],
|
|
401
453
|
|
|
402
|
-
['span', {class: 'is-hidden-mobile', style: 'padding-left:10px; padding-right:5px;', i18n: 'operation mode'}, 'operation mode'],
|
|
454
|
+
['span', {class: 'GRAPH_MODE_LABEL is-hidden-mobile', style: 'padding-left:10px; padding-right:5px;', i18n: 'operation mode'}, 'operation mode'],
|
|
403
455
|
['div', {class: 'select'}, [
|
|
404
|
-
['select', {class: '
|
|
456
|
+
['select', {class: 'GRAPH_MODE_SELECT'}, mode_options]
|
|
405
457
|
], {
|
|
406
458
|
change: (evt) => {
|
|
407
459
|
evt.stopPropagation();
|
|
@@ -409,7 +461,7 @@ function make_toolbar(gobj)
|
|
|
409
461
|
}
|
|
410
462
|
}],
|
|
411
463
|
|
|
412
|
-
['button', {class: 'button
|
|
464
|
+
['button', {class: 'GRAPH_REFRESH button'}, [
|
|
413
465
|
['i', {class: 'yi-arrows-rotate'}],
|
|
414
466
|
['span', {class: 'is-hidden-mobile', style: 'padding-left:5px;', i18n: 'refresh'}, 'refresh']
|
|
415
467
|
], {
|
|
@@ -418,6 +470,21 @@ function make_toolbar(gobj)
|
|
|
418
470
|
gobj_send_event(gobj, "EV_REFRESH_TREEDB", {evt}, gobj);
|
|
419
471
|
}
|
|
420
472
|
}],
|
|
473
|
+
|
|
474
|
+
/* Inspect the treedb's raw tranger json in the lazy tree viewer
|
|
475
|
+
* (print-tranger on the C_NODE service). A treedb can be huge, so
|
|
476
|
+
* the viewer drills in on demand — see open_json_viewer. */
|
|
477
|
+
['button', {class: 'button ml-2 TREEDB_JSON_BTN',
|
|
478
|
+
title: t('raw json'), 'aria-label': t('raw json'),
|
|
479
|
+
'data-i18n-title': 'raw json', 'data-i18n-aria-label': 'raw json'}, [
|
|
480
|
+
['i', {class: 'yi-eye'}],
|
|
481
|
+
['span', {class: 'is-hidden-mobile', style: 'padding-left:5px;', i18n: 'raw json'}, 'raw json']
|
|
482
|
+
], {
|
|
483
|
+
click: (evt) => {
|
|
484
|
+
evt.stopPropagation();
|
|
485
|
+
gobj_send_event(gobj, "EV_OPEN_JSON", {}, gobj);
|
|
486
|
+
}
|
|
487
|
+
}],
|
|
421
488
|
];
|
|
422
489
|
|
|
423
490
|
/*
|
|
@@ -434,7 +501,7 @@ function make_toolbar(gobj)
|
|
|
434
501
|
const $toolbar_header = yui_toolbar({}, [
|
|
435
502
|
['div', {class: 'yui-horizontal-toolbar-section left'}, left_items],
|
|
436
503
|
['div', {class: 'yui-horizontal-toolbar-section center'}, center_items],
|
|
437
|
-
['div', {class: 'yui-horizontal-toolbar-section right
|
|
504
|
+
['div', {class: 'GRAPH_MODE_BUTTONS yui-horizontal-toolbar-section right'}, right_items]
|
|
438
505
|
]);
|
|
439
506
|
|
|
440
507
|
refresh_language($toolbar_header, t);
|
|
@@ -442,6 +509,242 @@ function make_toolbar(gobj)
|
|
|
442
509
|
return $toolbar_header;
|
|
443
510
|
}
|
|
444
511
|
|
|
512
|
+
/************************************************************
|
|
513
|
+
* "← topics": a real hash link back to the topics grid (host-supplied
|
|
514
|
+
* `back_route`), shown only when set. Lets a graph reached from a topic
|
|
515
|
+
* card's graph icon return to the cards landing — symmetric with the
|
|
516
|
+
* topics view's own back button. Absent (e.g. wattyzer) ⇒ no button.
|
|
517
|
+
*
|
|
518
|
+
* It is deliberately NOT a toolbar item: yui_toolbar() lays its items in
|
|
519
|
+
* a horizontally SCROLLING container, and this is the only control here
|
|
520
|
+
* that leaves the view instead of acting on the graph — a way out cannot
|
|
521
|
+
* depend on where the toolbar happens to be scrolled. It sits pinned
|
|
522
|
+
* ahead of the toolbar, on the same row (see build_ui), like the topics
|
|
523
|
+
* view's back button, whose plain non-scrolling strip never moves.
|
|
524
|
+
************************************************************/
|
|
525
|
+
function make_back_to_topics(gobj)
|
|
526
|
+
{
|
|
527
|
+
let back_route = gobj_read_attr(gobj, "back_route") || "";
|
|
528
|
+
if(!back_route) {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
let $back = createElement2(
|
|
533
|
+
['a', {class: 'GRAPH_BACK_TOPICS button ml-1 mr-1 is-flex-shrink-0',
|
|
534
|
+
href: back_route,
|
|
535
|
+
title: t('topics'), 'aria-label': t('topics'),
|
|
536
|
+
'data-i18n-title': 'topics', 'data-i18n-aria-label': 'topics'}, [
|
|
537
|
+
['span', {class: 'icon'}, [['i', {class: 'yi-arrow-left'}]]],
|
|
538
|
+
['span', {class: 'is-hidden-mobile', style: 'padding-left:5px;',
|
|
539
|
+
i18n: 'topics'}, 'topics']
|
|
540
|
+
]]
|
|
541
|
+
);
|
|
542
|
+
refresh_language($back, t);
|
|
543
|
+
|
|
544
|
+
return $back;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/************************************************************
|
|
548
|
+
* True on a phone-width viewport (Bulma's mobile breakpoint).
|
|
549
|
+
************************************************************/
|
|
550
|
+
function is_mobile()
|
|
551
|
+
{
|
|
552
|
+
return typeof window !== "undefined" && window.innerWidth <= 768;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/************************************************************
|
|
556
|
+
* Raw-tranger JSON viewer: a C_YUI_JSON driving its own DOM, hosted in a
|
|
557
|
+
* moveable C_YUI_WINDOW on desktop / an adaptive modal sheet on mobile. It
|
|
558
|
+
* is a helper of THIS view (CHILD model: it publishes EV_EXPAND_PATH back
|
|
559
|
+
* to us), single at a time.
|
|
560
|
+
*
|
|
561
|
+
* A treedb's tranger can be enormous, so the first fetch is collapsed
|
|
562
|
+
* (print-tranger with lists/dicts limits) and the viewer drills in on
|
|
563
|
+
* demand: EV_EXPAND_PATH -> print-tranger path=<path> -> EV_SUBTREE_LOADED.
|
|
564
|
+
*
|
|
565
|
+
* print-tranger must exist on the backend C_NODE service (SDK >= the
|
|
566
|
+
* release that added it); against an older backend the command answers
|
|
567
|
+
* "command not found" and the viewer shows that error.
|
|
568
|
+
************************************************************/
|
|
569
|
+
function open_json_viewer(gobj)
|
|
570
|
+
{
|
|
571
|
+
let priv = gobj.priv;
|
|
572
|
+
if(priv.json_win || priv.json_modal) {
|
|
573
|
+
return; /* already open */
|
|
574
|
+
}
|
|
575
|
+
if(gclass_find_by_name("C_YUI_JSON") === null) {
|
|
576
|
+
log_error(`${gobj_short_name(gobj)}: C_YUI_JSON not registered by the app`);
|
|
577
|
+
yui_shell_show_error(yui_shell_of(gobj), "raw json viewer unavailable", {t: t});
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
let mobile = is_mobile();
|
|
582
|
+
let shell = yui_shell_of(gobj);
|
|
583
|
+
|
|
584
|
+
let jv = gobj_create_service(
|
|
585
|
+
`treedb-json-${clean_name(gobj_name(gobj))}`,
|
|
586
|
+
"C_YUI_JSON",
|
|
587
|
+
{
|
|
588
|
+
/* No `title`: the host titles it — the window's title bar on
|
|
589
|
+
* desktop, the dialog's header on mobile. The viewer's own
|
|
590
|
+
* title would land INSIDE that host, doubling it. */
|
|
591
|
+
subscriber: gobj /* publishes EV_EXPAND_PATH to us */
|
|
592
|
+
},
|
|
593
|
+
gobj
|
|
594
|
+
);
|
|
595
|
+
if(!jv) {
|
|
596
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the JSON viewer`);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
priv.json_gobj = jv;
|
|
600
|
+
gobj_start(jv);
|
|
601
|
+
let $box = gobj_read_pointer_attr(jv, "$container");
|
|
602
|
+
|
|
603
|
+
if(mobile) {
|
|
604
|
+
if(!shell) {
|
|
605
|
+
log_error(`${gobj_short_name(gobj)}: no shell, cannot open the JSON sheet`);
|
|
606
|
+
close_json_viewer(gobj);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
priv.json_modal = yui_shell_show_modal(shell, $box, {
|
|
610
|
+
dialog: true,
|
|
611
|
+
logical_class: "TREEDB_JSON_SHEET",
|
|
612
|
+
title_prefix: priv.treedb_name,
|
|
613
|
+
title: "raw json",
|
|
614
|
+
t: t,
|
|
615
|
+
on_close: () => {
|
|
616
|
+
if(gobj_is_destroying(gobj)) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
gobj_send_event(gobj, "EV_JSON_CLOSED", {}, gobj);
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
} else {
|
|
623
|
+
let $win_parent = (shell && yui_shell_popup_layer(shell)) ||
|
|
624
|
+
(typeof document !== "undefined" && document.getElementById("top-layer")) ||
|
|
625
|
+
null;
|
|
626
|
+
|
|
627
|
+
priv.json_win = gobj_create_service(
|
|
628
|
+
`treedb-jsonwin-${clean_name(gobj_name(gobj))}`,
|
|
629
|
+
"C_YUI_WINDOW",
|
|
630
|
+
{
|
|
631
|
+
$parent: $win_parent,
|
|
632
|
+
subscriber: null,
|
|
633
|
+
modal: false,
|
|
634
|
+
showMax: true,
|
|
635
|
+
showFooter: false,
|
|
636
|
+
resizable: true,
|
|
637
|
+
center: true,
|
|
638
|
+
auto_save_size_and_position: true,
|
|
639
|
+
width: 640,
|
|
640
|
+
height: 620,
|
|
641
|
+
logical_class: "TREEDB_JSON_WINDOW",
|
|
642
|
+
title_prefix: priv.treedb_name,
|
|
643
|
+
title: "raw json",
|
|
644
|
+
icon: "yi-eye",
|
|
645
|
+
body: $box,
|
|
646
|
+
manager: null,
|
|
647
|
+
on_close: () => {
|
|
648
|
+
if(gobj_is_destroying(gobj)) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
gobj_send_event(gobj, "EV_JSON_CLOSED", {}, gobj);
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
gobj
|
|
655
|
+
);
|
|
656
|
+
if(!priv.json_win) {
|
|
657
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the JSON window`);
|
|
658
|
+
close_json_viewer(gobj);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
gobj_start(priv.json_win);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
request_print_tranger(gobj, ""); /* first fetch: whole tranger, collapsed */
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/************************************************************
|
|
668
|
+
* Close the JSON viewer (user dismiss / teardown). Destroys the viewer
|
|
669
|
+
* gobj and whichever presenter is up, then clears the refs.
|
|
670
|
+
************************************************************/
|
|
671
|
+
function close_json_viewer(gobj)
|
|
672
|
+
{
|
|
673
|
+
let priv = gobj.priv;
|
|
674
|
+
let jv = priv.json_gobj;
|
|
675
|
+
let win = priv.json_win;
|
|
676
|
+
let modal = priv.json_modal;
|
|
677
|
+
|
|
678
|
+
priv.json_gobj = null;
|
|
679
|
+
priv.json_win = null;
|
|
680
|
+
priv.json_modal = null;
|
|
681
|
+
|
|
682
|
+
if(win && is_gobj(win)) {
|
|
683
|
+
try {
|
|
684
|
+
/* STOP, then destroy — same rule as the viewer below: the
|
|
685
|
+
* window was STARTED on open, and gobj_destroy() raises the
|
|
686
|
+
* `destroying` flag before it can stop a running gobj, so
|
|
687
|
+
* destroying it straight logs two errors and skips mt_stop.
|
|
688
|
+
* The ✕ path already stopped it (close_window) — guard. */
|
|
689
|
+
if(gobj_is_running(win)) {
|
|
690
|
+
gobj_stop(win);
|
|
691
|
+
}
|
|
692
|
+
gobj_destroy(win);
|
|
693
|
+
} catch(e) {
|
|
694
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if(modal && typeof modal.close === "function") {
|
|
698
|
+
try {
|
|
699
|
+
modal.close();
|
|
700
|
+
} catch(e) {
|
|
701
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if(jv && is_gobj(jv)) {
|
|
705
|
+
try {
|
|
706
|
+
/* STOP, then destroy — the viewer was STARTED in open_json_viewer.
|
|
707
|
+
* gobj_destroy() raises the `destroying` flag before it can stop a
|
|
708
|
+
* running gobj, so destroying it straight logs "Destroying a
|
|
709
|
+
* RUNNING gobj" + "gobj NULL or DESTROYED" and skips its mt_stop. */
|
|
710
|
+
gobj_stop(jv);
|
|
711
|
+
gobj_destroy(jv);
|
|
712
|
+
} catch(e) {
|
|
713
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/************************************************************
|
|
719
|
+
* Fetch the treedb's raw tranger (or one subtree when `path` is set) as
|
|
720
|
+
* bounded, drillable JSON. Collapsed at 100 so a huge tranger stays a
|
|
721
|
+
* small payload of `__collapsed__` stubs the viewer expands on demand.
|
|
722
|
+
************************************************************/
|
|
723
|
+
function request_print_tranger(gobj, path)
|
|
724
|
+
{
|
|
725
|
+
let priv = gobj.priv;
|
|
726
|
+
if(!priv.gobj_remote_yuno) {
|
|
727
|
+
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
728
|
+
let jv = priv.json_gobj;
|
|
729
|
+
if(path && jv && is_gobj(jv) && !gobj_is_destroying(jv)) {
|
|
730
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
731
|
+
{path: path, error: t("no session")}, gobj);
|
|
732
|
+
}
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
let ret = gobj_command(priv.gobj_remote_yuno, "print-tranger",
|
|
736
|
+
{
|
|
737
|
+
service: priv.treedb_name,
|
|
738
|
+
expanded: 1,
|
|
739
|
+
lists_limit: 100,
|
|
740
|
+
dicts_limit: 100,
|
|
741
|
+
path: path || ""
|
|
742
|
+
}, gobj);
|
|
743
|
+
if(ret) {
|
|
744
|
+
log_error(ret);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
445
748
|
/************************************************************
|
|
446
749
|
* Populate layout dropdown from child's available layouts
|
|
447
750
|
************************************************************/
|
|
@@ -451,7 +754,7 @@ function populate_nodes_tree_options(gobj)
|
|
|
451
754
|
let $container = priv.$container;
|
|
452
755
|
|
|
453
756
|
let layout_names = gobj_read_attr(priv.gobj_nodes_tree, "layout_names");
|
|
454
|
-
let $layout_select = $container.querySelector('.
|
|
757
|
+
let $layout_select = $container.querySelector('.GRAPH_LAYOUT_SELECT');
|
|
455
758
|
if($layout_select && layout_names) {
|
|
456
759
|
for(let name of layout_names) {
|
|
457
760
|
let option = document.createElement('option');
|
|
@@ -467,7 +770,7 @@ function populate_nodes_tree_options(gobj)
|
|
|
467
770
|
}
|
|
468
771
|
|
|
469
772
|
// Restore persisted operation_mode selection
|
|
470
|
-
let $operation_mode_select = $container.querySelector('.
|
|
773
|
+
let $operation_mode_select = $container.querySelector('.GRAPH_MODE_SELECT');
|
|
471
774
|
$operation_mode_select.value = priv.operation_mode;
|
|
472
775
|
}
|
|
473
776
|
|
|
@@ -507,6 +810,48 @@ function treedb_nodes(gobj, treedb_name, topic_name, options)
|
|
|
507
810
|
}
|
|
508
811
|
}
|
|
509
812
|
|
|
813
|
+
/************************************************************
|
|
814
|
+
* Command to remote service: re-read ONE node (by id).
|
|
815
|
+
*
|
|
816
|
+
* Used to resync a node whose links changed: the answer is fed to the
|
|
817
|
+
* nodes-tree as EV_NODE_UPDATED, whose fkey diff (old vs new refs) is what
|
|
818
|
+
* draws or clears the edge. Reusing that path keeps the tree the single
|
|
819
|
+
* owner of the edge model — the alternative (deriving the edge here from
|
|
820
|
+
* the event's parent/child ids) would duplicate it.
|
|
821
|
+
************************************************************/
|
|
822
|
+
function treedb_get_node(gobj, treedb_name, topic_name, node_id)
|
|
823
|
+
{
|
|
824
|
+
let priv = gobj.priv;
|
|
825
|
+
|
|
826
|
+
if(!priv.gobj_remote_yuno) {
|
|
827
|
+
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
let kw = {
|
|
832
|
+
service: treedb_name,
|
|
833
|
+
treedb_name: treedb_name,
|
|
834
|
+
topic_name: topic_name,
|
|
835
|
+
node_id: node_id,
|
|
836
|
+
options: {
|
|
837
|
+
list_dict: true /* same shape as the initial `nodes` load */
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
kw.__md_command__ = { // Data to be returned
|
|
842
|
+
topic_name: topic_name,
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
let ret = gobj_command(priv.gobj_remote_yuno,
|
|
846
|
+
"node",
|
|
847
|
+
kw,
|
|
848
|
+
gobj
|
|
849
|
+
);
|
|
850
|
+
if(ret) {
|
|
851
|
+
log_error(ret);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
510
855
|
/************************************************************
|
|
511
856
|
* Command to remote service
|
|
512
857
|
************************************************************/
|
|
@@ -754,6 +1099,36 @@ function process_treedb_descs(gobj, descs)
|
|
|
754
1099
|
//create_combo(gobj, desc);
|
|
755
1100
|
get_nodes(gobj, topic_name);
|
|
756
1101
|
}
|
|
1102
|
+
|
|
1103
|
+
/* Declare per-topic focus sub-routes to the site map (ROUTING.md). */
|
|
1104
|
+
register_sub_routes(gobj);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/************************************************************
|
|
1108
|
+
* Declare this graph's per-topic focus sub-routes to the site map.
|
|
1109
|
+
* Route-agnostic except for the host-supplied `base_route`. Cleared
|
|
1110
|
+
* on stop.
|
|
1111
|
+
************************************************************/
|
|
1112
|
+
function register_sub_routes(gobj)
|
|
1113
|
+
{
|
|
1114
|
+
let shell = yui_shell_of(gobj);
|
|
1115
|
+
let base = gobj_read_str_attr(gobj, "base_route");
|
|
1116
|
+
if(!shell || !base) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
let descs = gobj_read_attr(gobj, "descs");
|
|
1120
|
+
let system = gobj_read_bool_attr(gobj, "system");
|
|
1121
|
+
let nodes = [];
|
|
1122
|
+
if(descs) {
|
|
1123
|
+
for(const topic of Object.keys(descs)) {
|
|
1124
|
+
if(!system && topic.substring(0, 2) === "__") {
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
nodes.push({route: base + "/" + topic, label: topic,
|
|
1128
|
+
icon: "yi-hexagon-nodes", gclass: GCLASS_NAME});
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
yui_shell_set_sub_routes(shell, base, nodes);
|
|
757
1132
|
}
|
|
758
1133
|
|
|
759
1134
|
/************************************************************
|
|
@@ -779,6 +1154,43 @@ function get_nodes(gobj, topic_name)
|
|
|
779
1154
|
);
|
|
780
1155
|
}
|
|
781
1156
|
|
|
1157
|
+
/************************************************************
|
|
1158
|
+
* EV_TREEDB_NODE_LINKED / EV_TREEDB_NODE_UNLINKED are TREEDB-wide, not
|
|
1159
|
+
* per-topic: their kw is the RELATIONSHIP
|
|
1160
|
+
* ({hook_name, parent_topic_name, child_topic_name, parent_id, child_id,
|
|
1161
|
+
* treedb_name}), with no `topic_name` — so they are subscribed ONCE, and
|
|
1162
|
+
* filtered by treedb_name alone (a {topic_name} filter would match nothing).
|
|
1163
|
+
*
|
|
1164
|
+
* NOTE: the backend only publishes them when its C_NODE service is
|
|
1165
|
+
* configured with `with_link_events` (SDF_RD, default FALSE). Without it,
|
|
1166
|
+
* a link/unlink is announced the backward-compatible way — an
|
|
1167
|
+
* EV_TREEDB_NODE_UPDATED of the PARENT — which cannot move an edge here:
|
|
1168
|
+
* an edge IS a fkey of the CHILD (link-saves-child), and the parent's fkeys
|
|
1169
|
+
* did not change. That is why an open Graph kept showing stale edges when
|
|
1170
|
+
* another operator linked two nodes.
|
|
1171
|
+
************************************************************/
|
|
1172
|
+
function subscribe_treedb_links(gobj)
|
|
1173
|
+
{
|
|
1174
|
+
let priv = gobj.priv;
|
|
1175
|
+
if(priv._links_subscribed) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
priv._links_subscribed = true;
|
|
1179
|
+
|
|
1180
|
+
for(let event of ["EV_TREEDB_NODE_LINKED", "EV_TREEDB_NODE_UNLINKED"]) {
|
|
1181
|
+
gobj_subscribe_event(priv.gobj_remote_yuno,
|
|
1182
|
+
event,
|
|
1183
|
+
{
|
|
1184
|
+
__service__: priv.treedb_name,
|
|
1185
|
+
__filter__: {
|
|
1186
|
+
"treedb_name": priv.treedb_name
|
|
1187
|
+
}
|
|
1188
|
+
},
|
|
1189
|
+
gobj
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
782
1194
|
/************************************************************
|
|
783
1195
|
*
|
|
784
1196
|
************************************************************/
|
|
@@ -788,6 +1200,8 @@ function subscribe_treedb(gobj, topic_name)
|
|
|
788
1200
|
const gobj_remote_yuno = priv.gobj_remote_yuno;
|
|
789
1201
|
const treedb_name = priv.treedb_name;
|
|
790
1202
|
|
|
1203
|
+
subscribe_treedb_links(gobj);
|
|
1204
|
+
|
|
791
1205
|
/*
|
|
792
1206
|
* Avoid repetitions of subscribings
|
|
793
1207
|
*/
|
|
@@ -881,6 +1295,34 @@ function unsubscribe_treedb(gobj, topic_name)
|
|
|
881
1295
|
},
|
|
882
1296
|
gobj
|
|
883
1297
|
);
|
|
1298
|
+
|
|
1299
|
+
/* The treedb-wide LINKED/UNLINKED subscription rides the per-topic
|
|
1300
|
+
* ones: when the last topic goes, it goes too — otherwise it outlives
|
|
1301
|
+
* every view of the treedb and keeps pushing events nobody will use
|
|
1302
|
+
* (the handler's _topics_subscribed guards make them no-ops, but the
|
|
1303
|
+
* traffic still crosses the wire). */
|
|
1304
|
+
let any_left = false;
|
|
1305
|
+
for(let k in priv._topics_subscribed) {
|
|
1306
|
+
if(priv._topics_subscribed[k]) {
|
|
1307
|
+
any_left = true;
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
if(!any_left && priv._links_subscribed) {
|
|
1312
|
+
priv._links_subscribed = false;
|
|
1313
|
+
for(let ev of ["EV_TREEDB_NODE_LINKED", "EV_TREEDB_NODE_UNLINKED"]) {
|
|
1314
|
+
gobj_unsubscribe_event(gobj_remote_yuno,
|
|
1315
|
+
ev,
|
|
1316
|
+
{
|
|
1317
|
+
__service__: treedb_name,
|
|
1318
|
+
__filter__: {
|
|
1319
|
+
"treedb_name": treedb_name
|
|
1320
|
+
}
|
|
1321
|
+
},
|
|
1322
|
+
gobj
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
884
1326
|
}
|
|
885
1327
|
|
|
886
1328
|
/********************************************
|
|
@@ -935,6 +1377,36 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
935
1377
|
let command = kw_get_str(gobj, __command__, "command", "", kw_flag_t.KW_REQUIRED);
|
|
936
1378
|
let kw_command = kw_get_dict(gobj, __command__, "kw", {}, kw_flag_t.KW_REQUIRED);
|
|
937
1379
|
|
|
1380
|
+
/*
|
|
1381
|
+
* print-tranger feeds the raw-JSON viewer, correlated by the echoed
|
|
1382
|
+
* `path`: empty = first whole-tranger fetch (EV_SET_JSON), a set path =
|
|
1383
|
+
* a lazy drill (EV_SUBTREE_LOADED). Handled before the generic error
|
|
1384
|
+
* path so a failed drill marks its own branch, not the whole view.
|
|
1385
|
+
*/
|
|
1386
|
+
if(command === "print-tranger") {
|
|
1387
|
+
let jv = priv.json_gobj;
|
|
1388
|
+
if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
|
|
1389
|
+
return 0; /* viewer closed before its answer landed: benign */
|
|
1390
|
+
}
|
|
1391
|
+
let path = kw_get_str(gobj, kw_command, "path", "", 0);
|
|
1392
|
+
if(result < 0) {
|
|
1393
|
+
if(path) {
|
|
1394
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
1395
|
+
{path: path, error: comment || "print-tranger failed"}, gobj);
|
|
1396
|
+
} else {
|
|
1397
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
1398
|
+
comment || "print-tranger failed", {t: t});
|
|
1399
|
+
}
|
|
1400
|
+
return 0;
|
|
1401
|
+
}
|
|
1402
|
+
if(path) {
|
|
1403
|
+
gobj_send_event(jv, "EV_SUBTREE_LOADED", {path: path, json: data}, gobj);
|
|
1404
|
+
} else {
|
|
1405
|
+
gobj_send_event(jv, "EV_SET_JSON", {json: data}, gobj);
|
|
1406
|
+
}
|
|
1407
|
+
return 0;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
938
1410
|
if(result < 0) {
|
|
939
1411
|
if(command === "descs") {
|
|
940
1412
|
/* The schema couldn't load (not a treedb, no authz for it, backend
|
|
@@ -973,6 +1445,24 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
973
1445
|
}
|
|
974
1446
|
break;
|
|
975
1447
|
|
|
1448
|
+
case "node":
|
|
1449
|
+
if(result >= 0) {
|
|
1450
|
+
/*
|
|
1451
|
+
* A node we re-read because its links changed. Feed it as an
|
|
1452
|
+
* UPDATE: the tree diffs its fkey refs against the ones it
|
|
1453
|
+
* holds and draws / clears exactly the edges that moved.
|
|
1454
|
+
*/
|
|
1455
|
+
gobj_send_event(priv.gobj_nodes_tree,
|
|
1456
|
+
"EV_NODE_UPDATED",
|
|
1457
|
+
{
|
|
1458
|
+
topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
|
|
1459
|
+
node: data
|
|
1460
|
+
},
|
|
1461
|
+
gobj
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
break;
|
|
1465
|
+
|
|
976
1466
|
case "create-node":
|
|
977
1467
|
case "update-node":
|
|
978
1468
|
case "delete-node":
|
|
@@ -1049,6 +1539,51 @@ function ac_treedb_node_updated(gobj, event, kw, src)
|
|
|
1049
1539
|
return 0;
|
|
1050
1540
|
}
|
|
1051
1541
|
|
|
1542
|
+
/********************************************
|
|
1543
|
+
* Remote subscription response: two nodes were LINKED or UNLINKED
|
|
1544
|
+
* (by us, or by another operator on the same treedb).
|
|
1545
|
+
*
|
|
1546
|
+
* The kw is the relationship, not a node:
|
|
1547
|
+
* {hook_name, parent_topic_name, child_topic_name,
|
|
1548
|
+
* parent_id, child_id, treedb_name}
|
|
1549
|
+
*
|
|
1550
|
+
* An edge IS a fkey of the CHILD (link-saves-child), so the child is what
|
|
1551
|
+
* must be resynced — re-read it, and its EV_NODE_UPDATED diff moves the
|
|
1552
|
+
* edge. The PARENT is re-read too: its hook (the children list it shows,
|
|
1553
|
+
* and what the hook-data viewer reads) changed in memory even though it was
|
|
1554
|
+
* never saved.
|
|
1555
|
+
*
|
|
1556
|
+
* Only topics already loaded in the graph are re-read: a link to a topic
|
|
1557
|
+
* the user never opened has no node here to update.
|
|
1558
|
+
********************************************/
|
|
1559
|
+
function ac_treedb_node_linked(gobj, event, kw, src)
|
|
1560
|
+
{
|
|
1561
|
+
let priv = gobj.priv;
|
|
1562
|
+
let treedb_name = kw_get_str(gobj, kw, "treedb_name", "", 0);
|
|
1563
|
+
let parent_topic_name = kw_get_str(gobj, kw, "parent_topic_name", "", 0);
|
|
1564
|
+
let child_topic_name = kw_get_str(gobj, kw, "child_topic_name", "", 0);
|
|
1565
|
+
let parent_id = kw_get_str(gobj, kw, "parent_id", "", 0);
|
|
1566
|
+
let child_id = kw_get_str(gobj, kw, "child_id", "", 0);
|
|
1567
|
+
|
|
1568
|
+
if(treedb_name !== priv.treedb_name) {
|
|
1569
|
+
log_error("It's not my treedb_name: " + treedb_name);
|
|
1570
|
+
return 0;
|
|
1571
|
+
}
|
|
1572
|
+
if(!child_id || !parent_id) {
|
|
1573
|
+
log_error(`${gobj_short_name(gobj)}: ${event} without parent_id/child_id`);
|
|
1574
|
+
return -1;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if(priv._topics_subscribed[child_topic_name]) {
|
|
1578
|
+
treedb_get_node(gobj, treedb_name, child_topic_name, child_id);
|
|
1579
|
+
}
|
|
1580
|
+
if(priv._topics_subscribed[parent_topic_name]) {
|
|
1581
|
+
treedb_get_node(gobj, treedb_name, parent_topic_name, parent_id);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
return 0;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1052
1587
|
/********************************************
|
|
1053
1588
|
* Remote subscription response
|
|
1054
1589
|
********************************************/
|
|
@@ -1090,6 +1625,50 @@ function ac_refresh_treedb(gobj, event, kw, src)
|
|
|
1090
1625
|
return 0;
|
|
1091
1626
|
}
|
|
1092
1627
|
|
|
1628
|
+
/********************************************
|
|
1629
|
+
* Open the raw-tranger JSON viewer.
|
|
1630
|
+
********************************************/
|
|
1631
|
+
function ac_open_json(gobj, event, kw, src)
|
|
1632
|
+
{
|
|
1633
|
+
open_json_viewer(gobj);
|
|
1634
|
+
return 0;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/********************************************
|
|
1638
|
+
* The viewer asked to load a collapsed subtree: re-issue print-tranger
|
|
1639
|
+
* for that path. The answer returns through ac_mt_command_answer and is
|
|
1640
|
+
* fed back as EV_SUBTREE_LOADED / EV_SUBTREE_ERROR.
|
|
1641
|
+
********************************************/
|
|
1642
|
+
function ac_json_expand_path(gobj, event, kw, src)
|
|
1643
|
+
{
|
|
1644
|
+
request_print_tranger(gobj, (kw && kw.path) || "");
|
|
1645
|
+
return 0;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/********************************************
|
|
1649
|
+
* The JSON viewer was dismissed (X / dock / Escape / back), or torn down
|
|
1650
|
+
* by close_json_viewer(): release the viewer and clear the refs.
|
|
1651
|
+
********************************************/
|
|
1652
|
+
function ac_json_closed(gobj, event, kw, src)
|
|
1653
|
+
{
|
|
1654
|
+
let priv = gobj.priv;
|
|
1655
|
+
let jv = priv.json_gobj;
|
|
1656
|
+
priv.json_gobj = null;
|
|
1657
|
+
priv.json_win = null;
|
|
1658
|
+
priv.json_modal = null;
|
|
1659
|
+
if(jv && is_gobj(jv)) {
|
|
1660
|
+
try {
|
|
1661
|
+
/* STOP before destroy — the viewer was STARTED in open_json_viewer
|
|
1662
|
+
* (see close_json_viewer for the full rationale). */
|
|
1663
|
+
gobj_stop(jv);
|
|
1664
|
+
gobj_destroy(jv);
|
|
1665
|
+
} catch(e) {
|
|
1666
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
return 0;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1093
1672
|
/********************************************
|
|
1094
1673
|
* Event from G6_nodes_tree
|
|
1095
1674
|
* kw: {
|
|
@@ -1399,7 +1978,7 @@ function ac_set_layout(gobj, event, kw, src)
|
|
|
1399
1978
|
gobj_write_str_attr(gobj, "layout", layout);
|
|
1400
1979
|
gobj_save_persistent_attrs(gobj, "layout");
|
|
1401
1980
|
|
|
1402
|
-
let $layout_select = priv.$container.querySelector('.
|
|
1981
|
+
let $layout_select = priv.$container.querySelector('.GRAPH_LAYOUT_SELECT');
|
|
1403
1982
|
$layout_select.value = priv.layout;
|
|
1404
1983
|
|
|
1405
1984
|
gobj_send_event(
|
|
@@ -1425,7 +2004,7 @@ function ac_set_operation_mode(gobj, event, kw, src)
|
|
|
1425
2004
|
gobj_write_str_attr(gobj, "operation_mode", operation_mode);
|
|
1426
2005
|
gobj_save_persistent_attrs(gobj, "operation_mode");
|
|
1427
2006
|
|
|
1428
|
-
let $operation_mode_select = priv.$container.querySelector('.
|
|
2007
|
+
let $operation_mode_select = priv.$container.querySelector('.GRAPH_MODE_SELECT');
|
|
1429
2008
|
$operation_mode_select.value = priv.operation_mode;
|
|
1430
2009
|
|
|
1431
2010
|
gobj_send_event(
|
|
@@ -1448,6 +2027,26 @@ function ac_set_operation_mode(gobj, event, kw, src)
|
|
|
1448
2027
|
return 0;
|
|
1449
2028
|
}
|
|
1450
2029
|
|
|
2030
|
+
/************************************************************
|
|
2031
|
+
* Focus the graph on a topic (a topic card's graph icon, or a
|
|
2032
|
+
* `.../graphs/db/<sel>/<topic>` deep link): forward it to the G6
|
|
2033
|
+
* tree, which centres + highlights that topic's nodes (deferring
|
|
2034
|
+
* until its data is loaded).
|
|
2035
|
+
************************************************************/
|
|
2036
|
+
function ac_set_focus_topic(gobj, event, kw, src)
|
|
2037
|
+
{
|
|
2038
|
+
let priv = gobj.priv;
|
|
2039
|
+
if(priv.gobj_nodes_tree) {
|
|
2040
|
+
gobj_send_event(
|
|
2041
|
+
priv.gobj_nodes_tree,
|
|
2042
|
+
"EV_FOCUS_TOPIC",
|
|
2043
|
+
{topic: kw && kw.topic},
|
|
2044
|
+
gobj
|
|
2045
|
+
);
|
|
2046
|
+
}
|
|
2047
|
+
return 0;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
1451
2050
|
/************************************************************
|
|
1452
2051
|
* Parent (routing) inform us that we go showing
|
|
1453
2052
|
*
|
|
@@ -1473,15 +2072,6 @@ function ac_hide(gobj, event, kw, src)
|
|
|
1473
2072
|
return gobj_send_event(priv.gobj_nodes_tree, event, kw, gobj);
|
|
1474
2073
|
}
|
|
1475
2074
|
|
|
1476
|
-
/************************************************************
|
|
1477
|
-
* we are subscribed to EV_RESIZE from __yui_main__
|
|
1478
|
-
************************************************************/
|
|
1479
|
-
function ac_resize(gobj, event, kw, src)
|
|
1480
|
-
{
|
|
1481
|
-
let priv = gobj.priv;
|
|
1482
|
-
return gobj_send_event(priv.gobj_nodes_tree, event, kw, gobj);
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
2075
|
|
|
1486
2076
|
|
|
1487
2077
|
|
|
@@ -1522,7 +2112,12 @@ function create_gclass(gclass_name)
|
|
|
1522
2112
|
["EV_TREEDB_NODE_CREATED", ac_treedb_node_created, null],
|
|
1523
2113
|
["EV_TREEDB_NODE_UPDATED", ac_treedb_node_updated, null],
|
|
1524
2114
|
["EV_TREEDB_NODE_DELETED", ac_treedb_node_deleted, null],
|
|
2115
|
+
["EV_TREEDB_NODE_LINKED", ac_treedb_node_linked, null],
|
|
2116
|
+
["EV_TREEDB_NODE_UNLINKED", ac_treedb_node_linked, null],
|
|
1525
2117
|
["EV_REFRESH_TREEDB", ac_refresh_treedb, null],
|
|
2118
|
+
["EV_OPEN_JSON", ac_open_json, null],
|
|
2119
|
+
["EV_EXPAND_PATH", ac_json_expand_path, null],
|
|
2120
|
+
["EV_JSON_CLOSED", ac_json_closed, null],
|
|
1526
2121
|
["EV_SHOW_HOOK_DATA", ac_show_hook_data, null],
|
|
1527
2122
|
["EV_SHOW_TREEDB_TOPIC", ac_show_treedb_topic, null],
|
|
1528
2123
|
["EV_VERTEX_CLICKED", ac_vertex_clicked, null],
|
|
@@ -1536,9 +2131,10 @@ function create_gclass(gclass_name)
|
|
|
1536
2131
|
["EV_CLOSE_WINDOW", ac_close_window, null],
|
|
1537
2132
|
["EV_SET_LAYOUT", ac_set_layout, null],
|
|
1538
2133
|
["EV_SET_OPERATION_MODE", ac_set_operation_mode, null],
|
|
2134
|
+
["EV_SET_FOCUS_TOPIC", ac_set_focus_topic, null],
|
|
1539
2135
|
["EV_SHOW", ac_show, null],
|
|
1540
2136
|
["EV_HIDE", ac_hide, null],
|
|
1541
|
-
["
|
|
2137
|
+
["EV_TRANSPORT_STATE", ac_transport_state, null],
|
|
1542
2138
|
]]
|
|
1543
2139
|
];
|
|
1544
2140
|
|
|
@@ -1550,7 +2146,12 @@ function create_gclass(gclass_name)
|
|
|
1550
2146
|
["EV_TREEDB_NODE_CREATED", event_flag_t.EVF_PUBLIC_EVENT],
|
|
1551
2147
|
["EV_TREEDB_NODE_UPDATED", event_flag_t.EVF_PUBLIC_EVENT],
|
|
1552
2148
|
["EV_TREEDB_NODE_DELETED", event_flag_t.EVF_PUBLIC_EVENT],
|
|
2149
|
+
["EV_TREEDB_NODE_LINKED", event_flag_t.EVF_PUBLIC_EVENT],
|
|
2150
|
+
["EV_TREEDB_NODE_UNLINKED", event_flag_t.EVF_PUBLIC_EVENT],
|
|
1553
2151
|
["EV_REFRESH_TREEDB", 0],
|
|
2152
|
+
["EV_OPEN_JSON", 0],
|
|
2153
|
+
["EV_EXPAND_PATH", 0],
|
|
2154
|
+
["EV_JSON_CLOSED", 0],
|
|
1554
2155
|
["EV_SHOW_HOOK_DATA", 0],
|
|
1555
2156
|
["EV_SHOW_TREEDB_TOPIC", 0],
|
|
1556
2157
|
["EV_VERTEX_CLICKED", 0],
|
|
@@ -1564,11 +2165,12 @@ function create_gclass(gclass_name)
|
|
|
1564
2165
|
["EV_CLOSE_WINDOW", 0],
|
|
1565
2166
|
["EV_SET_LAYOUT", 0],
|
|
1566
2167
|
["EV_SET_OPERATION_MODE", 0],
|
|
2168
|
+
["EV_SET_FOCUS_TOPIC", 0],
|
|
1567
2169
|
["EV_OPERATION_MODE_CHANGED",
|
|
1568
2170
|
event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
|
|
1569
2171
|
["EV_SHOW", 0],
|
|
1570
2172
|
["EV_HIDE", 0],
|
|
1571
|
-
["
|
|
2173
|
+
["EV_TRANSPORT_STATE", 0],
|
|
1572
2174
|
];
|
|
1573
2175
|
|
|
1574
2176
|
/*----------------------------------------*
|