@yuneta/gobj-js 7.26.2 → 7.26.4

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.
@@ -4007,6 +4007,12 @@
4007
4007
  * called gobj_short_name() on a null gobj ("gobj bad type"), and a
4008
4008
  * null middle segment threw a TypeError.
4009
4009
  ************************************************************/
4010
+ function list_segment_index(list, seg) {
4011
+ if (!/^[0-9]+$/.test(seg)) return -1;
4012
+ const idx = Number(seg);
4013
+ if (idx >= list.length) return -1;
4014
+ return idx;
4015
+ }
4010
4016
  function kw_find_path(gobj, kw, path, verbose) {
4011
4017
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
4012
4018
  const prefix = gobj ? `${gobj_short_name(gobj)}: ` : "";
@@ -4022,11 +4028,12 @@
4022
4028
  let len = ss.length;
4023
4029
  for (let i = 0; i < len; i++) {
4024
4030
  if (is_array(kw)) {
4025
- if (!/^[0-9]+$/.test(ss[i])) {
4031
+ const idx = list_segment_index(kw, ss[i]);
4032
+ if (idx < 0) {
4026
4033
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
4027
4034
  return;
4028
4035
  }
4029
- ss[i] = String(Number(ss[i]));
4036
+ ss[i] = String(idx);
4030
4037
  }
4031
4038
  if (is_prototype_segment(kw, ss[i])) {
4032
4039
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
@@ -4061,8 +4068,14 @@
4061
4068
  function kw_delete(gobj, kw, path) {
4062
4069
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
4063
4070
  let [path_, k] = strrchr_split(path, "`");
4064
- if (!empty_string(k)) json_object_del(kw_find_path(gobj, kw, path_, true), k);
4065
- else json_object_del(kw, path);
4071
+ if (!empty_string(k)) {
4072
+ let v = kw_find_path(gobj, kw, path_, true);
4073
+ if (is_array(v)) {
4074
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_delete(): cannot delete an item of a list: '${path}'`);
4075
+ return -1;
4076
+ }
4077
+ json_object_del(v, k);
4078
+ } else json_object_del(kw, path);
4066
4079
  return 0;
4067
4080
  }
4068
4081
  /************************************************************
@@ -4418,6 +4431,14 @@
4418
4431
  let len = ss.length;
4419
4432
  for (let i = 0; i < len; i++) {
4420
4433
  let key = ss[i];
4434
+ if (is_array(kw)) {
4435
+ const idx = list_segment_index(kw, key);
4436
+ if (idx < 0) {
4437
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_set_dict_value(): segment '${key}' is not an index of its list: '${path}'`);
4438
+ return -1;
4439
+ }
4440
+ key = String(idx);
4441
+ }
4421
4442
  if (i === len - 1) set_own_key(kw, key, value);
4422
4443
  else {
4423
4444
  let seg = is_prototype_segment(kw, key) ? void 0 : kw[key];
@@ -5404,6 +5425,68 @@
5404
5425
  /***************************************************************************
5405
5426
  *
5406
5427
  ***************************************************************************/
5428
+ /***************************************************************************
5429
+ * Markup that createElement2() inserts AS HTML, said out loud.
5430
+ *
5431
+ * createElement2() reads a string that starts with '<' as markup. That
5432
+ * decision depends on the CONTENT of the string, not on who wrote it, so
5433
+ * a value from a backend or typed by a user that starts with '<' runs as
5434
+ * html. html_markup() is the explicit way to ask for markup: the string
5435
+ * is the caller's own (an icon, an svg), never data.
5436
+ *
5437
+ * ['span', {class: 'icon'}, html_markup('<i class="yi-copy"></i>')]
5438
+ *
5439
+ * With set_strings_are_text(true) a plain string is ALWAYS text and
5440
+ * html_markup() is the only way to insert markup.
5441
+ ***************************************************************************/
5442
+ var HtmlMarkup = class {
5443
+ constructor(html) {
5444
+ this.html = html;
5445
+ Object.freeze(this);
5446
+ }
5447
+ toString() {
5448
+ return this.html;
5449
+ }
5450
+ };
5451
+ function html_markup(html) {
5452
+ if (typeof html !== "string") {
5453
+ log_error(`html_markup: not a string, type ${typeof html}`);
5454
+ return new HtmlMarkup("");
5455
+ }
5456
+ return new HtmlMarkup(html);
5457
+ }
5458
+ function is_html_markup(v) {
5459
+ return v instanceof HtmlMarkup;
5460
+ }
5461
+ function markup_fragment(markup) {
5462
+ const template = document.createElement("template");
5463
+ template.innerHTML = markup.html.trim();
5464
+ return template.content;
5465
+ }
5466
+ var __strings_are_text__ = false;
5467
+ var __markup_strings_warned__ = /* @__PURE__ */ new Set();
5468
+ /***************************************************************************
5469
+ * The mode of createElement2() for a plain string that starts with '<':
5470
+ * false (default): it is markup, as it always was.
5471
+ * true: it is TEXT; markup is asked for with html_markup().
5472
+ * An app turns it on once its own code and the libraries it mounts pass
5473
+ * every deliberate markup through html_markup(). A string that starts
5474
+ * with '<' is then logged once (a deliberate one not migrated yet, or a
5475
+ * datum that used to run as html).
5476
+ ***************************************************************************/
5477
+ function set_strings_are_text(on) {
5478
+ __strings_are_text__ = !!on;
5479
+ }
5480
+ function strings_are_text() {
5481
+ return __strings_are_text__;
5482
+ }
5483
+ function warn_markup_string(content) {
5484
+ if (__markup_strings_warned__.size >= 100) return;
5485
+ const key = content.slice(0, 80);
5486
+ if (__markup_strings_warned__.has(key)) return;
5487
+ __markup_strings_warned__.add(key);
5488
+ log_warning(`createElement2: a string that starts with '<' is TEXT (set_strings_are_text): use html_markup() if it is markup: ${JSON.stringify(key)}`);
5489
+ }
5407
5490
  function createOneHtml(htmlString) {
5408
5491
  const template = document.createElement("template");
5409
5492
  template.innerHTML = htmlString.trim();
@@ -5565,6 +5648,7 @@
5565
5648
  * if string begins with '<' then
5566
5649
  * it will be converted to HTMLElement
5567
5650
  * content = createOneHtml(string)
5651
+ * | html_markup(string): markup, said out loud (any mode)
5568
5652
  * ⚠️ So a string is MARKUP when it starts with '<': never
5569
5653
  * pass DATA (a value from a backend, a name a user typed)
5570
5654
  * as a bare string, or "<img src=x onerror=...>" runs.
@@ -5574,6 +5658,8 @@
5574
5658
  * appended as it is and a string is always text -- but a
5575
5659
  * list whose first item is a string is read as ONE
5576
5660
  * description (the string is its tag).
5661
+ * With set_strings_are_text(true) a plain string is
5662
+ * always text, and markup only comes in html_markup().
5577
5663
  * if attrs['i18n'] is not empty then (or 'data-i18n' or 'data_i18next')
5578
5664
  * it will be overridden with the translation
5579
5665
  * content = translate(attrs['i18n']));
@@ -5616,19 +5702,24 @@
5616
5702
  el.setAttribute("data-i18n", data_i18next);
5617
5703
  }
5618
5704
  } else el.setAttribute(attr, attrs[attr]);
5619
- if (is_string(content)) {
5705
+ if (is_html_markup(content)) el.appendChild(markup_fragment(content));
5706
+ else if (is_string(content)) {
5620
5707
  content = content.trim();
5621
- if (content.length > 0 && content[0] === "<") el.appendChild(createOneHtml(content));
5622
- else if (translate_fn && data_i18next) {
5623
- let translated = translate_fn(data_i18next);
5624
- el.textContent = translated ? translated : content;
5625
- } else el.textContent = content;
5708
+ if (content.length > 0 && content[0] === "<" && !__strings_are_text__) el.appendChild(createOneHtml(content));
5709
+ else {
5710
+ if (content.length > 0 && content[0] === "<") warn_markup_string(content);
5711
+ if (translate_fn && data_i18next) {
5712
+ let translated = translate_fn(data_i18next);
5713
+ el.textContent = translated ? translated : content;
5714
+ } else el.textContent = content;
5715
+ }
5626
5716
  } else if (is_array(content)) {
5627
5717
  if (content.length > 0) {
5628
5718
  if (is_string(content[0])) el.appendChild(createElement2(content, translate_fn));
5629
5719
  else for (let child of content) if (Array.isArray(child)) {
5630
5720
  if (child.length > 0) el.appendChild(createElement2(child, translate_fn));
5631
5721
  } else if (child instanceof Node) el.appendChild(child);
5722
+ else if (is_html_markup(child)) el.appendChild(markup_fragment(child));
5632
5723
  else if (is_string(child)) el.appendChild(document.createTextNode(child));
5633
5724
  else if (child !== null && child !== void 0 && child !== false) log_error(`createElement2: child ignored, type ${typeof child}`);
5634
5725
  }
@@ -7234,13 +7325,13 @@
7234
7325
  websocket = new WebSocket(url);
7235
7326
  } catch (e) {
7236
7327
  log_error(`${gobj_short_name(gobj)}: Cannot open WebSocket to '${url}', Error: ${e.message}`);
7237
- gobj_post_event(gobj, "EV_ON_CLOSE", {
7328
+ if (gobj_post_event(gobj, "EV_ON_CLOSE", {
7238
7329
  url,
7239
7330
  ws_seq,
7240
7331
  code: 1006,
7241
7332
  reason: e.message,
7242
7333
  was_clean: false
7243
- }, gobj);
7334
+ }, gobj) < 0) set_timeout(gobj.priv.gobj_timer, next_retry(gobj));
7244
7335
  return null;
7245
7336
  }
7246
7337
  if (!websocket) {
@@ -7979,6 +8070,7 @@
7979
8070
  exports.gobj_yuno_id = gobj_yuno_id;
7980
8071
  exports.gobj_yuno_name = gobj_yuno_name;
7981
8072
  exports.gobj_yuno_role = gobj_yuno_role;
8073
+ exports.html_markup = html_markup;
7982
8074
  exports.id_index_in_obj_list = id_index_in_obj_list;
7983
8075
  exports.index_in_list = index_in_list;
7984
8076
  exports.is_array = is_array;
@@ -7986,6 +8078,7 @@
7986
8078
  exports.is_date = is_date;
7987
8079
  exports.is_function = is_function;
7988
8080
  exports.is_gobj = is_gobj;
8081
+ exports.is_html_markup = is_html_markup;
7989
8082
  exports.is_metadata_key = is_metadata_key;
7990
8083
  exports.is_null = is_null;
7991
8084
  exports.is_number = is_number;
@@ -8077,6 +8170,7 @@
8077
8170
  exports.set_console_log_filter = set_console_log_filter;
8078
8171
  exports.set_log_callback = set_log_callback;
8079
8172
  exports.set_remote_log_functions = set_remote_log_functions;
8173
+ exports.set_strings_are_text = set_strings_are_text;
8080
8174
  exports.set_timeout = set_timeout;
8081
8175
  exports.set_timeout_periodic = set_timeout_periodic;
8082
8176
  exports.sprintf = sprintf;
@@ -8084,6 +8178,7 @@
8084
8178
  exports.str_in_list = str_in_list;
8085
8179
  exports.strcasecmp = strcasecmp;
8086
8180
  exports.strcmp = strcmp;
8181
+ exports.strings_are_text = strings_are_text;
8087
8182
  exports.strncmp = strncmp;
8088
8183
  exports.strs_in_list = strs_in_list;
8089
8184
  exports.strstr = strstr;