@yuneta/gobj-ui 7.26.28 → 7.26.30

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.
@@ -65,6 +65,7 @@ import {
65
65
 
66
66
  import { resolve_route, normalize_route } from "./route_resolver.js";
67
67
  import { text_node } from "./yui_text.js";
68
+ import { badge_node, badge_tone, paint_badge } from "./yui_badge.js";
68
69
  import { build_nav_map } from "./route_map_model.js";
69
70
  import {
70
71
  section_index_target,
@@ -118,6 +119,14 @@ let PRIVATE_DATA = {
118
119
  stages: {},
119
120
  navs: [],
120
121
  item_index: {},
122
+ /* Menu item id -> {badge, badge_tone}: what yui_shell_set_nav_item_badge()
123
+ * said last for that id. It wins over the item's own `badge`, and
124
+ * every nav the shell creates is handed it (`badges`), so a nav built
125
+ * or rebuilt after the call draws it too. */
126
+ nav_badges: {},
127
+ /* Parent item id -> the items yui_shell_set_submenu() put in place:
128
+ * menu items as much as the declared ones (nav_item_declared). */
129
+ dynamic_items: {},
121
130
  /* Section route -> last position visited inside it. Mirrors the
122
131
  * url for the duration of the page; see remember_position(). */
123
132
  section_pos: {},
@@ -281,7 +290,8 @@ function mt_start(gobj)
281
290
 
282
291
  build_item_index(gobj, config);
283
292
  instantiate_menus(gobj, config);
284
- build_toolbar(gobj, config);
293
+ /* The app's shell declaration: its toolbar's keys are read there */
294
+ build_toolbar(gobj, /* i18n: data */ config);
285
295
 
286
296
  /* lifecycle: "eager" — preinstantiate views that must exist from boot. */
287
297
  preinstantiate_eager_views(gobj);
@@ -1242,6 +1252,7 @@ function instantiate_nav_in_zone(gobj, menu, menu_id, zone_id, level, nav_label)
1242
1252
  level: level,
1243
1253
  show_on: render_cfg.show_on || "",
1244
1254
  back_route: render_cfg.back_route || "",
1255
+ badges: Object.assign({}, priv.nav_badges),
1245
1256
  shell: gobj
1246
1257
  },
1247
1258
  gobj
@@ -1693,6 +1704,7 @@ function navigate_to(gobj, route, depth, no_drain)
1693
1704
 
1694
1705
  function build_view_gobj(gobj, entry, route, stage)
1695
1706
  {
1707
+ let priv = gobj.priv;
1696
1708
  let target = entry.target;
1697
1709
  /* The deep-link tail is NOT injected into kw: gobj_create2
1698
1710
  * validates kw against the view's SDATA strictly, so an extra
@@ -1701,6 +1713,11 @@ function build_view_gobj(gobj, entry, route, stage)
1701
1713
  * mount (and on every later subpath-only change) — that single
1702
1714
  * mechanism is how a view owns its dynamic 3rd level. */
1703
1715
  let kw = target.kw || {};
1716
+ if(target.gclass === "C_YUI_NAV") {
1717
+ /* A section-index landing is a nav of the shell, mounted when
1718
+ * the user first walks into it: it draws the badges set before. */
1719
+ kw = Object.assign({}, kw, {badges: Object.assign({}, priv.nav_badges)});
1720
+ }
1704
1721
  let name = target.name || `view_${safe_id(route)}`;
1705
1722
 
1706
1723
  let view;
@@ -1781,7 +1798,7 @@ function safe_id(s)
1781
1798
  ************************************************************/
1782
1799
  function build_toolbar(gobj, config)
1783
1800
  {
1784
- let tb = config && config.toolbar;
1801
+ const tb = config && config.toolbar;
1785
1802
  if(!tb || !is_array(tb.items)) {
1786
1803
  return;
1787
1804
  }
@@ -1847,51 +1864,6 @@ function build_toolbar(gobj, config)
1847
1864
  $zone.appendChild($bar);
1848
1865
  }
1849
1866
 
1850
- /************************************************************
1851
- * The badge node of a toolbar item — the little count pinned to
1852
- * the corner of its icon (unread notifications, active alarms).
1853
- *
1854
- * ALWAYS rendered, hidden when empty, so the runtime setter has a
1855
- * node to write into and does not have to rebuild the button.
1856
- *
1857
- * EMPTY IS 0, "" AND null ALIKE: a badge reading "0" is worse than
1858
- * no badge — it draws the eye to say nothing. A count is only
1859
- * worth a pixel when there IS something.
1860
- *
1861
- * Capped at 99+: the toolbar is a fixed-width row, and a four
1862
- * digit pill pushes the items next to it off a phone screen.
1863
- *
1864
- * role="status" and NOT aria-hidden, deliberately. The button
1865
- * carries an explicit aria-label, and an explicit label REPLACES
1866
- * the element's content for a screen reader — so a badge inside
1867
- * it would be silent. As its own live region it is both read and
1868
- * announced when the count changes, which is the whole point of a
1869
- * badge that means "something needs you".
1870
- ************************************************************/
1871
- function badge_text(value)
1872
- {
1873
- if(value === null || value === undefined || value === false) {
1874
- return "";
1875
- }
1876
- if(typeof value === "number") {
1877
- if(!isFinite(value) || value <= 0) {
1878
- return "";
1879
- }
1880
- return (value > 99) ? "99+" : String(Math.floor(value));
1881
- }
1882
- return String(value).trim();
1883
- }
1884
-
1885
- function badge_node(value)
1886
- {
1887
- let text = badge_text(value);
1888
- let attrs = {class: "yui-toolbar-badge", role: "status"};
1889
- if(!text) {
1890
- attrs.hidden = "hidden";
1891
- }
1892
- return ["span", attrs, text];
1893
- }
1894
-
1895
1867
  /************************************************************
1896
1868
  * The attributes that turn a toolbar item into a real link, or
1897
1869
  * null when this item is not one. Answering null is what keeps
@@ -1930,11 +1902,14 @@ function build_toolbar_action_item(gobj, it)
1930
1902
  if(!empty_string(it.icon)) {
1931
1903
  /* The badge is anchored to the ICON, not to the button: with a
1932
1904
  * label on (desktop) the button is wide, and a badge pinned to
1933
- * its corner would float far from the glyph it counts. */
1905
+ * its corner would float far from the glyph it counts. The
1906
+ * count, its tone and their rules: yui_badge.js. */
1934
1907
  children.push(["span", {class: "icon yui-toolbar-icon"},
1935
1908
  [
1936
1909
  ["i", {class: it.icon, "aria-hidden": "true"}],
1937
- badge_node(it.badge)
1910
+ badge_node(it.badge, it.badge_tone,
1911
+ "TOOLBAR_ITEM_BADGE yui-badge yui-toolbar-badge",
1912
+ `C_YUI_SHELL: toolbar item '${it.id || "?"}'`)
1938
1913
  ]
1939
1914
  ]);
1940
1915
  }
@@ -2341,7 +2316,7 @@ function open_toolbar_dropdown(gobj, item, action, $trigger)
2341
2316
  let raw_items = is_array(action.items) ? action.items : [];
2342
2317
  for(let i = 0; i < raw_items.length; i++) {
2343
2318
  let sub = raw_items[i];
2344
- let $row = build_dropdown_row(gobj, sub, i);
2319
+ let $row = build_dropdown_row(gobj, /* i18n: data */ sub, i); // the app's shell declaration
2345
2320
  if(!$row) {
2346
2321
  continue;
2347
2322
  }
@@ -3219,6 +3194,60 @@ function update_secondary_nav_visibility(gobj, entry)
3219
3194
  }
3220
3195
  }
3221
3196
 
3197
+ /************************************************************
3198
+ * Does a menu of this shell declare an item `item_id`: an item
3199
+ * of a menu of app_config.json, of one of their submenus, or
3200
+ * one given to yui_shell_set_submenu() since.
3201
+ ************************************************************/
3202
+ function nav_item_declared(gobj, item_id)
3203
+ {
3204
+ let priv = gobj.priv;
3205
+ let config = gobj_read_attr(gobj, "config") || {};
3206
+ let menus = is_object(config.menu) ? config.menu : {};
3207
+ let holds = (items) => is_array(items) && items.some((it) => it && it.id === item_id);
3208
+
3209
+ for(let menu_id in menus) {
3210
+ let items = menus[menu_id] && menus[menu_id].items;
3211
+ if(!is_array(items)) {
3212
+ continue;
3213
+ }
3214
+ if(holds(items)) {
3215
+ return true;
3216
+ }
3217
+ for(let it of items) {
3218
+ if(it && it.submenu && holds(it.submenu.items)) {
3219
+ return true;
3220
+ }
3221
+ }
3222
+ }
3223
+ for(let parent_item_id in priv.dynamic_items) {
3224
+ if(holds(priv.dynamic_items[parent_item_id])) {
3225
+ return true;
3226
+ }
3227
+ }
3228
+ return false;
3229
+ }
3230
+
3231
+ /************************************************************
3232
+ * Every C_YUI_NAV of this shell: the navs of its menus, and the
3233
+ * section-index landings mounted in a stage.
3234
+ ************************************************************/
3235
+ function shell_navs(gobj)
3236
+ {
3237
+ let priv = gobj.priv;
3238
+ let navs = priv.navs.slice();
3239
+ for(let stage_name in priv.stages) {
3240
+ let views = priv.stages[stage_name].items || {};
3241
+ for(let route in views) {
3242
+ let view = views[route];
3243
+ if(view && gobj_gclass_name(view) === "C_YUI_NAV") {
3244
+ navs.push(view);
3245
+ }
3246
+ }
3247
+ }
3248
+ return navs;
3249
+ }
3250
+
3222
3251
 
3223
3252
 
3224
3253
 
@@ -3348,8 +3377,11 @@ function ac_nav_item_close(gobj, event, kw, src)
3348
3377
  * items of a primary item's submenu (its secondary nav) at runtime
3349
3378
  * and re-registers their routes so navigation resolves. Item
3350
3379
  * descriptors may carry { id, name, icon, route, class, closable,
3351
- * target:{stage,gclass,kw,lifecycle} }. Routes present before but
3352
- * absent now are pruned (index entry removed, mounted view destroyed).
3380
+ * badge, badge_tone, target:{stage,gclass,kw,lifecycle} }. Routes
3381
+ * present before but absent now are pruned (index entry removed,
3382
+ * mounted view destroyed). A badge set with
3383
+ * yui_shell_set_nav_item_badge() for an id stays on that id here too:
3384
+ * the rebuilt nav draws it, whatever the descriptor's own `badge`.
3353
3385
  ************************************************************/
