@freereportjs/core 0.8.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 +55 -55
- package/dist/cdn/freereport.min.js +44 -44
- package/dist/index.d.ts +58 -3
- package/dist/index.js +294 -169
- 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.8.0";
|
|
191
|
-
var releaseDate = "2026-10-15";
|
|
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 = [];
|
|
@@ -5859,6 +5926,27 @@ function createViewer(el, options) {
|
|
|
5859
5926
|
const pageH = result.height * MM_PX * scale + PAGE_GAP;
|
|
5860
5927
|
current = Math.min(result.pages.length - 1, Math.max(0, Math.round(body.scrollTop / pageH)));
|
|
5861
5928
|
if (pageInput && document.activeElement !== pageInput) pageInput.value = String(current + 1);
|
|
5929
|
+
notifyPage();
|
|
5930
|
+
};
|
|
5931
|
+
let reported = -1;
|
|
5932
|
+
const notifyPage = () => {
|
|
5933
|
+
if (!result?.pages.length || current === reported) return;
|
|
5934
|
+
reported = current;
|
|
5935
|
+
try {
|
|
5936
|
+
options.onPageChange?.(current + 1, result.pages.length);
|
|
5937
|
+
} catch (e) {
|
|
5938
|
+
console.error(e);
|
|
5939
|
+
}
|
|
5940
|
+
};
|
|
5941
|
+
const fail = (e, during) => {
|
|
5942
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
5943
|
+
console.error(err);
|
|
5944
|
+
try {
|
|
5945
|
+
options.onError?.(err, during);
|
|
5946
|
+
} catch (x) {
|
|
5947
|
+
console.error(x);
|
|
5948
|
+
}
|
|
5949
|
+
return err;
|
|
5862
5950
|
};
|
|
5863
5951
|
const goTo = (n2) => {
|
|
5864
5952
|
if (!result) return;
|
|
@@ -5867,6 +5955,7 @@ function createViewer(el, options) {
|
|
|
5867
5955
|
if (holder) body.scrollTop = holder.offsetTop - pagesEl.offsetTop;
|
|
5868
5956
|
current = i;
|
|
5869
5957
|
if (pageInput) pageInput.value = String(i + 1);
|
|
5958
|
+
notifyPage();
|
|
5870
5959
|
};
|
|
5871
5960
|
const setZoom = (z) => {
|
|
5872
5961
|
const ratio = result ? body.scrollTop / Math.max(1, body.scrollHeight) : 0;
|
|
@@ -5875,34 +5964,54 @@ function createViewer(el, options) {
|
|
|
5875
5964
|
body.scrollTop = ratio * body.scrollHeight;
|
|
5876
5965
|
};
|
|
5877
5966
|
let runSeq = 0;
|
|
5878
|
-
|
|
5879
|
-
|
|
5967
|
+
let runs = 0;
|
|
5968
|
+
let latest = Promise.resolve();
|
|
5969
|
+
const setState = (state) => {
|
|
5970
|
+
el.dataset.frState = state;
|
|
5971
|
+
};
|
|
5972
|
+
const runOnce = async (seq2) => {
|
|
5880
5973
|
busy(t.loading);
|
|
5974
|
+
setState("busy");
|
|
5975
|
+
let during = "data";
|
|
5976
|
+
const stale = () => seq2 !== runSeq;
|
|
5881
5977
|
try {
|
|
5882
5978
|
if (options.fetchData) {
|
|
5883
5979
|
const fetched = await options.fetchData(params);
|
|
5884
|
-
if (
|
|
5980
|
+
if (stale()) return await latest;
|
|
5885
5981
|
report.setData(fetched);
|
|
5886
5982
|
}
|
|
5983
|
+
during = "layout";
|
|
5887
5984
|
report.setParams(params);
|
|
5888
5985
|
await ensureFonts(report.definition);
|
|
5889
5986
|
if (destroyed) return;
|
|
5987
|
+
if (stale()) return await latest;
|
|
5890
5988
|
result = report.layout();
|
|
5989
|
+
reported = -1;
|
|
5891
5990
|
build();
|
|
5991
|
+
el.dataset.frRun = String(++runs);
|
|
5992
|
+
setState("ready");
|
|
5993
|
+
try {
|
|
5994
|
+
options.onReady?.({ pages: result.pages.length, rows: result.rowCount, ms: result.ms, params: { ...params } });
|
|
5995
|
+
} catch (e) {
|
|
5996
|
+
console.error(e);
|
|
5997
|
+
}
|
|
5892
5998
|
} catch (e) {
|
|
5893
|
-
|
|
5894
|
-
|
|
5999
|
+
if (stale()) return await latest;
|
|
6000
|
+
const err = fail(e, during);
|
|
6001
|
+
pagesEl.innerHTML = `<p class="fr-empty">${t.error}: ${escapeHtml(err.message)}</p>`;
|
|
6002
|
+
el.dataset.frRun = String(++runs);
|
|
6003
|
+
setState("error");
|
|
5895
6004
|
} finally {
|
|
5896
|
-
busy(null);
|
|
6005
|
+
if (!stale()) busy(null);
|
|
5897
6006
|
}
|
|
5898
6007
|
};
|
|
6008
|
+
const run = () => latest = runOnce(++runSeq);
|
|
5899
6009
|
const exportAs = async (kind) => {
|
|
5900
6010
|
busy(t.exporting);
|
|
5901
6011
|
try {
|
|
5902
6012
|
await report.download(kind, options.fileName);
|
|
5903
6013
|
} catch (e) {
|
|
5904
|
-
|
|
5905
|
-
alertText(`${t.error}: ${e.message}`);
|
|
6014
|
+
alertText(`${t.error}: ${fail(e, "output").message}`);
|
|
5906
6015
|
} finally {
|
|
5907
6016
|
busy(null);
|
|
5908
6017
|
}
|
|
@@ -5944,7 +6053,7 @@ function createViewer(el, options) {
|
|
|
5944
6053
|
setZoom(Math.round((scale - 0.1) * 10) / 10);
|
|
5945
6054
|
break;
|
|
5946
6055
|
case "print":
|
|
5947
|
-
void report.print().catch((err) => alertText(`${t.error}: ${err.message}`));
|
|
6056
|
+
void report.print().catch((err) => alertText(`${t.error}: ${fail(err, "output").message}`));
|
|
5948
6057
|
break;
|
|
5949
6058
|
case "pdf":
|
|
5950
6059
|
void exportAs("pdf");
|
|
@@ -6019,7 +6128,7 @@ function createViewer(el, options) {
|
|
|
6019
6128
|
e.preventDefault();
|
|
6020
6129
|
setZoom(Math.round(scale * (e.deltaY < 0 ? 1.1 : 1 / 1.1) * 100) / 100);
|
|
6021
6130
|
}, { passive: false });
|
|
6022
|
-
form?.addEventListener("submit", (e) => {
|
|
6131
|
+
form?.addEventListener("submit", async (e) => {
|
|
6023
6132
|
e.preventDefault();
|
|
6024
6133
|
const next = { ...params };
|
|
6025
6134
|
form.querySelectorAll("input, select").forEach((inp) => {
|
|
@@ -6027,7 +6136,21 @@ function createViewer(el, options) {
|
|
|
6027
6136
|
const v = inp.value;
|
|
6028
6137
|
next[inp.name] = type === "number" ? v === "" ? null : Number(v) : type === "boolean" ? v === "true" : v;
|
|
6029
6138
|
});
|
|
6030
|
-
|
|
6139
|
+
const before = el.dataset.frState;
|
|
6140
|
+
setState("busy");
|
|
6141
|
+
let answer;
|
|
6142
|
+
try {
|
|
6143
|
+
answer = await options.onParamsSubmit?.({ ...next });
|
|
6144
|
+
} catch (x) {
|
|
6145
|
+
fail(x, "data");
|
|
6146
|
+
setState(before ?? "ready");
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
if (answer === false) {
|
|
6150
|
+
setState(before ?? "ready");
|
|
6151
|
+
return;
|
|
6152
|
+
}
|
|
6153
|
+
params = answer && typeof answer === "object" ? { ...next, ...answer } : next;
|
|
6031
6154
|
void run();
|
|
6032
6155
|
});
|
|
6033
6156
|
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
@@ -6138,6 +6261,8 @@ function createViewer(el, options) {
|
|
|
6138
6261
|
listeners.abort();
|
|
6139
6262
|
el.innerHTML = "";
|
|
6140
6263
|
el.classList.remove("fr-viewer");
|
|
6264
|
+
delete el.dataset.frState;
|
|
6265
|
+
delete el.dataset.frRun;
|
|
6141
6266
|
}
|
|
6142
6267
|
};
|
|
6143
6268
|
}
|
|
@@ -6185,7 +6310,7 @@ function ensureIds(def) {
|
|
|
6185
6310
|
for (const s of bandSlots(def)) for (const el of s.band.elements) if (el.id) all.add(el.id);
|
|
6186
6311
|
const seen = /* @__PURE__ */ new Set();
|
|
6187
6312
|
for (const s of bandSlots(def)) for (const el of s.band.elements) {
|
|
6188
|
-
if (el.id && /^[\w
|
|
6313
|
+
if (el.id && /^[\w:.-]{1,64}$/.test(String(el.id)) && !seen.has(el.id)) {
|
|
6189
6314
|
seen.add(el.id);
|
|
6190
6315
|
continue;
|
|
6191
6316
|
}
|