@yuneta/gobj-ui 7.23.167 → 7.23.169

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24331,7 +24331,8 @@ function request_print_tranger$1(gobj, path) {
24331
24331
  expanded: 1,
24332
24332
  lists_limit: 100,
24333
24333
  dicts_limit: 100,
24334
- path: path || ""
24334
+ path: path || "",
24335
+ __md_command__: { path: path || "" }
24335
24336
  }, gobj);
24336
24337
  if (ret) log_error(ret);
24337
24338
  }
@@ -25557,49 +25558,65 @@ function yui_icon_is_defined(name) {
25557
25558
  /***********************************************************************
25558
25559
  * yui_row_search.js
25559
25560
  *
25560
- * ¿ESTÁ ESTE TÉRMINO EN ESTA FILA?
25561
+ * IS THIS TERM IN THIS ROW?
25562
+ *
25563
+ * The search box of a treedb table looked at the row's values with
25564
+ * `String(val)`, and a treedb row is not flat: an fkey arrives as a
25565
+ * LIST OF OBJECTS `[{id, topic_name, hook_name}]`, and `String()`
25566
+ * of that is `"[object Object]"`. So looking for the workshop of
25567
+ * a device -- which is where the datum an operator has in mind
25568
+ * lives -- never found anything, and the box gave no hint that it
25569
+ * had looked somewhere else.
25570
+ *
25571
+ * THREE RULES, AND THE LAST TWO ARE WHAT MAKE IT USEFUL:
25572
+ *
25573
+ * - It walks down lists and objects to a short depth: an fkey is
25574
+ * two levels down and nothing anybody searches for is deeper.
25575
+ * - **Of an fkey only the `id` is looked at.** `topic_name` and
25576
+ * `hook_name` are the SAME two words on every row, so looking at
25577
+ * them turns the term into a wildcard: searching "devices" would
25578
+ * bring the whole topic. The `id` is the only thing that names
25579
+ * the linked thing.
25580
+ * - **A hook read as its COUNT is not looked at at all.** Since
25581
+ * `hook_size` (7.23.163) a topic table loads a hook as
25582
+ * `[{"size": N}]` instead of the id of every child, and that
25583
+ * number is not a value of the row: matching it made "5" answer
25584
+ * every row whose hook happens to hold 5 children, next to the
25585
+ * rows that really say 5. A count nobody can see in the cell as
25586
+ * text is not what a person typing in a search box is after.
25587
+ *
25588
+ * Keys that start with `_` are not looked at on any level: they
25589
+ * belong to the scaffolding (`_check_box_state_`, `_operation`) or
25590
+ * are metadata (`__md_treedb__`), and nobody searches by them.
25561
25591
  *
25562
- * La caja de búsqueda de una tabla de treedb miraba los valores de
25563
- * la fila con `String(val)`, y una fila de treedb no es plana: un
25564
- * fkey llega como una LISTA DE OBJETOS
25565
- * `[{id, topic_name, hook_name}]`, y `String()` de eso es
25566
- * `"[object Object]"`. O sea que buscar el taller de un equipo --
25567
- * que es donde vive el dato que un operador tiene en la cabeza --
25568
- * no encontraba nunca nada, y la caja no daba ninguna pista de que
25569
- * había mirado en otro sitio.
25570
- *
25571
- * DOS REGLAS, Y LA SEGUNDA ES LA QUE HACE QUE SIRVA:
25572
- *
25573
- * - Se baja por listas y objetos hasta una profundidad corta: un
25574
- * fkey está a dos niveles y nada de lo que se busca está más
25575
- * hondo.
25576
- * - **De un fkey se mira sólo el `id`.** `topic_name` y
25577
- * `hook_name` son las MISMAS dos palabras en todas las filas, así
25578
- * que mirarlas convierte el término en un comodín: buscar
25579
- * "devices" traería el topic entero. El `id` es lo único que
25580
- * nombra la cosa enlazada.
25581
- *
25582
- * Las claves que empiezan por `_` no se miran en ningún nivel: son
25583
- * del armazón (`_check_box_state_`, `_operation`) o metadatos
25584
- * (`__md_treedb__`), y nadie busca por ellas.
25585
- *
25586
- * Copyright (c) 2026, ArtGins.
25592
+ * Copyright (c) 2024-2026, ArtGins.
25587
25593
  * All Rights Reserved.
25588
25594
  ***********************************************************************/
25589
25595
  var MAX_DEPTH = 4;
25590
25596
  /***************************************************************
25591
- * Un fkey, tal y como `nodes` lo contesta con `list_dict`.
25597
+ * An fkey, as `nodes` answers it with `list_dict`.
25592
25598
  ***************************************************************/
25593
25599
  function is_fkey_ref(value) {
25594
25600
  return !!value && typeof value === "object" && !Array.isArray(value) && "id" in value && "topic_name" in value && "hook_name" in value;
25595
25601
  }
25596
25602
  /***************************************************************
25597
- * ¿Contiene `value` el término? El término viene YA en
25598
- * minúsculas: quien busca lo baja una vez y no una por celda.
25603
+ * A hook read with `hook_size`: the COUNT of its children,
25604
+ * `[{"size": N}]`, in the place of the children themselves.
25605
+ * The same shape `delete_impact`'s `ref_count()` reads.
25606
+ ***************************************************************/
25607
+ function is_hook_size(value) {
25608
+ if (!Array.isArray(value) || value.length !== 1) return false;
25609
+ const first = value[0];
25610
+ return !!first && typeof first === "object" && !Array.isArray(first) && typeof first.size === "number" && Object.keys(first).length === 1;
25611
+ }
25612
+ /***************************************************************
25613
+ * Does `value` hold the term? The term comes ALREADY in lower
25614
+ * case: whoever searches lowers it once, not once per cell.
25599
25615
  ***************************************************************/
25600
25616
  function value_matches(value, term, depth) {
25601
25617
  if (value === null || value === void 0) return false;
25602
25618
  if (depth > MAX_DEPTH) return false;
25619
+ if (is_hook_size(value)) return false;
25603
25620
  if (Array.isArray(value)) return value.some((v) => value_matches(v, term, depth + 1));
25604
25621
  if (typeof value === "object") {
25605
25622
  if (is_fkey_ref(value)) return value_matches(value.id, term, depth + 1);
@@ -25611,7 +25628,7 @@ function value_matches(value, term, depth) {
25611
25628
  return String(value).toLowerCase().includes(term);
25612
25629
  }
25613
25630
  /***************************************************************
25614
- * ¿Está el término en alguna parte de esta fila?
25631
+ * Is the term anywhere in this row?
25615
25632
  ***************************************************************/
25616
25633
  function row_matches(row, term) {
25617
25634
  if (!row || typeof row !== "object" || !term) return false;
@@ -25817,8 +25834,8 @@ var attrs_table$5 = [
25817
25834
  SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
25818
25835
  SDATA(data_type_t.DTP_BOOLEAN, "with_radio", 0, false, "Auxiliary first column to select one row"),
25819
25836
  SDATA(data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Show the shared selection bar (\"N selected\" + a way out) above the table while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, and a host that has not defined \"{{n}} selected\" and \"clear selection\" would render the keys"),
25820
- SDATA(data_type_t.DTP_BOOLEAN, "broadcast_select_rows_event", 0, false, "Broadcast select rows event"),
25821
- SDATA(data_type_t.DTP_BOOLEAN, "broadcast_unselect_rows_event", 0, false, "Broadcast unselect rows event"),
25837
+ SDATA(data_type_t.DTP_BOOLEAN, "broadcast_select_rows_event", 0, false, "Publish EV_SELECT_ROWS to the subscriber. OFF by default: the event is an OUTPUT event, so a host that turns it on must declare EV_SELECT_ROWS in its own FSM or it answers \"Event NOT DEFINED in state\" on every selected row"),
25838
+ SDATA(data_type_t.DTP_BOOLEAN, "broadcast_unselect_rows_event", 0, false, "Publish EV_UNSELECT_ROWS to the subscriber. Same contract as broadcast_select_rows_event, and INDEPENDENT of it: a host that wants both sets both"),
25822
25839
  SDATA(data_type_t.DTP_JSON, "tabulator_settings", 0, {
25823
25840
  layout: "fitDataFill",
25824
25841
  columnDefaults: { resizable: true },
@@ -26097,7 +26114,11 @@ function build_ui$5(gobj) {
26097
26114
  "button",
26098
26115
  {
26099
26116
  id: ``,
26100
- class: "button button-edit-record mr-1"
26117
+ class: "button button-edit-record mr-1",
26118
+ title: t("edit"),
26119
+ "data-i18n-title": "edit",
26120
+ "aria-label": t("edit"),
26121
+ "data-i18n-aria-label": "edit"
26101
26122
  },
26102
26123
  [yui_toolbar_icon("yi-pen"), [
26103
26124
  "span",
@@ -26120,6 +26141,10 @@ function build_ui$5(gobj) {
26120
26141
  {
26121
26142
  id: ``,
26122
26143
  class: "button button-new-record mr-1",
26144
+ title: t("new"),
26145
+ "data-i18n-title": "new",
26146
+ "aria-label": t("new"),
26147
+ "data-i18n-aria-label": "new",
26123
26148
  disabled: true
26124
26149
  },
26125
26150
  [yui_toolbar_icon("yi-plus"), [
@@ -26144,6 +26169,10 @@ function build_ui$5(gobj) {
26144
26169
  {
26145
26170
  id: ``,
26146
26171
  class: "button button-delete-record mr-1",
26172
+ title: t("delete"),
26173
+ "data-i18n-title": "delete",
26174
+ "aria-label": t("delete"),
26175
+ "data-i18n-aria-label": "delete",
26147
26176
  disabled: true
26148
26177
  },
26149
26178
  [yui_toolbar_icon("yi-trash"), [
@@ -26168,6 +26197,10 @@ function build_ui$5(gobj) {
26168
26197
  {
26169
26198
  id: ``,
26170
26199
  class: "button button-copy-record mr-1",
26200
+ title: t("copy"),
26201
+ "data-i18n-title": "copy",
26202
+ "aria-label": t("copy"),
26203
+ "data-i18n-aria-label": "copy",
26171
26204
  disabled: true
26172
26205
  },
26173
26206
  [yui_toolbar_icon("yi-copy"), [
@@ -26192,6 +26225,10 @@ function build_ui$5(gobj) {
26192
26225
  {
26193
26226
  id: ``,
26194
26227
  class: "button button-paste-record mr-1",
26228
+ title: t("paste"),
26229
+ "data-i18n-title": "paste",
26230
+ "aria-label": t("paste"),
26231
+ "data-i18n-aria-label": "paste",
26195
26232
  disabled: true
26196
26233
  },
26197
26234
  [yui_toolbar_icon("yi-paste"), [
@@ -26231,7 +26268,11 @@ function build_ui$5(gobj) {
26231
26268
  class: "input",
26232
26269
  type: "text",
26233
26270
  placeholder: t("search"),
26234
- "data-i18n-placeholder": "search"
26271
+ "data-i18n-placeholder": "search",
26272
+ title: t("search"),
26273
+ "data-i18n-title": "search",
26274
+ "aria-label": t("search"),
26275
+ "data-i18n-aria-label": "search"
26235
26276
  }], [
26236
26277
  "span",
26237
26278
  { class: "icon is-left" },
@@ -26251,7 +26292,9 @@ function build_ui$5(gobj) {
26251
26292
  {
26252
26293
  class: "button mr-1",
26253
26294
  title: t("refresh"),
26254
- "data-i18n-title": "refresh"
26295
+ "data-i18n-title": "refresh",
26296
+ "aria-label": t("refresh"),
26297
+ "data-i18n-aria-label": "refresh"
26255
26298
  },
26256
26299
  [yui_toolbar_icon("yi-arrows-rotate"), [
26257
26300
  "span",
@@ -26784,7 +26827,7 @@ function create_tabulator(gobj) {
26784
26827
  let index = cell.getRow().getPosition();
26785
26828
  if (e.target.closest(".edit")) {
26786
26829
  e.stopPropagation();
26787
- show_edit_form(gobj, row, index);
26830
+ gobj_send_event(gobj, "EV_EDIT_RECORD", { index }, gobj);
26788
26831
  } else if (e.target.closest(".remove")) {
26789
26832
  e.stopPropagation();
26790
26833
  gobj_send_event(gobj, "EV_DELETE_ROWS", {
@@ -27189,8 +27232,9 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
27189
27232
  * C_YUI_FORM renders a col that is not `writable` read-only on
27190
27233
  * its own (`build_form_field_conf`), so this is the whole of
27191
27234
  * it. What must NOT follow is sending them back:
27192
- * `transform__form_record_2_treedb_record()` drops them, and
27193
- * the reason is written there.
27235
+ * `strip_read_only_cols()` drops them from every write of the
27236
+ * form (`publish_treedb_write()`), and the reason is written at
27237
+ * `col_goes_back_to_treedb()`.
27194
27238
  *
27195
27239
  * Still out: hooks (a hook is not a field of the record, it is
27196
27240
  * the children hanging off it), hidden cols, and the `_`
@@ -27245,11 +27289,11 @@ function open_form_dialog(gobj, mode, record) {
27245
27289
  return;
27246
27290
  }
27247
27291
  let pkey = desc.pkey || "id";
27248
- let title;
27249
- if (mode === "create") title = t("new") + " " + t(topic_name);
27250
- else {
27292
+ let title = mode === "create" ? "new" : mode === "view" ? "view" : "edit";
27293
+ let title_prefix = "";
27294
+ if (mode !== "create") {
27251
27295
  let rid = record ? String(record[pkey] ?? "") : "";
27252
- title = t(topic_name) + (empty_string(rid) ? "" : " — " + rid);
27296
+ title_prefix = empty_string(rid) ? "" : rid;
27253
27297
  }
27254
27298
  let view_only = mode === "view";
27255
27299
  let form_mode = view_only ? "update" : mode;
@@ -27284,6 +27328,7 @@ function open_form_dialog(gobj, mode, record) {
27284
27328
  priv.form_modal = yui_shell_show_modal(shell, $body, {
27285
27329
  dialog: true,
27286
27330
  logical_class: "TREEDB_FORM_SHEET",
27331
+ title_prefix,
27287
27332
  title,
27288
27333
  t,
27289
27334
  before_close: function() {
@@ -27312,14 +27357,7 @@ function form_may_close(gobj) {
27312
27357
  let kw = {};
27313
27358
  gobj_send_event(priv.form, "EV_WINDOW_TO_CLOSE", kw, gobj);
27314
27359
  if (kw.abort_close) {
27315
- yui_shell_confirm_yesnocancel(yui_shell_of(gobj), kw.warning || "all changes will be lost", {
27316
- t,
27317
- yes_label: "yes",
27318
- no_label: "no",
27319
- cancel_label: "cancel"
27320
- }).then(function(answer) {
27321
- if (answer === "yes" && priv.form_modal) priv.form_modal.close();
27322
- });
27360
+ confirm_then$1(gobj, kw.warning || "all changes will be lost", { what: "close_form" });
27323
27361
  return false;
27324
27362
  }
27325
27363
  }
@@ -27341,6 +27379,31 @@ function teardown_form_child(gobj) {
27341
27379
  priv.form_modal = null;
27342
27380
  }
27343
27381
  /************************************************************
27382
+ * Ask, and turn the answer into an EVENT.
27383
+ *
27384
+ * A dialog's promise settles outside the machine: acting from its
27385
+ * `.then` puts the decision -- delete these records, throw these
27386
+ * edits away -- in a callback the `machine` trace never sees, and
27387
+ * in a view that may be gone by the time the person answers.
27388
+ *
27389
+ * `kw` carries an IDENTITY and never an object: the trace dumps
27390
+ * it, and a gobj, a Tabulator row or a DOM node in there is a
27391
+ * circular structure that breaks the very trace the FSM feeds.
27392
+ *
27393
+ * Same shape as C_YUI_SCHEMA_EDITOR's confirm_then().
27394
+ ************************************************************/
27395
+ function confirm_then$1(gobj, message, kw) {
27396
+ yui_shell_confirm_yesnocancel(yui_shell_of(gobj), message, {
27397
+ t,
27398
+ yes_label: "yes",
27399
+ no_label: "no",
27400
+ cancel_label: "cancel"
27401
+ }).then(function(answer) {
27402
+ if (answer !== "yes" || gobj_is_destroying(gobj)) return;
27403
+ gobj_send_event(gobj, "EV_CONFIRMED", kw, gobj);
27404
+ });
27405
+ }
27406
+ /************************************************************
27344
27407
  * Force the dialog down (bypasses the unsaved guard): used after a
27345
27408
  * successful save and on teardown. Closing the modal runs its
27346
27409
  * on_close, which tears the form child down.
@@ -27921,7 +27984,7 @@ function pkey_is_rowid(gobj) {
27921
27984
  * Show the edit "update" form of a row (record)
27922
27985
  * internally called from the icon edit at Op column
27923
27986
  ************************************************************/
27924
- function show_edit_form(gobj, row, index) {
27987
+ function show_edit_form(gobj, row) {
27925
27988
  open_form_dialog(gobj, pkey_is_rowid(gobj) ? "create" : "update", row);
27926
27989
  }
27927
27990
  /************************************************************
@@ -27983,18 +28046,61 @@ function get_schema_col(gobj, id) {
27983
28046
  return null;
27984
28047
  }
27985
28048
  /************************************************************
28049
+ * Does column `col` travel back to treedb on a write?
28050
+ *
28051
+ * The form SHOWS every field and sends back only what the topic
28052
+ * accepts: the writable cols, the fkeys (a link is edited by
28053
+ * linking) and the pkey, which is not written but is what
28054
+ * ADDRESSES the record.
28055
+ *
28056
+ * It matters because `treedb_update_node()` does not check
28057
+ * `writable` -- it writes any col it is handed -- so a read-only
28058
+ * field travelling back is written with whatever the form made of
28059
+ * it, and the fields that describe a record are exactly the ones
28060
+ * that do not survive a round trip through a widget: a `time` is an
28061
+ * integer rendered as a `datetime-local`, with no seconds, so every
28062
+ * save moved it back up to 59 s. Nothing looked wrong until the
28063
+ * stored timestamp had moved.
28064
+ *
28065
+ * `is_file` and not the type: a `file` column IS an fkey (it is
28066
+ * flagged ['fkey','file']) but answers `type: "file"`, and the write
28067
+ * goes out with `autolink`, which rebuilds the links from what the
28068
+ * record carries. Dropped, a read-only `file` column -- the one only
28069
+ * a load fills -- was UNLINKED by every save of any other field.
28070
+ ************************************************************/
28071
+ function col_goes_back_to_treedb(gobj, col) {
28072
+ let pkey = gobj_read_attr(gobj, "desc").pkey || "id";
28073
+ const field_desc = treedb_get_field_desc(col);
28074
+ if (field_desc.is_writable || field_desc.type === "fkey" || field_desc.is_file || col.id === pkey) return true;
28075
+ return false;
28076
+ }
28077
+ /************************************************************
28078
+ * The record the FORM hands over, without the columns that do
28079
+ * not go back to treedb (see col_goes_back_to_treedb()). The
28080
+ * form has already encoded the values, so only the columns are
28081
+ * taken out; keys that are not columns are left as they are.
28082
+ ************************************************************/
28083
+ function strip_read_only_cols(gobj, record) {
28084
+ let row = {};
28085
+ for (let field_name of Object.keys(record || {})) {
28086
+ let col = get_schema_col(gobj, field_name);
28087
+ if (col && !col_goes_back_to_treedb(gobj, col)) continue;
28088
+ row[field_name] = record[field_name];
28089
+ }
28090
+ return row;
28091
+ }
28092
+ /************************************************************
27986
28093
  * Convert from frontend to backend
27987
28094
  * operation: "create" "update"
27988
28095
  ************************************************************/
27989
28096
  function transform__form_record_2_treedb_record(gobj, kw, operation) {
27990
- let pkey = gobj_read_attr(gobj, "desc").pkey || "id";
28097
+ gobj_read_attr(gobj, "desc").pkey;
27991
28098
  let row = {};
27992
28099
  for (let field_name of Object.keys(kw)) {
27993
28100
  if (empty_string(field_name)) continue;
27994
28101
  let col = get_schema_col(gobj, field_name);
27995
28102
  if (!col) continue;
27996
- const field_desc = treedb_get_field_desc(col);
27997
- if (!field_desc.is_writable && field_desc.type !== "fkey" && !field_desc.is_file && col.id !== pkey) continue;
28103
+ if (!col_goes_back_to_treedb(gobj, col)) continue;
27998
28104
  let value = kw[field_name];
27999
28105
  row[field_name] = transform__form_value_2_treedb_value(gobj, col, value, operation);
28000
28106
  }
@@ -28309,10 +28415,10 @@ function ac_edition_mode(gobj, event, kw, src) {
28309
28415
  tabulator.showColumn("_operation");
28310
28416
  tabulator.showColumn("_check_box_state_");
28311
28417
  $button_new_record.removeAttribute("disabled");
28312
- $button_paste_record.removeAttribute("disabled");
28418
+ if ($button_paste_record) $button_paste_record.removeAttribute("disabled");
28313
28419
  if (yui_selected_rows(tabulator).length) {
28314
28420
  $button_delete_record.removeAttribute("disabled");
28315
- $button_copy_record.removeAttribute("disabled");
28421
+ if ($button_copy_record) $button_copy_record.removeAttribute("disabled");
28316
28422
  }
28317
28423
  } else {
28318
28424
  $button_edit_record.classList.remove("is-primary");
@@ -28322,8 +28428,8 @@ function ac_edition_mode(gobj, event, kw, src) {
28322
28428
  tabulator.hideColumn("_check_box_state_");
28323
28429
  $button_new_record.setAttribute("disabled", true);
28324
28430
  $button_delete_record.setAttribute("disabled", true);
28325
- $button_copy_record.setAttribute("disabled", true);
28326
- $button_paste_record.setAttribute("disabled", true);
28431
+ if ($button_copy_record) $button_copy_record.setAttribute("disabled", true);
28432
+ if ($button_paste_record) $button_paste_record.setAttribute("disabled", true);
28327
28433
  }
28328
28434
  render_selection_bar(gobj);
28329
28435
  return 0;
@@ -28377,31 +28483,57 @@ function ac_delete_rows(gobj, event, kw, src) {
28377
28483
  });
28378
28484
  return 0;
28379
28485
  }
28380
- yui_shell_confirm_yesnocancel(yui_shell_of(gobj), build_delete_question(gobj, rows), {
28381
- t,
28382
- yes_label: "yes",
28383
- no_label: "no",
28384
- cancel_label: "cancel"
28385
- }).then(function(answer) {
28386
- if (answer === "yes") for (let row of rows) gobj_publish_event(gobj, "EV_DELETE_RECORD", {
28387
- topic_name: gobj_read_str_attr(gobj, "topic_name"),
28388
- record: row
28389
- });
28390
- });
28391
- } else yui_shell_confirm_yesnocancel(yui_shell_of(gobj), build_delete_question(gobj, kw.row), {
28392
- t,
28393
- yes_label: "yes",
28394
- no_label: "no",
28395
- cancel_label: "cancel"
28396
- }).then(function(answer) {
28397
- if (answer === "yes") gobj_publish_event(gobj, "EV_DELETE_RECORD", {
28398
- topic_name: gobj_read_str_attr(gobj, "topic_name"),
28399
- record: kw.row
28400
- });
28486
+ confirm_then$1(gobj, build_delete_question(gobj, rows), { what: "delete_selection" });
28487
+ } else confirm_then$1(gobj, build_delete_question(gobj, kw.row), {
28488
+ what: "delete_row",
28489
+ index: kw.index
28401
28490
  });
28402
28491
  return 0;
28403
28492
  }
28404
28493
  /************************************************************
28494
+ * {what, ...} -- the person said yes to what confirm_then()
28495
+ * asked. The rows are read again HERE and not carried in the
28496
+ * kw: the kw is plain json, and a Tabulator row is not.
28497
+ ************************************************************/
28498
+ function ac_confirmed$2(gobj, event, kw, src) {
28499
+ let priv = gobj.priv;
28500
+ let what = kw ? kw.what : "";
28501
+ if (what === "close_form") {
28502
+ if (priv.form_modal) priv.form_modal.close();
28503
+ return 0;
28504
+ }
28505
+ if (refuse_if_readonly$2(gobj, event)) return -1;
28506
+ let tabulator = gobj_read_attr(gobj, "tabulator");
28507
+ let topic_name = gobj_read_str_attr(gobj, "topic_name");
28508
+ if (what === "delete_selection") {
28509
+ let rows = yui_selected_rows(tabulator);
28510
+ if (!rows.length) {
28511
+ log_error(`${gobj_short_name(gobj)}: ${event} with the selection gone`);
28512
+ return -1;
28513
+ }
28514
+ for (let row of rows) gobj_publish_event(gobj, "EV_DELETE_RECORD", {
28515
+ topic_name,
28516
+ record: row
28517
+ });
28518
+ return 0;
28519
+ }
28520
+ if (what === "delete_row") {
28521
+ let index = kw.index;
28522
+ let row = tabulator && typeof index === "number" ? tabulator.getRowFromPosition(index) : null;
28523
+ if (!row) {
28524
+ log_error(`${gobj_short_name(gobj)}: ${event} names no row: ${index}`);
28525
+ return -1;
28526
+ }
28527
+ gobj_publish_event(gobj, "EV_DELETE_RECORD", {
28528
+ topic_name,
28529
+ record: row.getData()
28530
+ });
28531
+ return 0;
28532
+ }
28533
+ log_error(`${gobj_short_name(gobj)}: ${event} of nothing this view asked about: ${what}`);
28534
+ return -1;
28535
+ }
28536
+ /************************************************************
28405
28537
  * From internal
28406
28538
  ************************************************************/
28407
28539
  function ac_copy_rows(gobj, event, kw, src) {
@@ -28456,9 +28588,7 @@ function ac_form_save_record(gobj, event, kw, src) {
28456
28588
  const cols = Object.keys(picked);
28457
28589
  if (cols.length === 0) {
28458
28590
  publish_treedb_write(gobj, mode, kw);
28459
- setTimeout(function() {
28460
- close_form_dialog(gobj);
28461
- }, 0);
28591
+ gobj_post_event(gobj, "EV_CLOSE_FORM", {}, gobj);
28462
28592
  return 0;
28463
28593
  }
28464
28594
  const serial = ++priv.read_serial;
@@ -28530,12 +28660,20 @@ function read_is_current(gobj, kw) {
28530
28660
  function publish_treedb_write(gobj, mode, record, jn_files) {
28531
28661
  const kw_write = {
28532
28662
  topic_name: gobj_read_str_attr(gobj, "topic_name"),
28533
- record
28663
+ record: strip_read_only_cols(gobj, record)
28534
28664
  };
28535
28665
  if (jn_files && Object.keys(jn_files).length > 0) kw_write.record.__files__ = jn_files;
28536
28666
  gobj_publish_event(gobj, mode === "create" ? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD", kw_write);
28537
28667
  }
28538
28668
  /************************************************************
28669
+ * The deferred close of the form dialog, posted by the save
28670
+ * from inside the form child's own publish stack.
28671
+ ************************************************************/
28672
+ function ac_close_form(gobj, event, kw, src) {
28673
+ close_form_dialog(gobj);
28674
+ return 0;
28675
+ }
28676
+ /************************************************************
28539
28677
  * {mode, record, picks} -- the bytes are here now
28540
28678
  ************************************************************/
28541
28679
  function ac_files_read(gobj, event, kw, src) {
@@ -28576,7 +28714,7 @@ function ac_select_rows(gobj, event, kw, src) {
28576
28714
  ************************************************************/
28577
28715
  function ac_unselect_rows(gobj, event, kw, src) {
28578
28716
  render_selection_state(gobj);
28579
- if (gobj_read_bool_attr(gobj, "broadcast_select_rows_event")) gobj_publish_event(gobj, event, kw);
28717
+ if (gobj_read_bool_attr(gobj, "broadcast_unselect_rows_event")) gobj_publish_event(gobj, event, kw);
28580
28718
  return 0;
28581
28719
  }
28582
28720
  /************************************************************
@@ -29056,6 +29194,26 @@ function ac_show_record(gobj, event, kw, src) {
29056
29194
  return 0;
29057
29195
  }
29058
29196
  /************************************************************
29197
+ * {index} -- the pencil of the Op column. Same shape as
29198
+ * EV_SHOW_RECORD: the kw carries WHICH row, and the row itself
29199
+ * is resolved here, where the table is.
29200
+ ************************************************************/
29201
+ function ac_edit_record(gobj, event, kw, src) {
29202
+ let tabulator = gobj_read_attr(gobj, "tabulator");
29203
+ let index = kw ? kw.index : void 0;
29204
+ if (!tabulator || typeof index !== "number") {
29205
+ log_error(`${gobj_short_name(gobj)}: no row ${index} to edit`);
29206
+ return -1;
29207
+ }
29208
+ let row = tabulator.getRowFromPosition(index);
29209
+ if (!row) {
29210
+ log_error(`${gobj_short_name(gobj)}: row ${index} is gone`);
29211
+ return -1;
29212
+ }
29213
+ show_edit_form(gobj, row.getData());
29214
+ return 0;
29215
+ }
29216
+ /************************************************************
29059
29217
  * The reader picked another page size: remember it, so coming back to
29060
29218
  * this topic opens where they left it instead of at 200.
29061
29219
  ************************************************************/
@@ -29180,6 +29338,11 @@ function create_gclass$5(gclass_name) {
29180
29338
  ac_form_save_record,
29181
29339
  null
29182
29340
  ],
29341
+ [
29342
+ "EV_CLOSE_FORM",
29343
+ ac_close_form,
29344
+ null
29345
+ ],
29183
29346
  [
29184
29347
  "EV_FILES_READ",
29185
29348
  ac_files_read,
@@ -29200,6 +29363,11 @@ function create_gclass$5(gclass_name) {
29200
29363
  ac_delete_rows,
29201
29364
  null
29202
29365
  ],
29366
+ [
29367
+ "EV_CONFIRMED",
29368
+ ac_confirmed$2,
29369
+ null
29370
+ ],
29203
29371
  [
29204
29372
  "EV_SELECT_ROWS",
29205
29373
  ac_select_rows,
@@ -29325,6 +29493,11 @@ function create_gclass$5(gclass_name) {
29325
29493
  ac_show_record,
29326
29494
  null
29327
29495
  ],
29496
+ [
29497
+ "EV_EDIT_RECORD",
29498
+ ac_edit_record,
29499
+ null
29500
+ ],
29328
29501
  [
29329
29502
  "EV_TOGGLE_COLUMN",
29330
29503
  ac_toggle_column,
@@ -29354,10 +29527,12 @@ function create_gclass$5(gclass_name) {
29354
29527
  ["EV_NODE_DELETED", 0],
29355
29528
  ["EV_EDITION_MODE", 0],
29356
29529
  ["EV_SAVE_RECORD", 0],
29530
+ ["EV_CLOSE_FORM", 0],
29357
29531
  ["EV_FILES_READ", 0],
29358
29532
  ["EV_FILES_FAILED", 0],
29359
29533
  ["EV_NEW_ROW", 0],
29360
29534
  ["EV_DELETE_ROWS", 0],
29535
+ ["EV_CONFIRMED", 0],
29361
29536
  ["EV_COPY_ROWS", 0],
29362
29537
  ["EV_PASTE_ROWS", 0],
29363
29538
  ["EV_CLEAR_SELECTION", 0],
@@ -29387,6 +29562,7 @@ function create_gclass$5(gclass_name) {
29387
29562
  ["EV_OPEN_COLUMNS", 0],
29388
29563
  ["EV_PAGE_SIZE_CHANGED", 0],
29389
29564
  ["EV_SHOW_RECORD", 0],
29565
+ ["EV_EDIT_RECORD", 0],
29390
29566
  ["EV_TOGGLE_COLUMN", 0],
29391
29567
  ["EV_EXPORT_TABLE", 0],
29392
29568
  ["EV_CREATE_RECORD", event_flag_t.EVF_OUTPUT_EVENT],
@@ -57920,13 +58096,38 @@ function build_graph$1(gobj) {
57920
58096
  graph.setLayout(layout);
57921
58097
  priv.graph_rendered = false;
57922
58098
  graph_render(gobj).then(() => {
58099
+ if (gobj_is_destroying(gobj) || !priv.graph) return;
57923
58100
  configure_events(gobj);
57924
58101
  configure_behaviour(gobj);
57925
58102
  configure_plugins(gobj);
57926
58103
  priv.graph_rendered = true;
58104
+ }).catch((e) => {
58105
+ log_error(`${gobj_short_name(gobj)}: graph render failed: ${e}`);
57927
58106
  });
57928
58107
  }
57929
58108
  /************************************************************
58109
+ * A G6 event, as PLAIN JSON.
58110
+ *
58111
+ * A kw is dumped by the `machine` trace (`trace_json(kw)`), and a
58112
+ * G6 / @antv/g event is circular: serializing one THROWS, so the
58113
+ * first thing a kw carrying `evt` breaks is the trace the FSM
58114
+ * exists to feed. It also drags a live graph element into a
58115
+ * message that may be read after that element is gone.
58116
+ *
58117
+ * What the actions read of it is an id and three scalars. The
58118
+ * event itself never leaves the callback that received it.
58119
+ ************************************************************/
58120
+ function g6_event_kw(evt) {
58121
+ let target = evt && evt.target ? evt.target : null;
58122
+ let client = evt && evt.client ? evt.client : null;
58123
+ return {
58124
+ id: target && target.id ? String(target.id) : "",
58125
+ shift: !!(evt && evt.shiftKey),
58126
+ client_x: client && typeof client.x === "number" ? client.x : 0,
58127
+ client_y: client && typeof client.y === "number" ? client.y : 0
58128
+ };
58129
+ }
58130
+ /************************************************************
57930
58131
  * Configure G6 event handlers
57931
58132
  ************************************************************/
57932
58133
  function configure_events(gobj) {
@@ -57934,7 +58135,7 @@ function configure_events(gobj) {
57934
58135
  let graph = priv.graph;
57935
58136
  graph.on(CanvasEvent.CLICK, (evt) => {
57936
58137
  if (consume_long_press_click(graph)) return;
57937
- gobj_send_event(gobj, "EV_CANVAS_CLICK", { evt }, gobj);
58138
+ gobj_send_event(gobj, "EV_CANVAS_CLICK", g6_event_kw(evt), gobj);
57938
58139
  });
57939
58140
  graph.on(NodeEvent.DRAG, (evt) => {
57940
58141
  update_resize_handles_position(gobj);
@@ -57945,21 +58146,21 @@ function configure_events(gobj) {
57945
58146
  update_port_icon_position(gobj);
57946
58147
  });
57947
58148
  graph.on(NodeEvent.DRAG_END, (evt) => {
57948
- gobj_send_event(gobj, "EV_NODE_DRAG_END", { evt }, gobj);
58149
+ gobj_send_event(gobj, "EV_NODE_DRAG_END", g6_event_kw(evt), gobj);
57949
58150
  });
57950
58151
  graph.on(NodeEvent.CLICK, (evt) => {
57951
58152
  if (consume_long_press_click(graph)) return;
57952
- gobj_send_event(gobj, "EV_NODE_CLICK", { evt }, gobj);
58153
+ gobj_send_event(gobj, "EV_NODE_CLICK", g6_event_kw(evt), gobj);
57953
58154
  });
57954
58155
  graph.on(NodeEvent.CONTEXT_MENU, (evt) => {
57955
- gobj_send_event(gobj, "EV_NODE_CONTEXT_MENU", { evt }, gobj);
58156
+ gobj_send_event(gobj, "EV_NODE_CONTEXT_MENU", g6_event_kw(evt), gobj);
57956
58157
  });
57957
58158
  graph.on(NodeEvent.DBLCLICK, (evt) => {
57958
- gobj_send_event(gobj, "EV_NODE_DBLCLICK", { evt }, gobj);
58159
+ gobj_send_event(gobj, "EV_NODE_DBLCLICK", g6_event_kw(evt), gobj);
57959
58160
  });
57960
58161
  graph.on(EdgeEvent.CLICK, (evt) => {
57961
58162
  if (consume_long_press_click(graph)) return;
57962
- gobj_send_event(gobj, "EV_EDGE_CLICK", { evt }, gobj);
58163
+ gobj_send_event(gobj, "EV_EDGE_CLICK", g6_event_kw(evt), gobj);
57963
58164
  });
57964
58165
  graph.on("aftertransform", () => {
57965
58166
  update_resize_handles_position(gobj);
@@ -58043,7 +58244,7 @@ function configure_plugins(gobj) {
58043
58244
  graph_add_plugin(gobj, "contextmenu", {
58044
58245
  trigger: "contextmenu",
58045
58246
  onClick: (value) => {
58046
- handle_context_menu_click(gobj, value);
58247
+ gobj_send_event(gobj, "EV_CONTEXT_MENU_ITEM", { value: String(value) }, gobj);
58047
58248
  },
58048
58249
  getItems: (e) => {
58049
58250
  return build_context_menu_items(gobj, e);
@@ -61759,7 +61960,7 @@ function show_edge_icon(gobj) {
61759
61960
  if (!priv._selected_edge_id) return;
61760
61961
  let mid = get_edge_viewport_midpoint(gobj, priv._selected_edge_id);
61761
61962
  if (!mid) return;
61762
- let icons = show_dual_icons(gobj, mid.x + 4, mid.y + floating_icon_dy(), t("edge properties"), () => toggle_edge_popover(gobj), t("unlink"), () => request_unlink_edge(gobj));
61963
+ let icons = show_dual_icons(gobj, mid.x + 4, mid.y + floating_icon_dy(), t("edge properties"), () => toggle_edge_popover(gobj), t("unlink"), () => gobj_send_event(gobj, "EV_REQUEST_UNLINK_EDGE", {}, gobj));
61763
61964
  priv._edge_icon_el = icons.icon_el;
61764
61965
  priv._edge_delete_el = icons.delete_el;
61765
61966
  }
@@ -61914,7 +62115,7 @@ function show_node_icon(gobj) {
61914
62115
  if (!priv._selected_node_id || priv._selected_port_key) return;
61915
62116
  let rect = get_node_viewport_rect(gobj, priv._selected_node_id);
61916
62117
  if (!rect) return;
61917
- let icons = show_dual_icons(gobj, rect.right + 4, rect.top + floating_icon_dy(), t("node properties"), () => toggle_node_popover(gobj), t("delete node"), () => request_delete_node(gobj));
62118
+ let icons = show_dual_icons(gobj, rect.right + 4, rect.top + floating_icon_dy(), t("node properties"), () => toggle_node_popover(gobj), t("delete node"), () => gobj_send_event(gobj, "EV_REQUEST_DELETE_NODE", {}, gobj));
61918
62119
  priv._node_icon_el = icons.icon_el;
61919
62120
  priv._node_delete_el = icons.delete_el;
61920
62121
  }
@@ -62627,7 +62828,10 @@ function request_delete_selection(gobj) {
62627
62828
  execute_delete_selection(gobj, nodes);
62628
62829
  return;
62629
62830
  }
62630
- show_confirm_popover(gobj, null, delete_question(nodes), "delete", "#ff4d4f", () => execute_delete_selection(gobj, nodes), "_delete_confirm_el");
62831
+ show_confirm_popover(gobj, null, delete_question(nodes), "delete", "#ff4d4f", () => gobj_send_event(gobj, "EV_CONFIRMED", {
62832
+ what: "delete_selection",
62833
+ ids: nodes.map((nd) => String(nd.id))
62834
+ }, gobj), "_delete_confirm_el");
62631
62835
  }
62632
62836
  function execute_delete_selection(gobj, nodes) {
62633
62837
  let priv = gobj.priv;
@@ -62644,7 +62848,10 @@ function execute_delete_selection(gobj, nodes) {
62644
62848
  ************************************************************/
62645
62849
  function show_delete_confirm(gobj, nodeData) {
62646
62850
  let priv = gobj.priv;
62647
- show_confirm_popover(gobj, priv._node_delete_el, delete_question([nodeData]), "delete", "#ff4d4f", () => execute_delete_node(gobj, nodeData), "_delete_confirm_el");
62851
+ show_confirm_popover(gobj, priv._node_delete_el, delete_question([nodeData]), "delete", "#ff4d4f", () => gobj_send_event(gobj, "EV_CONFIRMED", {
62852
+ what: "delete_node",
62853
+ id: String(nodeData.id)
62854
+ }, gobj), "_delete_confirm_el");
62648
62855
  }
62649
62856
  function hide_delete_confirm(gobj) {
62650
62857
  hide_overlay(gobj, "_delete_confirm_el");
@@ -62742,7 +62949,10 @@ function show_create_popover(gobj) {
62742
62949
  idInput.focus();
62743
62950
  return;
62744
62951
  }
62745
- execute_create_node(gobj, topicSelect.value, node_id);
62952
+ gobj_send_event(gobj, "EV_SUBMIT_NEW_NODE", {
62953
+ topic_name: String(topicSelect.value),
62954
+ id: node_id
62955
+ }, gobj);
62746
62956
  }
62747
62957
  }]);
62748
62958
  createBtn = popover.querySelector("button:last-child");
@@ -62802,7 +63012,10 @@ function execute_unlink_edge(gobj, edgeData) {
62802
63012
  function show_unlink_confirm(gobj, edgeData) {
62803
63013
  let priv = gobj.priv;
62804
63014
  const d = edgeData.data;
62805
- show_confirm_popover(gobj, priv._edge_delete_el, t("unlink") + " " + d.child_id + " → " + d.parent_id + "?\n" + t("neither record is deleted"), "unlink", "#ff4d4f", () => execute_unlink_edge(gobj, edgeData), "_unlink_confirm_el");
63015
+ show_confirm_popover(gobj, priv._edge_delete_el, t("unlink") + " " + d.child_id + " → " + d.parent_id + "?\n" + t("neither record is deleted"), "unlink", "#ff4d4f", () => gobj_send_event(gobj, "EV_CONFIRMED", {
63016
+ what: "unlink_edge",
63017
+ id: String(edgeData.id)
63018
+ }, gobj), "_unlink_confirm_el");
62806
63019
  }
62807
63020
  function hide_unlink_confirm(gobj) {
62808
63021
  hide_overlay(gobj, "_unlink_confirm_el");
@@ -64685,7 +64898,7 @@ function ac_toggle_node_mode(gobj, event, kw, src) {
64685
64898
  ************************************************************/
64686
64899
  function ac_node_dblclick(gobj, event, kw, src) {
64687
64900
  let priv = gobj.priv;
64688
- let node_id = kw && kw.evt && kw.evt.target ? kw.evt.target.id : "";
64901
+ let node_id = kw && kw.id ? kw.id : "";
64689
64902
  let nd = null;
64690
64903
  try {
64691
64904
  nd = priv.graph.getNodeData(node_id);
@@ -64881,7 +65094,7 @@ function ac_brush_select(gobj, event, kw, src) {
64881
65094
  function ac_node_click$2(gobj, event, kw, src) {
64882
65095
  let priv = gobj.priv;
64883
65096
  let graph = priv.graph;
64884
- let node_id = kw.evt.target.id;
65097
+ let node_id = kw && kw.id ? kw.id : "";
64885
65098
  let more_nd = null;
64886
65099
  try {
64887
65100
  more_nd = graph.getNodeData(node_id);
@@ -64908,10 +65121,10 @@ function ac_node_click$2(gobj, event, kw, src) {
64908
65121
  topic_name: nodedata.data.desc.topic_name,
64909
65122
  record: nodedata.data.record
64910
65123
  });
64911
- if (priv.edit_mode && (kw.evt.shiftKey || priv.selection_mode)) toggle_in_selection(gobj, node_id);
65124
+ if (priv.edit_mode && (kw.shift || priv.selection_mode)) toggle_in_selection(gobj, node_id);
64912
65125
  else if (priv.edit_mode) {
64913
65126
  let containerRect = priv.$container.getBoundingClientRect();
64914
- let canvasPoint = graph.getCanvasByViewport([kw.evt.client.x - containerRect.left, kw.evt.client.y - containerRect.top]);
65127
+ let canvasPoint = graph.getCanvasByViewport([kw.client_x - containerRect.left, kw.client_y - containerRect.top]);
64915
65128
  let port_key = detect_port_click(gobj, node_id, canvasPoint[0], canvasPoint[1]);
64916
65129
  if (priv._linking_mode) {
64917
65130
  if (port_key && try_complete_link(gobj, node_id, port_key)) return 0;
@@ -64934,7 +65147,7 @@ function ac_node_click$2(gobj, event, kw, src) {
64934
65147
  function ac_edge_click(gobj, event, kw, src) {
64935
65148
  let priv = gobj.priv;
64936
65149
  let graph = priv.graph;
64937
- let edge_id = kw.evt.target.id;
65150
+ let edge_id = kw && kw.id ? kw.id : "";
64938
65151
  try {
64939
65152
  let edgedata = graph.getEdgeData(edge_id);
64940
65153
  if (edgedata && edgedata.data) {
@@ -64963,6 +65176,106 @@ function ac_node_context_menu(gobj, event, kw, src) {
64963
65176
  return 0;
64964
65177
  }
64965
65178
  /************************************************************
65179
+ * {value} -- an item of the context menu was picked.
65180
+ ************************************************************/
65181
+ function ac_context_menu_item(gobj, event, kw, src) {
65182
+ handle_context_menu_click(gobj, kw && kw.value ? kw.value : "");
65183
+ return 0;
65184
+ }
65185
+ /************************************************************
65186
+ * The delete icon floating beside the selected node.
65187
+ ************************************************************/
65188
+ function ac_request_delete_node(gobj, event, kw, src) {
65189
+ request_delete_node(gobj);
65190
+ return 0;
65191
+ }
65192
+ /************************************************************
65193
+ * The unlink icon floating beside the selected edge.
65194
+ ************************************************************/
65195
+ function ac_request_unlink_edge(gobj, event, kw, src) {
65196
+ request_unlink_edge(gobj);
65197
+ return 0;
65198
+ }
65199
+ /************************************************************
65200
+ * {topic_name, id} -- the create popover was filled in.
65201
+ ************************************************************/
65202
+ function ac_submit_new_node(gobj, event, kw, src) {
65203
+ let topic_name = kw && kw.topic_name ? kw.topic_name : "";
65204
+ let node_id = kw && kw.id ? kw.id : "";
65205
+ if (!topic_name || !node_id) {
65206
+ log_error(`${gobj_short_name(gobj)}: ${event} without topic or id`);
65207
+ return -1;
65208
+ }
65209
+ execute_create_node(gobj, topic_name, node_id);
65210
+ return 0;
65211
+ }
65212
+ /************************************************************
65213
+ * {what, id|ids} -- the person said yes in a confirm popover.
65214
+ *
65215
+ * The kw carries IDS and not the graph elements they name: a kw
65216
+ * is plain json, and a node/edge datum of a live graph is not
65217
+ * something to park in a message. They are resolved here, where
65218
+ * the graph is -- and an id that no longer names anything is a
65219
+ * loud refusal instead of a silent nothing.
65220
+ ************************************************************/
65221
+ function ac_confirmed$1(gobj, event, kw, src) {
65222
+ let graph = gobj.priv.graph;
65223
+ let what = kw && kw.what ? kw.what : "";
65224
+ if (!graph) {
65225
+ log_error(`${gobj_short_name(gobj)}: ${event} with no graph`);
65226
+ return -1;
65227
+ }
65228
+ if (what === "delete_selection") {
65229
+ let ids = kw && Array.isArray(kw.ids) ? kw.ids : [];
65230
+ let nodes = [];
65231
+ for (let id of ids) {
65232
+ let nd = null;
65233
+ try {
65234
+ nd = graph.getNodeData(id);
65235
+ } catch (e) {
65236
+ nd = null;
65237
+ }
65238
+ if (nd && nd.data && nd.data.desc) nodes.push(nd);
65239
+ }
65240
+ if (!nodes.length) {
65241
+ log_error(`${gobj_short_name(gobj)}: ${event} names no node any more`);
65242
+ return -1;
65243
+ }
65244
+ execute_delete_selection(gobj, nodes);
65245
+ return 0;
65246
+ }
65247
+ if (what === "delete_node") {
65248
+ let nd = null;
65249
+ try {
65250
+ nd = graph.getNodeData(kw.id);
65251
+ } catch (e) {
65252
+ nd = null;
65253
+ }
65254
+ if (!nd || !nd.data || !nd.data.desc) {
65255
+ log_error(`${gobj_short_name(gobj)}: ${event} names no node: ${kw.id}`);
65256
+ return -1;
65257
+ }
65258
+ execute_delete_node(gobj, nd);
65259
+ return 0;
65260
+ }
65261
+ if (what === "unlink_edge") {
65262
+ let ed = null;
65263
+ try {
65264
+ ed = graph.getEdgeData(kw.id);
65265
+ } catch (e) {
65266
+ ed = null;
65267
+ }
65268
+ if (!ed || !ed.data) {
65269
+ log_error(`${gobj_short_name(gobj)}: ${event} names no edge: ${kw.id}`);
65270
+ return -1;
65271
+ }
65272
+ execute_unlink_edge(gobj, ed);
65273
+ return 0;
65274
+ }
65275
+ log_error(`${gobj_short_name(gobj)}: ${event} of nothing this view asked about: ${what}`);
65276
+ return -1;
65277
+ }
65278
+ /************************************************************
64966
65279
  * Canvas click
64967
65280
  ************************************************************/
64968
65281
  function ac_canvas_click(gobj, event, kw, src) {
@@ -65126,6 +65439,31 @@ function create_gclass$4(gclass_name) {
65126
65439
  ac_node_context_menu,
65127
65440
  null
65128
65441
  ],
65442
+ [
65443
+ "EV_CONTEXT_MENU_ITEM",
65444
+ ac_context_menu_item,
65445
+ null
65446
+ ],
65447
+ [
65448
+ "EV_REQUEST_DELETE_NODE",
65449
+ ac_request_delete_node,
65450
+ null
65451
+ ],
65452
+ [
65453
+ "EV_REQUEST_UNLINK_EDGE",
65454
+ ac_request_unlink_edge,
65455
+ null
65456
+ ],
65457
+ [
65458
+ "EV_SUBMIT_NEW_NODE",
65459
+ ac_submit_new_node,
65460
+ null
65461
+ ],
65462
+ [
65463
+ "EV_CONFIRMED",
65464
+ ac_confirmed$1,
65465
+ null
65466
+ ],
65129
65467
  [
65130
65468
  "EV_CANVAS_CLICK",
65131
65469
  ac_canvas_click,
@@ -65342,6 +65680,11 @@ function create_gclass$4(gclass_name) {
65342
65680
  ["EV_NODE_CLICK", 0],
65343
65681
  ["EV_EDGE_CLICK", 0],
65344
65682
  ["EV_NODE_CONTEXT_MENU", 0],
65683
+ ["EV_CONTEXT_MENU_ITEM", 0],
65684
+ ["EV_REQUEST_DELETE_NODE", 0],
65685
+ ["EV_REQUEST_UNLINK_EDGE", 0],
65686
+ ["EV_SUBMIT_NEW_NODE", 0],
65687
+ ["EV_CONFIRMED", 0],
65345
65688
  ["EV_CANVAS_CLICK", 0],
65346
65689
  ["EV_NODE_DRAG_END", 0],
65347
65690
  ["EV_BRUSH_SELECT", 0],
@@ -66406,7 +66749,8 @@ function request_print_tranger(gobj, path) {
66406
66749
  expanded: 1,
66407
66750
  lists_limit: 100,
66408
66751
  dicts_limit: 100,
66409
- path: path || ""
66752
+ path: path || "",
66753
+ __md_command__: { path: path || "" }
66410
66754
  }, gobj);
66411
66755
  if (ret) log_error(ret);
66412
66756
  }