@feedmepos/mf-report 5.28.5 → 5.29.0
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/dist/{App-Bc8Mp5vR.js → App-CuBsRwfx.js} +4 -4
- package/dist/{DateRangeSelect-cbSEHMzW.js → DateRangeSelect-CzjlwjuC.js} +1 -1
- package/dist/{Default.vue_vue_type_script_setup_true_lang-BpJFA_ET.js → Default.vue_vue_type_script_setup_true_lang-Di9_F5Yk.js} +1 -1
- package/dist/{DynamicWidget.vue_vue_type_script_setup_true_lang-Dx-cxJP6.js → DynamicWidget.vue_vue_type_script_setup_true_lang-C73R6Fb9.js} +5 -5
- package/dist/{GlobalFilterSelect.vue_vue_type_script_setup_true_lang-Cxrti7_L.js → GlobalFilterSelect.vue_vue_type_script_setup_true_lang-tLRFdLNs.js} +9 -10
- package/dist/{InsightView-Bo0Fb5QG.js → InsightView-BcnmLQ8c.js} +5 -5
- package/dist/{Integration-B4tC2MlL.js → Integration-D4VFT1M8.js} +3 -3
- package/dist/{Integrations-RB8iuvdJ.js → Integrations-v8oF1TL7.js} +2 -2
- package/dist/{Layout-DBCPtUH7.js → Layout-Ds8RSWmi.js} +1 -1
- package/dist/{LocationSelect.vue_vue_type_script_setup_true_lang-w2H9qOgJ.js → LocationSelect.vue_vue_type_script_setup_true_lang-Dk6u8nUH.js} +1638 -1634
- package/dist/{MenuTab.vue_vue_type_script_setup_true_lang-BLVGUSWf.js → MenuTab.vue_vue_type_script_setup_true_lang-BWt7pqFb.js} +5 -5
- package/dist/{NavigationTab.vue_vue_type_script_setup_true_lang-N31ee-oZ.js → NavigationTab.vue_vue_type_script_setup_true_lang-CU00GINo.js} +3853 -3823
- package/dist/OverviewView-1v80g6KX.js +81 -0
- package/dist/{Report-Di6GyP1r.js → Report-DcFB4gM6.js} +3 -3
- package/dist/ReportCanvasView-BQO6Mywc.js +5190 -0
- package/dist/{ReportEditor-BfNmiEVn.js → ReportEditor-Bgcisf1s.js} +7 -7
- package/dist/{ReportView-JWJ-6FCu.js → ReportView-Q_juD8Kr.js} +6 -6
- package/dist/{ReportViewRoute-BMjjJqN2.js → ReportViewRoute-DwStGqyz.js} +2 -2
- package/dist/{Setting-D13NIemB.js → Setting-C9rlM23O.js} +3 -3
- package/dist/{Template-D16bqzrx.js → Template-BZd0NB5w.js} +3 -3
- package/dist/{TileEditor-1Sn9ZSb2.js → TileEditor-B2F9r3RS.js} +6 -6
- package/dist/{TilePageStack.vue_vue_type_script_setup_true_lang-BOXsV7-r.js → TilePageStack.vue_vue_type_script_setup_true_lang-BmbrJAph.js} +2 -2
- package/dist/{TransitionFade-DwJkmMTb.js → TransitionFade-Dkeo9qr0.js} +5 -5
- package/dist/{app-CVBllEjW.js → app-CoQ_GRKo.js} +1819 -1326
- package/dist/app.js +1 -1
- package/dist/assets/{formatChartData.worker-DZp5dSDQ.js → formatChartData.worker-D7sGqQhX.js} +34 -34
- package/dist/assets/{processTableData.worker-Kyuhw4lS.js → processTableData.worker-CltxwQxU.js} +34 -34
- package/dist/{report-Cn4Ik1zm.js → report-Dau_so1j.js} +1 -1
- package/dist/report-canvas-libs/components-1.js +1042 -0
- package/dist/report-canvas-libs/fm-ui-1.js +599 -180
- package/dist/style.css +1 -1
- package/dist/{useRestaurantPermission-C8bLPfk8.js → useRestaurantPermission-CPoHZTxx.js} +1 -1
- package/package.json +2 -1
- package/dist/OverviewView-WKe6Zxe1.js +0 -61
- package/dist/ReportCanvasView-Dg2ItHBO.js +0 -1336
|
@@ -1,1336 +0,0 @@
|
|
|
1
|
-
import { ref as q, shallowRef as en, onMounted as W, onBeforeUnmount as tn, defineComponent as rn, watch as H, computed as on, createElementBlock as T, openBlock as L, createElementVNode as P, createCommentVNode as U, withDirectives as an, createVNode as V, unref as j, isRef as F, withCtx as sn, toDisplayString as J, vShow as dn } from "vue";
|
|
2
|
-
import { useRoute as cn } from "vue-router";
|
|
3
|
-
import { u as ln } from "./_plugin-vue_export-helper-C6QDExPN.js";
|
|
4
|
-
import { storeToRefs as N } from "pinia";
|
|
5
|
-
import { _ as un } from "./LocationSelect.vue_vue_type_script_setup_true_lang-w2H9qOgJ.js";
|
|
6
|
-
import { D as fn } from "./DateRangeSelect-cbSEHMzW.js";
|
|
7
|
-
import { F as B, r as D, u as G, a as X, a0 as mn } from "./app-CVBllEjW.js";
|
|
8
|
-
function $(r) {
|
|
9
|
-
const { businessId: c } = N(G());
|
|
10
|
-
if (!c.value) throw new Error("Business Id is not defined");
|
|
11
|
-
return `/${c.value}/report-canvas/${r}`;
|
|
12
|
-
}
|
|
13
|
-
const pn = (r) => {
|
|
14
|
-
const c = {
|
|
15
|
-
fetchType: B.Get,
|
|
16
|
-
url: $(r)
|
|
17
|
-
};
|
|
18
|
-
return D.fetch(c);
|
|
19
|
-
}, gn = (r, c, l, s) => {
|
|
20
|
-
const p = {
|
|
21
|
-
fetchType: B.Post,
|
|
22
|
-
url: `${$(r)}/query/${c}`,
|
|
23
|
-
data: { params: l, timezone: s }
|
|
24
|
-
};
|
|
25
|
-
return D.fetch(p);
|
|
26
|
-
}, hn = (r, c, l) => {
|
|
27
|
-
const s = {
|
|
28
|
-
fetchType: B.Post,
|
|
29
|
-
url: `${$(r)}/section/${c}/ack`,
|
|
30
|
-
data: l
|
|
31
|
-
};
|
|
32
|
-
return D.fetch(s);
|
|
33
|
-
}, vn = ["@from", "@to", "@locationIds", "@timezone"], bn = ["@from", "@to", "@locationIds"], yn = 1, xn = 1500;
|
|
34
|
-
function wn(r) {
|
|
35
|
-
if (!(!(r instanceof mn) || r.status !== 429))
|
|
36
|
-
return Math.min(Math.max(r.retryAfterMs ?? xn, 500), 5e3);
|
|
37
|
-
}
|
|
38
|
-
const kn = {
|
|
39
|
-
MY: "MYR",
|
|
40
|
-
SG: "SGD",
|
|
41
|
-
TH: "THB",
|
|
42
|
-
ID: "IDR",
|
|
43
|
-
PH: "PHP",
|
|
44
|
-
VN: "VND",
|
|
45
|
-
BN: "BND",
|
|
46
|
-
KH: "KHR",
|
|
47
|
-
MM: "MMK",
|
|
48
|
-
AE: "AED",
|
|
49
|
-
MO: "MOP",
|
|
50
|
-
MV: "MVR",
|
|
51
|
-
AU: "AUD",
|
|
52
|
-
JP: "JPY",
|
|
53
|
-
TW: "TWD",
|
|
54
|
-
HK: "HKD"
|
|
55
|
-
};
|
|
56
|
-
function K(r) {
|
|
57
|
-
return r == null ? !0 : Array.isArray(r) ? r.length === 0 : r === "";
|
|
58
|
-
}
|
|
59
|
-
function In(r) {
|
|
60
|
-
const c = X(), { dateRange: l, selectedLocationIds: s, locations: p, timezone: g } = N(c), { country: I } = N(G()), o = q({}), i = en({}), w = q([]), S = [], y = {}, h = {};
|
|
61
|
-
function C(n) {
|
|
62
|
-
S.push(n);
|
|
63
|
-
}
|
|
64
|
-
function O(n) {
|
|
65
|
-
i.value = { ...i.value, [n.queryId]: n }, S.forEach((t) => t(n));
|
|
66
|
-
}
|
|
67
|
-
function _(n) {
|
|
68
|
-
const t = {};
|
|
69
|
-
for (const m of n.params)
|
|
70
|
-
t[m.name] = m.default ?? null;
|
|
71
|
-
t["@from"] = l.value.startDate ?? null, t["@to"] = l.value.endDate ?? null, t["@timezone"] = g.value, t["@locationIds"] = s.value.length ? [...s.value] : p.value.map((m) => m.id), o.value = t;
|
|
72
|
-
}
|
|
73
|
-
function a() {
|
|
74
|
-
return {
|
|
75
|
-
locations: p.value.map((n) => ({
|
|
76
|
-
id: n.id,
|
|
77
|
-
name: n.name || n.id,
|
|
78
|
-
code: n.code
|
|
79
|
-
})),
|
|
80
|
-
timezone: g.value,
|
|
81
|
-
locale: navigator.language || "en-US",
|
|
82
|
-
currency: kn[(I.value || "").toUpperCase()] ?? ""
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function v(n) {
|
|
86
|
-
return JSON.stringify(n.dependsOn.map((t) => o.value[t] ?? null));
|
|
87
|
-
}
|
|
88
|
-
function x(n) {
|
|
89
|
-
return !!n.lazy && n.dependsOn.some((t) => K(o.value[t] ?? null));
|
|
90
|
-
}
|
|
91
|
-
function A() {
|
|
92
|
-
return !bn.some((n) => n in o.value && K(o.value[n] ?? null));
|
|
93
|
-
}
|
|
94
|
-
async function E(n) {
|
|
95
|
-
var m;
|
|
96
|
-
const t = (y[n.id] ?? 0) + 1;
|
|
97
|
-
y[n.id] = t, O({
|
|
98
|
-
queryId: n.id,
|
|
99
|
-
runId: t,
|
|
100
|
-
status: "loading",
|
|
101
|
-
rows: ((m = i.value[n.id]) == null ? void 0 : m.rows) ?? [],
|
|
102
|
-
truncated: !1
|
|
103
|
-
});
|
|
104
|
-
for (let k = 0; ; k++)
|
|
105
|
-
try {
|
|
106
|
-
const b = await gn(
|
|
107
|
-
r,
|
|
108
|
-
n.id,
|
|
109
|
-
o.value,
|
|
110
|
-
g.value
|
|
111
|
-
);
|
|
112
|
-
if (y[n.id] !== t) return;
|
|
113
|
-
O({
|
|
114
|
-
queryId: n.id,
|
|
115
|
-
runId: t,
|
|
116
|
-
status: "ok",
|
|
117
|
-
rows: b.rows ?? [],
|
|
118
|
-
truncated: !!b.truncated
|
|
119
|
-
});
|
|
120
|
-
return;
|
|
121
|
-
} catch (b) {
|
|
122
|
-
if (y[n.id] !== t) return;
|
|
123
|
-
const f = wn(b);
|
|
124
|
-
if (f !== void 0 && k < yn) {
|
|
125
|
-
if (await new Promise((d) => setTimeout(d, f)), y[n.id] !== t) return;
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
O({
|
|
129
|
-
queryId: n.id,
|
|
130
|
-
runId: t,
|
|
131
|
-
status: "error",
|
|
132
|
-
rows: [],
|
|
133
|
-
truncated: !1,
|
|
134
|
-
error: b instanceof Error ? b.message : String(b)
|
|
135
|
-
});
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
function R(n = {}) {
|
|
140
|
-
var m, k, b;
|
|
141
|
-
const t = !A();
|
|
142
|
-
for (const f of w.value) {
|
|
143
|
-
if (x(f)) {
|
|
144
|
-
delete h[f.id], ((m = i.value[f.id]) == null ? void 0 : m.status) !== "idle" && O({
|
|
145
|
-
queryId: f.id,
|
|
146
|
-
runId: y[f.id] ?? 0,
|
|
147
|
-
status: "idle",
|
|
148
|
-
rows: [],
|
|
149
|
-
truncated: !1
|
|
150
|
-
});
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
if (t) {
|
|
154
|
-
((k = i.value[f.id]) == null ? void 0 : k.status) !== "loading" && O({
|
|
155
|
-
queryId: f.id,
|
|
156
|
-
runId: y[f.id] ?? 0,
|
|
157
|
-
status: "loading",
|
|
158
|
-
rows: ((b = i.value[f.id]) == null ? void 0 : b.rows) ?? [],
|
|
159
|
-
truncated: !1
|
|
160
|
-
});
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
const d = v(f);
|
|
164
|
-
!n.force && h[f.id] === d || (h[f.id] = d, E(f));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
function M(n) {
|
|
168
|
-
const t = { ...o.value };
|
|
169
|
-
let m = !1;
|
|
170
|
-
for (const [k, b] of Object.entries(n))
|
|
171
|
-
k in t && JSON.stringify(t[k] ?? null) !== JSON.stringify(b ?? null) && (t[k] = b, m = !0);
|
|
172
|
-
return m ? (o.value = t, R(), !0) : !1;
|
|
173
|
-
}
|
|
174
|
-
function e(n) {
|
|
175
|
-
w.value = n.queries, i.value = {};
|
|
176
|
-
for (const t of Object.keys(y)) delete y[t];
|
|
177
|
-
for (const t of Object.keys(h)) delete h[t];
|
|
178
|
-
_(n), R();
|
|
179
|
-
}
|
|
180
|
-
function u(n, t = []) {
|
|
181
|
-
w.value = n.queries;
|
|
182
|
-
const m = JSON.stringify(o.value), k = {};
|
|
183
|
-
for (const d of n.params)
|
|
184
|
-
k[d.name] = d.name in o.value ? o.value[d.name] : d.default ?? null;
|
|
185
|
-
o.value = k;
|
|
186
|
-
const b = new Set(n.queries.map((d) => d.id));
|
|
187
|
-
for (const d of Object.keys(y)) b.has(d) || delete y[d];
|
|
188
|
-
for (const d of Object.keys(h)) b.has(d) || delete h[d];
|
|
189
|
-
const f = Object.keys(i.value).filter((d) => !b.has(d));
|
|
190
|
-
if (f.length) {
|
|
191
|
-
const d = { ...i.value };
|
|
192
|
-
for (const nn of f) delete d[nn];
|
|
193
|
-
i.value = d;
|
|
194
|
-
}
|
|
195
|
-
for (const d of t) delete h[d];
|
|
196
|
-
return R(), JSON.stringify(o.value) !== m;
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
params: o,
|
|
200
|
-
results: i,
|
|
201
|
-
queries: w,
|
|
202
|
-
env: a,
|
|
203
|
-
load: e,
|
|
204
|
-
applyCanvas: u,
|
|
205
|
-
run: R,
|
|
206
|
-
setParams: M,
|
|
207
|
-
onResult: C,
|
|
208
|
-
RESERVED: vn
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function En(r) {
|
|
212
|
-
const c = r.replace(/^\//, "");
|
|
213
|
-
try {
|
|
214
|
-
const l = new URL(import.meta.url);
|
|
215
|
-
if (typeof location < "u" && location.hostname !== l.hostname)
|
|
216
|
-
return new URL(c, l).href;
|
|
217
|
-
} catch {
|
|
218
|
-
}
|
|
219
|
-
return `${"/".replace(/\/$/, "")}/${c}`;
|
|
220
|
-
}
|
|
221
|
-
const Z = {
|
|
222
|
-
"chart.js@4": "chart.js-4.js",
|
|
223
|
-
"fm-ui@1": "fm-ui-1.js"
|
|
224
|
-
}, z = /* @__PURE__ */ new Map();
|
|
225
|
-
function Sn(r) {
|
|
226
|
-
return En(`report-canvas-libs/${Z[r]}`);
|
|
227
|
-
}
|
|
228
|
-
async function Y(r) {
|
|
229
|
-
const c = [...new Set(r)].filter((s) => Z[s]), l = {};
|
|
230
|
-
return await Promise.all(
|
|
231
|
-
c.map(async (s) => {
|
|
232
|
-
if (!z.has(s)) {
|
|
233
|
-
const g = await fetch(Sn(s));
|
|
234
|
-
if (!g.ok) return;
|
|
235
|
-
z.set(s, await g.text());
|
|
236
|
-
}
|
|
237
|
-
const p = z.get(s);
|
|
238
|
-
p && (l[s] = p);
|
|
239
|
-
})
|
|
240
|
-
), l;
|
|
241
|
-
}
|
|
242
|
-
const On = `<meta charset="utf-8" />
|
|
243
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
244
|
-
<!--
|
|
245
|
-
Runtime shell for AI-written report sections. Loaded into ONE iframe per report with
|
|
246
|
-
sandbox="allow-scripts" and no allow-same-origin, so this document runs on an opaque
|
|
247
|
-
origin and cannot reach the host. Section code needs inline+eval by design; the sandbox
|
|
248
|
-
attribute is the boundary, and this CSP's job is to keep the frame off the network.
|
|
249
|
-
-->
|
|
250
|
-
<meta
|
|
251
|
-
http-equiv="Content-Security-Policy"
|
|
252
|
-
content="default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval'; style-src 'unsafe-inline'; img-src data: blob:; font-src data:; connect-src 'none'; form-action 'none'; base-uri 'none'"
|
|
253
|
-
/>
|
|
254
|
-
<title>Report</title>
|
|
255
|
-
<style>
|
|
256
|
-
/* Design tokens, from the B2B_POS working file's Report page. A report reads these;
|
|
257
|
-
a merchant asking for a different look is one redefinition here, not a rewrite. */
|
|
258
|
-
:root {
|
|
259
|
-
color-scheme: light;
|
|
260
|
-
--fm-fg: #1c1c1e;
|
|
261
|
-
--fm-body: #2e2e2e;
|
|
262
|
-
--fm-muted: #6b6c70;
|
|
263
|
-
--fm-faint: #aeaeb2;
|
|
264
|
-
--fm-line: #f0f0f0;
|
|
265
|
-
--fm-card: #ffffff;
|
|
266
|
-
--fm-page: #f3f3f6;
|
|
267
|
-
--fm-signature: #ff7823;
|
|
268
|
-
--fm-signature-tint: #fff3e6;
|
|
269
|
-
--fm-signature-dark: #6b3100;
|
|
270
|
-
--fm-danger: #99231d;
|
|
271
|
-
--fm-danger-bg: #ffebea;
|
|
272
|
-
--fm-aqua-500: #0a5384;
|
|
273
|
-
--fm-aqua-300: #3ab0ff;
|
|
274
|
-
--fm-aqua-100: #b7e2ff;
|
|
275
|
-
--fm-purple-500: #512191;
|
|
276
|
-
--fm-purple-300: #8f61e8;
|
|
277
|
-
--fm-purple-100: #eddfff;
|
|
278
|
-
--fm-green-500: #306a01;
|
|
279
|
-
--fm-green-300: #aeef4b;
|
|
280
|
-
--fm-green-100: #f4fde6;
|
|
281
|
-
--fm-plum-500: #8c146a;
|
|
282
|
-
--fm-plum-300: #ffc4c3;
|
|
283
|
-
--fm-plum-100: #fff7fc;
|
|
284
|
-
--fm-mustard-500: #903e1f;
|
|
285
|
-
--fm-mustard-300: #ffc746;
|
|
286
|
-
--fm-mustard-100: #fef5ea;
|
|
287
|
-
--fm-berry-500: #5f090a;
|
|
288
|
-
--fm-berry-300: #e94850;
|
|
289
|
-
--fm-berry-100: #fee4dc;
|
|
290
|
-
--fm-success: #1f7735;
|
|
291
|
-
--fm-success-bg: #ebf9ee;
|
|
292
|
-
--fm-warning: #995900;
|
|
293
|
-
--fm-warning-bg: #fff4e6;
|
|
294
|
-
--fm-error: #99231d;
|
|
295
|
-
--fm-error-bg: #ffebea;
|
|
296
|
-
--fm-info: #004999;
|
|
297
|
-
--fm-info-bg: #e6f2ff;
|
|
298
|
-
--fm-radius: 12px;
|
|
299
|
-
--fm-radius-sm: 8px;
|
|
300
|
-
--fm-gap: 12px;
|
|
301
|
-
}
|
|
302
|
-
* {
|
|
303
|
-
box-sizing: border-box;
|
|
304
|
-
}
|
|
305
|
-
html,
|
|
306
|
-
body {
|
|
307
|
-
margin: 0;
|
|
308
|
-
padding: 0;
|
|
309
|
-
height: 100%;
|
|
310
|
-
}
|
|
311
|
-
body {
|
|
312
|
-
font: 14px/1.5 Inter, "Helvetica Neue", Arial, sans-serif;
|
|
313
|
-
color: var(--fm-fg);
|
|
314
|
-
background: #fff;
|
|
315
|
-
overflow-y: auto;
|
|
316
|
-
}
|
|
317
|
-
body.fmr-page {
|
|
318
|
-
background: var(--fm-page);
|
|
319
|
-
}
|
|
320
|
-
/* The board: 12 columns, ragged bottoms. A section spans all 12 unless it says otherwise. */
|
|
321
|
-
#report {
|
|
322
|
-
display: grid;
|
|
323
|
-
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
324
|
-
align-items: start;
|
|
325
|
-
gap: var(--fm-gap);
|
|
326
|
-
padding: 12px 16px 24px;
|
|
327
|
-
}
|
|
328
|
-
#report > section {
|
|
329
|
-
grid-column: 1 / -1;
|
|
330
|
-
min-width: 0;
|
|
331
|
-
}
|
|
332
|
-
/* ---- components ------------------------------------------------------
|
|
333
|
-
The FeedMe report component set. A report is composed from these; each one
|
|
334
|
-
is already the design system, so nothing here needs restyling to look right. */
|
|
335
|
-
.fmr-card {
|
|
336
|
-
background: var(--fm-card);
|
|
337
|
-
border: 1px solid var(--fm-line);
|
|
338
|
-
border-radius: var(--fm-radius);
|
|
339
|
-
}
|
|
340
|
-
.fmr-tile {
|
|
341
|
-
display: flex;
|
|
342
|
-
flex-direction: column;
|
|
343
|
-
overflow: hidden;
|
|
344
|
-
}
|
|
345
|
-
.fmr-tile-head {
|
|
346
|
-
display: flex;
|
|
347
|
-
align-items: flex-start;
|
|
348
|
-
justify-content: space-between;
|
|
349
|
-
gap: 16px;
|
|
350
|
-
flex-wrap: wrap;
|
|
351
|
-
padding: 16px 20px 12px;
|
|
352
|
-
}
|
|
353
|
-
.fmr-tile-title {
|
|
354
|
-
margin: 0;
|
|
355
|
-
font-size: 16px;
|
|
356
|
-
font-weight: 600;
|
|
357
|
-
color: var(--fm-fg);
|
|
358
|
-
}
|
|
359
|
-
/* One factual line. Not a place to explain the report. */
|
|
360
|
-
.fmr-tile-note {
|
|
361
|
-
margin: 4px 0 0;
|
|
362
|
-
font-size: 12px;
|
|
363
|
-
color: var(--fm-muted);
|
|
364
|
-
}
|
|
365
|
-
.fmr-tile-body {
|
|
366
|
-
padding: 0 20px 16px;
|
|
367
|
-
}
|
|
368
|
-
.fmr-tile-foot {
|
|
369
|
-
display: flex;
|
|
370
|
-
align-items: center;
|
|
371
|
-
justify-content: space-between;
|
|
372
|
-
gap: 12px;
|
|
373
|
-
flex-wrap: wrap;
|
|
374
|
-
border-top: 1px solid var(--fm-line);
|
|
375
|
-
padding: 10px 20px 12px;
|
|
376
|
-
font-size: 12px;
|
|
377
|
-
color: var(--fm-muted);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/* Scorecards: label, value, optional trend. */
|
|
381
|
-
.fmr-kpis {
|
|
382
|
-
display: grid;
|
|
383
|
-
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
384
|
-
gap: var(--fm-gap);
|
|
385
|
-
}
|
|
386
|
-
.fmr-kpi {
|
|
387
|
-
padding: 14px 16px;
|
|
388
|
-
min-width: 0;
|
|
389
|
-
}
|
|
390
|
-
.fmr-kpi-label {
|
|
391
|
-
font-size: 14px;
|
|
392
|
-
color: var(--fm-muted);
|
|
393
|
-
}
|
|
394
|
-
.fmr-kpi-value {
|
|
395
|
-
display: flex;
|
|
396
|
-
align-items: baseline;
|
|
397
|
-
gap: 6px;
|
|
398
|
-
margin-top: 5px;
|
|
399
|
-
font-size: 24px;
|
|
400
|
-
font-weight: 700;
|
|
401
|
-
color: var(--fm-fg);
|
|
402
|
-
font-variant-numeric: tabular-nums;
|
|
403
|
-
}
|
|
404
|
-
.fmr-kpi-cur {
|
|
405
|
-
font-size: 14px;
|
|
406
|
-
font-weight: 600;
|
|
407
|
-
}
|
|
408
|
-
.fmr-kpi-note {
|
|
409
|
-
margin-top: 6px;
|
|
410
|
-
font-size: 12px;
|
|
411
|
-
color: var(--fm-muted);
|
|
412
|
-
}
|
|
413
|
-
.fmr-trend {
|
|
414
|
-
display: inline-flex;
|
|
415
|
-
align-items: center;
|
|
416
|
-
gap: 4px;
|
|
417
|
-
border-radius: 999px;
|
|
418
|
-
padding: 3px 8px;
|
|
419
|
-
font-size: 12px;
|
|
420
|
-
font-weight: 600;
|
|
421
|
-
}
|
|
422
|
-
.fmr-trend.up { color: var(--fm-success); background: var(--fm-green-100); }
|
|
423
|
-
.fmr-trend.down { color: var(--fm-error); background: var(--fm-berry-100); }
|
|
424
|
-
.fmr-trend.flat { color: var(--fm-muted); background: var(--fm-line); }
|
|
425
|
-
|
|
426
|
-
/* Breakdown: a share row over one stacked bar. */
|
|
427
|
-
.fmr-breakdown {
|
|
428
|
-
display: grid;
|
|
429
|
-
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
430
|
-
gap: 12px 24px;
|
|
431
|
-
}
|
|
432
|
-
.fmr-breakdown-label {
|
|
433
|
-
display: flex;
|
|
434
|
-
align-items: center;
|
|
435
|
-
gap: 6px;
|
|
436
|
-
font-size: 14px;
|
|
437
|
-
color: var(--fm-muted);
|
|
438
|
-
}
|
|
439
|
-
.fmr-breakdown-value {
|
|
440
|
-
margin-top: 2px;
|
|
441
|
-
font-size: 14px;
|
|
442
|
-
font-weight: 600;
|
|
443
|
-
color: var(--fm-fg);
|
|
444
|
-
font-variant-numeric: tabular-nums;
|
|
445
|
-
}
|
|
446
|
-
.fmr-stack {
|
|
447
|
-
display: flex;
|
|
448
|
-
gap: 2px;
|
|
449
|
-
height: 12px;
|
|
450
|
-
margin-top: 14px;
|
|
451
|
-
}
|
|
452
|
-
.fmr-stack > span {
|
|
453
|
-
border-radius: 4px;
|
|
454
|
-
min-width: 2px;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/* Legend — you draw it in HTML; chart.js legends stay off. */
|
|
458
|
-
.fmr-legend {
|
|
459
|
-
display: flex;
|
|
460
|
-
align-items: center;
|
|
461
|
-
flex-wrap: wrap;
|
|
462
|
-
gap: 6px 20px;
|
|
463
|
-
}
|
|
464
|
-
.fmr-legend-item {
|
|
465
|
-
display: inline-flex;
|
|
466
|
-
align-items: center;
|
|
467
|
-
gap: 6px;
|
|
468
|
-
font-size: 12px;
|
|
469
|
-
color: var(--fm-muted);
|
|
470
|
-
}
|
|
471
|
-
.fmr-swatch {
|
|
472
|
-
width: 10px;
|
|
473
|
-
height: 10px;
|
|
474
|
-
border-radius: 2px;
|
|
475
|
-
flex: none;
|
|
476
|
-
}
|
|
477
|
-
.fmr-swatch.line {
|
|
478
|
-
width: 12px;
|
|
479
|
-
height: 2px;
|
|
480
|
-
border-radius: 0;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/* Table: no zebra, no vertical rules. */
|
|
484
|
-
table.fmr-table {
|
|
485
|
-
width: 100%;
|
|
486
|
-
border-collapse: collapse;
|
|
487
|
-
}
|
|
488
|
-
table.fmr-table th {
|
|
489
|
-
padding: 0 12px 10px;
|
|
490
|
-
text-align: left;
|
|
491
|
-
font-size: 14px;
|
|
492
|
-
font-weight: 400;
|
|
493
|
-
color: var(--fm-muted);
|
|
494
|
-
white-space: nowrap;
|
|
495
|
-
}
|
|
496
|
-
table.fmr-table td {
|
|
497
|
-
height: 52px;
|
|
498
|
-
padding: 7px 12px;
|
|
499
|
-
border-top: 1px solid var(--fm-line);
|
|
500
|
-
font-size: 14px;
|
|
501
|
-
color: var(--fm-fg);
|
|
502
|
-
vertical-align: middle;
|
|
503
|
-
}
|
|
504
|
-
table.fmr-table th:first-child,
|
|
505
|
-
table.fmr-table td:first-child {
|
|
506
|
-
padding-left: 20px;
|
|
507
|
-
}
|
|
508
|
-
table.fmr-table th:last-child,
|
|
509
|
-
table.fmr-table td:last-child {
|
|
510
|
-
padding-right: 20px;
|
|
511
|
-
}
|
|
512
|
-
table.fmr-table td:first-child {
|
|
513
|
-
font-weight: 600;
|
|
514
|
-
}
|
|
515
|
-
.fmr-num {
|
|
516
|
-
text-align: right;
|
|
517
|
-
font-variant-numeric: tabular-nums;
|
|
518
|
-
}
|
|
519
|
-
.fmr-dim {
|
|
520
|
-
color: var(--fm-muted);
|
|
521
|
-
}
|
|
522
|
-
/* the th/td rules above outrank a bare class, so the table needs its own. */
|
|
523
|
-
table.fmr-table th.fmr-num,
|
|
524
|
-
table.fmr-table td.fmr-num {
|
|
525
|
-
text-align: right;
|
|
526
|
-
}
|
|
527
|
-
table.fmr-table td.fmr-dim {
|
|
528
|
-
color: var(--fm-muted);
|
|
529
|
-
}
|
|
530
|
-
table.fmr-table td.fmr-empty {
|
|
531
|
-
font-weight: 400;
|
|
532
|
-
color: var(--fm-muted);
|
|
533
|
-
}
|
|
534
|
-
.fmr-scroll-x {
|
|
535
|
-
overflow-x: auto;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/* Pager: a table's footer. fmr-tile-foot gives it the frame, this adds the two buttons. */
|
|
539
|
-
.fmr-pager[hidden] {
|
|
540
|
-
display: none;
|
|
541
|
-
}
|
|
542
|
-
.fmr-pager-btns {
|
|
543
|
-
display: flex;
|
|
544
|
-
align-items: center;
|
|
545
|
-
gap: 16px;
|
|
546
|
-
}
|
|
547
|
-
.fmr-pager-btns .fmr-link[disabled] {
|
|
548
|
-
color: var(--fm-faint);
|
|
549
|
-
cursor: default;
|
|
550
|
-
text-decoration: none;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/* Tabs — orange marks the active one, the only place a report uses the signature. */
|
|
554
|
-
.fmr-tabs {
|
|
555
|
-
display: flex;
|
|
556
|
-
gap: 4px;
|
|
557
|
-
border-bottom: 1px solid var(--fm-line);
|
|
558
|
-
padding: 0 20px;
|
|
559
|
-
}
|
|
560
|
-
.fmr-tab {
|
|
561
|
-
border: 0;
|
|
562
|
-
background: none;
|
|
563
|
-
padding: 12px 16px;
|
|
564
|
-
font: inherit;
|
|
565
|
-
font-size: 14px;
|
|
566
|
-
font-weight: 600;
|
|
567
|
-
color: var(--fm-muted);
|
|
568
|
-
cursor: pointer;
|
|
569
|
-
border-bottom: 2px solid transparent;
|
|
570
|
-
margin-bottom: -1px;
|
|
571
|
-
}
|
|
572
|
-
.fmr-tab[aria-selected="true"] {
|
|
573
|
-
color: var(--fm-signature);
|
|
574
|
-
border-bottom-color: var(--fm-signature);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/* Status pairs — the dark tone on its own pale ground, never a brand tint. */
|
|
578
|
-
.fmr-badge {
|
|
579
|
-
display: inline-block;
|
|
580
|
-
border-radius: 999px;
|
|
581
|
-
padding: 3px 8px;
|
|
582
|
-
font-size: 12px;
|
|
583
|
-
font-weight: 600;
|
|
584
|
-
}
|
|
585
|
-
.fmr-badge.success { color: var(--fm-success); background: var(--fm-success-bg); }
|
|
586
|
-
.fmr-badge.warning { color: var(--fm-warning); background: var(--fm-warning-bg); }
|
|
587
|
-
.fmr-badge.error { color: var(--fm-error); background: var(--fm-error-bg); }
|
|
588
|
-
.fmr-badge.info { color: var(--fm-info); background: var(--fm-info-bg); }
|
|
589
|
-
|
|
590
|
-
.fmr-link {
|
|
591
|
-
border: 0;
|
|
592
|
-
background: none;
|
|
593
|
-
padding: 0;
|
|
594
|
-
font: inherit;
|
|
595
|
-
font-weight: 600;
|
|
596
|
-
color: var(--fm-info);
|
|
597
|
-
cursor: pointer;
|
|
598
|
-
text-decoration: underline;
|
|
599
|
-
text-decoration-color: transparent;
|
|
600
|
-
text-underline-offset: 2px;
|
|
601
|
-
}
|
|
602
|
-
.fmr-link:hover,
|
|
603
|
-
.fmr-link:focus-visible {
|
|
604
|
-
text-decoration-color: currentColor;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/* A chart canvas needs a height its parent does not give it. */
|
|
608
|
-
.fmr-chart {
|
|
609
|
-
position: relative;
|
|
610
|
-
height: 240px;
|
|
611
|
-
}
|
|
612
|
-
.fmr-chart.sm { height: 180px; }
|
|
613
|
-
.fmr-chart.lg { height: 320px; }
|
|
614
|
-
|
|
615
|
-
/* The two states the shell does not paint. */
|
|
616
|
-
.fmr-note {
|
|
617
|
-
margin: 0 20px 16px;
|
|
618
|
-
padding: 8px 0;
|
|
619
|
-
font-size: 14px;
|
|
620
|
-
color: var(--fm-muted);
|
|
621
|
-
}
|
|
622
|
-
.fmr-note.error {
|
|
623
|
-
border-radius: var(--fm-radius-sm);
|
|
624
|
-
padding: 11px 12px;
|
|
625
|
-
color: var(--fm-error);
|
|
626
|
-
background: var(--fm-error-bg);
|
|
627
|
-
}
|
|
628
|
-
.fm-section-error {
|
|
629
|
-
border: 1px solid var(--fm-line);
|
|
630
|
-
border-left: 3px solid var(--fm-danger);
|
|
631
|
-
background: var(--fm-danger-bg);
|
|
632
|
-
border-radius: 6px;
|
|
633
|
-
padding: 12px 14px;
|
|
634
|
-
}
|
|
635
|
-
.fm-section-error b {
|
|
636
|
-
color: var(--fm-danger);
|
|
637
|
-
}
|
|
638
|
-
.fm-section-error pre {
|
|
639
|
-
margin: 6px 0 0;
|
|
640
|
-
white-space: pre-wrap;
|
|
641
|
-
font-size: 12px;
|
|
642
|
-
color: var(--fm-muted);
|
|
643
|
-
}
|
|
644
|
-
.fm-placeholder {
|
|
645
|
-
color: var(--fm-muted);
|
|
646
|
-
padding: 24px 16px;
|
|
647
|
-
}
|
|
648
|
-
/* Loading belongs to the shell: a section declares its queries and never draws a skeleton. */
|
|
649
|
-
#fm-progress {
|
|
650
|
-
position: fixed;
|
|
651
|
-
inset: 0 0 auto;
|
|
652
|
-
z-index: 30;
|
|
653
|
-
height: 2px;
|
|
654
|
-
overflow: hidden;
|
|
655
|
-
background: var(--fm-signature-tint);
|
|
656
|
-
opacity: 0;
|
|
657
|
-
transition: opacity 0.15s linear;
|
|
658
|
-
pointer-events: none;
|
|
659
|
-
}
|
|
660
|
-
#fm-progress[data-on] {
|
|
661
|
-
opacity: 1;
|
|
662
|
-
}
|
|
663
|
-
#fm-progress i {
|
|
664
|
-
display: block;
|
|
665
|
-
width: 32%;
|
|
666
|
-
height: 100%;
|
|
667
|
-
background: var(--fm-signature);
|
|
668
|
-
}
|
|
669
|
-
section[data-fm-busy] {
|
|
670
|
-
position: relative;
|
|
671
|
-
min-height: 48px;
|
|
672
|
-
}
|
|
673
|
-
/* A scrim, not a replacement: the designed panel stays put and stale values stay clickable. */
|
|
674
|
-
section[data-fm-busy]::after {
|
|
675
|
-
content: "";
|
|
676
|
-
position: absolute;
|
|
677
|
-
inset: -4px;
|
|
678
|
-
border-radius: 10px;
|
|
679
|
-
background: rgba(255, 255, 255, 0.62);
|
|
680
|
-
pointer-events: none;
|
|
681
|
-
}
|
|
682
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
683
|
-
#fm-progress i {
|
|
684
|
-
animation: fm-slide 1.1s linear infinite;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
@keyframes fm-slide {
|
|
688
|
-
from {
|
|
689
|
-
transform: translateX(-100%);
|
|
690
|
-
}
|
|
691
|
-
to {
|
|
692
|
-
transform: translateX(320%);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
.fm-text h1,
|
|
696
|
-
.fm-text h2,
|
|
697
|
-
.fm-text h3 {
|
|
698
|
-
margin: 0 0 8px;
|
|
699
|
-
line-height: 1.3;
|
|
700
|
-
}
|
|
701
|
-
.fm-text p {
|
|
702
|
-
margin: 0 0 8px;
|
|
703
|
-
}
|
|
704
|
-
.fm-text code {
|
|
705
|
-
background: #f0f0f0;
|
|
706
|
-
border-radius: 3px;
|
|
707
|
-
padding: 1px 4px;
|
|
708
|
-
font-size: 12px;
|
|
709
|
-
}
|
|
710
|
-
</style>
|
|
711
|
-
|
|
712
|
-
<div id="fm-progress" data-on aria-hidden="true"><i></i></div>
|
|
713
|
-
<div id="report" aria-busy="true"></div>
|
|
714
|
-
|
|
715
|
-
<script>
|
|
716
|
-
;(function () {
|
|
717
|
-
"use strict"
|
|
718
|
-
|
|
719
|
-
var root = document.getElementById("report")
|
|
720
|
-
|
|
721
|
-
var env = {}
|
|
722
|
-
var params = {}
|
|
723
|
-
var results = Object.create(null) // queryId -> { rows, status, runId, truncated, error }
|
|
724
|
-
var subs = Object.create(null) // queryId -> [cb]
|
|
725
|
-
var loadedLibs = Object.create(null)
|
|
726
|
-
var mounted = Object.create(null) // sectionId -> container element
|
|
727
|
-
var versions = Object.create(null) // sectionId -> mounted source version
|
|
728
|
-
var failed = Object.create(null) // sectionId -> true once this mount reported an error
|
|
729
|
-
var order = [] // section ids, render order
|
|
730
|
-
var mounting = null // section id currently executing, for window.onerror attribution
|
|
731
|
-
var declared = Object.create(null) // sectionId -> the query ids that section reads
|
|
732
|
-
var progress = document.getElementById("fm-progress")
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
* Section code shares this window and can call parent.postMessage itself, so a render
|
|
736
|
-
* outcome carries a nonce only this closure holds — never on \`window\`, never in the DOM.
|
|
737
|
-
* The host learns it from \`ready\` and drops any ack without it.
|
|
738
|
-
*/
|
|
739
|
-
var NONCE = (function () {
|
|
740
|
-
try {
|
|
741
|
-
var buffer = new Uint32Array(4)
|
|
742
|
-
crypto.getRandomValues(buffer)
|
|
743
|
-
return Array.prototype.map
|
|
744
|
-
.call(buffer, function (value) {
|
|
745
|
-
return value.toString(36)
|
|
746
|
-
})
|
|
747
|
-
.join("")
|
|
748
|
-
} catch (err) {
|
|
749
|
-
return String(Math.random()).slice(2) + String(new Date().getTime())
|
|
750
|
-
}
|
|
751
|
-
})()
|
|
752
|
-
|
|
753
|
-
function post(message) {
|
|
754
|
-
// Opaque origins cannot be named, so "*" is the only usable target. The host
|
|
755
|
-
// authenticates us by comparing event.source to its own iframe.contentWindow.
|
|
756
|
-
parent.postMessage(message, "*")
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
/** Only the shell may claim a section rendered or threw. */
|
|
760
|
-
function postOutcome(message) {
|
|
761
|
-
message.nonce = NONCE
|
|
762
|
-
post(message)
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function reportError(sectionId, err) {
|
|
766
|
-
var message = err && err.message ? String(err.message) : String(err)
|
|
767
|
-
var stack = err && err.stack ? String(err.stack).slice(0, 4000) : undefined
|
|
768
|
-
failed[sectionId] = true
|
|
769
|
-
renderError(sectionId, message)
|
|
770
|
-
postOutcome({
|
|
771
|
-
type: "error",
|
|
772
|
-
sectionId: sectionId,
|
|
773
|
-
version: versions[sectionId],
|
|
774
|
-
message: message,
|
|
775
|
-
stack: stack
|
|
776
|
-
})
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function renderError(sectionId, message) {
|
|
780
|
-
var el = mounted[sectionId]
|
|
781
|
-
if (!el) return
|
|
782
|
-
el.innerHTML = ""
|
|
783
|
-
var box = document.createElement("div")
|
|
784
|
-
box.className = "fm-section-error"
|
|
785
|
-
var title = document.createElement("b")
|
|
786
|
-
title.textContent = "This part of the report could not be shown"
|
|
787
|
-
var detail = document.createElement("pre")
|
|
788
|
-
detail.textContent = message
|
|
789
|
-
box.appendChild(title)
|
|
790
|
-
box.appendChild(detail)
|
|
791
|
-
el.appendChild(box)
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
// --- markdown (text sections): escape first, then a small safe subset
|
|
795
|
-
|
|
796
|
-
function escapeHtml(text) {
|
|
797
|
-
return text
|
|
798
|
-
.replace(/&/g, "&")
|
|
799
|
-
.replace(/</g, "<")
|
|
800
|
-
.replace(/>/g, ">")
|
|
801
|
-
.replace(/"/g, """)
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
function renderMarkdown(source) {
|
|
805
|
-
var lines = escapeHtml(String(source || "")).split(/\\r?\\n/)
|
|
806
|
-
var out = []
|
|
807
|
-
var list = null
|
|
808
|
-
function closeList() {
|
|
809
|
-
if (list) {
|
|
810
|
-
out.push("<" + list.tag + ">" + list.items.join("") + "</" + list.tag + ">")
|
|
811
|
-
list = null
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
for (var i = 0; i < lines.length; i++) {
|
|
815
|
-
var line = lines[i]
|
|
816
|
-
var heading = /^(#{1,3})\\s+(.*)$/.exec(line)
|
|
817
|
-
var bullet = /^\\s*[-*]\\s+(.*)$/.exec(line)
|
|
818
|
-
var numbered = /^\\s*\\d+\\.\\s+(.*)$/.exec(line)
|
|
819
|
-
if (heading) {
|
|
820
|
-
closeList()
|
|
821
|
-
var level = heading[1].length
|
|
822
|
-
out.push("<h" + level + ">" + inline(heading[2]) + "</h" + level + ">")
|
|
823
|
-
} else if (bullet || numbered) {
|
|
824
|
-
var tag = bullet ? "ul" : "ol"
|
|
825
|
-
if (!list || list.tag !== tag) {
|
|
826
|
-
closeList()
|
|
827
|
-
list = { tag: tag, items: [] }
|
|
828
|
-
}
|
|
829
|
-
list.items.push("<li>" + inline((bullet || numbered)[1]) + "</li>")
|
|
830
|
-
} else if (!line.trim()) {
|
|
831
|
-
closeList()
|
|
832
|
-
} else {
|
|
833
|
-
closeList()
|
|
834
|
-
out.push("<p>" + inline(line) + "</p>")
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
closeList()
|
|
838
|
-
return out.join("")
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
function inline(text) {
|
|
842
|
-
return text
|
|
843
|
-
.replace(/\`([^\`]+)\`/g, "<code>$1</code>")
|
|
844
|
-
.replace(/\\*\\*([^*]+)\\*\\*/g, "<strong>$1</strong>")
|
|
845
|
-
.replace(/(^|[^*])\\*([^*]+)\\*/g, "$1<em>$2</em>")
|
|
846
|
-
.replace(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^)\\s]+|mailto:[^)\\s]+)\\)/g, function (_all, label, href) {
|
|
847
|
-
return '<a href="' + href + '" target="_blank" rel="noreferrer noopener">' + label + "</a>"
|
|
848
|
-
})
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// --- libs
|
|
852
|
-
|
|
853
|
-
function installLibs(bundles) {
|
|
854
|
-
if (!bundles) return
|
|
855
|
-
Object.keys(bundles).forEach(function (key) {
|
|
856
|
-
if (loadedLibs[key]) return
|
|
857
|
-
var script = document.createElement("script")
|
|
858
|
-
script.textContent = bundles[key]
|
|
859
|
-
document.head.appendChild(script)
|
|
860
|
-
loadedLibs[key] = true
|
|
861
|
-
})
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// --- sections
|
|
865
|
-
|
|
866
|
-
function container(sectionId) {
|
|
867
|
-
var el = mounted[sectionId]
|
|
868
|
-
if (el) {
|
|
869
|
-
el.innerHTML = ""
|
|
870
|
-
return el
|
|
871
|
-
}
|
|
872
|
-
el = document.createElement("section")
|
|
873
|
-
el.id = sectionId
|
|
874
|
-
el.dataset.sectionId = sectionId
|
|
875
|
-
mounted[sectionId] = el
|
|
876
|
-
return el
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
/**
|
|
880
|
-
* Sections share one DOM and one CSS cascade on purpose, but top-level JS is wrapped
|
|
881
|
-
* so two sections cannot collide on a \`const\` — they share state through \`window\`.
|
|
882
|
-
* Always attempts the persisted source, even one saved as \`status: "error"\`: that is
|
|
883
|
-
* what "restore last good source" means, and a genuinely broken one throws and re-acks.
|
|
884
|
-
*/
|
|
885
|
-
function mountSection(section) {
|
|
886
|
-
var el = container(section.id)
|
|
887
|
-
versions[section.id] = section.version
|
|
888
|
-
failed[section.id] = false
|
|
889
|
-
declared[section.id] = (section.queryIds || []).slice()
|
|
890
|
-
mounting = section.id
|
|
891
|
-
try {
|
|
892
|
-
if (section.kind === "text") {
|
|
893
|
-
var text = document.createElement("div")
|
|
894
|
-
text.className = "fm-text"
|
|
895
|
-
text.innerHTML = renderMarkdown(section.source)
|
|
896
|
-
el.appendChild(text)
|
|
897
|
-
return
|
|
898
|
-
}
|
|
899
|
-
var doc = new DOMParser().parseFromString(
|
|
900
|
-
"<body>" + String(section.source || "") + "</body>",
|
|
901
|
-
"text/html"
|
|
902
|
-
)
|
|
903
|
-
var scripts = []
|
|
904
|
-
Array.prototype.slice.call(doc.body.childNodes).forEach(function (node) {
|
|
905
|
-
if (node.tagName === "SCRIPT") {
|
|
906
|
-
scripts.push(node.textContent || "")
|
|
907
|
-
} else {
|
|
908
|
-
el.appendChild(document.importNode(node, true))
|
|
909
|
-
}
|
|
910
|
-
})
|
|
911
|
-
// Inline scripts throw past the caller's try/catch, so wrap the source itself.
|
|
912
|
-
scripts.forEach(function (code, index) {
|
|
913
|
-
var tag = document.createElement("script")
|
|
914
|
-
tag.textContent = "try{(function(){\\n" + code + "\\n})()}catch(e){window.FM.__err(e)}"
|
|
915
|
-
el.appendChild(tag)
|
|
916
|
-
void index
|
|
917
|
-
})
|
|
918
|
-
} catch (err) {
|
|
919
|
-
reportError(section.id, err)
|
|
920
|
-
} finally {
|
|
921
|
-
mounting = null
|
|
922
|
-
if (!failed[section.id]) {
|
|
923
|
-
postOutcome({ type: "rendered", sectionId: section.id, version: section.version })
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
function unmountSection(sectionId) {
|
|
929
|
-
var el = mounted[sectionId]
|
|
930
|
-
if (el && el.parentNode) el.parentNode.removeChild(el)
|
|
931
|
-
delete mounted[sectionId]
|
|
932
|
-
delete versions[sectionId]
|
|
933
|
-
delete failed[sectionId]
|
|
934
|
-
delete declared[sectionId]
|
|
935
|
-
Object.keys(subs).forEach(function (queryId) {
|
|
936
|
-
subs[queryId] = subs[queryId].filter(function (entry) {
|
|
937
|
-
return entry.sectionId !== sectionId
|
|
938
|
-
})
|
|
939
|
-
})
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
function renderAll(sections) {
|
|
943
|
-
root.innerHTML = ""
|
|
944
|
-
mounted = Object.create(null)
|
|
945
|
-
versions = Object.create(null)
|
|
946
|
-
failed = Object.create(null)
|
|
947
|
-
declared = Object.create(null)
|
|
948
|
-
subs = Object.create(null)
|
|
949
|
-
order = sections.map(function (section) {
|
|
950
|
-
return section.id
|
|
951
|
-
})
|
|
952
|
-
sections.forEach(function (section) {
|
|
953
|
-
var el = container(section.id)
|
|
954
|
-
root.appendChild(el)
|
|
955
|
-
mountSection(section)
|
|
956
|
-
})
|
|
957
|
-
if (sections.length === 0) {
|
|
958
|
-
var empty = document.createElement("div")
|
|
959
|
-
empty.className = "fm-placeholder"
|
|
960
|
-
empty.textContent = "This report is empty. Ask in the chat to build it."
|
|
961
|
-
root.appendChild(empty)
|
|
962
|
-
}
|
|
963
|
-
root.removeAttribute("aria-busy")
|
|
964
|
-
paintBusy()
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
/** Hot-swap: replace only the named sections, appending any that are new. */
|
|
968
|
-
function swapSections(sections) {
|
|
969
|
-
sections.forEach(function (section) {
|
|
970
|
-
var existing = mounted[section.id]
|
|
971
|
-
if (existing) {
|
|
972
|
-
unmountSection(section.id)
|
|
973
|
-
var replacement = container(section.id)
|
|
974
|
-
root.insertBefore(replacement, positionAfter(section.id))
|
|
975
|
-
mountSection(section)
|
|
976
|
-
return
|
|
977
|
-
}
|
|
978
|
-
order.push(section.id)
|
|
979
|
-
var el = container(section.id)
|
|
980
|
-
root.appendChild(el)
|
|
981
|
-
mountSection(section)
|
|
982
|
-
})
|
|
983
|
-
paintBusy()
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
function positionAfter(sectionId) {
|
|
987
|
-
var index = order.indexOf(sectionId)
|
|
988
|
-
for (var i = index + 1; i < order.length; i++) {
|
|
989
|
-
if (mounted[order[i]]) return mounted[order[i]]
|
|
990
|
-
}
|
|
991
|
-
return null
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
function notify(queryId) {
|
|
995
|
-
var list = subs[queryId] || []
|
|
996
|
-
list.slice().forEach(function (entry) {
|
|
997
|
-
try {
|
|
998
|
-
entry.cb(results[queryId] ? results[queryId].rows : [])
|
|
999
|
-
} catch (err) {
|
|
1000
|
-
reportError(entry.sectionId, err)
|
|
1001
|
-
}
|
|
1002
|
-
})
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
// --- loading
|
|
1006
|
-
|
|
1007
|
-
/**
|
|
1008
|
-
* Every report gets the same loading affordance, so no section hand-rolls one: a scrim
|
|
1009
|
-
* over each section whose declared query is in flight, plus one bar for the report.
|
|
1010
|
-
* Only \`loading\` counts — an idle lazy query keeps the empty state it was designed with,
|
|
1011
|
-
* and a query the shell has not heard about yet is not assumed to be running.
|
|
1012
|
-
*/
|
|
1013
|
-
function paintBusy() {
|
|
1014
|
-
Object.keys(mounted).forEach(function (sectionId) {
|
|
1015
|
-
var el = mounted[sectionId]
|
|
1016
|
-
var busy = !failed[sectionId] && (declared[sectionId] || []).some(isRunning)
|
|
1017
|
-
if (busy) {
|
|
1018
|
-
el.setAttribute("data-fm-busy", "")
|
|
1019
|
-
el.setAttribute("aria-busy", "true")
|
|
1020
|
-
} else {
|
|
1021
|
-
el.removeAttribute("data-fm-busy")
|
|
1022
|
-
el.removeAttribute("aria-busy")
|
|
1023
|
-
}
|
|
1024
|
-
})
|
|
1025
|
-
if (progress) {
|
|
1026
|
-
if (Object.keys(results).some(isRunning)) progress.setAttribute("data-on", "")
|
|
1027
|
-
else progress.removeAttribute("data-on")
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
function isRunning(queryId) {
|
|
1032
|
-
var result = results[queryId]
|
|
1033
|
-
return !!result && result.status === "loading"
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
// --- FM: the only API section code may use (see the agent system prompt)
|
|
1037
|
-
|
|
1038
|
-
window.env = env
|
|
1039
|
-
window.FM = {
|
|
1040
|
-
rows: function (queryId) {
|
|
1041
|
-
var result = results[queryId]
|
|
1042
|
-
return result && result.rows ? result.rows : []
|
|
1043
|
-
},
|
|
1044
|
-
status: function (queryId) {
|
|
1045
|
-
var result = results[queryId]
|
|
1046
|
-
return result ? result.status : "idle"
|
|
1047
|
-
},
|
|
1048
|
-
onRows: function (queryId, cb) {
|
|
1049
|
-
if (typeof cb !== "function") return
|
|
1050
|
-
if (!subs[queryId]) subs[queryId] = []
|
|
1051
|
-
subs[queryId].push({ sectionId: mounting, cb: cb })
|
|
1052
|
-
// fire immediately so a section renders its current state without waiting
|
|
1053
|
-
try {
|
|
1054
|
-
cb(window.FM.rows(queryId))
|
|
1055
|
-
} catch (err) {
|
|
1056
|
-
reportError(mounting, err)
|
|
1057
|
-
}
|
|
1058
|
-
},
|
|
1059
|
-
setParams: function (values) {
|
|
1060
|
-
if (!values || typeof values !== "object") return
|
|
1061
|
-
post({ type: "setParams", values: values })
|
|
1062
|
-
},
|
|
1063
|
-
params: function () {
|
|
1064
|
-
return JSON.parse(JSON.stringify(params))
|
|
1065
|
-
},
|
|
1066
|
-
/** Takes no section id on purpose: the shell attributes the failure to what it is mounting. */
|
|
1067
|
-
__err: function (err) {
|
|
1068
|
-
if (mounting) reportError(mounting, err)
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
window.addEventListener("error", function (event) {
|
|
1073
|
-
if (!mounting) return
|
|
1074
|
-
reportError(mounting, event.error || new Error(event.message))
|
|
1075
|
-
})
|
|
1076
|
-
window.addEventListener("unhandledrejection", function (event) {
|
|
1077
|
-
if (!mounting) return
|
|
1078
|
-
reportError(mounting, event.reason || new Error("unhandled rejection"))
|
|
1079
|
-
})
|
|
1080
|
-
|
|
1081
|
-
// --- host channel
|
|
1082
|
-
|
|
1083
|
-
window.addEventListener("message", function (event) {
|
|
1084
|
-
// the host is the only window that can reach us; anything else is not ours
|
|
1085
|
-
if (event.source !== window.parent) return
|
|
1086
|
-
var data = event.data
|
|
1087
|
-
if (!data || typeof data !== "object") return
|
|
1088
|
-
switch (data.type) {
|
|
1089
|
-
case "init":
|
|
1090
|
-
env = data.env || {}
|
|
1091
|
-
window.env = env
|
|
1092
|
-
params = data.params || {}
|
|
1093
|
-
installLibs(data.libs)
|
|
1094
|
-
renderAll(data.sections || [])
|
|
1095
|
-
break
|
|
1096
|
-
case "sections":
|
|
1097
|
-
installLibs(data.libs)
|
|
1098
|
-
swapSections(data.sections || [])
|
|
1099
|
-
break
|
|
1100
|
-
case "params":
|
|
1101
|
-
params = data.values || {}
|
|
1102
|
-
Object.keys(subs).forEach(notify)
|
|
1103
|
-
break
|
|
1104
|
-
// Mutated in place, never replaced: sections captured \`env\` at mount and a new
|
|
1105
|
-
// object would leave every one of them reading the empty first snapshot.
|
|
1106
|
-
case "env":
|
|
1107
|
-
Object.keys(env).forEach(function (key) { delete env[key] })
|
|
1108
|
-
Object.keys(data.env || {}).forEach(function (key) { env[key] = data.env[key] })
|
|
1109
|
-
Object.keys(subs).forEach(notify)
|
|
1110
|
-
break
|
|
1111
|
-
case "rows":
|
|
1112
|
-
var previous = results[data.queryId]
|
|
1113
|
-
if (previous && previous.runId > data.runId) return // superseded run
|
|
1114
|
-
results[data.queryId] = {
|
|
1115
|
-
rows: data.rows || [],
|
|
1116
|
-
status: data.status,
|
|
1117
|
-
runId: data.runId,
|
|
1118
|
-
truncated: !!data.truncated,
|
|
1119
|
-
error: data.error
|
|
1120
|
-
}
|
|
1121
|
-
notify(data.queryId)
|
|
1122
|
-
paintBusy()
|
|
1123
|
-
break
|
|
1124
|
-
}
|
|
1125
|
-
})
|
|
1126
|
-
|
|
1127
|
-
post({ type: "ready", nonce: NONCE })
|
|
1128
|
-
})()
|
|
1129
|
-
<\/script>
|
|
1130
|
-
`, Rn = On, Q = 8e3;
|
|
1131
|
-
function Cn(r, c) {
|
|
1132
|
-
const l = q(!1);
|
|
1133
|
-
let s = null;
|
|
1134
|
-
const p = /* @__PURE__ */ new Map();
|
|
1135
|
-
let g;
|
|
1136
|
-
const I = /* @__PURE__ */ new Map();
|
|
1137
|
-
function o() {
|
|
1138
|
-
var e;
|
|
1139
|
-
return ((e = r.value) == null ? void 0 : e.contentWindow) ?? null;
|
|
1140
|
-
}
|
|
1141
|
-
function i(e) {
|
|
1142
|
-
var u;
|
|
1143
|
-
(u = o()) == null || u.postMessage(JSON.parse(JSON.stringify(e)), "*");
|
|
1144
|
-
}
|
|
1145
|
-
function w(e) {
|
|
1146
|
-
const u = o();
|
|
1147
|
-
if (!u || e.source !== u) return;
|
|
1148
|
-
const n = e.data;
|
|
1149
|
-
if (!(!n || typeof n != "object"))
|
|
1150
|
-
switch (n.type) {
|
|
1151
|
-
case "ready":
|
|
1152
|
-
if (l.value) return;
|
|
1153
|
-
g = n.nonce, l.value = !0, s == null || s(), s = null, A();
|
|
1154
|
-
break;
|
|
1155
|
-
case "setParams":
|
|
1156
|
-
c.onSetParams(n.values ?? {});
|
|
1157
|
-
break;
|
|
1158
|
-
case "rendered":
|
|
1159
|
-
if (!S(n)) return;
|
|
1160
|
-
h(n.sectionId, n.version), c.onSectionRendered(n.sectionId, n.version);
|
|
1161
|
-
break;
|
|
1162
|
-
case "error":
|
|
1163
|
-
if (!S(n)) return;
|
|
1164
|
-
h(n.sectionId, n.version), c.onSectionError(n.sectionId, n.version, n.message);
|
|
1165
|
-
break;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
function S(e) {
|
|
1169
|
-
return !g || e.nonce === g ? !0 : (console.warn("[report-canvas] dropped a render outcome without the shell nonce"), !1);
|
|
1170
|
-
}
|
|
1171
|
-
function y(e, u) {
|
|
1172
|
-
return `${e}@${u}`;
|
|
1173
|
-
}
|
|
1174
|
-
function h(e, u) {
|
|
1175
|
-
if (u === void 0) return;
|
|
1176
|
-
const n = y(e, u), t = I.get(n);
|
|
1177
|
-
t !== void 0 && (clearTimeout(t), I.delete(n));
|
|
1178
|
-
}
|
|
1179
|
-
function C() {
|
|
1180
|
-
I.forEach((e) => clearTimeout(e)), I.clear();
|
|
1181
|
-
}
|
|
1182
|
-
function O(e) {
|
|
1183
|
-
for (const u of e) {
|
|
1184
|
-
const n = y(u.id, u.version);
|
|
1185
|
-
I.has(n) || I.set(
|
|
1186
|
-
n,
|
|
1187
|
-
setTimeout(() => {
|
|
1188
|
-
I.delete(n), c.onSectionError(
|
|
1189
|
-
u.id,
|
|
1190
|
-
u.version,
|
|
1191
|
-
`the section did not report a render within ${Q}ms`
|
|
1192
|
-
);
|
|
1193
|
-
}, Q)
|
|
1194
|
-
);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
async function _(e, u, n) {
|
|
1198
|
-
const t = await Y(e.flatMap((k) => k.libs ?? [])), m = () => {
|
|
1199
|
-
i({ type: "init", sections: e, params: u, libs: t, env: n }), C(), O(e);
|
|
1200
|
-
};
|
|
1201
|
-
l.value ? m() : s = m;
|
|
1202
|
-
}
|
|
1203
|
-
async function a(e) {
|
|
1204
|
-
const u = await Y(e.flatMap((n) => n.libs ?? []));
|
|
1205
|
-
i({ type: "sections", sections: e, libs: u }), O(e);
|
|
1206
|
-
}
|
|
1207
|
-
function v(e) {
|
|
1208
|
-
i({ type: "params", values: e });
|
|
1209
|
-
}
|
|
1210
|
-
function x(e) {
|
|
1211
|
-
i({ type: "env", env: e });
|
|
1212
|
-
}
|
|
1213
|
-
function A() {
|
|
1214
|
-
const e = [...p.values()];
|
|
1215
|
-
p.clear(), e.forEach(E);
|
|
1216
|
-
}
|
|
1217
|
-
function E(e) {
|
|
1218
|
-
i({
|
|
1219
|
-
type: "rows",
|
|
1220
|
-
queryId: e.queryId,
|
|
1221
|
-
runId: e.runId,
|
|
1222
|
-
rows: e.rows,
|
|
1223
|
-
status: e.status,
|
|
1224
|
-
truncated: e.truncated,
|
|
1225
|
-
error: e.error
|
|
1226
|
-
});
|
|
1227
|
-
}
|
|
1228
|
-
function R(e) {
|
|
1229
|
-
if (!l.value) {
|
|
1230
|
-
p.set(e.queryId, e);
|
|
1231
|
-
return;
|
|
1232
|
-
}
|
|
1233
|
-
E(e);
|
|
1234
|
-
}
|
|
1235
|
-
W(() => window.addEventListener("message", w)), tn(() => {
|
|
1236
|
-
window.removeEventListener("message", w), C();
|
|
1237
|
-
});
|
|
1238
|
-
function M() {
|
|
1239
|
-
l.value = !1, s = null, g = void 0, p.clear(), C();
|
|
1240
|
-
}
|
|
1241
|
-
return { ready: l, init: _, swapSections: a, pushParams: v, pushEnv: x, pushRows: R, reset: M };
|
|
1242
|
-
}
|
|
1243
|
-
const Mn = { class: "flex h-full min-h-0 w-full" }, jn = { class: "flex min-w-0 grow flex-col" }, qn = { class: "flex items-start overflow-x-auto border-b border-fm-color-border-secondary px-16 py-8 scrollbar-hide" }, _n = {
|
|
1244
|
-
key: 0,
|
|
1245
|
-
class: "fm-typo-en-body-xs-400 border-b border-fm-color-border-secondary bg-fm-color-system-warning-100 px-16 py-8"
|
|
1246
|
-
}, An = {
|
|
1247
|
-
key: 1,
|
|
1248
|
-
class: "fm-typo-en-body-sm-400 p-16 text-fm-color-typo-error"
|
|
1249
|
-
}, Nn = ["srcdoc", "title"], Hn = /* @__PURE__ */ rn({
|
|
1250
|
-
__name: "ReportCanvasView",
|
|
1251
|
-
setup(r) {
|
|
1252
|
-
const c = cn(), { t: l } = ln(), s = String(c.query.reportId ?? ""), p = q(), g = q(""), I = q(), o = In(s), { dateRange: i, selectedLocationIds: w, locations: S, timezone: y } = N(X()), h = Cn(I, {
|
|
1253
|
-
onSetParams: (a) => {
|
|
1254
|
-
o.setParams(a) && h.pushParams(o.params.value);
|
|
1255
|
-
},
|
|
1256
|
-
onSectionRendered: (a, v) => void C(a, v, "ok"),
|
|
1257
|
-
onSectionError: (a, v, x) => {
|
|
1258
|
-
console.warn("[report-canvas] section failed", { reportId: s, sectionId: a, message: x }), C(a, v, "error", x);
|
|
1259
|
-
}
|
|
1260
|
-
});
|
|
1261
|
-
o.onResult((a) => h.pushRows(a));
|
|
1262
|
-
async function C(a, v, x, A) {
|
|
1263
|
-
var M;
|
|
1264
|
-
const E = (M = p.value) == null ? void 0 : M.sections.find((e) => e.id === a);
|
|
1265
|
-
if (!E || v !== E.version) return;
|
|
1266
|
-
const R = x === "error" ? A ?? "the section failed to render" : void 0;
|
|
1267
|
-
if (!(E.status === x && E.error === R)) {
|
|
1268
|
-
E.status = x, E.error = R;
|
|
1269
|
-
try {
|
|
1270
|
-
await hn(s, a, { version: v, status: x, error: R });
|
|
1271
|
-
} catch (e) {
|
|
1272
|
-
console.warn("[report-canvas] ack failed", { reportId: s, sectionId: a, err: e });
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
async function O() {
|
|
1277
|
-
try {
|
|
1278
|
-
g.value = "";
|
|
1279
|
-
const a = await pn(s);
|
|
1280
|
-
p.value = a, o.load(a), await h.init(a.sections, o.params.value, o.env());
|
|
1281
|
-
} catch (a) {
|
|
1282
|
-
g.value = a instanceof Error ? a.message : String(a);
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
H([i, w, S], () => {
|
|
1286
|
-
if (!p.value) return;
|
|
1287
|
-
o.setParams({
|
|
1288
|
-
"@from": i.value.startDate ?? null,
|
|
1289
|
-
"@to": i.value.endDate ?? null,
|
|
1290
|
-
"@locationIds": w.value.length ? [...w.value] : S.value.map((v) => v.id)
|
|
1291
|
-
}) && h.pushParams(o.params.value);
|
|
1292
|
-
}), H([S, y], () => {
|
|
1293
|
-
p.value && h.pushEnv(o.env());
|
|
1294
|
-
});
|
|
1295
|
-
const _ = on(
|
|
1296
|
-
() => Object.values(o.results.value).some((a) => a.truncated)
|
|
1297
|
-
);
|
|
1298
|
-
return W(async () => {
|
|
1299
|
-
await O();
|
|
1300
|
-
}), (a, v) => (L(), T("div", Mn, [
|
|
1301
|
-
P("div", jn, [
|
|
1302
|
-
P("div", qn, [
|
|
1303
|
-
V(un, {
|
|
1304
|
-
modelValue: j(w),
|
|
1305
|
-
"onUpdate:modelValue": v[1] || (v[1] = (x) => F(w) ? w.value = x : null),
|
|
1306
|
-
options: j(S)
|
|
1307
|
-
}, {
|
|
1308
|
-
default: sn(() => [
|
|
1309
|
-
V(fn, {
|
|
1310
|
-
"date-range": j(i),
|
|
1311
|
-
"onUpdate:dateRange": v[0] || (v[0] = (x) => F(i) ? i.value = x : null)
|
|
1312
|
-
}, null, 8, ["date-range"])
|
|
1313
|
-
]),
|
|
1314
|
-
_: 1
|
|
1315
|
-
}, 8, ["modelValue", "options"])
|
|
1316
|
-
]),
|
|
1317
|
-
_.value ? (L(), T("div", _n, J(j(l)("reportV5.reportCanvas.canvas.truncated")), 1)) : U("", !0),
|
|
1318
|
-
g.value ? (L(), T("p", An, J(g.value), 1)) : U("", !0),
|
|
1319
|
-
an(P("iframe", {
|
|
1320
|
-
ref_key: "frame",
|
|
1321
|
-
ref: I,
|
|
1322
|
-
"data-testid": "report-canvas-frame",
|
|
1323
|
-
class: "min-h-0 grow border-0",
|
|
1324
|
-
srcdoc: j(Rn),
|
|
1325
|
-
sandbox: "allow-scripts",
|
|
1326
|
-
title: j(l)("reportV5.reportCanvas.canvas.title")
|
|
1327
|
-
}, null, 8, Nn), [
|
|
1328
|
-
[dn, !g.value]
|
|
1329
|
-
])
|
|
1330
|
-
])
|
|
1331
|
-
]));
|
|
1332
|
-
}
|
|
1333
|
-
});
|
|
1334
|
-
export {
|
|
1335
|
-
Hn as default
|
|
1336
|
-
};
|