@yuneta/gobj-ui 7.25.8 → 7.25.10
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 +72 -12
- package/dist/gobj-ui.cjs.js +327 -97
- package/dist/gobj-ui.es.js +327 -97
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +418 -76
- package/src/schema_editor_reload.wiring.test.js +551 -7
- package/src/shell_modals.js +17 -5
- package/src/shell_toast_once.test.js +77 -0
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
* the owner's design of M36 (2026-09-21 review) every write here
|
|
27
27
|
* raised both versions, so an edit half made was already the
|
|
28
28
|
* schema of the next start. The topic list marks what this session
|
|
29
|
-
* wrote and has not saved (the
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* wrote and has not saved (EV_REFRESH forgets it: the view's own
|
|
30
|
+
* Refresh button sends it, and a host may after a save -- the
|
|
31
|
+
* agent console's does not) and what the host says is not saved
|
|
32
|
+
* (EV_DRAFTS, replaced whole each time it is sent).
|
|
32
33
|
*
|
|
33
34
|
* WHAT ELSE IS HERE, and why each is here and not somewhere else:
|
|
34
35
|
*
|
|
@@ -124,6 +125,7 @@ import {yui_tint} from "./bulma_tint.js";
|
|
|
124
125
|
import {host_draft_ids} from "./host_drafts.js";
|
|
125
126
|
|
|
126
127
|
import "./c_yui_schema_editor.css";
|
|
128
|
+
import "./yui_toolbar.css"; /* `selected_state`, the export's chosen view */
|
|
127
129
|
|
|
128
130
|
import {
|
|
129
131
|
build_schema_model,
|
|
@@ -135,6 +137,7 @@ import {
|
|
|
135
137
|
col_enum,
|
|
136
138
|
is_empty_value,
|
|
137
139
|
fkey_ref,
|
|
140
|
+
parse_fkey_ref,
|
|
138
141
|
next_order,
|
|
139
142
|
moved_orders,
|
|
140
143
|
} from "./schema_model.js";
|
|
@@ -202,7 +205,9 @@ SDATA_END()
|
|
|
202
205
|
let PRIVATE_DATA = {
|
|
203
206
|
/* What the store answered, and what it means */
|
|
204
207
|
records: null, /* {treedbs, topics, cols} as they arrived */
|
|
208
|
+
records_before: null, /* the records of the model shown, while a load replaces them */
|
|
205
209
|
model: null, /* build_schema_model() of the above */
|
|
210
|
+
model_gen: 0, /* which LOAD the model is: a dialog is stamped with it */
|
|
206
211
|
pending: 0, /* `nodes` requests still in flight */
|
|
207
212
|
load_error: "",
|
|
208
213
|
load_round: 0, /* which load an answer belongs to: echoed as `round` */
|
|
@@ -224,6 +229,7 @@ let PRIVATE_DATA = {
|
|
|
224
229
|
/* Children and overlays */
|
|
225
230
|
diagram_gobj: null,
|
|
226
231
|
dialog: null,
|
|
232
|
+
dialog_gen: 0, /* the model_gen the open dialog was built on */
|
|
227
233
|
|
|
228
234
|
/* A write, or a plan of them, in flight */
|
|
229
235
|
save_queue: null,
|
|
@@ -444,6 +450,90 @@ function refuse_if_readonly(gobj, event)
|
|
|
444
450
|
return true;
|
|
445
451
|
}
|
|
446
452
|
|
|
453
|
+
/***************************************************************
|
|
454
|
+
* A decision taken on a model a load has since REPLACED: the Save
|
|
455
|
+
* of a form, a confirmation, an import. Each carries the
|
|
456
|
+
* `model_gen` it was built on (a number, not the model: a kw is
|
|
457
|
+
* dumped by the machine trace).
|
|
458
|
+
*
|
|
459
|
+
* Refused, and said. A column form sends every field it shows, so
|
|
460
|
+
* its Save after a reload wrote the OLD record over the newer one
|
|
461
|
+
* (fourth independent review, probe A3); a form whose column the
|
|
462
|
+
* reload removed answered "names no column" for ever. end_load()
|
|
463
|
+
* closes such a dialog when the load lands; this is the same rule
|
|
464
|
+
* for whatever arrives anyway -- a stale screen, a confirmation
|
|
465
|
+
* that was up, a keyboard.
|
|
466
|
+
*
|
|
467
|
+
* A kw with no stamp is not a dialog's and is not asked.
|
|
468
|
+
***************************************************************/
|
|
469
|
+
function built_on_a_replaced_model(gobj, event, kw)
|
|
470
|
+
{
|
|
471
|
+
let priv = gobj.priv;
|
|
472
|
+
|
|
473
|
+
if(!kw || kw.model_gen === undefined || kw.model_gen === priv.model_gen) {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, it was decided on ` +
|
|
477
|
+
`the schemas a reload replaced`);
|
|
478
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
479
|
+
"the schemas were read again: open the dialog again", {t: t});
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/***************************************************************
|
|
484
|
+
* The reload a load refused IN session left owed (a routing
|
|
485
|
+
* adapter's deadline on a slow `nodes`, ac_mt_command_answer()):
|
|
486
|
+
* the model shown is kept, and it may be older than the store.
|
|
487
|
+
* The session is up, so no reconnect will ask for it again -- it
|
|
488
|
+
* was owed for as long as the session lasted, and a form opened
|
|
489
|
+
* on that model wrote every field of it over the newer record
|
|
490
|
+
* (fifth independent review; A3 of the fourth, by another path).
|
|
491
|
+
*
|
|
492
|
+
* So the operator's NEXT action asks it. An edit -- a form, a
|
|
493
|
+
* write, a confirmation, what reads the model whole (check,
|
|
494
|
+
* export) -- is refused and said: it would be computed on what
|
|
495
|
+
* the store may no longer hold, and the load that lands closes a
|
|
496
|
+
* dialog built on the old model anyway (end_load()). A move is
|
|
497
|
+
* not refused (reload_after_move()): it goes, and reads there.
|
|
498
|
+
*
|
|
499
|
+
* Not on a timer, and not at once: a load refused on a deadline
|
|
500
|
+
* asked again at once is refused the same way, and one asked on a
|
|
501
|
+
* clock is polling. Out of session nothing is asked; the reconnect
|
|
502
|
+
* does it (ac_transport_state()). The reload keeps this session's
|
|
503
|
+
* draft chips: the host did not ask for it.
|
|
504
|
+
***************************************************************/
|
|
505
|
+
function owes_reload(gobj)
|
|
506
|
+
{
|
|
507
|
+
let state = gobj_current_state(gobj);
|
|
508
|
+
|
|
509
|
+
return gobj.priv.reload_on_open && transport_in_session(gobj) &&
|
|
510
|
+
state !== "ST_LOADING" && state !== "ST_SAVING";
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function reload_first(gobj, event)
|
|
514
|
+
{
|
|
515
|
+
if(!owes_reload(gobj)) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
gobj.priv.reload_on_open = false;
|
|
519
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas shown ` +
|
|
520
|
+
`may be out of date: they are read again first`);
|
|
521
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
522
|
+
"the schemas shown may be out of date: they are read again, try again when they are in",
|
|
523
|
+
{t: t});
|
|
524
|
+
request_model(gobj, true);
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function reload_after_move(gobj)
|
|
529
|
+
{
|
|
530
|
+
if(!owes_reload(gobj)) {
|
|
531
|
+
return 0;
|
|
532
|
+
}
|
|
533
|
+
gobj.priv.reload_on_open = false;
|
|
534
|
+
return request_model(gobj, true);
|
|
535
|
+
}
|
|
536
|
+
|
|
447
537
|
|
|
448
538
|
|
|
449
539
|
|
|
@@ -465,17 +555,26 @@ function refuse_if_readonly(gobj, event)
|
|
|
465
555
|
* and the toolbar stops answering: left up, a click on a card, on
|
|
466
556
|
* Back or on the drawing answered "Event NOT DEFINED in state
|
|
467
557
|
* ST_LOADING" and was lost (third independent review). A dialog
|
|
468
|
-
* left open is answered in ST_LOADING instead (ac_wait_for_the_load)
|
|
558
|
+
* left open is answered in ST_LOADING instead (ac_wait_for_the_load),
|
|
559
|
+
* and a load that LANDS closes it (end_load()): it was built on the
|
|
560
|
+
* model the load replaced, and its Save sends every field it shows.
|
|
561
|
+
* An import plan is forgotten here for the same reason: it is a list
|
|
562
|
+
* of writes computed against the model going away -- once the load
|
|
563
|
+
* has LEFT (one that could not be sent replaces nothing).
|
|
469
564
|
*
|
|
470
|
-
* `keep_written`: a load
|
|
471
|
-
*
|
|
472
|
-
*
|
|
565
|
+
* `keep_written`: a load nobody ASKED for -- the reconnect, a write
|
|
566
|
+
* that turned out done, the reload a refused load owed -- keeps what
|
|
567
|
+
* this session wrote. Only EV_REFRESH forgets it: the view's own
|
|
568
|
+
* Refresh button, or a host that sends it (after a Save, say; the
|
|
569
|
+
* agent console does not).
|
|
473
570
|
*
|
|
474
571
|
* A load that cannot leave WHOLE did not happen (out of session it
|
|
475
572
|
* is not even asked): the records the model was built on are put
|
|
476
573
|
* back (emptied, the next write patched them into a model with no
|
|
477
574
|
* treedb), the round is spent so a request that did leave is
|
|
478
|
-
* stale, and the session back asks again.
|
|
575
|
+
* stale, and the session back asks again. A load that left and
|
|
576
|
+
* FAILED keeps them the same way (`records_before`,
|
|
577
|
+
* ac_mt_command_answer()).
|
|
479
578
|
***************************************************************/
|
|
480
579
|
function request_model(gobj, keep_written)
|
|
481
580
|
{
|
|
@@ -486,7 +585,13 @@ function request_model(gobj, keep_written)
|
|
|
486
585
|
return -1; /* not mounted on a transport yet: mt_start retries via EV_TRANSPORT_STATE */
|
|
487
586
|
}
|
|
488
587
|
|
|
489
|
-
|
|
588
|
+
/* A load asked while one is in flight (the host's EV_REFRESH is
|
|
589
|
+
* heard in ST_LOADING) keeps the records the model SHOWN was built
|
|
590
|
+
* on: `priv.records` is the half the first load got by then, and
|
|
591
|
+
* put back after a failure it was a model with no treedb. */
|
|
592
|
+
let previous = (gobj_current_state(gobj) === "ST_LOADING")?
|
|
593
|
+
priv.records_before : priv.records;
|
|
594
|
+
priv.records_before = previous;
|
|
490
595
|
priv.records = {treedbs: [], topics: [], cols: []};
|
|
491
596
|
priv.load_error = "";
|
|
492
597
|
priv.pending = 0;
|
|
@@ -511,6 +616,7 @@ function request_model(gobj, keep_written)
|
|
|
511
616
|
}
|
|
512
617
|
if(left < 3) {
|
|
513
618
|
priv.records = previous;
|
|
619
|
+
priv.records_before = null;
|
|
514
620
|
priv.load_round++;
|
|
515
621
|
priv.reload_on_open = true;
|
|
516
622
|
priv.load_error = t("cannot reach the treedb");
|
|
@@ -520,6 +626,9 @@ function request_model(gobj, keep_written)
|
|
|
520
626
|
end_load(gobj);
|
|
521
627
|
return -1;
|
|
522
628
|
}
|
|
629
|
+
/* Only a load that LEFT replaces the model the plan was computed
|
|
630
|
+
* on; one that could not be sent keeps it, and the plan with it. */
|
|
631
|
+
forget_import_plan(gobj);
|
|
523
632
|
priv.pending = left;
|
|
524
633
|
return 0;
|
|
525
634
|
}
|
|
@@ -605,8 +714,8 @@ function build_model(gobj)
|
|
|
605
714
|
* Called ONLY when records arrive from the store: that is what a
|
|
606
715
|
* measurement of "changed since" can be measured against.
|
|
607
716
|
*
|
|
608
|
-
* `written` is forgotten only by
|
|
609
|
-
*
|
|
717
|
+
* `written` is forgotten only by an EV_REFRESH (the view's own
|
|
718
|
+
* Refresh button, or a host that sends one). Since M36 a write moves no
|
|
610
719
|
* version, so `written` is the draft chip of what this session
|
|
611
720
|
* wrote and no longer half of a version measurement: a reload of
|
|
612
721
|
* the reconnect, or of a write that turned out done, forgot the
|
|
@@ -643,9 +752,12 @@ function start_measuring(gobj)
|
|
|
643
752
|
* until the host saves it (C_TREEDB's save-schema, which raises the
|
|
644
753
|
* versions of what changed). Two sources say a topic is one, and
|
|
645
754
|
* they are kept APART because they are forgotten differently:
|
|
646
|
-
* - `written`: what THIS session wrote
|
|
647
|
-
*
|
|
648
|
-
*
|
|
755
|
+
* - `written`: what THIS session wrote. Only EV_REFRESH forgets
|
|
756
|
+
* it -- the view's own Refresh button sends it (in the agent
|
|
757
|
+
* console that is the only sender), and a host may after a
|
|
758
|
+
* Save; the reconnect's reload, the one owed after a write, a
|
|
759
|
+
* refused load's and a late write's keep it (start_measuring(),
|
|
760
|
+
* request_model());
|
|
649
761
|
* - `host_ids`: what the host said last (EV_DRAFTS), replaced WHOLE
|
|
650
762
|
* by the next EV_DRAFTS. Folded into `written`, a topic the host
|
|
651
763
|
* named once stayed a draft after the Save that published it.
|
|
@@ -847,6 +959,14 @@ function render_toolbar(gobj)
|
|
|
847
959
|
* url's; ST_EMPTY has a treedb in the url and none in the model),
|
|
848
960
|
* and a button there sends what only a treedb screen declares. */
|
|
849
961
|
let on_a_treedb = state !== "ST_IDLE" && state !== "ST_EMPTY";
|
|
962
|
+
/* ...and the screen can outlive the treedb or the topic it is ON: a
|
|
963
|
+
* reload that no longer has them keeps the position (it says so,
|
|
964
|
+
* and Back is the way out). Asked of the MODEL, not of the state:
|
|
965
|
+
* gated on the state, Diagram, Check, Export, Import and New topic
|
|
966
|
+
* stayed up there and each logged "with no treedb open" (fourth
|
|
967
|
+
* independent review, probe D2). */
|
|
968
|
+
let treedb_here = on_a_treedb && !!current_treedb(gobj);
|
|
969
|
+
let topic_here = treedb_here && !!current_topic(gobj);
|
|
850
970
|
/* Nothing is legal while the model is in the air: every action is
|
|
851
971
|
* computed against it. */
|
|
852
972
|
let in_flight = state === "ST_SAVING" || state === "ST_LOADING";
|
|
@@ -892,7 +1012,7 @@ function render_toolbar(gobj)
|
|
|
892
1012
|
* refused says why when it is pressed.
|
|
893
1013
|
*----------------------------------------------*/
|
|
894
1014
|
let $right = [];
|
|
895
|
-
if(priv.treedb_id && has_model &&
|
|
1015
|
+
if(priv.treedb_id && has_model && treedb_here) {
|
|
896
1016
|
/* The compass, like every other door to a SCHEMA: the
|
|
897
1017
|
* `hexagon-nodes` it wore is the graph of the DATA. */
|
|
898
1018
|
$right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-compass-drafting",
|
|
@@ -906,7 +1026,7 @@ function render_toolbar(gobj)
|
|
|
906
1026
|
"import", "EV_IMPORT", false));
|
|
907
1027
|
}
|
|
908
1028
|
}
|
|
909
|
-
if(priv.topic_name && !readonly && !priv.diagram &&
|
|
1029
|
+
if(priv.topic_name && !readonly && !priv.diagram && topic_here) {
|
|
910
1030
|
/* Only while there IS somewhere to go back to: a drag is a write,
|
|
911
1031
|
* and this is the way back from one. */
|
|
912
1032
|
if(undo_order_writes(gobj, current_topic(gobj)).length > 0) {
|
|
@@ -916,7 +1036,7 @@ function render_toolbar(gobj)
|
|
|
916
1036
|
$right.push(toolbar_button("SCHEMA_ADD_COL_BTN", "yi-plus",
|
|
917
1037
|
"new column", "EV_ADD_COLUMN", false));
|
|
918
1038
|
}
|
|
919
|
-
if(priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram &&
|
|
1039
|
+
if(priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram && treedb_here) {
|
|
920
1040
|
$right.push(toolbar_button("SCHEMA_ADD_TOPIC_BTN", "yi-plus",
|
|
921
1041
|
"new topic", "EV_ADD_TOPIC", false));
|
|
922
1042
|
}
|
|
@@ -1631,6 +1751,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix)
|
|
|
1631
1751
|
let priv = gobj.priv;
|
|
1632
1752
|
|
|
1633
1753
|
close_dialog(gobj);
|
|
1754
|
+
priv.dialog_gen = priv.model_gen;
|
|
1634
1755
|
priv.dialog = yui_shell_show_modal(yui_shell_of(gobj), $content, {
|
|
1635
1756
|
dialog: true,
|
|
1636
1757
|
title: title,
|
|
@@ -1679,6 +1800,10 @@ function name_control($spec, label)
|
|
|
1679
1800
|
$spec[1]["aria-label"] = t(label);
|
|
1680
1801
|
$spec[1]["data-i18n-aria-label"] = label;
|
|
1681
1802
|
}
|
|
1803
|
+
if(!$spec[1].title) {
|
|
1804
|
+
$spec[1].title = t(label);
|
|
1805
|
+
$spec[1]["data-i18n-title"] = label;
|
|
1806
|
+
}
|
|
1682
1807
|
return true;
|
|
1683
1808
|
}
|
|
1684
1809
|
for(let child of ($spec[2] || [])) {
|
|
@@ -1689,6 +1814,19 @@ function name_control($spec, label)
|
|
|
1689
1814
|
return false;
|
|
1690
1815
|
}
|
|
1691
1816
|
|
|
1817
|
+
/***************************************************************
|
|
1818
|
+
* The four attributes that NAME a control -- a title and an
|
|
1819
|
+
* aria-label, each with the key a language change re-translates
|
|
1820
|
+
* it by. A visible label is not a name (CLAUDE.md).
|
|
1821
|
+
***************************************************************/
|
|
1822
|
+
function name_attrs(key, attrs)
|
|
1823
|
+
{
|
|
1824
|
+
return Object.assign({
|
|
1825
|
+
title: t(key), "data-i18n-title": key,
|
|
1826
|
+
"aria-label": t(key), "data-i18n-aria-label": key
|
|
1827
|
+
}, attrs);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1692
1830
|
function field(logical, name, label, $control, help)
|
|
1693
1831
|
{
|
|
1694
1832
|
/* The label is a SIBLING of the control -- Bulma's `field` shape --
|
|
@@ -1842,6 +1980,7 @@ function open_column_form(gobj, topic, col, prefill)
|
|
|
1842
1980
|
let hook_col = hook_topic ? hook[hook_topic] : "";
|
|
1843
1981
|
let treedb = current_treedb(gobj);
|
|
1844
1982
|
let sibling_topics = treedb ? treedb.topics.map(x => x.name) : [];
|
|
1983
|
+
let model_gen = priv.model_gen;
|
|
1845
1984
|
|
|
1846
1985
|
let $form = createElement2(
|
|
1847
1986
|
["div", {class: "SCHEMA_COL_FORM box"}, [
|
|
@@ -1883,9 +2022,10 @@ function open_column_form(gobj, topic, col, prefill)
|
|
|
1883
2022
|
text_input("description", record.description, "")),
|
|
1884
2023
|
["div", {class: "SCHEMA_COL_FORM_ACTIONS is-flex mt-4",
|
|
1885
2024
|
style: "gap:.5rem; justify-content:flex-end;"}, [
|
|
1886
|
-
["button", {class: "SCHEMA_COL_FORM_CANCEL button", type: "button"},
|
|
2025
|
+
["button", name_attrs("cancel", {class: "SCHEMA_COL_FORM_CANCEL button", type: "button"}),
|
|
1887
2026
|
[["span", {i18n: "cancel"}, t("cancel")]]],
|
|
1888
|
-
["button", {class: "SCHEMA_COL_FORM_SAVE button is-primary",
|
|
2027
|
+
["button", name_attrs("save", {class: "SCHEMA_COL_FORM_SAVE button is-primary",
|
|
2028
|
+
type: "button"}),
|
|
1889
2029
|
[["span", {class: "icon"}, [["i", {class: "yi-floppy-disk"}]]],
|
|
1890
2030
|
["span", {i18n: "save"}, t("save")]]]
|
|
1891
2031
|
]]
|
|
@@ -1926,10 +2066,11 @@ function open_column_form(gobj, topic, col, prefill)
|
|
|
1926
2066
|
let values = read_form($form);
|
|
1927
2067
|
values.flag = read_flags($form);
|
|
1928
2068
|
gobj_send_event(gobj, "EV_SAVE_COLUMN", {
|
|
1929
|
-
topic:
|
|
1930
|
-
col:
|
|
1931
|
-
creating:
|
|
1932
|
-
values:
|
|
2069
|
+
topic: topic.name,
|
|
2070
|
+
col: creating ? "" : col.name,
|
|
2071
|
+
creating: creating,
|
|
2072
|
+
values: values,
|
|
2073
|
+
model_gen: model_gen
|
|
1933
2074
|
}, gobj);
|
|
1934
2075
|
});
|
|
1935
2076
|
|
|
@@ -1962,6 +2103,7 @@ function open_topic_form(gobj, treedb, topic)
|
|
|
1962
2103
|
let record = (topic && topic.record) || {};
|
|
1963
2104
|
let creating = !topic;
|
|
1964
2105
|
let col_names = topic ? topic.cols.map(c => c.name) : [];
|
|
2106
|
+
let model_gen = gobj.priv.model_gen;
|
|
1965
2107
|
|
|
1966
2108
|
let $form = createElement2(
|
|
1967
2109
|
["div", {class: "SCHEMA_TOPIC_FORM box"}, [
|
|
@@ -2001,10 +2143,10 @@ function open_topic_form(gobj, treedb, topic)
|
|
|
2001
2143
|
]],
|
|
2002
2144
|
["div", {class: "SCHEMA_TOPIC_FORM_ACTIONS is-flex mt-4",
|
|
2003
2145
|
style: "gap:.5rem; justify-content:flex-end;"}, [
|
|
2004
|
-
["button", {class: "SCHEMA_TOPIC_FORM_CANCEL button", type: "button"},
|
|
2146
|
+
["button", name_attrs("cancel", {class: "SCHEMA_TOPIC_FORM_CANCEL button", type: "button"}),
|
|
2005
2147
|
[["span", {i18n: "cancel"}, t("cancel")]]],
|
|
2006
|
-
["button", {class: "SCHEMA_TOPIC_FORM_SAVE button is-primary",
|
|
2007
|
-
|
|
2148
|
+
["button", name_attrs("save", {class: "SCHEMA_TOPIC_FORM_SAVE button is-primary",
|
|
2149
|
+
type: "button"}),
|
|
2008
2150
|
[["span", {class: "icon"}, [["i", {class: "yi-floppy-disk"}]]],
|
|
2009
2151
|
["span", {i18n: "save"}, t("save")]]]
|
|
2010
2152
|
]]
|
|
@@ -2018,9 +2160,10 @@ function open_topic_form(gobj, treedb, topic)
|
|
|
2018
2160
|
$form.querySelector(".SCHEMA_TOPIC_FORM_SAVE").addEventListener("click", (evt) => {
|
|
2019
2161
|
evt.stopPropagation();
|
|
2020
2162
|
gobj_send_event(gobj, "EV_SAVE_TOPIC", {
|
|
2021
|
-
topic:
|
|
2022
|
-
creating:
|
|
2023
|
-
values:
|
|
2163
|
+
topic: creating ? "" : topic.name,
|
|
2164
|
+
creating: creating,
|
|
2165
|
+
values: read_form($form),
|
|
2166
|
+
model_gen: model_gen
|
|
2024
2167
|
}, gobj);
|
|
2025
2168
|
});
|
|
2026
2169
|
|
|
@@ -2092,13 +2235,21 @@ function open_export(gobj, treedb)
|
|
|
2092
2235
|
|
|
2093
2236
|
let $content = createElement2(
|
|
2094
2237
|
["div", {class: "SCHEMA_EXPORT box"}, [
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2238
|
+
/* Two BUTTONS, not Bulma tabs: a tab's <a> with no href is
|
|
2239
|
+
* reached by no keyboard and named by nothing but its "C".
|
|
2240
|
+
* The one shown is the selected segment (`selected_state`,
|
|
2241
|
+
* yui_toolbar.css) and says so with aria-pressed. */
|
|
2242
|
+
["div", {class: "SCHEMA_EXPORT_TABS buttons has-addons mb-2"}, [
|
|
2243
|
+
["button", {class: "SCHEMA_EXPORT_TAB button selected_state", type: "button",
|
|
2244
|
+
"data-pane": "c", "aria-pressed": "true",
|
|
2245
|
+
title: t("schema as c source"), "data-i18n-title": "schema as c source",
|
|
2246
|
+
"aria-label": t("schema as c source"), "data-i18n-aria-label": "schema as c source"},
|
|
2247
|
+
[["span", {}, "C"]]],
|
|
2248
|
+
["button", {class: "SCHEMA_EXPORT_TAB button", type: "button",
|
|
2249
|
+
"data-pane": "json", "aria-pressed": "false",
|
|
2250
|
+
title: t("schema as json"), "data-i18n-title": "schema as json",
|
|
2251
|
+
"aria-label": t("schema as json"), "data-i18n-aria-label": "schema as json"},
|
|
2252
|
+
[["span", {}, "JSON"]]]
|
|
2102
2253
|
]],
|
|
2103
2254
|
["p", {class: "SCHEMA_EXPORT_HELP help mb-2",
|
|
2104
2255
|
i18n: "an edit made here works and lives in no source: this is what to paste back"},
|
|
@@ -2129,7 +2280,8 @@ function open_export(gobj, treedb)
|
|
|
2129
2280
|
$tab.addEventListener("click", (evt) => {
|
|
2130
2281
|
evt.stopPropagation();
|
|
2131
2282
|
for(let $other of $content.querySelectorAll(".SCHEMA_EXPORT_TAB")) {
|
|
2132
|
-
$other.classList.toggle("
|
|
2283
|
+
$other.classList.toggle("selected_state", $other === $tab);
|
|
2284
|
+
$other.setAttribute("aria-pressed", ($other === $tab)? "true": "false");
|
|
2133
2285
|
}
|
|
2134
2286
|
$text.value = ($tab.dataset.pane === "c") ? c_text : json_text;
|
|
2135
2287
|
});
|
|
@@ -2173,14 +2325,15 @@ function copy_text(gobj, $text)
|
|
|
2173
2325
|
***************************************************************/
|
|
2174
2326
|
function open_import(gobj, treedb)
|
|
2175
2327
|
{
|
|
2328
|
+
let model_gen = gobj.priv.model_gen;
|
|
2176
2329
|
let $content = createElement2(
|
|
2177
2330
|
["div", {class: "SCHEMA_IMPORT box"}, [
|
|
2178
2331
|
["p", {class: "SCHEMA_IMPORT_HELP help mb-2",
|
|
2179
2332
|
i18n: "paste a schema as json: the plan is shown before anything is written"},
|
|
2180
2333
|
t("paste a schema as json: the plan is shown before anything is written")],
|
|
2181
|
-
["textarea", {class: "SCHEMA_IMPORT_TEXT textarea is-small is-family-monospace",
|
|
2334
|
+
["textarea", name_attrs("schema", {class: "SCHEMA_IMPORT_TEXT textarea is-small is-family-monospace",
|
|
2182
2335
|
rows: "12", spellcheck: "false",
|
|
2183
|
-
placeholder: '{"id": "...", "schema_version": "1", "topics": []}'}, ""],
|
|
2336
|
+
placeholder: '{"id": "...", "schema_version": "1", "topics": []}'}), ""],
|
|
2184
2337
|
["label", {class: "SCHEMA_IMPORT_PRUNE checkbox mt-2"}, [
|
|
2185
2338
|
["input", {type: "checkbox", class: "SCHEMA_IMPORT_PRUNE_BOX", checked: "checked"}],
|
|
2186
2339
|
["span", {class: "ml-1", i18n: "delete what the pasted schema does not declare"},
|
|
@@ -2189,13 +2342,13 @@ function open_import(gobj, treedb)
|
|
|
2189
2342
|
["div", {class: "SCHEMA_IMPORT_PLAN mt-3"}, []],
|
|
2190
2343
|
["div", {class: "SCHEMA_IMPORT_ACTIONS is-flex mt-3",
|
|
2191
2344
|
style: "gap:.5rem; justify-content:flex-end;"}, [
|
|
2192
|
-
["button", {class: "SCHEMA_IMPORT_CANCEL button", type: "button"},
|
|
2345
|
+
["button", name_attrs("cancel", {class: "SCHEMA_IMPORT_CANCEL button", type: "button"}),
|
|
2193
2346
|
[["span", {i18n: "cancel"}, t("cancel")]]],
|
|
2194
|
-
["button", {class: "SCHEMA_IMPORT_PREVIEW button", type: "button"},
|
|
2347
|
+
["button", name_attrs("preview", {class: "SCHEMA_IMPORT_PREVIEW button", type: "button"}),
|
|
2195
2348
|
[["span", {class: "icon"}, [["i", {class: "yi-eye"}]]],
|
|
2196
2349
|
["span", {i18n: "preview"}, t("preview")]]],
|
|
2197
|
-
["button", {class: "SCHEMA_IMPORT_RUN button is-warning",
|
|
2198
|
-
|
|
2350
|
+
["button", name_attrs("import", {class: "SCHEMA_IMPORT_RUN button is-warning",
|
|
2351
|
+
type: "button", disabled: "disabled"}),
|
|
2199
2352
|
[["span", {class: "icon"}, [["i", {class: "yi-upload"}]]],
|
|
2200
2353
|
["span", {i18n: "import"}, t("import")]]]
|
|
2201
2354
|
]]
|
|
@@ -2214,13 +2367,14 @@ function open_import(gobj, treedb)
|
|
|
2214
2367
|
$content.querySelector(".SCHEMA_IMPORT_PREVIEW").addEventListener("click", (evt) => {
|
|
2215
2368
|
evt.stopPropagation();
|
|
2216
2369
|
gobj_send_event(gobj, "EV_PREVIEW_IMPORT", {
|
|
2217
|
-
text:
|
|
2218
|
-
prune:
|
|
2370
|
+
text: $text.value,
|
|
2371
|
+
prune: $prune.checked,
|
|
2372
|
+
model_gen: model_gen
|
|
2219
2373
|
}, gobj);
|
|
2220
2374
|
});
|
|
2221
2375
|
$run.addEventListener("click", (evt) => {
|
|
2222
2376
|
evt.stopPropagation();
|
|
2223
|
-
gobj_send_event(gobj, "EV_APPLY_IMPORT", {}, gobj);
|
|
2377
|
+
gobj_send_event(gobj, "EV_APPLY_IMPORT", {model_gen: model_gen}, gobj);
|
|
2224
2378
|
});
|
|
2225
2379
|
|
|
2226
2380
|
gobj.priv.import_pane = $plan_pane;
|
|
@@ -2228,6 +2382,23 @@ function open_import(gobj, treedb)
|
|
|
2228
2382
|
open_dialog(gobj, $content, "import", "SCHEMA_IMPORT_DIALOG", treedb.id);
|
|
2229
2383
|
}
|
|
2230
2384
|
|
|
2385
|
+
/***************************************************************
|
|
2386
|
+
* The plan is a list of writes computed against the model shown:
|
|
2387
|
+
* a load replaces the model, so the plan goes with it and the
|
|
2388
|
+
* dialog, if it stays up (a load that failed keeps the model),
|
|
2389
|
+
* asks for a Preview again.
|
|
2390
|
+
***************************************************************/
|
|
2391
|
+
function forget_import_plan(gobj)
|
|
2392
|
+
{
|
|
2393
|
+
let priv = gobj.priv;
|
|
2394
|
+
|
|
2395
|
+
priv.import_plan = null;
|
|
2396
|
+
clear(priv.import_pane);
|
|
2397
|
+
if(priv.import_run) {
|
|
2398
|
+
priv.import_run.disabled = true;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2231
2402
|
/***************************************************************
|
|
2232
2403
|
* The plan, drawn where the paste is: one line per write, the
|
|
2233
2404
|
* deletions apart and counted, because they are what the
|
|
@@ -2304,7 +2475,7 @@ function open_orphans(gobj)
|
|
|
2304
2475
|
["td", {class: "SCHEMA_ORPHAN_ID"}, [["code", {}, `${entry.id}`]]],
|
|
2305
2476
|
["td", {class: "SCHEMA_ORPHAN_ACTIONS has-text-right"},
|
|
2306
2477
|
readonly ? [] : [row_icon(gobj, "SCHEMA_ORPHAN_DELETE", "yi-trash", "delete",
|
|
2307
|
-
"EV_DELETE_ORPHAN", {kind: kind, id: entry.id})]]
|
|
2478
|
+
"EV_DELETE_ORPHAN", {kind: kind, id: entry.id, model_gen: priv.model_gen})]]
|
|
2308
2479
|
]];
|
|
2309
2480
|
};
|
|
2310
2481
|
|
|
@@ -2519,6 +2690,11 @@ function transport_dropped(gobj)
|
|
|
2519
2690
|
priv.load_round++;
|
|
2520
2691
|
priv.pending = 0;
|
|
2521
2692
|
priv.load_error = "";
|
|
2693
|
+
if(priv.records_before) {
|
|
2694
|
+
/* The records of the model kept, not the half the load got. */
|
|
2695
|
+
priv.records = priv.records_before;
|
|
2696
|
+
priv.records_before = null;
|
|
2697
|
+
}
|
|
2522
2698
|
return end_load(gobj);
|
|
2523
2699
|
}
|
|
2524
2700
|
return 0;
|
|
@@ -2530,6 +2706,12 @@ function transport_dropped(gobj)
|
|
|
2530
2706
|
* in flight (ac_show() kept it), or the one it had. Without a
|
|
2531
2707
|
* model there is no screen to go to: the position is kept for the
|
|
2532
2708
|
* next load, and the editor says why it is empty.
|
|
2709
|
+
*
|
|
2710
|
+
* A dialog built on the model this load REPLACED is closed, and the
|
|
2711
|
+
* operator told to open it again: it shows the old record, and its
|
|
2712
|
+
* Save would write every field of it over the newer one. A load
|
|
2713
|
+
* that failed or never left replaced nothing (`model_gen` did not
|
|
2714
|
+
* move), and the dialog stays with what was typed.
|
|
2533
2715
|
***************************************************************/
|
|
2534
2716
|
function end_load(gobj)
|
|
2535
2717
|
{
|
|
@@ -2537,6 +2719,13 @@ function end_load(gobj)
|
|
|
2537
2719
|
let seg = priv.pending_seg;
|
|
2538
2720
|
|
|
2539
2721
|
priv.pending_seg = null;
|
|
2722
|
+
if(priv.dialog && priv.dialog_gen !== priv.model_gen) {
|
|
2723
|
+
log_warning(`${gobj_short_name(gobj)}: a dialog was open on the schemas ` +
|
|
2724
|
+
`the load replaced: closed`);
|
|
2725
|
+
close_dialog(gobj);
|
|
2726
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
2727
|
+
"the schemas were read again: open the dialog again", {t: t});
|
|
2728
|
+
}
|
|
2540
2729
|
if(priv.model) {
|
|
2541
2730
|
if(seg !== null) {
|
|
2542
2731
|
return apply_seg(gobj, seg);
|
|
@@ -2885,6 +3074,25 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2885
3074
|
return 0;
|
|
2886
3075
|
}
|
|
2887
3076
|
if(priv.load_error) {
|
|
3077
|
+
/* Refused IN session (a deadline of a routing adapter on a
|
|
3078
|
+
* slow `nodes`): the model shown is what the store held a
|
|
3079
|
+
* moment ago, and blanking it took the screen and every open
|
|
3080
|
+
* form with it -- a form's Save then answered "Event NOT
|
|
3081
|
+
* DEFINED in state ST_IDLE" (fourth independent review). It
|
|
3082
|
+
* stays, as it does when the load cannot leave, and the
|
|
3083
|
+
* reload is owed: the operator's next action asks it
|
|
3084
|
+
* (reload_first(), fifth independent review). */
|
|
3085
|
+
if(priv.model) {
|
|
3086
|
+
log_warning(`${gobj_short_name(gobj)}: the schemas could not be read ` +
|
|
3087
|
+
`again (${priv.load_error}): the previous ones stay`);
|
|
3088
|
+
priv.records = priv.records_before;
|
|
3089
|
+
priv.records_before = null;
|
|
3090
|
+
priv.reload_on_open = true;
|
|
3091
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
3092
|
+
"cannot read the schemas again: the ones shown may be out of date, your next change reads them first",
|
|
3093
|
+
{t: t});
|
|
3094
|
+
return end_load(gobj);
|
|
3095
|
+
}
|
|
2888
3096
|
priv.model = null;
|
|
2889
3097
|
return end_load(gobj);
|
|
2890
3098
|
}
|
|
@@ -2893,6 +3101,8 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2893
3101
|
* (NOT in build_model(): that also runs on the patch after every
|
|
2894
3102
|
* write, which is exactly when the undo has to survive.) */
|
|
2895
3103
|
priv.order_undo = {};
|
|
3104
|
+
priv.records_before = null;
|
|
3105
|
+
priv.model_gen++;
|
|
2896
3106
|
build_model(gobj);
|
|
2897
3107
|
/* Same reason as order_undo above: this is the LOAD, and the
|
|
2898
3108
|
* baseline is what a later "the version did not move" is measured
|
|
@@ -2931,12 +3141,26 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2931
3141
|
patch_record(gobj, topic_name, record);
|
|
2932
3142
|
} else {
|
|
2933
3143
|
/* The treedb wrote it and did not describe it back: ask
|
|
2934
|
-
* again rather than draw a model that has drifted.
|
|
2935
|
-
|
|
3144
|
+
* again rather than draw a model that has drifted. What
|
|
3145
|
+
* was queued after it is not sent -- it was computed on
|
|
3146
|
+
* the model that drifted -- and that is SAID: dropped in
|
|
3147
|
+
* silence, a drag of five columns stopped at the first.
|
|
3148
|
+
* The reload is also the one a late write may have owed
|
|
3149
|
+
* (reload_after_write): left set, the next write loaded
|
|
3150
|
+
* the whole model once more (fourth independent review). */
|
|
3151
|
+
let not_sent = priv.save_queue.length - 1;
|
|
3152
|
+
log_warning(`${gobj_short_name(gobj)}: '${command}' answered no record` +
|
|
3153
|
+
(not_sent > 0 ? `: ${not_sent} write(s) after it were not sent` : ""));
|
|
3154
|
+
if(not_sent > 0) {
|
|
3155
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
3156
|
+
"the treedb did not describe a write back: the writes after it were not sent",
|
|
3157
|
+
{t: t});
|
|
3158
|
+
}
|
|
2936
3159
|
if(topic_name === T_TOPICS || topic_name === T_COLS) {
|
|
2937
3160
|
let write = priv.save_queue[0];
|
|
2938
3161
|
mark_written(gobj, topic_name, write ? write.record : null);
|
|
2939
3162
|
}
|
|
3163
|
+
priv.reload_after_write = false;
|
|
2940
3164
|
priv.save_queue = [];
|
|
2941
3165
|
priv.save_return = "";
|
|
2942
3166
|
priv.save_wrote = false;
|
|
@@ -3010,8 +3234,14 @@ function late_write_done(gobj, command, topic_name, data)
|
|
|
3010
3234
|
/***************************************************************
|
|
3011
3235
|
* The topic a record written belongs to, read off the RECORD and
|
|
3012
3236
|
* not off the screen: a write answered late may be about a topic
|
|
3013
|
-
* the operator has left. A column names its topic in its fkey
|
|
3014
|
-
*
|
|
3237
|
+
* the operator has left. A column names its topic in its fkey; a
|
|
3238
|
+
* topic is its own id.
|
|
3239
|
+
*
|
|
3240
|
+
* The fkey is read with parse_fkey_ref(), because the store answers
|
|
3241
|
+
* in list_dict -- `[{topic_name, id, hook_name}]` -- and not in the
|
|
3242
|
+
* `topics^<topic id>^cols` strings this view writes. Split as a
|
|
3243
|
+
* string, every production answer marked nothing (fourth independent
|
|
3244
|
+
* review, probe C).
|
|
3015
3245
|
***************************************************************/
|
|
3016
3246
|
function mark_written_record(gobj, topic_name, record)
|
|
3017
3247
|
{
|
|
@@ -3027,13 +3257,8 @@ function mark_written_record(gobj, topic_name, record)
|
|
|
3027
3257
|
if(topic_name !== T_COLS) {
|
|
3028
3258
|
return;
|
|
3029
3259
|
}
|
|
3030
|
-
let
|
|
3031
|
-
|
|
3032
|
-
for(let ref of refs) {
|
|
3033
|
-
let parts = String(ref).split("^");
|
|
3034
|
-
if(parts.length >= 2 && parts[1]) {
|
|
3035
|
-
priv.written[parts[1]] = true;
|
|
3036
|
-
}
|
|
3260
|
+
for(let ref of parse_fkey_ref(record.topics)) {
|
|
3261
|
+
priv.written[ref.id] = true;
|
|
3037
3262
|
}
|
|
3038
3263
|
}
|
|
3039
3264
|
|
|
@@ -3142,16 +3367,43 @@ function ac_refresh(gobj, event, kw, src)
|
|
|
3142
3367
|
* operator can still reach, and what it asks is computed against
|
|
3143
3368
|
* a model the answers in the air are about to replace.
|
|
3144
3369
|
*
|
|
3145
|
-
* Refused, and SAID
|
|
3146
|
-
*
|
|
3147
|
-
*
|
|
3148
|
-
*
|
|
3370
|
+
* Refused, and SAID. Undeclared, it answered "Event NOT DEFINED
|
|
3371
|
+
* in state ST_LOADING" and the edit was gone with no word (third
|
|
3372
|
+
* independent review).
|
|
3373
|
+
*
|
|
3374
|
+
* And NOT "try again": the same Save, once the load was in, sent
|
|
3375
|
+
* the form built on the old model -- every field of the old record
|
|
3376
|
+
* over the newer one (fourth independent review). The load that
|
|
3377
|
+
* lands closes such a dialog (end_load()); one that fails leaves it
|
|
3378
|
+
* up, on the model it was built on.
|
|
3149
3379
|
***************************************************************/
|
|
3150
3380
|
function ac_wait_for_the_load(gobj, event, kw, src)
|
|
3151
3381
|
{
|
|
3152
3382
|
log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas are loading`);
|
|
3153
3383
|
yui_shell_show_error(yui_shell_of(gobj),
|
|
3154
|
-
"the schemas are loading:
|
|
3384
|
+
"the schemas are loading: wait for them", {t: t});
|
|
3385
|
+
return -1;
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
/***************************************************************
|
|
3389
|
+
* A confirmation answered where no treedb is open: ST_EMPTY (a
|
|
3390
|
+
* reload landed on zero treedbs while it was up) or ST_IDLE (no
|
|
3391
|
+
* model). A confirmation is a shell modal, not this view's dialog,
|
|
3392
|
+
* so the load that lands does not close it, and its Yes arrives
|
|
3393
|
+
* anyway. Undeclared, it answered "Event NOT DEFINED" (fifth
|
|
3394
|
+
* independent review).
|
|
3395
|
+
*
|
|
3396
|
+
* Refused with the stamp's own words: whatever it was decided on
|
|
3397
|
+
* is not on screen any more.
|
|
3398
|
+
***************************************************************/
|
|
3399
|
+
function ac_confirmed_on_nothing(gobj, event, kw, src)
|
|
3400
|
+
{
|
|
3401
|
+
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
3402
|
+
return -1;
|
|
3403
|
+
}
|
|
3404
|
+
log_warning(`${gobj_short_name(gobj)}: ${event} refused, no treedb is open`);
|
|
3405
|
+
yui_shell_show_error(yui_shell_of(gobj),
|
|
3406
|
+
"the schemas were read again: open the dialog again", {t: t});
|
|
3155
3407
|
return -1;
|
|
3156
3408
|
}
|
|
3157
3409
|
|
|
@@ -3186,32 +3438,34 @@ function ac_drafts(gobj, event, kw, src)
|
|
|
3186
3438
|
***************************************************************/
|
|
3187
3439
|
function ac_select_treedb(gobj, event, kw, src)
|
|
3188
3440
|
{
|
|
3189
|
-
|
|
3441
|
+
go(gobj, kw.treedb, "", false, true);
|
|
3442
|
+
return reload_after_move(gobj);
|
|
3190
3443
|
}
|
|
3191
3444
|
|
|
3192
3445
|
function ac_select_topic(gobj, event, kw, src)
|
|
3193
3446
|
{
|
|
3194
|
-
|
|
3447
|
+
go(gobj, gobj.priv.treedb_id, kw.topic, false, true);
|
|
3448
|
+
return reload_after_move(gobj);
|
|
3195
3449
|
}
|
|
3196
3450
|
|
|
3197
3451
|
function ac_back(gobj, event, kw, src)
|
|
3198
3452
|
{
|
|
3199
3453
|
let priv = gobj.priv;
|
|
3200
3454
|
|
|
3201
|
-
if(priv.diagram) {
|
|
3202
|
-
|
|
3203
|
-
}
|
|
3204
|
-
|
|
3205
|
-
return go(gobj, priv.treedb_id, "", false, true);
|
|
3455
|
+
if(priv.diagram || priv.topic_name) {
|
|
3456
|
+
go(gobj, priv.treedb_id, "", false, true);
|
|
3457
|
+
} else {
|
|
3458
|
+
go(gobj, "", "", false, true);
|
|
3206
3459
|
}
|
|
3207
|
-
return
|
|
3460
|
+
return reload_after_move(gobj);
|
|
3208
3461
|
}
|
|
3209
3462
|
|
|
3210
3463
|
function ac_toggle_diagram(gobj, event, kw, src)
|
|
3211
3464
|
{
|
|
3212
3465
|
let priv = gobj.priv;
|
|
3213
3466
|
|
|
3214
|
-
|
|
3467
|
+
go(gobj, priv.treedb_id, priv.topic_name, !priv.diagram, true);
|
|
3468
|
+
return reload_after_move(gobj);
|
|
3215
3469
|
}
|
|
3216
3470
|
|
|
3217
3471
|
/***************************************************************
|
|
@@ -3226,11 +3480,15 @@ function ac_node_click(gobj, event, kw, src)
|
|
|
3226
3480
|
if(!topic) {
|
|
3227
3481
|
return 0;
|
|
3228
3482
|
}
|
|
3229
|
-
|
|
3483
|
+
go(gobj, gobj.priv.treedb_id, topic, false, true);
|
|
3484
|
+
return reload_after_move(gobj);
|
|
3230
3485
|
}
|
|
3231
3486
|
|
|
3232
3487
|
function ac_show_orphans(gobj, event, kw, src)
|
|
3233
3488
|
{
|
|
3489
|
+
if(reload_first(gobj, event)) {
|
|
3490
|
+
return -1;
|
|
3491
|
+
}
|
|
3234
3492
|
open_orphans(gobj);
|
|
3235
3493
|
return 0;
|
|
3236
3494
|
}
|
|
@@ -3245,6 +3503,7 @@ function ac_show_orphans(gobj, event, kw, src)
|
|
|
3245
3503
|
***************************************************************/
|
|
3246
3504
|
function confirm_then(gobj, message, detail, kw)
|
|
3247
3505
|
{
|
|
3506
|
+
kw = Object.assign({model_gen: gobj.priv.model_gen}, kw);
|
|
3248
3507
|
yui_shell_confirm_danger(yui_shell_of(gobj), message, {
|
|
3249
3508
|
t: t,
|
|
3250
3509
|
detail: detail
|
|
@@ -3266,6 +3525,9 @@ function ac_add_column(gobj, event, kw, src)
|
|
|
3266
3525
|
if(refuse_if_readonly(gobj, event)) {
|
|
3267
3526
|
return -1;
|
|
3268
3527
|
}
|
|
3528
|
+
if(reload_first(gobj, event)) {
|
|
3529
|
+
return -1;
|
|
3530
|
+
}
|
|
3269
3531
|
if(!topic) {
|
|
3270
3532
|
log_error(`${gobj_short_name(gobj)}: ${event} with no topic open`);
|
|
3271
3533
|
return -1;
|
|
@@ -3283,6 +3545,9 @@ function ac_edit_column(gobj, event, kw, src)
|
|
|
3283
3545
|
if(refuse_if_readonly(gobj, event)) {
|
|
3284
3546
|
return -1;
|
|
3285
3547
|
}
|
|
3548
|
+
if(reload_first(gobj, event)) {
|
|
3549
|
+
return -1;
|
|
3550
|
+
}
|
|
3286
3551
|
if(!col) {
|
|
3287
3552
|
log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.col}`);
|
|
3288
3553
|
return -1;
|
|
@@ -3305,6 +3570,9 @@ function ac_duplicate_column(gobj, event, kw, src)
|
|
|
3305
3570
|
if(refuse_if_readonly(gobj, event)) {
|
|
3306
3571
|
return -1;
|
|
3307
3572
|
}
|
|
3573
|
+
if(reload_first(gobj, event)) {
|
|
3574
|
+
return -1;
|
|
3575
|
+
}
|
|
3308
3576
|
if(!col) {
|
|
3309
3577
|
log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.col}`);
|
|
3310
3578
|
return -1;
|
|
@@ -3330,6 +3598,12 @@ function ac_save_column(gobj, event, kw, src)
|
|
|
3330
3598
|
if(refuse_if_readonly(gobj, event)) {
|
|
3331
3599
|
return -1;
|
|
3332
3600
|
}
|
|
3601
|
+
if(reload_first(gobj, event)) {
|
|
3602
|
+
return -1;
|
|
3603
|
+
}
|
|
3604
|
+
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
3605
|
+
return -1;
|
|
3606
|
+
}
|
|
3333
3607
|
if(!topic || (!kw.creating && !col)) {
|
|
3334
3608
|
log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.topic}.${kw.col}`);
|
|
3335
3609
|
return -1;
|
|
@@ -3362,6 +3636,9 @@ function ac_delete_column(gobj, event, kw, src)
|
|
|
3362
3636
|
if(refuse_if_readonly(gobj, event)) {
|
|
3363
3637
|
return -1;
|
|
3364
3638
|
}
|
|
3639
|
+
if(reload_first(gobj, event)) {
|
|
3640
|
+
return -1;
|
|
3641
|
+
}
|
|
3365
3642
|
if(!col) {
|
|
3366
3643
|
log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.col}`);
|
|
3367
3644
|
return -1;
|
|
@@ -3384,6 +3661,9 @@ function ac_move_column(gobj, event, kw, src)
|
|
|
3384
3661
|
if(refuse_if_readonly(gobj, event)) {
|
|
3385
3662
|
return -1;
|
|
3386
3663
|
}
|
|
3664
|
+
if(reload_first(gobj, event)) {
|
|
3665
|
+
return -1;
|
|
3666
|
+
}
|
|
3387
3667
|
if(!topic) {
|
|
3388
3668
|
log_error(`${gobj_short_name(gobj)}: ${event} with no topic open`);
|
|
3389
3669
|
return -1;
|
|
@@ -3423,6 +3703,9 @@ function ac_undo_order(gobj, event, kw, src)
|
|
|
3423
3703
|
if(refuse_if_readonly(gobj, event)) {
|
|
3424
3704
|
return -1;
|
|
3425
3705
|
}
|
|
3706
|
+
if(reload_first(gobj, event)) {
|
|
3707
|
+
return -1;
|
|
3708
|
+
}
|
|
3426
3709
|
if(!topic) {
|
|
3427
3710
|
log_error(`${gobj_short_name(gobj)}: ${event} with no topic open`);
|
|
3428
3711
|
return -1;
|
|
@@ -3450,6 +3733,9 @@ function ac_add_topic(gobj, event, kw, src)
|
|
|
3450
3733
|
if(refuse_if_readonly(gobj, event)) {
|
|
3451
3734
|
return -1;
|
|
3452
3735
|
}
|
|
3736
|
+
if(reload_first(gobj, event)) {
|
|
3737
|
+
return -1;
|
|
3738
|
+
}
|
|
3453
3739
|
if(!treedb) {
|
|
3454
3740
|
log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
|
|
3455
3741
|
return -1;
|
|
@@ -3467,6 +3753,9 @@ function ac_edit_topic(gobj, event, kw, src)
|
|
|
3467
3753
|
if(refuse_if_readonly(gobj, event)) {
|
|
3468
3754
|
return -1;
|
|
3469
3755
|
}
|
|
3756
|
+
if(reload_first(gobj, event)) {
|
|
3757
|
+
return -1;
|
|
3758
|
+
}
|
|
3470
3759
|
if(!topic) {
|
|
3471
3760
|
log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
|
|
3472
3761
|
return -1;
|
|
@@ -3484,6 +3773,12 @@ function ac_save_topic(gobj, event, kw, src)
|
|
|
3484
3773
|
if(refuse_if_readonly(gobj, event)) {
|
|
3485
3774
|
return -1;
|
|
3486
3775
|
}
|
|
3776
|
+
if(reload_first(gobj, event)) {
|
|
3777
|
+
return -1;
|
|
3778
|
+
}
|
|
3779
|
+
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
3780
|
+
return -1;
|
|
3781
|
+
}
|
|
3487
3782
|
if(!treedb || (!kw.creating && !topic)) {
|
|
3488
3783
|
log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
|
|
3489
3784
|
return -1;
|
|
@@ -3520,6 +3815,9 @@ function ac_delete_topic(gobj, event, kw, src)
|
|
|
3520
3815
|
if(refuse_if_readonly(gobj, event)) {
|
|
3521
3816
|
return -1;
|
|
3522
3817
|
}
|
|
3818
|
+
if(reload_first(gobj, event)) {
|
|
3819
|
+
return -1;
|
|
3820
|
+
}
|
|
3523
3821
|
if(!topic) {
|
|
3524
3822
|
log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
|
|
3525
3823
|
return -1;
|
|
@@ -3538,6 +3836,12 @@ function ac_delete_orphan(gobj, event, kw, src)
|
|
|
3538
3836
|
if(refuse_if_readonly(gobj, event)) {
|
|
3539
3837
|
return -1;
|
|
3540
3838
|
}
|
|
3839
|
+
if(reload_first(gobj, event)) {
|
|
3840
|
+
return -1;
|
|
3841
|
+
}
|
|
3842
|
+
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
3843
|
+
return -1;
|
|
3844
|
+
}
|
|
3541
3845
|
if(empty_string(kw.id)) {
|
|
3542
3846
|
log_error(`${gobj_short_name(gobj)}: ${event} names nothing`);
|
|
3543
3847
|
return -1;
|
|
@@ -3558,6 +3862,10 @@ function ac_validate(gobj, event, kw, src)
|
|
|
3558
3862
|
let priv = gobj.priv;
|
|
3559
3863
|
let treedb = current_treedb(gobj);
|
|
3560
3864
|
|
|
3865
|
+
if(reload_first(gobj, event)) {
|
|
3866
|
+
return -1;
|
|
3867
|
+
}
|
|
3868
|
+
|
|
3561
3869
|
if(!treedb) {
|
|
3562
3870
|
log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
|
|
3563
3871
|
return -1;
|
|
@@ -3577,6 +3885,10 @@ function ac_export(gobj, event, kw, src)
|
|
|
3577
3885
|
{
|
|
3578
3886
|
let treedb = current_treedb(gobj);
|
|
3579
3887
|
|
|
3888
|
+
if(reload_first(gobj, event)) {
|
|
3889
|
+
return -1;
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3580
3892
|
if(!treedb) {
|
|
3581
3893
|
log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
|
|
3582
3894
|
return -1;
|
|
@@ -3592,6 +3904,9 @@ function ac_import(gobj, event, kw, src)
|
|
|
3592
3904
|
if(refuse_if_readonly(gobj, event)) {
|
|
3593
3905
|
return -1;
|
|
3594
3906
|
}
|
|
3907
|
+
if(reload_first(gobj, event)) {
|
|
3908
|
+
return -1;
|
|
3909
|
+
}
|
|
3595
3910
|
if(!treedb) {
|
|
3596
3911
|
log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
|
|
3597
3912
|
return -1;
|
|
@@ -3610,6 +3925,13 @@ function ac_preview_import(gobj, event, kw, src)
|
|
|
3610
3925
|
let treedb = current_treedb(gobj);
|
|
3611
3926
|
let incoming = null;
|
|
3612
3927
|
|
|
3928
|
+
if(reload_first(gobj, event)) {
|
|
3929
|
+
return -1;
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
3933
|
+
return -1;
|
|
3934
|
+
}
|
|
3613
3935
|
if(!treedb) {
|
|
3614
3936
|
log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
|
|
3615
3937
|
return -1;
|
|
@@ -3637,6 +3959,12 @@ function ac_apply_import(gobj, event, kw, src)
|
|
|
3637
3959
|
if(refuse_if_readonly(gobj, event)) {
|
|
3638
3960
|
return -1;
|
|
3639
3961
|
}
|
|
3962
|
+
if(reload_first(gobj, event)) {
|
|
3963
|
+
return -1;
|
|
3964
|
+
}
|
|
3965
|
+
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
3966
|
+
return -1;
|
|
3967
|
+
}
|
|
3640
3968
|
if(!plan || plan.writes.length === 0) {
|
|
3641
3969
|
log_error(`${gobj_short_name(gobj)}: ${event} with no plan to run`);
|
|
3642
3970
|
return -1;
|
|
@@ -3660,8 +3988,12 @@ function run_import(gobj)
|
|
|
3660
3988
|
let priv = gobj.priv;
|
|
3661
3989
|
let plan = priv.import_plan;
|
|
3662
3990
|
|
|
3991
|
+
/* Not an ERROR: a load that left forgets the plan (request_model()),
|
|
3992
|
+
* and the confirmation of it may still be up. The operator's Yes
|
|
3993
|
+
* is answered, not lost in the log. */
|
|
3663
3994
|
if(!plan) {
|
|
3664
|
-
|
|
3995
|
+
log_warning(`${gobj_short_name(gobj)}: the import plan is gone`);
|
|
3996
|
+
yui_shell_show_error(yui_shell_of(gobj), "the import plan is gone: preview it again", {t: t});
|
|
3665
3997
|
return -1;
|
|
3666
3998
|
}
|
|
3667
3999
|
close_dialog(gobj);
|
|
@@ -3687,6 +4019,12 @@ function ac_confirmed(gobj, event, kw, src)
|
|
|
3687
4019
|
if(refuse_if_readonly(gobj, event)) {
|
|
3688
4020
|
return -1;
|
|
3689
4021
|
}
|
|
4022
|
+
if(reload_first(gobj, event)) {
|
|
4023
|
+
return -1;
|
|
4024
|
+
}
|
|
4025
|
+
if(built_on_a_replaced_model(gobj, event, kw)) {
|
|
4026
|
+
return -1;
|
|
4027
|
+
}
|
|
3690
4028
|
if(kw.what === "import") {
|
|
3691
4029
|
return run_import(gobj);
|
|
3692
4030
|
}
|
|
@@ -3782,7 +4120,9 @@ function create_gclass(gclass_name)
|
|
|
3782
4120
|
* a click is worth routing through a machine.
|
|
3783
4121
|
*---------------------------------------------*/
|
|
3784
4122
|
const states = [
|
|
3785
|
-
["ST_IDLE", COMMON.
|
|
4123
|
+
["ST_IDLE", COMMON.concat([
|
|
4124
|
+
["EV_CONFIRMED", ac_confirmed_on_nothing, null]
|
|
4125
|
+
])],
|
|
3786
4126
|
|
|
3787
4127
|
["ST_LOADING", COMMON.concat([
|
|
3788
4128
|
["EV_SAVE_COLUMN", ac_wait_for_the_load, null],
|
|
@@ -3793,7 +4133,9 @@ function create_gclass(gclass_name)
|
|
|
3793
4133
|
["EV_CONFIRMED", ac_wait_for_the_load, null]
|
|
3794
4134
|
])],
|
|
3795
4135
|
|
|
3796
|
-
["ST_EMPTY", COMMON.
|
|
4136
|
+
["ST_EMPTY", COMMON.concat([
|
|
4137
|
+
["EV_CONFIRMED", ac_confirmed_on_nothing, null]
|
|
4138
|
+
])],
|
|
3797
4139
|
|
|
3798
4140
|
["ST_TREEDBS", COMMON.concat([
|
|
3799
4141
|
["EV_SELECT_TREEDB", ac_select_treedb, null],
|