@xuda.io/xuda-worker-bundle-min 1.3.2438 → 1.3.2440

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.
Files changed (2) hide show
  1. package/index.js +746 -158
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -5,6 +5,49 @@ if (typeof IS_DOCKER === 'undefined' || typeof IS_PROCESS_SERVER === 'undefined'
5
5
  var DOCS_OBJ = {};
6
6
  }
7
7
 
8
+ // Minimal jQuery shim for plugins that still reference $
9
+ if (typeof $ === 'undefined' && typeof document !== 'undefined') {
10
+ var $ = function (selector) {
11
+ var nodes = typeof selector === 'string'
12
+ ? Array.from(document.querySelectorAll(selector))
13
+ : selector?.nodeType ? [selector] : (selector?.length ? Array.from(selector) : []);
14
+ var obj = {
15
+ 0: nodes[0], length: nodes.length,
16
+ toArray: function () { return nodes.slice(); },
17
+ find: function (s) { var r = []; for (var i = 0; i < nodes.length; i++) { r.push.apply(r, Array.from(nodes[i].querySelectorAll(s))); } return $(r); },
18
+ each: function (fn) { for (var i = 0; i < nodes.length; i++) { fn.call(nodes[i], i, nodes[i]); } return obj; },
19
+ on: function (ev, fn) { for (var i = 0; i < nodes.length; i++) nodes[i].addEventListener(ev, fn); return obj; },
20
+ off: function (ev, fn) { for (var i = 0; i < nodes.length; i++) nodes[i].removeEventListener(ev, fn); return obj; },
21
+ addClass: function (c) { for (var i = 0; i < nodes.length; i++) nodes[i].classList?.add(c); return obj; },
22
+ removeClass: function (c) { for (var i = 0; i < nodes.length; i++) nodes[i].classList?.remove(c); return obj; },
23
+ hasClass: function (c) { return nodes[0]?.classList?.contains(c) || false; },
24
+ attr: function (k, v) { if (typeof v === 'undefined') return nodes[0]?.getAttribute(k); for (var i = 0; i < nodes.length; i++) nodes[i].setAttribute(k, v); return obj; },
25
+ css: function (k, v) { for (var i = 0; i < nodes.length; i++) nodes[i].style[k] = v; return obj; },
26
+ data: function () { return nodes[0]?.__xuData || (nodes[0] ? (nodes[0].__xuData = {}) : {}); },
27
+ val: function (v) { if (typeof v === 'undefined') return nodes[0]?.value; for (var i = 0; i < nodes.length; i++) nodes[i].value = v; return obj; },
28
+ html: function (v) { if (typeof v === 'undefined') return nodes[0]?.innerHTML; for (var i = 0; i < nodes.length; i++) nodes[i].innerHTML = v; return obj; },
29
+ text: function (v) { if (typeof v === 'undefined') return nodes[0]?.textContent; for (var i = 0; i < nodes.length; i++) nodes[i].textContent = v; return obj; },
30
+ show: function () { for (var i = 0; i < nodes.length; i++) nodes[i].style.display = ''; return obj; },
31
+ hide: function () { for (var i = 0; i < nodes.length; i++) nodes[i].style.display = 'none'; return obj; },
32
+ remove: function () { for (var i = 0; i < nodes.length; i++) nodes[i].remove?.(); return obj; },
33
+ empty: function () { for (var i = 0; i < nodes.length; i++) nodes[i].innerHTML = ''; return obj; },
34
+ append: function (c) { var n = c?.nodeType ? c : c?.[0]; if (n && nodes[0]) nodes[0].appendChild(n); return obj; },
35
+ parent: function () { return $(nodes[0]?.parentElement ? [nodes[0].parentElement] : []); },
36
+ children: function () { return $(nodes[0] ? Array.from(nodes[0].children) : []); },
37
+ trigger: function (ev, d) { for (var i = 0; i < nodes.length; i++) nodes[i].dispatchEvent(new CustomEvent(ev, { detail: d })); return obj; },
38
+ is: function (s) { return nodes[0]?.matches?.(s) || false; },
39
+ prop: function (k, v) { if (typeof v === 'undefined') return nodes[0]?.[k]; for (var i = 0; i < nodes.length; i++) nodes[i][k] = v; return obj; },
40
+ unbind: function () { return obj; },
41
+ clone: function () { return $(nodes[0]?.cloneNode(true) ? [nodes[0].cloneNode(true)] : []); },
42
+ };
43
+ obj[Symbol.iterator] = function () { var i = 0; return { next: function () { return i < nodes.length ? { value: nodes[i++], done: false } : { done: true }; } }; };
44
+ return obj;
45
+ };
46
+ $.each = function (o, fn) { if (Array.isArray(o)) { for (var i = 0; i < o.length; i++) fn(i, o[i]); } else { Object.keys(o || {}).forEach(function (k) { fn(k, o[k]); }); } };
47
+ $.cookie = function () { return null; };
48
+ var jQuery = $;
49
+ }
50
+
8
51
  var glb = {};
9
52
  var func = {};
10
53
  func.UI = {};
@@ -21,6 +64,51 @@ func.runtime.ui = {};
21
64
  func.runtime.widgets = {};
22
65
  glb.IS_STUDIO = null;
23
66
 
67
+ // Lodash replacement utilities
68
+ var xu_isEmpty = function (val) {
69
+ if (val == null) return true;
70
+ if (typeof val === 'boolean' || typeof val === 'number') return !val;
71
+ if (typeof val === 'string' || Array.isArray(val)) return val.length === 0;
72
+ if (val instanceof Map || val instanceof Set) return val.size === 0;
73
+ return Object.keys(val).length === 0;
74
+ };
75
+
76
+ var xu_isEqual = function (a, b) {
77
+ if (a === b) return true;
78
+ if (a == null || b == null) return a === b;
79
+ if (typeof a !== typeof b) return false;
80
+ if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
81
+ if (typeof a !== 'object') return false;
82
+ var keysA = Object.keys(a);
83
+ var keysB = Object.keys(b);
84
+ if (keysA.length !== keysB.length) return false;
85
+ for (var i = 0; i < keysA.length; i++) {
86
+ if (!Object.prototype.hasOwnProperty.call(b, keysA[i]) || !xu_isEqual(a[keysA[i]], b[keysA[i]])) return false;
87
+ }
88
+ return true;
89
+ };
90
+
91
+ var xu_get = function (obj, path, defaultVal) {
92
+ var keys = typeof path === 'string' ? path.split('.') : path;
93
+ var result = obj;
94
+ for (var i = 0; i < keys.length; i++) {
95
+ if (result == null) return defaultVal;
96
+ result = result[keys[i]];
97
+ }
98
+ return result === undefined ? defaultVal : result;
99
+ };
100
+
101
+ var xu_set = function (obj, path, value) {
102
+ var keys = typeof path === 'string' ? path.split('.') : path;
103
+ var current = obj;
104
+ for (var i = 0; i < keys.length - 1; i++) {
105
+ if (current[keys[i]] == null) current[keys[i]] = {};
106
+ current = current[keys[i]];
107
+ }
108
+ current[keys[keys.length - 1]] = value;
109
+ return obj;
110
+ };
111
+
24
112
  var PROJECT_OBJ = {};
25
113
 
26
114
  var APP_OBJ = {};
@@ -185,6 +273,105 @@ func.runtime.platform = {
185
273
  return true;
186
274
  },
187
275
  };
276
+
277
+ // ── Platform-agnostic event bus ──
278
+ // Works in browser, worker, and Node environments.
279
+ // In browser, bridge DOM events into this bus so core code never touches $(document) directly.
280
+ func.runtime.platform._event_bus = {};
281
+ func.runtime.platform.on = function (name, handler) {
282
+ if (!func.runtime.platform._event_bus[name]) {
283
+ func.runtime.platform._event_bus[name] = [];
284
+ }
285
+ func.runtime.platform._event_bus[name].push(handler);
286
+ };
287
+ func.runtime.platform.off = function (name, handler) {
288
+ const handlers = func.runtime.platform._event_bus[name];
289
+ if (!handlers) return;
290
+ if (!handler) {
291
+ delete func.runtime.platform._event_bus[name];
292
+ return;
293
+ }
294
+ const index = handlers.indexOf(handler);
295
+ if (index !== -1) {
296
+ handlers.splice(index, 1);
297
+ }
298
+ };
299
+ func.runtime.platform._emitting = {};
300
+ func.runtime.platform.emit = function (name, data) {
301
+ // re-entrancy guard: prevent infinite loops when DOM bridge triggers the same event
302
+ if (func.runtime.platform._emitting[name]) return;
303
+ func.runtime.platform._emitting[name] = true;
304
+ try {
305
+ const handlers = func.runtime.platform._event_bus[name];
306
+ if (handlers) {
307
+ for (let i = 0; i < handlers.length; i++) {
308
+ handlers[i](data);
309
+ }
310
+ }
311
+ // also fire on DOM if in browser (for backward compatibility with custom event listeners)
312
+ if (func.runtime.platform.has_document()) {
313
+ document.dispatchEvent(new CustomEvent(name, { detail: Array.isArray(data) ? data : [data] }));
314
+ }
315
+ } finally {
316
+ func.runtime.platform._emitting[name] = false;
317
+ }
318
+ };
319
+
320
+ // ── Platform helpers for DOM-independent resource loading ──
321
+ func.runtime.platform.load_script = function (url, type, callback) {
322
+ if (typeof document !== 'undefined') {
323
+ const script = document.createElement('script');
324
+ script.src = url;
325
+ if (type) script.type = type;
326
+ script.onload = callback;
327
+ document.head.appendChild(script);
328
+ } else if (callback) {
329
+ callback();
330
+ }
331
+ };
332
+ func.runtime.platform.load_css = function (href) {
333
+ if (typeof document === 'undefined') return;
334
+ try {
335
+ if (document.querySelector('link[href="' + href + '"]')) return;
336
+ } catch (err) {
337
+ return;
338
+ }
339
+ const link = document.createElement('link');
340
+ link.rel = 'stylesheet';
341
+ link.type = 'text/css';
342
+ link.href = href;
343
+ document.head.insertBefore(link, document.head.firstChild);
344
+ };
345
+ func.runtime.platform.remove_js_css = function (filename, filetype) {
346
+ if (typeof document === 'undefined') return;
347
+ const tagName = filetype === 'js' ? 'script' : filetype === 'css' ? 'link' : 'none';
348
+ const attr = filetype === 'js' ? 'src' : filetype === 'css' ? 'href' : 'none';
349
+ const elements = document.getElementsByTagName(tagName);
350
+ for (let i = elements.length - 1; i >= 0; i--) {
351
+ if (elements[i] && elements[i].getAttribute(attr) != null && elements[i].getAttribute(attr).indexOf(filename) !== -1) {
352
+ elements[i].parentNode.removeChild(elements[i]);
353
+ }
354
+ }
355
+ };
356
+ func.runtime.platform.inject_css = function (cssText) {
357
+ if (typeof document === 'undefined' || !cssText) return;
358
+ const style = document.createElement('style');
359
+ style.type = 'text/css';
360
+ style.textContent = cssText;
361
+ document.head.appendChild(style);
362
+ };
363
+ func.runtime.platform.set_title = function (title) {
364
+ if (typeof document !== 'undefined') {
365
+ document.title = title;
366
+ }
367
+ };
368
+ func.runtime.platform.set_cursor = function (element, cursor) {
369
+ const node = func.runtime.ui?.get_first_node ? func.runtime.ui.get_first_node(element) : element;
370
+ if (node?.style) {
371
+ node.style.cursor = cursor;
372
+ }
373
+ };
374
+
188
375
  func.runtime.env = {
189
376
  get_url_params: function () {
190
377
  const search = func.runtime.platform.get_url_search();
@@ -563,7 +750,7 @@ func.runtime.bind.build_datasource_changes = function (dsSessionP, currentRecord
563
750
  },
564
751
  };
565
752
  };
