@xuda.io/runtime-bundle 1.0.1436 → 1.0.1438
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/js/xuda-runtime-bundle.js +1644 -55
- package/js/xuda-runtime-bundle.min.js +3 -3
- package/js/xuda-runtime-mini-bundle.js +10 -2
- package/js/xuda-runtime-mini-bundle.min.js +1 -1
- package/js/xuda-runtime-slim.js +1644 -55
- package/js/xuda-runtime-slim.min.es.js +1644 -55
- package/js/xuda-runtime-slim.min.js +3 -3
- package/js/xuda-server-bundle.min.mjs +1 -1
- package/js/xuda-server-bundle.mjs +1033 -8
- package/js/xuda-worker-bundle.js +1033 -8
- package/js/xuda-worker-bundle.min.js +1 -1
- package/js/xuda_common-bundle.js +338 -6
- package/js/xuda_common-bundle.min.js +1 -1
- package/package.json +1 -1
package/js/xuda-worker-bundle.js
CHANGED
|
@@ -291,7 +291,30 @@ func.runtime.platform.emit = function (name, data) {
|
|
|
291
291
|
};
|
|
292
292
|
|
|
293
293
|
// ── Platform helpers for DOM-independent resource loading ──
|
|
294
|
-
func.runtime.platform.
|
|
294
|
+
func.runtime.platform.apply_element_attributes = function (node, attributes, excluded_keys = []) {
|
|
295
|
+
if (!node?.setAttribute || !attributes) {
|
|
296
|
+
return node;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const excluded = new Set(excluded_keys || []);
|
|
300
|
+
const attr_keys = Object.keys(attributes);
|
|
301
|
+
for (let index = 0; index < attr_keys.length; index++) {
|
|
302
|
+
const key = attr_keys[index];
|
|
303
|
+
if (!key || excluded.has(key)) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const value = attributes[key];
|
|
308
|
+
if (value === false || typeof value === 'undefined') {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
node.setAttribute(key, value === null ? '' : `${value}`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return node;
|
|
316
|
+
};
|
|
317
|
+
func.runtime.platform.load_script = function (url, type, callback, attributes) {
|
|
295
318
|
const doc = func.runtime.platform.get_document();
|
|
296
319
|
if (!doc?.createElement || !doc?.head?.appendChild) {
|
|
297
320
|
if (callback) {
|
|
@@ -299,19 +322,63 @@ func.runtime.platform.load_script = function (url, type, callback) {
|
|
|
299
322
|
}
|
|
300
323
|
return;
|
|
301
324
|
}
|
|
325
|
+
const find_existing_script = function () {
|
|
326
|
+
const asset_key = attributes?.['data-xuda-asset-key'];
|
|
327
|
+
const scripts = doc.querySelectorAll ? Array.from(doc.querySelectorAll('script')) : [];
|
|
328
|
+
return scripts.find(function (script) {
|
|
329
|
+
if (asset_key && script.getAttribute('data-xuda-asset-key') === asset_key) {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
return !!(url && script.getAttribute('src') === url);
|
|
333
|
+
}) || null;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const existing_script = find_existing_script();
|
|
337
|
+
if (existing_script) {
|
|
338
|
+
if (callback) {
|
|
339
|
+
if (existing_script.getAttribute('data-xuda-loaded') === 'true' || !url) {
|
|
340
|
+
callback();
|
|
341
|
+
} else {
|
|
342
|
+
existing_script.addEventListener('load', callback, { once: true });
|
|
343
|
+
existing_script.addEventListener('error', callback, { once: true });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return existing_script;
|
|
347
|
+
}
|
|
348
|
+
|
|
302
349
|
const script = doc.createElement('script');
|
|
303
350
|
script.src = url;
|
|
304
351
|
if (type) script.type = type;
|
|
305
|
-
script
|
|
352
|
+
func.runtime.platform.apply_element_attributes(script, attributes, ['src', 'type']);
|
|
353
|
+
script.onload = function () {
|
|
354
|
+
script.setAttribute('data-xuda-loaded', 'true');
|
|
355
|
+
if (callback) {
|
|
356
|
+
callback();
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
script.onerror = function () {
|
|
360
|
+
if (callback) {
|
|
361
|
+
callback();
|
|
362
|
+
}
|
|
363
|
+
};
|
|
306
364
|
doc.head.appendChild(script);
|
|
365
|
+
return script;
|
|
307
366
|
};
|
|
308
|
-
func.runtime.platform.load_css = function (href) {
|
|
367
|
+
func.runtime.platform.load_css = function (href, attributes) {
|
|
309
368
|
const doc = func.runtime.platform.get_document();
|
|
310
369
|
if (!doc?.createElement || !doc?.head) {
|
|
311
370
|
return;
|
|
312
371
|
}
|
|
313
372
|
try {
|
|
314
|
-
|
|
373
|
+
const asset_key = attributes?.['data-xuda-asset-key'];
|
|
374
|
+
const existing_links = doc.querySelectorAll ? Array.from(doc.querySelectorAll('link')) : [];
|
|
375
|
+
const existing = existing_links.find(function (link) {
|
|
376
|
+
if (asset_key && link.getAttribute('data-xuda-asset-key') === asset_key) {
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
return !!(href && link.getAttribute('href') === href);
|
|
380
|
+
});
|
|
381
|
+
if (existing) return existing;
|
|
315
382
|
} catch (err) {
|
|
316
383
|
return;
|
|
317
384
|
}
|
|
@@ -319,7 +386,9 @@ func.runtime.platform.load_css = function (href) {
|
|
|
319
386
|
link.rel = 'stylesheet';
|
|
320
387
|
link.type = 'text/css';
|
|
321
388
|
link.href = href;
|
|
389
|
+
func.runtime.platform.apply_element_attributes(link, attributes, ['href']);
|
|
322
390
|
doc.head.insertBefore(link, doc.head.firstChild);
|
|
391
|
+
return link;
|
|
323
392
|
};
|
|
324
393
|
func.runtime.platform.remove_js_css = function (filename, filetype) {
|
|
325
394
|
const doc = func.runtime.platform.get_document();
|
|
@@ -353,6 +422,128 @@ func.runtime.platform.set_cursor = function (element, cursor) {
|
|
|
353
422
|
node.style.cursor = cursor;
|
|
354
423
|
}
|
|
355
424
|
};
|
|
425
|
+
func.runtime.program.normalize_doc_for_runtime = function (doc) {
|
|
426
|
+
if (!doc || doc.__xudaRuntimeNormalized || !Array.isArray(doc.progUi) || !doc.progUi.length) {
|
|
427
|
+
return doc;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const normalize_tag_name = function (tag_name) {
|
|
431
|
+
return `${tag_name || ''}`.trim().toLowerCase();
|
|
432
|
+
};
|
|
433
|
+
const merge_attributes = function (target, source) {
|
|
434
|
+
const merged = { ...(target || {}) };
|
|
435
|
+
const source_attributes = source || {};
|
|
436
|
+
const keys = Object.keys(source_attributes);
|
|
437
|
+
|
|
438
|
+
for (let index = 0; index < keys.length; index++) {
|
|
439
|
+
const key = keys[index];
|
|
440
|
+
const value = source_attributes[key];
|
|
441
|
+
if (typeof value === 'undefined') {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (key === 'class' && merged.class && value) {
|
|
446
|
+
const next_value = `${merged.class} ${value}`.trim();
|
|
447
|
+
merged.class = Array.from(new Set(next_value.split(/\s+/).filter(Boolean))).join(' ');
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (key === 'style' && merged.style && value) {
|
|
452
|
+
merged.style = `${merged.style}; ${value}`.trim();
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (typeof merged[key] === 'undefined') {
|
|
457
|
+
merged[key] = value;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return merged;
|
|
462
|
+
};
|
|
463
|
+
const normalize_nodes = function (nodes, state) {
|
|
464
|
+
const normalized_nodes = [];
|
|
465
|
+
|
|
466
|
+
for (let index = 0; index < (nodes || []).length; index++) {
|
|
467
|
+
const node = nodes[index];
|
|
468
|
+
if (!node || typeof node !== 'object') {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const tag_name = normalize_tag_name(node.tagName);
|
|
473
|
+
|
|
474
|
+
if (tag_name === '!doctype') {
|
|
475
|
+
state.changed = true;
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (tag_name === 'html') {
|
|
480
|
+
state.changed = true;
|
|
481
|
+
state.root_attributes = merge_attributes(state.root_attributes, node.attributes);
|
|
482
|
+
normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (tag_name === 'head') {
|
|
487
|
+
state.changed = true;
|
|
488
|
+
normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (tag_name === 'body') {
|
|
493
|
+
state.changed = true;
|
|
494
|
+
state.root_attributes = merge_attributes(state.root_attributes, node.attributes);
|
|
495
|
+
normalized_nodes.push.apply(normalized_nodes, normalize_nodes(node.children, state));
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
let next_node = node;
|
|
500
|
+
if (Array.isArray(node.children) && node.children.length) {
|
|
501
|
+
const next_children = normalize_nodes(node.children, state);
|
|
502
|
+
if (next_children !== node.children) {
|
|
503
|
+
next_node = {
|
|
504
|
+
...node,
|
|
505
|
+
children: next_children,
|
|
506
|
+
};
|
|
507
|
+
state.changed = true;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
normalized_nodes.push(next_node);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return normalized_nodes;
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const [root_node, ...extra_nodes] = doc.progUi;
|
|
518
|
+
if (!root_node || typeof root_node !== 'object') {
|
|
519
|
+
return doc;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const state = {
|
|
523
|
+
changed: false,
|
|
524
|
+
root_attributes: {},
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const normalized_children = normalize_nodes([...(root_node.children || []), ...extra_nodes], state);
|
|
528
|
+
const merged_attributes = merge_attributes(root_node.attributes, state.root_attributes);
|
|
529
|
+
|
|
530
|
+
if (!state.changed && !Object.keys(state.root_attributes).length) {
|
|
531
|
+
doc.__xudaRuntimeNormalized = true;
|
|
532
|
+
return doc;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return {
|
|
536
|
+
...doc,
|
|
537
|
+
__xudaRuntimeNormalized: true,
|
|
538
|
+
progUi: [
|
|
539
|
+
{
|
|
540
|
+
...root_node,
|
|
541
|
+
attributes: merged_attributes,
|
|
542
|
+
children: normalized_children,
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
};
|
|
546
|
+
};
|
|
356
547
|
|
|
357
548
|
func.runtime.env = {
|
|
358
549
|
get_url_params: function () {
|
|
@@ -610,6 +801,147 @@ func.runtime.workers.delete_promise = function (SESSION_ID, worker_id, promise_q
|
|
|
610
801
|
delete registry_entry.promise_queue[promise_queue_id];
|
|
611
802
|
return true;
|
|
612
803
|
};
|
|
804
|
+
func.runtime.render.clone_runtime_options = function (value) {
|
|
805
|
+
if (typeof structuredClone === 'function') {
|
|
806
|
+
try {
|
|
807
|
+
return structuredClone(value);
|
|
808
|
+
} catch (_) {}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (Array.isArray(value)) {
|
|
812
|
+
return value.map(function (item) {
|
|
813
|
+
return func.runtime.render.clone_runtime_options(item);
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (value && typeof value === 'object') {
|
|
818
|
+
const cloned = {};
|
|
819
|
+
const keys = Object.keys(value);
|
|
820
|
+
for (let index = 0; index < keys.length; index++) {
|
|
821
|
+
const key = keys[index];
|
|
822
|
+
cloned[key] = func.runtime.render.clone_runtime_options(value[key]);
|
|
823
|
+
}
|
|
824
|
+
return cloned;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
return value;
|
|
828
|
+
};
|
|
829
|
+
func.runtime.render.normalize_runtime_bootstrap = function (raw_options = {}) {
|
|
830
|
+
const options = raw_options || {};
|
|
831
|
+
let app_computing_mode = options.app_computing_mode || '';
|
|
832
|
+
let app_render_mode = options.app_render_mode || '';
|
|
833
|
+
let app_client_activation = options.app_client_activation || '';
|
|
834
|
+
let ssr_payload = options.ssr_payload || null;
|
|
835
|
+
|
|
836
|
+
if (typeof ssr_payload === 'string') {
|
|
837
|
+
try {
|
|
838
|
+
ssr_payload = JSON.parse(ssr_payload);
|
|
839
|
+
} catch (_) {
|
|
840
|
+
ssr_payload = null;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (ssr_payload && typeof ssr_payload === 'object') {
|
|
845
|
+
ssr_payload = func.runtime.render.clone_runtime_options(ssr_payload);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (!app_computing_mode) {
|
|
849
|
+
if (app_render_mode === 'ssr_first_page' || app_render_mode === 'ssr_full') {
|
|
850
|
+
app_computing_mode = 'server';
|
|
851
|
+
} else {
|
|
852
|
+
app_computing_mode = 'main';
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
switch (app_computing_mode) {
|
|
857
|
+
case 'main':
|
|
858
|
+
app_render_mode = 'csr';
|
|
859
|
+
app_client_activation = 'none';
|
|
860
|
+
break;
|
|
861
|
+
|
|
862
|
+
case 'worker':
|
|
863
|
+
app_render_mode = 'csr';
|
|
864
|
+
app_client_activation = 'none';
|
|
865
|
+
break;
|
|
866
|
+
|
|
867
|
+
default:
|
|
868
|
+
app_computing_mode = 'server';
|
|
869
|
+
if (app_render_mode !== 'ssr_full') {
|
|
870
|
+
app_render_mode = 'ssr_first_page';
|
|
871
|
+
}
|
|
872
|
+
app_client_activation = app_render_mode === 'ssr_full' ? 'hydrate' : 'takeover';
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (ssr_payload && typeof ssr_payload === 'object') {
|
|
877
|
+
if (!ssr_payload.app_render_mode) {
|
|
878
|
+
ssr_payload.app_render_mode = app_render_mode;
|
|
879
|
+
}
|
|
880
|
+
if (!ssr_payload.app_client_activation) {
|
|
881
|
+
ssr_payload.app_client_activation = app_client_activation;
|
|
882
|
+
}
|
|
883
|
+
if (!ssr_payload.app_computing_mode) {
|
|
884
|
+
ssr_payload.app_computing_mode = app_computing_mode;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return {
|
|
889
|
+
app_computing_mode,
|
|
890
|
+
app_render_mode,
|
|
891
|
+
app_client_activation,
|
|
892
|
+
ssr_payload,
|
|
893
|
+
};
|
|
894
|
+
};
|
|
895
|
+
func.runtime.render.apply_runtime_bootstrap_defaults = function (target = {}) {
|
|
896
|
+
const normalized = func.runtime.render.normalize_runtime_bootstrap(target);
|
|
897
|
+
target.app_computing_mode = normalized.app_computing_mode;
|
|
898
|
+
target.app_render_mode = normalized.app_render_mode;
|
|
899
|
+
target.app_client_activation = normalized.app_client_activation;
|
|
900
|
+
target.ssr_payload = normalized.ssr_payload;
|
|
901
|
+
return normalized;
|
|
902
|
+
};
|
|
903
|
+
func.runtime.render.is_server_render_mode = function (target = {}) {
|
|
904
|
+
const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
|
|
905
|
+
return normalized.app_computing_mode === 'server' && normalized.app_render_mode !== 'csr';
|
|
906
|
+
};
|
|
907
|
+
func.runtime.render.is_takeover_mode = function (target = {}) {
|
|
908
|
+
const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
|
|
909
|
+
return normalized.app_client_activation === 'takeover';
|
|
910
|
+
};
|
|
911
|
+
func.runtime.render.is_hydration_mode = function (target = {}) {
|
|
912
|
+
const normalized = func.runtime.render.normalize_runtime_bootstrap(target?.opt || target);
|
|
913
|
+
return normalized.app_client_activation === 'hydrate';
|
|
914
|
+
};
|
|
915
|
+
func.runtime.render.get_ssr_payload = function (target = {}) {
|
|
916
|
+
if (target?.opt?.ssr_payload) {
|
|
917
|
+
return target.opt.ssr_payload;
|
|
918
|
+
}
|
|
919
|
+
if (target?.ssr_payload) {
|
|
920
|
+
return target.ssr_payload;
|
|
921
|
+
}
|
|
922
|
+
const win = func.runtime.platform.get_window();
|
|
923
|
+
return win?.__XUDA_SSR__ || null;
|
|
924
|
+
};
|
|
925
|
+
func.runtime.render.should_use_ssr_payload = function (SESSION_ID, paramsP) {
|
|
926
|
+
const session = SESSION_OBJ?.[SESSION_ID];
|
|
927
|
+
const payload = func.runtime.render.get_ssr_payload(session);
|
|
928
|
+
if (!payload || payload._consumed) {
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
if (paramsP?.prog_id && payload.prog_id && payload.prog_id !== paramsP.prog_id) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
return true;
|
|
935
|
+
};
|
|
936
|
+
func.runtime.render.mark_ssr_payload_consumed = function (SESSION_ID) {
|
|
937
|
+
const session = SESSION_OBJ?.[SESSION_ID];
|
|
938
|
+
const payload = func.runtime.render.get_ssr_payload(session);
|
|
939
|
+
if (!payload || typeof payload !== 'object') {
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
payload._consumed = true;
|
|
943
|
+
return true;
|
|
944
|
+
};
|
|
613
945
|
func.runtime.render.get_root_data_system = function (SESSION_ID) {
|
|
614
946
|
return SESSION_OBJ[SESSION_ID]?.DS_GLB?.[0]?.data_system || null;
|
|
615
947
|
};
|
|
@@ -1013,10 +1345,10 @@ func.runtime.resources.load_cdn = async function (SESSION_ID, resource) {
|
|
|
1013
1345
|
await func.utils.load_js_on_demand(normalized_resource.src);
|
|
1014
1346
|
break;
|
|
1015
1347
|
case 'css':
|
|
1016
|
-
|
|
1348
|
+
func.runtime.platform.load_css(normalized_resource.src);
|
|
1017
1349
|
break;
|
|
1018
1350
|
case 'module':
|
|
1019
|
-
func.utils.load_js_on_demand(normalized_resource.src, 'module');
|
|
1351
|
+
await func.utils.load_js_on_demand(normalized_resource.src, 'module');
|
|
1020
1352
|
break;
|
|
1021
1353
|
default:
|
|
1022
1354
|
await func.utils.load_js_on_demand(normalized_resource.src);
|
|
@@ -2353,6 +2685,691 @@ func.common.fastHash = function (inputString) {
|
|
|
2353
2685
|
};
|
|
2354
2686
|
|
|
2355
2687
|
glb.new_xu_render = false;
|
|
2688
|
+
func.runtime = func.runtime || {};
|
|
2689
|
+
func.runtime.ui = func.runtime.ui || {};
|
|
2690
|
+
func.runtime.render = func.runtime.render || {};
|
|
2691
|
+
func.runtime.widgets = func.runtime.widgets || {};
|
|
2692
|
+
|
|
2693
|
+
// Shared render-tree contract helpers live here so browser and headless runtimes can resolve the same UI structure.
|
|
2694
|
+
|
|
2695
|
+
func.runtime.render.TREE_CONTRACT_VERSION = func.runtime.render.TREE_CONTRACT_VERSION || 'xuda.render_tree.v1';
|
|
2696
|
+
func.runtime.render._tree_widget_capability_cache = func.runtime.render._tree_widget_capability_cache || {};
|
|
2697
|
+
|
|
2698
|
+
func.runtime.render.safe_clone_tree_value = function (value) {
|
|
2699
|
+
if (typeof structuredClone === 'function') {
|
|
2700
|
+
try {
|
|
2701
|
+
return structuredClone(value);
|
|
2702
|
+
} catch (_) {
|
|
2703
|
+
// Fall through to the recursive clone below.
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
if (Array.isArray(value)) {
|
|
2708
|
+
return value.map(function (item) {
|
|
2709
|
+
return func.runtime.render.safe_clone_tree_value(item);
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
if (value && typeof value === 'object') {
|
|
2714
|
+
const cloned = {};
|
|
2715
|
+
const keys = Object.keys(value);
|
|
2716
|
+
for (let index = 0; index < keys.length; index++) {
|
|
2717
|
+
const key = keys[index];
|
|
2718
|
+
cloned[key] = func.runtime.render.safe_clone_tree_value(value[key]);
|
|
2719
|
+
}
|
|
2720
|
+
return cloned;
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
return value;
|
|
2724
|
+
};
|
|
2725
|
+
func.runtime.render.sort_tree_debug_value = function (value) {
|
|
2726
|
+
if (Array.isArray(value)) {
|
|
2727
|
+
return value.map(function (item) {
|
|
2728
|
+
return func.runtime.render.sort_tree_debug_value(item);
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
if (value && typeof value === 'object') {
|
|
2733
|
+
const sorted = {};
|
|
2734
|
+
const keys = Object.keys(value).sort();
|
|
2735
|
+
for (let index = 0; index < keys.length; index++) {
|
|
2736
|
+
const key = keys[index];
|
|
2737
|
+
sorted[key] = func.runtime.render.sort_tree_debug_value(value[key]);
|
|
2738
|
+
}
|
|
2739
|
+
return sorted;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
return value;
|
|
2743
|
+
};
|
|
2744
|
+
func.runtime.render.is_tree_node = function (nodeP) {
|
|
2745
|
+
return !!nodeP?.contract && nodeP.contract === func.runtime.render.TREE_CONTRACT_VERSION;
|
|
2746
|
+
};
|
|
2747
|
+
func.runtime.render.get_tree_source_node = function (nodeP) {
|
|
2748
|
+
if (!func.runtime.render.is_tree_node(nodeP)) {
|
|
2749
|
+
return nodeP || null;
|
|
2750
|
+
}
|
|
2751
|
+
return nodeP?.meta?.source_node || null;
|
|
2752
|
+
};
|
|
2753
|
+
func.runtime.render.get_tree_source_snapshot = function (nodeP) {
|
|
2754
|
+
if (!func.runtime.render.is_tree_node(nodeP)) {
|
|
2755
|
+
return func.runtime.render.safe_clone_tree_value(nodeP);
|
|
2756
|
+
}
|
|
2757
|
+
return nodeP?.meta?.source_snapshot || null;
|
|
2758
|
+
};
|
|
2759
|
+
func.runtime.render.get_tree_node_kind = function (nodeP) {
|
|
2760
|
+
const tag_name = typeof nodeP?.tagName === 'string' ? nodeP.tagName.toLowerCase() : '';
|
|
2761
|
+
const node_type = typeof nodeP?.type === 'string' ? nodeP.type.toLowerCase() : '';
|
|
2762
|
+
|
|
2763
|
+
if (tag_name === 'xu-widget') return 'widget';
|
|
2764
|
+
if (tag_name === 'xu-single-view') return 'single_view';
|
|
2765
|
+
if (tag_name === 'xu-multi-view') return 'multi_view';
|
|
2766
|
+
if (tag_name === 'xu-panel') return 'panel';
|
|
2767
|
+
if (tag_name === 'xu-teleport') return 'teleport';
|
|
2768
|
+
if (tag_name === 'xurender') return 'placeholder';
|
|
2769
|
+
if (tag_name === '#text' || node_type === 'text') return 'text';
|
|
2770
|
+
if (!tag_name && typeof nodeP?.content === 'string' && !Array.isArray(nodeP?.children)) return 'text';
|
|
2771
|
+
return 'element';
|
|
2772
|
+
};
|
|
2773
|
+
func.runtime.render.get_tree_node_id = function (nodeP, pathP) {
|
|
2774
|
+
if (nodeP?.id) {
|
|
2775
|
+
return nodeP.id;
|
|
2776
|
+
}
|
|
2777
|
+
if (nodeP?.id_org) {
|
|
2778
|
+
return nodeP.id_org;
|
|
2779
|
+
}
|
|
2780
|
+
const normalized_path = Array.isArray(pathP) && pathP.length ? pathP.join('.') : 'root';
|
|
2781
|
+
return `tree-node-${normalized_path}`;
|
|
2782
|
+
};
|
|
2783
|
+
func.runtime.render.get_tree_controls = function (attributes) {
|
|
2784
|
+
const attrs = attributes || {};
|
|
2785
|
+
const get_first_defined = function (keys) {
|
|
2786
|
+
for (let index = 0; index < keys.length; index++) {
|
|
2787
|
+
const key = keys[index];
|
|
2788
|
+
if (Object.prototype.hasOwnProperty.call(attrs, key)) {
|
|
2789
|
+
return attrs[key];
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
return null;
|
|
2793
|
+
};
|
|
2794
|
+
return {
|
|
2795
|
+
xu_for: get_first_defined(['xu-for', 'xu-exp:xu-for']),
|
|
2796
|
+
xu_if: get_first_defined(['xu-if', 'xu-exp:xu-if']),
|
|
2797
|
+
xu_render: get_first_defined(['xu-render', 'xu-exp:xu-render']),
|
|
2798
|
+
};
|
|
2799
|
+
};
|
|
2800
|
+
func.runtime.render.get_tree_node_capabilities = async function (options) {
|
|
2801
|
+
const attributes = options?.attributes || {};
|
|
2802
|
+
const plugin_name = attributes['xu-widget'];
|
|
2803
|
+
if (!plugin_name) {
|
|
2804
|
+
return null;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
const cache = func.runtime.render._tree_widget_capability_cache;
|
|
2808
|
+
if (cache[plugin_name]) {
|
|
2809
|
+
return func.runtime.render.safe_clone_tree_value(cache[plugin_name]);
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
let capabilities = {
|
|
2813
|
+
browser: true,
|
|
2814
|
+
headless: false,
|
|
2815
|
+
};
|
|
2816
|
+
|
|
2817
|
+
try {
|
|
2818
|
+
if (options.SESSION_ID && options.paramsP && func.runtime.widgets?.create_context && func.runtime.widgets?.get_definition) {
|
|
2819
|
+
const widget_context = func.runtime.widgets.create_context(options.SESSION_ID, options.paramsP, attributes);
|
|
2820
|
+
const definition = await func.runtime.widgets.get_definition(widget_context);
|
|
2821
|
+
capabilities = func.runtime.widgets.normalize_capabilities(definition);
|
|
2822
|
+
}
|
|
2823
|
+
} catch (_) {
|
|
2824
|
+
// Keep the safe browser-only default when the widget definition is unavailable.
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
cache[plugin_name] = capabilities;
|
|
2828
|
+
return func.runtime.render.safe_clone_tree_value(capabilities);
|
|
2829
|
+
};
|
|
2830
|
+
func.runtime.render.ensure_tree_node = async function (options) {
|
|
2831
|
+
if (!options?.nodeP) {
|
|
2832
|
+
return null;
|
|
2833
|
+
}
|
|
2834
|
+
if (func.runtime.render.is_tree_node(options.nodeP)) {
|
|
2835
|
+
return options.nodeP;
|
|
2836
|
+
}
|
|
2837
|
+
return await func.runtime.render.build_tree(options);
|
|
2838
|
+
};
|
|
2839
|
+
func.runtime.render.build_tree = async function (options) {
|
|
2840
|
+
if (Array.isArray(options?.nodeP)) {
|
|
2841
|
+
return await func.runtime.render.build_tree_list({
|
|
2842
|
+
...options,
|
|
2843
|
+
nodesP: options.nodeP,
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
const nodeP = options?.nodeP;
|
|
2848
|
+
if (!nodeP) {
|
|
2849
|
+
return null;
|
|
2850
|
+
}
|
|
2851
|
+
if (func.runtime.render.is_tree_node(nodeP)) {
|
|
2852
|
+
return nodeP;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
const pathP = Array.isArray(options?.pathP) ? options.pathP.slice() : [];
|
|
2856
|
+
const tree_path = pathP.length ? pathP.slice() : [0];
|
|
2857
|
+
const attributes = func.runtime.render.safe_clone_tree_value(nodeP.attributes || {});
|
|
2858
|
+
if (typeof nodeP.content !== 'undefined' && typeof attributes['xu-content'] === 'undefined') {
|
|
2859
|
+
attributes['xu-content'] = func.runtime.render.safe_clone_tree_value(nodeP.content);
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
const widget_capabilities = await func.runtime.render.get_tree_node_capabilities({
|
|
2863
|
+
SESSION_ID: options?.SESSION_ID,
|
|
2864
|
+
paramsP: options?.paramsP,
|
|
2865
|
+
attributes,
|
|
2866
|
+
});
|
|
2867
|
+
const children = [];
|
|
2868
|
+
const child_nodes = Array.isArray(nodeP.children) ? nodeP.children : [];
|
|
2869
|
+
const parent_tree_id = tree_path.join('.');
|
|
2870
|
+
|
|
2871
|
+
for (let index = 0; index < child_nodes.length; index++) {
|
|
2872
|
+
const child_tree = await func.runtime.render.build_tree({
|
|
2873
|
+
...options,
|
|
2874
|
+
nodeP: child_nodes[index],
|
|
2875
|
+
pathP: tree_path.concat(index),
|
|
2876
|
+
parent_tree_id: parent_tree_id,
|
|
2877
|
+
keyP: index,
|
|
2878
|
+
parent_nodeP: nodeP,
|
|
2879
|
+
});
|
|
2880
|
+
if (child_tree) {
|
|
2881
|
+
children.push(child_tree);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
const tree = {
|
|
2886
|
+
contract: func.runtime.render.TREE_CONTRACT_VERSION,
|
|
2887
|
+
id: func.runtime.render.get_tree_node_id(nodeP, tree_path),
|
|
2888
|
+
xu_tree_id: `tree.${tree_path.join('.')}`,
|
|
2889
|
+
kind: func.runtime.render.get_tree_node_kind(nodeP),
|
|
2890
|
+
tagName: nodeP.tagName || null,
|
|
2891
|
+
attributes,
|
|
2892
|
+
text: typeof nodeP.text !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.text) : null,
|
|
2893
|
+
content: typeof nodeP.content !== 'undefined' ? func.runtime.render.safe_clone_tree_value(nodeP.content) : null,
|
|
2894
|
+
children,
|
|
2895
|
+
meta: {
|
|
2896
|
+
tree_id: tree_path.join('.'),
|
|
2897
|
+
path: tree_path,
|
|
2898
|
+
parent_tree_id: options?.parent_tree_id || null,
|
|
2899
|
+
key: typeof options?.keyP === 'undefined' ? null : options.keyP,
|
|
2900
|
+
recordid: nodeP?.recordid || null,
|
|
2901
|
+
dependency_fields: func.runtime.render.safe_clone_tree_value(nodeP?.dependency_fields || null),
|
|
2902
|
+
iterate_info: func.runtime.render.safe_clone_tree_value(options?.parent_infoP?.iterate_info || nodeP?.iterate_info || null),
|
|
2903
|
+
controls: func.runtime.render.get_tree_controls(attributes),
|
|
2904
|
+
capabilities: widget_capabilities,
|
|
2905
|
+
widget: attributes['xu-widget']
|
|
2906
|
+
? {
|
|
2907
|
+
plugin_name: attributes['xu-widget'],
|
|
2908
|
+
method: attributes['xu-method'] || '_default',
|
|
2909
|
+
capabilities: widget_capabilities,
|
|
2910
|
+
}
|
|
2911
|
+
: null,
|
|
2912
|
+
source_node_id: nodeP?.id || nodeP?.id_org || null,
|
|
2913
|
+
source_node: nodeP,
|
|
2914
|
+
source_snapshot: func.runtime.ui?.get_node_snapshot
|
|
2915
|
+
? func.runtime.ui.get_node_snapshot(nodeP)
|
|
2916
|
+
: func.runtime.render.safe_clone_tree_value(nodeP),
|
|
2917
|
+
},
|
|
2918
|
+
};
|
|
2919
|
+
|
|
2920
|
+
return tree;
|
|
2921
|
+
};
|
|
2922
|
+
func.runtime.render.build_tree_list = async function (options) {
|
|
2923
|
+
const nodes = Array.isArray(options?.nodesP) ? options.nodesP : [];
|
|
2924
|
+
const trees = [];
|
|
2925
|
+
|
|
2926
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
2927
|
+
const tree = await func.runtime.render.build_tree({
|
|
2928
|
+
...options,
|
|
2929
|
+
nodeP: nodes[index],
|
|
2930
|
+
pathP: Array.isArray(options?.pathP) && options.pathP.length ? options.pathP.concat(index) : [index],
|
|
2931
|
+
keyP: index,
|
|
2932
|
+
});
|
|
2933
|
+
if (tree) {
|
|
2934
|
+
trees.push(tree);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
return trees;
|
|
2939
|
+
};
|
|
2940
|
+
func.runtime.render.sanitize_tree_for_debug = function (treeP) {
|
|
2941
|
+
if (Array.isArray(treeP)) {
|
|
2942
|
+
return treeP.map(function (child) {
|
|
2943
|
+
return func.runtime.render.sanitize_tree_for_debug(child);
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
if (!func.runtime.render.is_tree_node(treeP)) {
|
|
2948
|
+
return func.runtime.render.sort_tree_debug_value(func.runtime.render.safe_clone_tree_value(treeP));
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
return {
|
|
2952
|
+
contract: treeP.contract,
|
|
2953
|
+
id: treeP.id,
|
|
2954
|
+
xu_tree_id: treeP.xu_tree_id || null,
|
|
2955
|
+
kind: treeP.kind,
|
|
2956
|
+
tagName: treeP.tagName,
|
|
2957
|
+
attributes: func.runtime.render.sort_tree_debug_value(treeP.attributes || {}),
|
|
2958
|
+
text: treeP.text,
|
|
2959
|
+
content: treeP.content,
|
|
2960
|
+
children: treeP.children.map(function (child) {
|
|
2961
|
+
return func.runtime.render.sanitize_tree_for_debug(child);
|
|
2962
|
+
}),
|
|
2963
|
+
meta: {
|
|
2964
|
+
tree_id: treeP.meta?.tree_id || null,
|
|
2965
|
+
path: func.runtime.render.safe_clone_tree_value(treeP.meta?.path || []),
|
|
2966
|
+
parent_tree_id: treeP.meta?.parent_tree_id || null,
|
|
2967
|
+
key: typeof treeP.meta?.key === 'undefined' ? null : treeP.meta.key,
|
|
2968
|
+
recordid: treeP.meta?.recordid || null,
|
|
2969
|
+
dependency_fields: func.runtime.render.sort_tree_debug_value(treeP.meta?.dependency_fields || null),
|
|
2970
|
+
iterate_info: func.runtime.render.sort_tree_debug_value(treeP.meta?.iterate_info || null),
|
|
2971
|
+
controls: func.runtime.render.sort_tree_debug_value(treeP.meta?.controls || null),
|
|
2972
|
+
capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta?.capabilities || null),
|
|
2973
|
+
widget: treeP.meta?.widget
|
|
2974
|
+
? {
|
|
2975
|
+
plugin_name: treeP.meta.widget.plugin_name,
|
|
2976
|
+
method: treeP.meta.widget.method,
|
|
2977
|
+
capabilities: func.runtime.render.sort_tree_debug_value(treeP.meta.widget.capabilities || null),
|
|
2978
|
+
}
|
|
2979
|
+
: null,
|
|
2980
|
+
source_node_id: treeP.meta?.source_node_id || null,
|
|
2981
|
+
},
|
|
2982
|
+
};
|
|
2983
|
+
};
|
|
2984
|
+
func.runtime.render.serialize_tree = function (treeP, spacing = 2) {
|
|
2985
|
+
return JSON.stringify(func.runtime.render.sanitize_tree_for_debug(treeP), null, spacing);
|
|
2986
|
+
};
|
|
2987
|
+
func.runtime = func.runtime || {};
|
|
2988
|
+
func.runtime.ui = func.runtime.ui || {};
|
|
2989
|
+
func.runtime.render = func.runtime.render || {};
|
|
2990
|
+
func.runtime.widgets = func.runtime.widgets || {};
|
|
2991
|
+
|
|
2992
|
+
// Shared string-renderer helpers live here so headless/server runtimes can materialize the render tree without a DOM.
|
|
2993
|
+
|
|
2994
|
+
func.runtime.render.HTML_VOID_TAGS = func.runtime.render.HTML_VOID_TAGS || {
|
|
2995
|
+
area: true,
|
|
2996
|
+
base: true,
|
|
2997
|
+
br: true,
|
|
2998
|
+
col: true,
|
|
2999
|
+
embed: true,
|
|
3000
|
+
hr: true,
|
|
3001
|
+
img: true,
|
|
3002
|
+
input: true,
|
|
3003
|
+
link: true,
|
|
3004
|
+
meta: true,
|
|
3005
|
+
param: true,
|
|
3006
|
+
source: true,
|
|
3007
|
+
track: true,
|
|
3008
|
+
wbr: true,
|
|
3009
|
+
};
|
|
3010
|
+
func.runtime.render.escape_html = function (value) {
|
|
3011
|
+
return `${value ?? ''}`
|
|
3012
|
+
.replaceAll('&', '&')
|
|
3013
|
+
.replaceAll('<', '<')
|
|
3014
|
+
.replaceAll('>', '>')
|
|
3015
|
+
.replaceAll('"', '"')
|
|
3016
|
+
.replaceAll("'", ''');
|
|
3017
|
+
};
|
|
3018
|
+
func.runtime.render.escape_html_attribute = function (value) {
|
|
3019
|
+
return func.runtime.render.escape_html(value);
|
|
3020
|
+
};
|
|
3021
|
+
func.runtime.render.is_html_void_tag = function (tag_name) {
|
|
3022
|
+
return !!func.runtime.render.HTML_VOID_TAGS[(tag_name || '').toLowerCase()];
|
|
3023
|
+
};
|
|
3024
|
+
func.runtime.render.is_falsey_render_value = function (value) {
|
|
3025
|
+
if (value === false || value === null || typeof value === 'undefined') {
|
|
3026
|
+
return true;
|
|
3027
|
+
}
|
|
3028
|
+
if (typeof value === 'number') {
|
|
3029
|
+
return value === 0;
|
|
3030
|
+
}
|
|
3031
|
+
if (typeof value === 'string') {
|
|
3032
|
+
const normalized = value.trim().toLowerCase();
|
|
3033
|
+
return normalized === '' || normalized === 'false' || normalized === '0' || normalized === 'null' || normalized === 'undefined' || normalized === 'off' || normalized === 'no';
|
|
3034
|
+
}
|
|
3035
|
+
return false;
|
|
3036
|
+
};
|
|
3037
|
+
func.runtime.render.should_render_tree_node = function (treeP) {
|
|
3038
|
+
const controls = treeP?.meta?.controls || {};
|
|
3039
|
+
if (controls.xu_if !== null && controls.xu_if !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_if)) {
|
|
3040
|
+
return false;
|
|
3041
|
+
}
|
|
3042
|
+
if (controls.xu_render !== null && controls.xu_render !== undefined && func.runtime.render.is_falsey_render_value(controls.xu_render)) {
|
|
3043
|
+
return false;
|
|
3044
|
+
}
|
|
3045
|
+
return true;
|
|
3046
|
+
};
|
|
3047
|
+
func.runtime.render.is_tree_control_attribute = function (key) {
|
|
3048
|
+
if (!key) {
|
|
3049
|
+
return false;
|
|
3050
|
+
}
|
|
3051
|
+
return (
|
|
3052
|
+
key.startsWith('xu-exp:') ||
|
|
3053
|
+
key === 'xu-widget' ||
|
|
3054
|
+
key === 'xu-method' ||
|
|
3055
|
+
key === 'xu-for' ||
|
|
3056
|
+
key === 'xu-for-key' ||
|
|
3057
|
+
key === 'xu-for-val' ||
|
|
3058
|
+
key === 'xu-if' ||
|
|
3059
|
+
key === 'xu-render' ||
|
|
3060
|
+
key === 'xu-bind' ||
|
|
3061
|
+
key === 'xu-content' ||
|
|
3062
|
+
key === 'xu-text' ||
|
|
3063
|
+
key === 'xu-html' ||
|
|
3064
|
+
key === 'xu-show' ||
|
|
3065
|
+
key === 'xu-panel-program' ||
|
|
3066
|
+
key === 'xu-teleport'
|
|
3067
|
+
);
|
|
3068
|
+
};
|
|
3069
|
+
func.runtime.render.get_string_renderer_tag_name = function (treeP) {
|
|
3070
|
+
switch (treeP?.kind) {
|
|
3071
|
+
case 'widget':
|
|
3072
|
+
case 'single_view':
|
|
3073
|
+
case 'multi_view':
|
|
3074
|
+
case 'panel':
|
|
3075
|
+
case 'teleport':
|
|
3076
|
+
return 'div';
|
|
3077
|
+
case 'placeholder':
|
|
3078
|
+
return null;
|
|
3079
|
+
case 'text':
|
|
3080
|
+
return null;
|
|
3081
|
+
default:
|
|
3082
|
+
return treeP?.tagName || 'div';
|
|
3083
|
+
}
|
|
3084
|
+
};
|
|
3085
|
+
func.runtime.render.get_tree_terminal_content = function (treeP) {
|
|
3086
|
+
const attributes = treeP?.attributes || {};
|
|
3087
|
+
if (typeof attributes['xu-html'] !== 'undefined' && attributes['xu-html'] !== null) {
|
|
3088
|
+
return {
|
|
3089
|
+
value: `${attributes['xu-html']}`,
|
|
3090
|
+
mode: 'html',
|
|
3091
|
+
};
|
|
3092
|
+
}
|
|
3093
|
+
if (typeof attributes['xu-content'] !== 'undefined' && attributes['xu-content'] !== null) {
|
|
3094
|
+
return {
|
|
3095
|
+
value: `${attributes['xu-content']}`,
|
|
3096
|
+
mode: 'html',
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
if (typeof attributes['xu-text'] !== 'undefined' && attributes['xu-text'] !== null) {
|
|
3100
|
+
return {
|
|
3101
|
+
value: `${attributes['xu-text']}`,
|
|
3102
|
+
mode: 'text',
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
if (treeP?.kind === 'text') {
|
|
3106
|
+
return {
|
|
3107
|
+
value: typeof treeP?.text !== 'undefined' && treeP?.text !== null ? `${treeP.text}` : `${treeP?.content || ''}`,
|
|
3108
|
+
mode: 'text',
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
return null;
|
|
3112
|
+
};
|
|
3113
|
+
func.runtime.render.render_tree_terminal_content = function (treeP) {
|
|
3114
|
+
const terminal = func.runtime.render.get_tree_terminal_content(treeP);
|
|
3115
|
+
if (!terminal) {
|
|
3116
|
+
return null;
|
|
3117
|
+
}
|
|
3118
|
+
if (terminal.mode === 'html') {
|
|
3119
|
+
return terminal.value;
|
|
3120
|
+
}
|
|
3121
|
+
return func.runtime.render.escape_html(terminal.value);
|
|
3122
|
+
};
|
|
3123
|
+
func.runtime.render.get_widget_fallback_markup = function (treeP) {
|
|
3124
|
+
const widget_meta = treeP?.meta?.widget || {};
|
|
3125
|
+
const capability_state = widget_meta?.capabilities?.headless ? 'headless-capable' : 'browser-only';
|
|
3126
|
+
return `<!--xuda-widget:${func.runtime.render.escape_html(widget_meta.plugin_name || 'unknown')}:${capability_state}-->`;
|
|
3127
|
+
};
|
|
3128
|
+
func.runtime.render.get_tree_string_attributes = function (treeP, renderer_context) {
|
|
3129
|
+
const attributes = func.runtime.render.safe_clone_tree_value(treeP?.attributes || {});
|
|
3130
|
+
const attr_pairs = [];
|
|
3131
|
+
const keys = Object.keys(attributes);
|
|
3132
|
+
|
|
3133
|
+
for (let index = 0; index < keys.length; index++) {
|
|
3134
|
+
const key = keys[index];
|
|
3135
|
+
if (func.runtime.render.is_tree_control_attribute(key)) {
|
|
3136
|
+
continue;
|
|
3137
|
+
}
|
|
3138
|
+
const value = attributes[key];
|
|
3139
|
+
if (value === false || value === null || typeof value === 'undefined') {
|
|
3140
|
+
continue;
|
|
3141
|
+
}
|
|
3142
|
+
if (value === true) {
|
|
3143
|
+
attr_pairs.push(key);
|
|
3144
|
+
continue;
|
|
3145
|
+
}
|
|
3146
|
+
const normalized_value = typeof value === 'object' ? JSON.stringify(value) : `${value}`;
|
|
3147
|
+
attr_pairs.push(`${key}="${func.runtime.render.escape_html_attribute(normalized_value)}"`);
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
attr_pairs.push(`data-xuda-kind="${func.runtime.render.escape_html_attribute(treeP?.kind || 'element')}"`);
|
|
3151
|
+
attr_pairs.push(`data-xuda-node-id="${func.runtime.render.escape_html_attribute(treeP?.id || treeP?.meta?.source_node_id || '')}"`);
|
|
3152
|
+
attr_pairs.push(`data-xuda-tree-id="${func.runtime.render.escape_html_attribute(treeP?.meta?.tree_id || '')}"`);
|
|
3153
|
+
|
|
3154
|
+
if (treeP?.kind === 'widget' && treeP?.meta?.widget) {
|
|
3155
|
+
attr_pairs.push(`data-xuda-widget="${func.runtime.render.escape_html_attribute(treeP.meta.widget.plugin_name || '')}"`);
|
|
3156
|
+
attr_pairs.push(`data-xuda-widget-method="${func.runtime.render.escape_html_attribute(treeP.meta.widget.method || '_default')}"`);
|
|
3157
|
+
attr_pairs.push(`data-xuda-widget-capability="${func.runtime.render.escape_html_attribute(treeP.meta.widget.capabilities?.headless ? 'headless' : 'browser')}"`);
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
if (treeP?.kind === 'teleport' && treeP?.attributes?.['xu-teleport']) {
|
|
3161
|
+
attr_pairs.push(`data-xuda-teleport-target="${func.runtime.render.escape_html_attribute(treeP.attributes['xu-teleport'])}"`);
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
if ((treeP?.meta?.controls?.xu_for !== null && treeP?.meta?.controls?.xu_for !== undefined) && !renderer_context?.strip_iteration_markers) {
|
|
3165
|
+
attr_pairs.push('data-xuda-xu-for="pending"');
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
return attr_pairs.length ? ' ' + attr_pairs.join(' ') : '';
|
|
3169
|
+
};
|
|
3170
|
+
func.runtime.render.render_tree_children_to_string = async function (treeP, renderer_context) {
|
|
3171
|
+
if (!Array.isArray(treeP?.children) || !treeP.children.length) {
|
|
3172
|
+
return '';
|
|
3173
|
+
}
|
|
3174
|
+
let html = '';
|
|
3175
|
+
for (let index = 0; index < treeP.children.length; index++) {
|
|
3176
|
+
html += await func.runtime.render.render_tree_to_string(treeP.children[index], {
|
|
3177
|
+
...renderer_context,
|
|
3178
|
+
parent_tree: treeP,
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
return html;
|
|
3182
|
+
};
|
|
3183
|
+
func.runtime.render.render_tree_to_string = async function (treeP, renderer_context = {}) {
|
|
3184
|
+
if (!treeP) {
|
|
3185
|
+
return '';
|
|
3186
|
+
}
|
|
3187
|
+
if (Array.isArray(treeP)) {
|
|
3188
|
+
let html = '';
|
|
3189
|
+
for (let index = 0; index < treeP.length; index++) {
|
|
3190
|
+
html += await func.runtime.render.render_tree_to_string(treeP[index], renderer_context);
|
|
3191
|
+
}
|
|
3192
|
+
return html;
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
const ensured_tree = await func.runtime.render.ensure_tree_node({
|
|
3196
|
+
SESSION_ID: renderer_context?.SESSION_ID,
|
|
3197
|
+
nodeP: treeP,
|
|
3198
|
+
paramsP: renderer_context?.paramsP,
|
|
3199
|
+
parent_infoP: renderer_context?.parent_infoP,
|
|
3200
|
+
keyP: renderer_context?.keyP,
|
|
3201
|
+
parent_nodeP: renderer_context?.parent_nodeP,
|
|
3202
|
+
});
|
|
3203
|
+
|
|
3204
|
+
if (!ensured_tree || !func.runtime.render.should_render_tree_node(ensured_tree)) {
|
|
3205
|
+
return '';
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
if (ensured_tree.kind === 'placeholder') {
|
|
3209
|
+
if (renderer_context?.include_placeholders) {
|
|
3210
|
+
return `<!--xuda-placeholder:${func.runtime.render.escape_html(ensured_tree.id || '')}-->`;
|
|
3211
|
+
}
|
|
3212
|
+
return '';
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
if (ensured_tree.kind === 'text') {
|
|
3216
|
+
return func.runtime.render.render_tree_terminal_content(ensured_tree) || '';
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
const tag_name = func.runtime.render.get_string_renderer_tag_name(ensured_tree);
|
|
3220
|
+
if (!tag_name || tag_name.toLowerCase() === 'script') {
|
|
3221
|
+
return '';
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
const attributes = func.runtime.render.get_tree_string_attributes(ensured_tree, renderer_context);
|
|
3225
|
+
const terminal_content = func.runtime.render.render_tree_terminal_content(ensured_tree);
|
|
3226
|
+
let children_html = terminal_content !== null ? terminal_content : await func.runtime.render.render_tree_children_to_string(ensured_tree, renderer_context);
|
|
3227
|
+
|
|
3228
|
+
if (ensured_tree.kind === 'widget' && !children_html) {
|
|
3229
|
+
children_html = func.runtime.render.get_widget_fallback_markup(ensured_tree);
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
if (func.runtime.render.is_html_void_tag(tag_name)) {
|
|
3233
|
+
return `<${tag_name}${attributes}>`;
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
return `<${tag_name}${attributes}>${children_html}</${tag_name}>`;
|
|
3237
|
+
};
|
|
3238
|
+
func.runtime.render.render_to_string = async function (options = {}) {
|
|
3239
|
+
const treeP = await func.runtime.render.ensure_tree_node({
|
|
3240
|
+
SESSION_ID: options.SESSION_ID,
|
|
3241
|
+
nodeP: options.treeP || options.nodeP,
|
|
3242
|
+
paramsP: options.paramsP,
|
|
3243
|
+
parent_infoP: options.parent_infoP,
|
|
3244
|
+
keyP: options.keyP,
|
|
3245
|
+
parent_nodeP: options.parent_nodeP,
|
|
3246
|
+
});
|
|
3247
|
+
|
|
3248
|
+
return await func.runtime.render.render_tree_to_string(treeP, options);
|
|
3249
|
+
};
|
|
3250
|
+
func.runtime.render.get_server_render_mode = function (options = {}) {
|
|
3251
|
+
const normalized = func.runtime.render.normalize_runtime_bootstrap({
|
|
3252
|
+
app_computing_mode: options.app_computing_mode,
|
|
3253
|
+
app_render_mode: options.app_render_mode,
|
|
3254
|
+
app_client_activation: options.app_client_activation,
|
|
3255
|
+
});
|
|
3256
|
+
|
|
3257
|
+
return normalized;
|
|
3258
|
+
};
|
|
3259
|
+
func.runtime.render.build_server_render_params = async function (options = {}) {
|
|
3260
|
+
const SESSION_ID = options.SESSION_ID;
|
|
3261
|
+
const prog_id = options.prog_id;
|
|
3262
|
+
const dsSessionP = options.dsSessionP;
|
|
3263
|
+
const _session = SESSION_OBJ?.[SESSION_ID] || {};
|
|
3264
|
+
const _ds = _session?.DS_GLB?.[dsSessionP] || {};
|
|
3265
|
+
const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
|
|
3266
|
+
|
|
3267
|
+
if (!viewDoc?.properties) {
|
|
3268
|
+
throw new Error(`view document not found for ${prog_id}`);
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
const base_params = _ds?.screen_params ? func.runtime.render.safe_clone_tree_value(_ds.screen_params) : {};
|
|
3272
|
+
const screenId = options.screenId || base_params.screenId || `ssr_${prog_id}_${dsSessionP || '0'}`;
|
|
3273
|
+
const paramsP = {
|
|
3274
|
+
...base_params,
|
|
3275
|
+
prog_id,
|
|
3276
|
+
sourceScreenP: null,
|
|
3277
|
+
$callingContainerP: null,
|
|
3278
|
+
triggerIdP: null,
|
|
3279
|
+
callingDataSource_objP: _ds,
|
|
3280
|
+
rowIdP: typeof options.rowIdP !== 'undefined' ? options.rowIdP : (_ds?.currentRecordId || null),
|
|
3281
|
+
renderType: viewDoc.properties?.renderType,
|
|
3282
|
+
parameters_obj_inP: options.parameters_obj_inP || base_params.parameters_obj_inP || options.parameters_raw_obj || {},
|
|
3283
|
+
source_functionP: options.source_functionP || base_params.source_functionP || 'render_string',
|
|
3284
|
+
is_panelP: false,
|
|
3285
|
+
screen_type: options.screen_type || base_params.screen_type || 'render_string',
|
|
3286
|
+
screenInfo: viewDoc,
|
|
3287
|
+
call_screen_propertiesP: base_params.call_screen_propertiesP,
|
|
3288
|
+
parentDataSourceNoP: typeof _ds?.parentDataSourceNo === 'undefined' || _ds?.parentDataSourceNo === null ? 0 : _ds.parentDataSourceNo,
|
|
3289
|
+
parameters_raw_obj: options.parameters_raw_obj || base_params.parameters_raw_obj || {},
|
|
3290
|
+
dsSessionP,
|
|
3291
|
+
screenId,
|
|
3292
|
+
containerIdP: base_params.containerIdP || `ssr_container_${screenId}`,
|
|
3293
|
+
};
|
|
3294
|
+
|
|
3295
|
+
if (_ds) {
|
|
3296
|
+
_ds.screen_params = paramsP;
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
return paramsP;
|
|
3300
|
+
};
|
|
3301
|
+
func.runtime.render.build_prog_tree = async function (options = {}) {
|
|
3302
|
+
const SESSION_ID = options.SESSION_ID;
|
|
3303
|
+
const prog_id = options.prog_id;
|
|
3304
|
+
const viewDoc = options.viewDoc || (await func.utils?.VIEWS_OBJ?.get?.(SESSION_ID, prog_id));
|
|
3305
|
+
|
|
3306
|
+
if (!viewDoc?.progUi?.length) {
|
|
3307
|
+
throw new Error(`progUi not found for ${prog_id}`);
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
const paramsP = options.paramsP || (await func.runtime.render.build_server_render_params({
|
|
3311
|
+
...options,
|
|
3312
|
+
SESSION_ID,
|
|
3313
|
+
prog_id,
|
|
3314
|
+
viewDoc,
|
|
3315
|
+
}));
|
|
3316
|
+
const root_index = typeof options.root_index === 'number' ? options.root_index : 0;
|
|
3317
|
+
const root_node = func.runtime.render.safe_clone_tree_value(viewDoc.progUi[root_index]);
|
|
3318
|
+
const tree = await func.runtime.render.build_tree({
|
|
3319
|
+
SESSION_ID,
|
|
3320
|
+
nodeP: root_node,
|
|
3321
|
+
paramsP,
|
|
3322
|
+
});
|
|
3323
|
+
|
|
3324
|
+
return {
|
|
3325
|
+
tree,
|
|
3326
|
+
paramsP,
|
|
3327
|
+
viewDoc,
|
|
3328
|
+
};
|
|
3329
|
+
};
|
|
3330
|
+
func.runtime.render.build_ssr_payload = function (render_program, options = {}) {
|
|
3331
|
+
const runtime_profile = func.runtime.render.get_server_render_mode(options);
|
|
3332
|
+
return {
|
|
3333
|
+
contract: 'xuda.ssr.v1',
|
|
3334
|
+
prog_id: options.prog_id,
|
|
3335
|
+
screenId: render_program.paramsP.screenId,
|
|
3336
|
+
containerId: render_program.paramsP.containerIdP,
|
|
3337
|
+
app_computing_mode: runtime_profile.app_computing_mode,
|
|
3338
|
+
app_render_mode: runtime_profile.app_render_mode,
|
|
3339
|
+
app_client_activation: runtime_profile.app_client_activation,
|
|
3340
|
+
tree_contract: func.runtime.render.TREE_CONTRACT_VERSION,
|
|
3341
|
+
};
|
|
3342
|
+
};
|
|
3343
|
+
func.runtime.render.build_ssr_screen_html = function (html, render_program, options = {}) {
|
|
3344
|
+
const payload = func.runtime.render.build_ssr_payload(render_program, options);
|
|
3345
|
+
const screenId = func.runtime.render.escape_html_attribute(payload.screenId || '');
|
|
3346
|
+
const containerId = func.runtime.render.escape_html_attribute(payload.containerId || '');
|
|
3347
|
+
const activation = func.runtime.render.escape_html_attribute(payload.app_client_activation || 'takeover');
|
|
3348
|
+
|
|
3349
|
+
return `<div data-xuda-ssr-embed="true" class="xu_embed_div"><div id="${screenId}" class="xu_embed_container" data-xuda-ssr-screen="true" data-xuda-ssr-screen-id="${screenId}" data-xuda-activation="${activation}" style="display: contents;"><div id="${containerId}" data-xuda-ssr-root-frame="true" data-xuda-ssr-screen-id="${screenId}" data-xuda-activation="${activation}" style="display: contents;">${html}</div></div></div>`;
|
|
3350
|
+
};
|
|
3351
|
+
func.runtime.render.render_prog_to_string = async function (options = {}) {
|
|
3352
|
+
const render_program = await func.runtime.render.build_prog_tree(options);
|
|
3353
|
+
const html = await func.runtime.render.render_to_string({
|
|
3354
|
+
...options,
|
|
3355
|
+
SESSION_ID: options.SESSION_ID,
|
|
3356
|
+
treeP: render_program.tree,
|
|
3357
|
+
paramsP: render_program.paramsP,
|
|
3358
|
+
});
|
|
3359
|
+
const ssr_payload = func.runtime.render.build_ssr_payload(render_program, options);
|
|
3360
|
+
const screen_html = func.runtime.render.build_ssr_screen_html(html, render_program, options);
|
|
3361
|
+
|
|
3362
|
+
return {
|
|
3363
|
+
prog_id: options.prog_id,
|
|
3364
|
+
dsSessionP: render_program.paramsP.dsSessionP,
|
|
3365
|
+
screenId: render_program.paramsP.screenId,
|
|
3366
|
+
html,
|
|
3367
|
+
screen_html,
|
|
3368
|
+
tree_json: func.runtime.render.serialize_tree(render_program.tree),
|
|
3369
|
+
paramsP: render_program.paramsP,
|
|
3370
|
+
ssr_payload,
|
|
3371
|
+
};
|
|
3372
|
+
};
|
|
2356
3373
|
glb.DEBUG_INFO_OBJ = {};
|
|
2357
3374
|
// var CONNECTION_ATTEMPTS = 0;
|
|
2358
3375
|
glb.APP_INFO = {};
|
|
@@ -6060,6 +7077,13 @@ func.utils.DOCS_OBJ = {};
|
|
|
6060
7077
|
func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
|
|
6061
7078
|
if (!idP || idP === '0') return;
|
|
6062
7079
|
|
|
7080
|
+
const normalize_runtime_doc = function (doc) {
|
|
7081
|
+
if (!doc || !func.runtime.program?.normalize_doc_for_runtime) {
|
|
7082
|
+
return doc;
|
|
7083
|
+
}
|
|
7084
|
+
return func.runtime.program.normalize_doc_for_runtime(doc);
|
|
7085
|
+
};
|
|
7086
|
+
|
|
6063
7087
|
var _session = SESSION_OBJ[SESSION_ID];
|
|
6064
7088
|
const _app_id = _session.app_id;
|
|
6065
7089
|
if (!DOCS_OBJ[_app_id]) {
|
|
@@ -6081,7 +7105,8 @@ func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
|
|
|
6081
7105
|
}
|
|
6082
7106
|
let val = _session.project_data?.programs?.[idP];
|
|
6083
7107
|
if (val) {
|
|
6084
|
-
|
|
7108
|
+
DOCS_OBJ[_app_id][idP] = normalize_runtime_doc(val);
|
|
7109
|
+
return DOCS_OBJ[_app_id][idP];
|
|
6085
7110
|
}
|
|
6086
7111
|
}
|
|
6087
7112
|
|
|
@@ -6089,7 +7114,7 @@ func.utils.DOCS_OBJ.get = async function (SESSION_ID, idP) {
|
|
|
6089
7114
|
const module = await func.common.get_module(SESSION_ID, `xuda-progs-loader-module.mjs`);
|
|
6090
7115
|
|
|
6091
7116
|
if (idP !== 'system') {
|
|
6092
|
-
DOCS_OBJ[_app_id][idP] = await module.DOCS_OBJ_get(SESSION_ID, idP);
|
|
7117
|
+
DOCS_OBJ[_app_id][idP] = normalize_runtime_doc(await module.DOCS_OBJ_get(SESSION_ID, idP));
|
|
6093
7118
|
if (DOCS_OBJ[_app_id][idP] && xu_isEmpty(DOCS_OBJ[_app_id][idP])) {
|
|
6094
7119
|
await func.utils.remove_cached_objects(SESSION_ID);
|
|
6095
7120
|
|