@yuneta/gobj-ui 7.25.14 → 7.25.16
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 +100 -4
- package/dist/gobj-ui.cjs.js +406 -94
- package/dist/gobj-ui.es.js +406 -94
- package/package.json +2 -2
- package/src/c_g6_nodes_tree.js +79 -15
- package/src/c_yui_schema_editor.js +343 -66
- package/src/c_yui_treedb_graph.js +53 -6
- package/src/c_yui_treedb_topic_with_form.js +10 -10
- package/src/c_yui_treedb_topics.js +7 -7
- package/src/form_busy.js +2 -3
- package/src/form_busy.test.js +1 -1
- package/src/form_time_value.js +1 -1
- package/src/form_time_value.test.js +1 -1
- package/src/form_writes_in_flight.js +1 -2
- package/src/form_writes_in_flight.test.js +1 -1
- package/src/g6_graphs_write_owed.wiring.test.js +200 -0
- package/src/g6_nodes_tree_fsm.test.js +33 -0
- package/src/graph_save_plan.js +28 -0
- package/src/graph_save_plan.test.js +36 -2
- package/src/host_drafts.js +2 -3
- package/src/host_drafts.test.js +2 -2
- package/src/schema_editor_drafts.wiring.test.js +2 -2
- package/src/schema_editor_reload.wiring.test.js +329 -49
- package/src/schema_editor_transport.wiring.test.js +2 -2
- package/src/schema_version_bump.test.js +1 -1
- package/src/shell_modals.js +31 -11
- package/src/shell_toast_once.test.js +73 -10
- package/src/table_cell_text.js +1 -1
- package/src/table_cell_text.test.js +1 -1
- package/src/treedb_graph_writes.wiring.test.js +208 -0
- package/src/treedb_topics_transport.wiring.test.js +2 -2
- package/src/yui_install.js +14 -3
- package/src/yui_install.test.js +23 -0
- package/src/yui_row_search.test.js +1 -1
- package/src/yui_table_select.test.js +1 -1
package/dist/gobj-ui.es.js
CHANGED
|
@@ -3033,9 +3033,8 @@ function maybe_apply_translator($node, opts) {
|
|
|
3033
3033
|
* it -- until every handle is closed or has timed out. Handed the
|
|
3034
3034
|
* first caller's handle and timer, a repeat asking `timeout: 0`
|
|
3035
3035
|
* was dismissed on the first one's timer, and closing one
|
|
3036
|
-
* caller's handle closed a toast another was still showing
|
|
3037
|
-
*
|
|
3038
|
-
* everybody.
|
|
3036
|
+
* caller's handle closed a toast another was still showing.
|
|
3037
|
+
* The operator's ✕ closes it for everybody.
|
|
3039
3038
|
***************************************************************/
|
|
3040
3039
|
function show_notification(shell, kind, message, opts) {
|
|
3041
3040
|
let $layer = notification_layer(shell);
|
|
@@ -3433,8 +3432,28 @@ function build_dialog(shell, message, buttons, opts) {
|
|
|
3433
3432
|
if ($primary) $primary.focus();
|
|
3434
3433
|
});
|
|
3435
3434
|
}
|
|
3435
|
+
/***************************************************************
|
|
3436
|
+
* The labels a confirmation shows when the caller names none.
|
|
3437
|
+
*
|
|
3438
|
+
* They are i18n KEYS, lower-case like every key: a button
|
|
3439
|
+
* translates its label with the app's t() -- its text, its title
|
|
3440
|
+
* and its aria-label. Until 7.25.16 they were "OK", "Yes", "No",
|
|
3441
|
+
* "Delete" and "Cancel", keys that no locale carries, so a dialog
|
|
3442
|
+
* that took the defaults read in English in every language.
|
|
3443
|
+
*
|
|
3444
|
+
* Written as `i18n:` entries so the validate-locales script of a
|
|
3445
|
+
* consumer finds them when it scans this module: an app that
|
|
3446
|
+
* mounts these dialogs must define the five keys.
|
|
3447
|
+
***************************************************************/
|
|
3448
|
+
var DEFAULT_LABEL = Object.freeze({
|
|
3449
|
+
ok: Object.freeze({ i18n: "ok" }),
|
|
3450
|
+
yes: Object.freeze({ i18n: "yes" }),
|
|
3451
|
+
no: Object.freeze({ i18n: "no" }),
|
|
3452
|
+
delete: Object.freeze({ i18n: "delete" }),
|
|
3453
|
+
cancel: Object.freeze({ i18n: "cancel" })
|
|
3454
|
+
});
|
|
3436
3455
|
function yui_shell_confirm_ok(shell, message, opts) {
|
|
3437
|
-
let label = opts && opts.ok_label ||
|
|
3456
|
+
let label = opts && opts.ok_label || DEFAULT_LABEL.ok.i18n;
|
|
3438
3457
|
if (!opts || !opts.type) opts = Object.assign({}, opts, { type: "success" });
|
|
3439
3458
|
return build_dialog(shell, message, [{
|
|
3440
3459
|
label,
|
|
@@ -3443,8 +3462,8 @@ function yui_shell_confirm_ok(shell, message, opts) {
|
|
|
3443
3462
|
}], opts).then(() => void 0);
|
|
3444
3463
|
}
|
|
3445
3464
|
function yui_shell_confirm_yesno(shell, message, opts) {
|
|
3446
|
-
let yes_label = opts && opts.yes_label ||
|
|
3447
|
-
let no_label = opts && opts.no_label ||
|
|
3465
|
+
let yes_label = opts && opts.yes_label || DEFAULT_LABEL.yes.i18n;
|
|
3466
|
+
let no_label = opts && opts.no_label || DEFAULT_LABEL.no.i18n;
|
|
3448
3467
|
return build_dialog(shell, message, [{
|
|
3449
3468
|
label: yes_label,
|
|
3450
3469
|
value: "yes",
|
|
@@ -3469,8 +3488,8 @@ function yui_shell_confirm_yesno(shell, message, opts) {
|
|
|
3469
3488
|
* pressed.
|
|
3470
3489
|
***************************************************************/
|
|
3471
3490
|
function yui_shell_confirm_danger(shell, message, opts) {
|
|
3472
|
-
let confirm_label = opts && opts.confirm_label ||
|
|
3473
|
-
let cancel_label = opts && opts.cancel_label ||
|
|
3491
|
+
let confirm_label = opts && opts.confirm_label || DEFAULT_LABEL.delete.i18n;
|
|
3492
|
+
let cancel_label = opts && opts.cancel_label || DEFAULT_LABEL.cancel.i18n;
|
|
3474
3493
|
if (!opts || !opts.type) opts = Object.assign({}, opts, { type: "danger" });
|
|
3475
3494
|
return build_dialog(shell, message, [{
|
|
3476
3495
|
label: confirm_label,
|
|
@@ -3482,9 +3501,9 @@ function yui_shell_confirm_danger(shell, message, opts) {
|
|
|
3482
3501
|
}], opts).then((v) => v === "confirm");
|
|
3483
3502
|
}
|
|
3484
3503
|
function yui_shell_confirm_yesnocancel(shell, message, opts) {
|
|
3485
|
-
let yes_label = opts && opts.yes_label ||
|
|
3486
|
-
let no_label = opts && opts.no_label ||
|
|
3487
|
-
let cancel_label = opts && opts.cancel_label ||
|
|
3504
|
+
let yes_label = opts && opts.yes_label || DEFAULT_LABEL.yes.i18n;
|
|
3505
|
+
let no_label = opts && opts.no_label || DEFAULT_LABEL.no.i18n;
|
|
3506
|
+
let cancel_label = opts && opts.cancel_label || DEFAULT_LABEL.cancel.i18n;
|
|
3488
3507
|
return build_dialog(shell, message, [
|
|
3489
3508
|
{
|
|
3490
3509
|
label: yes_label,
|
|
@@ -7420,7 +7439,7 @@ function json_diff_rows(doc_a, doc_b) {
|
|
|
7420
7439
|
* A Date -> the value of a `datetime-local` input, in local time,
|
|
7421
7440
|
* WITH seconds: the input is built with `step: 1`, which is what
|
|
7422
7441
|
* lets it hold them. Written as "YYYY-MM-DDTHH:mm" it lost them on
|
|
7423
|
-
* every save of any field
|
|
7442
|
+
* every save of any field.
|
|
7424
7443
|
************************************************************/
|
|
7425
7444
|
function date_to_datetime_local(value) {
|
|
7426
7445
|
if (!(value instanceof Date) || isNaN(value.getTime())) return "";
|
|
@@ -23125,8 +23144,7 @@ function yui_shell_show_route_map(shell, opts) {
|
|
|
23125
23144
|
* answers, so the host has to answer EVERY way a write ends --
|
|
23126
23145
|
* and the transport closing under a write in flight, or a
|
|
23127
23146
|
* command that never left because there was no session, are two
|
|
23128
|
-
* of them that nobody answered: the form stayed busy for ever
|
|
23129
|
-
* (N8 of the 2026-09-22 review).
|
|
23147
|
+
* of them that nobody answered: the form stayed busy for ever.
|
|
23130
23148
|
*
|
|
23131
23149
|
* A write is known by its TOPIC and its `form_write` serial,
|
|
23132
23150
|
* never by the serial alone: each topic has its own form, and
|
|
@@ -25677,8 +25695,8 @@ function ac_update_record(gobj, event, kw, src) {
|
|
|
25677
25695
|
/************************************************************
|
|
25678
25696
|
* A write sent with no session never leaves: C_IEVENT_CLI logs
|
|
25679
25697
|
* "Not in session" and answers null, as it does when the command
|
|
25680
|
-
* went -- so a form waiting for the answer waited for ever
|
|
25681
|
-
*
|
|
25698
|
+
* went -- so a form waiting for the answer waited for ever. Refused
|
|
25699
|
+
* before it is sent, aloud.
|
|
25682
25700
|
************************************************************/
|
|
25683
25701
|
function refuse_without_session(gobj, topic_name) {
|
|
25684
25702
|
if (is_connected$1(gobj)) return false;
|
|
@@ -26790,9 +26808,8 @@ function delete_impact(desc, records) {
|
|
|
26790
26808
|
* that back. So a second busy(true) while already busy must not
|
|
26791
26809
|
* run at all -- it read the buttons it had just disabled as
|
|
26792
26810
|
* "disabled on their own", and the busy(false) of a refused write
|
|
26793
|
-
* left Save and Cancel dead (
|
|
26794
|
-
*
|
|
26795
|
-
* the write). The caller owns `state` ({busy}) for the form's life.
|
|
26811
|
+
* left Save and Cancel dead (a record with a picked file went
|
|
26812
|
+
* busy for the read, then busy again for the write). The caller owns `state` ({busy}) for the form's life.
|
|
26796
26813
|
*
|
|
26797
26814
|
* Copyright (c) 2026, ArtGins.
|
|
26798
26815
|
* All Rights Reserved.
|
|
@@ -26830,7 +26847,7 @@ function set_toolbar_busy($container, state, busy) {
|
|
|
26830
26847
|
* (only its own `plaintext` formatter escapes), so a record's text
|
|
26831
26848
|
* returned as a string is parsed as markup: `a<b and c>d` showed
|
|
26832
26849
|
* as `ad`, and a field holding `<img src=x onerror=...>` ran it in
|
|
26833
|
-
* the operator's browser
|
|
26850
|
+
* the operator's browser. Record
|
|
26834
26851
|
* data goes in as a TEXT NODE, and a cell built around it as DOM,
|
|
26835
26852
|
* never as an HTML string with the data interpolated.
|
|
26836
26853
|
*
|
|
@@ -29592,7 +29609,7 @@ function ac_new_row(gobj, event, kw, src) {
|
|
|
29592
29609
|
* What crosses the confirmation is the rows' IDENTITY, never their
|
|
29593
29610
|
* position: the view applies EV_TREEDB_NODE_* of every writer while the
|
|
29594
29611
|
* dialog is open, and a position names whatever row sits there when the
|
|
29595
|
-
* person answers -- with force:true
|
|
29612
|
+
* person answers -- with force:true.
|
|
29596
29613
|
************************************************************/
|
|
29597
29614
|
function ac_delete_rows(gobj, event, kw, src) {
|
|
29598
29615
|
if (refuse_if_readonly$2(gobj, event)) return -1;
|
|
@@ -29803,8 +29820,8 @@ function ac_files_read(gobj, event, kw, src) {
|
|
|
29803
29820
|
* open and BUSY, and the serial it waits for is taken here.
|
|
29804
29821
|
*
|
|
29805
29822
|
* A refused write used to close the form all the same and throw
|
|
29806
|
-
* away what was typed, although the README said it stayed open
|
|
29807
|
-
*
|
|
29823
|
+
* away what was typed, although the README said it stayed open:
|
|
29824
|
+
* the close was posted with the
|
|
29808
29825
|
* publish, before the backend had answered anything.
|
|
29809
29826
|
************************************************************/
|
|
29810
29827
|
function wait_for_write_answer(gobj) {
|
|
@@ -30976,6 +30993,28 @@ function apply_graphs_echo(live, saved, records, rec) {
|
|
|
30976
30993
|
delete saved[rec.topic];
|
|
30977
30994
|
}
|
|
30978
30995
|
}
|
|
30996
|
+
/***************************************************************
|
|
30997
|
+
* A write of one topic's arrangement did NOT land: the backend
|
|
30998
|
+
* refused it, the transport refused it on the way out, or there
|
|
30999
|
+
* was no session to send it on.
|
|
31000
|
+
*
|
|
31001
|
+
* saved {topic_name: properties} what the backend has
|
|
31002
|
+
* topic_name the topic of the refused record
|
|
31003
|
+
*
|
|
31004
|
+
* The view records a write as saved BEFORE it leaves, because
|
|
31005
|
+
* nothing answers it when it lands. So a refused one left the view
|
|
31006
|
+
* believing the backend held an arrangement it does not, and the
|
|
31007
|
+
* next Save found nothing to write. What the backend holds for the
|
|
31008
|
+
* topic is not known any more, so it is forgotten: the next Save
|
|
31009
|
+
* writes the topic again (plan_graph_saves()).
|
|
31010
|
+
*
|
|
31011
|
+
* Returns TRUE if there was something to forget.
|
|
31012
|
+
***************************************************************/
|
|
31013
|
+
function forget_refused_graphs_write(saved, topic_name) {
|
|
31014
|
+
if (!is_object(saved) || !topic_name || !Object.prototype.hasOwnProperty.call(saved, topic_name)) return false;
|
|
31015
|
+
delete saved[topic_name];
|
|
31016
|
+
return true;
|
|
31017
|
+
}
|
|
30979
31018
|
//#endregion
|
|
30980
31019
|
//#region src/treedb_node_label.js
|
|
30981
31020
|
/***********************************************************************
|
|
@@ -59208,6 +59247,7 @@ var PRIVATE_DATA$4 = {
|
|
|
59208
59247
|
__graphs__: [],
|
|
59209
59248
|
_graph_properties: {},
|
|
59210
59249
|
_saved_graph_properties: {},
|
|
59250
|
+
_graphs_writes_owed: {},
|
|
59211
59251
|
yet_showed: false,
|
|
59212
59252
|
edit_mode: false,
|
|
59213
59253
|
operation_mode: null,
|
|
@@ -60903,6 +60943,7 @@ function save_topic_graph_properties(gobj, topic_name) {
|
|
|
60903
60943
|
if (!topic_arrangement_changed(properties, priv._saved_graph_properties[topic_name])) return;
|
|
60904
60944
|
properties.__origin__ = gobj_read_str_attr(gobj_yuno(), "node_uuid");
|
|
60905
60945
|
priv._saved_graph_properties[topic_name] = json_deep_copy(properties);
|
|
60946
|
+
delete priv._graphs_writes_owed[topic_name];
|
|
60906
60947
|
let kw_update = {
|
|
60907
60948
|
treedb_name: priv.treedb_name,
|
|
60908
60949
|
topic_name: "__graphs__",
|
|
@@ -60984,8 +61025,13 @@ function update_history_buttons(gobj) {
|
|
|
60984
61025
|
} else {
|
|
60985
61026
|
disableElements($container, ".EV_HISTORY_UNDO");
|
|
60986
61027
|
set_active_state($container, ".EV_HISTORY_UNDO", false);
|
|
60987
|
-
|
|
60988
|
-
|
|
61028
|
+
if (graphs_write_is_owed(gobj)) {
|
|
61029
|
+
enableElements($container, ".EV_SAVE_GRAPH");
|
|
61030
|
+
set_submit_state($container, ".EV_SAVE_GRAPH", true);
|
|
61031
|
+
} else {
|
|
61032
|
+
disableElements($container, ".EV_SAVE_GRAPH");
|
|
61033
|
+
set_submit_state($container, ".EV_SAVE_GRAPH", false);
|
|
61034
|
+
}
|
|
60989
61035
|
}
|
|
60990
61036
|
} else {
|
|
60991
61037
|
disableElements($container, ".EV_HISTORY_REDO");
|
|
@@ -61942,6 +61988,15 @@ function run_preview_undo(gobj, key) {
|
|
|
61942
61988
|
log_error(`${gobj_short_name(gobj)}: cannot undo the preview: ${e}`);
|
|
61943
61989
|
}
|
|
61944
61990
|
}
|
|
61991
|
+
/************************************************************
|
|
61992
|
+
* Is a `__graphs__` write owed? The host said one did not land
|
|
61993
|
+
* (EV_GRAPHS_WRITE_REFUSED), and no Save has written that topic
|
|
61994
|
+
* since. The Save button stays lit while it is: the history
|
|
61995
|
+
* knows nothing of it, so `canUndo()` alone would put it out.
|
|
61996
|
+
************************************************************/
|
|
61997
|
+
function graphs_write_is_owed(gobj) {
|
|
61998
|
+
return Object.keys(gobj.priv._graphs_writes_owed).length > 0;
|
|
61999
|
+
}
|
|
61945
62000
|
function mark_graph_dirty(gobj) {
|
|
61946
62001
|
let $container = gobj_read_attr(gobj, "$container");
|
|
61947
62002
|
enableElements($container, ".EV_SAVE_GRAPH");
|
|
@@ -65682,6 +65737,7 @@ function ac_clear_data(gobj, event, kw, src) {
|
|
|
65682
65737
|
deselect_node(gobj);
|
|
65683
65738
|
hide_create_popover(gobj);
|
|
65684
65739
|
gobj_write_attr(gobj, "records", {});
|
|
65740
|
+
priv._graphs_writes_owed = {};
|
|
65685
65741
|
priv._nodes_total = 0;
|
|
65686
65742
|
priv._nodes_placed = 0;
|
|
65687
65743
|
priv._fold_model = null;
|
|
@@ -65774,15 +65830,36 @@ function ac_save_graph(gobj, event, kw, src) {
|
|
|
65774
65830
|
disableElements($container, ".EV_SAVE_GRAPH");
|
|
65775
65831
|
set_submit_state($container, ".EV_SAVE_GRAPH", false);
|
|
65776
65832
|
let history = graph_get_plugin(gobj, "history");
|
|
65777
|
-
if (history)
|
|
65778
|
-
history.clear();
|
|
65779
|
-
update_history_buttons(gobj);
|
|
65780
|
-
}
|
|
65833
|
+
if (history) history.clear();
|
|
65781
65834
|
save_geometry(gobj);
|
|
65835
|
+
update_history_buttons(gobj);
|
|
65782
65836
|
}
|
|
65783
65837
|
return 0;
|
|
65784
65838
|
}
|
|
65785
65839
|
/************************************************************
|
|
65840
|
+
* The host says a write of `__graphs__` did not land: the
|
|
65841
|
+
* backend refused it, the transport refused it on the way out,
|
|
65842
|
+
* or there was no session to send it on. kw: {topic}
|
|
65843
|
+
*
|
|
65844
|
+
* save_topic_graph_properties() recorded it as saved before it
|
|
65845
|
+
* left. Forgotten now, and the Save is lit again: the next Save
|
|
65846
|
+
* writes the topic again. Before, the view believed the backend
|
|
65847
|
+
* held the arrangement, and the next Save had nothing to write.
|
|
65848
|
+
************************************************************/
|
|
65849
|
+
function ac_graphs_write_refused(gobj, event, kw, src) {
|
|
65850
|
+
let priv = gobj.priv;
|
|
65851
|
+
let topic_name = kw && kw.topic;
|
|
65852
|
+
if (!topic_name) {
|
|
65853
|
+
log_error(`${gobj_short_name(gobj)}: ${event} names no topic`);
|
|
65854
|
+
return -1;
|
|
65855
|
+
}
|
|
65856
|
+
log_warning(`${gobj_short_name(gobj)}: the arrangement of '${topic_name}' was not saved: the next Save writes it again`);
|
|
65857
|
+
forget_refused_graphs_write(priv._saved_graph_properties, topic_name);
|
|
65858
|
+
priv._graphs_writes_owed[topic_name] = true;
|
|
65859
|
+
if (priv.edit_mode) mark_graph_dirty(gobj);
|
|
65860
|
+
return 0;
|
|
65861
|
+
}
|
|
65862
|
+
/************************************************************
|
|
65786
65863
|
* Node created, from subscription
|
|
65787
65864
|
************************************************************/
|
|
65788
65865
|
function ac_node_created(gobj, event, kw, src) {
|
|
@@ -66850,6 +66927,11 @@ function create_gclass$4(gclass_name) {
|
|
|
66850
66927
|
ac_node_deleted,
|
|
66851
66928
|
null
|
|
66852
66929
|
],
|
|
66930
|
+
[
|
|
66931
|
+
"EV_GRAPHS_WRITE_REFUSED",
|
|
66932
|
+
ac_graphs_write_refused,
|
|
66933
|
+
null
|
|
66934
|
+
],
|
|
66853
66935
|
[
|
|
66854
66936
|
"EV_NODE_CLICK",
|
|
66855
66937
|
ac_node_click$2,
|
|
@@ -67103,6 +67185,7 @@ function create_gclass$4(gclass_name) {
|
|
|
67103
67185
|
["EV_NODE_CREATED", 0],
|
|
67104
67186
|
["EV_NODE_UPDATED", 0],
|
|
67105
67187
|
["EV_NODE_DELETED", 0],
|
|
67188
|
+
["EV_GRAPHS_WRITE_REFUSED", 0],
|
|
67106
67189
|
["EV_NODE_CLICK", 0],
|
|
67107
67190
|
["EV_EDGE_CLICK", 0],
|
|
67108
67191
|
["EV_NODE_CONTEXT_MENU", 0],
|
|
@@ -68279,7 +68362,7 @@ function treedb_update_node(gobj, treedb_name, topic_name, record, options) {
|
|
|
68279
68362
|
let priv = gobj.priv;
|
|
68280
68363
|
if (!priv.gobj_remote_yuno) {
|
|
68281
68364
|
log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
|
|
68282
|
-
return;
|
|
68365
|
+
return -1;
|
|
68283
68366
|
}
|
|
68284
68367
|
let command = "update-node";
|
|
68285
68368
|
let kw = {
|
|
@@ -68291,7 +68374,11 @@ function treedb_update_node(gobj, treedb_name, topic_name, record, options) {
|
|
|
68291
68374
|
};
|
|
68292
68375
|
kw.__md_command__ = { topic_name };
|
|
68293
68376
|
let ret = gobj_command(priv.gobj_remote_yuno, command, kw, gobj);
|
|
68294
|
-
if (ret)
|
|
68377
|
+
if (ret) {
|
|
68378
|
+
log_error(ret);
|
|
68379
|
+
return -1;
|
|
68380
|
+
}
|
|
68381
|
+
return 0;
|
|
68295
68382
|
}
|
|
68296
68383
|
/************************************************************
|
|
68297
68384
|
* Command to remote service
|
|
@@ -68564,6 +68651,7 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
|
|
|
68564
68651
|
case "delete-node":
|
|
68565
68652
|
case "link-nodes":
|
|
68566
68653
|
case "unlink-nodes":
|
|
68654
|
+
if (result < 0 && command === "update-node") graphs_write_refused(gobj, kw_get_str(gobj, kw_command, "topic_name", "", 0), kw_get_dict(gobj, kw_command, "record", {}, 0));
|
|
68567
68655
|
if (result >= 0 && kw_get_str(gobj, kw_command, "topic_name", "", 0) !== "__graphs__") gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
|
|
68568
68656
|
treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
|
|
68569
68657
|
topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
|
|
@@ -68807,12 +68895,40 @@ function ac_create_node(gobj, event, kw, src) {
|
|
|
68807
68895
|
* Send to backend
|
|
68808
68896
|
********************************************/
|
|
68809
68897
|
function ac_update_node(gobj, event, kw, src) {
|
|
68810
|
-
let
|
|
68811
|
-
if (refuse_if_readonly$1(gobj, event)) return -1;
|
|
68812
|
-
let treedb_name = priv.treedb_name;
|
|
68898
|
+
let treedb_name = gobj.priv.treedb_name;
|
|
68813
68899
|
let topic_name = kw.topic_name;
|
|
68814
68900
|
let record = kw.record;
|
|
68815
|
-
|
|
68901
|
+
let options = kw.options || {};
|
|
68902
|
+
if (refuse_if_readonly$1(gobj, event)) {
|
|
68903
|
+
graphs_write_refused(gobj, topic_name, record);
|
|
68904
|
+
return -1;
|
|
68905
|
+
}
|
|
68906
|
+
if (topic_name === "__graphs__" && !is_connected(gobj)) {
|
|
68907
|
+
log_warning(`${gobj_short_name(gobj)}: no session, the arrangement was not sent`);
|
|
68908
|
+
graphs_write_refused(gobj, topic_name, record);
|
|
68909
|
+
return -1;
|
|
68910
|
+
}
|
|
68911
|
+
let ret = treedb_update_node(gobj, treedb_name, topic_name, record, options);
|
|
68912
|
+
if (ret < 0) graphs_write_refused(gobj, topic_name, record);
|
|
68913
|
+
return ret;
|
|
68914
|
+
}
|
|
68915
|
+
/************************************************************
|
|
68916
|
+
* A write of `__graphs__` did not land. The engine
|
|
68917
|
+
* (C_G6_NODES_TREE) took it for granted when it left -- nothing
|
|
68918
|
+
* answers it when it lands -- so it is told, and its next Save
|
|
68919
|
+
* writes the topic again. Other topics are the operator's data:
|
|
68920
|
+
* the answer's error says what happened to them.
|
|
68921
|
+
************************************************************/
|
|
68922
|
+
function graphs_write_refused(gobj, topic_name, record) {
|
|
68923
|
+
let priv = gobj.priv;
|
|
68924
|
+
if (topic_name !== "__graphs__") return;
|
|
68925
|
+
let topic = record && typeof record === "object" ? record.topic || record.id : "";
|
|
68926
|
+
if (!topic) {
|
|
68927
|
+
log_error(`${gobj_short_name(gobj)}: a refused __graphs__ write names no topic`);
|
|
68928
|
+
return;
|
|
68929
|
+
}
|
|
68930
|
+
if (!priv.gobj_nodes_tree) return;
|
|
68931
|
+
gobj_send_event(priv.gobj_nodes_tree, "EV_GRAPHS_WRITE_REFUSED", { topic }, gobj);
|
|
68816
68932
|
}
|
|
68817
68933
|
/********************************************
|
|
68818
68934
|
* Message from G6_nodes_tree
|
|
@@ -70468,9 +70584,8 @@ function register_c_yui_treedb_schema() {
|
|
|
70468
70584
|
* it, and that mark lived in the memory of the session only:
|
|
70469
70585
|
* a reload of the page, a reconnect or a refresh of the model
|
|
70470
70586
|
* lost the chip, the banner and the export warning while
|
|
70471
|
-
* __system__ still differed from the file in use
|
|
70472
|
-
*
|
|
70473
|
-
* C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
|
|
70587
|
+
* __system__ still differed from the file in use. The host
|
|
70588
|
+
* reads that difference from C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
|
|
70474
70589
|
* hands it here; the editor applies it to its model, in place
|
|
70475
70590
|
* of what the host said before.
|
|
70476
70591
|
*
|
|
@@ -72138,11 +72253,10 @@ function write_options(op) {
|
|
|
72138
72253
|
* AN EDIT IS A DRAFT. A write here moves no version and reaches no
|
|
72139
72254
|
* treedb: the host SAVES it (C_TREEDB's save-schema raises the
|
|
72140
72255
|
* topic_version of what changed and the schema_version, once) and
|
|
72141
|
-
* APPLIES it (apply-schema + a restart of the owning yuno).
|
|
72142
|
-
*
|
|
72143
|
-
*
|
|
72144
|
-
*
|
|
72145
|
-
* wrote and has not saved (EV_REFRESH forgets it: the view's own
|
|
72256
|
+
* APPLIES it (apply-schema + a restart of the owning yuno). If a
|
|
72257
|
+
* write here raised both versions, an edit half made would already
|
|
72258
|
+
* be the schema of the next start. The topic list marks what this
|
|
72259
|
+
* session wrote and has not saved (EV_REFRESH forgets it: the view's own
|
|
72146
72260
|
* Refresh button sends it, and a host may after a save -- the
|
|
72147
72261
|
* agent console's does not) and what the host says is not saved
|
|
72148
72262
|
* (EV_DRAFTS, replaced whole each time it is sent).
|
|
@@ -72208,6 +72322,7 @@ var HOOK_TOPICS = "topics";
|
|
|
72208
72322
|
var HOOK_COLS = "cols";
|
|
72209
72323
|
var DIAGRAM_SEG = "diagram";
|
|
72210
72324
|
var PURPOSE = "schema_editor";
|
|
72325
|
+
var DIALOGS_THAT_READ_NOTHING = ["SCHEMA_EXPORT_DIALOG", "SCHEMA_REPORT_DIALOG"];
|
|
72211
72326
|
/***************************************************************
|
|
72212
72327
|
* Attrs
|
|
72213
72328
|
***************************************************************/
|
|
@@ -72240,6 +72355,7 @@ var PRIVATE_DATA$1 = {
|
|
|
72240
72355
|
diagram_gobj: null,
|
|
72241
72356
|
dialog: null,
|
|
72242
72357
|
dialog_gen: 0,
|
|
72358
|
+
dialog_kind: "",
|
|
72243
72359
|
save_queue: null,
|
|
72244
72360
|
save_return: "",
|
|
72245
72361
|
save_wrote: false,
|
|
@@ -72249,6 +72365,8 @@ var PRIVATE_DATA$1 = {
|
|
|
72249
72365
|
dropped_round: 0,
|
|
72250
72366
|
dropped_write: 0,
|
|
72251
72367
|
reload_after_write: false,
|
|
72368
|
+
refresh_after_write: false,
|
|
72369
|
+
reopen_form: null,
|
|
72252
72370
|
pending_seg: null,
|
|
72253
72371
|
import_plan: null,
|
|
72254
72372
|
import_pane: null,
|
|
@@ -72408,9 +72526,9 @@ function refuse_if_readonly(gobj, event) {
|
|
|
72408
72526
|
* dumped by the machine trace).
|
|
72409
72527
|
*
|
|
72410
72528
|
* Refused, and said. A column form sends every field it shows, so
|
|
72411
|
-
* its Save after a reload wrote the OLD record over the newer one
|
|
72412
|
-
*
|
|
72413
|
-
*
|
|
72529
|
+
* its Save after a reload wrote the OLD record over the newer one;
|
|
72530
|
+
* a form whose column the reload removed answered "names no column"
|
|
72531
|
+
* for ever. end_load()
|
|
72414
72532
|
* closes such a dialog when the load lands; this is the same rule
|
|
72415
72533
|
* for whatever arrives anyway -- a stale screen, a confirmation
|
|
72416
72534
|
* that was up, a keyboard.
|
|
@@ -72430,8 +72548,7 @@ function built_on_a_replaced_model(gobj, event, kw) {
|
|
|
72430
72548
|
* the model shown is kept, and it may be older than the store.
|
|
72431
72549
|
* The session is up, so no reconnect will ask for it again -- it
|
|
72432
72550
|
* was owed for as long as the session lasted, and a form opened
|
|
72433
|
-
* on that model wrote every field of it over the newer record
|
|
72434
|
-
* (fifth independent review; A3 of the fourth, by another path).
|
|
72551
|
+
* on that model wrote every field of it over the newer record.
|
|
72435
72552
|
*
|
|
72436
72553
|
* So the operator's NEXT action asks it. An edit -- a form, a
|
|
72437
72554
|
* write, a confirmation, what reads the model whole (check,
|
|
@@ -72455,14 +72572,61 @@ function owes_reload(gobj) {
|
|
|
72455
72572
|
let state = gobj_current_state(gobj);
|
|
72456
72573
|
return gobj.priv.reload_on_open && transport_in_session(gobj) && state !== "ST_LOADING" && state !== "ST_SAVING";
|
|
72457
72574
|
}
|
|
72458
|
-
function reload_first(gobj, event) {
|
|
72575
|
+
function reload_first(gobj, event, reopen) {
|
|
72576
|
+
let priv = gobj.priv;
|
|
72459
72577
|
if (!owes_reload(gobj)) return false;
|
|
72460
|
-
|
|
72578
|
+
priv.reload_on_open = false;
|
|
72579
|
+
if (reopen && priv.dialog) {
|
|
72580
|
+
priv.reopen_form = Object.assign(json_deep_copy(reopen), {
|
|
72581
|
+
dialog: priv.dialog,
|
|
72582
|
+
treedb_id: priv.treedb_id
|
|
72583
|
+
});
|
|
72584
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas shown may be out of date: they are read again first, and the form opens again on them with its changes`);
|
|
72585
|
+
yui_shell_show_error(yui_shell_of(gobj), "the schemas shown may be out of date: they are read again, and the form opens again on them with your changes", { t });
|
|
72586
|
+
request_model(gobj, true);
|
|
72587
|
+
return true;
|
|
72588
|
+
}
|
|
72461
72589
|
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas shown may be out of date: they are read again first`);
|
|
72462
72590
|
yui_shell_show_error(yui_shell_of(gobj), "the schemas shown may be out of date: they are read again, try again when they are in", { t });
|
|
72463
72591
|
request_model(gobj, true);
|
|
72464
72592
|
return true;
|
|
72465
72593
|
}
|
|
72594
|
+
/***************************************************************
|
|
72595
|
+
* The form reload_first() took down, opened again on the model
|
|
72596
|
+
* the load just made, with the operator's changes on top.
|
|
72597
|
+
*
|
|
72598
|
+
* Only where it can mean the same thing: on the treedb it was
|
|
72599
|
+
* opened on, on the screen whose Save it has, and on a record
|
|
72600
|
+
* that is still there. Otherwise it is not opened, and that is
|
|
72601
|
+
* said -- a form opened on another treedb, or on nothing, would
|
|
72602
|
+
* save somewhere the operator never meant.
|
|
72603
|
+
***************************************************************/
|
|
72604
|
+
function reopen_form(gobj, r) {
|
|
72605
|
+
let priv = gobj.priv;
|
|
72606
|
+
let state = gobj_current_state(gobj);
|
|
72607
|
+
let treedb = current_treedb(gobj);
|
|
72608
|
+
let topic = null;
|
|
72609
|
+
let col = null;
|
|
72610
|
+
let fits = false;
|
|
72611
|
+
if (treedb && r.treedb_id === priv.treedb_id) {
|
|
72612
|
+
if (r.form === "column" && state === "ST_COLUMNS") {
|
|
72613
|
+
topic = find_topic(priv.model, priv.treedb_id, r.topic);
|
|
72614
|
+
col = topic && !r.creating ? find_col(priv.model, priv.treedb_id, r.topic, r.col) : null;
|
|
72615
|
+
fits = !!topic && (r.creating || !!col);
|
|
72616
|
+
} else if (r.form === "topic" && state === "ST_TOPICS") {
|
|
72617
|
+
topic = r.creating ? null : find_topic(priv.model, priv.treedb_id, r.topic);
|
|
72618
|
+
fits = r.creating || !!topic;
|
|
72619
|
+
}
|
|
72620
|
+
}
|
|
72621
|
+
if (!fits) {
|
|
72622
|
+
log_warning(`${gobj_short_name(gobj)}: the ${r.form} form was not opened again: what it edited is not on the schemas the reload read (${r.treedb_id} ${r.topic}${r.col ? "." + r.col : ""}, now ${state})`);
|
|
72623
|
+
yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again and what the form was editing is not there any more", { t });
|
|
72624
|
+
return -1;
|
|
72625
|
+
}
|
|
72626
|
+
if (r.form === "column") open_column_form(gobj, topic, col, null, r.changed);
|
|
72627
|
+
else open_topic_form(gobj, treedb, topic, r.changed);
|
|
72628
|
+
return 0;
|
|
72629
|
+
}
|
|
72466
72630
|
function reload_after_move(gobj) {
|
|
72467
72631
|
if (!owes_reload(gobj)) return 0;
|
|
72468
72632
|
gobj.priv.reload_on_open = false;
|
|
@@ -72481,7 +72645,7 @@ function reload_after_move(gobj) {
|
|
|
72481
72645
|
* none of the actions of a screen, so the screen it replaces goes
|
|
72482
72646
|
* and the toolbar stops answering: left up, a click on a card, on
|
|
72483
72647
|
* Back or on the drawing answered "Event NOT DEFINED in state
|
|
72484
|
-
* ST_LOADING" and was lost
|
|
72648
|
+
* ST_LOADING" and was lost. A dialog
|
|
72485
72649
|
* left open is answered in ST_LOADING instead (ac_wait_for_the_load),
|
|
72486
72650
|
* and a load that LANDS closes it (end_load()): it was built on the
|
|
72487
72651
|
* model the load replaced, and its Save sends every field it shows.
|
|
@@ -72608,7 +72772,7 @@ function build_model(gobj) {
|
|
|
72608
72772
|
* measurement of "changed since" can be measured against.
|
|
72609
72773
|
*
|
|
72610
72774
|
* `written` is forgotten only by an EV_REFRESH (the view's own
|
|
72611
|
-
* Refresh button, or a host that sends one). Since
|
|
72775
|
+
* Refresh button, or a host that sends one). Since 7.23.196 a write moves no
|
|
72612
72776
|
* version, so `written` is the draft chip of what this session
|
|
72613
72777
|
* wrote and no longer half of a version measurement: a reload of
|
|
72614
72778
|
* the reconnect, or of a write that turned out done, forgot the
|
|
@@ -73701,6 +73865,7 @@ function close_dialog(gobj) {
|
|
|
73701
73865
|
if (!priv.dialog) return;
|
|
73702
73866
|
let dialog = priv.dialog;
|
|
73703
73867
|
priv.dialog = null;
|
|
73868
|
+
priv.dialog_kind = "";
|
|
73704
73869
|
priv.export_view = null;
|
|
73705
73870
|
try {
|
|
73706
73871
|
dialog.close();
|
|
@@ -73712,6 +73877,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix) {
|
|
|
73712
73877
|
let priv = gobj.priv;
|
|
73713
73878
|
close_dialog(gobj);
|
|
73714
73879
|
priv.dialog_gen = priv.model_gen;
|
|
73880
|
+
priv.dialog_kind = logical;
|
|
73715
73881
|
priv.dialog = yui_shell_show_modal(yui_shell_of(gobj), $content, {
|
|
73716
73882
|
dialog: true,
|
|
73717
73883
|
title,
|
|
@@ -73720,6 +73886,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix) {
|
|
|
73720
73886
|
t,
|
|
73721
73887
|
on_close: () => {
|
|
73722
73888
|
priv.dialog = null;
|
|
73889
|
+
priv.dialog_kind = "";
|
|
73723
73890
|
priv.export_view = null;
|
|
73724
73891
|
}
|
|
73725
73892
|
});
|
|
@@ -73883,6 +74050,55 @@ function read_form($root) {
|
|
|
73883
74050
|
return out;
|
|
73884
74051
|
}
|
|
73885
74052
|
/***************************************************************
|
|
74053
|
+
* A column form as its Save sends it: the fields and the flags.
|
|
74054
|
+
***************************************************************/
|
|
74055
|
+
function column_form_state($form) {
|
|
74056
|
+
let values = read_form($form);
|
|
74057
|
+
values.flag = read_flags($form);
|
|
74058
|
+
return values;
|
|
74059
|
+
}
|
|
74060
|
+
/***************************************************************
|
|
74061
|
+
* The fields the operator CHANGED: those whose value is not the
|
|
74062
|
+
* one the form was opened with.
|
|
74063
|
+
***************************************************************/
|
|
74064
|
+
function form_changes(initial, values) {
|
|
74065
|
+
let changed = {};
|
|
74066
|
+
for (let name of Object.keys(values)) if (JSON.stringify(values[name]) !== JSON.stringify(initial[name])) changed[name] = values[name];
|
|
74067
|
+
return changed;
|
|
74068
|
+
}
|
|
74069
|
+
/***************************************************************
|
|
74070
|
+
* Put changes back into a form just built (reopen_form()). The
|
|
74071
|
+
* flags are drawn again for the type the changes leave, holding
|
|
74072
|
+
* the flags they name, so none of them is hidden by the type.
|
|
74073
|
+
***************************************************************/
|
|
74074
|
+
function apply_form_changes(gobj, $form, changes) {
|
|
74075
|
+
for (let name of Object.keys(changes)) {
|
|
74076
|
+
if (name === "flag") continue;
|
|
74077
|
+
let $control = $form.querySelector(`[data-name="${name}"]`);
|
|
74078
|
+
if (!$control) {
|
|
74079
|
+
log_warning(`${gobj_short_name(gobj)}: a change of '${name}' has no field in the form opened again: not put back`);
|
|
74080
|
+
continue;
|
|
74081
|
+
}
|
|
74082
|
+
if ($control.type === "checkbox") $control.checked = !!changes[name];
|
|
74083
|
+
else $control.value = changes[name];
|
|
74084
|
+
}
|
|
74085
|
+
let $type = $form.querySelector("[data-name=\"type\"]");
|
|
74086
|
+
if ($type && $form.querySelector(".SCHEMA_FLAGS")) {
|
|
74087
|
+
let flags = Array.isArray(changes.flag) ? changes.flag : read_flags($form);
|
|
74088
|
+
redraw_flags(gobj, $form, $type.value, flags);
|
|
74089
|
+
}
|
|
74090
|
+
}
|
|
74091
|
+
/***************************************************************
|
|
74092
|
+
* Draw the flags of a column form again, for `type`, with
|
|
74093
|
+
* `current` checked.
|
|
74094
|
+
***************************************************************/
|
|
74095
|
+
function redraw_flags(gobj, $form, type, current) {
|
|
74096
|
+
let $holder = $form.querySelector(".SCHEMA_FLAGS");
|
|
74097
|
+
let $new = createElement2(flags_field(gobj, type, current));
|
|
74098
|
+
$holder.parentNode.replaceChild($new, $holder);
|
|
74099
|
+
refresh_language($new, t);
|
|
74100
|
+
}
|
|
74101
|
+
/***************************************************************
|
|
73886
74102
|
* The flags of a column, as checkboxes that say what they do.
|
|
73887
74103
|
*
|
|
73888
74104
|
* Grouped the way they act, greyed when they are meaningless on
|
|
@@ -73956,7 +74172,7 @@ function read_flags($root) {
|
|
|
73956
74172
|
* Offering a field that silently does something else is worse
|
|
73957
74173
|
* than not offering it.
|
|
73958
74174
|
***************************************************************/
|
|
73959
|
-
function open_column_form(gobj, topic, col, prefill) {
|
|
74175
|
+
function open_column_form(gobj, topic, col, prefill, changes) {
|
|
73960
74176
|
let priv = gobj.priv;
|
|
73961
74177
|
let record = prefill || col && col.record || {};
|
|
73962
74178
|
let creating = !col;
|
|
@@ -74060,11 +74276,7 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
74060
74276
|
]);
|
|
74061
74277
|
let $type = $form.querySelector("[data-name=\"type\"]");
|
|
74062
74278
|
$type.addEventListener("change", () => {
|
|
74063
|
-
|
|
74064
|
-
let current = read_flags($form);
|
|
74065
|
-
let $new = createElement2(flags_field(gobj, $type.value, current));
|
|
74066
|
-
$holder.parentNode.replaceChild($new, $holder);
|
|
74067
|
-
refresh_language($new, t);
|
|
74279
|
+
redraw_flags(gobj, $form, $type.value, read_flags($form));
|
|
74068
74280
|
});
|
|
74069
74281
|
$form.addEventListener("change", (evt) => {
|
|
74070
74282
|
let $box = evt.target.closest(".SCHEMA_FLAG_BOX");
|
|
@@ -74076,15 +74288,17 @@ function open_column_form(gobj, topic, col, prefill) {
|
|
|
74076
74288
|
evt.stopPropagation();
|
|
74077
74289
|
close_dialog(gobj);
|
|
74078
74290
|
});
|
|
74291
|
+
let initial = column_form_state($form);
|
|
74292
|
+
if (changes) apply_form_changes(gobj, $form, changes);
|
|
74079
74293
|
$form.querySelector(".SCHEMA_COL_FORM_SAVE").addEventListener("click", (evt) => {
|
|
74080
74294
|
evt.stopPropagation();
|
|
74081
|
-
let values =
|
|
74082
|
-
values.flag = read_flags($form);
|
|
74295
|
+
let values = column_form_state($form);
|
|
74083
74296
|
gobj_send_event(gobj, "EV_SAVE_COLUMN", {
|
|
74084
74297
|
topic: topic.name,
|
|
74085
74298
|
col: creating ? "" : col.name,
|
|
74086
74299
|
creating,
|
|
74087
74300
|
values,
|
|
74301
|
+
changed: creating ? values : form_changes(initial, values),
|
|
74088
74302
|
model_gen
|
|
74089
74303
|
}, gobj);
|
|
74090
74304
|
});
|
|
@@ -74104,7 +74318,7 @@ function stringify_field(value) {
|
|
|
74104
74318
|
* actually has: a pkey naming no column is a topic that does
|
|
74105
74319
|
* not open, and it is the first thing the validator looks for.
|
|
74106
74320
|
***************************************************************/
|
|
74107
|
-
function open_topic_form(gobj, treedb, topic) {
|
|
74321
|
+
function open_topic_form(gobj, treedb, topic, changes) {
|
|
74108
74322
|
let record = topic && topic.record || {};
|
|
74109
74323
|
let creating = !topic;
|
|
74110
74324
|
let col_names = topic ? topic.cols.map((c) => c.name) : [];
|
|
@@ -74199,12 +74413,16 @@ function open_topic_form(gobj, treedb, topic) {
|
|
|
74199
74413
|
evt.stopPropagation();
|
|
74200
74414
|
close_dialog(gobj);
|
|
74201
74415
|
});
|
|
74416
|
+
let initial = read_form($form);
|
|
74417
|
+
if (changes) apply_form_changes(gobj, $form, changes);
|
|
74202
74418
|
$form.querySelector(".SCHEMA_TOPIC_FORM_SAVE").addEventListener("click", (evt) => {
|
|
74203
74419
|
evt.stopPropagation();
|
|
74420
|
+
let values = read_form($form);
|
|
74204
74421
|
gobj_send_event(gobj, "EV_SAVE_TOPIC", {
|
|
74205
74422
|
topic: creating ? "" : topic.name,
|
|
74206
74423
|
creating,
|
|
74207
|
-
values
|
|
74424
|
+
values,
|
|
74425
|
+
changed: creating ? values : form_changes(initial, values),
|
|
74208
74426
|
model_gen
|
|
74209
74427
|
}, gobj);
|
|
74210
74428
|
});
|
|
@@ -74914,11 +75132,11 @@ function transport_in_session(gobj) {
|
|
|
74914
75132
|
* The state is part of the question, and that is only true
|
|
74915
75133
|
* because NOTHING moves the editor out of ST_LOADING or ST_SAVING
|
|
74916
75134
|
* but the end of what is in flight: a position the host sends
|
|
74917
|
-
* meanwhile waits in `pending_seg` (ac_show()).
|
|
74918
|
-
*
|
|
74919
|
-
*
|
|
74920
|
-
*
|
|
74921
|
-
*
|
|
75135
|
+
* meanwhile waits in `pending_seg` (ac_show()). If EV_SHOW moved a
|
|
75136
|
+
* RELOAD out of ST_LOADING, every answer of it would be "for a load
|
|
75137
|
+
* that is over": the records it had emptied would stay empty, and
|
|
75138
|
+
* the next write would patch them into a model with no treedb at
|
|
75139
|
+
* all.
|
|
74922
75140
|
***************************************************************/
|
|
74923
75141
|
function is_current_load(gobj, md) {
|
|
74924
75142
|
return gobj_current_state(gobj) === "ST_LOADING" && kw_get_int(gobj, md, "round", 0, 0) === gobj.priv.load_round;
|
|
@@ -74974,20 +75192,28 @@ function transport_dropped(gobj) {
|
|
|
74974
75192
|
function end_load(gobj) {
|
|
74975
75193
|
let priv = gobj.priv;
|
|
74976
75194
|
let seg = priv.pending_seg;
|
|
75195
|
+
let reopen = priv.reopen_form;
|
|
74977
75196
|
priv.pending_seg = null;
|
|
75197
|
+
priv.reopen_form = null;
|
|
74978
75198
|
if (priv.dialog && priv.dialog_gen !== priv.model_gen) {
|
|
74979
|
-
|
|
75199
|
+
if (!reopen || reopen.dialog !== priv.dialog) {
|
|
75200
|
+
reopen = null;
|
|
75201
|
+
log_warning(`${gobj_short_name(gobj)}: a dialog was open on the schemas the load replaced: closed`);
|
|
75202
|
+
yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t });
|
|
75203
|
+
}
|
|
74980
75204
|
close_dialog(gobj);
|
|
74981
|
-
|
|
74982
|
-
|
|
75205
|
+
} else reopen = null;
|
|
75206
|
+
let ret = 0;
|
|
74983
75207
|
if (priv.model) {
|
|
74984
|
-
if (seg !== null)
|
|
74985
|
-
|
|
75208
|
+
if (seg !== null) ret = apply_seg(gobj, seg);
|
|
75209
|
+
else ret = go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
|
|
75210
|
+
} else {
|
|
75211
|
+
if (seg !== null) adopt_seg(gobj, seg);
|
|
75212
|
+
gobj_change_state(gobj, "ST_IDLE");
|
|
75213
|
+
render(gobj);
|
|
74986
75214
|
}
|
|
74987
|
-
if (
|
|
74988
|
-
|
|
74989
|
-
render(gobj);
|
|
74990
|
-
return 0;
|
|
75215
|
+
if (reopen) reopen_form(gobj, reopen);
|
|
75216
|
+
return ret;
|
|
74991
75217
|
}
|
|
74992
75218
|
/***************************************************************
|
|
74993
75219
|
* The queue is done, or gave up. Either way the model is what
|
|
@@ -75012,7 +75238,11 @@ function end_writes(gobj, error) {
|
|
|
75012
75238
|
render(gobj);
|
|
75013
75239
|
publish_check(gobj);
|
|
75014
75240
|
}
|
|
75015
|
-
|
|
75241
|
+
if (priv.refresh_after_write) {
|
|
75242
|
+
priv.refresh_after_write = false;
|
|
75243
|
+
priv.reload_after_write = false;
|
|
75244
|
+
request_model(gobj, false);
|
|
75245
|
+
} else reload_if_owed(gobj);
|
|
75016
75246
|
return error ? -1 : 0;
|
|
75017
75247
|
}
|
|
75018
75248
|
/***************************************************************
|
|
@@ -75124,10 +75354,59 @@ function position_seg(gobj) {
|
|
|
75124
75354
|
* ...and the same in reverse. A tail naming a treedb or a topic
|
|
75125
75355
|
* the store does not have lands one level up rather than on an
|
|
75126
75356
|
* empty screen: a shared link outlives the schema it points at.
|
|
75357
|
+
*
|
|
75358
|
+
* Every position the HOST sends comes through here, at once
|
|
75359
|
+
* (ac_show()) or when what was in flight ends (end_load(),
|
|
75360
|
+
* end_writes()). A move to another position closes the dialog of
|
|
75361
|
+
* the screen the editor left (close_dialog_left_behind()).
|
|
75127
75362
|
***************************************************************/
|
|
75128
75363
|
function apply_seg(gobj, seg) {
|
|
75364
|
+
let before = position_seg(gobj);
|
|
75129
75365
|
let pos = seg_position(seg);
|
|
75130
|
-
|
|
75366
|
+
let ret = go(gobj, pos.treedb_id, pos.topic_name, pos.diagram, false);
|
|
75367
|
+
if (position_seg(gobj) !== before) close_dialog_left_behind(gobj);
|
|
75368
|
+
return ret;
|
|
75369
|
+
}
|
|
75370
|
+
/***************************************************************
|
|
75371
|
+
* The host moved the editor while a dialog was up. C_YUI_SHELL
|
|
75372
|
+
* keeps the overlays open on a move that changes only the subpath
|
|
75373
|
+
* (a url typed in, a link), so the dialog outlives its screen.
|
|
75374
|
+
*
|
|
75375
|
+
* A form, the import and the orphans send events that only the
|
|
75376
|
+
* screen they were opened from declares, and they name their
|
|
75377
|
+
* topic under the treedb open at the time. Left up on another
|
|
75378
|
+
* screen, their Save answered "Event NOT DEFINED" and the edit was
|
|
75379
|
+
* lost with no word. So the dialog is closed and the operator told.
|
|
75380
|
+
* The import plan goes with its dialog: it was computed for the
|
|
75381
|
+
* treedb the editor left.
|
|
75382
|
+
*
|
|
75383
|
+
* The export and the check stay: they show the texts they were
|
|
75384
|
+
* opened with, and what they send is legal in every state.
|
|
75385
|
+
***************************************************************/
|
|
75386
|
+
function close_dialog_left_behind(gobj) {
|
|
75387
|
+
let priv = gobj.priv;
|
|
75388
|
+
if (!priv.dialog || DIALOGS_THAT_READ_NOTHING.includes(priv.dialog_kind)) return;
|
|
75389
|
+
log_warning(`${gobj_short_name(gobj)}: the host moved the view while the dialog ${priv.dialog_kind} was open: closed`);
|
|
75390
|
+
close_dialog(gobj);
|
|
75391
|
+
forget_import_plan(gobj);
|
|
75392
|
+
yui_shell_show_error(yui_shell_of(gobj), "the view moved: open the dialog again", { t });
|
|
75393
|
+
}
|
|
75394
|
+
/***************************************************************
|
|
75395
|
+
* A confirmation answered on another screen than the one it was
|
|
75396
|
+
* asked from. A confirmation is a shell modal, so a move sent by
|
|
75397
|
+
* the host does not close it. It names its topic and column, and
|
|
75398
|
+
* the actions look them up under the treedb open NOW: a Yes after
|
|
75399
|
+
* a move to another treedb deleted the topic or column of the same
|
|
75400
|
+
* name THERE.
|
|
75401
|
+
*
|
|
75402
|
+
* Refused, and said. A kw with no `seg` stamp is not a
|
|
75403
|
+
* confirmation's and is not asked.
|
|
75404
|
+
***************************************************************/
|
|
75405
|
+
function asked_on_another_screen(gobj, event, kw) {
|
|
75406
|
+
if (!kw || kw.seg === void 0 || kw.seg === position_seg(gobj)) return false;
|
|
75407
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, it was asked on '${kw.seg}' and the view is on '${position_seg(gobj)}'`);
|
|
75408
|
+
yui_shell_show_error(yui_shell_of(gobj), "the view moved: open the dialog again", { t });
|
|
75409
|
+
return true;
|
|
75131
75410
|
}
|
|
75132
75411
|
function seg_position(seg) {
|
|
75133
75412
|
let parts = String(seg || "").split("/").filter((x) => x.length > 0);
|
|
@@ -75244,7 +75523,9 @@ function ac_mt_command_answer(gobj, event, kw, src) {
|
|
|
75244
75523
|
priv.save_return = "";
|
|
75245
75524
|
priv.save_wrote = false;
|
|
75246
75525
|
gobj_publish_event(gobj, "EV_RECORD_WRITTEN", { treedb_name: gobj_read_str_attr(gobj, "treedb_name") });
|
|
75247
|
-
|
|
75526
|
+
let refresh = priv.refresh_after_write;
|
|
75527
|
+
priv.refresh_after_write = false;
|
|
75528
|
+
request_model(gobj, !refresh);
|
|
75248
75529
|
return 0;
|
|
75249
75530
|
}
|
|
75250
75531
|
}
|
|
@@ -75303,8 +75584,7 @@ function late_write_done(gobj, command, topic_name, data) {
|
|
|
75303
75584
|
* The fkey is read with parse_fkey_ref(), because the store answers
|
|
75304
75585
|
* in list_dict -- `[{topic_name, id, hook_name}]` -- and not in the
|
|
75305
75586
|
* `topics^<topic id>^cols` strings this view writes. Split as a
|
|
75306
|
-
* string, every production answer marked nothing
|
|
75307
|
-
* review, probe C).
|
|
75587
|
+
* string, every production answer marked nothing.
|
|
75308
75588
|
***************************************************************/
|
|
75309
75589
|
function mark_written_record(gobj, topic_name, record) {
|
|
75310
75590
|
let priv = gobj.priv;
|
|
@@ -75361,7 +75641,7 @@ function ac_hide(gobj, event, kw, src) {
|
|
|
75361
75641
|
*
|
|
75362
75642
|
* Nothing else ends a load or a write the drop cut: their answers
|
|
75363
75643
|
* will never come, and waiting in ST_LOADING skipped the reload
|
|
75364
|
-
* itself
|
|
75644
|
+
* itself.
|
|
75365
75645
|
***************************************************************/
|
|
75366
75646
|
function ac_transport_state(gobj, event, kw, src) {
|
|
75367
75647
|
let priv = gobj.priv;
|
|
@@ -75392,6 +75672,21 @@ function ac_refresh$1(gobj, event, kw, src) {
|
|
|
75392
75672
|
return request_model(gobj, false);
|
|
75393
75673
|
}
|
|
75394
75674
|
/***************************************************************
|
|
75675
|
+
* EV_REFRESH while writes are in flight: the view's own Refresh
|
|
75676
|
+
* is busy then, so it is a host's (after a Save, say).
|
|
75677
|
+
*
|
|
75678
|
+
* Run when the writes END (end_writes()), not now. Run now, the
|
|
75679
|
+
* load replaced ST_SAVING and what was still queued was never
|
|
75680
|
+
* sent, with no word: nothing but the end of what is in flight
|
|
75681
|
+
* moves this view out of ST_LOADING or ST_SAVING.
|
|
75682
|
+
***************************************************************/
|
|
75683
|
+
function ac_refresh_after_the_writes(gobj, event, kw, src) {
|
|
75684
|
+
let priv = gobj.priv;
|
|
75685
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} while writes are in flight: the schemas are read again when the writes end`);
|
|
75686
|
+
priv.refresh_after_write = true;
|
|
75687
|
+
return 0;
|
|
75688
|
+
}
|
|
75689
|
+
/***************************************************************
|
|
75395
75690
|
* What a DIALOG sends while the model is being read again: the
|
|
75396
75691
|
* Save of a form left open when the reload started, a
|
|
75397
75692
|
* confirmation, an import. The loading screen took the rest of
|
|
@@ -75400,12 +75695,11 @@ function ac_refresh$1(gobj, event, kw, src) {
|
|
|
75400
75695
|
* a model the answers in the air are about to replace.
|
|
75401
75696
|
*
|
|
75402
75697
|
* Refused, and SAID. Undeclared, it answered "Event NOT DEFINED
|
|
75403
|
-
* in state ST_LOADING" and the edit was gone with no word
|
|
75404
|
-
* independent review).
|
|
75698
|
+
* in state ST_LOADING" and the edit was gone with no word.
|
|
75405
75699
|
*
|
|
75406
75700
|
* And NOT "try again": the same Save, once the load was in, sent
|
|
75407
75701
|
* the form built on the old model -- every field of the old record
|
|
75408
|
-
* over the newer one
|
|
75702
|
+
* over the newer one. The load that
|
|
75409
75703
|
* lands closes such a dialog (end_load()); one that fails leaves it
|
|
75410
75704
|
* up, on the model it was built on.
|
|
75411
75705
|
***************************************************************/
|
|
@@ -75419,8 +75713,7 @@ function ac_wait_for_the_load(gobj, event, kw, src) {
|
|
|
75419
75713
|
* reload landed on zero treedbs while it was up) or ST_IDLE (no
|
|
75420
75714
|
* model). A confirmation is a shell modal, not this view's dialog,
|
|
75421
75715
|
* so the load that lands does not close it, and its Yes arrives
|
|
75422
|
-
* anyway. Undeclared, it answered "Event NOT DEFINED"
|
|
75423
|
-
* independent review).
|
|
75716
|
+
* anyway. Undeclared, it answered "Event NOT DEFINED".
|
|
75424
75717
|
*
|
|
75425
75718
|
* Refused with the stamp's own words: whatever it was decided on
|
|
75426
75719
|
* is not on screen any more.
|
|
@@ -75436,13 +75729,12 @@ function ac_confirmed_on_nothing(gobj, event, kw, src) {
|
|
|
75436
75729
|
* topics hold a draft in __system__ that is not saved (C_TREEDB's
|
|
75437
75730
|
* saved-schema, `draft_changed`). The mark of a write lived in this
|
|
75438
75731
|
* session's memory only, so a reload of the page, a reconnect or a
|
|
75439
|
-
* refresh showed no draft while __system__ still differed
|
|
75440
|
-
* the 2026-09-22 review).
|
|
75732
|
+
* refresh showed no draft while __system__ still differed.
|
|
75441
75733
|
*
|
|
75442
75734
|
* REPLACES what the host said before -- the host is the truth after
|
|
75443
75735
|
* a reload, and a Save is exactly the host saying "none any more".
|
|
75444
75736
|
* It only ever added, and a topic stayed a draft after the Save that
|
|
75445
|
-
* published it
|
|
75737
|
+
* published it. What this session wrote
|
|
75446
75738
|
* stays marked: it is kept apart (see topic_is_draft()).
|
|
75447
75739
|
***************************************************************/
|
|
75448
75740
|
function ac_drafts(gobj, event, kw, src) {
|
|
@@ -75499,7 +75791,10 @@ function ac_show_orphans(gobj, event, kw, src) {
|
|
|
75499
75791
|
* to feed.
|
|
75500
75792
|
***************************************************************/
|
|
75501
75793
|
function confirm_then(gobj, message, detail, kw) {
|
|
75502
|
-
kw = Object.assign({
|
|
75794
|
+
kw = Object.assign({
|
|
75795
|
+
model_gen: gobj.priv.model_gen,
|
|
75796
|
+
seg: position_seg(gobj)
|
|
75797
|
+
}, kw);
|
|
75503
75798
|
yui_shell_confirm_danger(yui_shell_of(gobj), message, {
|
|
75504
75799
|
t,
|
|
75505
75800
|
detail,
|
|
@@ -75566,7 +75861,13 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
75566
75861
|
let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
|
|
75567
75862
|
let col = kw.creating ? null : find_col(priv.model, priv.treedb_id, kw.topic, kw.col);
|
|
75568
75863
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75569
|
-
if (reload_first(gobj, event
|
|
75864
|
+
if (reload_first(gobj, event, {
|
|
75865
|
+
form: "column",
|
|
75866
|
+
topic: kw.topic,
|
|
75867
|
+
col: kw.col,
|
|
75868
|
+
creating: !!kw.creating,
|
|
75869
|
+
changed: kw.changed || {}
|
|
75870
|
+
})) return -1;
|
|
75570
75871
|
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75571
75872
|
if (!topic || !kw.creating && !col) {
|
|
75572
75873
|
log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.topic}.${kw.col}`);
|
|
@@ -75694,7 +75995,13 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
75694
75995
|
let treedb = current_treedb(gobj);
|
|
75695
75996
|
let topic = kw.creating ? null : find_topic(priv.model, priv.treedb_id, kw.topic);
|
|
75696
75997
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75697
|
-
if (reload_first(gobj, event
|
|
75998
|
+
if (reload_first(gobj, event, {
|
|
75999
|
+
form: "topic",
|
|
76000
|
+
topic: kw.topic,
|
|
76001
|
+
col: "",
|
|
76002
|
+
creating: !!kw.creating,
|
|
76003
|
+
changed: kw.changed || {}
|
|
76004
|
+
})) return -1;
|
|
75698
76005
|
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
75699
76006
|
if (!treedb || !kw.creating && !topic) {
|
|
75700
76007
|
log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
|
|
@@ -75885,6 +76192,7 @@ function ac_confirmed(gobj, event, kw, src) {
|
|
|
75885
76192
|
if (refuse_if_readonly(gobj, event)) return -1;
|
|
75886
76193
|
if (reload_first(gobj, event)) return -1;
|
|
75887
76194
|
if (built_on_a_replaced_model(gobj, event, kw)) return -1;
|
|
76195
|
+
if (asked_on_another_screen(gobj, event, kw)) return -1;
|
|
75888
76196
|
if (kw.what === "import") return run_import(gobj);
|
|
75889
76197
|
if (kw.what === "topic") {
|
|
75890
76198
|
let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
|
|
@@ -76156,7 +76464,11 @@ function create_gclass$1(gclass_name) {
|
|
|
76156
76464
|
null
|
|
76157
76465
|
]
|
|
76158
76466
|
])],
|
|
76159
|
-
["ST_SAVING", COMMON.
|
|
76467
|
+
["ST_SAVING", COMMON.filter((a) => a[0] !== "EV_REFRESH").concat([[
|
|
76468
|
+
"EV_REFRESH",
|
|
76469
|
+
ac_refresh_after_the_writes,
|
|
76470
|
+
null
|
|
76471
|
+
]])]
|
|
76160
76472
|
];
|
|
76161
76473
|
const event_types = [
|
|
76162
76474
|
["EV_MT_COMMAND_ANSWER", event_flag_t.EVF_PUBLIC_EVENT],
|