566
- func.runtime.bind.resolve_field = async function (SESSION_ID, prog_id, dsSessionP, field_id) {
753
+ func.runtime.bind.resolve_field = async function (SESSION_ID, prog_id, dsSessionP, field_id, iterate_info) {
567
754
  let _prog_id = prog_id;
568
755
  let _dsP = dsSessionP;
569
756
  let is_dynamic_field = false;
@@ -579,7 +766,20 @@ func.runtime.bind.resolve_field = async function (SESSION_ID, prog_id, dsSession
579
766
 
580
767
  if (['_FOR_VAL', '_FOR_KEY'].includes(field_id)) {
581
768
  is_dynamic_field = true;
582
- field_prop = SESSION_OBJ[SESSION_ID]?.DS_GLB?.[_dsP]?.dynamic_fields?.[field_id];
769
+ if (iterate_info && (iterate_info.iterator_val === field_id || iterate_info.iterator_key === field_id)) {
770
+ const iter_value = iterate_info.iterator_val === field_id ? iterate_info._val : iterate_info._key;
771
+ const toType = function (obj) {
772
+ return {}.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
773
+ };
774
+ field_prop = {
775
+ id: field_id,
776
+ data: { type: 'virtual', field_id },
777
+ props: { fieldType: typeof iter_value !== 'undefined' ? toType(iter_value) : 'string' },
778
+ value: iter_value,
779
+ };
780
+ } else {
781
+ field_prop = SESSION_OBJ[SESSION_ID]?.DS_GLB?.[_dsP]?.dynamic_fields?.[field_id];
782
+ }
583
783
  } else {
584
784
  field_prop = await find_in_view(field_id, _prog_id);
585
785
  if (!field_prop) {
@@ -610,7 +810,7 @@ func.runtime.bind.get_field_type = function (field_prop) {
610
810
  };
611
811
  func.runtime.bind.toggle_array_value = function (arr_value_before_cast, value_from_getter) {
612
812
  if (arr_value_before_cast.includes(value_from_getter)) {
613
- return arr_value_before_cast.filter((item) => !_.isEqual(item, value_from_getter));
813
+ return arr_value_before_cast.filter((item) => !xu_isEqual(item, value_from_getter));
614
814
  }
615
815
  arr_value_before_cast.push(value_from_getter);
616
816
  return arr_value_before_cast;
@@ -632,7 +832,7 @@ func.runtime.bind.get_source_value = function (_ds, bind_field_id, is_dynamic_fi
632
832
  };
633
833
  func.runtime.bind.format_display_value = function ($elm, field_prop, bind_field_id, expression_value, value, input_field_type) {
634
834
  const field_type = func.runtime.bind.get_field_type(field_prop);
635
- const elm_value = $elm.attr('value');
835
+ const elm_value = func.runtime.ui.get_attr($elm, 'value');
636
836
 
637
837
  if (field_type === 'array' && input_field_type === 'checkbox' && elm_value) {
638
838
  return value.includes(elm_value);
@@ -658,27 +858,27 @@ func.runtime.bind.update_reference_source_array = async function (options) {
658
858
 
659
859
  const arr_idx = Number(options.iterate_info._key);
660
860
  const dataset_arr = await func.datasource.get_value(options.SESSION_ID, reference_source_obj.fieldIdP, options.dsSessionP, reference_source_obj.currentRecordId);
661
- let new_arr = _.cloneDeep(dataset_arr.ret.value);
861
+ let new_arr = klona.klona(dataset_arr.ret.value);
662
862
 
663
863
  if (field_type === 'object' && options.val_is_reference_field) {
664
864
  let obj_item = new_arr[arr_idx];
665
865
  let e_exp = options.expression_value.replace(options.bind_field_id, 'obj_item');
666
- eval(e_exp + (options.input_field_type === 'string' ? `="${options.value}"` : `=${options.value}`));
866
+ eval(e_exp + `=${JSON.stringify(options.value)}`);
667
867
  new_arr[arr_idx] = obj_item;
668
868
  } else {
669
869
  new_arr[arr_idx] = options.value;
670
870
  }
671
871
 
672
872
  let datasource_changes = func.runtime.bind.build_datasource_changes(options.dsSessionP, options.currentRecordId, reference_source_obj.fieldIdP, new_arr);
673
- await func.datasource.update(options.SESSION_ID, datasource_changes, null, true);
873
+ await func.datasource.update(options.SESSION_ID, datasource_changes);
674
874
  return true;
675
875
  };
676
876
  func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
677
877
  let normalized_resource = resource;
678
- if (!_.isObject(normalized_resource) && _.isString(normalized_resource)) {
878
+ if (!(typeof normalized_resource === 'object' && normalized_resource !== null) && typeof normalized_resource === 'string') {
679
879
  normalized_resource = { src: normalized_resource, type: 'js' };
680
880
  }
681
- if (!_.isObject(normalized_resource)) {
881
+ if (!(typeof normalized_resource === 'object' && normalized_resource !== null)) {
682
882
  throw new Error('cdn resource in wrong format');
683
883
  }
684
884
 
@@ -724,7 +924,7 @@ func.runtime.resources.load_plugin_runtime_css = async function (SESSION_ID, plu
724
924
  return true;
725
925
  };
726
926
  func.runtime.resources.resolve_plugin_properties = async function (SESSION_ID, dsSessionP, attributes, properties) {
727
- let resolved_properties = _.cloneDeep(properties);
927
+ let resolved_properties = klona.klona(properties);
728
928
  for await (let [prop_name, prop_val] of Object.entries(resolved_properties || {})) {
729
929
  prop_val.value = attributes?.[prop_name];
730
930
  if (attributes?.[`xu-exp:${prop_name}`]) {
@@ -750,7 +950,7 @@ func.runtime.resources.run_ui_plugin = async function (SESSION_ID, paramsP, $elm
750
950
  const plugin_runtime_src = await func.runtime.resources.get_plugin_module_url(SESSION_ID, plugin_name, plugin, 'runtime.mjs');
751
951
  const plugin_runtime_resources = await import(plugin_runtime_src);
752
952
 
753
- if (plugin_runtime_resources.cdn && _.isArray(plugin_runtime_resources.cdn)) {
953
+ if (plugin_runtime_resources.cdn && Array.isArray(plugin_runtime_resources.cdn)) {
754
954
  for await (const resource of plugin_runtime_resources.cdn) {
755
955
  await func.runtime.resources.load_cdn(SESSION_ID, resource);
756
956
  }
@@ -882,12 +1082,12 @@ func.runtime.widgets.build_params = function (context, $containerP, plugin_setup
882
1082
  };
883
1083
  };
884
1084
  func.common.find_item_by_key = function (arr, key, val) {
885
- return _.find(arr, function (e) {
1085
+ return arr.find(function (e) {
886
1086
  return e.data[key] === val;
887
1087
  });
888
1088
  };
889
1089
  func.common.find_item_by_key_root = function (arr, key, val) {
890
- return _.find(arr, function (e) {
1090
+ return arr.find(function (e) {
891
1091
  return e[key] === val;
892
1092
  });
893
1093
  };
@@ -1147,7 +1347,7 @@ func.common.db = async function (SESSION_ID, serviceP, dataP, opt = {}, dsSessio
1147
1347
  }
1148
1348
 
1149
1349
  await db.get(row_id);
1150
- let _data = _.cloneDeep(dataP);
1350
+ let _data = klona.klona(dataP);
1151
1351
  _data.ids = [row_id];
1152
1352
  return await func.db.pouch['dbs_delete'](SESSION_ID, _data);
1153
1353
  } catch (err) {
@@ -1236,7 +1436,7 @@ func.common.db = async function (SESSION_ID, serviceP, dataP, opt = {}, dsSessio
1236
1436
 
1237
1437
  const get_white_spaced_data = function (data) {
1238
1438
  var e = {};
1239
- _.forEach(data, function (val, key) {
1439
+ for (const [key, val] of Object.entries(data)) {
1240
1440
  if (!val) {
1241
1441
  if (typeof val === 'boolean') {
1242
1442
  e[key] = 'false';
@@ -1250,7 +1450,7 @@ func.common.db = async function (SESSION_ID, serviceP, dataP, opt = {}, dsSessio
1250
1450
  e[key] = val;
1251
1451
  }
1252
1452
  }
1253
- });
1453
+ }
1254
1454
 
1255
1455
  if (data.fields && !data.fields.length) {
1256
1456
  e.fields = '';
@@ -1499,14 +1699,14 @@ var UI_FRAMEWORK_PLUGIN = {};
1499
1699
  func.common.get_cast_val = async function (SESSION_ID, source, attributeP, typeP, valP, errorP) {
1500
1700
  const report_conversion_error = function (res) {
1501
1701
  if (errorP) {
1502
- return func.utils.debug_report(SESSION_ID, _.capitalize(source), errorP, 'W');
1702
+ return func.utils.debug_report(SESSION_ID, source.charAt(0).toUpperCase() + source.slice(1).toLowerCase(), errorP, 'W');
1503
1703
  }
1504
1704
  var msg = `error converting ${attributeP} from ${valP} to ${typeP}`;
1505
- func.utils.debug_report(SESSION_ID, _.capitalize(source), msg, 'E');
1705
+ func.utils.debug_report(SESSION_ID, source.charAt(0).toUpperCase() + source.slice(1).toLowerCase(), msg, 'E');
1506
1706
  };
1507
1707
  const report_conversion_warn = function (msg) {
1508
1708
  var msg = `type mismatch auto conversion made to ${attributeP} from value ${valP} to ${typeP}`;
1509
- func.utils.debug_report(SESSION_ID, _.capitalize(source), msg, 'W');
1709
+ func.utils.debug_report(SESSION_ID, source.charAt(0).toUpperCase() + source.slice(1).toLowerCase(), msg, 'W');
1510
1710
  };
1511
1711
  const module = await func.common.get_module(SESSION_ID, `xuda-get-cast-util-module.mjs`);
1512
1712
  return module.cast(typeP, valP, report_conversion_error, report_conversion_warn);
@@ -1524,11 +1724,17 @@ var WEBSOCKET_PROCESS_PID = null;
1524
1724
  glb.worker_queue_num = 0;
1525
1725
  glb.websocket_queue_num = 0;
1526
1726
 
1727
+ func.common._import_cache = func.common._import_cache || {};
1728
+
1527
1729
  func.common.get_module = async function (SESSION_ID, module, paramsP = {}) {
1528
1730
  let ret;
1529
1731
 
1530
1732
  const get_ret = async function (src) {
1531
- const module_ret = await import(src);
1733
+ // Cache the import() result to avoid repeated dynamic imports
1734
+ if (!func.common._import_cache[src]) {
1735
+ func.common._import_cache[src] = await import(src);
1736
+ }
1737
+ const module_ret = func.common._import_cache[src];
1532
1738
  var params = get_params();
1533
1739
 
1534
1740
  const ret = module_ret.XudaModule ? new module_ret.XudaModule(params) : await invoke_init_module(module_ret, params);
@@ -1545,7 +1751,6 @@ func.common.get_module = async function (SESSION_ID, module, paramsP = {}) {
1545
1751
  PROJECT_OBJ,
1546
1752
  DOCS_OBJ,
1547
1753
  SESSION_OBJ,
1548
- _,
1549
1754
  ...paramsP,
1550
1755
  };
1551
1756
  if (typeof IS_PROCESS_SERVER !== 'undefined') params.IS_PROCESS_SERVER = IS_PROCESS_SERVER;
@@ -1579,7 +1784,7 @@ func.common.get_module = async function (SESSION_ID, module, paramsP = {}) {
1579
1784
  }
1580
1785
 
1581
1786
  const rep = function () {
1582
- return _.endsWith(module, '.js') ? module.replace('.js', '.min.js') : module.replace('.mjs', '.min.mjs');
1787
+ return module.endsWith('.js') ? module.replace('.js', '.min.js') : module.replace('.mjs', '.min.mjs');
1583
1788
  };
1584
1789
 
1585
1790
  if (typeof IS_DOCKER !== 'undefined' || typeof IS_PROCESS_SERVER !== 'undefined') {
@@ -1737,7 +1942,7 @@ func.api.watch = function (path, cb, opt = {}) {
1737
1942
  _session.watchers[path] = { ...opt, handler: cb };
1738
1943
 
1739
1944
  if (opt.immediate) {
1740
- const value = _.get(SESSION_OBJ[SESSION_ID].DS_GLB[0], path);
1945
+ const value = xu_get(SESSION_OBJ[SESSION_ID].DS_GLB[0], path);
1741
1946
  cb({ path, newValue: value, oldValue: value, timestamp: Date.now(), opt });
1742
1947
 
1743
1948
  if (opt.once) {
@@ -1889,10 +2094,12 @@ func.common.get_data_from_websocket = async function (SESSION_ID, serviceP, data
1889
2094
  } else {
1890
2095
  if (RUNTIME_SERVER_WEBSOCKET && RUNTIME_SERVER_WEBSOCKET_CONNECTED) {
1891
2096
  RUNTIME_SERVER_WEBSOCKET.emit('message', obj);
1892
- $('body').on('get_ws_data_response_' + glb.websocket_queue_num, (e, data) => {
2097
+ const _ws_event = 'get_ws_data_response_' + glb.websocket_queue_num;
2098
+ const _ws_handler = function (data) {
1893
2099
  resolve(data.data);
1894
- $('body').off('get_ws_data_response_' + data.e.websocket_queue_num);
1895
- });
2100
+ func.runtime.platform.off('get_ws_data_response_' + data.e.websocket_queue_num, _ws_handler);
2101
+ };
2102
+ func.runtime.platform.on(_ws_event, _ws_handler);
1896
2103
  } else {
1897
2104
  throw new Error('fail to fetch from ws websocket inactive');
1898
2105
  }
@@ -1906,10 +2113,11 @@ func.common.get_data_from_websocket = async function (SESSION_ID, serviceP, data
1906
2113
 
1907
2114
  if (RUNTIME_SERVER_WEBSOCKET && RUNTIME_SERVER_WEBSOCKET_CONNECTED) {
1908
2115
  RUNTIME_SERVER_WEBSOCKET.emit('message', obj);
1909
- $('body').on('heartbeat_response', (e, data) => {
2116
+ const _hb_handler = function (data) {
1910
2117
  resolve(data.data);
1911
- $('body').off('heartbeat_response');
1912
- });
2118
+ func.runtime.platform.off('heartbeat_response', _hb_handler);
2119
+ };
2120
+ func.runtime.platform.on('heartbeat_response', _hb_handler);
1913
2121
  } else {
1914
2122
  throw new Error('fail to fetch from ws websocket inactive');
1915
2123
  }
@@ -2144,6 +2352,395 @@ glb.run_xu_before = [
2144
2352
  glb.run_xu_after = ['xu-bind', 'xu-class', 'xu-script', 'xu-ui-plugin', 'xu-ref'];
2145
2353
  glb.attr_abbreviations_arr = ['xu-click', 'xu-dblclick', 'xu-contextmenu', 'xu-focus', 'xu-keyup', 'xu-change', 'xu-blur', 'xu-init'];
2146
2354
  glb.solid_attributes = ['disabled'];
2355
+ // ── Headless/SSR DOM adapter ──
2356
+ // Provides a no-op / in-memory implementation of func.runtime.ui
2357
+ // for environments without a DOM (Node.js, workers, SSR).
2358
+ // Elements are plain objects; metadata lives in _meta_store.
2359
+
2360
+ func.runtime = func.runtime || {};
2361
+ func.runtime.ui = func.runtime.ui || {};
2362
+ func.runtime.render = func.runtime.render || {};
2363
+ func.runtime.widgets = func.runtime.widgets || {};
2364
+ func.runtime.ui.ui_id_hash_cache = func.runtime.ui.ui_id_hash_cache || new Map();
2365
+ func.runtime.ui.node_snapshot_cache = func.runtime.ui.node_snapshot_cache || new WeakMap();
2366
+ func.runtime.ui.node_child_items_cache = func.runtime.ui.node_child_items_cache || new WeakMap();
2367
+ func.runtime.ui.node_children_by_id_cache = func.runtime.ui.node_children_by_id_cache || new WeakMap();
2368
+
2369
+ // ── Metadata store (shared with browser adapter) ──
2370
+ func.runtime.ui._meta_store = func.runtime.ui._meta_store || {};
2371
+ func.runtime.ui._element_id_to_xu_ui_id = func.runtime.ui._element_id_to_xu_ui_id || {};
2372
+
2373
+ func.runtime.ui.set_meta = function (xu_ui_id, key, value) {
2374
+ if (!xu_ui_id) return;
2375
+ if (!func.runtime.ui._meta_store[xu_ui_id]) {
2376
+ func.runtime.ui._meta_store[xu_ui_id] = {};
2377
+ }
2378
+ func.runtime.ui._meta_store[xu_ui_id][key] = value;
2379
+ };
2380
+ func.runtime.ui.get_meta = function (xu_ui_id, key) {
2381
+ const entry = func.runtime.ui._meta_store[xu_ui_id];
2382
+ if (!entry) return undefined;
2383
+ return key ? entry[key] : entry;
2384
+ };
2385
+ func.runtime.ui.delete_meta = function (xu_ui_id) {
2386
+ delete func.runtime.ui._meta_store[xu_ui_id];
2387
+ for (const id in func.runtime.ui._element_id_to_xu_ui_id) {
2388
+ if (func.runtime.ui._element_id_to_xu_ui_id[id] === xu_ui_id) {
2389
+ delete func.runtime.ui._element_id_to_xu_ui_id[id];
2390
+ }
2391
+ }
2392
+ };
2393
+ func.runtime.ui.register_element_id = function (element_id, xu_ui_id) {
2394
+ if (element_id && xu_ui_id) {
2395
+ func.runtime.ui._element_id_to_xu_ui_id[element_id] = xu_ui_id;
2396
+ }
2397
+ };
2398
+ func.runtime.ui.get_meta_by_element_id = function (element_id) {
2399
+ if (!element_id) return undefined;
2400
+ const clean_id = element_id.startsWith('#') ? element_id.substring(1) : element_id;
2401
+ const xu_ui_id = func.runtime.ui._element_id_to_xu_ui_id[clean_id];
2402
+ if (xu_ui_id) {
2403
+ return func.runtime.ui._meta_store[xu_ui_id];
2404
+ }
2405
+ return undefined;
2406
+ };
2407
+ func.runtime.ui.find_element_by_id = function () {
2408
+ return null;
2409
+ };
2410
+ func.runtime.ui.get_session_root = function () {
2411
+ return null;
2412
+ };
2413
+ func.runtime.ui.clear_screen_blockers = function () {
2414
+ // no-op in headless
2415
+ };
2416
+
2417
+ // ── Virtual element representation ──
2418
+ var _next_id = 1;
2419
+ function create_virtual_element(tag, attrs) {
2420
+ return {
2421
+ _v_id: _next_id++,
2422
+ tag: tag || 'div',
2423
+ attrs: attrs || {},
2424
+ children: [],
2425
+ parent: null,
2426
+ style: {},
2427
+ classList: [],
2428
+ textContent: '',
2429
+ innerHTML: '',
2430
+ hidden: false,
2431
+ _data: {},
2432
+ };
2433
+ }
2434
+
2435
+ // ── Core adapter methods ──
2436
+ func.runtime.ui.as_jquery = function (target) {
2437
+ if (!target) return { length: 0, data: function () { return {}; }, toArray: function () { return []; }, attr: function () { return undefined; } };
2438
+ if (target._v_id) return target;
2439
+ return target;
2440
+ };
2441
+ func.runtime.ui.get_first_node = function (target) {
2442
+ if (!target) return null;
2443
+ if (target._v_id) return target;
2444
+ if (Array.isArray(target)) return target[0] || null;
2445
+ return null;
2446
+ };
2447
+ func.runtime.ui.get_data = function (target) {
2448
+ if (!target) return {};
2449
+ if (target._v_id) {
2450
+ const xu_ui_id = target.attrs?.['xu-ui-id'];
2451
+ if (xu_ui_id) {
2452
+ const meta = func.runtime.ui._meta_store[xu_ui_id];
2453
+ if (meta) return meta;
2454
+ }
2455
+ return target._data;
2456
+ }
2457
+ return {};
2458
+ };
2459
+ func.runtime.ui.get_parent = function (target) {
2460
+ if (target?._v_id) return target.parent;
2461
+ return null;
2462
+ };
2463
+ func.runtime.ui.get_children = function (target) {
2464
+ if (target?._v_id) return target.children.slice();
2465
+ return [];
2466
+ };
2467
+ func.runtime.ui.find_by_selector = function () {
2468
+ return { length: 0, toArray: function () { return []; } };
2469
+ };
2470
+ func.runtime.ui.insert_before = function ($element) { return $element; };
2471
+ func.runtime.ui.insert_after = function ($element) { return $element; };
2472
+ func.runtime.ui.has_selector = function () { return false; };
2473
+ func.runtime.ui.append_html = function (target) { return target; };
2474
+ func.runtime.ui.set_style = function (target, prop, value) {
2475
+ if (target?._v_id) target.style[prop] = value;
2476
+ return target;
2477
+ };
2478
+ func.runtime.ui.get_attr = function (target, key) {
2479
+ if (target?._v_id) return target.attrs[key];
2480
+ return undefined;
2481
+ };
2482
+ func.runtime.ui.set_attr = function (target, key, value) {
2483
+ if (target?._v_id) target.attrs[key] = value;
2484
+ return target;
2485
+ };
2486
+ func.runtime.ui.set_data = function (target, key, value) {
2487
+ if (target?._v_id) {
2488
+ target._data[key] = value;
2489
+ const xu_ui_id = target.attrs?.['xu-ui-id'];
2490
+ if (xu_ui_id) {
2491
+ func.runtime.ui.set_meta(xu_ui_id, key, value);
2492
+ }
2493
+ }
2494
+ return target;
2495
+ };
2496
+ func.runtime.ui.clear_data = function (target) {
2497
+ if (target?._v_id) {
2498
+ target._data = {};
2499
+ const xu_ui_id = target.attrs?.['xu-ui-id'];
2500
+ if (xu_ui_id) func.runtime.ui.delete_meta(xu_ui_id);
2501
+ }
2502
+ return target;
2503
+ };
2504
+ func.runtime.ui.add_class = function (target, cls) {
2505
+ if (target?._v_id && cls && !target.classList.includes(cls)) target.classList.push(cls);
2506
+ return target;
2507
+ };
2508
+ func.runtime.ui.remove_class = function (target, cls) {
2509
+ if (target?._v_id) {
2510
+ const idx = target.classList.indexOf(cls);
2511
+ if (idx !== -1) target.classList.splice(idx, 1);
2512
+ }
2513
+ return target;
2514
+ };
2515
+ func.runtime.ui.set_html = function (target, value) {
2516
+ if (target?._v_id) target.innerHTML = value;
2517
+ return target;
2518
+ };
2519
+ func.runtime.ui.set_text = function (target, value) {
2520
+ if (target?._v_id) target.textContent = value;
2521
+ return target;
2522
+ };
2523
+ func.runtime.ui.show = function (target) {
2524
+ if (target?._v_id) target.hidden = false;
2525
+ return target;
2526
+ };
2527
+ func.runtime.ui.hide = function (target) {
2528
+ if (target?._v_id) target.hidden = true;
2529
+ return target;
2530
+ };
2531
+ func.runtime.ui.append = function ($target, $element) {
2532
+ if ($target?._v_id && $element?._v_id) {
2533
+ $target.children.push($element);
2534
+ $element.parent = $target;
2535
+ }
2536
+ return $element;
2537
+ };
2538
+ func.runtime.ui.append_to = function ($element, $target) {
2539
+ return func.runtime.ui.append($target, $element);
2540
+ };
2541
+ func.runtime.ui.empty = function (target) {
2542
+ if (target?._v_id) target.children = [];
2543
+ return target;
2544
+ };
2545
+ func.runtime.ui.remove = function (target) {
2546
+ if (target?._v_id) {
2547
+ const xu_ui_id = target.attrs?.['xu-ui-id'];
2548
+ if (xu_ui_id) func.runtime.ui.delete_meta(xu_ui_id);
2549
+ if (target.parent?._v_id) {
2550
+ const idx = target.parent.children.indexOf(target);
2551
+ if (idx !== -1) target.parent.children.splice(idx, 1);
2552
+ }
2553
+ }
2554
+ return true;
2555
+ };
2556
+ func.runtime.ui.set_display_contents = function ($element) {
2557
+ return func.runtime.ui.set_style($element, 'display', 'contents');
2558
+ };
2559
+ func.runtime.ui.create_xurender = function (xu_ui_id, $target, hidden) {
2560
+ const el = create_virtual_element('xurender', { 'xu-ui-id': xu_ui_id });
2561
+ if (hidden) el.hidden = true;
2562
+ return func.runtime.ui.append_to(el, $target);
2563
+ };
2564
+ func.runtime.ui.replace_with = function ($source, $target) {
2565
+ if ($source?._v_id && $source.parent?._v_id) {
2566
+ const idx = $source.parent.children.indexOf($source);
2567
+ if (idx !== -1 && $target?._v_id) {
2568
+ $source.parent.children[idx] = $target;
2569
+ $target.parent = $source.parent;
2570
+ }
2571
+ }
2572
+ return $target;
2573
+ };
2574
+ func.runtime.ui.remove_xu_ui = function (xu_ui_id) {
2575
+ func.runtime.ui.delete_meta(xu_ui_id);
2576
+ return true;
2577
+ };
2578
+ func.runtime.ui.build_debug_info = function (nodeP, $container, items) {
2579
+ const container_data = func.runtime.ui.get_data($container);
2580
+ return {
2581
+ id: nodeP.id,
2582
+ parent_id: container_data?.xuData?.ui_id,
2583
+ items: items,
2584
+ };
2585
+ };
2586
+ func.runtime.ui.get_node_snapshot = function (nodeP) {
2587
+ if (!nodeP) return nodeP;
2588
+ if (func.runtime.ui.node_snapshot_cache.has(nodeP)) {
2589
+ return func.runtime.ui.node_snapshot_cache.get(nodeP);
2590
+ }
2591
+ const snapshot = klona.klona(nodeP);
2592
+ func.runtime.ui.node_snapshot_cache.set(nodeP, snapshot);
2593
+ return snapshot;
2594
+ };
2595
+ func.runtime.ui.get_node_child_items = function (nodeP) {
2596
+ if (!nodeP?.children?.length) return [];
2597
+ if (func.runtime.ui.node_child_items_cache.has(nodeP)) {
2598
+ return func.runtime.ui.node_child_items_cache.get(nodeP);
2599
+ }
2600
+ const items = nodeP.children.map(function (val) { return val.xu_tree_id || val.id; });
2601
+ func.runtime.ui.node_child_items_cache.set(nodeP, items);
2602
+ return items;
2603
+ };
2604
+ func.runtime.ui.get_node_children_by_id = function (nodeP) {
2605
+ if (!nodeP?.children?.length) return {};
2606
+ if (func.runtime.ui.node_children_by_id_cache.has(nodeP)) {
2607
+ return func.runtime.ui.node_children_by_id_cache.get(nodeP);
2608
+ }
2609
+ const children_by_id = {};
2610
+ for (let i = 0; i < nodeP.children.length; i++) {
2611
+ const child = nodeP.children[i];
2612
+ if (child?.id) children_by_id[child.id] = child;
2613
+ }
2614
+ func.runtime.ui.node_children_by_id_cache.set(nodeP, children_by_id);
2615
+ return children_by_id;
2616
+ };
2617
+ func.runtime.ui.build_container_xu_data = function (options) {
2618
+ const container_data = func.runtime.ui.get_data(options.$container);
2619
+ const containerXuData = container_data?.xuData;
2620
+ return {
2621
+ SESSION_ID: options.SESSION_ID,
2622
+ prog_id: options.paramsP.prog_id,
2623
+ nodeid: options.nodeP.id,
2624
+ ui_type: options.nodeP.tagName,
2625
+ recordid: options.currentRecordId,
2626
+ paramsP: options.paramsP,
2627
+ key: options.keyP,
2628
+ key_path: options.key_path,
2629
+ screenId: options.paramsP.screenId,
2630
+ parent_container: containerXuData?.ui_id,
2631
+ elem_key: options.elem_key,
2632
+ properties: options.prop,
2633
+ node: options.nodeP,
2634
+ node_org: func.runtime.ui.get_node_snapshot(options.nodeP),
2635
+ is_panelP: options.paramsP.is_panelP,
2636
+ ui_id: options.ui_id,
2637
+ elem_prop: options.elem_propP,
2638
+ debug_info: func.runtime.ui.build_debug_info(options.nodeP, options.$container, options.items),
2639
+ parent_node: options.parent_nodeP,
2640
+ currentRecordId: options.currentRecordId,
2641
+ $root_container: options.$root_container,
2642
+ parent_element_ui_id: containerXuData?.ui_id,
2643
+ is_placeholder: !!options.is_placeholder,
2644
+ };
2645
+ };
2646
+ func.runtime.ui.apply_container_meta = function ($div, options) {
2647
+ if ($div?._v_id) $div.attrs['xu-ui-id'] = options.ui_id;
2648
+ const xuData = func.runtime.ui.build_container_xu_data(options);
2649
+ if (options.parent_infoP?.iterate_info) {
2650
+ xuData.iterate_info = options.parent_infoP.iterate_info;
2651
+ }
2652
+ if ($div?._v_id) {
2653
+ $div._data.xuData = xuData;
2654
+ $div._data.xuAttributes = {};
2655
+ }
2656
+ func.runtime.ui.set_meta(options.ui_id, 'xuData', xuData);
2657
+ func.runtime.ui.set_meta(options.ui_id, 'xuAttributes', {});
2658
+ if (options.is_placeholder && $div?._v_id) $div.classList.push('display_none');
2659
+ if (options.classP && $div?._v_id) $div.classList.push(options.classP);
2660
+ return $div;
2661
+ };
2662
+ func.runtime.ui.get_append_target = function ($container, $appendToP) {
2663
+ return $appendToP || $container || null;
2664
+ };
2665
+ func.runtime.ui.create_element = function (tag_name) {
2666
+ return create_virtual_element(tag_name);
2667
+ };
2668
+ func.runtime.ui.create_svg_element = function () {
2669
+ return create_virtual_element('svg');
2670
+ };
2671
+ func.runtime.ui.create_container_element = function (div_typeP) {
2672
+ return create_virtual_element(div_typeP || 'div');
2673
+ };
2674
+ func.runtime.ui.build_xu_ui_id_seed = function (nodeP, dsSessionP, key_path, currentRecordId) {
2675
+ const nodeId = nodeP.xu_tree_id || nodeP.id;
2676
+ const elem_key = `${nodeId}-${key_path}-${currentRecordId}`;
2677
+ return `${nodeP.id}-${elem_key}-${dsSessionP?.toString() || ''}`;
2678
+ };
2679
+ func.runtime.ui.generate_xu_ui_id = async function (SESSION_ID, nodeP, $container, paramsP, keyP, precomputed) {
2680
+ precomputed = precomputed || {};
2681
+ const dsSessionP = paramsP.dsSessionP;
2682
+ const _ds = SESSION_OBJ[SESSION_ID].DS_GLB[dsSessionP];
2683
+ const containerXuData = precomputed.container_xu_data || func.runtime.ui.get_data($container)?.xuData;
2684
+ const currentRecordId = typeof precomputed.currentRecordId !== 'undefined' ? precomputed.currentRecordId : containerXuData?.recordid || _ds?.currentRecordId || '';
2685
+ const key_path = precomputed.key_path || `${containerXuData?.key_path || '0'}-${keyP || '0'}`;
2686
+ const ui_id = func.runtime.ui.build_xu_ui_id_seed(nodeP, dsSessionP, key_path, currentRecordId);
2687
+
2688
+ if (func.runtime.ui.ui_id_hash_cache.has(ui_id)) {
2689
+ return func.runtime.ui.ui_id_hash_cache.get(ui_id);
2690
+ }
2691
+
2692
+ const hashed_ui_id = await func.common.fastHash(ui_id);
2693
+ func.runtime.ui.ui_id_hash_cache.set(ui_id, hashed_ui_id);
2694
+ return hashed_ui_id;
2695
+ };
2696
+ func.runtime.ui.create_container = async function (options) {
2697
+ const _paramsP = klona.klona(options.paramsP);
2698
+ const _ds = SESSION_OBJ[options.SESSION_ID].DS_GLB[_paramsP.dsSessionP];
2699
+ const $appendTo = func.runtime.ui.get_append_target(options.$container, options.$appendToP);
2700
+ if (!$appendTo) return null;
2701
+ const container_data = func.runtime.ui.get_data(options.$container);
2702
+ const container_xu_data = container_data?.xuData;
2703
+ const items = func.runtime.ui.get_node_child_items(options.nodeP);
2704
+ const currentRecordId = container_xu_data?.recordid || (_ds ? _ds.currentRecordId : '');
2705
+
2706
+ try {
2707
+ const key_path = `${container_xu_data?.key_path || '0'}-${options.keyP || '0'}`;
2708
+ const elem_key = `${options.nodeP.xu_tree_id || options.nodeP.id}-${key_path}-${currentRecordId}`;
2709
+ const $div = func.runtime.ui.create_container_element(options.div_typeP);
2710
+ const new_ui_id = await func.runtime.ui.generate_xu_ui_id(options.SESSION_ID, options.nodeP, options.$container, options.paramsP, options.keyP, {
2711
+ container_xu_data,
2712
+ currentRecordId,
2713
+ key_path,
2714
+ });
2715
+
2716
+ func.runtime.ui.apply_container_meta($div, {
2717
+ ui_id: new_ui_id,
2718
+ paramsP: _paramsP,
2719
+ nodeP: options.nodeP,
2720
+ currentRecordId,
2721
+ keyP: options.keyP,
2722
+ key_path,
2723
+ $container: options.$container,
2724
+ prop: options.prop,
2725
+ elem_key,
2726
+ elem_propP: options.elem_propP,
2727
+ items,
2728
+ parent_nodeP: options.parent_nodeP,
2729
+ $root_container: options.$root_container,
2730
+ parent_infoP: options.parent_infoP,
2731
+ is_placeholder: options.is_placeholder,
2732
+ classP: options.classP,
2733
+ SESSION_ID: options.SESSION_ID,
2734
+ });
2735
+
2736
+ func.runtime.ui.append_to($div, $appendTo);
2737
+ return $div;
2738
+ } catch (e) {
2739
+ console.error(e);
2740
+ }
2741
+
2742
+ return null;
2743
+ };
2147
2744
  func.datasource = {};
2148
2745
  func.datasource.create = async function (
2149
2746
  SESSION_ID,
@@ -2338,7 +2935,7 @@ func.datasource.create = async function (
2338
2935
  }
2339
2936
  // vvvv run at worker vvvv
2340
2937
  if (_ds) IS_DATASOURCE_REFRESH = true;
2341
- var data = _.assignIn(
2938
+ var data = Object.assign(
2342
2939
  {
2343
2940
  session_id: SESSION_ID,
2344
2941
  dataSourceSessionGlobal: SESSION_OBJ[SESSION_ID].dataSourceSessionGlobal,
@@ -2393,7 +2990,7 @@ func.datasource.prepare = async function (SESSION_ID, prog_id, dataSourceNoP, pa
2393
2990
  // &&
2394
2991
  // glb.PARAMETER_NODES_ARR.includes(screenInfo.properties.menuType)
2395
2992
  ) {
2396
- if (!_.isEmpty(screenInfo.properties.progParams)) {
2993
+ if (!xu_isEmpty(screenInfo.properties.progParams)) {
2397
2994
  _ds.in_parameters = {};
2398
2995
  _ds.out_parameters = {};
2399
2996
 
@@ -2525,7 +3122,7 @@ func.datasource.prepare = async function (SESSION_ID, prog_id, dataSourceNoP, pa
2525
3122
  _ds.refreshed = true;
2526
3123
 
2527
3124
  if (_ds.watcher) {
2528
- _.set(_ds, _ds.watcher.path, _ds.watcher.newValue);
3125
+ xu_set(_ds, _ds.watcher.path, _ds.watcher.newValue);
2529
3126
  }
2530
3127
  try {
2531
3128
  if (!_ds.v) _ds.v = {};
@@ -3512,7 +4109,7 @@ func.datasource.execute_field_init_events = async function (SESSION_ID, dataSour
3512
4109
  var expression = undefined;
3513
4110
  if (val.eventInfo.props.condition) expression = val.eventInfo.props.condition;
3514
4111
  var expCond = {};
3515
- if (expression && !_.isEmpty(expression)) {
4112
+ if (expression && !xu_isEmpty(expression)) {
3516
4113
  // check if expression exist
3517
4114
  expCond = await func.expression.get(SESSION_ID, expression, dataSourceSession, 'condition', rowIdP, null, null, val.fieldId); // execute expression
3518
4115
  cond = expCond.result;
@@ -3656,7 +4253,7 @@ func.datasource.get_view_events_count = async function (SESSION_ID, dataSourceSe
3656
4253
 
3657
4254
  _ds.viewEventExec_arr[index] = [];
3658
4255
 
3659
- if (!_prog.progEvents || _.isEmpty(_prog.progEvents)) return 0;
4256
+ if (!_prog.progEvents || xu_isEmpty(_prog.progEvents)) return 0;
3660
4257
 
3661
4258
  for (const event_obj of _prog.progEvents) {
3662
4259
  if (event_obj.data.type !== typeP) continue; // was false?? changed to true 020317
@@ -3669,7 +4266,7 @@ func.datasource.get_view_events_count = async function (SESSION_ID, dataSourceSe
3669
4266
  }
3670
4267
  }
3671
4268
 
3672
- if (_.isEmpty(event_obj.workflow)) continue;
4269
+ if (xu_isEmpty(event_obj.workflow)) continue;
3673
4270
  for (const trigger_obj of event_obj.workflow) {
3674
4271
  if (trigger_obj.data.enabled) {
3675
4272
  var expression;
@@ -3722,7 +4319,7 @@ func.datasource.execute_view_events = async function (SESSION_ID, dataSourceSess
3722
4319
  var index = typeP;
3723
4320
  if (eventIdP) index = typeP + '_' + eventIdP;
3724
4321
  var arr = _ds.viewEventExec_arr[index];
3725
- if (_.isEmpty(arr)) return;
4322
+ if (xu_isEmpty(arr)) return;
3726
4323
 
3727
4324
  for await (const val of arr) {
3728
4325
  if (!glb.IS_WORKER || !func.utils.is_onscreen_event(val.eventInfo.data.action) || (glb.IS_WORKER && _ds.v.run_at === 'server' && !func.utils.is_onscreen_event(val.eventInfo.data.action))) {
@@ -3735,7 +4332,8 @@ func.datasource.execute_view_events = async function (SESSION_ID, dataSourceSess
3735
4332
  if (cond) {
3736
4333
  var elem_params = undefined;
3737
4334
  if (!glb.IS_WORKER) {
3738
- elem_params = $('#' + _ds.containerId).data('params');
4335
+ const container_meta = func.runtime.ui.get_meta_by_element_id(_ds.containerId);
4336
+ elem_params = container_meta?.params;
3739
4337
  }
3740
4338
  const ret = await func.events.execute(
3741
4339
  SESSION_ID,
@@ -3853,12 +4451,12 @@ func.datasource.clean_all = function (SESSION_ID, dsP) {
3853
4451
 
3854
4452
  var get_child_ds = function (ds) {
3855
4453
  var arr = [];
3856
- _.forEach(SESSION_OBJ[SESSION_ID].DS_GLB, function (val, key) {
4454
+ for (const [key, val] of Object.entries(SESSION_OBJ[SESSION_ID].DS_GLB)) {
3857
4455
  if (val.parentDataSourceNo == ds) {
3858
4456
  arr.push(key);
3859
4457
  arr = arr.concat(get_child_ds(key));
3860
4458
  }
3861
- });
4459
+ }
3862
4460
  return arr;
3863
4461
  };
3864
4462
 
@@ -3871,13 +4469,13 @@ func.datasource.clean = function (SESSION_ID, screenIdP) {
3871
4469
  var arr = [];
3872
4470
  for (const [key, val] of Object.entries(SESSION_OBJ[SESSION_ID].DS_GLB)) {
3873
4471
  try {
4472
+ const _screen_el = val.screenId ? document.getElementById(val.screenId) : null;
4473
+ const screen_parent_id = _screen_el?.parentElement?.id || null;
3874
4474
  if (
3875
4475
  Number(key) > 0 &&
3876
4476
  (val.screenId === screenIdP ||
3877
4477
  val.rootScreenId === screenIdP ||
3878
- $('#' + val.screenId)
3879
- .parent()
3880
- .attr('id') === screenIdP ||
4478
+ screen_parent_id === screenIdP ||
3881
4479
  (val && val.parentDataSourceNo && arr.includes(val.parentDataSourceNo.toString())))
3882
4480
  ) {
3883
4481
  arr.push(key);
@@ -3943,14 +4541,12 @@ func.datasource.del = function (SESSION_ID, dsP) {
3943
4541
 
3944
4542
  var delete_pending_jobs = function () {
3945
4543
  var arr = [];
3946
- _.forEach(SESSION_OBJ[SESSION_ID].WORKER_OBJ.jobs, function (val, key) {
4544
+ for (const [key, val] of Object.entries(SESSION_OBJ[SESSION_ID].WORKER_OBJ.jobs)) {
3947
4545
  if (val && val.dsSessionP == dsP) {
3948
4546
  arr.push(key);
3949
- for (const [key, val] of Object.entries($('.screen_blocker'))) {
3950
- $(val).remove();
3951
- }
4547
+ func.runtime.ui.clear_screen_blockers();
3952
4548
  }
3953
- });
4549
+ }
3954
4550
  for (let val of arr.reverse()) {
3955
4551
  SESSION_OBJ[SESSION_ID].WORKER_OBJ.jobs.splice(val, 1);
3956
4552
  }
@@ -3969,8 +4565,9 @@ func.datasource.del = function (SESSION_ID, dsP) {
3969
4565
  }
3970
4566
  }
3971
4567
  }
3972
- if ($(SESSION_OBJ[SESSION_ID].root_element).find('xu-nav').length) {
3973
- var ds_obj = $(SESSION_OBJ[SESSION_ID].root_element).find('xu-nav')?.data()?.xuData.nav_params;
4568
+ const _nav_node = func.runtime.ui.get_first_node(SESSION_OBJ[SESSION_ID].root_element)?.querySelector?.('xu-nav');
4569
+ if (_nav_node) {
4570
+ var ds_obj = func.runtime.ui.get_data(_nav_node)?.xuData?.nav_params;
3974
4571
  if (ds_obj) {
3975
4572
  delete ds_obj[dsP];
3976
4573
  }
@@ -4077,7 +4674,7 @@ func.datasource.update = async function (SESSION_ID, datasource_changes, update_
4077
4674
  for (const [field_id, value] of Object.entries(fields_data)) {
4078
4675
  // mechanism to make update directly on the datasource object
4079
4676
  if (record_id === 'datasource_main') {
4080
- _.set(_ds, field_id, value);
4677
+ xu_set(_ds, field_id, value);
4081
4678
  const ret = update_xu_ref(dataSource);
4082
4679
  if (ret) {
4083
4680
  fields_changed.push(field_id);
@@ -4125,7 +4722,7 @@ func.datasource.update = async function (SESSION_ID, datasource_changes, update_
4125
4722
  try {
4126
4723
  const row_idx = func.common.find_ROWID_idx(_ds, record_id);
4127
4724
  // if (_ds.data_feed.rows[row_idx][field_id] !== value) {
4128
- if (!_.isEqual(_ds.data_feed.rows[row_idx][field_id], value)) {
4725
+ if (!xu_isEqual(_ds.data_feed.rows[row_idx][field_id], value)) {
4129
4726
  _ds.data_feed.rows[row_idx][field_id] = value;
4130
4727
  await set_fieldComputed_dependencies(dataSource, field_id, null);
4131
4728
 
@@ -4198,11 +4795,11 @@ func.datasource.update = async function (SESSION_ID, datasource_changes, update_
4198
4795
  }
4199
4796
 
4200
4797
  if (glb.IS_WORKER) {
4201
- if (!update_local_scope_only && !_.isEmpty(client_datasource_changes)) {
4798
+ if (!update_local_scope_only && !xu_isEmpty(client_datasource_changes)) {
4202
4799
  func.utils.post_back_to_client(SESSION_ID, 'update_client_eventChangesResults_from_worker', _session.worker_id, client_datasource_changes);
4203
4800
  }
4204
4801
  } else {
4205
- if (!update_local_scope_only && !_.isEmpty(server_datasource_changes)) {
4802
+ if (!update_local_scope_only && !xu_isEmpty(server_datasource_changes)) {
4206
4803
  const ret = await func.index.call_worker(SESSION_ID, {
4207
4804
  service: 'update_datasource_changes_from_client',
4208
4805
  data: {
@@ -4303,7 +4900,7 @@ func.datasource.callback = async function (SESSION_ID, dsSessionP, rowIdP, jobNo
4303
4900
  dsSession: dsSessionP,
4304
4901
  prop: _ds.log_prop + ' ' + 'adapter',
4305
4902
  });
4306
- if (!glb.IS_WORKER) $(_session.root_element).css('cursor', 'default');
4903
+ if (!glb.IS_WORKER) func.runtime.platform.set_cursor(_session.root_element, 'default');
4307
4904
 
4308
4905
  if (_ds.prog_id) {
4309
4906
  let _ds = _session.DS_GLB[dsSessionP];
@@ -4855,7 +5452,7 @@ func.datasource.get_viewLoops = async function (SESSION_ID, dataSourceSession, d
4855
5452
  // var v = _ds.v;
4856
5453
  var ret = default_limit;
4857
5454
 
4858
- if (batch_source === 'db_data') ret = _.size(data.rows);
5455
+ if (batch_source === 'db_data') ret = data.rows.length;
4859
5456
  if (batch_source === 'array' || batch_source === 'csv') ret = data.length;
4860
5457
  if (batch_source === 'json') ret = Object.keys(data).length;
4861
5458
  if (_ds.progDataSource?.dataSourceLimit) {
@@ -4882,7 +5479,7 @@ func.datasource.set_VIEW_data = async function (SESSION_ID, args, _ds) {
4882
5479
  };
4883
5480
  _ds.viewEventExec_arr = {};
4884
5481
 
4885
- var view = _.cloneDeep(await func.utils.VIEWS_OBJ.get(SESSION_ID, args.prog_id));
5482
+ var view = klona.klona(await func.utils.VIEWS_OBJ.get(SESSION_ID, args.prog_id));
4886
5483
  // var view = klona.klona(await func.utils.VIEWS_OBJ.get(SESSION_ID, args.prog_id));
4887
5484
 
4888
5485
  _ds.v.dataSourceSrcType = view.dataSourceSrcType;
@@ -4928,11 +5525,11 @@ func.datasource.get_cast_val = async function (SESSION_ID, source, dsSession, va
4928
5525
  if (error) {
4929
5526
  return func.utils.debug_report(SESSION_ID, msg, '', 'W');
4930
5527
  }
4931
- func.utils.debug_report(SESSION_ID, msg + ' ' + _.capitalize(source) + prog_info, '', 'E');
5528
+ func.utils.debug_report(SESSION_ID, msg + ' ' + (source.charAt(0).toUpperCase() + source.slice(1).toLowerCase()) + prog_info, '', 'E');
4932
5529
  };
4933
5530
  const report_conversion_warn = function (res) {
4934
5531
  var msg = `type mismatch auto conversion from value ${valP} to ${typeP}`;
4935
- func.utils.debug_report(SESSION_ID, msg + ' ' + _.capitalize(source) + prog_info, '', 'W');
5532
+ func.utils.debug_report(SESSION_ID, msg + ' ' + (source.charAt(0).toUpperCase() + source.slice(1).toLowerCase()) + prog_info, '', 'W');
4936
5533
  };
4937
5534
  // var ret = valP;
4938
5535
  if (error) {
@@ -5001,7 +5598,7 @@ func.datasource.update_changes_for_out_parameter = async function (SESSION_ID, d
5001
5598
  }
5002
5599
  }
5003
5600
  }
5004
- if (!_.isEmpty(data)) {
5601
+ if (!xu_isEmpty(data)) {
5005
5602
  let datasource_changes = {
5006
5603
  [calling_dsP]: { [_calling_ds.currentRecordId]: data },
5007
5604
  };
@@ -5112,7 +5709,7 @@ func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
5112
5709
 
5113
5710
  if (idP !== 'system') {
5114
5711
  DOCS_OBJ[_app_id][idP] = await module.DOCS_OBJ_get(SESSION_ID, idP);
5115
- if (DOCS_OBJ[_app_id][idP] && _.isEmpty(DOCS_OBJ[_app_id][idP])) {
5712
+ if (DOCS_OBJ[_app_id][idP] && xu_isEmpty(DOCS_OBJ[_app_id][idP])) {
5116
5713
  await func.utils.remove_cached_objects(SESSION_ID);
5117
5714
 
5118
5715
  delete DOCS_OBJ[_app_id][idP];
@@ -5124,7 +5721,7 @@ func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
5124
5721
  if (APP_OBJ[_app_id].app_imported_projects) {
5125
5722
  for await (const imported_app_id of APP_OBJ[_app_id].app_imported_projects) {
5126
5723
  var view_ret = await module.DOCS_OBJ_get(SESSION_ID, 'global_' + imported_app_id);
5127
- DOCS_OBJ[_app_id][idP] = _.merge(DOCS_OBJ[_app_id][idP], view_ret);
5724
+ DOCS_OBJ[_app_id][idP] = Object.assign(DOCS_OBJ[_app_id][idP], view_ret);
5128
5725
  }
5129
5726
  }
5130
5727
  return DOCS_OBJ[_app_id][idP];
@@ -5206,13 +5803,13 @@ func.utils.get_dateTime = async function (SESSION_ID, typeP, dateP) {
5206
5803
  let ts = await get_server_ts();
5207
5804
  sysDate = new Date(ts);
5208
5805
  }
5209
- var day = _.padStart(sysDate.getDate(), 2, '0');
5210
- var month = _.padStart(sysDate.getMonth() + 1, 2, '0');
5806
+ var day = String(sysDate.getDate()).padStart(2, '0');
5807
+ var month = String(sysDate.getMonth() + 1).padStart(2, '0');
5211
5808
  var year = sysDate.getFullYear();
5212
- var week = _.padStart(getWeekNumber(sysDate), 2, '0');
5213
- var hour = _.padStart(sysDate.getHours(), 2, '0');
5214
- var minute = _.padStart(sysDate.getMinutes(), 2, '0');
5215
- var second = _.padStart(sysDate.getSeconds(), 2, '0');
5809
+ var week = String(getWeekNumber(sysDate)).padStart(2, '0');
5810
+ var hour = String(sysDate.getHours()).padStart(2, '0');
5811
+ var minute = String(sysDate.getMinutes()).padStart(2, '0');
5812
+ var second = String(sysDate.getSeconds()).padStart(2, '0');
5216
5813
  if (typeP === 'SYS_DATE') return year + '-' + month + '-' + day;
5217
5814
  if (typeP === 'SYS_DATE_TIME') return year + '-' + month + '-' + day + 'T' + hour + ':' + minute;
5218
5815
  if (typeP === 'SYS_DATE_VALUE') return sysDate.valueOf();
@@ -5250,7 +5847,7 @@ func.utils.clean_returned_datasource = function (SESSION_ID, DS) {
5250
5847
  var _session = SESSION_OBJ[SESSION_ID];
5251
5848
  if (!_session.DS_GLB[DS]) return;
5252
5849
 
5253
- var obj = _.clone(_session.DS_GLB[DS]);
5850
+ var obj = { ..._session.DS_GLB[DS] };
5254
5851
 
5255
5852
  delete obj.screen_params;
5256
5853
 
@@ -5270,7 +5867,7 @@ func.utils.clean_returned_datasource = function (SESSION_ID, DS) {
5270
5867
 
5271
5868
  const clean_empty_objects = function () {
5272
5869
  for (const [key, val] of Object.entries(obj)) {
5273
- if (typeof val === 'object' && !Array.isArray(val) && _.isEmpty(val)) {
5870
+ if (typeof val === 'object' && !Array.isArray(val) && xu_isEmpty(val)) {
5274
5871
  delete obj[key];
5275
5872
  }
5276
5873
  }
@@ -5373,7 +5970,7 @@ func.utils.job_worker = function (session_id) {
5373
5970
  func.utils.debug_report(SESSION_ID, 'utils.worker.reset', 'worker not responding', 'E', '', _session.WORKER_OBJ.jobs);
5374
5971
  _session.WORKER_OBJ.jobs = [];
5375
5972
  _session.WORKER_OBJ.stat = null;
5376
- $('.screen_blocker').remove();
5973
+ func.runtime.ui.clear_screen_blockers();
5377
5974
  };
5378
5975
  return {
5379
5976
  _interval: null,
@@ -5514,8 +6111,9 @@ func.utils.makeid = function (length) {
5514
6111
  func.utils.get_device = function () {
5515
6112
  var device;
5516
6113
  try {
5517
- if (window.cordova) {
5518
- device = window.cordova.platformId;
6114
+ const win = func.runtime.platform.get_window();
6115
+ if (win?.cordova) {
6116
+ device = win.cordova.platformId;
5519
6117
  }
5520
6118
  } catch (e) {
5521
6119
  console.error('error using ui element in server side request');
@@ -5660,7 +6258,7 @@ func.utils.ws_worker.functions = {
5660
6258
  });
5661
6259
  },
5662
6260
  update_datasource_changes_from_client: async function (params, promise_queue_id) {
5663
- if (_.isEmpty(SESSION_OBJ)) return;
6261
+ if (xu_isEmpty(SESSION_OBJ)) return;
5664
6262
  var SESSION_ID = params.session_id;
5665
6263
  var _session = SESSION_OBJ[SESSION_ID];
5666
6264
  if (!_session) {
@@ -5856,16 +6454,8 @@ func.utils.clean_stringify_null = function (key, value) {
5856
6454
 
5857
6455
  func.utils.load_js_on_demand = async function (js_src, type) {
5858
6456
  const get_script = function (callback) {
5859
- function getScript(scriptUrl, callback) {
5860
- const script = document.createElement('script');
5861
- script.src = scriptUrl;
5862
- if (type) script.type = type;
5863
- script.onload = callback;
5864
-
5865
- document.head.appendChild(script);
5866
- }
5867
6457
  if (glb.IS_WORKER) {
5868
- callback(script);
6458
+ callback();
5869
6459
  return;
5870
6460
  }
5871
6461
  function isScriptLoaded(src) {
@@ -5875,8 +6465,7 @@ func.utils.load_js_on_demand = async function (js_src, type) {
5875
6465
  if (isScriptLoaded(js_src)) {
5876
6466
  callback(false);
5877
6467
  } else {
5878
- getScript(js_src, function (response, status) {
5879
- // console.log(response,status)
6468
+ func.runtime.platform.load_script(js_src, type, function () {
5880
6469
  callback(true);
5881
6470
  GLB_JS_SCRIPTS_LOADED.push(js_src);
5882
6471
  });
@@ -5889,33 +6478,11 @@ func.utils.load_js_on_demand = async function (js_src, type) {
5889
6478
  };
5890
6479
 
5891
6480
  func.utils.load_css_on_demand = function (css_href) {
5892
- function isCssLoaded(src) {
5893
- try {
5894
- return document.querySelector('link[href="' + src + '"]') ? true : false;
5895
- } catch (err) {
5896
- console.warn(`load css ${css_href} failed`);
5897
- return true;
5898
- }
5899
- }
5900
-
5901
- if (isCssLoaded(css_href)) {
5902
- return;
5903
- }
5904
- return $('<link/>', {
5905
- rel: 'stylesheet',
5906
- type: 'text/css',
5907
- href: css_href,
5908
- }).prependTo('head');
6481
+ func.runtime.platform.load_css(css_href);
5909
6482
  };
5910
6483
 
5911
6484
  func.utils.remove_js_css_file = function (filename, filetype) {
5912
- var targetelement = filetype == 'js' ? 'script' : filetype == 'css' ? 'link' : 'none'; //determine element type to create nodelist from
5913
- var targetattr = filetype == 'js' ? 'src' : filetype == 'css' ? 'href' : 'none'; //determine corresponding attribute to test for
5914
- var allsuspects = document.getElementsByTagName(targetelement);
5915
- for (var i = allsuspects.length; i >= 0; i--) {
5916
- //search backwards within node list for matching elements to remove
5917
- if (allsuspects[i] && allsuspects[i].getAttribute(targetattr) != null && allsuspects[i].getAttribute(targetattr).indexOf(filename) != -1) allsuspects[i].parentNode.removeChild(allsuspects[i]); //remove element by calling parentNode.removeChild()
5918
- }
6485
+ func.runtime.platform.remove_js_css(filename, filetype);
5919
6486
  };
5920
6487
 
5921
6488
  func.utils.replace_studio_drive_url = function (SESSION_ID, val) {
@@ -6262,7 +6829,7 @@ func.utils.alerts.popup = function (title, message, alert_type) {
6262
6829
  },
6263
6830
  ];
6264
6831
 
6265
- popup.create(_.upperFirst(alert_type), title, message, buttons);
6832
+ popup.create(alert_type.charAt(0).toUpperCase() + alert_type.slice(1), title, message, buttons);
6266
6833
  };
6267
6834
 
6268
6835
  func.utils.get_system_error_msg = function () {
@@ -6909,7 +7476,7 @@ func.utils.call_plugin_api = function (SESSION_ID, plugin_nameP, dataP) {
6909
7476
  app_token: _session.app_token,
6910
7477
  };
6911
7478
 
6912
- data = _.assignIn(data, dataP);
7479
+ data = Object.assign(data, dataP);
6913
7480
 
6914
7481
  fetch(`https://xuda.ai/ppi/${plugin_nameP}`, {
6915
7482
  method: 'POST',
@@ -7067,7 +7634,7 @@ func.utils.get_resource_filename = function (build, filename) {
7067
7634
  };
7068
7635
 
7069
7636
  func.utils.set_SYS_GLOBAL_OBJ_WIDGET_INFO = async function (SESSION_ID, docP) {
7070
- var obj = _.clone(docP);
7637
+ var obj = { ...docP };
7071
7638
  obj.date = await func.utils.get_dateTime(SESSION_ID, 'SYS_DATE', docP.date);
7072
7639
  obj.time = await func.utils.get_dateTime(SESSION_ID, 'SYS_TIME', docP.date);
7073
7640
 
@@ -7182,7 +7749,7 @@ func.events.validate = async function (SESSION_ID, triggerP, dsSessionP, eventId
7182
7749
 
7183
7750
  let value = await func.common.get_cast_val(SESSION_ID, 'events', fieldId, field_info.props.fieldType, args[fieldId].value);
7184
7751
 
7185
- if (!_.isEmpty(args[fieldId].fx)) {
7752
+ if (!xu_isEmpty(args[fieldId].fx)) {
7186
7753
  const fx_ret = await func.expression.get(SESSION_ID, args[fieldId].fx, dsSessionP, 'update');
7187
7754
  value = fx_ret.result;
7188
7755
  }
@@ -7204,7 +7771,7 @@ func.events.validate = async function (SESSION_ID, triggerP, dsSessionP, eventId
7204
7771
  if (_event.workflow) {
7205
7772
  // check if event property exist
7206
7773
 
7207
- if (!_event.workflow || _.isEmpty(_event.workflow)) return;
7774
+ if (!_event.workflow || xu_isEmpty(_event.workflow)) return;
7208
7775
  // check events has rows
7209
7776
  for (const trigger_obj of _event.workflow) {
7210
7777
  //run events rows
@@ -7224,17 +7791,19 @@ func.events.validate = async function (SESSION_ID, triggerP, dsSessionP, eventId
7224
7791
  if (_ds.panel_div_id) {
7225
7792
  try {
7226
7793
  container = '#' + _ds.panel_div_id;
7227
- if ($(container).data().xuData.panel_info) {
7228
- screen_prop = $(container).data().xuData.panel_info.paramsP;
7794
+ const panel_meta = func.runtime.ui.get_meta_by_element_id(_ds.panel_div_id);
7795
+ if (panel_meta?.xuData?.panel_info) {
7796
+ screen_prop = panel_meta.xuData.panel_info.paramsP;
7229
7797
  } else {
7230
7798
  ///////////////
7231
7799
  container = '#' + _session.DS_GLB[dsSessionP].screenId;
7800
+ const screen_meta = func.runtime.ui.get_meta_by_element_id(_session.DS_GLB[dsSessionP].screenId);
7232
7801
 
7233
- if ($(container) && $(container).data()) {
7234
- screen_prop = $(container).data().xuData.paramsP;
7802
+ if (screen_meta?.xuData) {
7803
+ screen_prop = screen_meta.xuData.paramsP;
7235
7804
  }
7236
7805
 
7237
- if (!$(container) || !$(container).length) {
7806
+ if (!screen_meta) {
7238
7807
  container = '#' + _session.DS_GLB[dsSessionP].containerId;
7239
7808
  }
7240
7809
  //////////////
@@ -7244,12 +7813,13 @@ func.events.validate = async function (SESSION_ID, triggerP, dsSessionP, eventId
7244
7813
  }
7245
7814
  } else {
7246
7815
  container = '#' + _ds.screenId;
7816
+ const screen_meta = func.runtime.ui.get_meta_by_element_id(_ds.screenId);
7247
7817
 
7248
- if ($(container) && $(container).data()) {
7249
- screen_prop = $(container).data().xuData.paramsP;
7818
+ if (screen_meta?.xuData) {
7819
+ screen_prop = screen_meta.xuData.paramsP;
7250
7820
  }
7251
7821
 
7252
- if (!$(container) || !$(container).length) {
7822
+ if (!screen_meta) {
7253
7823
  container = '#' + _ds.containerId;
7254
7824
  }
7255
7825
  }
@@ -7420,12 +7990,12 @@ func.events.find_job_index = function (SESSION_ID, jobNoP) {
7420
7990
  var _session = SESSION_OBJ[SESSION_ID];
7421
7991
  var ret = null;
7422
7992
  if (!_session.WORKER_OBJ) return ret;
7423
- _.forEach(_session.WORKER_OBJ.jobs, function (val, key) {
7993
+ for (const [key, val] of Object.entries(_session.WORKER_OBJ.jobs)) {
7424
7994
  if (val && val.job_num == jobNoP) {
7425
7995
  ret = key;
7426
- return false;
7996
+ break;
7427
7997
  }
7428
- });
7998
+ }
7429
7999
  return ret;
7430
8000
  };
7431
8001
  func.events.execute = async function (
@@ -7487,7 +8057,7 @@ func.events.execute = async function (
7487
8057
  var field_elm = elementP;
7488
8058
 
7489
8059
  var calling_field_id = field_elm;
7490
- if (field_elm && typeof field_elm === 'object') calling_field_id = field_elm.attr('xu-ui-id');
8060
+ if (field_elm && typeof field_elm === 'object') calling_field_id = func.runtime.ui.get_attr(field_elm, 'xu-ui-id');
7491
8061
 
7492
8062
  var log_nodeId;
7493
8063
  var log_prog_id;
@@ -7650,7 +8220,7 @@ func.events.execute = async function (
7650
8220
  var $calling_container;
7651
8221
  if (_session.WORKER_OBJ.jobs[job_index]) {
7652
8222
  if (_session.WORKER_OBJ.jobs[job_index].paramsP) {
7653
- $calling_container = $('#' + _session.WORKER_OBJ.jobs[job_index].paramsP.callingContainerP);
8223
+ $calling_container = func.runtime.ui.find_element_by_id(_session.WORKER_OBJ.jobs[job_index].paramsP.callingContainerP);
7654
8224
  } else {
7655
8225
  $calling_container = ''; // calling from datasource 0
7656
8226
  _session.WORKER_OBJ.jobs[job_index].paramsP = {};
@@ -7666,7 +8236,7 @@ func.events.execute = async function (
7666
8236
  return await func.runtime.ui.init_screen({
7667
8237
  SESSION_ID,
7668
8238
  prog_id: await get_prog_id(),
7669
- sourceScreenP: $(containerP)?.data()?.xuData.screenId,
8239
+ sourceScreenP: func.runtime.ui.get_data(containerP)?.xuData?.screenId,
7670
8240
  callingDataSource_objP: _session.DS_GLB[dsSession],
7671
8241
  $callingContainerP: $calling_container,
7672
8242
  triggerIdP: eventIdP,
@@ -7698,7 +8268,7 @@ func.events.execute = async function (
7698
8268
  var _session = SESSION_OBJ[SESSION_ID];
7699
8269
 
7700
8270
  const set_SYS_GLOBAL_OBJ_WIDGET_INFO = async function (docP) {
7701
- var obj = _.clone(docP);
8271
+ var obj = { ...docP };
7702
8272
  obj.date = await func.utils.get_dateTime(SESSION_ID, 'SYS_DATE', docP.date);
7703
8273
  obj.time = await func.utils.get_dateTime(SESSION_ID, 'SYS_TIME', docP.date);
7704
8274
 
@@ -7913,7 +8483,7 @@ func.events.execute = async function (
7913
8483
  emit_event: async function () {
7914
8484
  if (refIdP.value) {
7915
8485
  // if (descP.value) {
7916
- $(document).trigger(refIdP.value, [_session.DS_GLB[dsSession]]);
8486
+ func.runtime.platform.emit(refIdP.value, [_session.DS_GLB[dsSession]]);
7917
8487
  } else {
7918
8488
  func.utils.debug_report(SESSION_ID, 'func.events.execute', 'Event name missing', 'E');
7919
8489
  }
@@ -7985,7 +8555,7 @@ func.events.execute = async function (
7985
8555
 
7986
8556
  let _ds_new = _session.DS_GLB[ret.dsSessionP];
7987
8557
  let parameters = args?.calling_trigger_prop?.data?.name?.parameters;
7988
- if (parameters && !_.isEmpty(parameters)) {
8558
+ if (parameters && !xu_isEmpty(parameters)) {
7989
8559
  await func.datasource.update_changes_for_out_parameter(SESSION_ID, _ds_new.dsSession, _ds.dsSession);
7990
8560
  }
7991
8561
 
@@ -8004,7 +8574,7 @@ func.events.execute = async function (
8004
8574
  },
8005
8575
  update: async function () {
8006
8576
  const obj_values_to_update = func.datasource.get_viewFields_for_update_function(SESSION_ID, calling_trigger_prop, null, dsSessionP);
8007
- if (!obj_values_to_update || _.isEmpty(obj_values_to_update)) {
8577
+ if (!obj_values_to_update || xu_isEmpty(obj_values_to_update)) {
8008
8578
  func.utils.debug_report(SESSION_ID, 'Update values object is empty', '', 'W');
8009
8579
  if (jobNoP) func.events.delete_job(SESSION_ID, jobNoP);
8010
8580
  return;
@@ -8014,12 +8584,14 @@ func.events.execute = async function (
8014
8584
  for await (const [key, val] of Object.entries(obj_values_to_update)) {
8015
8585
  var $element;
8016
8586
 
8587
+ var iterate_info = null;
8017
8588
  if (elementP) {
8018
- $element = $(`[xu-ui-id="${elementP}"]`).clone(true);
8589
+ const element_meta = func.runtime.ui.get_meta(elementP, 'xuData');
8590
+ iterate_info = element_meta?.iterate_info || null;
8019
8591
  }
8020
8592
 
8021
- let ret_field_id = await func.expression.get(SESSION_ID, val.id.trim(), dsSessionP, 'update', null, null, null, null, null, null, $element?.length ? $element?.data()?.xuData?.iterate_info : null);
8022
- let ret_value = await func.expression.get(SESSION_ID, val.val.trim(), dsSessionP, 'update', null, null, null, null, null, null, $element?.length ? $element?.data()?.xuData?.iterate_info : null);
8593
+ let ret_field_id = await func.expression.get(SESSION_ID, val.id.trim(), dsSessionP, 'update', null, null, null, null, null, null, iterate_info);
8594
+ let ret_value = await func.expression.get(SESSION_ID, val.val.trim(), dsSessionP, 'update', null, null, null, null, null, null, iterate_info);
8023
8595
  let _field_id = ret_field_id.result;
8024
8596
  let _value = ret_value.result;
8025
8597
 
@@ -8194,11 +8766,11 @@ func.events.execute = async function (
8194
8766
  if (error) {
8195
8767
  return func.utils.debug_report(SESSION_ID, msg, '', 'W');
8196
8768
  }
8197
- func.utils.debug_report(SESSION_ID, msg + ' ' + _.capitalize(source) + prog_info, '', 'E');
8769
+ func.utils.debug_report(SESSION_ID, msg + ' ' + (source.charAt(0).toUpperCase() + source.slice(1).toLowerCase()) + prog_info, '', 'E');
8198
8770
  };
8199
8771
  const report_conversion_warn = function (res) {
8200
8772
  var msg = `${elementP} >${triggerP} >${functionP} > type mismatch auto conversion from value ${valP} to ${typeP}`;
8201
- func.utils.debug_report(SESSION_ID, msg + ' ' + _.capitalize(source) + prog_info, '', 'W');
8773
+ func.utils.debug_report(SESSION_ID, msg + ' ' + (source.charAt(0).toUpperCase() + source.slice(1).toLowerCase()) + prog_info, '', 'W');
8202
8774
  };
8203
8775
  // var ret = valP;
8204
8776
  if (error) {
@@ -8213,8 +8785,7 @@ func.events.execute = async function (
8213
8785
  // report_conversion_warn
8214
8786
  // );
8215
8787
 
8216
- var payload = _.reduce(
8217
- payload_arr,
8788
+ var payload = payload_arr.reduce(
8218
8789
  (ret, val, key) => {
8219
8790
  ret[val.key] = module.cast(val.type, val.val, report_conversion_error, report_conversion_warn);
8220
8791
 
@@ -8331,12 +8902,12 @@ func.events.check_jobs_idle = async function (SESSION_ID, jobsP) {
8331
8902
  var listener = setInterval(function () {
8332
8903
  var found;
8333
8904
  for (const [key, val] of Object.entries(jobsP)) {
8334
- _.forEach(_session.WORKER_OBJ.jobs, function (val2, key2) {
8905
+ for (const [key2, val2] of Object.entries(_session.WORKER_OBJ.jobs)) {
8335
8906
  if (key2 === val) {
8336
8907
  found = true;
8337
- return false;
8908
+ break;
8338
8909
  }
8339
- });
8910
+ }
8340
8911
  }
8341
8912
  if (!found) {
8342
8913
  do_callback();
@@ -8344,7 +8915,7 @@ func.events.check_jobs_idle = async function (SESSION_ID, jobsP) {
8344
8915
  }
8345
8916
  }, 100);
8346
8917
  var do_callback = function () {
8347
- window.clearInterval(listener);
8918
+ clearInterval(listener);
8348
8919
  resolve();
8349
8920
  };
8350
8921
  });
@@ -8356,7 +8927,7 @@ setInterval(function () {
8356
8927
  }, 1000);
8357
8928
 
8358
8929
  func.events.set_browser_changes = function (dsP, fieldsChangedP) {
8359
- if (fieldsChangedP.includes('SYS_GLOBAL_STR_BROWSER_TITLE')) document.title = dsP.dataset_new['SYS_GLOBAL_STR_BROWSER_TITLE'];
8930
+ if (fieldsChangedP.includes('SYS_GLOBAL_STR_BROWSER_TITLE')) func.runtime.platform.set_title(dsP.dataset_new['SYS_GLOBAL_STR_BROWSER_TITLE']);
8360
8931
  };
8361
8932
 
8362
8933
  func.events.execute_PENDING_OPEN_URL_EVENTS = async function () {
@@ -8379,7 +8950,7 @@ func.events.execute_PENDING_OPEN_URL_EVENTS = async function () {
8379
8950
  prog_id: params_obj.prog,
8380
8951
  sourceScreenP: null,
8381
8952
  callingDataSource_objP: null,
8382
- $callingContainerP: $('#embed_' + SESSION_ID),
8953
+ $callingContainerP: func.runtime.ui.get_session_root(SESSION_ID),
8383
8954
  triggerIdP: null,
8384
8955
  rowIdP: null,
8385
8956
  jobNoP: null,
@@ -8405,7 +8976,7 @@ func.events.invoke = async function (event_id) {
8405
8976
  var ds;
8406
8977
  for await (const [ds_key, val] of Object.entries(_session.DS_GLB)) {
8407
8978
  const _view_obj = await func.utils.VIEWS_OBJ.get(SESSION_ID, val.prog_id);
8408
- if (_.isEmpty(_view_obj.progEvents)) continue;
8979
+ if (xu_isEmpty(_view_obj.progEvents)) continue;
8409
8980
  if (ds) break;
8410
8981
  for await (const [key, val] of Object.entries(_view_obj.progEvents)) {
8411
8982
  if (val?.data?.type === 'user_defined' && val.data.event_name === event_id) {
@@ -8462,7 +9033,7 @@ func.expression.get = async function (SESSION_ID, valP, dsSessionP, sourceP, row
8462
9033
  }
8463
9034
  }
8464
9035
 
8465
- ret = ret.replace(/\&amp;/g, '&');
9036
+ if (ret.includes('&amp;')) ret = ret.replace(/\&amp;/g, '&');
8466
9037
  ret = func.utils.replace_studio_drive_url(SESSION_ID, ret);
8467
9038
 
8468
9039
  const end_results = function () {
@@ -8526,13 +9097,13 @@ func.expression.get = async function (SESSION_ID, valP, dsSessionP, sourceP, row
8526
9097
  const variable_not_exist = async function () {
8527
9098
  try {
8528
9099
  if (sourceP !== 'arguments') {
8529
- if (ret && ret.substr(0, 6) === '_DATE_') {
8530
- ret = ret.substr(6);
9100
+ if (ret && ret.startsWith('_DATE_')) {
9101
+ ret = ret.slice(6);
8531
9102
  } else if (
8532
- (ret && ret.length === 10 && ret.substr(4, 1) === '-' && ret.substr(7, 1) === '-') ||
8533
- ret === 'self' // bypass eval for date 2017-03-22
9103
+ ret === 'self' || // bypass eval for 'self'
9104
+ (ret && ret.length === 10 && ret[4] === '-' && ret[7] === '-') // bypass eval for date 2017-03-22
8534
9105
  ) {
8535
- ret = ret;
9106
+ // date or 'self' — skip eval, return as-is
8536
9107
  } else {
8537
9108
  ret = await func.expression.secure_eval(SESSION_ID, sourceP, ret, jobNo, dsSessionP, js_script_callback);
8538
9109
  }
@@ -8566,8 +9137,10 @@ func.expression.get = async function (SESSION_ID, valP, dsSessionP, sourceP, row
8566
9137
  var var_Arr = [];
8567
9138
  const split = func.expression.parse(ret) || [];
8568
9139
  // console.log(valP, split);
8569
- for await (const [arr_key, val] of Object.entries(split)) {
9140
+ const split_entries = Object.entries(split);
9141
+ for (let entry_i = 0; entry_i < split_entries.length; entry_i++) {
8570
9142
  // run each field
9143
+ const [arr_key, val] = split_entries[entry_i];
8571
9144
  const key = Number(arr_key);
8572
9145
  var_Arr[key] = {};
8573
9146
  var_Arr[key].value = val.value;
@@ -8612,14 +9185,16 @@ func.expression.get = async function (SESSION_ID, valP, dsSessionP, sourceP, row
8612
9185
  var prevData = var_Arr[key - 1].value;
8613
9186
  var_Arr[key].value = prevData[data]; // @objB[@var]
8614
9187
  if (val.value.indexOf('.') > -1) {
8615
- property2 = await func.expression.get_property(val.value).property2; //val.value.substr(val.value.indexOf(".") + 1, val.value.length); // get .
9188
+ const props_split = await func.expression.get_property(val.value);
9189
+ property2 = props_split.property2;
8616
9190
  if (prevData[data]) set_value(prevData[data][property2]);
8617
9191
  // var_Arr[key].value = prevData[data][property2]; //@objB[@var].property
8618
9192
  }
8619
9193
  delete var_Arr[key - 1];
8620
9194
  } else {
8621
- property1 = await func.expression.get_property(val.value).property1;
8622
- property2 = await func.expression.get_property(val.value).property2;
9195
+ const props = await func.expression.get_property(val.value);
9196
+ property1 = props.property1;
9197
+ property2 = props.property2;
8623
9198
  if (property1) {
8624
9199
  var_Arr[key].value = data[property1]; // @var["value"] or @var.property
8625
9200
  if (property2) {
@@ -8667,7 +9242,7 @@ func.expression.get = async function (SESSION_ID, valP, dsSessionP, sourceP, row
8667
9242
  var exp_exist;
8668
9243
  var var_error_found;
8669
9244
  // merge arr values
8670
- _.forEach(var_Arr, function (val, key) {
9245
+ var_Arr.forEach(function (val, key) {
8671
9246
  if (sourceP === 'UI Property EXP') {
8672
9247
  let ret = func.utils.get_drive_url(SESSION_ID, val.value, true);
8673
9248
  if (ret.changed) {
@@ -8743,7 +9318,7 @@ func.expression.get = async function (SESSION_ID, valP, dsSessionP, sourceP, row
8743
9318
  if (exp.res) res = exp.res;
8744
9319
  // do second pass when exp exist
8745
9320
  else res = [exp.result];
8746
- fields = _.assignIn(exp.fields, fieldsP);
9321
+ fields = Object.assign(exp.fields, fieldsP);
8747
9322
  }
8748
9323
  var result = join(res);
8749
9324
 
@@ -9065,9 +9640,15 @@ func.expression.get = async function (SESSION_ID, valP, dsSessionP, sourceP, row
9065
9640
  // return res;
9066
9641
  // };
9067
9642
 
9643
+ func.expression._parse_cache = new Map();
9644
+
9068
9645
  func.expression.parse = function (input) {
9069
9646
  if (typeof input !== 'string') return [];
9070
9647
 
9648
+ if (func.expression._parse_cache.has(input)) {
9649
+ return func.expression._parse_cache.get(input).map(function (s) { return Object.assign({}, s); });
9650
+ }
9651
+
9071
9652
  const segments = [];
9072
9653
  let pos = 0;
9073
9654
 
@@ -9090,7 +9671,14 @@ func.expression.parse = function (input) {
9090
9671
  pos += part.length;
9091
9672
  }
9092
9673
 
9093
- return segments;
9674
+ // Evict oldest entry if cache exceeds limit
9675
+ if (func.expression._parse_cache.size >= 500) {
9676
+ const firstKey = func.expression._parse_cache.keys().next().value;
9677
+ func.expression._parse_cache.delete(firstKey);
9678
+ }
9679
+ func.expression._parse_cache.set(input, segments);
9680
+
9681
+ return segments.map(function (s) { return Object.assign({}, s); });
9094
9682
  };
9095
9683
 
9096
9684
  func.expression.get_property = async function (valP) {