3354
3386
  function find_secondary_nav(priv, parent_item_id)
3355
3387
  {
@@ -3470,6 +3502,7 @@ function yui_shell_set_submenu(shell_gobj, parent_item_id, items)
3470
3502
  };
3471
3503
  }
3472
3504
  priv.dynamic_routes[parent_item_id] = new_routes;
3505
+ priv.dynamic_items[parent_item_id] = items;
3473
3506
 
3474
3507
  /* Push the new items into the nav (rebuilds its DOM in place), and
3475
3508
  * re-apply the translator: the rebuilt labels carry their keys as
@@ -4232,22 +4265,31 @@ function yui_shell_set_toolbar_item_icon(shell_gobj, item_id, icon_class)
4232
4265
  * its icon.
4233
4266
  *
4234
4267
  * yui_shell_set_toolbar_item_badge(shell, "alarms", 3);
4268
+ * yui_shell_set_toolbar_item_badge(shell, "alarms", 3, "warning");
4235
4269
  * yui_shell_set_toolbar_item_badge(shell, "alarms", 0); // hidden
4236
4270
  *
4237
4271
  * This is the API that matters: a count is a RUNTIME fact. The
4238
- * `badge` field of a toolbar item only seeds the first paint, and
4239
- * an app whose number never changes did not need a badge.
4272
+ * `badge` / `badge_tone` fields of a toolbar item only seed the
4273
+ * first paint, and an app whose number never changes did not need
4274
+ * a badge.
4240
4275
  *
4241
4276
  * 0 / "" / null / false all clear it, and a number over 99 renders
4242
- * "99+" — see badge_node(). Accepts a string for the states that
4243
- * are not counts ("!", "…").
4277
+ * "99+" — see yui_badge.js. Accepts a string for the states that
4278
+ * are not counts ("!", "…"). `tone`, optional, is one of
4279
+ * BADGE_TONES ("danger" | "warning" | "info" | "success" |
4280
+ * "primary" | "link"): Bulma's is-<tone> on the badge. Each call
4281
+ * states the WHOLE badge: no tone is the default look, not the
4282
+ * tone it had. The twin of yui_shell_set_nav_item_badge().
4244
4283
  *
4245
4284
  * Silent no-op on an unknown item id: a toolbar that does not
4246
4285
  * declare the item (an app without alarms) must not be an error
4247
- * at every tick of whatever feeds the number.
4286
+ * at every tick of whatever feeds the number. An unknown TONE is
4287
+ * the caller's error: logged, and drawn with the default look.
4248
4288
  ************************************************************/
