@yuneta/gobj-ui 7.23.167 → 7.23.169
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 +2 -2
- package/dist/gobj-ui.cjs.js +448 -104
- package/dist/gobj-ui.es.js +448 -104
- package/package.json +1 -1
- package/src/broadcast_attrs.test.js +115 -0
- package/src/c_g6_nodes_tree.js +208 -19
- package/src/c_yui_treedb_graph.js +4 -1
- package/src/c_yui_treedb_topic_with_form.js +276 -98
- package/src/c_yui_treedb_topics.js +4 -1
- package/src/yui_row_search.js +56 -27
- package/src/yui_row_search.test.js +51 -1
package/package.json
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* broadcast_attrs.test.js
|
|
3
|
+
*
|
|
4
|
+
* A `broadcast_<x>_event` attr gates the publish of EV_<X>,
|
|
5
|
+
* and NOTHING ELSE gates it.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS IS A TEST: the two attrs come in pairs that differ by
|
|
8
|
+
* one word, and the guard is a string. `ac_unselect_rows()` read
|
|
9
|
+
* "broadcast_select_rows_event" -- the SELECT one -- so a host
|
|
10
|
+
* that asked only for the unselect got nothing, and a host that
|
|
11
|
+
* asked only for the select got both. The attr was declared, the
|
|
12
|
+
* event was declared, the action was wired, and the whole path
|
|
13
|
+
* typechecks: the only thing wrong was one word inside a string,
|
|
14
|
+
* in a branch no test walks and no host turns on today.
|
|
15
|
+
*
|
|
16
|
+
* The rule, stated once: every attr named `broadcast_<x>_event`
|
|
17
|
+
* is read by name, and the publish it guards is of EV_<X>.
|
|
18
|
+
*
|
|
19
|
+
* Copyright (c) 2026, ArtGins.
|
|
20
|
+
* All Rights Reserved.
|
|
21
|
+
***********************************************************************/
|
|
22
|
+
import {describe, it, expect} from "vitest";
|
|
23
|
+
import {readdirSync, readFileSync} from "node:fs";
|
|
24
|
+
import {dirname, join} from "node:path";
|
|
25
|
+
import {fileURLToPath} from "node:url";
|
|
26
|
+
|
|
27
|
+
const SRC = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
|
|
29
|
+
const SOURCES = readdirSync(SRC)
|
|
30
|
+
.filter((f) => f.endsWith(".js") && !f.endsWith(".test.js"))
|
|
31
|
+
.map((f) => [f, readFileSync(join(SRC, f), "utf8")]);
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/***************************************************************
|
|
35
|
+
* The body of the function holding `index`, by brace depth.
|
|
36
|
+
***************************************************************/
|
|
37
|
+
function enclosing_function(src, index)
|
|
38
|
+
{
|
|
39
|
+
let start = src.lastIndexOf("\nfunction ", index);
|
|
40
|
+
if(start < 0) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
let open = src.indexOf("{", start);
|
|
44
|
+
if(open < 0 || open > index) {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
let depth = 0;
|
|
48
|
+
for(let k = open; k < src.length; k++) {
|
|
49
|
+
if(src[k] === "{") {
|
|
50
|
+
depth++;
|
|
51
|
+
} else if(src[k] === "}") {
|
|
52
|
+
depth--;
|
|
53
|
+
if(depth === 0) {
|
|
54
|
+
return src.slice(open, k + 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
describe("broadcast_<x>_event attrs", function() {
|
|
63
|
+
it("every one of them is read by its own name", function() {
|
|
64
|
+
let unread = [];
|
|
65
|
+
for(const [file, src] of SOURCES) {
|
|
66
|
+
const attrs = [...src.matchAll(
|
|
67
|
+
/^SDATA\(\s*data_type_t\.DTP_BOOLEAN\s*,\s*"(broadcast_[a-z0-9_]+_event)"/gmi
|
|
68
|
+
)].map((m) => m[1]);
|
|
69
|
+
for(const attr of new Set(attrs)) {
|
|
70
|
+
if(!src.includes(`gobj_read_bool_attr(gobj, "${attr}")`)) {
|
|
71
|
+
unread.push(`${file}: ${attr}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
expect(unread).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("the publish each one guards is of the event it names", function() {
|
|
79
|
+
let wrong = [];
|
|
80
|
+
for(const [file, src] of SOURCES) {
|
|
81
|
+
const attrs = [...src.matchAll(
|
|
82
|
+
/^SDATA\(\s*data_type_t\.DTP_BOOLEAN\s*,\s*"(broadcast_[a-z0-9_]+_event)"/gmi
|
|
83
|
+
)].map((m) => m[1]);
|
|
84
|
+
for(const attr of new Set(attrs)) {
|
|
85
|
+
/* broadcast_unselect_rows_event -> EV_UNSELECT_ROWS */
|
|
86
|
+
const event = "EV_" + attr
|
|
87
|
+
.replace(/^broadcast_/, "")
|
|
88
|
+
.replace(/_event$/, "")
|
|
89
|
+
.toUpperCase();
|
|
90
|
+
const guard = `gobj_read_bool_attr(gobj, "${attr}")`;
|
|
91
|
+
let at = src.indexOf(guard);
|
|
92
|
+
if(at < 0) {
|
|
93
|
+
continue; /* the first test already reports it */
|
|
94
|
+
}
|
|
95
|
+
/* The action publishes `event`, the variable the FSM
|
|
96
|
+
* handed it -- so what proves the pairing is the
|
|
97
|
+
* event the FSM routes to this action. */
|
|
98
|
+
const body = enclosing_function(src, at);
|
|
99
|
+
const action = src.slice(0, at).match(
|
|
100
|
+
/\nfunction (ac_[a-z0-9_]+)\s*\(/g
|
|
101
|
+
);
|
|
102
|
+
const name = action? action[action.length - 1].trim()
|
|
103
|
+
.replace(/^function /, "").replace(/\s*\($/, "") : "";
|
|
104
|
+
const routed = new RegExp(
|
|
105
|
+
`\\["${event}"\\s*,\\s*${name}\\s*,`
|
|
106
|
+
).test(src);
|
|
107
|
+
const publishes = /gobj_publish_event\(gobj, event, kw\)/.test(body);
|
|
108
|
+
if(!(name && publishes && routed)) {
|
|
109
|
+
wrong.push(`${file}: ${attr} does not gate ${event}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
expect(wrong).toEqual([]);
|
|
114
|
+
});
|
|
115
|
+
});
|
package/src/c_g6_nodes_tree.js
CHANGED
|
@@ -1014,6 +1014,15 @@ function build_graph(gobj)
|
|
|
1014
1014
|
|
|
1015
1015
|
priv.graph_rendered = false;
|
|
1016
1016
|
graph_render(gobj).then(() => {
|
|
1017
|
+
/*
|
|
1018
|
+
* render() settles on its own time, and the view can be gone
|
|
1019
|
+
* by then: mt_destroy() destroys the graph and nulls
|
|
1020
|
+
* priv.graph, so configure_events() would wire handlers onto
|
|
1021
|
+
* null. A tab closed right after it was opened is enough.
|
|
1022
|
+
*/
|
|
1023
|
+
if(gobj_is_destroying(gobj) || !priv.graph) {
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1017
1026
|
configure_events(gobj);
|
|
1018
1027
|
configure_behaviour(gobj);
|
|
1019
1028
|
configure_plugins(gobj);
|
|
@@ -1023,9 +1032,41 @@ function build_graph(gobj)
|
|
|
1023
1032
|
* so deferred mode/layout/theme changes can reconfigure.
|
|
1024
1033
|
*/
|
|
1025
1034
|
priv.graph_rendered = true;
|
|
1035
|
+
}).catch((e) => {
|
|
1036
|
+
/*
|
|
1037
|
+
* Without this the graph stays blank and the only trace is
|
|
1038
|
+
* an unhandled rejection in the browser console, which says
|
|
1039
|
+
* nothing about WHO failed to render.
|
|
1040
|
+
*/
|
|
1041
|
+
log_error(`${gobj_short_name(gobj)}: graph render failed: ${e}`);
|
|
1026
1042
|
});
|
|
1027
1043
|
}
|
|
1028
1044
|
|
|
1045
|
+
/************************************************************
|
|
1046
|
+
* A G6 event, as PLAIN JSON.
|
|
1047
|
+
*
|
|
1048
|
+
* A kw is dumped by the `machine` trace (`trace_json(kw)`), and a
|
|
1049
|
+
* G6 / @antv/g event is circular: serializing one THROWS, so the
|
|
1050
|
+
* first thing a kw carrying `evt` breaks is the trace the FSM
|
|
1051
|
+
* exists to feed. It also drags a live graph element into a
|
|
1052
|
+
* message that may be read after that element is gone.
|
|
1053
|
+
*
|
|
1054
|
+
* What the actions read of it is an id and three scalars. The
|
|
1055
|
+
* event itself never leaves the callback that received it.
|
|
1056
|
+
************************************************************/
|
|
1057
|
+
function g6_event_kw(evt)
|
|
1058
|
+
{
|
|
1059
|
+
let target = (evt && evt.target)? evt.target : null;
|
|
1060
|
+
let client = (evt && evt.client)? evt.client : null;
|
|
1061
|
+
|
|
1062
|
+
return {
|
|
1063
|
+
id: (target && target.id)? String(target.id) : "",
|
|
1064
|
+
shift: !!(evt && evt.shiftKey),
|
|
1065
|
+
client_x: (client && typeof client.x === "number")? client.x : 0,
|
|
1066
|
+
client_y: (client && typeof client.y === "number")? client.y : 0
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1029
1070
|
/************************************************************
|
|
1030
1071
|
* Configure G6 event handlers
|
|
1031
1072
|
************************************************************/
|
|
@@ -1042,7 +1083,7 @@ function configure_events(gobj)
|
|
|
1042
1083
|
if(consume_long_press_click(graph)) {
|
|
1043
1084
|
return;
|
|
1044
1085
|
}
|
|
1045
|
-
gobj_send_event(gobj, "EV_CANVAS_CLICK",
|
|
1086
|
+
gobj_send_event(gobj, "EV_CANVAS_CLICK", g6_event_kw(evt), gobj);
|
|
1046
1087
|
});
|
|
1047
1088
|
|
|
1048
1089
|
graph.on(NodeEvent.DRAG, (evt) => {
|
|
@@ -1055,29 +1096,29 @@ function configure_events(gobj)
|
|
|
1055
1096
|
});
|
|
1056
1097
|
|
|
1057
1098
|
graph.on(NodeEvent.DRAG_END, (evt) => {
|
|
1058
|
-
gobj_send_event(gobj, "EV_NODE_DRAG_END",
|
|
1099
|
+
gobj_send_event(gobj, "EV_NODE_DRAG_END", g6_event_kw(evt), gobj);
|
|
1059
1100
|
});
|
|
1060
1101
|
|
|
1061
1102
|
graph.on(NodeEvent.CLICK, (evt) => {
|
|
1062
1103
|
if(consume_long_press_click(graph)) {
|
|
1063
1104
|
return;
|
|
1064
1105
|
}
|
|
1065
|
-
gobj_send_event(gobj, "EV_NODE_CLICK",
|
|
1106
|
+
gobj_send_event(gobj, "EV_NODE_CLICK", g6_event_kw(evt), gobj);
|
|
1066
1107
|
});
|
|
1067
1108
|
|
|
1068
1109
|
graph.on(NodeEvent.CONTEXT_MENU, (evt) => {
|
|
1069
|
-
gobj_send_event(gobj, "EV_NODE_CONTEXT_MENU",
|
|
1110
|
+
gobj_send_event(gobj, "EV_NODE_CONTEXT_MENU", g6_event_kw(evt), gobj);
|
|
1070
1111
|
});
|
|
1071
1112
|
|
|
1072
1113
|
graph.on(NodeEvent.DBLCLICK, (evt) => {
|
|
1073
|
-
gobj_send_event(gobj, "EV_NODE_DBLCLICK",
|
|
1114
|
+
gobj_send_event(gobj, "EV_NODE_DBLCLICK", g6_event_kw(evt), gobj);
|
|
1074
1115
|
});
|
|
1075
1116
|
|
|
1076
1117
|
graph.on(EdgeEvent.CLICK, (evt) => {
|
|
1077
1118
|
if(consume_long_press_click(graph)) {
|
|
1078
1119
|
return;
|
|
1079
1120
|
}
|
|
1080
|
-
gobj_send_event(gobj, "EV_EDGE_CLICK",
|
|
1121
|
+
gobj_send_event(gobj, "EV_EDGE_CLICK", g6_event_kw(evt), gobj);
|
|
1081
1122
|
});
|
|
1082
1123
|
|
|
1083
1124
|
graph.on('aftertransform', () => {
|
|
@@ -1281,8 +1322,13 @@ function configure_plugins(gobj)
|
|
|
1281
1322
|
'contextmenu',
|
|
1282
1323
|
{
|
|
1283
1324
|
trigger: 'contextmenu',
|
|
1325
|
+
/* The plugin's callback only turns the pick into an
|
|
1326
|
+
* EVENT: what the items DO -- unlink an edge, open a
|
|
1327
|
+
* form, delete -- belongs in an action, where the
|
|
1328
|
+
* `machine` trace can see it. */
|
|
1284
1329
|
onClick: (value) => {
|
|
1285
|
-
|
|
1330
|
+
gobj_send_event(gobj, "EV_CONTEXT_MENU_ITEM",
|
|
1331
|
+
{value: String(value)}, gobj);
|
|
1286
1332
|
},
|
|
1287
1333
|
getItems: (e) => {
|
|
1288
1334
|
return build_context_menu_items(gobj, e);
|
|
@@ -6751,7 +6797,8 @@ function show_edge_icon(gobj)
|
|
|
6751
6797
|
|
|
6752
6798
|
let icons = show_dual_icons(gobj, mid.x + 4, mid.y + floating_icon_dy(),
|
|
6753
6799
|
t('edge properties'), () => toggle_edge_popover(gobj),
|
|
6754
|
-
t('unlink'),
|
|
6800
|
+
t('unlink'),
|
|
6801
|
+
() => gobj_send_event(gobj, "EV_REQUEST_UNLINK_EDGE", {}, gobj)
|
|
6755
6802
|
);
|
|
6756
6803
|
priv._edge_icon_el = icons.icon_el;
|
|
6757
6804
|
priv._edge_delete_el = icons.delete_el;
|
|
@@ -6976,7 +7023,8 @@ function show_node_icon(gobj)
|
|
|
6976
7023
|
|
|
6977
7024
|
let icons = show_dual_icons(gobj, rect.right + 4, rect.top + floating_icon_dy(),
|
|
6978
7025
|
t('node properties'), () => toggle_node_popover(gobj),
|
|
6979
|
-
t('delete node'),
|
|
7026
|
+
t('delete node'),
|
|
7027
|
+
() => gobj_send_event(gobj, "EV_REQUEST_DELETE_NODE", {}, gobj)
|
|
6980
7028
|
);
|
|
6981
7029
|
priv._node_icon_el = icons.icon_el;
|
|
6982
7030
|
priv._node_delete_el = icons.delete_el;
|
|
@@ -8108,7 +8156,10 @@ function request_delete_selection(gobj)
|
|
|
8108
8156
|
show_confirm_popover(gobj, null,
|
|
8109
8157
|
delete_question(nodes),
|
|
8110
8158
|
'delete', '#ff4d4f',
|
|
8111
|
-
() =>
|
|
8159
|
+
() => gobj_send_event(gobj, "EV_CONFIRMED", {
|
|
8160
|
+
what: "delete_selection",
|
|
8161
|
+
ids: nodes.map((nd) => String(nd.id))
|
|
8162
|
+
}, gobj),
|
|
8112
8163
|
'_delete_confirm_el'
|
|
8113
8164
|
);
|
|
8114
8165
|
}
|
|
@@ -8145,7 +8196,10 @@ function show_delete_confirm(gobj, nodeData)
|
|
|
8145
8196
|
show_confirm_popover(gobj, priv._node_delete_el,
|
|
8146
8197
|
delete_question([nodeData]),
|
|
8147
8198
|
'delete', '#ff4d4f',
|
|
8148
|
-
() =>
|
|
8199
|
+
() => gobj_send_event(gobj, "EV_CONFIRMED", {
|
|
8200
|
+
what: "delete_node",
|
|
8201
|
+
id: String(nodeData.id)
|
|
8202
|
+
}, gobj),
|
|
8149
8203
|
'_delete_confirm_el'
|
|
8150
8204
|
);
|
|
8151
8205
|
}
|
|
@@ -8297,7 +8351,10 @@ function show_create_popover(gobj)
|
|
|
8297
8351
|
idInput.focus();
|
|
8298
8352
|
return;
|
|
8299
8353
|
}
|
|
8300
|
-
|
|
8354
|
+
gobj_send_event(gobj, "EV_SUBMIT_NEW_NODE", {
|
|
8355
|
+
topic_name: String(topicSelect.value),
|
|
8356
|
+
id: node_id
|
|
8357
|
+
}, gobj);
|
|
8301
8358
|
},
|
|
8302
8359
|
},
|
|
8303
8360
|
]);
|
|
@@ -8403,7 +8460,10 @@ function show_unlink_confirm(gobj, edgeData)
|
|
|
8403
8460
|
t('unlink') + ' ' + d.child_id + ' → ' + d.parent_id + '?' +
|
|
8404
8461
|
'\n' + t('neither record is deleted'),
|
|
8405
8462
|
'unlink', '#ff4d4f',
|
|
8406
|
-
() =>
|
|
8463
|
+
() => gobj_send_event(gobj, "EV_CONFIRMED", {
|
|
8464
|
+
what: "unlink_edge",
|
|
8465
|
+
id: String(edgeData.id)
|
|
8466
|
+
}, gobj),
|
|
8407
8467
|
'_unlink_confirm_el'
|
|
8408
8468
|
);
|
|
8409
8469
|
}
|
|
@@ -11155,7 +11215,7 @@ function ac_toggle_node_mode(gobj, event, kw, src)
|
|
|
11155
11215
|
function ac_node_dblclick(gobj, event, kw, src)
|
|
11156
11216
|
{
|
|
11157
11217
|
let priv = gobj.priv;
|
|
11158
|
-
let node_id = kw && kw.
|
|
11218
|
+
let node_id = (kw && kw.id)? kw.id : "";
|
|
11159
11219
|
let nd = null;
|
|
11160
11220
|
try {
|
|
11161
11221
|
nd = priv.graph.getNodeData(node_id);
|
|
@@ -11418,7 +11478,7 @@ function ac_node_click(gobj, event, kw, src)
|
|
|
11418
11478
|
{
|
|
11419
11479
|
let priv = gobj.priv;
|
|
11420
11480
|
let graph = priv.graph;
|
|
11421
|
-
let node_id = kw.
|
|
11481
|
+
let node_id = (kw && kw.id)? kw.id : "";
|
|
11422
11482
|
|
|
11423
11483
|
/*
|
|
11424
11484
|
* A `+N` chip is one thing only: the next page of its group. It
|
|
@@ -11464,7 +11524,7 @@ function ac_node_click(gobj, event, kw, src)
|
|
|
11464
11524
|
record: nodedata.data.record
|
|
11465
11525
|
});
|
|
11466
11526
|
|
|
11467
|
-
if(priv.edit_mode && (kw.
|
|
11527
|
+
if(priv.edit_mode && (kw.shift || priv.selection_mode)) {
|
|
11468
11528
|
/* Shift+click extends the selection, the way it does
|
|
11469
11529
|
* everywhere -- and so does a plain tap while the
|
|
11470
11530
|
* toolbar's selection mode is on, which is that key
|
|
@@ -11477,8 +11537,8 @@ function ac_node_click(gobj, event, kw, src)
|
|
|
11477
11537
|
// Convert client coords to viewport (container-relative) then to canvas
|
|
11478
11538
|
let containerRect = priv.$container.getBoundingClientRect();
|
|
11479
11539
|
let canvasPoint = graph.getCanvasByViewport([
|
|
11480
|
-
kw.
|
|
11481
|
-
kw.
|
|
11540
|
+
kw.client_x - containerRect.left,
|
|
11541
|
+
kw.client_y - containerRect.top
|
|
11482
11542
|
]);
|
|
11483
11543
|
let port_key = detect_port_click(
|
|
11484
11544
|
gobj, node_id, canvasPoint[0], canvasPoint[1]
|
|
@@ -11520,7 +11580,7 @@ function ac_edge_click(gobj, event, kw, src)
|
|
|
11520
11580
|
{
|
|
11521
11581
|
let priv = gobj.priv;
|
|
11522
11582
|
let graph = priv.graph;
|
|
11523
|
-
let edge_id = kw.
|
|
11583
|
+
let edge_id = (kw && kw.id)? kw.id : "";
|
|
11524
11584
|
|
|
11525
11585
|
try {
|
|
11526
11586
|
let edgedata = graph.getEdgeData(edge_id);
|
|
@@ -11556,6 +11616,125 @@ function ac_node_context_menu(gobj, event, kw, src)
|
|
|
11556
11616
|
return 0;
|
|
11557
11617
|
}
|
|
11558
11618
|
|
|
11619
|
+
/************************************************************
|
|
11620
|
+
* {value} -- an item of the context menu was picked.
|
|
11621
|
+
************************************************************/
|
|
11622
|
+
function ac_context_menu_item(gobj, event, kw, src)
|
|
11623
|
+
{
|
|
11624
|
+
handle_context_menu_click(gobj, (kw && kw.value)? kw.value : "");
|
|
11625
|
+
return 0;
|
|
11626
|
+
}
|
|
11627
|
+
|
|
11628
|
+
/************************************************************
|
|
11629
|
+
* The delete icon floating beside the selected node.
|
|
11630
|
+
************************************************************/
|
|
11631
|
+
function ac_request_delete_node(gobj, event, kw, src)
|
|
11632
|
+
{
|
|
11633
|
+
request_delete_node(gobj);
|
|
11634
|
+
return 0;
|
|
11635
|
+
}
|
|
11636
|
+
|
|
11637
|
+
/************************************************************
|
|
11638
|
+
* The unlink icon floating beside the selected edge.
|
|
11639
|
+
************************************************************/
|
|
11640
|
+
function ac_request_unlink_edge(gobj, event, kw, src)
|
|
11641
|
+
{
|
|
11642
|
+
request_unlink_edge(gobj);
|
|
11643
|
+
return 0;
|
|
11644
|
+
}
|
|
11645
|
+
|
|
11646
|
+
/************************************************************
|
|
11647
|
+
* {topic_name, id} -- the create popover was filled in.
|
|
11648
|
+
************************************************************/
|
|
11649
|
+
function ac_submit_new_node(gobj, event, kw, src)
|
|
11650
|
+
{
|
|
11651
|
+
let topic_name = (kw && kw.topic_name)? kw.topic_name : "";
|
|
11652
|
+
let node_id = (kw && kw.id)? kw.id : "";
|
|
11653
|
+
|
|
11654
|
+
if(!topic_name || !node_id) {
|
|
11655
|
+
log_error(`${gobj_short_name(gobj)}: ${event} without topic or id`);
|
|
11656
|
+
return -1;
|
|
11657
|
+
}
|
|
11658
|
+
execute_create_node(gobj, topic_name, node_id);
|
|
11659
|
+
return 0;
|
|
11660
|
+
}
|
|
11661
|
+
|
|
11662
|
+
/************************************************************
|
|
11663
|
+
* {what, id|ids} -- the person said yes in a confirm popover.
|
|
11664
|
+
*
|
|
11665
|
+
* The kw carries IDS and not the graph elements they name: a kw
|
|
11666
|
+
* is plain json, and a node/edge datum of a live graph is not
|
|
11667
|
+
* something to park in a message. They are resolved here, where
|
|
11668
|
+
* the graph is -- and an id that no longer names anything is a
|
|
11669
|
+
* loud refusal instead of a silent nothing.
|
|
11670
|
+
************************************************************/
|
|
11671
|
+
function ac_confirmed(gobj, event, kw, src)
|
|
11672
|
+
{
|
|
11673
|
+
let priv = gobj.priv;
|
|
11674
|
+
let graph = priv.graph;
|
|
11675
|
+
let what = (kw && kw.what)? kw.what : "";
|
|
11676
|
+
|
|
11677
|
+
if(!graph) {
|
|
11678
|
+
log_error(`${gobj_short_name(gobj)}: ${event} with no graph`);
|
|
11679
|
+
return -1;
|
|
11680
|
+
}
|
|
11681
|
+
|
|
11682
|
+
if(what === "delete_selection") {
|
|
11683
|
+
let ids = (kw && Array.isArray(kw.ids))? kw.ids : [];
|
|
11684
|
+
let nodes = [];
|
|
11685
|
+
for(let id of ids) {
|
|
11686
|
+
let nd = null;
|
|
11687
|
+
try {
|
|
11688
|
+
nd = graph.getNodeData(id);
|
|
11689
|
+
} catch(e) {
|
|
11690
|
+
nd = null;
|
|
11691
|
+
}
|
|
11692
|
+
if(nd && nd.data && nd.data.desc) {
|
|
11693
|
+
nodes.push(nd);
|
|
11694
|
+
}
|
|
11695
|
+
}
|
|
11696
|
+
if(!nodes.length) {
|
|
11697
|
+
log_error(`${gobj_short_name(gobj)}: ${event} names no node any more`);
|
|
11698
|
+
return -1;
|
|
11699
|
+
}
|
|
11700
|
+
execute_delete_selection(gobj, nodes);
|
|
11701
|
+
return 0;
|
|
11702
|
+
}
|
|
11703
|
+
|
|
11704
|
+
if(what === "delete_node") {
|
|
11705
|
+
let nd = null;
|
|
11706
|
+
try {
|
|
11707
|
+
nd = graph.getNodeData(kw.id);
|
|
11708
|
+
} catch(e) {
|
|
11709
|
+
nd = null;
|
|
11710
|
+
}
|
|
11711
|
+
if(!nd || !nd.data || !nd.data.desc) {
|
|
11712
|
+
log_error(`${gobj_short_name(gobj)}: ${event} names no node: ${kw.id}`);
|
|
11713
|
+
return -1;
|
|
11714
|
+
}
|
|
11715
|
+
execute_delete_node(gobj, nd);
|
|
11716
|
+
return 0;
|
|
11717
|
+
}
|
|
11718
|
+
|
|
11719
|
+
if(what === "unlink_edge") {
|
|
11720
|
+
let ed = null;
|
|
11721
|
+
try {
|
|
11722
|
+
ed = graph.getEdgeData(kw.id);
|
|
11723
|
+
} catch(e) {
|
|
11724
|
+
ed = null;
|
|
11725
|
+
}
|
|
11726
|
+
if(!ed || !ed.data) {
|
|
11727
|
+
log_error(`${gobj_short_name(gobj)}: ${event} names no edge: ${kw.id}`);
|
|
11728
|
+
return -1;
|
|
11729
|
+
}
|
|
11730
|
+
execute_unlink_edge(gobj, ed);
|
|
11731
|
+
return 0;
|
|
11732
|
+
}
|
|
11733
|
+
|
|
11734
|
+
log_error(`${gobj_short_name(gobj)}: ${event} of nothing this view asked about: ${what}`);
|
|
11735
|
+
return -1;
|
|
11736
|
+
}
|
|
11737
|
+
|
|
11559
11738
|
/************************************************************
|
|
11560
11739
|
* Canvas click
|
|
11561
11740
|
************************************************************/
|
|
@@ -11757,6 +11936,11 @@ function create_gclass(gclass_name)
|
|
|
11757
11936
|
["EV_NODE_CLICK", ac_node_click, null],
|
|
11758
11937
|
["EV_EDGE_CLICK", ac_edge_click, null],
|
|
11759
11938
|
["EV_NODE_CONTEXT_MENU", ac_node_context_menu, null],
|
|
11939
|
+
["EV_CONTEXT_MENU_ITEM", ac_context_menu_item, null],
|
|
11940
|
+
["EV_REQUEST_DELETE_NODE", ac_request_delete_node, null],
|
|
11941
|
+
["EV_REQUEST_UNLINK_EDGE", ac_request_unlink_edge, null],
|
|
11942
|
+
["EV_SUBMIT_NEW_NODE", ac_submit_new_node, null],
|
|
11943
|
+
["EV_CONFIRMED", ac_confirmed, null],
|
|
11760
11944
|
["EV_CANVAS_CLICK", ac_canvas_click, null],
|
|
11761
11945
|
["EV_NODE_DRAG_END", ac_node_drag_end, null],
|
|
11762
11946
|
["EV_BRUSH_SELECT", ac_brush_select, null],
|
|
@@ -11821,6 +12005,11 @@ function create_gclass(gclass_name)
|
|
|
11821
12005
|
["EV_NODE_CLICK", 0],
|
|
11822
12006
|
["EV_EDGE_CLICK", 0],
|
|
11823
12007
|
["EV_NODE_CONTEXT_MENU", 0],
|
|
12008
|
+
["EV_CONTEXT_MENU_ITEM", 0],
|
|
12009
|
+
["EV_REQUEST_DELETE_NODE", 0],
|
|
12010
|
+
["EV_REQUEST_UNLINK_EDGE", 0],
|
|
12011
|
+
["EV_SUBMIT_NEW_NODE", 0],
|
|
12012
|
+
["EV_CONFIRMED", 0],
|
|
11824
12013
|
["EV_CANVAS_CLICK", 0],
|
|
11825
12014
|
["EV_NODE_DRAG_END", 0],
|
|
11826
12015
|
["EV_BRUSH_SELECT", 0],
|
|
@@ -1335,7 +1335,10 @@ function request_print_tranger(gobj, path)
|
|
|
1335
1335
|
expanded: 1,
|
|
1336
1336
|
lists_limit: 100,
|
|
1337
1337
|
dicts_limit: 100,
|
|
1338
|
-
path: path || ""
|
|
1338
|
+
path: path || "",
|
|
1339
|
+
/* What comes BACK: the answer carries only this frame, and
|
|
1340
|
+
* without the path a drill replaced the whole document. */
|
|
1341
|
+
__md_command__: {path: path || ""}
|
|
1339
1342
|
}, gobj);
|
|
1340
1343
|
if(ret) {
|
|
1341
1344
|
log_error(ret);
|