@aigne/afs-ui 1.11.0-beta.12
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/LICENSE.md +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/aup-protocol.cjs +235 -0
- package/dist/aup-protocol.d.cts +78 -0
- package/dist/aup-protocol.d.cts.map +1 -0
- package/dist/aup-protocol.d.mts +78 -0
- package/dist/aup-protocol.d.mts.map +1 -0
- package/dist/aup-protocol.mjs +235 -0
- package/dist/aup-protocol.mjs.map +1 -0
- package/dist/aup-registry.cjs +2489 -0
- package/dist/aup-registry.mjs +2487 -0
- package/dist/aup-registry.mjs.map +1 -0
- package/dist/aup-spec.cjs +1467 -0
- package/dist/aup-spec.mjs +1466 -0
- package/dist/aup-spec.mjs.map +1 -0
- package/dist/aup-types.cjs +165 -0
- package/dist/aup-types.d.cts +157 -0
- package/dist/aup-types.d.cts.map +1 -0
- package/dist/aup-types.d.mts +157 -0
- package/dist/aup-types.d.mts.map +1 -0
- package/dist/aup-types.mjs +157 -0
- package/dist/aup-types.mjs.map +1 -0
- package/dist/backend.cjs +14 -0
- package/dist/backend.d.cts +104 -0
- package/dist/backend.d.cts.map +1 -0
- package/dist/backend.d.mts +104 -0
- package/dist/backend.d.mts.map +1 -0
- package/dist/backend.mjs +13 -0
- package/dist/backend.mjs.map +1 -0
- package/dist/degradation.cjs +85 -0
- package/dist/degradation.d.cts +17 -0
- package/dist/degradation.d.cts.map +1 -0
- package/dist/degradation.d.mts +17 -0
- package/dist/degradation.d.mts.map +1 -0
- package/dist/degradation.mjs +84 -0
- package/dist/degradation.mjs.map +1 -0
- package/dist/index.cjs +36 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.mjs +13 -0
- package/dist/runtime.cjs +117 -0
- package/dist/runtime.d.cts +59 -0
- package/dist/runtime.d.cts.map +1 -0
- package/dist/runtime.d.mts +59 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +118 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/session.cjs +159 -0
- package/dist/session.d.cts +80 -0
- package/dist/session.d.cts.map +1 -0
- package/dist/session.d.mts +80 -0
- package/dist/session.d.mts.map +1 -0
- package/dist/session.mjs +159 -0
- package/dist/session.mjs.map +1 -0
- package/dist/snapshot.cjs +162 -0
- package/dist/snapshot.mjs +163 -0
- package/dist/snapshot.mjs.map +1 -0
- package/dist/term-page.cjs +264 -0
- package/dist/term-page.mjs +264 -0
- package/dist/term-page.mjs.map +1 -0
- package/dist/term.cjs +295 -0
- package/dist/term.d.cts +84 -0
- package/dist/term.d.cts.map +1 -0
- package/dist/term.d.mts +84 -0
- package/dist/term.d.mts.map +1 -0
- package/dist/term.mjs +296 -0
- package/dist/term.mjs.map +1 -0
- package/dist/tty.cjs +136 -0
- package/dist/tty.d.cts +53 -0
- package/dist/tty.d.cts.map +1 -0
- package/dist/tty.d.mts +53 -0
- package/dist/tty.d.mts.map +1 -0
- package/dist/tty.mjs +135 -0
- package/dist/tty.mjs.map +1 -0
- package/dist/ui-provider.cjs +4615 -0
- package/dist/ui-provider.d.cts +307 -0
- package/dist/ui-provider.d.cts.map +1 -0
- package/dist/ui-provider.d.mts +307 -0
- package/dist/ui-provider.d.mts.map +1 -0
- package/dist/ui-provider.mjs +4616 -0
- package/dist/ui-provider.mjs.map +1 -0
- package/dist/web-page/core.cjs +1388 -0
- package/dist/web-page/core.mjs +1387 -0
- package/dist/web-page/core.mjs.map +1 -0
- package/dist/web-page/css.cjs +1699 -0
- package/dist/web-page/css.mjs +1698 -0
- package/dist/web-page/css.mjs.map +1 -0
- package/dist/web-page/icons.cjs +248 -0
- package/dist/web-page/icons.mjs +248 -0
- package/dist/web-page/icons.mjs.map +1 -0
- package/dist/web-page/overlay-themes.cjs +514 -0
- package/dist/web-page/overlay-themes.mjs +513 -0
- package/dist/web-page/overlay-themes.mjs.map +1 -0
- package/dist/web-page/renderers/action.cjs +72 -0
- package/dist/web-page/renderers/action.mjs +72 -0
- package/dist/web-page/renderers/action.mjs.map +1 -0
- package/dist/web-page/renderers/broadcast.cjs +160 -0
- package/dist/web-page/renderers/broadcast.mjs +160 -0
- package/dist/web-page/renderers/broadcast.mjs.map +1 -0
- package/dist/web-page/renderers/calendar.cjs +137 -0
- package/dist/web-page/renderers/calendar.mjs +137 -0
- package/dist/web-page/renderers/calendar.mjs.map +1 -0
- package/dist/web-page/renderers/canvas.cjs +173 -0
- package/dist/web-page/renderers/canvas.mjs +173 -0
- package/dist/web-page/renderers/canvas.mjs.map +1 -0
- package/dist/web-page/renderers/cdn-loader.cjs +25 -0
- package/dist/web-page/renderers/cdn-loader.mjs +25 -0
- package/dist/web-page/renderers/cdn-loader.mjs.map +1 -0
- package/dist/web-page/renderers/chart.cjs +101 -0
- package/dist/web-page/renderers/chart.mjs +101 -0
- package/dist/web-page/renderers/chart.mjs.map +1 -0
- package/dist/web-page/renderers/deck.cjs +390 -0
- package/dist/web-page/renderers/deck.mjs +390 -0
- package/dist/web-page/renderers/deck.mjs.map +1 -0
- package/dist/web-page/renderers/device.cjs +1015 -0
- package/dist/web-page/renderers/device.mjs +1015 -0
- package/dist/web-page/renderers/device.mjs.map +1 -0
- package/dist/web-page/renderers/editor.cjs +127 -0
- package/dist/web-page/renderers/editor.mjs +127 -0
- package/dist/web-page/renderers/editor.mjs.map +1 -0
- package/dist/web-page/renderers/finance-chart.cjs +178 -0
- package/dist/web-page/renderers/finance-chart.mjs +178 -0
- package/dist/web-page/renderers/finance-chart.mjs.map +1 -0
- package/dist/web-page/renderers/frame.cjs +274 -0
- package/dist/web-page/renderers/frame.mjs +274 -0
- package/dist/web-page/renderers/frame.mjs.map +1 -0
- package/dist/web-page/renderers/globe.cjs +119 -0
- package/dist/web-page/renderers/globe.mjs +119 -0
- package/dist/web-page/renderers/globe.mjs.map +1 -0
- package/dist/web-page/renderers/input.cjs +137 -0
- package/dist/web-page/renderers/input.mjs +137 -0
- package/dist/web-page/renderers/input.mjs.map +1 -0
- package/dist/web-page/renderers/list.cjs +1243 -0
- package/dist/web-page/renderers/list.mjs +1243 -0
- package/dist/web-page/renderers/list.mjs.map +1 -0
- package/dist/web-page/renderers/map.cjs +126 -0
- package/dist/web-page/renderers/map.mjs +126 -0
- package/dist/web-page/renderers/map.mjs.map +1 -0
- package/dist/web-page/renderers/media.cjs +106 -0
- package/dist/web-page/renderers/media.mjs +106 -0
- package/dist/web-page/renderers/media.mjs.map +1 -0
- package/dist/web-page/renderers/moonphase.cjs +105 -0
- package/dist/web-page/renderers/moonphase.mjs +105 -0
- package/dist/web-page/renderers/moonphase.mjs.map +1 -0
- package/dist/web-page/renderers/natal-chart.cjs +222 -0
- package/dist/web-page/renderers/natal-chart.mjs +222 -0
- package/dist/web-page/renderers/natal-chart.mjs.map +1 -0
- package/dist/web-page/renderers/overlay.cjs +531 -0
- package/dist/web-page/renderers/overlay.mjs +531 -0
- package/dist/web-page/renderers/overlay.mjs.map +1 -0
- package/dist/web-page/renderers/table.cjs +74 -0
- package/dist/web-page/renderers/table.mjs +74 -0
- package/dist/web-page/renderers/table.mjs.map +1 -0
- package/dist/web-page/renderers/terminal.cjs +30 -0
- package/dist/web-page/renderers/terminal.mjs +30 -0
- package/dist/web-page/renderers/terminal.mjs.map +1 -0
- package/dist/web-page/renderers/text.cjs +109 -0
- package/dist/web-page/renderers/text.mjs +109 -0
- package/dist/web-page/renderers/text.mjs.map +1 -0
- package/dist/web-page/renderers/ticker.cjs +133 -0
- package/dist/web-page/renderers/ticker.mjs +133 -0
- package/dist/web-page/renderers/ticker.mjs.map +1 -0
- package/dist/web-page/renderers/time.cjs +69 -0
- package/dist/web-page/renderers/time.mjs +69 -0
- package/dist/web-page/renderers/time.mjs.map +1 -0
- package/dist/web-page/renderers/unknown.cjs +20 -0
- package/dist/web-page/renderers/unknown.mjs +20 -0
- package/dist/web-page/renderers/unknown.mjs.map +1 -0
- package/dist/web-page/renderers/view.cjs +161 -0
- package/dist/web-page/renderers/view.mjs +161 -0
- package/dist/web-page/renderers/view.mjs.map +1 -0
- package/dist/web-page/renderers/wm.cjs +669 -0
- package/dist/web-page/renderers/wm.mjs +669 -0
- package/dist/web-page/renderers/wm.mjs.map +1 -0
- package/dist/web-page/skeleton.cjs +103 -0
- package/dist/web-page/skeleton.mjs +103 -0
- package/dist/web-page/skeleton.mjs.map +1 -0
- package/dist/web-page.cjs +114 -0
- package/dist/web-page.d.cts +19 -0
- package/dist/web-page.d.cts.map +1 -0
- package/dist/web-page.d.mts +19 -0
- package/dist/web-page.d.mts.map +1 -0
- package/dist/web-page.mjs +115 -0
- package/dist/web-page.mjs.map +1 -0
- package/dist/web.cjs +827 -0
- package/dist/web.d.cts +144 -0
- package/dist/web.d.cts.map +1 -0
- package/dist/web.d.mts +144 -0
- package/dist/web.d.mts.map +1 -0
- package/dist/web.mjs +828 -0
- package/dist/web.mjs.map +1 -0
- package/dist/wm-state.cjs +172 -0
- package/dist/wm-state.mjs +171 -0
- package/dist/wm-state.mjs.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
//#region src/web-page/renderers/frame.ts
|
|
2
|
+
const FRAME_JS = `
|
|
3
|
+
// ── Frame Renderer (sandboxed iframe for page isolation) ──
|
|
4
|
+
|
|
5
|
+
// Track known iframe contentWindows for origin validation
|
|
6
|
+
var _aupFrameWindows = new Set();
|
|
7
|
+
var _aupBridgeWindows = new Set();
|
|
8
|
+
var _aupBridgeOriginByWindow = new Map();
|
|
9
|
+
|
|
10
|
+
function _isHttpUrl(src) {
|
|
11
|
+
try {
|
|
12
|
+
var u = new URL(src, location.href);
|
|
13
|
+
return u.protocol === "http:" || u.protocol === "https:";
|
|
14
|
+
} catch (_ex) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function _isSameOriginUrl(src) {
|
|
20
|
+
try {
|
|
21
|
+
return new URL(src, location.href).origin === location.origin;
|
|
22
|
+
} catch (_ex) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function _resolveFrameSrc(rawSrc, bridgeRequested) {
|
|
28
|
+
var src = typeof rawSrc === "string" ? rawSrc.trim() : "";
|
|
29
|
+
if (!src) return null;
|
|
30
|
+
|
|
31
|
+
if (src.indexOf("/pages/") === 0) {
|
|
32
|
+
var pageId = src.replace(/^\\/pages\\//, "");
|
|
33
|
+
if (!pageId) return null;
|
|
34
|
+
var query = [];
|
|
35
|
+
if (_afsSessionId) query.push("sid=" + encodeURIComponent(_afsSessionId));
|
|
36
|
+
if (_afsSessionToken) query.push("st=" + encodeURIComponent(_afsSessionToken));
|
|
37
|
+
if (bridgeRequested) query.push("bridge=1");
|
|
38
|
+
var localUrl = location.origin + "/p/" + encodeURIComponent(pageId);
|
|
39
|
+
if (query.length) localUrl += "?" + query.join("&");
|
|
40
|
+
return { url: localUrl, bridgeEnabled: !!bridgeRequested };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (_isHttpUrl(src)) {
|
|
44
|
+
return {
|
|
45
|
+
url: src,
|
|
46
|
+
bridgeEnabled: !!bridgeRequested && _isSameOriginUrl(src),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function _normalizeSandbox(rawSandbox, bridgeEnabled) {
|
|
54
|
+
var base = ["allow-scripts", "allow-forms", "allow-popups"];
|
|
55
|
+
var allowed = {
|
|
56
|
+
"allow-downloads": 1,
|
|
57
|
+
"allow-forms": 1,
|
|
58
|
+
"allow-modals": 1,
|
|
59
|
+
"allow-orientation-lock": 1,
|
|
60
|
+
"allow-pointer-lock": 1,
|
|
61
|
+
"allow-popups": 1,
|
|
62
|
+
"allow-popups-to-escape-sandbox": 1,
|
|
63
|
+
"allow-presentation": 1,
|
|
64
|
+
"allow-same-origin": 1,
|
|
65
|
+
"allow-scripts": 1,
|
|
66
|
+
"allow-storage-access-by-user-activation": 1,
|
|
67
|
+
"allow-top-navigation": 1,
|
|
68
|
+
"allow-top-navigation-by-user-activation": 1,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
var seen = {};
|
|
72
|
+
var out = [];
|
|
73
|
+
var tokens = String(rawSandbox || "").split(/\\s+/);
|
|
74
|
+
for (var i = 0; i < tokens.length; i++) {
|
|
75
|
+
var token = tokens[i];
|
|
76
|
+
if (!token || !allowed[token] || seen[token]) continue;
|
|
77
|
+
seen[token] = 1;
|
|
78
|
+
out.push(token);
|
|
79
|
+
}
|
|
80
|
+
for (var j = 0; j < base.length; j++) {
|
|
81
|
+
if (!seen[base[j]]) out.push(base[j]);
|
|
82
|
+
}
|
|
83
|
+
if (!bridgeEnabled) {
|
|
84
|
+
out = out.filter(function(token) { return token !== "allow-same-origin"; });
|
|
85
|
+
}
|
|
86
|
+
return out.join(" ");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function _unregisterFrameWindow(iframe) {
|
|
90
|
+
var prev = iframe && iframe._aupTrackedWindow;
|
|
91
|
+
if (!prev) return;
|
|
92
|
+
_aupFrameWindows.delete(prev);
|
|
93
|
+
_aupBridgeWindows.delete(prev);
|
|
94
|
+
_aupBridgeOriginByWindow.delete(prev);
|
|
95
|
+
// Clean up bridge subscriptions owned by this window
|
|
96
|
+
if (window._aupBridgeSubsByWindow) {
|
|
97
|
+
var subIds = window._aupBridgeSubsByWindow.get(prev);
|
|
98
|
+
if (subIds) {
|
|
99
|
+
subIds.forEach(function(sid) {
|
|
100
|
+
if (window._aupBridgeSubs && window._aupBridgeSubs[sid]) {
|
|
101
|
+
try { window._aupBridgeSubs[sid](); } catch(_ex) {}
|
|
102
|
+
delete window._aupBridgeSubs[sid];
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
window._aupBridgeSubsByWindow.delete(prev);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
iframe._aupTrackedWindow = null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function _registerFrameWindow(iframe, bridgeEnabled, srcUrl) {
|
|
112
|
+
_unregisterFrameWindow(iframe);
|
|
113
|
+
try {
|
|
114
|
+
var win = iframe.contentWindow;
|
|
115
|
+
if (!win) return;
|
|
116
|
+
iframe._aupTrackedWindow = win;
|
|
117
|
+
_aupFrameWindows.add(win);
|
|
118
|
+
if (bridgeEnabled) {
|
|
119
|
+
_aupBridgeWindows.add(win);
|
|
120
|
+
try {
|
|
121
|
+
_aupBridgeOriginByWindow.set(win, new URL(srcUrl, location.href).origin);
|
|
122
|
+
} catch (_ex) {}
|
|
123
|
+
}
|
|
124
|
+
} catch (_ex) {}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function renderAupFrame(node) {
|
|
128
|
+
var el = document.createElement("div");
|
|
129
|
+
el.className = "aup-frame";
|
|
130
|
+
var p = node.props || {};
|
|
131
|
+
var src = typeof p.src === "string" ? p.src : "";
|
|
132
|
+
var bridge = !!p.bridge;
|
|
133
|
+
var loading = p.loading || "lazy";
|
|
134
|
+
var size = p.size || {};
|
|
135
|
+
var fallback = p.fallback || "";
|
|
136
|
+
|
|
137
|
+
if (size.width) el.setAttribute("data-size-width", "1");
|
|
138
|
+
if (size.height) el.setAttribute("data-size-height", "1");
|
|
139
|
+
|
|
140
|
+
if (!src) {
|
|
141
|
+
var placeholder = document.createElement("div");
|
|
142
|
+
placeholder.className = "aup-frame-error";
|
|
143
|
+
placeholder.innerHTML = '<span class="aup-frame-error-icon">\\u26a0</span>'
|
|
144
|
+
+ '<span class="aup-frame-error-msg">Frame: set src prop to embed a page</span>';
|
|
145
|
+
el.appendChild(placeholder);
|
|
146
|
+
return el;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var resolved = _resolveFrameSrc(src, bridge);
|
|
150
|
+
if (!resolved) {
|
|
151
|
+
var invalid = document.createElement("div");
|
|
152
|
+
invalid.className = "aup-frame-error";
|
|
153
|
+
invalid.innerHTML = '<span class="aup-frame-error-icon">\\u26a0</span>'
|
|
154
|
+
+ '<span class="aup-frame-error-msg">Frame: src must be http(s) URL or /pages/*</span>';
|
|
155
|
+
el.appendChild(invalid);
|
|
156
|
+
return el;
|
|
157
|
+
}
|
|
158
|
+
var resolvedSrc = resolved.url;
|
|
159
|
+
var bridgeEnabled = !!resolved.bridgeEnabled;
|
|
160
|
+
var sandbox = _normalizeSandbox(p.sandbox, bridgeEnabled);
|
|
161
|
+
|
|
162
|
+
// Create iframe
|
|
163
|
+
var iframe = document.createElement("iframe");
|
|
164
|
+
iframe.setAttribute("sandbox", sandbox);
|
|
165
|
+
iframe.setAttribute("loading", loading === "eager" ? "eager" : "lazy");
|
|
166
|
+
iframe.setAttribute("referrerpolicy", "no-referrer");
|
|
167
|
+
if (p.transparent) { iframe.setAttribute("allowtransparency", "true"); iframe.style.background = "transparent"; el.style.background = "transparent"; }
|
|
168
|
+
if (p.overlay) {
|
|
169
|
+
// Override .aup-frame class styles that break overlay
|
|
170
|
+
el.className = "aup-frame-overlay";
|
|
171
|
+
el.style.cssText = "position:fixed;inset:0;z-index:99999;pointer-events:none;background:transparent;border:none;overflow:visible;margin:0;padding:0;min-height:0;";
|
|
172
|
+
iframe.style.cssText = "pointer-events:none;background:transparent;width:100%;height:100%;border:none;display:block;min-height:0;";
|
|
173
|
+
iframe.setAttribute("allowtransparency", "true");
|
|
174
|
+
el.setAttribute("data-aup-overlay", node.id || "");
|
|
175
|
+
iframe.onload = function() {
|
|
176
|
+
_registerFrameWindow(iframe, bridgeEnabled, resolvedSrc);
|
|
177
|
+
};
|
|
178
|
+
iframe.src = resolvedSrc;
|
|
179
|
+
el.appendChild(iframe);
|
|
180
|
+
setTimeout(function() { document.body.appendChild(el); }, 0);
|
|
181
|
+
return document.createElement("div");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Loading skeleton (non-overlay frames only)
|
|
185
|
+
var skeleton = document.createElement("div");
|
|
186
|
+
skeleton.className = "aup-frame-loading";
|
|
187
|
+
skeleton.innerHTML = '<div class="aup-frame-loading-bar"></div>';
|
|
188
|
+
el.appendChild(skeleton);
|
|
189
|
+
if (size.width) iframe.style.width = size.width;
|
|
190
|
+
if (size.height) { iframe.style.height = size.height; el.style.minHeight = "0"; }
|
|
191
|
+
|
|
192
|
+
iframe.onload = function() {
|
|
193
|
+
skeleton.remove();
|
|
194
|
+
_registerFrameWindow(iframe, bridgeEnabled, resolvedSrc);
|
|
195
|
+
// Fire AUP load event
|
|
196
|
+
if (node.events && node.events.load) {
|
|
197
|
+
_fireAupEvent(node.id, "load", {});
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
iframe.onerror = function() {
|
|
202
|
+
_showFrameError(el, skeleton, iframe, resolvedSrc, fallback, bridge, node);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// Also handle load errors via timeout — iframes don't reliably fire onerror
|
|
206
|
+
var errorTimer = setTimeout(function() {
|
|
207
|
+
if (skeleton.parentNode) {
|
|
208
|
+
// Still loading after 30s — likely broken
|
|
209
|
+
_showFrameError(el, skeleton, iframe, resolvedSrc, fallback, bridge, node);
|
|
210
|
+
}
|
|
211
|
+
}, 30000);
|
|
212
|
+
|
|
213
|
+
iframe.addEventListener("load", function() { clearTimeout(errorTimer); }, { once: true });
|
|
214
|
+
|
|
215
|
+
iframe.src = resolvedSrc;
|
|
216
|
+
el.appendChild(iframe);
|
|
217
|
+
|
|
218
|
+
return el;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function _showFrameError(wrapper, skeleton, iframe, src, fallback, bridge, node) {
|
|
222
|
+
if (skeleton.parentNode) skeleton.remove();
|
|
223
|
+
iframe.style.display = "none";
|
|
224
|
+
_unregisterFrameWindow(iframe);
|
|
225
|
+
|
|
226
|
+
// Remove existing error if retrying
|
|
227
|
+
var old = wrapper.querySelector(".aup-frame-error");
|
|
228
|
+
if (old) old.remove();
|
|
229
|
+
|
|
230
|
+
var errorEl = document.createElement("div");
|
|
231
|
+
errorEl.className = "aup-frame-error";
|
|
232
|
+
errorEl.innerHTML = '<span class="aup-frame-error-icon">\\u26a0</span>'
|
|
233
|
+
+ '<span class="aup-frame-error-msg">Failed to load page</span>';
|
|
234
|
+
|
|
235
|
+
var retryBtn = document.createElement("button");
|
|
236
|
+
retryBtn.className = "aup-frame-retry";
|
|
237
|
+
retryBtn.textContent = "Retry";
|
|
238
|
+
retryBtn.onclick = function() {
|
|
239
|
+
errorEl.remove();
|
|
240
|
+
iframe.style.display = "";
|
|
241
|
+
var retry = _resolveFrameSrc(fallback || src, bridge);
|
|
242
|
+
iframe.src = retry ? retry.url : src;
|
|
243
|
+
};
|
|
244
|
+
errorEl.appendChild(retryBtn);
|
|
245
|
+
wrapper.appendChild(errorEl);
|
|
246
|
+
|
|
247
|
+
// Fire AUP error event
|
|
248
|
+
if (node.events && node.events.error) {
|
|
249
|
+
_fireAupEvent(node.id, "error", { src: src });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function _fireAupEvent(nodeId, event, data) {
|
|
254
|
+
// Check if this node lives inside a device container — route to device WS
|
|
255
|
+
var nodeEl = document.querySelector('[data-aup-id="' + nodeId + '"]');
|
|
256
|
+
if (nodeEl) {
|
|
257
|
+
var deviceEl = nodeEl.closest('[data-aup-device-id]');
|
|
258
|
+
if (deviceEl && deviceEl._aupDeviceWs && deviceEl._aupDeviceWs.readyState === 1) {
|
|
259
|
+
deviceEl._aupDeviceWs.send(JSON.stringify({
|
|
260
|
+
type: "aup_event", nodeId: nodeId, event: event, data: data
|
|
261
|
+
}));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Default: send to parent WS
|
|
266
|
+
if (ws && ws.readyState === 1) {
|
|
267
|
+
ws.send(JSON.stringify({ type: "aup_event", nodeId: nodeId, event: event, data: data }));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
`;
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
export { FRAME_JS };
|
|
274
|
+
//# sourceMappingURL=frame.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.mjs","names":[],"sources":["../../../src/web-page/renderers/frame.ts"],"sourcesContent":["export const FRAME_JS = `\n // ── Frame Renderer (sandboxed iframe for page isolation) ──\n\n // Track known iframe contentWindows for origin validation\n var _aupFrameWindows = new Set();\n var _aupBridgeWindows = new Set();\n var _aupBridgeOriginByWindow = new Map();\n\n function _isHttpUrl(src) {\n try {\n var u = new URL(src, location.href);\n return u.protocol === \"http:\" || u.protocol === \"https:\";\n } catch (_ex) {\n return false;\n }\n }\n\n function _isSameOriginUrl(src) {\n try {\n return new URL(src, location.href).origin === location.origin;\n } catch (_ex) {\n return false;\n }\n }\n\n function _resolveFrameSrc(rawSrc, bridgeRequested) {\n var src = typeof rawSrc === \"string\" ? rawSrc.trim() : \"\";\n if (!src) return null;\n\n if (src.indexOf(\"/pages/\") === 0) {\n var pageId = src.replace(/^\\\\/pages\\\\//, \"\");\n if (!pageId) return null;\n var query = [];\n if (_afsSessionId) query.push(\"sid=\" + encodeURIComponent(_afsSessionId));\n if (_afsSessionToken) query.push(\"st=\" + encodeURIComponent(_afsSessionToken));\n if (bridgeRequested) query.push(\"bridge=1\");\n var localUrl = location.origin + \"/p/\" + encodeURIComponent(pageId);\n if (query.length) localUrl += \"?\" + query.join(\"&\");\n return { url: localUrl, bridgeEnabled: !!bridgeRequested };\n }\n\n if (_isHttpUrl(src)) {\n return {\n url: src,\n bridgeEnabled: !!bridgeRequested && _isSameOriginUrl(src),\n };\n }\n\n return null;\n }\n\n function _normalizeSandbox(rawSandbox, bridgeEnabled) {\n var base = [\"allow-scripts\", \"allow-forms\", \"allow-popups\"];\n var allowed = {\n \"allow-downloads\": 1,\n \"allow-forms\": 1,\n \"allow-modals\": 1,\n \"allow-orientation-lock\": 1,\n \"allow-pointer-lock\": 1,\n \"allow-popups\": 1,\n \"allow-popups-to-escape-sandbox\": 1,\n \"allow-presentation\": 1,\n \"allow-same-origin\": 1,\n \"allow-scripts\": 1,\n \"allow-storage-access-by-user-activation\": 1,\n \"allow-top-navigation\": 1,\n \"allow-top-navigation-by-user-activation\": 1,\n };\n\n var seen = {};\n var out = [];\n var tokens = String(rawSandbox || \"\").split(/\\\\s+/);\n for (var i = 0; i < tokens.length; i++) {\n var token = tokens[i];\n if (!token || !allowed[token] || seen[token]) continue;\n seen[token] = 1;\n out.push(token);\n }\n for (var j = 0; j < base.length; j++) {\n if (!seen[base[j]]) out.push(base[j]);\n }\n if (!bridgeEnabled) {\n out = out.filter(function(token) { return token !== \"allow-same-origin\"; });\n }\n return out.join(\" \");\n }\n\n function _unregisterFrameWindow(iframe) {\n var prev = iframe && iframe._aupTrackedWindow;\n if (!prev) return;\n _aupFrameWindows.delete(prev);\n _aupBridgeWindows.delete(prev);\n _aupBridgeOriginByWindow.delete(prev);\n // Clean up bridge subscriptions owned by this window\n if (window._aupBridgeSubsByWindow) {\n var subIds = window._aupBridgeSubsByWindow.get(prev);\n if (subIds) {\n subIds.forEach(function(sid) {\n if (window._aupBridgeSubs && window._aupBridgeSubs[sid]) {\n try { window._aupBridgeSubs[sid](); } catch(_ex) {}\n delete window._aupBridgeSubs[sid];\n }\n });\n window._aupBridgeSubsByWindow.delete(prev);\n }\n }\n iframe._aupTrackedWindow = null;\n }\n\n function _registerFrameWindow(iframe, bridgeEnabled, srcUrl) {\n _unregisterFrameWindow(iframe);\n try {\n var win = iframe.contentWindow;\n if (!win) return;\n iframe._aupTrackedWindow = win;\n _aupFrameWindows.add(win);\n if (bridgeEnabled) {\n _aupBridgeWindows.add(win);\n try {\n _aupBridgeOriginByWindow.set(win, new URL(srcUrl, location.href).origin);\n } catch (_ex) {}\n }\n } catch (_ex) {}\n }\n\n function renderAupFrame(node) {\n var el = document.createElement(\"div\");\n el.className = \"aup-frame\";\n var p = node.props || {};\n var src = typeof p.src === \"string\" ? p.src : \"\";\n var bridge = !!p.bridge;\n var loading = p.loading || \"lazy\";\n var size = p.size || {};\n var fallback = p.fallback || \"\";\n\n if (size.width) el.setAttribute(\"data-size-width\", \"1\");\n if (size.height) el.setAttribute(\"data-size-height\", \"1\");\n\n if (!src) {\n var placeholder = document.createElement(\"div\");\n placeholder.className = \"aup-frame-error\";\n placeholder.innerHTML = '<span class=\"aup-frame-error-icon\">\\\\u26a0</span>'\n + '<span class=\"aup-frame-error-msg\">Frame: set src prop to embed a page</span>';\n el.appendChild(placeholder);\n return el;\n }\n\n var resolved = _resolveFrameSrc(src, bridge);\n if (!resolved) {\n var invalid = document.createElement(\"div\");\n invalid.className = \"aup-frame-error\";\n invalid.innerHTML = '<span class=\"aup-frame-error-icon\">\\\\u26a0</span>'\n + '<span class=\"aup-frame-error-msg\">Frame: src must be http(s) URL or /pages/*</span>';\n el.appendChild(invalid);\n return el;\n }\n var resolvedSrc = resolved.url;\n var bridgeEnabled = !!resolved.bridgeEnabled;\n var sandbox = _normalizeSandbox(p.sandbox, bridgeEnabled);\n\n // Create iframe\n var iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\"sandbox\", sandbox);\n iframe.setAttribute(\"loading\", loading === \"eager\" ? \"eager\" : \"lazy\");\n iframe.setAttribute(\"referrerpolicy\", \"no-referrer\");\n if (p.transparent) { iframe.setAttribute(\"allowtransparency\", \"true\"); iframe.style.background = \"transparent\"; el.style.background = \"transparent\"; }\n if (p.overlay) {\n // Override .aup-frame class styles that break overlay\n el.className = \"aup-frame-overlay\";\n el.style.cssText = \"position:fixed;inset:0;z-index:99999;pointer-events:none;background:transparent;border:none;overflow:visible;margin:0;padding:0;min-height:0;\";\n iframe.style.cssText = \"pointer-events:none;background:transparent;width:100%;height:100%;border:none;display:block;min-height:0;\";\n iframe.setAttribute(\"allowtransparency\", \"true\");\n el.setAttribute(\"data-aup-overlay\", node.id || \"\");\n iframe.onload = function() {\n _registerFrameWindow(iframe, bridgeEnabled, resolvedSrc);\n };\n iframe.src = resolvedSrc;\n el.appendChild(iframe);\n setTimeout(function() { document.body.appendChild(el); }, 0);\n return document.createElement(\"div\");\n }\n\n // Loading skeleton (non-overlay frames only)\n var skeleton = document.createElement(\"div\");\n skeleton.className = \"aup-frame-loading\";\n skeleton.innerHTML = '<div class=\"aup-frame-loading-bar\"></div>';\n el.appendChild(skeleton);\n if (size.width) iframe.style.width = size.width;\n if (size.height) { iframe.style.height = size.height; el.style.minHeight = \"0\"; }\n\n iframe.onload = function() {\n skeleton.remove();\n _registerFrameWindow(iframe, bridgeEnabled, resolvedSrc);\n // Fire AUP load event\n if (node.events && node.events.load) {\n _fireAupEvent(node.id, \"load\", {});\n }\n };\n\n iframe.onerror = function() {\n _showFrameError(el, skeleton, iframe, resolvedSrc, fallback, bridge, node);\n };\n\n // Also handle load errors via timeout — iframes don't reliably fire onerror\n var errorTimer = setTimeout(function() {\n if (skeleton.parentNode) {\n // Still loading after 30s — likely broken\n _showFrameError(el, skeleton, iframe, resolvedSrc, fallback, bridge, node);\n }\n }, 30000);\n\n iframe.addEventListener(\"load\", function() { clearTimeout(errorTimer); }, { once: true });\n\n iframe.src = resolvedSrc;\n el.appendChild(iframe);\n\n return el;\n }\n\n function _showFrameError(wrapper, skeleton, iframe, src, fallback, bridge, node) {\n if (skeleton.parentNode) skeleton.remove();\n iframe.style.display = \"none\";\n _unregisterFrameWindow(iframe);\n\n // Remove existing error if retrying\n var old = wrapper.querySelector(\".aup-frame-error\");\n if (old) old.remove();\n\n var errorEl = document.createElement(\"div\");\n errorEl.className = \"aup-frame-error\";\n errorEl.innerHTML = '<span class=\"aup-frame-error-icon\">\\\\u26a0</span>'\n + '<span class=\"aup-frame-error-msg\">Failed to load page</span>';\n\n var retryBtn = document.createElement(\"button\");\n retryBtn.className = \"aup-frame-retry\";\n retryBtn.textContent = \"Retry\";\n retryBtn.onclick = function() {\n errorEl.remove();\n iframe.style.display = \"\";\n var retry = _resolveFrameSrc(fallback || src, bridge);\n iframe.src = retry ? retry.url : src;\n };\n errorEl.appendChild(retryBtn);\n wrapper.appendChild(errorEl);\n\n // Fire AUP error event\n if (node.events && node.events.error) {\n _fireAupEvent(node.id, \"error\", { src: src });\n }\n }\n\n function _fireAupEvent(nodeId, event, data) {\n // Check if this node lives inside a device container — route to device WS\n var nodeEl = document.querySelector('[data-aup-id=\"' + nodeId + '\"]');\n if (nodeEl) {\n var deviceEl = nodeEl.closest('[data-aup-device-id]');\n if (deviceEl && deviceEl._aupDeviceWs && deviceEl._aupDeviceWs.readyState === 1) {\n deviceEl._aupDeviceWs.send(JSON.stringify({\n type: \"aup_event\", nodeId: nodeId, event: event, data: data\n }));\n return;\n }\n }\n // Default: send to parent WS\n if (ws && ws.readyState === 1) {\n ws.send(JSON.stringify({ type: \"aup_event\", nodeId: nodeId, event: event, data: data }));\n }\n }\n`;\n"],"mappings":";AAAA,MAAa,WAAW"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/web-page/renderers/globe.ts
|
|
3
|
+
const GLOBE_JS = `
|
|
4
|
+
// ── Globe Map (MapLibre GL — 3D globe with flyTo) ──
|
|
5
|
+
function renderAupGlobe(node) {
|
|
6
|
+
var el = document.createElement("div");
|
|
7
|
+
el.className = "aup-map";
|
|
8
|
+
var p = node.props || {};
|
|
9
|
+
var center = p.center || [0, 20];
|
|
10
|
+
var zoom = p.zoom || 1.5;
|
|
11
|
+
var tileStyle = p.tileStyle || "carto-voyager";
|
|
12
|
+
if (p.height) el.style.height = p.height;
|
|
13
|
+
var mapDiv = document.createElement("div");
|
|
14
|
+
mapDiv.style.width = "100%";
|
|
15
|
+
mapDiv.style.height = "100%";
|
|
16
|
+
el.appendChild(mapDiv);
|
|
17
|
+
|
|
18
|
+
function tileUrl(style) {
|
|
19
|
+
var t = _mapTileStyles[style] || _mapTileStyles["carto-voyager"];
|
|
20
|
+
// MapLibre needs explicit subdomain — replace {s} with "a", {r} with @2x for retina
|
|
21
|
+
return t.url.replace("{s}", "a").replace("{r}", "@2x");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function initGlobe() {
|
|
25
|
+
if (typeof maplibregl === "undefined") {
|
|
26
|
+
loadCSS("https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.css");
|
|
27
|
+
var loading = document.createElement("div");
|
|
28
|
+
loading.className = "aup-map-loading";
|
|
29
|
+
loading.textContent = "Loading globe...";
|
|
30
|
+
el.insertBefore(loading, mapDiv);
|
|
31
|
+
loadScript("https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.js", function() {
|
|
32
|
+
if (loading.parentNode) loading.parentNode.removeChild(loading);
|
|
33
|
+
createGlobe();
|
|
34
|
+
});
|
|
35
|
+
} else {
|
|
36
|
+
createGlobe();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createGlobe() {
|
|
41
|
+
try {
|
|
42
|
+
var tile = _mapTileStyles[tileStyle] || _mapTileStyles["carto-voyager"];
|
|
43
|
+
var map = new maplibregl.Map({
|
|
44
|
+
container: mapDiv,
|
|
45
|
+
style: {
|
|
46
|
+
version: 8,
|
|
47
|
+
sources: { basemap: { type: "raster", tiles: [tileUrl(tileStyle)], tileSize: 256, attribution: tile.attr } },
|
|
48
|
+
layers: [{ id: "basemap", type: "raster", source: "basemap" }],
|
|
49
|
+
glyphs: "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf"
|
|
50
|
+
},
|
|
51
|
+
center: [center[1], center[0]], // MapLibre uses [lng, lat]
|
|
52
|
+
zoom: zoom,
|
|
53
|
+
projection: "globe",
|
|
54
|
+
attributionControl: false
|
|
55
|
+
});
|
|
56
|
+
map.addControl(new maplibregl.AttributionControl({ compact: true }), "bottom-right");
|
|
57
|
+
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "bottom-right");
|
|
58
|
+
|
|
59
|
+
// Add markers
|
|
60
|
+
var markers = p.markers || [];
|
|
61
|
+
for (var i = 0; i < markers.length; i++) {
|
|
62
|
+
addGlobeMarker(map, markers[i]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// flyTo sequence
|
|
66
|
+
var flyMarkers = p.flyTo || markers;
|
|
67
|
+
if (flyMarkers.length > 0 && p.autoFly !== false) {
|
|
68
|
+
var flyIdx = 0;
|
|
69
|
+
var flyDelay = p.flyInterval || 4000;
|
|
70
|
+
var flyZoom = p.flyZoom || 5;
|
|
71
|
+
map.once("load", function() {
|
|
72
|
+
setTimeout(function flyNext() {
|
|
73
|
+
if (!el.isConnected) return; // stop if removed from DOM
|
|
74
|
+
var target = flyMarkers[flyIdx % flyMarkers.length];
|
|
75
|
+
map.flyTo({
|
|
76
|
+
center: [target.lng, target.lat],
|
|
77
|
+
zoom: target.zoom || flyZoom,
|
|
78
|
+
duration: p.flyDuration || 3000,
|
|
79
|
+
essential: true
|
|
80
|
+
});
|
|
81
|
+
flyIdx++;
|
|
82
|
+
setTimeout(flyNext, flyDelay);
|
|
83
|
+
}, 1500);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Support tileStyle switching via custom event
|
|
88
|
+
el._globeMap = map;
|
|
89
|
+
el._setTileStyle = function(newStyle) {
|
|
90
|
+
var t = _mapTileStyles[newStyle] || _mapTileStyles["carto-voyager"];
|
|
91
|
+
var src = map.getSource("basemap");
|
|
92
|
+
if (src && src.setTiles) {
|
|
93
|
+
src.setTiles([tileUrl(newStyle)]);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
} catch(e) { mapDiv.textContent = "Globe error: " + e.message; }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function addGlobeMarker(map, m) {
|
|
100
|
+
var color = m.color || _markerIntentColors[m.intent || "primary"] || "#6366f1";
|
|
101
|
+
var size = (m.radius || m.size || 7) * 2;
|
|
102
|
+
var dot = document.createElement("div");
|
|
103
|
+
dot.style.cssText = "width:" + size + "px;height:" + size + "px;border-radius:50%;background:" + color + ";border:2px solid rgba(255,255,255,0.9);box-shadow:0 2px 8px rgba(0,0,0,0.3);cursor:pointer;";
|
|
104
|
+
var marker = new maplibregl.Marker({ element: dot }).setLngLat([m.lng, m.lat]).addTo(map);
|
|
105
|
+
if (m.label) {
|
|
106
|
+
var popup = new maplibregl.Popup({ offset: size / 2 + 4, closeButton: false })
|
|
107
|
+
.setHTML('<div style="font-family:var(--font-body);font-size:0.85em;padding:2px 4px;">' + _escapeHtml(String(m.label)) + "</div>");
|
|
108
|
+
marker.setPopup(popup);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setTimeout(initGlobe, 0);
|
|
113
|
+
return el;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
exports.GLOBE_JS = GLOBE_JS;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
//#region src/web-page/renderers/globe.ts
|
|
2
|
+
const GLOBE_JS = `
|
|
3
|
+
// ── Globe Map (MapLibre GL — 3D globe with flyTo) ──
|
|
4
|
+
function renderAupGlobe(node) {
|
|
5
|
+
var el = document.createElement("div");
|
|
6
|
+
el.className = "aup-map";
|
|
7
|
+
var p = node.props || {};
|
|
8
|
+
var center = p.center || [0, 20];
|
|
9
|
+
var zoom = p.zoom || 1.5;
|
|
10
|
+
var tileStyle = p.tileStyle || "carto-voyager";
|
|
11
|
+
if (p.height) el.style.height = p.height;
|
|
12
|
+
var mapDiv = document.createElement("div");
|
|
13
|
+
mapDiv.style.width = "100%";
|
|
14
|
+
mapDiv.style.height = "100%";
|
|
15
|
+
el.appendChild(mapDiv);
|
|
16
|
+
|
|
17
|
+
function tileUrl(style) {
|
|
18
|
+
var t = _mapTileStyles[style] || _mapTileStyles["carto-voyager"];
|
|
19
|
+
// MapLibre needs explicit subdomain — replace {s} with "a", {r} with @2x for retina
|
|
20
|
+
return t.url.replace("{s}", "a").replace("{r}", "@2x");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function initGlobe() {
|
|
24
|
+
if (typeof maplibregl === "undefined") {
|
|
25
|
+
loadCSS("https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.css");
|
|
26
|
+
var loading = document.createElement("div");
|
|
27
|
+
loading.className = "aup-map-loading";
|
|
28
|
+
loading.textContent = "Loading globe...";
|
|
29
|
+
el.insertBefore(loading, mapDiv);
|
|
30
|
+
loadScript("https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.js", function() {
|
|
31
|
+
if (loading.parentNode) loading.parentNode.removeChild(loading);
|
|
32
|
+
createGlobe();
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
createGlobe();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createGlobe() {
|
|
40
|
+
try {
|
|
41
|
+
var tile = _mapTileStyles[tileStyle] || _mapTileStyles["carto-voyager"];
|
|
42
|
+
var map = new maplibregl.Map({
|
|
43
|
+
container: mapDiv,
|
|
44
|
+
style: {
|
|
45
|
+
version: 8,
|
|
46
|
+
sources: { basemap: { type: "raster", tiles: [tileUrl(tileStyle)], tileSize: 256, attribution: tile.attr } },
|
|
47
|
+
layers: [{ id: "basemap", type: "raster", source: "basemap" }],
|
|
48
|
+
glyphs: "https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf"
|
|
49
|
+
},
|
|
50
|
+
center: [center[1], center[0]], // MapLibre uses [lng, lat]
|
|
51
|
+
zoom: zoom,
|
|
52
|
+
projection: "globe",
|
|
53
|
+
attributionControl: false
|
|
54
|
+
});
|
|
55
|
+
map.addControl(new maplibregl.AttributionControl({ compact: true }), "bottom-right");
|
|
56
|
+
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "bottom-right");
|
|
57
|
+
|
|
58
|
+
// Add markers
|
|
59
|
+
var markers = p.markers || [];
|
|
60
|
+
for (var i = 0; i < markers.length; i++) {
|
|
61
|
+
addGlobeMarker(map, markers[i]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// flyTo sequence
|
|
65
|
+
var flyMarkers = p.flyTo || markers;
|
|
66
|
+
if (flyMarkers.length > 0 && p.autoFly !== false) {
|
|
67
|
+
var flyIdx = 0;
|
|
68
|
+
var flyDelay = p.flyInterval || 4000;
|
|
69
|
+
var flyZoom = p.flyZoom || 5;
|
|
70
|
+
map.once("load", function() {
|
|
71
|
+
setTimeout(function flyNext() {
|
|
72
|
+
if (!el.isConnected) return; // stop if removed from DOM
|
|
73
|
+
var target = flyMarkers[flyIdx % flyMarkers.length];
|
|
74
|
+
map.flyTo({
|
|
75
|
+
center: [target.lng, target.lat],
|
|
76
|
+
zoom: target.zoom || flyZoom,
|
|
77
|
+
duration: p.flyDuration || 3000,
|
|
78
|
+
essential: true
|
|
79
|
+
});
|
|
80
|
+
flyIdx++;
|
|
81
|
+
setTimeout(flyNext, flyDelay);
|
|
82
|
+
}, 1500);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Support tileStyle switching via custom event
|
|
87
|
+
el._globeMap = map;
|
|
88
|
+
el._setTileStyle = function(newStyle) {
|
|
89
|
+
var t = _mapTileStyles[newStyle] || _mapTileStyles["carto-voyager"];
|
|
90
|
+
var src = map.getSource("basemap");
|
|
91
|
+
if (src && src.setTiles) {
|
|
92
|
+
src.setTiles([tileUrl(newStyle)]);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
} catch(e) { mapDiv.textContent = "Globe error: " + e.message; }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function addGlobeMarker(map, m) {
|
|
99
|
+
var color = m.color || _markerIntentColors[m.intent || "primary"] || "#6366f1";
|
|
100
|
+
var size = (m.radius || m.size || 7) * 2;
|
|
101
|
+
var dot = document.createElement("div");
|
|
102
|
+
dot.style.cssText = "width:" + size + "px;height:" + size + "px;border-radius:50%;background:" + color + ";border:2px solid rgba(255,255,255,0.9);box-shadow:0 2px 8px rgba(0,0,0,0.3);cursor:pointer;";
|
|
103
|
+
var marker = new maplibregl.Marker({ element: dot }).setLngLat([m.lng, m.lat]).addTo(map);
|
|
104
|
+
if (m.label) {
|
|
105
|
+
var popup = new maplibregl.Popup({ offset: size / 2 + 4, closeButton: false })
|
|
106
|
+
.setHTML('<div style="font-family:var(--font-body);font-size:0.85em;padding:2px 4px;">' + _escapeHtml(String(m.label)) + "</div>");
|
|
107
|
+
marker.setPopup(popup);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setTimeout(initGlobe, 0);
|
|
112
|
+
return el;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
export { GLOBE_JS };
|
|
119
|
+
//# sourceMappingURL=globe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"globe.mjs","names":[],"sources":["../../../src/web-page/renderers/globe.ts"],"sourcesContent":["export const GLOBE_JS = `\n // ── Globe Map (MapLibre GL — 3D globe with flyTo) ──\n function renderAupGlobe(node) {\n var el = document.createElement(\"div\");\n el.className = \"aup-map\";\n var p = node.props || {};\n var center = p.center || [0, 20];\n var zoom = p.zoom || 1.5;\n var tileStyle = p.tileStyle || \"carto-voyager\";\n if (p.height) el.style.height = p.height;\n var mapDiv = document.createElement(\"div\");\n mapDiv.style.width = \"100%\";\n mapDiv.style.height = \"100%\";\n el.appendChild(mapDiv);\n\n function tileUrl(style) {\n var t = _mapTileStyles[style] || _mapTileStyles[\"carto-voyager\"];\n // MapLibre needs explicit subdomain — replace {s} with \"a\", {r} with @2x for retina\n return t.url.replace(\"{s}\", \"a\").replace(\"{r}\", \"@2x\");\n }\n\n function initGlobe() {\n if (typeof maplibregl === \"undefined\") {\n loadCSS(\"https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.css\");\n var loading = document.createElement(\"div\");\n loading.className = \"aup-map-loading\";\n loading.textContent = \"Loading globe...\";\n el.insertBefore(loading, mapDiv);\n loadScript(\"https://cdn.jsdelivr.net/npm/maplibre-gl@4/dist/maplibre-gl.js\", function() {\n if (loading.parentNode) loading.parentNode.removeChild(loading);\n createGlobe();\n });\n } else {\n createGlobe();\n }\n }\n\n function createGlobe() {\n try {\n var tile = _mapTileStyles[tileStyle] || _mapTileStyles[\"carto-voyager\"];\n var map = new maplibregl.Map({\n container: mapDiv,\n style: {\n version: 8,\n sources: { basemap: { type: \"raster\", tiles: [tileUrl(tileStyle)], tileSize: 256, attribution: tile.attr } },\n layers: [{ id: \"basemap\", type: \"raster\", source: \"basemap\" }],\n glyphs: \"https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf\"\n },\n center: [center[1], center[0]], // MapLibre uses [lng, lat]\n zoom: zoom,\n projection: \"globe\",\n attributionControl: false\n });\n map.addControl(new maplibregl.AttributionControl({ compact: true }), \"bottom-right\");\n map.addControl(new maplibregl.NavigationControl({ showCompass: false }), \"bottom-right\");\n\n // Add markers\n var markers = p.markers || [];\n for (var i = 0; i < markers.length; i++) {\n addGlobeMarker(map, markers[i]);\n }\n\n // flyTo sequence\n var flyMarkers = p.flyTo || markers;\n if (flyMarkers.length > 0 && p.autoFly !== false) {\n var flyIdx = 0;\n var flyDelay = p.flyInterval || 4000;\n var flyZoom = p.flyZoom || 5;\n map.once(\"load\", function() {\n setTimeout(function flyNext() {\n if (!el.isConnected) return; // stop if removed from DOM\n var target = flyMarkers[flyIdx % flyMarkers.length];\n map.flyTo({\n center: [target.lng, target.lat],\n zoom: target.zoom || flyZoom,\n duration: p.flyDuration || 3000,\n essential: true\n });\n flyIdx++;\n setTimeout(flyNext, flyDelay);\n }, 1500);\n });\n }\n\n // Support tileStyle switching via custom event\n el._globeMap = map;\n el._setTileStyle = function(newStyle) {\n var t = _mapTileStyles[newStyle] || _mapTileStyles[\"carto-voyager\"];\n var src = map.getSource(\"basemap\");\n if (src && src.setTiles) {\n src.setTiles([tileUrl(newStyle)]);\n }\n };\n } catch(e) { mapDiv.textContent = \"Globe error: \" + e.message; }\n }\n\n function addGlobeMarker(map, m) {\n var color = m.color || _markerIntentColors[m.intent || \"primary\"] || \"#6366f1\";\n var size = (m.radius || m.size || 7) * 2;\n var dot = document.createElement(\"div\");\n dot.style.cssText = \"width:\" + size + \"px;height:\" + size + \"px;border-radius:50%;background:\" + color + \";border:2px solid rgba(255,255,255,0.9);box-shadow:0 2px 8px rgba(0,0,0,0.3);cursor:pointer;\";\n var marker = new maplibregl.Marker({ element: dot }).setLngLat([m.lng, m.lat]).addTo(map);\n if (m.label) {\n var popup = new maplibregl.Popup({ offset: size / 2 + 4, closeButton: false })\n .setHTML('<div style=\"font-family:var(--font-body);font-size:0.85em;padding:2px 4px;\">' + _escapeHtml(String(m.label)) + \"</div>\");\n marker.setPopup(popup);\n }\n }\n\n setTimeout(initGlobe, 0);\n return el;\n }\n\n`;\n"],"mappings":";AAAA,MAAa,WAAW"}
|