4249
- function yui_shell_set_toolbar_item_badge(shell_gobj, item_id, value)
4289
+ function yui_shell_set_toolbar_item_badge(shell_gobj, item_id, value, tone)
4250
4290
  {
4291
+ tone = badge_tone(tone, `C_YUI_SHELL: yui_shell_set_toolbar_item_badge('${item_id}')`);
4292
+
4251
4293
  if(!shell_gobj || !is_gobj(shell_gobj) || empty_string(item_id)) {
4252
4294
  return;
4253
4295
  }
@@ -4261,21 +4303,59 @@ function yui_shell_set_toolbar_item_badge(shell_gobj, item_id, value)
4261
4303
  if(!$badge) {
4262
4304
  return;
4263
4305
  }
4306
+ paint_badge($badge, value, tone);
4307
+ }
4264
4308
 
4265
- let text = badge_text(value);
4266
- if(!text) {
4267
- $badge.textContent = "";
4268
- $badge.setAttribute("hidden", "hidden");
4309
+ /************************************************************
4310
+ * Set (or clear) the badge of a MENU item — the twin of
4311
+ * yui_shell_set_toolbar_item_badge(), for an entry of a menu of
4312
+ * app_config.json, of one of their submenus, or one given to
4313
+ * yui_shell_set_submenu().
4314
+ *
4315
+ * yui_shell_set_nav_item_badge(shell, "alarms", 3);
4316
+ * yui_shell_set_nav_item_badge(shell, "alarms", 3, "warning");
4317
+ * yui_shell_set_nav_item_badge(shell, "alarms", 0); // hidden
4318
+ *
4319
+ * The same value, tone and rules as the toolbar's (yui_badge.js),
4320
+ * each call stating the whole badge.
4321
+ *
4322
+ * EVERY instance of the item gets it: the same entry is drawn by
4323
+ * as many navs as there are zones hosting its menu (a rail on
4324
+ * desktop, an icon bar on a phone), and by a drawer that repeats
4325
+ * it. Each nav repaints it in place (EV_SET_ITEM_BADGE), and
4326
+ * KEEPS it: a rebuild of the nav (a yui_shell_set_submenu()) draws
4327
+ * it again. The shell keeps it too, for the navs it creates
4328
+ * afterwards (a section-index landing, mounted when the user walks
4329
+ * into it). From then on it is the badge of that id: the `badge`
4330
+ * field of an item only seeds the paint until this is called.
4331
+ *
4332
+ * Silent no-op on an id no menu declares, as the toolbar's: a menu
4333
+ * without the entry (an app without alarms) must not be an error
4334
+ * at every tick of whatever feeds the number. An unknown TONE is
4335
+ * the caller's error: logged, and drawn with the default look.
4336
+ ************************************************************/
4337
+ function yui_shell_set_nav_item_badge(shell_gobj, item_id, value, tone)
4338
+ {
4339
+ tone = badge_tone(tone, `C_YUI_SHELL: yui_shell_set_nav_item_badge('${item_id}')`);
4340
+
4341
+ /* Silent, as the toolbar's: whatever feeds the number may tick
4342
+ * before the shell exists, after it is gone, and in an app whose
4343
+ * menus have no such entry. */
4344
+ if(!shell_gobj || !is_gobj(shell_gobj) || gobj_is_destroying(shell_gobj) ||
4345
+ empty_string(item_id)) {
4269
4346
  return;
4270
4347
  }
4271
- /* Do not touch the DOM when nothing changed: role="status" is a
4272
- * live region, and rewriting the same number would have a screen
4273
- * reader announce it again on every tick. */
4274
- if($badge.textContent === text && !$badge.hasAttribute("hidden")) {
4348
+ let priv = shell_gobj.priv;
4349
+ if(!priv || !nav_item_declared(shell_gobj, item_id)) {
4275
4350
  return;
4276
4351
  }
4277
- $badge.textContent = text;
4278
- $badge.removeAttribute("hidden");
4352
+ let badge = (value === undefined) ? null : value;
4353
+ priv.nav_badges[item_id] = {badge: badge, badge_tone: tone};
4354
+
4355
+ for(let nav of shell_navs(shell_gobj)) {
4356
+ gobj_send_event(nav, "EV_SET_ITEM_BADGE",
4357
+ {item_id: item_id, badge: badge, badge_tone: tone}, shell_gobj);
4358
+ }
4279
4359
  }
4280
4360
 
4281
4361
  /************************************************************
@@ -4329,6 +4409,7 @@ export {
4329
4409
  yui_shell_set_connection_state,
4330
4410
  yui_shell_set_toolbar_item_icon,
4331
4411
  yui_shell_set_toolbar_item_badge,
4412
+ yui_shell_set_nav_item_badge,
4332
4413
  yui_shell_close_dropdown,
4333
4414
  yui_shell_set_submenu
4334
4415
  };
@@ -1361,8 +1361,7 @@ function answer_print_tranger_refused(gobj, path, answer)
1361
1361
  gobj_send_event(jv, "EV_SUBTREE_ERROR",
1362
1362
  Object.assign({path: path}, answer), gobj);
1363
1363
  } else {
1364
- yui_shell_show_error(yui_shell_of(gobj),
1365
- /* i18n: data */ answer.i18n || answer.error, {t: t});
1364
+ yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, {t: t});
1366
1365
  }
