@yuneta/gobj-ui 2.6.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 +331 -11
- package/dist/gobj-ui.cjs.js +21386 -17919
- package/dist/gobj-ui.es.js +26059 -22613
- package/index.js +45 -9
- package/package.json +4 -4
- package/src/c_g6_nodes_tree.js +128 -40
- package/src/c_yui_form.css +9 -0
- package/src/c_yui_form.js +113 -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 +120 -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 +18 -0
- package/src/c_yui_treedb_topic_with_form.js +153 -103
- package/src/c_yui_treedb_topics.css +73 -0
- package/src/c_yui_treedb_topics.js +1070 -29
- package/src/c_yui_window.css +22 -0
- package/src/c_yui_window.js +182 -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 +162 -52
- package/src/shell_route_map.css +225 -0
- package/src/shell_route_map.js +363 -0
- package/src/tabulator.css +245 -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.css +28 -0
- package/src/yui_toolbar.js +86 -43
- package/src/c_yui_main.css +0 -501
- package/src/c_yui_main.js +0 -1623
- package/src/c_yui_routing.css +0 -18
- package/src/c_yui_routing.js +0 -837
- package/src/c_yui_tabs.js +0 -487
- package/src/themes.js +0 -215
- package/src/ytable.css +0 -63
- package/src/ytable.js +0 -505
|
@@ -33,16 +33,31 @@ import {
|
|
|
33
33
|
gobj_read_str_attr,
|
|
34
34
|
gobj_read_bool_attr,
|
|
35
35
|
gobj_start,
|
|
36
|
+
gobj_stop,
|
|
37
|
+
gobj_is_running,
|
|
38
|
+
gobj_current_state,
|
|
36
39
|
gobj_create_service,
|
|
40
|
+
gobj_create_pure_child,
|
|
37
41
|
gobj_command,
|
|
38
42
|
gobj_match_children,
|
|
39
43
|
msg_iev_get_stack,
|
|
40
44
|
kw_get_dict, gobj_stop_children,
|
|
41
45
|
refresh_language,
|
|
46
|
+
gobj_destroy,
|
|
47
|
+
is_gobj,
|
|
48
|
+
gobj_is_destroying,
|
|
49
|
+
log_warning,
|
|
50
|
+
gclass_find_by_name,
|
|
51
|
+
clean_name,
|
|
52
|
+
is_object,
|
|
53
|
+
is_array,
|
|
54
|
+
empty_string,
|
|
42
55
|
} from "@yuneta/gobj-js";
|
|
43
56
|
|
|
44
|
-
import
|
|
45
|
-
|
|
57
|
+
import "./c_yui_treedb_topics.css";
|
|
58
|
+
|
|
59
|
+
import {yui_shell_show_error, yui_shell_show_modal, yui_shell_popup_layer} from "./shell_modals.js";
|
|
60
|
+
import {yui_shell_of, yui_shell_set_sub_routes} from "./c_yui_shell.js";
|
|
46
61
|
|
|
47
62
|
import {t} from "i18next";
|
|
48
63
|
|
|
@@ -61,6 +76,10 @@ SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB s
|
|
|
61
76
|
SDATA(data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
|
|
62
77
|
SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
|
|
63
78
|
SDATA(data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
|
|
79
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_cards_landing",0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
|
|
80
|
+
SDATA(data_type_t.DTP_JSON, "card_action_routes",0, null, "Per-card hash-route templates {info, table, graph} with a {topic} placeholder (host-supplied, route-agnostic). Present ⇒ cards show 3 icon actions; absent ⇒ a single card that opens the table."),
|
|
81
|
+
SDATA(data_type_t.DTP_JSON, "landing_routes", 0, null, "Host-supplied hashes for the two landing sub-views {cards, schema}; the toggle navigates to them so the landing is URL-addressable (ROUTING.md). Absent ⇒ toggle flips in-view only (legacy)."),
|
|
82
|
+
SDATA(data_type_t.DTP_STRING, "base_route", 0, "", "This view's base route (host-supplied); used to declare its sub-routes (topics / info / schema) to the site map (ROUTING.md contributor)."),
|
|
64
83
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Root HTML element, show/hide managed by external routing"),
|
|
65
84
|
SDATA(data_type_t.DTP_POINTER, "$current_item", 0, null, "Currently selected item"),
|
|
66
85
|
SDATA(data_type_t.DTP_STRING, "last_selection", 0, null, "Last href selection"),
|
|
@@ -73,6 +92,13 @@ let PRIVATE_DATA = {
|
|
|
73
92
|
gobj_remote_yuno: null,
|
|
74
93
|
descs: null,
|
|
75
94
|
_topics_subscribed: {},
|
|
95
|
+
_pending_info: null, /* topic whose info panel to show once descs load */
|
|
96
|
+
_selected_card_topic: null, /* highlighted topic card in the landing grid */
|
|
97
|
+
_landing_view: "cards",/* landing sub-view: "cards" | "schema" */
|
|
98
|
+
schema_gobj: null, /* C_YUI_TREEDB_SCHEMA child (built lazily) */
|
|
99
|
+
json_gobj: null, /* C_YUI_JSON raw-tranger viewer (or null) */
|
|
100
|
+
json_win: null, /* C_YUI_WINDOW hosting it, desktop (or null) */
|
|
101
|
+
json_modal: null, /* shell modal hosting it, mobile (or null) */
|
|
76
102
|
};
|
|
77
103
|
|
|
78
104
|
let __gclass__ = null;
|
|
@@ -117,6 +143,13 @@ function mt_start(gobj)
|
|
|
117
143
|
***************************************************************/
|
|
118
144
|
function mt_stop(gobj)
|
|
119
145
|
{
|
|
146
|
+
/* Retire our site-map sub-routes so a torn-down view leaves no
|
|
147
|
+
* stale children in the map (ROUTING.md contributor). */
|
|
148
|
+
let shell = yui_shell_of(gobj);
|
|
149
|
+
if(shell) {
|
|
150
|
+
yui_shell_set_sub_routes(shell, gobj_read_str_attr(gobj, "base_route"), null);
|
|
151
|
+
}
|
|
152
|
+
close_json_viewer(gobj);
|
|
120
153
|
gobj_stop_children(gobj);
|
|
121
154
|
}
|
|
122
155
|
|
|
@@ -217,15 +250,125 @@ function build_ui(gobj)
|
|
|
217
250
|
let $container = createElement2(
|
|
218
251
|
['div', {class: `C_YUI_TREEDB_TOPICS ${gobj_read_attr(gobj, "treedb_name")}`, style: 'height:100%; display:flex; flex-direction:column;'}, [
|
|
219
252
|
['div', {class: 'is-flex-grow-0'}, [
|
|
253
|
+
['div', {class: 'is-flex is-align-items-center TREEDB_TOPICS_TOOLBAR',
|
|
254
|
+
style: 'gap:.25rem; padding:.25rem .25rem;'}, [
|
|
255
|
+
/* This toolbar never holds more than TWO buttons at once
|
|
256
|
+
* (back|toggle on the left, json on the right), so the
|
|
257
|
+
* labels stay on mobile — a deliberate exception to the
|
|
258
|
+
* icon-only-on-mobile rule: two bare squares side by side
|
|
259
|
+
* read as the same control. The graph's toolbar has many
|
|
260
|
+
* more, so it keeps is-hidden-mobile. */
|
|
261
|
+
|
|
262
|
+
/* Back to the topic-cards grid (cards-landing mode only);
|
|
263
|
+
* hidden until a topic is open. */
|
|
264
|
+
['button', {class: 'button TREEDB_TOPICS_BACK is-hidden',
|
|
265
|
+
title: t('topics'), 'aria-label': t('topics'),
|
|
266
|
+
'data-i18n-title': 'topics', 'data-i18n-aria-label': 'topics'}, [
|
|
267
|
+
['span', {class: 'icon'}, [['i', {class: 'yi-arrow-left'}]]],
|
|
268
|
+
['span', {i18n: 'topics'}, 'topics']
|
|
269
|
+
], {
|
|
270
|
+
click: (evt) => {
|
|
271
|
+
evt.stopPropagation();
|
|
272
|
+
gobj_send_event(gobj, "EV_BACK_TO_TOPICS", {}, gobj);
|
|
273
|
+
}
|
|
274
|
+
}],
|
|
275
|
+
/* Landing view toggle: cards grid <-> schema graph
|
|
276
|
+
* (topics as nodes). Shown only on the landing. */
|
|
277
|
+
['button', {class: 'button TREEDB_LANDING_TOGGLE is-hidden',
|
|
278
|
+
title: t('schema graph'), 'aria-label': t('schema graph'),
|
|
279
|
+
'data-i18n-title': 'schema graph',
|
|
280
|
+
'data-i18n-aria-label': 'schema graph'}, [
|
|
281
|
+
['span', {class: 'icon'}, [['i', {class: 'yi-hexagon-nodes'}]]],
|
|
282
|
+
['span', {i18n: 'schema'}, 'schema']
|
|
283
|
+
], {
|
|
284
|
+
click: (evt) => {
|
|
285
|
+
evt.stopPropagation();
|
|
286
|
+
gobj_send_event(gobj, "EV_TOGGLE_LANDING_VIEW", {}, gobj);
|
|
287
|
+
}
|
|
288
|
+
}],
|
|
289
|
+
/* Inspect the treedb's raw tranger json (whole service,
|
|
290
|
+
* print-tranger, lazy drill). Last + margin-left:auto:
|
|
291
|
+
* flush right, away from the back arrow it sat next to. */
|
|
292
|
+
['button', {class: 'button TREEDB_JSON_BTN',
|
|
293
|
+
style: 'margin-left:auto;',
|
|
294
|
+
title: t('raw json'), 'aria-label': t('raw json'),
|
|
295
|
+
'data-i18n-title': 'raw json', 'data-i18n-aria-label': 'raw json'}, [
|
|
296
|
+
['span', {class: 'icon'}, [['i', {class: 'yi-eye'}]]],
|
|
297
|
+
['span', {i18n: 'raw json'}, 'raw json']
|
|
298
|
+
], {
|
|
299
|
+
click: (evt) => {
|
|
300
|
+
evt.stopPropagation();
|
|
301
|
+
gobj_send_event(gobj, "EV_OPEN_JSON", {}, gobj);
|
|
302
|
+
}
|
|
303
|
+
}],
|
|
304
|
+
]],
|
|
220
305
|
['div', {class: `tabs ${gobj_read_attr(gobj, "tabs_style")}`, style: ''}, [
|
|
221
306
|
['ul', {}]
|
|
222
307
|
]],
|
|
223
308
|
]],
|
|
224
309
|
['div', {class: 'is-flex-grow-1 sub-container', style: 'height:100%; min-height:0; overflow: auto;'}, [
|
|
225
|
-
]]
|
|
310
|
+
]],
|
|
311
|
+
/* Cards-landing grid (list->detail): one card per topic, shown on
|
|
312
|
+
* entry; a click opens the topic's table. Empty/hidden in the
|
|
313
|
+
* legacy tabs-only mode. Reuses the shell's .yui-nav-cards look. */
|
|
314
|
+
['div', {class: 'is-flex-grow-1 TREEDB_TOPICS_LANDING is-hidden',
|
|
315
|
+
style: 'min-height:0; overflow:auto;'}, [
|
|
316
|
+
['div', {class: 'yui-nav-cards'}, []]
|
|
317
|
+
]],
|
|
318
|
+
/* Schema-graph landing (topics as nodes): hosts a C_YUI_TREEDB_SCHEMA
|
|
319
|
+
* child, built lazily on first toggle. */
|
|
320
|
+
['div', {class: 'is-flex-grow-1 TREEDB_TOPICS_SCHEMA is-hidden',
|
|
321
|
+
style: 'min-height:0; overflow:hidden;'}, []],
|
|
322
|
+
/* Routed topic-info panel (the card's info icon / .../<topic>/info):
|
|
323
|
+
* a read-only schema view built from the topic's desc. Hidden until
|
|
324
|
+
* the info route is active. */
|
|
325
|
+
['div', {class: 'is-flex-grow-1 TREEDB_TOPIC_INFO is-hidden',
|
|
326
|
+
style: 'min-height:0; overflow:auto;'}, []]
|
|
226
327
|
]]
|
|
227
328
|
);
|
|
228
329
|
gobj_write_attr(gobj, "$container", $container);
|
|
330
|
+
refresh_language($container, t);
|
|
331
|
+
refresh_toolbar_buttons(gobj); /* starts disabled until connected / a tree topic */
|
|
332
|
+
|
|
333
|
+
/* Cards-landing: present the (still empty) grid from the first paint so
|
|
334
|
+
* there is no flash of an empty tabs bar while the schema loads. */
|
|
335
|
+
if(gobj_read_bool_attr(gobj, "with_cards_landing")) {
|
|
336
|
+
show_topics_landing(gobj);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/************************************************************
|
|
341
|
+
* Is the backend session live? The Raw JSON viewer issues a remote
|
|
342
|
+
* print-tranger, so it only makes sense with a session — the remote
|
|
343
|
+
* (C_IEVENT_CLI) is in ST_SESSION exactly while connected.
|
|
344
|
+
************************************************************/
|
|
345
|
+
function is_connected(gobj)
|
|
346
|
+
{
|
|
347
|
+
let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
348
|
+
return !!remote && gobj_current_state(remote) === "ST_SESSION";
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/************************************************************
|
|
352
|
+
* Enable the "Raw JSON" button only when the backend session is up (it
|
|
353
|
+
* issues a remote print-tranger). Called at build, on every tab change, and
|
|
354
|
+
* when the host forwards a transport edge (EV_TRANSPORT_STATE). `connected`
|
|
355
|
+
* defaults to the live session state; the host passes it explicitly on a
|
|
356
|
+
* transport edge because the remote's state may not have settled yet. Native
|
|
357
|
+
* `disabled` dims the button (Bulma) and blocks its click.
|
|
358
|
+
************************************************************/
|
|
359
|
+
function refresh_toolbar_buttons(gobj, connected)
|
|
360
|
+
{
|
|
361
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
362
|
+
if(!$container) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if(typeof connected !== "boolean") {
|
|
366
|
+
connected = is_connected(gobj);
|
|
367
|
+
}
|
|
368
|
+
let $raw = $container.querySelector(".TREEDB_JSON_BTN");
|
|
369
|
+
if($raw) {
|
|
370
|
+
$raw.disabled = !connected;
|
|
371
|
+
}
|
|
229
372
|
}
|
|
230
373
|
|
|
231
374
|
/************************************************************
|
|
@@ -239,10 +382,10 @@ function show_load_error(gobj, message)
|
|
|
239
382
|
if(!$container) {
|
|
240
383
|
return;
|
|
241
384
|
}
|
|
242
|
-
let $err = $container.querySelector(".
|
|
385
|
+
let $err = $container.querySelector(".TOPICS_LOAD_ERROR");
|
|
243
386
|
if(!$err) {
|
|
244
387
|
$err = createElement2(
|
|
245
|
-
["div", {class: "notification is-danger is-light m-3
|
|
388
|
+
["div", {class: "TOPICS_LOAD_ERROR notification is-danger is-light m-3"}, []]
|
|
246
389
|
);
|
|
247
390
|
$container.insertBefore($err, $container.firstChild);
|
|
248
391
|
}
|
|
@@ -330,13 +473,7 @@ function add_tab(gobj, gobj2, id, text, icon)
|
|
|
330
473
|
*/
|
|
331
474
|
$a.addEventListener("click", function(ev) {
|
|
332
475
|
ev.preventDefault();
|
|
333
|
-
|
|
334
|
-
gobj_send_event(gobj, "EV_SHOW", {href: id}, gobj);
|
|
335
|
-
/* … and tell any host so it can mirror the topic into the
|
|
336
|
-
* URL path (deep-link / reload restore). Optional sub:
|
|
337
|
-
* EVF_NO_WARN_SUBS so standalone use stays quiet. */
|
|
338
|
-
let topic = id.indexOf("?") >= 0 ? id.split("?")[1] : id;
|
|
339
|
-
gobj_publish_event(gobj, "EV_TOPIC_SELECTED", {topic: topic});
|
|
476
|
+
select_topic_by_id(gobj, id);
|
|
340
477
|
});
|
|
341
478
|
|
|
342
479
|
/*
|
|
@@ -369,6 +506,452 @@ function remove_tab(gobj, gobj2, id)
|
|
|
369
506
|
if($child_content && $child_content.parentNode) {
|
|
370
507
|
$child_content.parentNode.removeChild($child_content);
|
|
371
508
|
}
|
|
509
|
+
/* Drop its landing card too, if any. */
|
|
510
|
+
let $card = $container.querySelector(
|
|
511
|
+
`.TREEDB_TOPIC_CARD[data-topic-href="${id}"]`);
|
|
512
|
+
if($card && $card.parentNode) {
|
|
513
|
+
$card.parentNode.removeChild($card);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/************************************************************
|
|
518
|
+
* Select a topic: the single entry point for a tab click AND a
|
|
519
|
+
* landing-card click. Switch locally now (instant, works standalone)
|
|
520
|
+
* and tell any host so it can mirror the topic into the URL path
|
|
521
|
+
* (deep-link / reload restore). Optional sub: EVF_NO_WARN_SUBS so
|
|
522
|
+
* standalone use stays quiet.
|
|
523
|
+
************************************************************/
|
|
524
|
+
function select_topic_by_id(gobj, id)
|
|
525
|
+
{
|
|
526
|
+
gobj_send_event(gobj, "EV_SHOW", {href: id}, gobj);
|
|
527
|
+
let topic = id.indexOf("?") >= 0 ? id.split("?")[1] : id;
|
|
528
|
+
gobj_publish_event(gobj, "EV_TOPIC_SELECTED", {topic: topic});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/************************************************************
|
|
532
|
+
* One icon-action anchor of a topic card: a REAL hash link
|
|
533
|
+
* (deep-linkable, middle-clickable) — the shell routes it on
|
|
534
|
+
* hashchange, no JS click handler needed.
|
|
535
|
+
************************************************************/
|
|
536
|
+
function card_action_anchor(logical_class, icon, i18n_key, href)
|
|
537
|
+
{
|
|
538
|
+
return ['a', {
|
|
539
|
+
class: logical_class,
|
|
540
|
+
href: href,
|
|
541
|
+
title: t(i18n_key),
|
|
542
|
+
'aria-label': t(i18n_key),
|
|
543
|
+
'data-i18n-title': i18n_key,
|
|
544
|
+
'data-i18n-aria-label': i18n_key
|
|
545
|
+
}, [
|
|
546
|
+
['span', {class: 'icon'}, [['i', {class: icon, 'aria-hidden': 'true'}]]]
|
|
547
|
+
]];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/************************************************************
|
|
551
|
+
* Add one topic card to the cards-landing grid (list->detail).
|
|
552
|
+
* With `card_action_routes` (host-supplied templates) the card
|
|
553
|
+
* shows three hash-routed icons — info / table / graph. Without,
|
|
554
|
+
* it falls back to a single card that opens the table (same id
|
|
555
|
+
* contract as the tab, `<gobj>?<topic>`).
|
|
556
|
+
************************************************************/
|
|
557
|
+
function add_topic_card(gobj, id, text, icon)
|
|
558
|
+
{
|
|
559
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
560
|
+
let $grid = $container.querySelector(".TREEDB_TOPICS_LANDING .yui-nav-cards");
|
|
561
|
+
if(!$grid) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
let routes = gobj_read_attr(gobj, "card_action_routes");
|
|
566
|
+
if(!is_object(routes)) {
|
|
567
|
+
/* Legacy single-action card: the whole card opens the table. */
|
|
568
|
+
let $card = createElement2(
|
|
569
|
+
['a', {class: 'yui-nav-item yui-nav-card TREEDB_TOPIC_CARD',
|
|
570
|
+
href: '#', 'data-topic-href': id,
|
|
571
|
+
'aria-label': text, 'data-i18n-aria-label': text}, [
|
|
572
|
+
['span', {class: 'icon is-medium'}, [['i', {class: icon || 'yi-table',
|
|
573
|
+
'aria-hidden': 'true'}]]],
|
|
574
|
+
['span', {class: 'yui-nav-label', i18n: text}, text]
|
|
575
|
+
]]
|
|
576
|
+
);
|
|
577
|
+
$card.addEventListener("click", function(ev) {
|
|
578
|
+
ev.preventDefault();
|
|
579
|
+
select_topic_by_id(gobj, id);
|
|
580
|
+
});
|
|
581
|
+
$grid.appendChild($card);
|
|
582
|
+
refresh_language($card, t);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/* Three-icon card: info / table / graph, all real hash anchors.
|
|
587
|
+
* {topic} in each template is the raw topic name (identifier-safe,
|
|
588
|
+
* matching the tab/host `<base_route>/<topic>` convention). */
|
|
589
|
+
let topic = id.indexOf("?") >= 0 ? id.split("?")[1] : id;
|
|
590
|
+
let fill = (tpl) => String(tpl || "").replace("{topic}", topic);
|
|
591
|
+
|
|
592
|
+
let actions = [];
|
|
593
|
+
if(routes.info) {
|
|
594
|
+
actions.push(card_action_anchor(
|
|
595
|
+
"TREEDB_CARD_INFO", "yi-circle-info", "info", fill(routes.info)));
|
|
596
|
+
}
|
|
597
|
+
if(routes.table) {
|
|
598
|
+
actions.push(card_action_anchor(
|
|
599
|
+
"TREEDB_CARD_TABLE", "yi-table", "table", fill(routes.table)));
|
|
600
|
+
}
|
|
601
|
+
if(routes.graph) {
|
|
602
|
+
actions.push(card_action_anchor(
|
|
603
|
+
"TREEDB_CARD_GRAPH", "yi-hexagon-nodes", "graph", fill(routes.graph)));
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
let $card = createElement2(
|
|
607
|
+
['div', {class: 'TREEDB_TOPIC_CARD', 'data-topic': topic}, [
|
|
608
|
+
['span', {class: 'TREEDB_TOPIC_CARD_NAME', i18n: text}, text],
|
|
609
|
+
['div', {class: 'TREEDB_TOPIC_CARD_ACTIONS'}, actions]
|
|
610
|
+
]]
|
|
611
|
+
);
|
|
612
|
+
if(gobj.priv._selected_card_topic === topic) {
|
|
613
|
+
$card.classList.add("is-selected");
|
|
614
|
+
}
|
|
615
|
+
/* A click anywhere on the card selects it (highlight). A click that
|
|
616
|
+
* ALSO lands on an icon still runs the icon's hash navigation (we don't
|
|
617
|
+
* preventDefault) — "click outside = select, click an icon = select +
|
|
618
|
+
* enter". Selection crosses the FSM like every action. */
|
|
619
|
+
$card.addEventListener("click", function() {
|
|
620
|
+
gobj_send_event(gobj, "EV_SELECT_TOPIC_CARD", {topic: topic}, gobj);
|
|
621
|
+
});
|
|
622
|
+
$grid.appendChild($card);
|
|
623
|
+
refresh_language($card, t);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/************************************************************
|
|
627
|
+
* Cards-landing mode: show the topic-cards grid (the section
|
|
628
|
+
* index), hide the tabs bar + the topic table, hide the back
|
|
629
|
+
* button. Deactivate any active tab so nothing looks selected.
|
|
630
|
+
************************************************************/
|
|
631
|
+
function show_topics_landing(gobj)
|
|
632
|
+
{
|
|
633
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
634
|
+
if(!$container) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
let $tabs = $container.querySelector(".tabs");
|
|
638
|
+
let $sub = $container.querySelector(".sub-container");
|
|
639
|
+
let $landing = $container.querySelector(".TREEDB_TOPICS_LANDING");
|
|
640
|
+
let $schema = $container.querySelector(".TREEDB_TOPICS_SCHEMA");
|
|
641
|
+
let $info = $container.querySelector(".TREEDB_TOPIC_INFO");
|
|
642
|
+
let $back = $container.querySelector(".TREEDB_TOPICS_BACK");
|
|
643
|
+
let $toggle = $container.querySelector(".TREEDB_LANDING_TOGGLE");
|
|
644
|
+
if($tabs) {
|
|
645
|
+
$tabs.classList.add("is-hidden");
|
|
646
|
+
}
|
|
647
|
+
if($sub) {
|
|
648
|
+
$sub.classList.add("is-hidden");
|
|
649
|
+
}
|
|
650
|
+
if($info) {
|
|
651
|
+
$info.classList.add("is-hidden");
|
|
652
|
+
}
|
|
653
|
+
if($back) {
|
|
654
|
+
$back.classList.add("is-hidden");
|
|
655
|
+
}
|
|
656
|
+
/* Cards grid vs schema graph — the two landing sub-views. */
|
|
657
|
+
let schema_mode = gobj.priv._landing_view === "schema";
|
|
658
|
+
if($toggle) {
|
|
659
|
+
$toggle.classList.remove("is-hidden"); /* the toggle lives on the landing */
|
|
660
|
+
$toggle.classList.toggle("is-primary", schema_mode);
|
|
661
|
+
}
|
|
662
|
+
if(schema_mode) {
|
|
663
|
+
if($landing) {
|
|
664
|
+
$landing.classList.add("is-hidden");
|
|
665
|
+
}
|
|
666
|
+
/* Reveal the pane BEFORE building: G6 renders at 0×0 in a
|
|
667
|
+
* display:none container. */
|
|
668
|
+
if($schema) {
|
|
669
|
+
$schema.classList.remove("is-hidden");
|
|
670
|
+
}
|
|
671
|
+
build_schema_child(gobj);
|
|
672
|
+
if(gobj.priv.schema_gobj) {
|
|
673
|
+
gobj_send_event(gobj.priv.schema_gobj, "EV_SHOW", {}, gobj);
|
|
674
|
+
}
|
|
675
|
+
} else {
|
|
676
|
+
if($schema) {
|
|
677
|
+
$schema.classList.add("is-hidden");
|
|
678
|
+
}
|
|
679
|
+
if($landing) {
|
|
680
|
+
$landing.classList.remove("is-hidden");
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/* Hide the currently shown topic content + deactivate its tab. */
|
|
685
|
+
let $current_item = gobj_read_attr(gobj, "$current_item");
|
|
686
|
+
if($current_item) {
|
|
687
|
+
$current_item.classList.remove("is-active");
|
|
688
|
+
let gobj2 = $current_item.gobj;
|
|
689
|
+
if(gobj2) {
|
|
690
|
+
let $child = gobj_read_attr(gobj2, "$container");
|
|
691
|
+
if($child) {
|
|
692
|
+
$child.classList.add("is-hidden");
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/************************************************************
|
|
699
|
+
* Lazily build the schema-graph child (C_YUI_TREEDB_SCHEMA) into
|
|
700
|
+
* the landing's schema pane. It draws the treedb as topics+links
|
|
701
|
+
* from `descs` (already loaded) and navigates on a node click via
|
|
702
|
+
* the table hash route.
|
|
703
|
+
************************************************************/
|
|
704
|
+
function build_schema_child(gobj)
|
|
705
|
+
{
|
|
706
|
+
let priv = gobj.priv;
|
|
707
|
+
if(priv.schema_gobj) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
/* Wait for the schema: on a deep-link/F5 to `.../schema` the route
|
|
711
|
+
* arrives before `descs` load. Building now would render an empty
|
|
712
|
+
* graph that never refreshes; process_treedb_descs re-shows the
|
|
713
|
+
* landing once descs arrive, which calls us again. */
|
|
714
|
+
if(!is_object(gobj_read_attr(gobj, "descs"))) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if(gclass_find_by_name("C_YUI_TREEDB_SCHEMA") === null) {
|
|
718
|
+
log_error(`${gobj_short_name(gobj)}: C_YUI_TREEDB_SCHEMA not registered by the app`);
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
722
|
+
let $pane = $container && $container.querySelector(".TREEDB_TOPICS_SCHEMA");
|
|
723
|
+
if(!$pane) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
let routes = gobj_read_attr(gobj, "card_action_routes");
|
|
727
|
+
let node_route = (is_object(routes) && routes.table) ? routes.table : "";
|
|
728
|
+
|
|
729
|
+
let schema = gobj_create_pure_child(
|
|
730
|
+
"schema_" + clean_name(gobj_name(gobj)),
|
|
731
|
+
"C_YUI_TREEDB_SCHEMA",
|
|
732
|
+
{
|
|
733
|
+
subscriber: gobj,
|
|
734
|
+
descs: gobj_read_attr(gobj, "descs"),
|
|
735
|
+
node_route: node_route,
|
|
736
|
+
system: gobj_read_bool_attr(gobj, "system")
|
|
737
|
+
},
|
|
738
|
+
gobj
|
|
739
|
+
);
|
|
740
|
+
if(!schema) {
|
|
741
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the schema view`);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
priv.schema_gobj = schema;
|
|
745
|
+
let $sc = gobj_read_attr(schema, "$container");
|
|
746
|
+
if($sc) {
|
|
747
|
+
$pane.appendChild($sc);
|
|
748
|
+
}
|
|
749
|
+
gobj_start(schema);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/************************************************************
|
|
753
|
+
* Cards-landing mode: enter a topic (hide the grid, show the
|
|
754
|
+
* tabs bar + the topic table + the back button). No-op unless
|
|
755
|
+
* the landing is enabled.
|
|
756
|
+
************************************************************/
|
|
757
|
+
function show_topic_detail(gobj)
|
|
758
|
+
{
|
|
759
|
+
if(!gobj_read_bool_attr(gobj, "with_cards_landing")) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
763
|
+
if(!$container) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
let $tabs = $container.querySelector(".tabs");
|
|
767
|
+
let $sub = $container.querySelector(".sub-container");
|
|
768
|
+
let $landing = $container.querySelector(".TREEDB_TOPICS_LANDING");
|
|
769
|
+
let $schema = $container.querySelector(".TREEDB_TOPICS_SCHEMA");
|
|
770
|
+
let $info = $container.querySelector(".TREEDB_TOPIC_INFO");
|
|
771
|
+
let $back = $container.querySelector(".TREEDB_TOPICS_BACK");
|
|
772
|
+
let $toggle = $container.querySelector(".TREEDB_LANDING_TOGGLE");
|
|
773
|
+
if($landing) {
|
|
774
|
+
$landing.classList.add("is-hidden");
|
|
775
|
+
}
|
|
776
|
+
if($schema) {
|
|
777
|
+
$schema.classList.add("is-hidden");
|
|
778
|
+
}
|
|
779
|
+
if($info) {
|
|
780
|
+
$info.classList.add("is-hidden");
|
|
781
|
+
}
|
|
782
|
+
if($toggle) {
|
|
783
|
+
$toggle.classList.add("is-hidden"); /* landing-only */
|
|
784
|
+
}
|
|
785
|
+
if($tabs) {
|
|
786
|
+
$tabs.classList.remove("is-hidden");
|
|
787
|
+
}
|
|
788
|
+
if($sub) {
|
|
789
|
+
$sub.classList.remove("is-hidden");
|
|
790
|
+
}
|
|
791
|
+
if($back) {
|
|
792
|
+
$back.classList.remove("is-hidden");
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/************************************************************
|
|
797
|
+
* Cards-landing mode: show the routed topic-info panel (the card's
|
|
798
|
+
* info icon / .../<topic>/info) — a read-only schema view built from
|
|
799
|
+
* the topic's desc. Hide the grid, tabs and table; show the back
|
|
800
|
+
* button. If the schema has not arrived yet, remember the topic and
|
|
801
|
+
* replay once descs load (deep-link / F5 on an info URL).
|
|
802
|
+
************************************************************/
|
|
803
|
+
function show_topic_info(gobj, topic)
|
|
804
|
+
{
|
|
805
|
+
let priv = gobj.priv;
|
|
806
|
+
if(empty_string(topic)) {
|
|
807
|
+
show_topics_landing(gobj);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
let descs = gobj_read_attr(gobj, "descs");
|
|
811
|
+
if(!descs) {
|
|
812
|
+
priv._pending_info = topic; /* wait for the schema */
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
let desc = descs[topic];
|
|
816
|
+
if(!is_object(desc)) {
|
|
817
|
+
/* Unknown topic (stale deep-link): fall back to the grid. */
|
|
818
|
+
log_error(`${gobj_short_name(gobj)}: info for unknown topic '${topic}'`);
|
|
819
|
+
show_topics_landing(gobj);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
824
|
+
let $info = $container && $container.querySelector(".TREEDB_TOPIC_INFO");
|
|
825
|
+
if(!$info) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
build_topic_info_panel(gobj, $info, topic, desc);
|
|
829
|
+
refresh_language($info, t);
|
|
830
|
+
|
|
831
|
+
let $tabs = $container.querySelector(".tabs");
|
|
832
|
+
let $sub = $container.querySelector(".sub-container");
|
|
833
|
+
let $landing = $container.querySelector(".TREEDB_TOPICS_LANDING");
|
|
834
|
+
let $schema = $container.querySelector(".TREEDB_TOPICS_SCHEMA");
|
|
835
|
+
let $back = $container.querySelector(".TREEDB_TOPICS_BACK");
|
|
836
|
+
let $toggle = $container.querySelector(".TREEDB_LANDING_TOGGLE");
|
|
837
|
+
if($landing) {
|
|
838
|
+
$landing.classList.add("is-hidden");
|
|
839
|
+
}
|
|
840
|
+
if($schema) {
|
|
841
|
+
$schema.classList.add("is-hidden");
|
|
842
|
+
}
|
|
843
|
+
if($toggle) {
|
|
844
|
+
$toggle.classList.add("is-hidden");
|
|
845
|
+
}
|
|
846
|
+
if($tabs) {
|
|
847
|
+
$tabs.classList.add("is-hidden");
|
|
848
|
+
}
|
|
849
|
+
if($sub) {
|
|
850
|
+
$sub.classList.add("is-hidden");
|
|
851
|
+
}
|
|
852
|
+
if($info) {
|
|
853
|
+
$info.classList.remove("is-hidden");
|
|
854
|
+
}
|
|
855
|
+
if($back) {
|
|
856
|
+
$back.classList.remove("is-hidden");
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/************************************************************
|
|
861
|
+
* Build the read-only topic-info panel from a topic desc: the
|
|
862
|
+
* topic name, its pkey, and a table of columns (name / type /
|
|
863
|
+
* key relationship). Everything is guarded — a malformed desc
|
|
864
|
+
* renders a shorter panel, never throws.
|
|
865
|
+
************************************************************/
|
|
866
|
+
function build_topic_info_panel(gobj, $info, topic, desc)
|
|
867
|
+
{
|
|
868
|
+
while($info.firstChild) {
|
|
869
|
+
$info.removeChild($info.firstChild);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
let pkey = desc.pkey || "";
|
|
873
|
+
|
|
874
|
+
/* Topic metadata (version matters to the operator): version /
|
|
875
|
+
* system / pkey / tkey. Each row is shown only when its value is
|
|
876
|
+
* present; the version is emphasised as a tag. */
|
|
877
|
+
let fmt_flag = (v) => {
|
|
878
|
+
if(is_array(v)) {
|
|
879
|
+
return v.join(", ");
|
|
880
|
+
}
|
|
881
|
+
if(is_object(v)) {
|
|
882
|
+
return Object.keys(v).join(", ");
|
|
883
|
+
}
|
|
884
|
+
return (v === undefined || v === null) ? "" : String(v);
|
|
885
|
+
};
|
|
886
|
+
let $meta = [];
|
|
887
|
+
let push_meta = (key, value, highlight) => {
|
|
888
|
+
if(value === undefined || value === null || value === "") {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
let $val = highlight
|
|
892
|
+
? ["span", {class: "tag is-info"}, `${value}`]
|
|
893
|
+
: ["code", {}, `${value}`];
|
|
894
|
+
$meta.push(
|
|
895
|
+
["tr", {}, [
|
|
896
|
+
["th", {i18n: key, style: "width:11rem;"}, key],
|
|
897
|
+
["td", {}, [$val]]
|
|
898
|
+
]]
|
|
899
|
+
);
|
|
900
|
+
};
|
|
901
|
+
push_meta("version", desc.topic_version, true);
|
|
902
|
+
push_meta("system", fmt_flag(desc.system_flag));
|
|
903
|
+
push_meta("pkey", pkey);
|
|
904
|
+
push_meta("tkey", desc.tkey);
|
|
905
|
+
|
|
906
|
+
let $rows = [];
|
|
907
|
+
let cols = is_array(desc.cols) ? desc.cols : [];
|
|
908
|
+
for(let col of cols) {
|
|
909
|
+
if(!col || !col.id || col.id.charAt(0) === "_") {
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
let type = col.type || (is_array(col.flag) ? col.flag.join(", ") : (col.flag || ""));
|
|
913
|
+
let rel = "";
|
|
914
|
+
if(col.id === pkey) {
|
|
915
|
+
rel = "pkey";
|
|
916
|
+
} else if(is_object(col.fkey)) {
|
|
917
|
+
rel = "→ " + Object.keys(col.fkey).join(", ");
|
|
918
|
+
} else if(is_object(col.hook)) {
|
|
919
|
+
rel = "hook → " + Object.keys(col.hook).join(", ");
|
|
920
|
+
}
|
|
921
|
+
$rows.push(
|
|
922
|
+
["tr", {}, [
|
|
923
|
+
["td", {}, [["code", {}, `${col.id}`]]],
|
|
924
|
+
["td", {}, `${type}`],
|
|
925
|
+
["td", {}, `${rel}`]
|
|
926
|
+
]]
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
let $panel = createElement2(
|
|
931
|
+
["div", {class: "TREEDB_TOPIC_INFO_CARD content"}, [
|
|
932
|
+
["h3", {class: "TREEDB_TOPIC_INFO_TITLE title is-5"}, [
|
|
933
|
+
["span", {class: "icon-text"}, [
|
|
934
|
+
["span", {class: "icon"}, [["i", {class: "yi-circle-info"}]]],
|
|
935
|
+
["span", {i18n: `${topic}`}, `${topic}`]
|
|
936
|
+
]]
|
|
937
|
+
]],
|
|
938
|
+
["table", {class: "table is-narrow TREEDB_TOPIC_INFO_META mb-4"}, [
|
|
939
|
+
["tbody", {}, $meta]
|
|
940
|
+
]],
|
|
941
|
+
["h4", {class: "title is-6 mb-2", i18n: "columns"}, "columns"],
|
|
942
|
+
["table", {class: "table is-fullwidth is-striped is-narrow"}, [
|
|
943
|
+
["thead", {}, [
|
|
944
|
+
["tr", {}, [
|
|
945
|
+
["th", {i18n: "column"}, "column"],
|
|
946
|
+
["th", {i18n: "type"}, "type"],
|
|
947
|
+
["th", {i18n: "key"}, "key"]
|
|
948
|
+
]]
|
|
949
|
+
]],
|
|
950
|
+
["tbody", {}, $rows]
|
|
951
|
+
]]
|
|
952
|
+
]]
|
|
953
|
+
);
|
|
954
|
+
$info.appendChild($panel);
|
|
372
955
|
}
|
|
373
956
|
|
|
374
957
|
/************************************************************
|
|
@@ -445,6 +1028,7 @@ function process_treedb_descs(gobj)
|
|
|
445
1028
|
|
|
446
1029
|
// TODO get icon from remote config
|
|
447
1030
|
add_tab(gobj, gobj_topic_form, id, key, "yi-table");
|
|
1031
|
+
add_topic_card(gobj, id, key, "yi-table");
|
|
448
1032
|
|
|
449
1033
|
gobj_start(gobj_topic_form);
|
|
450
1034
|
}
|
|
@@ -457,26 +1041,42 @@ function process_treedb_descs(gobj)
|
|
|
457
1041
|
* (e.g. the persisted topic no longer exists in the schema).
|
|
458
1042
|
*/
|
|
459
1043
|
let href = gobj_read_str_attr(gobj, "last_selection");
|
|
460
|
-
if(
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
1044
|
+
if(gobj_read_bool_attr(gobj, "with_cards_landing")) {
|
|
1045
|
+
/* Cards-landing: the grid IS the entry point, so DON'T auto-restore
|
|
1046
|
+
* the persisted topic — only honour an explicit deep-link (a host
|
|
1047
|
+
* EV_SHOW / EV_SHOW_TOPIC_INFO carrying a topic arrived while descs
|
|
1048
|
+
* were loading). No topic in flight ⇒ land on the grid. */
|
|
1049
|
+
if(gobj.priv._pending_info) {
|
|
1050
|
+
let ti = gobj.priv._pending_info;
|
|
1051
|
+
gobj.priv._pending_info = null;
|
|
1052
|
+
show_topic_info(gobj, ti);
|
|
1053
|
+
} else if(href && href.indexOf("?") >= 0) {
|
|
1054
|
+
gobj_send_event(gobj, "EV_SHOW", {href: href}, gobj);
|
|
1055
|
+
} else {
|
|
1056
|
+
show_topics_landing(gobj);
|
|
1057
|
+
}
|
|
1058
|
+
} else {
|
|
1059
|
+
if(!href) {
|
|
1060
|
+
try {
|
|
1061
|
+
let topic = window.localStorage.getItem(
|
|
1062
|
+
`yui_treedb_topics:${gobj_name(gobj)}`
|
|
1063
|
+
);
|
|
1064
|
+
if(topic) {
|
|
1065
|
+
href = `${gobj_name(gobj)}?${topic}`;
|
|
1066
|
+
}
|
|
1067
|
+
} catch(e) {
|
|
1068
|
+
// localStorage unavailable — fall through to first tab
|
|
467
1069
|
}
|
|
468
|
-
} catch(e) {
|
|
469
|
-
// localStorage unavailable — fall through to first tab
|
|
470
1070
|
}
|
|
1071
|
+
gobj_send_event(
|
|
1072
|
+
gobj,
|
|
1073
|
+
"EV_SHOW",
|
|
1074
|
+
{
|
|
1075
|
+
href: href
|
|
1076
|
+
},
|
|
1077
|
+
gobj
|
|
1078
|
+
);
|
|
471
1079
|
}
|
|
472
|
-
gobj_send_event(
|
|
473
|
-
gobj,
|
|
474
|
-
"EV_SHOW",
|
|
475
|
-
{
|
|
476
|
-
href: href
|
|
477
|
-
},
|
|
478
|
-
gobj
|
|
479
|
-
);
|
|
480
1080
|
|
|
481
1081
|
/*
|
|
482
1082
|
* Subscribe events to manage Ui_treedb_topic_form kids
|
|
@@ -493,6 +1093,49 @@ function process_treedb_descs(gobj)
|
|
|
493
1093
|
let topic_name = gobj_read_attr(kid, "topic_name");
|
|
494
1094
|
get_nodes(gobj, topic_name);
|
|
495
1095
|
}
|
|
1096
|
+
|
|
1097
|
+
/* Declare our view-owned sub-routes to the site map now the schema
|
|
1098
|
+
* is known (ROUTING.md contributor). */
|
|
1099
|
+
register_sub_routes(gobj);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/************************************************************
|
|
1103
|
+
* Declare this view's deep, view-owned sub-routes to the site map:
|
|
1104
|
+
* the schema landing, and per-topic table + info. Route-agnostic
|
|
1105
|
+
* except for the host-supplied `base_route`. Cleared on stop.
|
|
1106
|
+
************************************************************/
|
|
1107
|
+
function register_sub_routes(gobj)
|
|
1108
|
+
{
|
|
1109
|
+
let shell = yui_shell_of(gobj);
|
|
1110
|
+
let base = gobj_read_str_attr(gobj, "base_route");
|
|
1111
|
+
if(!shell || empty_string(base)) {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
let system = gobj_read_bool_attr(gobj, "system");
|
|
1115
|
+
let descs = gobj_read_attr(gobj, "descs");
|
|
1116
|
+
let nodes = [];
|
|
1117
|
+
if(is_object(gobj_read_attr(gobj, "landing_routes"))) {
|
|
1118
|
+
nodes.push({route: base + "/schema", label: "schema",
|
|
1119
|
+
icon: "yi-hexagon-nodes", gclass: "C_YUI_TREEDB_SCHEMA"});
|
|
1120
|
+
}
|
|
1121
|
+
if(is_object(descs)) {
|
|
1122
|
+
for(let topic of Object.keys(descs)) {
|
|
1123
|
+
if(!system && topic.substring(0, 2) === "__") {
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
nodes.push({
|
|
1127
|
+
route: base + "/" + topic,
|
|
1128
|
+
label: topic,
|
|
1129
|
+
icon: "yi-table",
|
|
1130
|
+
gclass: GCLASS_NAME,
|
|
1131
|
+
children: [
|
|
1132
|
+
{route: base + "/" + topic + "/info", label: "info",
|
|
1133
|
+
icon: "yi-circle-info", gclass: GCLASS_NAME}
|
|
1134
|
+
]
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
yui_shell_set_sub_routes(shell, base, nodes);
|
|
496
1139
|
}
|
|
497
1140
|
|
|
498
1141
|
/************************************************************
|
|
@@ -776,6 +1419,203 @@ function treedb_delete_node(gobj, treedb_name, topic_name, record, options)
|
|
|
776
1419
|
}
|
|
777
1420
|
}
|
|
778
1421
|
|
|
1422
|
+
/************************************************************
|
|
1423
|
+
* True on a phone-width viewport (Bulma's mobile breakpoint).
|
|
1424
|
+
************************************************************/
|
|
1425
|
+
function is_mobile()
|
|
1426
|
+
{
|
|
1427
|
+
return typeof window !== "undefined" && window.innerWidth <= 768;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
/************************************************************
|
|
1431
|
+
* JSON viewer (a single C_YUI_JSON in a window/modal): print-tranger of the
|
|
1432
|
+
* whole service (lazy drill). CHILD model: the viewer publishes
|
|
1433
|
+
* EV_EXPAND_PATH to us.
|
|
1434
|
+
************************************************************/
|
|
1435
|
+
function open_json_viewer(gobj)
|
|
1436
|
+
{
|
|
1437
|
+
let priv = gobj.priv;
|
|
1438
|
+
|
|
1439
|
+
/* Already open: just re-fetch. */
|
|
1440
|
+
if(priv.json_win || priv.json_modal) {
|
|
1441
|
+
request_print_tranger(gobj, "");
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
if(gclass_find_by_name("C_YUI_JSON") === null) {
|
|
1446
|
+
log_error(`${gobj_short_name(gobj)}: C_YUI_JSON not registered by the app`);
|
|
1447
|
+
yui_shell_show_error(yui_shell_of(gobj), "raw json viewer unavailable", {t: t});
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
let mobile = is_mobile();
|
|
1452
|
+
let shell = yui_shell_of(gobj);
|
|
1453
|
+
|
|
1454
|
+
let jv = gobj_create_service(
|
|
1455
|
+
`treedb-topics-json-${clean_name(gobj_name(gobj))}`,
|
|
1456
|
+
"C_YUI_JSON",
|
|
1457
|
+
{
|
|
1458
|
+
/* No `title`: the host titles it — the window's title bar on
|
|
1459
|
+
* desktop, the dialog's header on mobile. The viewer's own
|
|
1460
|
+
* title would land INSIDE that host, doubling it. */
|
|
1461
|
+
subscriber: gobj /* publishes EV_EXPAND_PATH to us */
|
|
1462
|
+
},
|
|
1463
|
+
gobj
|
|
1464
|
+
);
|
|
1465
|
+
if(!jv) {
|
|
1466
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the JSON viewer`);
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
priv.json_gobj = jv;
|
|
1470
|
+
gobj_start(jv);
|
|
1471
|
+
let $box = gobj_read_pointer_attr(jv, "$container");
|
|
1472
|
+
|
|
1473
|
+
if(mobile) {
|
|
1474
|
+
if(!shell) {
|
|
1475
|
+
log_error(`${gobj_short_name(gobj)}: no shell, cannot open the JSON sheet`);
|
|
1476
|
+
close_json_viewer(gobj);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
priv.json_modal = yui_shell_show_modal(shell, $box, {
|
|
1480
|
+
dialog: true,
|
|
1481
|
+
logical_class: "TREEDB_JSON_SHEET",
|
|
1482
|
+
title_prefix: priv.treedb_name,
|
|
1483
|
+
title: "raw json",
|
|
1484
|
+
t: t,
|
|
1485
|
+
on_close: () => {
|
|
1486
|
+
if(gobj_is_destroying(gobj)) {
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
gobj_send_event(gobj, "EV_JSON_CLOSED", {}, gobj);
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
} else {
|
|
1493
|
+
let $win_parent = (shell && yui_shell_popup_layer(shell)) ||
|
|
1494
|
+
(typeof document !== "undefined" && document.getElementById("top-layer")) ||
|
|
1495
|
+
null;
|
|
1496
|
+
|
|
1497
|
+
priv.json_win = gobj_create_service(
|
|
1498
|
+
`treedb-topics-jsonwin-${clean_name(gobj_name(gobj))}`,
|
|
1499
|
+
"C_YUI_WINDOW",
|
|
1500
|
+
{
|
|
1501
|
+
$parent: $win_parent,
|
|
1502
|
+
subscriber: null,
|
|
1503
|
+
modal: false,
|
|
1504
|
+
showMax: true,
|
|
1505
|
+
showFooter: false,
|
|
1506
|
+
resizable: true,
|
|
1507
|
+
center: true,
|
|
1508
|
+
auto_save_size_and_position: true,
|
|
1509
|
+
width: 640,
|
|
1510
|
+
height: 620,
|
|
1511
|
+
logical_class: "TREEDB_JSON_WINDOW",
|
|
1512
|
+
title_prefix: priv.treedb_name,
|
|
1513
|
+
title: "raw json",
|
|
1514
|
+
icon: "yi-eye",
|
|
1515
|
+
body: $box,
|
|
1516
|
+
manager: null,
|
|
1517
|
+
on_close: () => {
|
|
1518
|
+
if(gobj_is_destroying(gobj)) {
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
gobj_send_event(gobj, "EV_JSON_CLOSED", {}, gobj);
|
|
1522
|
+
}
|
|
1523
|
+
},
|
|
1524
|
+
gobj
|
|
1525
|
+
);
|
|
1526
|
+
if(!priv.json_win) {
|
|
1527
|
+
log_error(`${gobj_short_name(gobj)}: cannot create the JSON window`);
|
|
1528
|
+
close_json_viewer(gobj);
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
gobj_start(priv.json_win);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
request_print_tranger(gobj, "");
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
/************************************************************
|
|
1538
|
+
* Close the JSON viewer (user dismiss / teardown).
|
|
1539
|
+
************************************************************/
|
|
1540
|
+
function close_json_viewer(gobj)
|
|
1541
|
+
{
|
|
1542
|
+
let priv = gobj.priv;
|
|
1543
|
+
let jv = priv.json_gobj;
|
|
1544
|
+
let win = priv.json_win;
|
|
1545
|
+
let modal = priv.json_modal;
|
|
1546
|
+
|
|
1547
|
+
priv.json_gobj = null;
|
|
1548
|
+
priv.json_win = null;
|
|
1549
|
+
priv.json_modal = null;
|
|
1550
|
+
|
|
1551
|
+
if(win && is_gobj(win)) {
|
|
1552
|
+
try {
|
|
1553
|
+
/* STOP, then destroy — same rule as the viewer below: the
|
|
1554
|
+
* window was STARTED on open, and gobj_destroy() raises the
|
|
1555
|
+
* `destroying` flag before it can stop a running gobj, so
|
|
1556
|
+
* destroying it straight logs two errors and skips mt_stop.
|
|
1557
|
+
* The ✕ path already stopped it (close_window) — guard. */
|
|
1558
|
+
if(gobj_is_running(win)) {
|
|
1559
|
+
gobj_stop(win);
|
|
1560
|
+
}
|
|
1561
|
+
gobj_destroy(win);
|
|
1562
|
+
} catch(e) {
|
|
1563
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
if(modal && typeof modal.close === "function") {
|
|
1567
|
+
try {
|
|
1568
|
+
modal.close();
|
|
1569
|
+
} catch(e) {
|
|
1570
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
if(jv && is_gobj(jv)) {
|
|
1574
|
+
try {
|
|
1575
|
+
/* STOP, then destroy — the viewer was STARTED in open_json_viewer.
|
|
1576
|
+
* gobj_destroy() raises the `destroying` flag before it can stop a
|
|
1577
|
+
* running gobj, so destroying it straight logs "Destroying a
|
|
1578
|
+
* RUNNING gobj" + "gobj NULL or DESTROYED" and skips its mt_stop. */
|
|
1579
|
+
gobj_stop(jv);
|
|
1580
|
+
gobj_destroy(jv);
|
|
1581
|
+
} catch(e) {
|
|
1582
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/************************************************************
|
|
1588
|
+
* Fetch the treedb's raw tranger (or one subtree when `path` is set),
|
|
1589
|
+
* collapsed at 100 so a huge tranger stays a small payload of
|
|
1590
|
+
* `__collapsed__` stubs the viewer expands on demand.
|
|
1591
|
+
************************************************************/
|
|
1592
|
+
function request_print_tranger(gobj, path)
|
|
1593
|
+
{
|
|
1594
|
+
let priv = gobj.priv;
|
|
1595
|
+
let remote = gobj_read_pointer_attr(gobj, "gobj_remote_yuno");
|
|
1596
|
+
if(!remote) {
|
|
1597
|
+
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
1598
|
+
let jv = priv.json_gobj;
|
|
1599
|
+
if(path && jv && is_gobj(jv) && !gobj_is_destroying(jv)) {
|
|
1600
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
1601
|
+
{path: path, error: t("no session")}, gobj);
|
|
1602
|
+
}
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
let ret = gobj_command(remote, "print-tranger",
|
|
1606
|
+
{
|
|
1607
|
+
service: priv.treedb_name,
|
|
1608
|
+
expanded: 1,
|
|
1609
|
+
lists_limit: 100,
|
|
1610
|
+
dicts_limit: 100,
|
|
1611
|
+
path: path || ""
|
|
1612
|
+
}, gobj);
|
|
1613
|
+
if(ret) {
|
|
1614
|
+
log_error(ret);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
|
|
779
1619
|
|
|
780
1620
|
|
|
781
1621
|
|
|
@@ -811,6 +1651,35 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
811
1651
|
let command = kw_get_str(gobj, __command__, "command", "", kw_flag_t.KW_REQUIRED);
|
|
812
1652
|
let kw_command = kw_get_dict(gobj, __command__, "kw", {}, kw_flag_t.KW_REQUIRED);
|
|
813
1653
|
|
|
1654
|
+
/*
|
|
1655
|
+
* The JSON viewer's feed: `print-tranger` (whole tranger, lazy drill by
|
|
1656
|
+
* echoed `path`). Handled before the generic error path, which returns
|
|
1657
|
+
* early.
|
|
1658
|
+
*/
|
|
1659
|
+
if(command === "print-tranger") {
|
|
1660
|
+
let jv = gobj.priv.json_gobj;
|
|
1661
|
+
if(!jv || !is_gobj(jv) || gobj_is_destroying(jv)) {
|
|
1662
|
+
return 0; /* viewer closed before its answer landed: benign */
|
|
1663
|
+
}
|
|
1664
|
+
let path = kw_get_str(gobj, kw_command, "path", "", 0);
|
|
1665
|
+
if(result < 0) {
|
|
1666
|
+
if(path) {
|
|
1667
|
+
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
1668
|
+
{path: path, error: comment || "print-tranger failed"}, gobj);
|
|
1669
|
+
} else {
|
|
1670
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
1671
|
+
comment || "print-tranger failed", {t: t});
|
|
1672
|
+
}
|
|
1673
|
+
return 0;
|
|
1674
|
+
}
|
|
1675
|
+
if(path) {
|
|
1676
|
+
gobj_send_event(jv, "EV_SUBTREE_LOADED", {path: path, json: data}, gobj);
|
|
1677
|
+
} else {
|
|
1678
|
+
gobj_send_event(jv, "EV_SET_JSON", {json: data}, gobj);
|
|
1679
|
+
}
|
|
1680
|
+
return 0;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
814
1683
|
if(result < 0) {
|
|
815
1684
|
if(command === "descs") {
|
|
816
1685
|
/* The schema couldn't load (not a treedb, no authz for it, backend
|
|
@@ -873,6 +1742,19 @@ function ac_show(gobj, event, kw, src)
|
|
|
873
1742
|
{
|
|
874
1743
|
let href = kw.href;
|
|
875
1744
|
|
|
1745
|
+
/* Cards-landing: a show without a concrete `?<topic>` means "enter the
|
|
1746
|
+
* workspace" — land on the grid instead of auto-opening the first tab.
|
|
1747
|
+
* The bare tab route is always the CARDS sub-view (schema has its own
|
|
1748
|
+
* `.../schema` route), so reset it here — this is what makes Back from
|
|
1749
|
+
* the schema landing return to the cards. */
|
|
1750
|
+
if(gobj_read_bool_attr(gobj, "with_cards_landing") &&
|
|
1751
|
+
(!href || href.indexOf("?") < 0)) {
|
|
1752
|
+
gobj.priv._landing_view = "cards";
|
|
1753
|
+
show_topics_landing(gobj);
|
|
1754
|
+
refresh_toolbar_buttons(gobj);
|
|
1755
|
+
return 0;
|
|
1756
|
+
}
|
|
1757
|
+
|
|
876
1758
|
let $container = gobj_read_attr(gobj, "$container");
|
|
877
1759
|
let $current_item = gobj_read_attr(gobj, "$current_item");
|
|
878
1760
|
let $a = $container.querySelector(`a[href="${href}"]`);
|
|
@@ -943,8 +1825,107 @@ function ac_show(gobj, event, kw, src)
|
|
|
943
1825
|
}
|
|
944
1826
|
gobj_send_event(gobj2, "EV_SHOW", kw, gobj);
|
|
945
1827
|
}
|
|
1828
|
+
/* A concrete topic is open: leave the cards grid for the detail
|
|
1829
|
+
* (tabs bar + table + back button). No-op in tabs-only mode. */
|
|
1830
|
+
show_topic_detail(gobj);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
refresh_toolbar_buttons(gobj); /* the new tab may or may not be a tree */
|
|
1834
|
+
return 0;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/************************************************************
|
|
1838
|
+
* Show the routed topic-info panel (card info icon / .../<topic>/info).
|
|
1839
|
+
************************************************************/
|
|
1840
|
+
function ac_show_topic_info(gobj, event, kw, src)
|
|
1841
|
+
{
|
|
1842
|
+
show_topic_info(gobj, kw && kw.topic);
|
|
1843
|
+
return 0;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
/************************************************************
|
|
1847
|
+
* Highlight the clicked topic card in the landing grid (single
|
|
1848
|
+
* selection). Visual only — navigation, when the click was on an
|
|
1849
|
+
* icon, is the anchor's own hash routing.
|
|
1850
|
+
************************************************************/
|
|
1851
|
+
function ac_select_topic_card(gobj, event, kw, src)
|
|
1852
|
+
{
|
|
1853
|
+
let topic = kw && kw.topic;
|
|
1854
|
+
gobj.priv._selected_card_topic = topic || null;
|
|
1855
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
1856
|
+
if(!$container) {
|
|
1857
|
+
return 0;
|
|
946
1858
|
}
|
|
1859
|
+
$container.querySelectorAll(".TREEDB_TOPIC_CARD").forEach(function($c) {
|
|
1860
|
+
$c.classList.toggle("is-selected", $c.dataset.topic === topic);
|
|
1861
|
+
});
|
|
1862
|
+
return 0;
|
|
1863
|
+
}
|
|
947
1864
|
|
|
1865
|
+
/************************************************************
|
|
1866
|
+
* Toggle the landing between the cards grid and the schema graph.
|
|
1867
|
+
* The landing is a POSITION, so this NAVIGATES (a real hash push via
|
|
1868
|
+
* the host-supplied `landing_routes`) rather than mutating in-view
|
|
1869
|
+
* state — the route change then drives the switch (EV_SET_LANDING_VIEW),
|
|
1870
|
+
* so it is URL-addressable, F5-safe and Back-friendly (ROUTING.md §3).
|
|
1871
|
+
* Falls back to an in-view flip only when no routes were supplied.
|
|
1872
|
+
************************************************************/
|
|
1873
|
+
function ac_toggle_landing_view(gobj, event, kw, src)
|
|
1874
|
+
{
|
|
1875
|
+
let priv = gobj.priv;
|
|
1876
|
+
let target = (priv._landing_view === "schema") ? "cards" : "schema";
|
|
1877
|
+
let routes = gobj_read_attr(gobj, "landing_routes");
|
|
1878
|
+
let href = is_object(routes) ? routes[target] : null;
|
|
1879
|
+
if(href && typeof window !== "undefined") {
|
|
1880
|
+
window.location.hash = href; /* push; route drives the switch */
|
|
1881
|
+
return 0;
|
|
1882
|
+
}
|
|
1883
|
+
/* Legacy: no routes → flip in-view (not URL-addressable). */
|
|
1884
|
+
set_landing_view(gobj, target);
|
|
1885
|
+
return 0;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
/************************************************************
|
|
1889
|
+
* Set the landing sub-view ("cards" | "schema") and render it.
|
|
1890
|
+
* Driven by the route (EV_SET_LANDING_VIEW) or the legacy toggle.
|
|
1891
|
+
************************************************************/
|
|
1892
|
+
function set_landing_view(gobj, view)
|
|
1893
|
+
{
|
|
1894
|
+
gobj.priv._landing_view = (view === "schema") ? "schema" : "cards";
|
|
1895
|
+
show_topics_landing(gobj); /* syncs the toggle + shows the pane */
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
/************************************************************
|
|
1899
|
+
* Route → landing sub-view (.../db/<sel>/schema or bare tab).
|
|
1900
|
+
************************************************************/
|
|
1901
|
+
function ac_set_landing_view(gobj, event, kw, src)
|
|
1902
|
+
{
|
|
1903
|
+
set_landing_view(gobj, kw && kw.view);
|
|
1904
|
+
return 0;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
/************************************************************
|
|
1908
|
+
* Back from a topic to the cards-landing grid (the section index).
|
|
1909
|
+
************************************************************/
|
|
1910
|
+
function ac_back_to_topics(gobj, event, kw, src)
|
|
1911
|
+
{
|
|
1912
|
+
show_topics_landing(gobj);
|
|
1913
|
+
/* Tell the host the topic segment is gone so a reload re-lands on the
|
|
1914
|
+
* grid (empty topic ⇒ the host drops the <topic> from the URL). */
|
|
1915
|
+
gobj_publish_event(gobj, "EV_TOPIC_SELECTED", {topic: ""});
|
|
1916
|
+
return 0;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
/************************************************************
|
|
1920
|
+
* The host (C_TREEDB_VIEW) forwards the backend transport edges here so the
|
|
1921
|
+
* toolbar can disable the JSON viewers the moment the session drops (and
|
|
1922
|
+
* re-enable them on reconnect) — the library view must not subscribe to the
|
|
1923
|
+
* C_IEVENT_CLI itself (that forwards the subscription upstream and breaks
|
|
1924
|
+
* the session).
|
|
1925
|
+
************************************************************/
|
|
1926
|
+
function ac_transport_state(gobj, event, kw, src)
|
|
1927
|
+
{
|
|
1928
|
+
refresh_toolbar_buttons(gobj, !!(kw && kw.connected));
|
|
948
1929
|
return 0;
|
|
949
1930
|
}
|
|
950
1931
|
|
|
@@ -1147,6 +2128,48 @@ function ac_refresh_topic(gobj, event, kw, src)
|
|
|
1147
2128
|
return 0;
|
|
1148
2129
|
}
|
|
1149
2130
|
|
|
2131
|
+
/********************************************
|
|
2132
|
+
* Open the raw-tranger JSON viewer (whole service).
|
|
2133
|
+
********************************************/
|
|
2134
|
+
function ac_open_json(gobj, event, kw, src)
|
|
2135
|
+
{
|
|
2136
|
+
open_json_viewer(gobj);
|
|
2137
|
+
return 0;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
/********************************************
|
|
2141
|
+
* The viewer asked to load a collapsed subtree (tranger mode only):
|
|
2142
|
+
* re-issue print-tranger for that path.
|
|
2143
|
+
********************************************/
|
|
2144
|
+
function ac_json_expand_path(gobj, event, kw, src)
|
|
2145
|
+
{
|
|
2146
|
+
request_print_tranger(gobj, (kw && kw.path) || "");
|
|
2147
|
+
return 0;
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
/********************************************
|
|
2151
|
+
* The JSON viewer was dismissed / torn down: release it, clear refs.
|
|
2152
|
+
********************************************/
|
|
2153
|
+
function ac_json_closed(gobj, event, kw, src)
|
|
2154
|
+
{
|
|
2155
|
+
let priv = gobj.priv;
|
|
2156
|
+
let jv = priv.json_gobj;
|
|
2157
|
+
priv.json_gobj = null;
|
|
2158
|
+
priv.json_win = null;
|
|
2159
|
+
priv.json_modal = null;
|
|
2160
|
+
if(jv && is_gobj(jv)) {
|
|
2161
|
+
try {
|
|
2162
|
+
/* STOP before destroy — the viewer was STARTED in open_json_viewer
|
|
2163
|
+
* (see close_json_viewer for the full rationale). */
|
|
2164
|
+
gobj_stop(jv);
|
|
2165
|
+
gobj_destroy(jv);
|
|
2166
|
+
} catch(e) {
|
|
2167
|
+
log_warning(`${gobj_short_name(gobj)}: already gone: ${e}`);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return 0;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
1150
2173
|
|
|
1151
2174
|
|
|
1152
2175
|
|
|
@@ -1191,8 +2214,17 @@ function create_gclass(gclass_name)
|
|
|
1191
2214
|
["EV_UPDATE_RECORD", ac_update_record, null],
|
|
1192
2215
|
["EV_DELETE_RECORD", ac_delete_record, null],
|
|
1193
2216
|
["EV_REFRESH_TOPIC", ac_refresh_topic, null],
|
|
2217
|
+
["EV_OPEN_JSON", ac_open_json, null],
|
|
2218
|
+
["EV_EXPAND_PATH", ac_json_expand_path, null],
|
|
2219
|
+
["EV_JSON_CLOSED", ac_json_closed, null],
|
|
1194
2220
|
["EV_SHOW", ac_show, null],
|
|
2221
|
+
["EV_SHOW_TOPIC_INFO", ac_show_topic_info, null],
|
|
2222
|
+
["EV_SELECT_TOPIC_CARD", ac_select_topic_card, null],
|
|
2223
|
+
["EV_TOGGLE_LANDING_VIEW", ac_toggle_landing_view, null],
|
|
2224
|
+
["EV_SET_LANDING_VIEW", ac_set_landing_view, null],
|
|
1195
2225
|
["EV_HIDE", ac_hide, null],
|
|
2226
|
+
["EV_BACK_TO_TOPICS", ac_back_to_topics, null],
|
|
2227
|
+
["EV_TRANSPORT_STATE", ac_transport_state, null],
|
|
1196
2228
|
]]
|
|
1197
2229
|
];
|
|
1198
2230
|
|
|
@@ -1208,8 +2240,17 @@ function create_gclass(gclass_name)
|
|
|
1208
2240
|
["EV_UPDATE_RECORD", 0],
|
|
1209
2241
|
["EV_DELETE_RECORD", 0],
|
|
1210
2242
|
["EV_REFRESH_TOPIC", 0],
|
|
2243
|
+
["EV_OPEN_JSON", 0],
|
|
2244
|
+
["EV_EXPAND_PATH", 0],
|
|
2245
|
+
["EV_JSON_CLOSED", 0],
|
|
1211
2246
|
["EV_SHOW", 0],
|
|
2247
|
+
["EV_SHOW_TOPIC_INFO", 0],
|
|
2248
|
+
["EV_SELECT_TOPIC_CARD", 0],
|
|
2249
|
+
["EV_TOGGLE_LANDING_VIEW", 0],
|
|
2250
|
+
["EV_SET_LANDING_VIEW", 0],
|
|
1212
2251
|
["EV_HIDE", 0],
|
|
2252
|
+
["EV_BACK_TO_TOPICS", 0],
|
|
2253
|
+
["EV_TRANSPORT_STATE", 0],
|
|
1213
2254
|
["EV_TOPIC_SELECTED",
|
|
1214
2255
|
event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS]
|
|
1215
2256
|
];
|