@freereportjs/core 0.9.0 → 0.10.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/cdn/freereport.esm.js +56 -56
- package/dist/cdn/freereport.min.js +47 -47
- package/dist/index.d.ts +43 -3
- package/dist/index.js +251 -163
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -186,138 +186,6 @@ function pdfFonts(pages) {
|
|
|
186
186
|
return out;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
// src/version.ts
|
|
190
|
-
var version = "0.9.0";
|
|
191
|
-
var releaseDate = "2026-10-16";
|
|
192
|
-
|
|
193
|
-
// src/license.ts
|
|
194
|
-
var PUBLIC_KEY = { kty: "EC", crv: "P-256", x: "MiJXJR8TZJ_-0VKk0wIQ-5mzRsyQ6ruGsbcRRdw9jrw", y: "MTdklYSzCM2pganffz7PPMfxSclQkBLqU_9se_VDznM" };
|
|
195
|
-
var PRICING_URL = "https://freereport.app/pricing/";
|
|
196
|
-
var USAGE_URL = "https://freereport.app/api/usage";
|
|
197
|
-
var STATUS_URL = "https://freereport.app/api/domain-status";
|
|
198
|
-
var b64urlDecode = (s) => {
|
|
199
|
-
const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - s.length % 4) % 4));
|
|
200
|
-
return Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
201
|
-
};
|
|
202
|
-
function isDevHost(host) {
|
|
203
|
-
const h = host.toLowerCase().replace(/^\[|\]$/g, "");
|
|
204
|
-
return !h || h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0" || h.endsWith(".localhost") || h.endsWith(".test") || h.endsWith(".local") || /^(10\.\d+|192\.168|172\.(1[6-9]|2\d|3[01]))\.\d+\.\d+$/.test(h);
|
|
205
|
-
}
|
|
206
|
-
function domainMatches(host, domains) {
|
|
207
|
-
const h = host.toLowerCase().replace(/\.$/, "");
|
|
208
|
-
return domains.some((d) => {
|
|
209
|
-
const raw = d.toLowerCase().replace(/\.$/, "");
|
|
210
|
-
const wildcard = raw.startsWith("*.");
|
|
211
|
-
const dom = wildcard ? raw.slice(2) : raw;
|
|
212
|
-
if (!dom) return false;
|
|
213
|
-
if (h === dom || h === `www.${dom}`) return true;
|
|
214
|
-
return wildcard && h.endsWith(`.${dom}`);
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
var today = (now) => `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
218
|
-
async function verifyLicense(key2, host, opts = {}) {
|
|
219
|
-
const parts = typeof key2 === "string" ? key2.trim().split(".") : [];
|
|
220
|
-
if (parts.length !== 3 || parts[0] !== "FR1") return { ok: false, reason: "format" };
|
|
221
|
-
let info;
|
|
222
|
-
try {
|
|
223
|
-
info = JSON.parse(new TextDecoder().decode(b64urlDecode(parts[1])));
|
|
224
|
-
if (!Array.isArray(info.domains) || typeof info.expires !== "string") return { ok: false, reason: "format" };
|
|
225
|
-
} catch {
|
|
226
|
-
return { ok: false, reason: "format" };
|
|
227
|
-
}
|
|
228
|
-
const subtle = typeof crypto !== "undefined" ? crypto.subtle : void 0;
|
|
229
|
-
if (!subtle) return { ok: false, reason: "unsupported", info };
|
|
230
|
-
try {
|
|
231
|
-
const pub = await subtle.importKey("jwk", opts.publicKey ?? PUBLIC_KEY, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
232
|
-
const signed = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
|
|
233
|
-
const ok = await subtle.verify({ name: "ECDSA", hash: "SHA-256" }, pub, b64urlDecode(parts[2]), signed);
|
|
234
|
-
if (!ok) return { ok: false, reason: "signature", info };
|
|
235
|
-
} catch {
|
|
236
|
-
return { ok: false, reason: "signature", info };
|
|
237
|
-
}
|
|
238
|
-
if (today(opts.now ?? /* @__PURE__ */ new Date()) > info.expires) return { ok: false, reason: "expired", info };
|
|
239
|
-
if (!domainMatches(host, info.domains)) return { ok: false, reason: "domain", info };
|
|
240
|
-
if (info.updates && (opts.released ?? releaseDate) > info.updates) return { ok: false, reason: "updates", info };
|
|
241
|
-
return { ok: true, info };
|
|
242
|
-
}
|
|
243
|
-
var globalKey;
|
|
244
|
-
var statuses = /* @__PURE__ */ new Map();
|
|
245
|
-
var badgeCheck = null;
|
|
246
|
-
function setLicenseKey(key2) {
|
|
247
|
-
globalKey = key2;
|
|
248
|
-
}
|
|
249
|
-
var NOTICE = {
|
|
250
|
-
missing: `FreeReport is free for personal use. Company use needs a domain license (\u20A9500,000 per domain per year, or \u20A9900,000 perpetual): ${PRICING_URL}
|
|
251
|
-
FreeReport\uB294 \uAC1C\uC778\uC5D0\uAC8C \uBB34\uB8CC\uC785\uB2C8\uB2E4. \uD68C\uC0AC\uC5D0\uC11C \uC4F0\uB824\uBA74 \uB3C4\uBA54\uC778 \uB77C\uC774\uC120\uC2A4(\uB3C4\uBA54\uC778\uB2F9 \uC5F0 50\uB9CC \uC6D0, \uC601\uAD6C 90\uB9CC \uC6D0)\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: ${PRICING_URL}`,
|
|
252
|
-
format: "FreeReport: the license key is not in the right format. / \uB77C\uC774\uC120\uC2A4 \uD0A4 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
253
|
-
signature: "FreeReport: the license key could not be verified. / \uB77C\uC774\uC120\uC2A4 \uD0A4\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
254
|
-
expired: "FreeReport: the license key has expired. / \uB77C\uC774\uC120\uC2A4 \uAE30\uAC04\uC774 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4.",
|
|
255
|
-
domain: "FreeReport: the license key is for a different domain. / \uB2E4\uB978 \uB3C4\uBA54\uC778\uC6A9 \uB77C\uC774\uC120\uC2A4 \uD0A4\uC785\uB2C8\uB2E4.",
|
|
256
|
-
updates: "FreeReport: this version was released after the update period of your perpetual license. / \uC601\uAD6C \uB77C\uC774\uC120\uC2A4\uC758 \uC5C5\uB370\uC774\uD2B8 \uAE30\uAC04 \uB4A4\uC5D0 \uB098\uC628 \uBC84\uC804\uC785\uB2C8\uB2E4."
|
|
257
|
-
};
|
|
258
|
-
function reportUsage(info, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
259
|
-
try {
|
|
260
|
-
if (isDevHost(host) || typeof navigator === "undefined") return;
|
|
261
|
-
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
262
|
-
const mark = `${host}|${day}|${info.licensed ? 1 : 0}`;
|
|
263
|
-
const store = typeof localStorage !== "undefined" ? localStorage : null;
|
|
264
|
-
if (store?.getItem("freereport:usage") === mark) return;
|
|
265
|
-
store?.setItem("freereport:usage", mark);
|
|
266
|
-
const body = JSON.stringify({ version: info.version, licensed: info.licensed, company: info.company, url: `${location.origin}${location.pathname}` });
|
|
267
|
-
if (typeof navigator.sendBeacon === "function") navigator.sendBeacon(USAGE_URL, body);
|
|
268
|
-
else if (typeof fetch === "function") void fetch(USAGE_URL, { method: "POST", body, mode: "no-cors", keepalive: true }).catch(() => void 0);
|
|
269
|
-
} catch {
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
function checkLicense(key2 = globalKey, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
273
|
-
if (isDevHost(host)) return Promise.resolve(true);
|
|
274
|
-
const k = key2 ?? globalKey;
|
|
275
|
-
const id = `${k ?? ""}@${host}`;
|
|
276
|
-
let status = statuses.get(id);
|
|
277
|
-
if (status) return status;
|
|
278
|
-
if (!k) {
|
|
279
|
-
console.info(NOTICE.missing);
|
|
280
|
-
reportUsage({ version, licensed: false }, host);
|
|
281
|
-
status = Promise.resolve(false);
|
|
282
|
-
} else {
|
|
283
|
-
status = verifyLicense(k, host).then((res) => {
|
|
284
|
-
reportUsage({ version, licensed: res.ok, company: res.ok ? res.info.company : void 0 }, host);
|
|
285
|
-
if (res.ok || res.reason === "unsupported") return res.ok || res.reason === "unsupported";
|
|
286
|
-
const extra = res.info ? ` (${res.info.domains.join(", ")}, ${res.info.expires})` : "";
|
|
287
|
-
console.warn(`${NOTICE[res.reason]}${extra}
|
|
288
|
-
${PRICING_URL}`);
|
|
289
|
-
return false;
|
|
290
|
-
}, () => false);
|
|
291
|
-
}
|
|
292
|
-
statuses.set(id, status);
|
|
293
|
-
return status;
|
|
294
|
-
}
|
|
295
|
-
function badgeRequested(host = typeof location !== "undefined" ? location.hostname : "") {
|
|
296
|
-
if (isDevHost(host) || typeof fetch !== "function") return Promise.resolve(false);
|
|
297
|
-
if (badgeCheck) return badgeCheck;
|
|
298
|
-
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
299
|
-
let store = null;
|
|
300
|
-
try {
|
|
301
|
-
store = typeof localStorage !== "undefined" ? localStorage : null;
|
|
302
|
-
const cached = JSON.parse(store?.getItem("freereport:badge") ?? "null");
|
|
303
|
-
if (cached && cached.host === host && cached.day === day) return badgeCheck = Promise.resolve(cached.show);
|
|
304
|
-
} catch {
|
|
305
|
-
}
|
|
306
|
-
badgeCheck = fetch(STATUS_URL, { mode: "cors", credentials: "omit" }).then((r) => r.ok ? r.json() : { badge: false }).then((d) => {
|
|
307
|
-
const show = d.badge === true;
|
|
308
|
-
try {
|
|
309
|
-
store?.setItem("freereport:badge", JSON.stringify({ host, day, show }));
|
|
310
|
-
} catch {
|
|
311
|
-
}
|
|
312
|
-
return show;
|
|
313
|
-
}, () => false);
|
|
314
|
-
return badgeCheck;
|
|
315
|
-
}
|
|
316
|
-
async function needsFreeBadge(key2, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
317
|
-
if (await checkLicense(key2, host)) return false;
|
|
318
|
-
return badgeRequested(host);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
189
|
// src/format.ts
|
|
322
190
|
var DIGITS = ["", "\uC77C", "\uC774", "\uC0BC", "\uC0AC", "\uC624", "\uC721", "\uCE60", "\uD314", "\uAD6C"];
|
|
323
191
|
var SMALL = ["", "\uC2ED", "\uBC31", "\uCC9C"];
|
|
@@ -499,6 +367,138 @@ function formatValue(v, format, locale = "ko") {
|
|
|
499
367
|
return String(v);
|
|
500
368
|
}
|
|
501
369
|
|
|
370
|
+
// src/version.ts
|
|
371
|
+
var version = "0.10.0";
|
|
372
|
+
var releaseDate = "2026-10-19";
|
|
373
|
+
|
|
374
|
+
// src/license.ts
|
|
375
|
+
var PUBLIC_KEY = { kty: "EC", crv: "P-256", x: "MiJXJR8TZJ_-0VKk0wIQ-5mzRsyQ6ruGsbcRRdw9jrw", y: "MTdklYSzCM2pganffz7PPMfxSclQkBLqU_9se_VDznM" };
|
|
376
|
+
var PRICING_URL = "https://freereport.app/pricing/";
|
|
377
|
+
var USAGE_URL = "https://freereport.app/api/usage";
|
|
378
|
+
var STATUS_URL = "https://freereport.app/api/domain-status";
|
|
379
|
+
var b64urlDecode = (s) => {
|
|
380
|
+
const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - s.length % 4) % 4));
|
|
381
|
+
return Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
382
|
+
};
|
|
383
|
+
function isDevHost(host) {
|
|
384
|
+
const h = host.toLowerCase().replace(/^\[|\]$/g, "");
|
|
385
|
+
return !h || h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0" || h.endsWith(".localhost") || h.endsWith(".test") || h.endsWith(".local") || /^(10\.\d+|192\.168|172\.(1[6-9]|2\d|3[01]))\.\d+\.\d+$/.test(h);
|
|
386
|
+
}
|
|
387
|
+
function domainMatches(host, domains) {
|
|
388
|
+
const h = host.toLowerCase().replace(/\.$/, "");
|
|
389
|
+
return domains.some((d) => {
|
|
390
|
+
const raw = d.toLowerCase().replace(/\.$/, "");
|
|
391
|
+
const wildcard = raw.startsWith("*.");
|
|
392
|
+
const dom = wildcard ? raw.slice(2) : raw;
|
|
393
|
+
if (!dom) return false;
|
|
394
|
+
if (h === dom || h === `www.${dom}`) return true;
|
|
395
|
+
return wildcard && h.endsWith(`.${dom}`);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
var today = (now) => `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
399
|
+
async function verifyLicense(key2, host, opts = {}) {
|
|
400
|
+
const parts = typeof key2 === "string" ? key2.trim().split(".") : [];
|
|
401
|
+
if (parts.length !== 3 || parts[0] !== "FR1") return { ok: false, reason: "format" };
|
|
402
|
+
let info;
|
|
403
|
+
try {
|
|
404
|
+
info = JSON.parse(new TextDecoder().decode(b64urlDecode(parts[1])));
|
|
405
|
+
if (!Array.isArray(info.domains) || typeof info.expires !== "string") return { ok: false, reason: "format" };
|
|
406
|
+
} catch {
|
|
407
|
+
return { ok: false, reason: "format" };
|
|
408
|
+
}
|
|
409
|
+
const subtle = typeof crypto !== "undefined" ? crypto.subtle : void 0;
|
|
410
|
+
if (!subtle) return { ok: false, reason: "unsupported", info };
|
|
411
|
+
try {
|
|
412
|
+
const pub = await subtle.importKey("jwk", opts.publicKey ?? PUBLIC_KEY, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
413
|
+
const signed = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
|
|
414
|
+
const ok = await subtle.verify({ name: "ECDSA", hash: "SHA-256" }, pub, b64urlDecode(parts[2]), signed);
|
|
415
|
+
if (!ok) return { ok: false, reason: "signature", info };
|
|
416
|
+
} catch {
|
|
417
|
+
return { ok: false, reason: "signature", info };
|
|
418
|
+
}
|
|
419
|
+
if (today(opts.now ?? /* @__PURE__ */ new Date()) > info.expires) return { ok: false, reason: "expired", info };
|
|
420
|
+
if (!domainMatches(host, info.domains)) return { ok: false, reason: "domain", info };
|
|
421
|
+
if (info.updates && (opts.released ?? releaseDate) > info.updates) return { ok: false, reason: "updates", info };
|
|
422
|
+
return { ok: true, info };
|
|
423
|
+
}
|
|
424
|
+
var globalKey;
|
|
425
|
+
var statuses = /* @__PURE__ */ new Map();
|
|
426
|
+
var badgeCheck = null;
|
|
427
|
+
function setLicenseKey(key2) {
|
|
428
|
+
globalKey = key2;
|
|
429
|
+
}
|
|
430
|
+
var NOTICE = {
|
|
431
|
+
missing: `FreeReport is free for personal use. Company use needs a domain license (\u20A9500,000 per domain per year, or \u20A9900,000 perpetual): ${PRICING_URL}
|
|
432
|
+
FreeReport\uB294 \uAC1C\uC778\uC5D0\uAC8C \uBB34\uB8CC\uC785\uB2C8\uB2E4. \uD68C\uC0AC\uC5D0\uC11C \uC4F0\uB824\uBA74 \uB3C4\uBA54\uC778 \uB77C\uC774\uC120\uC2A4(\uB3C4\uBA54\uC778\uB2F9 \uC5F0 50\uB9CC \uC6D0, \uC601\uAD6C 90\uB9CC \uC6D0)\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: ${PRICING_URL}`,
|
|
433
|
+
format: "FreeReport: the license key is not in the right format. / \uB77C\uC774\uC120\uC2A4 \uD0A4 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
434
|
+
signature: "FreeReport: the license key could not be verified. / \uB77C\uC774\uC120\uC2A4 \uD0A4\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
435
|
+
expired: "FreeReport: the license key has expired. / \uB77C\uC774\uC120\uC2A4 \uAE30\uAC04\uC774 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4.",
|
|
436
|
+
domain: "FreeReport: the license key is for a different domain. / \uB2E4\uB978 \uB3C4\uBA54\uC778\uC6A9 \uB77C\uC774\uC120\uC2A4 \uD0A4\uC785\uB2C8\uB2E4.",
|
|
437
|
+
updates: "FreeReport: this version was released after the update period of your perpetual license. / \uC601\uAD6C \uB77C\uC774\uC120\uC2A4\uC758 \uC5C5\uB370\uC774\uD2B8 \uAE30\uAC04 \uB4A4\uC5D0 \uB098\uC628 \uBC84\uC804\uC785\uB2C8\uB2E4."
|
|
438
|
+
};
|
|
439
|
+
function reportUsage(info, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
440
|
+
try {
|
|
441
|
+
if (isDevHost(host) || typeof navigator === "undefined") return;
|
|
442
|
+
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
443
|
+
const mark = `${host}|${day}|${info.licensed ? 1 : 0}`;
|
|
444
|
+
const store = typeof localStorage !== "undefined" ? localStorage : null;
|
|
445
|
+
if (store?.getItem("freereport:usage") === mark) return;
|
|
446
|
+
store?.setItem("freereport:usage", mark);
|
|
447
|
+
const body = JSON.stringify({ version: info.version, licensed: info.licensed, company: info.company, url: `${location.origin}${location.pathname}` });
|
|
448
|
+
if (typeof navigator.sendBeacon === "function") navigator.sendBeacon(USAGE_URL, body);
|
|
449
|
+
else if (typeof fetch === "function") void fetch(USAGE_URL, { method: "POST", body, mode: "no-cors", keepalive: true }).catch(() => void 0);
|
|
450
|
+
} catch {
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function checkLicense(key2 = globalKey, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
454
|
+
if (isDevHost(host)) return Promise.resolve(true);
|
|
455
|
+
const k = key2 ?? globalKey;
|
|
456
|
+
const id = `${k ?? ""}@${host}`;
|
|
457
|
+
let status = statuses.get(id);
|
|
458
|
+
if (status) return status;
|
|
459
|
+
if (!k) {
|
|
460
|
+
console.info(NOTICE.missing);
|
|
461
|
+
reportUsage({ version, licensed: false }, host);
|
|
462
|
+
status = Promise.resolve(false);
|
|
463
|
+
} else {
|
|
464
|
+
status = verifyLicense(k, host).then((res) => {
|
|
465
|
+
reportUsage({ version, licensed: res.ok, company: res.ok ? res.info.company : void 0 }, host);
|
|
466
|
+
if (res.ok || res.reason === "unsupported") return res.ok || res.reason === "unsupported";
|
|
467
|
+
const extra = res.info ? ` (${res.info.domains.join(", ")}, ${res.info.expires})` : "";
|
|
468
|
+
console.warn(`${NOTICE[res.reason]}${extra}
|
|
469
|
+
${PRICING_URL}`);
|
|
470
|
+
return false;
|
|
471
|
+
}, () => false);
|
|
472
|
+
}
|
|
473
|
+
statuses.set(id, status);
|
|
474
|
+
return status;
|
|
475
|
+
}
|
|
476
|
+
function badgeRequested(host = typeof location !== "undefined" ? location.hostname : "") {
|
|
477
|
+
if (isDevHost(host) || typeof fetch !== "function") return Promise.resolve(false);
|
|
478
|
+
if (badgeCheck) return badgeCheck;
|
|
479
|
+
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
480
|
+
let store = null;
|
|
481
|
+
try {
|
|
482
|
+
store = typeof localStorage !== "undefined" ? localStorage : null;
|
|
483
|
+
const cached = JSON.parse(store?.getItem("freereport:badge") ?? "null");
|
|
484
|
+
if (cached && cached.host === host && cached.day === day) return badgeCheck = Promise.resolve(cached.show);
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
badgeCheck = fetch(STATUS_URL, { mode: "cors", credentials: "omit" }).then((r) => r.ok ? r.json() : { badge: false }).then((d) => {
|
|
488
|
+
const show = d.badge === true;
|
|
489
|
+
try {
|
|
490
|
+
store?.setItem("freereport:badge", JSON.stringify({ host, day, show }));
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
return show;
|
|
494
|
+
}, () => false);
|
|
495
|
+
return badgeCheck;
|
|
496
|
+
}
|
|
497
|
+
async function needsFreeBadge(key2, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
498
|
+
if (await checkLicense(key2, host)) return false;
|
|
499
|
+
return badgeRequested(host);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
502
|
// src/expr.ts
|
|
503
503
|
var OPS = ["===", "!==", "==", "!=", "<=", ">=", "&&", "||", "??", "<", ">", "+", "-", "*", "/", "%", "!", "?", ":", "(", ")", ",", ".", "[", "]"];
|
|
504
504
|
function tokenize(src) {
|
|
@@ -933,7 +933,7 @@ function checkExpression(src) {
|
|
|
933
933
|
return e.message;
|
|
934
934
|
}
|
|
935
935
|
}
|
|
936
|
-
function interpolate(template, scope) {
|
|
936
|
+
function interpolate(template, scope, locale) {
|
|
937
937
|
if (!template.includes("{")) return template.replace(/}}/g, "}");
|
|
938
938
|
let out = "";
|
|
939
939
|
let i = 0;
|
|
@@ -961,7 +961,7 @@ function interpolate(template, scope) {
|
|
|
961
961
|
}
|
|
962
962
|
const { expr, format } = splitFormat(template.slice(i + 1, end));
|
|
963
963
|
const v = evaluate(expr, scope);
|
|
964
|
-
out += formatValue(v, format);
|
|
964
|
+
out += formatValue(v, format, locale);
|
|
965
965
|
i = end + 1;
|
|
966
966
|
}
|
|
967
967
|
return out;
|
|
@@ -1335,6 +1335,11 @@ function pathData(d) {
|
|
|
1335
1335
|
return d.map((v) => typeof v === "number" ? n(v) : v).join(" ");
|
|
1336
1336
|
}
|
|
1337
1337
|
var opacityAttr = (o) => o !== void 0 && o < 1 ? ` opacity="${n(o)}"` : "";
|
|
1338
|
+
var sourceAttrs = (it) => {
|
|
1339
|
+
const s = it.source;
|
|
1340
|
+
if (!s?.elementId) return "";
|
|
1341
|
+
return ` data-fr-id="${esc(s.elementId)}" data-fr-kind="${it.kind}"${s.row !== void 0 ? ` data-fr-row="${s.row}"` : ""}`;
|
|
1342
|
+
};
|
|
1338
1343
|
function textSvg(t, clipId) {
|
|
1339
1344
|
const sizeMm = t.font.size * PT;
|
|
1340
1345
|
const family = `${quoteFamily(t.font.family)}, ${FALLBACK_FONTS}`.replace(/"/g, "'");
|
|
@@ -1348,11 +1353,12 @@ function textSvg(t, clipId) {
|
|
|
1348
1353
|
}
|
|
1349
1354
|
const overflow = t.source?.band !== "watermark" && t.lines.some((l) => l.x < t.x - 0.05 || l.x + l.width > t.x + t.w + 0.05 || l.y - sizeMm * 0.8 < t.y - 0.05 || l.y + sizeMm * 0.2 > t.y + t.h + 0.05);
|
|
1350
1355
|
const transform = t.rotate ? ` transform="rotate(${n(t.rotate)} ${n(t.x + t.w / 2)} ${n(t.y + t.h / 2)})"` : "";
|
|
1351
|
-
|
|
1356
|
+
const hooks = sourceAttrs(t) + (t.overflow ? ' data-fr-overflow="1"' : "");
|
|
1357
|
+
if (!overflow) return { defs: "", body: `<g ${attrs}${hooks}${transform}${opacityAttr(t.opacity)}>${body}</g>` };
|
|
1352
1358
|
const id = clipId();
|
|
1353
1359
|
return {
|
|
1354
1360
|
defs: `<clipPath id="${id}"><rect x="${n(t.x)}" y="${n(t.y)}" width="${n(t.w)}" height="${n(t.h)}"/></clipPath>`,
|
|
1355
|
-
body: `<g ${attrs} clip-path="url(#${id})"${transform}${opacityAttr(t.opacity)}>${body}</g>`
|
|
1361
|
+
body: `<g ${attrs}${hooks} clip-path="url(#${id})"${transform}${opacityAttr(t.opacity)}>${body}</g>`
|
|
1356
1362
|
};
|
|
1357
1363
|
}
|
|
1358
1364
|
function itemSvg(it, clipId) {
|
|
@@ -1401,7 +1407,8 @@ function pageToSvg(page, opts = {}) {
|
|
|
1401
1407
|
for (const it of page.items) {
|
|
1402
1408
|
const r = itemSvg(it, clipId);
|
|
1403
1409
|
defs += r.defs;
|
|
1404
|
-
|
|
1410
|
+
const hooks = it.kind === "text" ? "" : sourceAttrs(it);
|
|
1411
|
+
body += hooks && r.body ? `<g${hooks}>${r.body}</g>` : r.body;
|
|
1405
1412
|
}
|
|
1406
1413
|
const size = opts.scale ? `width="${n(page.width * opts.scale)}" height="${n(page.height * opts.scale)}"` : `width="${n(page.width)}mm" height="${n(page.height)}mm"`;
|
|
1407
1414
|
return `<svg xmlns="http://www.w3.org/2000/svg" ${size} viewBox="0 0 ${n(page.width)} ${n(page.height)}"${opts.className ? ` class="${opts.className}"` : ""} text-rendering="geometricPrecision">${defs ? `<defs>${defs}</defs>` : ""}<rect width="100%" height="100%" fill="#fff"/>${body}</svg>`;
|
|
@@ -2900,7 +2907,7 @@ function prepare(el, scope, ctx, bandCanGrow) {
|
|
|
2900
2907
|
let text = "";
|
|
2901
2908
|
let value;
|
|
2902
2909
|
if (!ctx.blank) {
|
|
2903
|
-
if (el.type === "text") text = interpolate(el.text ?? "", scope);
|
|
2910
|
+
if (el.type === "text") text = interpolate(el.text ?? "", scope, ctx.locale);
|
|
2904
2911
|
else {
|
|
2905
2912
|
value = evaluate(el.expr, scope);
|
|
2906
2913
|
text = value == null || value === "" ? el.nullText ?? "" : formatValue(value, el.format, ctx.locale);
|
|
@@ -2945,6 +2952,7 @@ function textItem(x, y, w, h, lines, font, st, measure) {
|
|
|
2945
2952
|
else if (st.align === "right") lx = x + w - pr - width;
|
|
2946
2953
|
return { text, x: lx, y: top + i * lineH + (lineH - font.size * PT) / 2 + font.size * PT * ASCENT, width };
|
|
2947
2954
|
});
|
|
2955
|
+
const overflow = total > innerH + 0.05 || out.some((l) => l.width > w - pl - pr + 0.05);
|
|
2948
2956
|
return {
|
|
2949
2957
|
kind: "text",
|
|
2950
2958
|
x,
|
|
@@ -2959,6 +2967,7 @@ function textItem(x, y, w, h, lines, font, st, measure) {
|
|
|
2959
2967
|
underline: st.underline || void 0,
|
|
2960
2968
|
strike: st.strike || void 0,
|
|
2961
2969
|
letterSpacing: st.letterSpacing || void 0,
|
|
2970
|
+
overflow: overflow || void 0,
|
|
2962
2971
|
text: lines.join("\n")
|
|
2963
2972
|
};
|
|
2964
2973
|
}
|
|
@@ -3027,25 +3036,68 @@ function barcodeItems(el, x, y, w, h, scope, ctx) {
|
|
|
3027
3036
|
return [textItem(x, y, w, h, ["(barcode?)"], { family: "sans-serif", size: 7, bold: false, italic: false }, { ...resolveStyle(ctx.def, el), color: "#cc0000" }, ctx.measure)];
|
|
3028
3037
|
}
|
|
3029
3038
|
}
|
|
3039
|
+
function shifts(prepared) {
|
|
3040
|
+
let grew = false;
|
|
3041
|
+
for (const p of prepared) if (p.needed > p.el.h + 0.01) {
|
|
3042
|
+
grew = true;
|
|
3043
|
+
break;
|
|
3044
|
+
}
|
|
3045
|
+
if (!grew) return null;
|
|
3046
|
+
const out = /* @__PURE__ */ new Map();
|
|
3047
|
+
const top = (p) => Math.min(p.el.y, p.el.y + p.el.h);
|
|
3048
|
+
const bottom = (p) => Math.max(p.el.y, p.el.y + p.el.h);
|
|
3049
|
+
const order = [...prepared].sort((a, b) => top(a) - top(b));
|
|
3050
|
+
for (const p of order) {
|
|
3051
|
+
if (p.el.shift === false) continue;
|
|
3052
|
+
let by = 0;
|
|
3053
|
+
for (const q of order) {
|
|
3054
|
+
if (top(q) >= top(p)) break;
|
|
3055
|
+
if (bottom(q) > top(p) + 0.01) continue;
|
|
3056
|
+
by = Math.max(by, (out.get(q) ?? 0) + Math.max(0, q.needed - q.el.h));
|
|
3057
|
+
}
|
|
3058
|
+
if (by > 0) out.set(p, by);
|
|
3059
|
+
}
|
|
3060
|
+
return out;
|
|
3061
|
+
}
|
|
3062
|
+
function addAnchor(out, el, ctx, left, top, y, h, hidden) {
|
|
3063
|
+
if (!el.id) return;
|
|
3064
|
+
const line = el.type === "line";
|
|
3065
|
+
out.push({
|
|
3066
|
+
id: el.id,
|
|
3067
|
+
band: ctx.kind,
|
|
3068
|
+
row: ctx.rowIndex,
|
|
3069
|
+
x: left + (line ? Math.min(el.x, el.x + el.w) : el.x),
|
|
3070
|
+
y: top + (line ? Math.min(y, y + h) : y),
|
|
3071
|
+
w: line ? Math.abs(el.w) : el.w,
|
|
3072
|
+
h: line ? Math.abs(h) : h,
|
|
3073
|
+
...hidden ? { hidden } : {}
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
3030
3076
|
function renderBand(band, scope, ctx, left, top, minHeight = 0) {
|
|
3031
3077
|
const bandGrow = band.canGrow ?? (ctx.kind === "detail" || ctx.kind === "groupHeader" || ctx.kind === "groupFooter" || ctx.kind === "summary" || ctx.kind === "title");
|
|
3032
3078
|
const prepared = [];
|
|
3079
|
+
const anchors = ctx.anchors && !ctx.blank ? [] : void 0;
|
|
3033
3080
|
for (const el of band.elements) {
|
|
3034
|
-
if (el.visible && !ctx.blank && !truthy(evaluate(el.visible, scope)))
|
|
3081
|
+
if (el.visible && !ctx.blank && !truthy(evaluate(el.visible, scope))) {
|
|
3082
|
+
if (anchors) addAnchor(anchors, el, ctx, left, top, el.y, el.h, true);
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3035
3085
|
prepared.push(prepare(el, scope, ctx, bandGrow));
|
|
3036
3086
|
}
|
|
3087
|
+
const shift2 = shifts(prepared);
|
|
3037
3088
|
let height = Math.max(band.height, minHeight);
|
|
3038
|
-
for (const p of prepared) height = Math.max(height, p.el.y + p.needed);
|
|
3089
|
+
for (const p of prepared) height = Math.max(height, p.el.y + (shift2?.get(p) ?? 0) + p.needed);
|
|
3039
3090
|
const items = [];
|
|
3040
3091
|
const bg = ctx.background ?? band.background;
|
|
3041
3092
|
if (bg) items.push({ kind: "box", x: left, y: top, w: ctx.width, h: height, fill: bg, source: { band: ctx.kind, row: ctx.rowIndex } });
|
|
3042
|
-
const growth = height - band.height;
|
|
3043
3093
|
for (const p of prepared) {
|
|
3044
3094
|
const el = p.el;
|
|
3095
|
+
const elY = shift2 ? el.y + (shift2.get(p) ?? 0) : el.y;
|
|
3045
3096
|
const touchesBottom = el.y + el.h >= band.height - 0.01;
|
|
3046
3097
|
let h = p.needed;
|
|
3047
|
-
if ((el.stretch ?? touchesBottom) && el.type !== "line" && el.type !== "table" && el.type !== "crosstab") h = Math.max(h,
|
|
3048
|
-
const x = left + el.x, y = top +
|
|
3098
|
+
if ((el.stretch ?? touchesBottom) && el.type !== "line" && el.type !== "table" && el.type !== "crosstab") h = Math.max(h, height - elY);
|
|
3099
|
+
const x = left + el.x, y = top + elY;
|
|
3100
|
+
if (anchors) addAnchor(anchors, el, ctx, left, top, elY, el.type === "line" ? el.h === 0 ? 0 : el.y + el.h >= band.height - 0.01 ? Math.max(el.h, height - elY) : el.h : h);
|
|
3049
3101
|
switch (el.type) {
|
|
3050
3102
|
case "text":
|
|
3051
3103
|
case "field": {
|
|
@@ -3060,7 +3112,7 @@ function renderBand(band, scope, ctx, left, top, minHeight = 0) {
|
|
|
3060
3112
|
break;
|
|
3061
3113
|
}
|
|
3062
3114
|
case "line": {
|
|
3063
|
-
const lh = el.h === 0 ? 0 : el.y + el.h >= band.height - 0.01 ? el.h
|
|
3115
|
+
const lh = el.h === 0 ? 0 : el.y + el.h >= band.height - 0.01 ? Math.max(el.h, height - elY) : el.h;
|
|
3064
3116
|
items.push({ kind: "line", x1: x, y1: y, x2: x + el.w, y2: y + lh, width: el.width ?? 0.2, color: el.color ?? "#000000", dash: el.dash, source: { band: ctx.kind, elementId: el.id } });
|
|
3065
3117
|
break;
|
|
3066
3118
|
}
|
|
@@ -3098,7 +3150,7 @@ function renderBand(band, scope, ctx, left, top, minHeight = 0) {
|
|
|
3098
3150
|
break;
|
|
3099
3151
|
}
|
|
3100
3152
|
}
|
|
3101
|
-
return { height, items };
|
|
3153
|
+
return anchors ? { height, items, anchors } : { height, items };
|
|
3102
3154
|
}
|
|
3103
3155
|
|
|
3104
3156
|
// src/layout.ts
|
|
@@ -3283,7 +3335,7 @@ function paginate(def, data, opts, knownTotal, docHash) {
|
|
|
3283
3335
|
groupLevel: e.group?.level
|
|
3284
3336
|
}
|
|
3285
3337
|
});
|
|
3286
|
-
const ctxFor = (kind, extra = {}) => ({ def, measure, locale, kind, width, previous, ...extra });
|
|
3338
|
+
const ctxFor = (kind, extra = {}) => ({ def, measure, locale, kind, width, previous, anchors: opts.anchors, ...extra });
|
|
3287
3339
|
const newPage = () => {
|
|
3288
3340
|
if (pages.length >= maxPages) throw new Error(`Report stopped at ${maxPages} pages (maxPages)`);
|
|
3289
3341
|
page = { number: pages.length + 1, total: 0, width: geo.width, height: geo.height, items: [] };
|
|
@@ -3298,13 +3350,14 @@ function paginate(def, data, opts, knownTotal, docHash) {
|
|
|
3298
3350
|
previous.clear();
|
|
3299
3351
|
};
|
|
3300
3352
|
const place = (band, kind, scope, extra = {}, pre) => {
|
|
3301
|
-
const r = pre ? { height: pre.height, items: shiftItems(pre.items, y) } : renderBand(band, scope, ctxFor(kind, extra), ml, y);
|
|
3353
|
+
const r = pre ? { height: pre.height, items: shiftItems(pre.items, y), anchors: pre.anchors?.map((a) => ({ ...a, y: a.y + y })) } : renderBand(band, scope, ctxFor(kind, extra), ml, y);
|
|
3302
3354
|
for (const it of r.items) page.items.push(it);
|
|
3355
|
+
if (r.anchors) (page.anchors ?? (page.anchors = [])).push(...r.anchors);
|
|
3303
3356
|
y += r.height;
|
|
3304
3357
|
hasBody = true;
|
|
3305
3358
|
return r.height;
|
|
3306
3359
|
};
|
|
3307
|
-
const heightOf = (band, kind, scope, extra = {}) => renderBand(band, scope, ctxFor(kind, { ...extra, previous: void 0 }), 0, 0).height;
|
|
3360
|
+
const heightOf = (band, kind, scope, extra = {}) => renderBand(band, scope, ctxFor(kind, { ...extra, previous: void 0, anchors: false }), 0, 0).height;
|
|
3308
3361
|
const renderChunk = (e, scope, extra) => {
|
|
3309
3362
|
const band = e.band;
|
|
3310
3363
|
const n2 = Math.max(1, Math.floor(band.columns ?? 1));
|
|
@@ -3317,7 +3370,7 @@ function paginate(def, data, opts, knownTotal, docHash) {
|
|
|
3317
3370
|
ml + i * (colW + gap),
|
|
3318
3371
|
0
|
|
3319
3372
|
));
|
|
3320
|
-
return { height: Math.max(...parts.map((p) => p.height)), items: parts.flatMap((p) => p.items) };
|
|
3373
|
+
return { height: Math.max(...parts.map((p) => p.height)), items: parts.flatMap((p) => p.items), anchors: opts.anchors ? parts.flatMap((p) => p.anchors ?? []) : void 0 };
|
|
3321
3374
|
};
|
|
3322
3375
|
const isTableEntry = (e) => e.kind === "detail" || e.kind === "groupHeader" || e.kind === "groupFooter";
|
|
3323
3376
|
const placeColumnHeader = (e) => {
|
|
@@ -3413,7 +3466,9 @@ function paginate(def, data, opts, knownTotal, docHash) {
|
|
|
3413
3466
|
const draw = (band, kind, top) => {
|
|
3414
3467
|
if (!band) return;
|
|
3415
3468
|
if (band.visible && !truthy2(evaluate(band.visible, scope))) return;
|
|
3416
|
-
|
|
3469
|
+
const r = renderBand(band, scope, ctxFor(kind, { previous: void 0 }), ml, top, 0);
|
|
3470
|
+
extraItems.push(...r.items);
|
|
3471
|
+
if (r.anchors) (p.anchors ?? (p.anchors = [])).push(...r.anchors);
|
|
3417
3472
|
};
|
|
3418
3473
|
if (!(def.pageHeaderSkipFirst && i === 0)) draw(b.pageHeader, "pageHeader", mt);
|
|
3419
3474
|
draw(b.pageFooter, "pageFooter", geo.height - mb - footerH);
|
|
@@ -5550,8 +5605,10 @@ function createReport(definition, data = {}, opts = {}) {
|
|
|
5550
5605
|
let rows = data;
|
|
5551
5606
|
let params = { ...opts.params };
|
|
5552
5607
|
let cached = null;
|
|
5608
|
+
let anchored = null;
|
|
5553
5609
|
void checkLicense(opts.licenseKey);
|
|
5554
5610
|
const layout = () => cached ?? (cached = layoutReport(def, rows, { ...opts, params }));
|
|
5611
|
+
const withAnchors = () => opts.anchors ? layout() : anchored ?? (anchored = layoutReport(def, rows, { ...opts, params, anchors: true }));
|
|
5555
5612
|
const allowed = async (kind, fileName) => {
|
|
5556
5613
|
if (!opts.onOutput) return true;
|
|
5557
5614
|
const l = layout();
|
|
@@ -5565,12 +5622,16 @@ function createReport(definition, data = {}, opts = {}) {
|
|
|
5565
5622
|
layout,
|
|
5566
5623
|
async ready() {
|
|
5567
5624
|
await ensureFonts(def);
|
|
5568
|
-
cached = null;
|
|
5625
|
+
cached = anchored = null;
|
|
5569
5626
|
return layout();
|
|
5570
5627
|
},
|
|
5628
|
+
anchors(prefix = "") {
|
|
5629
|
+
const l = withAnchors();
|
|
5630
|
+
return l.pages.flatMap((p, i) => (p.anchors ?? []).filter((a) => a.id.startsWith(prefix)).map((a) => ({ ...a, page: i + 1, pageWidth: p.width, pageHeight: p.height })));
|
|
5631
|
+
},
|
|
5571
5632
|
setData(d) {
|
|
5572
5633
|
rows = d;
|
|
5573
|
-
cached = null;
|
|
5634
|
+
cached = anchored = null;
|
|
5574
5635
|
},
|
|
5575
5636
|
fingerprint() {
|
|
5576
5637
|
const l = layout();
|
|
@@ -5578,11 +5639,11 @@ function createReport(definition, data = {}, opts = {}) {
|
|
|
5578
5639
|
},
|
|
5579
5640
|
setParams(p) {
|
|
5580
5641
|
params = { ...p };
|
|
5581
|
-
cached = null;
|
|
5642
|
+
cached = anchored = null;
|
|
5582
5643
|
},
|
|
5583
5644
|
setDefinition(d) {
|
|
5584
5645
|
def = d;
|
|
5585
|
-
cached = null;
|
|
5646
|
+
cached = anchored = null;
|
|
5586
5647
|
},
|
|
5587
5648
|
async print(o = {}) {
|
|
5588
5649
|
if (!await allowed("print")) return false;
|
|
@@ -5719,7 +5780,7 @@ var PAGE_GAP = 16;
|
|
|
5719
5780
|
function paramInput(name, p, value, t) {
|
|
5720
5781
|
const id = `fr-p-${name.replace(/[^\w-]/g, "_")}`;
|
|
5721
5782
|
const label = `<label for="${id}">${escapeHtml(p.label ?? name)}${p.required ? ' <b aria-hidden="true">*</b>' : ""}</label>`;
|
|
5722
|
-
const v = value == null ? "" : value instanceof Date ? value
|
|
5783
|
+
const v = value == null ? "" : value instanceof Date ? formatDate(value, "yyyy-MM-dd") : String(value);
|
|
5723
5784
|
const req = p.required ? " required" : "";
|
|
5724
5785
|
if (p.type === "select" || p.options) {
|
|
5725
5786
|
return `<div class="fr-param">${label}<select id="${id}" name="${escapeHtml(name)}"${req}>${(p.options ?? []).map((o) => `<option value="${escapeHtml(String(o.value))}"${String(o.value) === v ? " selected" : ""}>${escapeHtml(String(o.label ?? o.value))}</option>`).join("")}</select></div>`;
|
|
@@ -5784,9 +5845,10 @@ function createViewer(el, options) {
|
|
|
5784
5845
|
const toggle = $(".fr-params-toggle");
|
|
5785
5846
|
const menuBtn = $('[data-act="menu"]');
|
|
5786
5847
|
const menuList = $(".fr-menu-list");
|
|
5848
|
+
const overlayText = $(".fr-overlay-text");
|
|
5787
5849
|
const busy = (text) => {
|
|
5788
5850
|
overlay.hidden = text === null;
|
|
5789
|
-
|
|
5851
|
+
overlayText.textContent = text ?? "";
|
|
5790
5852
|
};
|
|
5791
5853
|
const showParams = (open) => {
|
|
5792
5854
|
if (!form || !toggle) return;
|
|
@@ -5838,6 +5900,7 @@ function createViewer(el, options) {
|
|
|
5838
5900
|
const holder = document.createElement("div");
|
|
5839
5901
|
holder.className = "fr-page";
|
|
5840
5902
|
holder.dataset.index = String(i);
|
|
5903
|
+
holder.dataset.page = String(i + 1);
|
|
5841
5904
|
holder.setAttribute("aria-label", `${t.page} ${i + 1}`);
|
|
5842
5905
|
frag.appendChild(holder);
|
|
5843
5906
|
});
|
|
@@ -5847,7 +5910,11 @@ function createViewer(el, options) {
|
|
|
5847
5910
|
for (const e of entries) if (e.isIntersecting) drawPage(e.target);
|
|
5848
5911
|
updateCurrent();
|
|
5849
5912
|
}, { root: body, rootMargin: "600px 0px" }) : null;
|
|
5850
|
-
pagesEl.querySelectorAll(".fr-page")
|
|
5913
|
+
const holders = pagesEl.querySelectorAll(".fr-page");
|
|
5914
|
+
holders.forEach((p, i) => observer ? observer.observe(p) : i < 3 && drawPage(p));
|
|
5915
|
+
const pageH = result.height * MM_PX * scale + PAGE_GAP;
|
|
5916
|
+
const from = Math.min(holders.length - 1, Math.max(0, Math.floor(body.scrollTop / pageH)));
|
|
5917
|
+
for (let i = from; i < Math.min(holders.length, from + 1 + Math.ceil(body.clientHeight / pageH)); i++) drawPage(holders[i]);
|
|
5851
5918
|
if (totalEl) totalEl.textContent = `/ ${result.pages.length}`;
|
|
5852
5919
|
lastQuery = "";
|
|
5853
5920
|
hits = [];
|
|
@@ -5897,35 +5964,48 @@ function createViewer(el, options) {
|
|
|
5897
5964
|
body.scrollTop = ratio * body.scrollHeight;
|
|
5898
5965
|
};
|
|
5899
5966
|
let runSeq = 0;
|
|
5900
|
-
|
|
5901
|
-
|
|
5967
|
+
let runs = 0;
|
|
5968
|
+
let latest = Promise.resolve();
|
|
5969
|
+
const setState = (state) => {
|
|
5970
|
+
el.dataset.frState = state;
|
|
5971
|
+
};
|
|
5972
|
+
const runOnce = async (seq2) => {
|
|
5902
5973
|
busy(t.loading);
|
|
5974
|
+
setState("busy");
|
|
5903
5975
|
let during = "data";
|
|
5976
|
+
const stale = () => seq2 !== runSeq;
|
|
5904
5977
|
try {
|
|
5905
5978
|
if (options.fetchData) {
|
|
5906
5979
|
const fetched = await options.fetchData(params);
|
|
5907
|
-
if (
|
|
5980
|
+
if (stale()) return await latest;
|
|
5908
5981
|
report.setData(fetched);
|
|
5909
5982
|
}
|
|
5910
5983
|
during = "layout";
|
|
5911
5984
|
report.setParams(params);
|
|
5912
5985
|
await ensureFonts(report.definition);
|
|
5913
5986
|
if (destroyed) return;
|
|
5987
|
+
if (stale()) return await latest;
|
|
5914
5988
|
result = report.layout();
|
|
5915
5989
|
reported = -1;
|
|
5916
5990
|
build();
|
|
5991
|
+
el.dataset.frRun = String(++runs);
|
|
5992
|
+
setState("ready");
|
|
5917
5993
|
try {
|
|
5918
5994
|
options.onReady?.({ pages: result.pages.length, rows: result.rowCount, ms: result.ms, params: { ...params } });
|
|
5919
5995
|
} catch (e) {
|
|
5920
5996
|
console.error(e);
|
|
5921
5997
|
}
|
|
5922
5998
|
} catch (e) {
|
|
5999
|
+
if (stale()) return await latest;
|
|
5923
6000
|
const err = fail(e, during);
|
|
5924
6001
|
pagesEl.innerHTML = `<p class="fr-empty">${t.error}: ${escapeHtml(err.message)}</p>`;
|
|
6002
|
+
el.dataset.frRun = String(++runs);
|
|
6003
|
+
setState("error");
|
|
5925
6004
|
} finally {
|
|
5926
|
-
busy(null);
|
|
6005
|
+
if (!stale()) busy(null);
|
|
5927
6006
|
}
|
|
5928
6007
|
};
|
|
6008
|
+
const run = () => latest = runOnce(++runSeq);
|
|
5929
6009
|
const exportAs = async (kind) => {
|
|
5930
6010
|
busy(t.exporting);
|
|
5931
6011
|
try {
|
|
@@ -6056,14 +6136,20 @@ function createViewer(el, options) {
|
|
|
6056
6136
|
const v = inp.value;
|
|
6057
6137
|
next[inp.name] = type === "number" ? v === "" ? null : Number(v) : type === "boolean" ? v === "true" : v;
|
|
6058
6138
|
});
|
|
6139
|
+
const before = el.dataset.frState;
|
|
6140
|
+
setState("busy");
|
|
6059
6141
|
let answer;
|
|
6060
6142
|
try {
|
|
6061
6143
|
answer = await options.onParamsSubmit?.({ ...next });
|
|
6062
6144
|
} catch (x) {
|
|
6063
6145
|
fail(x, "data");
|
|
6146
|
+
setState(before ?? "ready");
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
if (answer === false) {
|
|
6150
|
+
setState(before ?? "ready");
|
|
6064
6151
|
return;
|
|
6065
6152
|
}
|
|
6066
|
-
if (answer === false) return;
|
|
6067
6153
|
params = answer && typeof answer === "object" ? { ...next, ...answer } : next;
|
|
6068
6154
|
void run();
|
|
6069
6155
|
});
|
|
@@ -6175,6 +6261,8 @@ function createViewer(el, options) {
|
|
|
6175
6261
|
listeners.abort();
|
|
6176
6262
|
el.innerHTML = "";
|
|
6177
6263
|
el.classList.remove("fr-viewer");
|
|
6264
|
+
delete el.dataset.frState;
|
|
6265
|
+
delete el.dataset.frRun;
|
|
6178
6266
|
}
|
|
6179
6267
|
};
|
|
6180
6268
|
}
|
|
@@ -6222,7 +6310,7 @@ function ensureIds(def) {
|
|
|
6222
6310
|
for (const s of bandSlots(def)) for (const el of s.band.elements) if (el.id) all.add(el.id);
|
|
6223
6311
|
const seen = /* @__PURE__ */ new Set();
|
|
6224
6312
|
for (const s of bandSlots(def)) for (const el of s.band.elements) {
|
|
6225
|
-
if (el.id && /^[\w
|
|
6313
|
+
if (el.id && /^[\w:.-]{1,64}$/.test(String(el.id)) && !seen.has(el.id)) {
|
|
6226
6314
|
seen.add(el.id);
|
|
6227
6315
|
continue;
|
|
6228
6316
|
}
|