1367
1366
  }
1368
1367
 
@@ -1413,7 +1412,7 @@ function request_print_tranger(gobj, path)
1413
1412
  }, gobj);
1414
1413
  if(ret) {
1415
1414
  log_error(ret);
1416
- answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
1415
+ answer_print_tranger_refused(gobj, path, {error: /* i18n: data */ refusal_text(ret)});
1417
1416
  return;
1418
1417
  }
1419
1418
  if(path) {
@@ -2193,7 +2192,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
2193
2192
  /* The schema couldn't load (not a treedb, no authz for it, backend
2194
2193
  * down…). Show an inline banner in the view rather than a blocking
2195
2194
  * app-modal that wedges the whole SPA behind an empty tab. */
2196
- show_load_error(gobj, t(comment));
2195
+ show_load_error(gobj, t(/* i18n: data */ comment));
2197
2196
  } else {
2198
2197
  yui_shell_show_error(yui_shell_of(gobj), /* i18n: data */ comment, {t: t});
2199
2198
  }
@@ -325,7 +325,7 @@ function present_json_popup(gobj, $box, opts)
325
325
 
326
326
  if(is_mobile()) {
327
327
  return {
328
- modal: yui_shell_show_modal(shell, /* i18n: data */ $box, {
328
+ modal: yui_shell_show_modal(shell, $box, {
329
329
  dialog: true,
330
330
  logical_class: opts.logical_class + "_SHEET",
331
331
  title_prefix: opts.title_prefix,
@@ -1512,7 +1512,7 @@ function create_tabulator(gobj)
1512
1512
 
1513
1513
  let colDef = {
1514
1514
  title: col_label(col, gobj_read_str_attr(gobj, "topic_name")),
1515
- titleFormatter: col_title_formatter(gobj, col),
1515
+ titleFormatter: col_title_formatter(gobj, /* i18n: data */ col), // a column of the schema
1516
1516
  field: col.id,
1517
1517
  sorter: sorter,
1518
1518
  hozAlign: hozAlign,
@@ -2496,7 +2496,7 @@ function open_form_dialog(gobj, mode, record)
2496
2496
  * are wired by the shell. `before_close` preserves the unsaved-changes
2497
2497
  * guard — it vetoes the dismiss and, on confirm, closes the modal
2498
2498
  * itself. `on_close` tears the hosted form child down. */
2499
- priv.form_modal = yui_shell_show_modal(shell, /* i18n: data */ $body, {
2499
+ priv.form_modal = yui_shell_show_modal(shell, $body, {
2500
2500
  dialog: true,
2501
2501
  logical_class: "TREEDB_FORM_SHEET",
2502
2502
  title_prefix: title_prefix,
@@ -2676,7 +2676,7 @@ function open_schema_dialog(gobj)
2676
2676
  /* Title split in two halves: the topic name is DATA (never
2677
2677
  * translated) and "schema" is the kind (carries its i18n key), so
2678
2678
  * the header re-translates on a language switch. */
2679
- let presented = present_json_popup(gobj, $box, {
2679
+ let presented = present_json_popup(gobj, /* i18n: data */ $box, { // the viewer's own DOM
2680
2680
  name: "schemawin_" + clean_name(gobj_name(gobj)),
2681
2681
  logical_class: "TREEDB_SCHEMA",
2682
2682
  title_prefix: gobj_read_str_attr(gobj, "topic_name"),
@@ -2803,11 +2803,11 @@ function open_cell_json_dialog(gobj, row_id, col_id)
2803
2803
  * i18n key — the shared `col.id` one, so the header re-translates on a
2804
2804
  * language switch and reads as the raw id where the app defines no key
2805
2805
  * (exactly what an untranslated column header already shows). */
2806
- let presented = present_json_popup(gobj, $box, {
2806
+ let presented = present_json_popup(gobj, /* i18n: data */ $box, { // the viewer's own DOM
2807
2807
  name: "cellwin_" + clean_name(gobj_name(gobj)),
2808
2808
  logical_class: "TREEDB_CELL_JSON",
2809
2809
  title_prefix: String(row_id),
2810
- title: /* i18n: data */ col_id,
2810
+ title: col_id,
2811
2811
  icon: "yi-js-square",
2812
2812
  forget_window: function() {
2813
2813
  priv.cell_json_win = null;
@@ -3019,7 +3019,7 @@ function open_cell_file_dialog(gobj, row_id, col_id)
3019
3019
  name: "filewin_" + clean_name(gobj_name(gobj)),
3020
3020
  logical_class: "TREEDB_CELL_FILE",
3021
3021
  title_prefix: String(row_id),
3022
- title: /* i18n: data */ col_id,
3022
+ title: col_id,
3023
3023
  icon: "yi-eye",
3024
3024
  forget_window: function() {
3025
3025
  priv.cell_file_win = null;
@@ -3167,7 +3167,7 @@ function open_hook_choice_dialog(gobj, targets, parent_id, hook)
3167
3167
  }];
3168
3168
  })]
3169
3169
  );
3170
- priv.hook_choice_modal = yui_shell_show_modal(shell, /* i18n: data */ $body, {
3170
+ priv.hook_choice_modal = yui_shell_show_modal(shell, $body, {
3171
3171
  dialog: true,
3172
3172
  logical_class: "TREEDB_HOOK_CHOICE_SHEET",
3173
3173
  title: "choose a topic",
@@ -4132,7 +4132,7 @@ function ac_delete_rows(gobj, event, kw, src)
4132
4132
  return 0;
4133
4133
  }
4134
4134
 
4135
- confirm_then(gobj, /* i18n: data */ build_delete_question(gobj, rows),
4135
+ confirm_then(gobj, build_delete_question(gobj, rows),
4136
4136
  {what: "delete_selection", ids: yui_row_ids(tabulator, rows)});
4137
4137
 
4138
4138
  } else {
@@ -4140,7 +4140,7 @@ function ac_delete_rows(gobj, event, kw, src)
4140
4140
  * Delete one row
4141
4141
  * {row: }
4142
4142
  *----------------------------*/
4143
- confirm_then(gobj, /* i18n: data */ build_delete_question(gobj, kw.row),
4143
+ confirm_then(gobj, build_delete_question(gobj, kw.row),
4144
4144
  {what: "delete_row", ids: yui_row_ids(tabulator, [kw.row])});
4145
4145
  }
4146
4146
 
@@ -4599,7 +4599,7 @@ function ac_show_hook_data(gobj, event, kw, src)
4599
4599
  String(kw.row_id), kw.col_id);
4600
4600
  return 0;
4601
4601
  }
4602
- open_hook_choice_dialog(gobj, targets, String(kw.row_id), kw.col_id);
4602
+ open_hook_choice_dialog(gobj, /* i18n: data */ targets, String(kw.row_id), kw.col_id); // the schema's topics
4603
4603
  return 0;
4604
4604
  }
4605
4605
 
@@ -4681,7 +4681,7 @@ function ac_refresh(gobj, event, kw, src)
4681
4681
  ************************************************************/
4682
4682
  function ac_show_cell_json(gobj, event, kw, src)
4683
4683
  {
4684
- open_cell_json_dialog(gobj, kw.row_id, kw.col_id);
4684
+ open_cell_json_dialog(gobj, kw.row_id, /* i18n: data */ kw.col_id);
4685
4685
 
4686
4686
  return 0;
4687
4687
  }
@@ -4695,7 +4695,7 @@ function ac_show_cell_json(gobj, event, kw, src)
4695
4695
  ************************************************************/
4696
4696
  function ac_show_cell_file(gobj, event, kw, src)
4697
4697
  {
4698
- open_cell_file_dialog(gobj, kw.row_id, kw.col_id);
4698
+ open_cell_file_dialog(gobj, kw.row_id, /* i18n: data */ kw.col_id);
4699
4699
 
4700
4700
  return 0;
4701
4701
  }
@@ -5078,7 +5078,7 @@ function ac_open_columns(gobj, event, kw, src)
5078
5078
  ));
5079
5079
  }
5080
5080
 
5081
- yui_shell_show_modal(shell, /* i18n: data */ $list, {
5081
+ yui_shell_show_modal(shell, $list, {
5082
5082
  dialog: true,
5083
5083
  logical_class: "TREEDB_COLUMNS_DIALOG",
5084
5084
  title_prefix: gobj_read_str_attr(gobj, "topic_name"),
@@ -5291,7 +5291,7 @@ function open_table_json_dialog(gobj, records)
5291
5291
  }
5292
5292
 
5293
5293
  /* The topic is DATA (the prefix), the title carries its key. */
5294
- let presented = present_json_popup(gobj, $box, {
5294
+ let presented = present_json_popup(gobj, /* i18n: data */ $box, { // the viewer's own DOM
5295
5295
  name: "tablejsonwin_" + clean_name(gobj_name(gobj)),
5296
5296
  logical_class: "TREEDB_TABLE_JSON",
5297
5297
  title_prefix: gobj_read_str_attr(gobj, "topic_name"),
@@ -1368,8 +1368,9 @@ function process_treedb_descs(gobj)
1368
1368
  );
1369
1369
 
1370
1370
  // TODO get icon from remote config
1371
- add_tab(gobj, gobj_topic_form, id, key, "yi-table");
1372
- add_topic_card(gobj, id, key, "yi-table", desc);
1371
+ /* `key` is a topic of the backend's schema: data */
1372
+ add_tab(gobj, gobj_topic_form, id, /* i18n: data */ key, "yi-table");
1373
+ add_topic_card(gobj, id, /* i18n: data */ key, "yi-table", desc);
1373
1374
 
1374
1375
  gobj_start(gobj_topic_form);
1375
1376
  }
@@ -1390,7 +1391,7 @@ function process_treedb_descs(gobj)
1390
1391
  if(gobj.priv._pending_info) {
1391
1392
  let ti = gobj.priv._pending_info;
1392
1393
  gobj.priv._pending_info = null;
1393
- show_topic_info(gobj, ti);
1394
+ show_topic_info(gobj, /* i18n: data */ ti); // a topic name
1394
1395
  } else if(href && href.indexOf("?") >= 0) {
1395
1396
  gobj_send_event(gobj, "EV_SHOW", {href: href}, gobj);
1396
1397
  } else {
@@ -2223,8 +2224,7 @@ function answer_print_tranger_refused(gobj, path, answer)
2223
2224
  gobj_send_event(jv, "EV_SUBTREE_ERROR",
2224
2225
  Object.assign({path: path}, answer), gobj);
2225
2226
  } else {
2226
- yui_shell_show_error(yui_shell_of(gobj),
2227
- /* i18n: data */ answer.i18n || answer.error, {t: t});
2227
+ yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, {t: t});
2228
2228
  }
2229
2229
  }
2230
2230
 
@@ -2274,7 +2274,7 @@ function request_print_tranger(gobj, path)
2274
2274
  }, gobj);
2275
2275
  if(ret) {
2276
2276
  log_error(ret);
2277
- answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
2277
+ answer_print_tranger_refused(gobj, path, {error: /* i18n: data */ refusal_text(ret)});
2278
2278
  return;
2279
2279
  }
2280
2280
  if(path) {
@@ -2459,7 +2459,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
2459
2459
  /* The schema couldn't load (not a treedb, no authz for it, backend
2460
2460
  * down…). Show an inline banner in the view rather than a blocking
2461
2461
  * app-modal that wedges the whole SPA behind an empty tab. */
2462
- show_load_error(gobj, t(comment));
2462
+ show_load_error(gobj, t(/* i18n: data */ comment));
2463
2463
  } else {
2464
2464
  yui_shell_show_error(yui_shell_of(gobj), /* i18n: data */ comment, {t: t});
2465
2465
  /* A refused write leaves the TABLE showing what the operator
@@ -2737,7 +2737,7 @@ function ac_show(gobj, event, kw, src)
2737
2737
  ************************************************************/
2738
2738
  function ac_show_topic_info(gobj, event, kw, src)
2739
2739
  {
2740
- show_topic_info(gobj, kw && kw.topic);
2740
+ show_topic_info(gobj, /* i18n: data */ kw && kw.topic); // a topic name
2741
2741
  return 0;
2742
2742
  }
2743
2743
 
@@ -510,9 +510,12 @@ function ac_register_window(gobj, event, kw, src)
510
510
  label_items.push(['span', {class: 'yui-dock-label-prefix'},
511
511
  text_node(prefix)]); // data, not markup
512
512
  }
513
+ /* An FSM action: what it is given is the kw of EV_REGISTER_WINDOW,
514
+ * which no reader can follow. `title_key` is the window's `title`,
515
+ * a key its HOST chose when it created the window. */
513
516
  if(title_key) {
514
517
  label_items.push(
515
- ['span', {class: 'yui-dock-label-kind', i18n: title_key},
518
+ ['span', {class: 'yui-dock-label-kind', i18n: /* i18n: data */ title_key},
516
519
  kind_text || title_key]
517
520
  );
518
521
  }
@@ -532,7 +535,7 @@ function ac_register_window(gobj, event, kw, src)
532
535
  let $chip = createElement2(
533
536
  ['div', Object.assign(
534
537
  {class: 'yui-dock-chip', role: 'button', tabindex: '0', title: title},
535
- (!prefix && title_key) ? {'data-i18n-title': title_key} : {}
538
+ (!prefix && title_key) ? {'data-i18n-title': /* i18n: data */ title_key} : {}
536
539
  ), [
537
540
  chip_lead(kw.icon),
538
541
  ['span', {class: 'yui-dock-label'}, label_items],