@formfeed/engine 0.3.10 → 0.3.11
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/index.cjs +2471 -354
- package/index.js +2471 -354
- package/package.json +1 -1
- package/types/frame/fit.d.cts +13 -0
- package/types/frame/fit.d.ts +13 -0
- package/types/frame/fit.entry.d.cts +1 -0
- package/types/frame/fit.entry.d.ts +1 -0
- package/types/frame/stage.d.cts +22 -0
- package/types/frame/stage.d.ts +22 -0
- package/types/frame/stage.entry.d.cts +1 -0
- package/types/frame/stage.entry.d.ts +1 -0
- package/types/frame/text-edit.d.cts +75 -0
- package/types/frame/text-edit.d.ts +75 -0
- package/types/frame/text-edit.entry.d.cts +1 -0
- package/types/frame/text-edit.entry.d.ts +1 -0
- package/types/index.d.cts +21 -14
- package/types/index.d.ts +21 -14
- package/types/lib/assemble.d.cts +5 -0
- package/types/lib/assemble.d.ts +5 -0
- package/types/lib/design/emit.d.cts +20 -0
- package/types/lib/design/emit.d.ts +20 -0
- package/types/lib/design/fonts.d.cts +27 -0
- package/types/lib/design/fonts.d.ts +27 -0
- package/types/lib/design/hash.d.cts +6 -0
- package/types/lib/design/hash.d.ts +6 -0
- package/types/lib/design/index.d.cts +12 -0
- package/types/lib/design/index.d.ts +12 -0
- package/types/lib/design/layers.d.cts +12 -0
- package/types/lib/design/layers.d.ts +12 -0
- package/types/lib/design/parse.d.cts +25 -0
- package/types/lib/design/parse.d.ts +25 -0
- package/types/lib/design/starters.d.cts +14 -0
- package/types/lib/design/starters.d.ts +14 -0
- package/types/lib/design/types.d.cts +171 -0
- package/types/lib/design/types.d.ts +171 -0
- package/types/lib/dialect.d.cts +32 -0
- package/types/lib/dialect.d.ts +32 -0
- package/types/lib/generated/frames.d.cts +6 -0
- package/types/lib/generated/frames.d.ts +6 -0
- package/types/lib/preview.d.cts +16 -2
- package/types/lib/preview.d.ts +16 -2
- package/types/lib/source-edit.d.cts +112 -0
- package/types/lib/source-edit.d.ts +112 -0
- package/types/lib/source-positions.d.cts +12 -3
- package/types/lib/source-positions.d.ts +12 -3
- package/types/lib/source-scan.d.cts +61 -0
- package/types/lib/source-scan.d.ts +61 -0
- package/types/lib/translation.d.cts +17 -0
- package/types/lib/translation.d.ts +17 -0
package/index.js
CHANGED
|
@@ -182,11 +182,11 @@ function validateEpc(input) {
|
|
|
182
182
|
const purpose = String(input.purpose ?? "");
|
|
183
183
|
if (purpose.length > 4) problems.push({ field: "purpose", code: "too_long", max: 4, actual: purpose.length });
|
|
184
184
|
const reference = String(input.reference ?? "").trim();
|
|
185
|
-
const
|
|
186
|
-
if (reference &&
|
|
185
|
+
const text2 = String(input.text ?? "").trim();
|
|
186
|
+
if (reference && text2) problems.push({ field: "reference", code: "reference_or_text" });
|
|
187
187
|
if (reference.length > 35)
|
|
188
188
|
problems.push({ field: "reference", code: "too_long", max: 35, actual: reference.length });
|
|
189
|
-
if (
|
|
189
|
+
if (text2.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text2.length });
|
|
190
190
|
const bytes = byteLength(epcPayload(input));
|
|
191
191
|
if (bytes > EPC_MAX_BYTES)
|
|
192
192
|
problems.push({ field: "payload", code: "too_long", max: EPC_MAX_BYTES, actual: bytes });
|
|
@@ -222,6 +222,12 @@ var barcodeTypes = {
|
|
|
222
222
|
datamatrix: "datamatrix",
|
|
223
223
|
pdf417: "pdf417"
|
|
224
224
|
};
|
|
225
|
+
var ownTextLayout = /* @__PURE__ */ new Set(["ean13", "ean8", "upca"]);
|
|
226
|
+
function codeText(helper, value) {
|
|
227
|
+
const text2 = value === void 0 || value === null ? "" : String(value);
|
|
228
|
+
if (text2 === "") throw new Error(`${helper}: nothing to encode, the value is empty or missing from the data`);
|
|
229
|
+
return text2;
|
|
230
|
+
}
|
|
225
231
|
var codeHelpers = [
|
|
226
232
|
{
|
|
227
233
|
name: "qrcode",
|
|
@@ -238,7 +244,7 @@ var codeHelpers = [
|
|
|
238
244
|
},
|
|
239
245
|
fn: (ctx, value, options) => {
|
|
240
246
|
const o = options ?? {};
|
|
241
|
-
const svg = qrSvg(
|
|
247
|
+
const svg = qrSvg(codeText("qrcode", value), o);
|
|
242
248
|
if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "QR code" });
|
|
243
249
|
return svgDataUri(svg);
|
|
244
250
|
}
|
|
@@ -258,16 +264,17 @@ var codeHelpers = [
|
|
|
258
264
|
fn: (ctx, value, options) => {
|
|
259
265
|
const o = options ?? {};
|
|
260
266
|
const bcid = barcodeTypes[String(o.type ?? "code128").toLowerCase()] ?? "code128";
|
|
267
|
+
const text2 = codeText("barcode", value);
|
|
261
268
|
const svg = toSVG({
|
|
262
269
|
bcid,
|
|
263
|
-
text:
|
|
270
|
+
text: text2,
|
|
264
271
|
height: o.height ?? 12,
|
|
265
272
|
...o.width === void 0 ? {} : { width: o.width },
|
|
266
273
|
scale: o.scale ?? 2,
|
|
267
274
|
includetext: o.text !== false,
|
|
268
|
-
textxalign: "center"
|
|
275
|
+
...ownTextLayout.has(bcid) ? {} : { textxalign: "center" }
|
|
269
276
|
});
|
|
270
|
-
if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${
|
|
277
|
+
if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text2}` });
|
|
271
278
|
return svgDataUri(svg);
|
|
272
279
|
}
|
|
273
280
|
},
|
|
@@ -318,6 +325,22 @@ import { toCardinal as frWords } from "n2words/fr";
|
|
|
318
325
|
import { toCardinal as itWords } from "n2words/it";
|
|
319
326
|
import { toCardinal as nlWords } from "n2words/nl";
|
|
320
327
|
|
|
328
|
+
// packages/engine/src/lib/translation.ts
|
|
329
|
+
function resolveTranslation(dictionaries, locale, key) {
|
|
330
|
+
const lang = locale.split("-")[0] ?? locale;
|
|
331
|
+
for (const candidate of [locale, lang, "en"]) {
|
|
332
|
+
const text2 = dictionaries?.[candidate]?.[key];
|
|
333
|
+
if (typeof text2 === "string") return { text: text2, from: candidate };
|
|
334
|
+
}
|
|
335
|
+
return { text: key, from: null };
|
|
336
|
+
}
|
|
337
|
+
function translationTarget(dictionaries, locale, key) {
|
|
338
|
+
const lang = locale.split("-")[0] ?? locale;
|
|
339
|
+
const { from } = resolveTranslation(dictionaries, locale, key);
|
|
340
|
+
if (from === locale || from === lang) return from;
|
|
341
|
+
return dictionaries?.[locale] ? locale : lang;
|
|
342
|
+
}
|
|
343
|
+
|
|
321
344
|
// packages/engine/src/lib/helpers/office.ts
|
|
322
345
|
var officeUnsupportedHelpers = /* @__PURE__ */ new Set(["chart", "markdown"]);
|
|
323
346
|
function officeUnsupported(name) {
|
|
@@ -362,7 +385,9 @@ var str = (v) => v === void 0 || v === null ? "" : String(v);
|
|
|
362
385
|
var opt = (v, fallback) => v === void 0 || v === null || v === "" ? fallback : v;
|
|
363
386
|
function requireFinite(helper, value) {
|
|
364
387
|
if (typeof value === "number" && !Number.isFinite(value))
|
|
365
|
-
throw new Error(
|
|
388
|
+
throw new Error(
|
|
389
|
+
`${helper}: got ${value}, the result of a calculation with a value that is missing or not a number`
|
|
390
|
+
);
|
|
366
391
|
}
|
|
367
392
|
var wordsByLang = {
|
|
368
393
|
de: deWords,
|
|
@@ -540,13 +565,20 @@ var formatHelpers = [
|
|
|
540
565
|
if (Number.isNaN(n)) return "";
|
|
541
566
|
const named = args.find(isNamedArgs) ?? {};
|
|
542
567
|
const [decimals, method] = args.filter((a) => !isNamedArgs(a));
|
|
543
|
-
const places = Number(
|
|
568
|
+
const places = Number(
|
|
569
|
+
opt(decimals, opt(named["precision"], opt(named["decimals"], 0)))
|
|
570
|
+
);
|
|
544
571
|
const how = String(opt(method, opt(named["method"], "common")));
|
|
545
572
|
const f = 10 ** places;
|
|
546
573
|
const scaled = Number((Math.abs(n) * f).toPrecision(15));
|
|
547
|
-
if (how === "floor")
|
|
548
|
-
|
|
549
|
-
if (how
|
|
574
|
+
if (how === "floor")
|
|
575
|
+
return (n < 0 ? -Math.ceil(scaled) : Math.floor(scaled)) / f;
|
|
576
|
+
if (how === "ceil")
|
|
577
|
+
return (n < 0 ? -Math.floor(scaled) : Math.ceil(scaled)) / f;
|
|
578
|
+
if (how !== "common")
|
|
579
|
+
throw new Error(
|
|
580
|
+
`round: method must be 'common', 'floor' or 'ceil', not '${how}'`
|
|
581
|
+
);
|
|
550
582
|
return Math.round(scaled + Number.EPSILON) / f * Math.sign(n);
|
|
551
583
|
}
|
|
552
584
|
},
|
|
@@ -778,11 +810,9 @@ var formatHelpers = [
|
|
|
778
810
|
category: "text"
|
|
779
811
|
},
|
|
780
812
|
fn: (ctx, key, params) => {
|
|
781
|
-
const
|
|
782
|
-
const lang = ctx.locale.split("-")[0] ?? ctx.locale;
|
|
783
|
-
const text = dictionaries[ctx.locale]?.[str(key)] ?? dictionaries[lang]?.[str(key)] ?? dictionaries["en"]?.[str(key)] ?? str(key);
|
|
813
|
+
const { text: text2 } = resolveTranslation(ctx.i18n, ctx.locale, str(key));
|
|
784
814
|
const p = params ?? {};
|
|
785
|
-
return
|
|
815
|
+
return text2.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
|
|
786
816
|
}
|
|
787
817
|
},
|
|
788
818
|
{
|
|
@@ -1107,8 +1137,8 @@ function imageMarkup(url, options = {}) {
|
|
|
1107
1137
|
options.fit ? `object-fit:${options.fit}` : ""
|
|
1108
1138
|
].filter(Boolean).join(";");
|
|
1109
1139
|
const cls = options.class ? ` class="${escapeHtml(options.class)}"` : "";
|
|
1110
|
-
const
|
|
1111
|
-
return `<img src="${escapeHtml(url)}" alt="${escapeHtml(options.alt ?? "")}"${cls}${
|
|
1140
|
+
const style2 = styles ? ` style="${styles}"` : "";
|
|
1141
|
+
return `<img src="${escapeHtml(url)}" alt="${escapeHtml(options.alt ?? "")}"${cls}${style2}>`;
|
|
1112
1142
|
}
|
|
1113
1143
|
function resolveAsset(base, url) {
|
|
1114
1144
|
if (!base || /^[a-z][a-z0-9+.-]*:/i.test(url)) return url;
|
|
@@ -1396,11 +1426,11 @@ function scanBlocks(source, spec) {
|
|
|
1396
1426
|
while (m = pattern.exec(source)) {
|
|
1397
1427
|
const keyword = m[1] ?? "";
|
|
1398
1428
|
const rest = m[2] ?? "";
|
|
1399
|
-
const
|
|
1429
|
+
const range2 = rangeOf(source, m.index, m.index + m[0].length);
|
|
1400
1430
|
if (inRaw) {
|
|
1401
1431
|
if (spec.closers[keyword] === inRaw) {
|
|
1402
|
-
const
|
|
1403
|
-
if (
|
|
1432
|
+
const open2 = stack.pop();
|
|
1433
|
+
if (open2) open2.block.close = range2;
|
|
1404
1434
|
inRaw = null;
|
|
1405
1435
|
}
|
|
1406
1436
|
continue;
|
|
@@ -1408,7 +1438,7 @@ function scanBlocks(source, spec) {
|
|
|
1408
1438
|
if (isBlockOpener(keyword, rest)) {
|
|
1409
1439
|
const block = {
|
|
1410
1440
|
type: spec.openers[keyword] ?? "other",
|
|
1411
|
-
open:
|
|
1441
|
+
open: range2
|
|
1412
1442
|
};
|
|
1413
1443
|
blocks.push(block);
|
|
1414
1444
|
stack.push({ keyword, block });
|
|
@@ -1422,7 +1452,7 @@ function scanBlocks(source, spec) {
|
|
|
1422
1452
|
code: "unexpected-end-tag",
|
|
1423
1453
|
message: `Unexpected {% ${keyword} %} without an open block`,
|
|
1424
1454
|
args: { keyword },
|
|
1425
|
-
range
|
|
1455
|
+
range: range2
|
|
1426
1456
|
});
|
|
1427
1457
|
} else if (top.keyword !== expected) {
|
|
1428
1458
|
diagnostics.push({
|
|
@@ -1430,16 +1460,16 @@ function scanBlocks(source, spec) {
|
|
|
1430
1460
|
code: "mismatched-end-tag",
|
|
1431
1461
|
message: `Expected {% end${top.keyword} %} to close the {% ${top.keyword} %} at line ${top.block.open.start.line}, found {% ${keyword} %}`,
|
|
1432
1462
|
args: { open: top.keyword, line: top.block.open.start.line, found: keyword },
|
|
1433
|
-
range
|
|
1463
|
+
range: range2
|
|
1434
1464
|
});
|
|
1435
1465
|
const idx = stack.map((s) => s.keyword).lastIndexOf(expected);
|
|
1436
1466
|
if (idx >= 0) {
|
|
1437
1467
|
const closed = stack.splice(idx, 1)[0];
|
|
1438
|
-
if (closed) closed.block.close =
|
|
1468
|
+
if (closed) closed.block.close = range2;
|
|
1439
1469
|
}
|
|
1440
1470
|
} else {
|
|
1441
1471
|
stack.pop();
|
|
1442
|
-
top.block.close =
|
|
1472
|
+
top.block.close = range2;
|
|
1443
1473
|
}
|
|
1444
1474
|
} else if (spec.continuations.includes(keyword) && !stack.length) {
|
|
1445
1475
|
diagnostics.push({
|
|
@@ -1447,17 +1477,17 @@ function scanBlocks(source, spec) {
|
|
|
1447
1477
|
code: "unexpected-tag",
|
|
1448
1478
|
message: `{% ${keyword} %} outside of a block`,
|
|
1449
1479
|
args: { keyword },
|
|
1450
|
-
range
|
|
1480
|
+
range: range2
|
|
1451
1481
|
});
|
|
1452
1482
|
}
|
|
1453
1483
|
}
|
|
1454
|
-
for (const
|
|
1484
|
+
for (const open2 of stack) {
|
|
1455
1485
|
diagnostics.push({
|
|
1456
1486
|
severity: "error",
|
|
1457
1487
|
code: "unclosed-block",
|
|
1458
|
-
message: `{% ${
|
|
1459
|
-
args: { keyword:
|
|
1460
|
-
range:
|
|
1488
|
+
message: `{% ${open2.keyword} %} is never closed; add {% end${open2.keyword} %}`,
|
|
1489
|
+
args: { keyword: open2.keyword },
|
|
1490
|
+
range: open2.block.open
|
|
1461
1491
|
});
|
|
1462
1492
|
}
|
|
1463
1493
|
return { blocks, diagnostics };
|
|
@@ -1889,8 +1919,8 @@ var SPEC = /^(?:([\s\S])?([<>=^]))?([+\- ])?(#)?(0)?(\d+)?([,_])?(?:\.(\d+))?([b
|
|
|
1889
1919
|
function group(digits, separator) {
|
|
1890
1920
|
return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
1891
1921
|
}
|
|
1892
|
-
function pythonExponent(
|
|
1893
|
-
return
|
|
1922
|
+
function pythonExponent(text2) {
|
|
1923
|
+
return text2.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
|
|
1894
1924
|
}
|
|
1895
1925
|
function formatNumber(value, type, precision, alternate) {
|
|
1896
1926
|
if (!Number.isFinite(value)) return Number.isNaN(value) ? "nan" : "inf";
|
|
@@ -1903,20 +1933,20 @@ function formatNumber(value, type, precision, alternate) {
|
|
|
1903
1933
|
return `${(abs * 100).toFixed(precision ?? 6)}%`;
|
|
1904
1934
|
case "e":
|
|
1905
1935
|
case "E": {
|
|
1906
|
-
const
|
|
1907
|
-
return type === "E" ?
|
|
1936
|
+
const text2 = pythonExponent(abs.toExponential(precision ?? 6));
|
|
1937
|
+
return type === "E" ? text2.toUpperCase() : text2;
|
|
1908
1938
|
}
|
|
1909
1939
|
case "g":
|
|
1910
1940
|
case "G": {
|
|
1911
1941
|
const p = precision === 0 ? 1 : precision ?? 6;
|
|
1912
1942
|
const exp = abs === 0 ? 0 : Math.floor(Math.log10(abs));
|
|
1913
|
-
let
|
|
1943
|
+
let text2 = exp < -4 || exp >= p ? pythonExponent(abs.toExponential(p - 1)) : abs.toFixed(Math.max(0, p - 1 - exp));
|
|
1914
1944
|
if (!alternate) {
|
|
1915
|
-
const [mantissa = "", exponent] =
|
|
1945
|
+
const [mantissa = "", exponent] = text2.split("e");
|
|
1916
1946
|
const trimmed = mantissa.includes(".") ? mantissa.replace(/0+$/, "").replace(/\.$/, "") : mantissa;
|
|
1917
|
-
|
|
1947
|
+
text2 = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
|
|
1918
1948
|
}
|
|
1919
|
-
return type === "G" ?
|
|
1949
|
+
return type === "G" ? text2.toUpperCase() : text2;
|
|
1920
1950
|
}
|
|
1921
1951
|
case "d":
|
|
1922
1952
|
case "n":
|
|
@@ -1985,35 +2015,35 @@ function lookupField(name, positional, named, next) {
|
|
|
1985
2015
|
function pythonFormat(template, positional, named = {}) {
|
|
1986
2016
|
let auto = 0;
|
|
1987
2017
|
const next = () => auto++;
|
|
1988
|
-
const render = (
|
|
2018
|
+
const render = (text2) => {
|
|
1989
2019
|
let out = "";
|
|
1990
|
-
for (let i = 0; i <
|
|
1991
|
-
const ch =
|
|
2020
|
+
for (let i = 0; i < text2.length; i++) {
|
|
2021
|
+
const ch = text2[i];
|
|
1992
2022
|
if (ch === "}") {
|
|
1993
2023
|
out += "}";
|
|
1994
|
-
if (
|
|
2024
|
+
if (text2[i + 1] === "}") i++;
|
|
1995
2025
|
continue;
|
|
1996
2026
|
}
|
|
1997
2027
|
if (ch !== "{") {
|
|
1998
2028
|
out += ch;
|
|
1999
2029
|
continue;
|
|
2000
2030
|
}
|
|
2001
|
-
if (
|
|
2031
|
+
if (text2[i + 1] === "{") {
|
|
2002
2032
|
out += "{";
|
|
2003
2033
|
i++;
|
|
2004
2034
|
continue;
|
|
2005
2035
|
}
|
|
2006
2036
|
let depth = 1;
|
|
2007
2037
|
let j = i + 1;
|
|
2008
|
-
for (; j <
|
|
2009
|
-
if (
|
|
2010
|
-
else if (
|
|
2038
|
+
for (; j < text2.length && depth > 0; j++) {
|
|
2039
|
+
if (text2[j] === "{") depth++;
|
|
2040
|
+
else if (text2[j] === "}") depth--;
|
|
2011
2041
|
}
|
|
2012
2042
|
if (depth > 0) {
|
|
2013
|
-
out +=
|
|
2043
|
+
out += text2.slice(i);
|
|
2014
2044
|
break;
|
|
2015
2045
|
}
|
|
2016
|
-
const field =
|
|
2046
|
+
const field = text2.slice(i + 1, j - 1);
|
|
2017
2047
|
const colon = field.indexOf(":");
|
|
2018
2048
|
const head = colon < 0 ? field : field.slice(0, colon);
|
|
2019
2049
|
const name = head.replace(/![rsa]$/, "");
|
|
@@ -3535,7 +3565,7 @@ function layeredPartials(own, shared2, onShared) {
|
|
|
3535
3565
|
};
|
|
3536
3566
|
}
|
|
3537
3567
|
|
|
3538
|
-
// packages/engine/src/lib/
|
|
3568
|
+
// packages/engine/src/lib/dialect.ts
|
|
3539
3569
|
var loop = (engine, item, list2, body) => engine === "handlebars" ? `{{#each ${list2}}}
|
|
3540
3570
|
${body.replace(new RegExp(`(?<![\\w.])${item}\\.`, "g"), "")}
|
|
3541
3571
|
{{/each}}` : `{% for ${item} in ${list2} %}
|
|
@@ -3556,6 +3586,32 @@ var tr = (engine, key, params = {}) => {
|
|
|
3556
3586
|
return `{{ '${key}' | t${entries.length ? ": " + entries.map(([k, v]) => `${k}: ${v}`).join(", ") : ""} }}`;
|
|
3557
3587
|
return `{{ t('${key}'${entries.length ? `, { ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")} }` : ""}) }}`;
|
|
3558
3588
|
};
|
|
3589
|
+
var ifBlock = (engine, cond, body) => engine === "handlebars" ? `{{#if ${cond}}}${body}{{/if}}` : `{% if ${cond} %}${body}{% endif %}`;
|
|
3590
|
+
function stringLiteral(engine, value) {
|
|
3591
|
+
if (engine === "liquid") {
|
|
3592
|
+
if (!value.includes("'")) return `'${value}'`;
|
|
3593
|
+
if (!value.includes('"')) return `"${value}"`;
|
|
3594
|
+
return `'${value.replace(/'/g, "\u2019")}'`;
|
|
3595
|
+
}
|
|
3596
|
+
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
3597
|
+
}
|
|
3598
|
+
function filterCall(engine, input, name, args = []) {
|
|
3599
|
+
if (engine === "handlebars")
|
|
3600
|
+
return `{{${name} ${input}${args.map((a) => ` ${a}`).join("")}}}`;
|
|
3601
|
+
if (engine === "liquid")
|
|
3602
|
+
return `{{ ${input} | ${name}${args.length ? `: ${args.join(", ")}` : ""} }}`;
|
|
3603
|
+
return `{{ ${input} | ${name}${args.length ? `(${args.join(", ")})` : ""} }}`;
|
|
3604
|
+
}
|
|
3605
|
+
function helperCall(engine, name, input, options = {}) {
|
|
3606
|
+
const entries = Object.entries(options);
|
|
3607
|
+
if (engine === "handlebars")
|
|
3608
|
+
return `{{${name} ${input}${entries.map(([k, v]) => ` ${k}=${v}`).join("")}}}`;
|
|
3609
|
+
if (engine === "liquid")
|
|
3610
|
+
return `{{ ${input} | ${name}${entries.length ? `: ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")}` : ""} }}`;
|
|
3611
|
+
return `{{ ${name}(${input}${entries.length ? `, { ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")} }` : ""}) }}`;
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
// packages/engine/src/lib/snippets.ts
|
|
3559
3615
|
function forAll(build) {
|
|
3560
3616
|
return {
|
|
3561
3617
|
jinja2: build("jinja2"),
|
|
@@ -3596,8 +3652,18 @@ var snippets = [
|
|
|
3596
3652
|
</div>$0`
|
|
3597
3653
|
),
|
|
3598
3654
|
sampleData: {
|
|
3599
|
-
company: {
|
|
3600
|
-
|
|
3655
|
+
company: {
|
|
3656
|
+
name: "Fennlor Studio GmbH",
|
|
3657
|
+
street: "Musterstra\xDFe 1",
|
|
3658
|
+
zip: "12345",
|
|
3659
|
+
city: "Musterstadt"
|
|
3660
|
+
},
|
|
3661
|
+
customer: {
|
|
3662
|
+
name: "Olvarest GmbH",
|
|
3663
|
+
street: "Beispielweg 2",
|
|
3664
|
+
zip: "54321",
|
|
3665
|
+
city: "Beispielstadt"
|
|
3666
|
+
}
|
|
3601
3667
|
},
|
|
3602
3668
|
// Form B puts the field 45 mm below the top edge and 20 mm from the left; the page margins decide where it lands
|
|
3603
3669
|
css: `.ff-window { width: 85mm; height: 45mm; box-sizing: border-box; padding: 0 5mm; overflow: hidden; }
|
|
@@ -3624,8 +3690,16 @@ var snippets = [
|
|
|
3624
3690
|
.ff-reference th { text-align: left; font-weight: normal; color: #555; padding: 1px 12px 1px 0; }
|
|
3625
3691
|
.ff-reference td { text-align: right; padding: 1px 0; }`,
|
|
3626
3692
|
i18n: {
|
|
3627
|
-
en: {
|
|
3628
|
-
|
|
3693
|
+
en: {
|
|
3694
|
+
"reference.customer": "Customer no.",
|
|
3695
|
+
"reference.invoice": "Invoice no.",
|
|
3696
|
+
"reference.date": "Date"
|
|
3697
|
+
},
|
|
3698
|
+
de: {
|
|
3699
|
+
"reference.customer": "Kundennummer",
|
|
3700
|
+
"reference.invoice": "Rechnungsnummer",
|
|
3701
|
+
"reference.date": "Datum"
|
|
3702
|
+
}
|
|
3629
3703
|
}
|
|
3630
3704
|
},
|
|
3631
3705
|
{
|
|
@@ -3640,14 +3714,24 @@ var snippets = [
|
|
|
3640
3714
|
</div>$0`
|
|
3641
3715
|
),
|
|
3642
3716
|
sampleData: {
|
|
3643
|
-
invoice: {
|
|
3717
|
+
invoice: {
|
|
3718
|
+
number: "2026-0042",
|
|
3719
|
+
period_start: "2026-09-01",
|
|
3720
|
+
period_end: "2026-09-30"
|
|
3721
|
+
}
|
|
3644
3722
|
},
|
|
3645
3723
|
css: `.ff-heading { margin: 24px 0 16px; }
|
|
3646
3724
|
.ff-heading h1 { font-family: var(--brand-font-heading, inherit); font-size: 18pt; color: var(--brand-color-primary, inherit); margin: 0 0 4px; }
|
|
3647
3725
|
.ff-heading p { margin: 0; color: #555; }`,
|
|
3648
3726
|
i18n: {
|
|
3649
|
-
en: {
|
|
3650
|
-
|
|
3727
|
+
en: {
|
|
3728
|
+
"heading.title": "Invoice {number}",
|
|
3729
|
+
"heading.period": "Service period:"
|
|
3730
|
+
},
|
|
3731
|
+
de: {
|
|
3732
|
+
"heading.title": "Rechnung {number}",
|
|
3733
|
+
"heading.period": "Leistungszeitraum:"
|
|
3734
|
+
}
|
|
3651
3735
|
}
|
|
3652
3736
|
},
|
|
3653
3737
|
{
|
|
@@ -3774,11 +3858,17 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
|
|
|
3774
3858
|
group: "invoice",
|
|
3775
3859
|
title: "Small business VAT note",
|
|
3776
3860
|
description: "States that no VAT is charged under \xA7 19 UStG (Kleinunternehmerregelung).",
|
|
3777
|
-
source: forAll(
|
|
3861
|
+
source: forAll(
|
|
3862
|
+
(e) => `<p class="ff-note">${tr(e, "vat.small_business")}</p>$0`
|
|
3863
|
+
),
|
|
3778
3864
|
css: `.ff-note { margin-top: 12px; font-size: 9pt; color: #444; }`,
|
|
3779
3865
|
i18n: {
|
|
3780
|
-
en: {
|
|
3781
|
-
|
|
3866
|
+
en: {
|
|
3867
|
+
"vat.small_business": "No VAT is charged under the small business scheme of \xA7 19 UStG."
|
|
3868
|
+
},
|
|
3869
|
+
de: {
|
|
3870
|
+
"vat.small_business": "Gem\xE4\xDF \xA7 19 UStG wird keine Umsatzsteuer berechnet."
|
|
3871
|
+
}
|
|
3782
3872
|
}
|
|
3783
3873
|
},
|
|
3784
3874
|
{
|
|
@@ -3839,7 +3929,9 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
|
|
|
3839
3929
|
<span>${tr(e, "footer.page")} <span class="pageNumber"></span> ${tr(e, "footer.of")} <span class="totalPages"></span></span>
|
|
3840
3930
|
</div>$0`
|
|
3841
3931
|
),
|
|
3842
|
-
sampleData: {
|
|
3932
|
+
sampleData: {
|
|
3933
|
+
company: { name: "Fennlor Studio GmbH", email: "hello@fennlor.example" }
|
|
3934
|
+
},
|
|
3843
3935
|
i18n: {
|
|
3844
3936
|
en: { "footer.page": "Page", "footer.of": "of" },
|
|
3845
3937
|
de: { "footer.page": "Seite", "footer.of": "von" }
|
|
@@ -3924,7 +4016,10 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
|
|
|
3924
4016
|
},
|
|
3925
4017
|
i18n: {
|
|
3926
4018
|
en: { "qr.alt": "Payment QR code", "qr.caption": "Scan to pay" },
|
|
3927
|
-
de: {
|
|
4019
|
+
de: {
|
|
4020
|
+
"qr.alt": "QR-Code zur Zahlung",
|
|
4021
|
+
"qr.caption": "Zum Bezahlen scannen"
|
|
4022
|
+
}
|
|
3928
4023
|
},
|
|
3929
4024
|
sampleData: {
|
|
3930
4025
|
payment: {
|
|
@@ -4043,7 +4138,9 @@ var selectorKey = (prelude) => (prelude.split(";").pop() ?? "").trim().replace(/
|
|
|
4043
4138
|
function mergeSnippetCss(css2, snippetCss) {
|
|
4044
4139
|
if (!snippetCss?.trim()) return css2;
|
|
4045
4140
|
const defined = new Set(
|
|
4046
|
-
[...stripComments(css2).matchAll(/([^{}]+)\{/g)].map(
|
|
4141
|
+
[...stripComments(css2).matchAll(/([^{}]+)\{/g)].map(
|
|
4142
|
+
(m) => selectorKey(m[1] ?? "")
|
|
4143
|
+
)
|
|
4047
4144
|
);
|
|
4048
4145
|
const missing = [...stripComments(snippetCss).matchAll(/([^{}]+)\{[^{}]*\}/g)].filter((m) => !defined.has(selectorKey(m[1] ?? ""))).map((m) => m[0].trim());
|
|
4049
4146
|
if (!missing.length) return css2;
|
|
@@ -4091,9 +4188,9 @@ function pick(input, ...keys) {
|
|
|
4091
4188
|
function toLength(value) {
|
|
4092
4189
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
4093
4190
|
if (typeof value === "number") return `${value}mm`;
|
|
4094
|
-
const
|
|
4095
|
-
if (/^-?\d+(\.\d+)?$/.test(
|
|
4096
|
-
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(
|
|
4191
|
+
const text2 = String(value).trim();
|
|
4192
|
+
if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
|
|
4193
|
+
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
|
|
4097
4194
|
return void 0;
|
|
4098
4195
|
}
|
|
4099
4196
|
function toMarginLength(value) {
|
|
@@ -4113,7 +4210,7 @@ function convertHeaderFooter(html) {
|
|
|
4113
4210
|
if (typeof html !== "string" || !html.trim()) return void 0;
|
|
4114
4211
|
return chromePlaceholders.reduce((out, [pattern, span]) => out.replace(pattern, span), html);
|
|
4115
4212
|
}
|
|
4116
|
-
var escapeText = (
|
|
4213
|
+
var escapeText = (text2) => text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4117
4214
|
var visibleMarkup = (html) => html.replace(/<style[\s\S]*?<\/style>/gi, "").trim();
|
|
4118
4215
|
function apitemplateChrome(take, kind) {
|
|
4119
4216
|
const custom = take(`custom_${kind}`);
|
|
@@ -4127,7 +4224,7 @@ function apitemplateChrome(take, kind) {
|
|
|
4127
4224
|
if (!slots.some(Boolean)) return { fromSlots: false };
|
|
4128
4225
|
const sizeText = size === void 0 ? "" : String(size).trim();
|
|
4129
4226
|
const fontSize2 = /^\d+(\.\d+)?$/.test(sizeText) ? `${sizeText}px` : /^\d+(\.\d+)?(px|pt|mm)$/.test(sizeText) ? sizeText : "9px";
|
|
4130
|
-
const cells = slots.map((
|
|
4227
|
+
const cells = slots.map((text2, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text2)}</span>`).join("");
|
|
4131
4228
|
const styles = customHtml.match(/<style[\s\S]*?<\/style>/gi)?.join("") ?? "";
|
|
4132
4229
|
return {
|
|
4133
4230
|
html: convertHeaderFooter(`${styles}<div style="display:flex;gap:4mm;font-size:${fontSize2}">${cells}</div>`),
|
|
@@ -4136,8 +4233,8 @@ function apitemplateChrome(take, kind) {
|
|
|
4136
4233
|
}
|
|
4137
4234
|
function clearsChromePadding(html, kind) {
|
|
4138
4235
|
if (!html) return false;
|
|
4139
|
-
for (const
|
|
4140
|
-
for (const rule2 of (
|
|
4236
|
+
for (const style2 of html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi))
|
|
4237
|
+
for (const rule2 of (style2[1] ?? "").matchAll(/([^{}]+)\{([^}]*)\}/g))
|
|
4141
4238
|
if (new RegExp(`#${kind}\\b`).test(rule2[1] ?? "") && /(^|;)\s*padding\s*:\s*0(px|mm)?\s*(!important\s*)?(;|$)/i.test((rule2[2] ?? "").trim()))
|
|
4142
4239
|
return true;
|
|
4143
4240
|
return false;
|
|
@@ -4288,9 +4385,9 @@ function checkTemplate(engineId, html, sampleData, name, docs) {
|
|
|
4288
4385
|
function lengthMm(value) {
|
|
4289
4386
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
4290
4387
|
if (typeof value === "number" && Number.isFinite(value)) return `${value}mm`;
|
|
4291
|
-
const
|
|
4292
|
-
if (/^-?\d+(\.\d+)?$/.test(
|
|
4293
|
-
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(
|
|
4388
|
+
const text2 = String(value).trim();
|
|
4389
|
+
if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
|
|
4390
|
+
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
|
|
4294
4391
|
return void 0;
|
|
4295
4392
|
}
|
|
4296
4393
|
function toBase64(value) {
|
|
@@ -4693,7 +4790,7 @@ var unsupportedFilters = {
|
|
|
4693
4790
|
in_time_zone: 'pass the zone to `date` as its second argument: `date: "dd.MM.yyyy", "Europe/Berlin"`'
|
|
4694
4791
|
};
|
|
4695
4792
|
function mapLiquid(source, fn) {
|
|
4696
|
-
return source.replace(/(\{\{-?|\{%-?)([\s\S]*?)(-?\}\}|-?%\})/g, (_m,
|
|
4793
|
+
return source.replace(/(\{\{-?|\{%-?)([\s\S]*?)(-?\}\}|-?%\})/g, (_m, open2, inner, close) => `${open2}${fn(inner)}${close}`);
|
|
4697
4794
|
}
|
|
4698
4795
|
function convertDialect(source, changes) {
|
|
4699
4796
|
const errors = [];
|
|
@@ -4807,11 +4904,11 @@ function inlineIncludes(source, blocks, errors, changes) {
|
|
|
4807
4904
|
}
|
|
4808
4905
|
var cssPaperFormats = { a3: "A3", a4: "A4", a5: "A5", letter: "Letter" };
|
|
4809
4906
|
var isoSizes = { a0: [841, 1189], a1: [594, 841], a2: [420, 594], a6: [105, 148] };
|
|
4810
|
-
var escapeHtml2 = (
|
|
4907
|
+
var escapeHtml2 = (text2) => text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4811
4908
|
function simpleHeaderFooter(part, margin) {
|
|
4812
4909
|
const cells = ["left", "center", "right"].map((side) => part[side] ?? "");
|
|
4813
4910
|
if (!cells.some((c) => c.trim())) return void 0;
|
|
4814
|
-
const cell3 = (
|
|
4911
|
+
const cell3 = (text2, align) => `<div style="flex: 1; text-align: ${align}">${escapeHtml2(text2).replace(/\[page\]/g, '<span class="pageNumber"></span>').replace(/\[topage\]/g, '<span class="totalPages"></span>')}</div>`;
|
|
4815
4912
|
return `<div style="display: flex; width: 100%; font-size: 10px; padding: 0 ${margin.right} 0 ${margin.left}">${cell3(cells[0] ?? "", "left")}${cell3(cells[1] ?? "", "center")}${cell3(cells[2] ?? "", "right")}</div>`;
|
|
4816
4913
|
}
|
|
4817
4914
|
function mapSettings(input, kind, warnings, changes) {
|
|
@@ -4980,11 +5077,11 @@ var DOCS3 = "/migrate/jsreport";
|
|
|
4980
5077
|
var COLLECTIONS = ["templates", "data", "assets", "components", "scripts", "folders"];
|
|
4981
5078
|
function readJsreportExport(files) {
|
|
4982
5079
|
const bundle = { templates: [], data: [], assets: [], components: [], scripts: [], folders: [] };
|
|
4983
|
-
for (const [path,
|
|
5080
|
+
for (const [path, text2] of Object.entries(files)) {
|
|
4984
5081
|
const normalized = path.replace(/\\/g, "/").replace(/^\.?\//, "");
|
|
4985
5082
|
if (normalized === "metadata.json") {
|
|
4986
5083
|
try {
|
|
4987
|
-
bundle.version = String(JSON.parse(
|
|
5084
|
+
bundle.version = String(JSON.parse(text2).reporterVersion ?? "");
|
|
4988
5085
|
} catch {
|
|
4989
5086
|
}
|
|
4990
5087
|
continue;
|
|
@@ -4992,7 +5089,7 @@ function readJsreportExport(files) {
|
|
|
4992
5089
|
const collection = normalized.split("/")[0];
|
|
4993
5090
|
if (!COLLECTIONS.includes(collection) || !normalized.endsWith(".json")) continue;
|
|
4994
5091
|
try {
|
|
4995
|
-
bundle[collection].push(JSON.parse(
|
|
5092
|
+
bundle[collection].push(JSON.parse(text2));
|
|
4996
5093
|
} catch {
|
|
4997
5094
|
}
|
|
4998
5095
|
}
|
|
@@ -5034,7 +5131,7 @@ function assetBase64(content) {
|
|
|
5034
5131
|
}
|
|
5035
5132
|
return toBase64(content);
|
|
5036
5133
|
}
|
|
5037
|
-
var escapeMustaches = (
|
|
5134
|
+
var escapeMustaches = (text2) => text2.replace(/\{\{/g, "\\{{");
|
|
5038
5135
|
function resolveAssets(source, bundle, options, warnings, changes) {
|
|
5039
5136
|
const v3 = /\{\{\{?\s*asset\s+(["'])([^"']+)\1(?:\s+(["'])([^"']+)\3)?\s*\}?\}\}/g;
|
|
5040
5137
|
const v2 = /\{#asset\s+([^\s}]+)(?:\s+@encoding=(\w+))?\s*\}/g;
|
|
@@ -5271,6 +5368,11 @@ function importJsreport(bundle, ref, options = {}) {
|
|
|
5271
5368
|
};
|
|
5272
5369
|
}
|
|
5273
5370
|
|
|
5371
|
+
// packages/engine/src/lib/generated/frames.ts
|
|
5372
|
+
var textEditRuntime = '"use strict";(()=>{var H=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function R(s,l={}){let g=l.clicks!==!1,p=l.live===!0,o=s.document,i=null,a=null,c=e=>{try{s.parent.postMessage(e,"*")}catch{}},b=o.createElement("style");b.setAttribute("data-formfeed","edit"),b.setAttribute("data-pagedjs-ignore",""),b.textContent=H,(o.head??o.documentElement).appendChild(b),g&&o.documentElement.classList.add("ff-edit-mode");let D=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!g)return;if(i&&!i.done&&i.element.contains(e.target)){e.stopImmediatePropagation();return}let t=D(e.target);if(!t)return;e.preventDefault(),e.stopImmediatePropagation(),i&&!i.done&&y();let n=t.getAttribute("data-ff-src")??"",r=t.getBoundingClientRect();a={src:n,element:t,x:e.clientX,y:e.clientY},c({type:"formfeed:edit-request",src:n,edit:t.getAttribute("data-ff-edit"),text:t.textContent??"",count:o.querySelectorAll(`[data-ff-src="${n.replace(/"/g,"")}"]`).length,split:t.hasAttribute("data-split-from")||t.hasAttribute("data-split-to"),rect:{left:r.left,top:r.top,width:r.width,height:r.height}})},!0),o.addEventListener("keydown",e=>{if(!g||i&&!i.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),c({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),s.addEventListener("message",e=>{if(e.source!==s.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?F(t):(t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end")&&(a=null,i&&!i.done&&h(!1)))});function F(e){i&&!i.done&&h(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):a&&a.src===e.src?a.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),n=e.at,r=e.layer?n&&Number.isFinite(n.x)&&Number.isFinite(n.y)?{x:Number(n.x),y:Number(n.y)}:null:a&&a.src===e.src?a:null;if(a=null,!t||!Array.isArray(e.items)){c({type:"formfeed:edit-cancel",token:String(e.token)});return}let m=Array.from(t.childNodes),f=o.createDocumentFragment();for(let u of e.items)if(u.kind==="text")f.appendChild(o.createTextNode(String(u.text)));else if(u.kind==="break")f.appendChild(o.createElement("br"));else if(u.kind==="chip"){let d=o.createElement("span");d.className="ff-chip",d.setAttribute("data-ff-chip",String(u.index)),d.setAttribute("contenteditable","false"),u.title&&(d.title=String(u.title)),d.textContent=String(u.text),f.appendChild(d)}for(let u of m)t.removeChild(u);t.appendChild(f),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,i={token:String(e.token),element:t,saved:m,allowBreaks:e.allowBreaks===!0,badge:e.note?B(t,String(e.note)):null,done:!1},t.addEventListener("keydown",E),t.addEventListener("beforeinput",v),t.addEventListener("paste",C),t.addEventListener("drop",L),t.addEventListener("focusout",A),p&&t.addEventListener("input",x),t.focus(),e.select==="all"?w(t):P(t,r),c({type:"formfeed:edit-started",token:i.token}),typeof e.insert=="string"&&e.insert&&k(e.insert.replace(/\\r\\n?|\\n/g," "))}function k(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=s.getSelection(),n=t&&t.rangeCount?t.getRangeAt(0):null;n&&(n.deleteContents(),n.insertNode(o.createTextNode(e)),n.collapse(!1),p&&x())}function x(){i&&!i.done&&c({type:"formfeed:edit-input",token:i.token,items:S(i.element)})}function B(e,t){let n=o.createElement("div");n.className="ff-edit-badge",n.textContent=t;let r=e.getBoundingClientRect();return n.style.left=`${r.left+s.scrollX}px`,n.style.top=`${Math.max(0,r.top+s.scrollY-22)}px`,o.body.appendChild(n),n}function w(e){let t=s.getSelection();if(!t)return;let n=o.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}function P(e,t){let n=s.getSelection();if(!n)return;let r=null,m=o;if(t){if(m.caretRangeFromPoint)r=m.caretRangeFromPoint(t.x,t.y);else if(m.caretPositionFromPoint){let f=m.caretPositionFromPoint(t.x,t.y);f&&(r=o.createRange(),r.setStart(f.offsetNode,f.offset))}}if(!r||!e.contains(r.startContainer))r=o.createRange(),r.selectNodeContents(e),r.collapse(!1);else if(t){let f=r.startContainer,d=(f.nodeType===1?f:f.parentElement)?.closest("[data-ff-chip]")??null;if(d&&d!==e&&e.contains(d)){let N=d.getBoundingClientRect();r=o.createRange(),t.x<N.left+N.width/2?r.setStartBefore(d):r.setStartAfter(d),r.collapse(!0)}}n.removeAllRanges(),n.addRange(r)}function E(e){let t=e.key;t==="Escape"?(e.preventDefault(),h(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),y()):t==="Enter"?(e.preventDefault(),i?.allowBreaks&&M()):t==="Tab"?(e.preventDefault(),y()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),p&&i&&!e.altKey&&c({type:"formfeed:edit-style",token:i.token,key:t.toLowerCase()}))}function v(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function C(e){e.preventDefault(),k((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function L(e){e.preventDefault()}function A(){s.setTimeout(()=>{i&&!i.done&&(!o.hasFocus()||!i.element.contains(o.activeElement))&&y()},0)}function M(){let e=s.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let n=o.createElement("br");t.insertNode(n),n.nextSibling||n.parentNode?.appendChild(o.createElement("br"));let r=o.createRange();r.setStartAfter(n),r.collapse(!0),e.removeAllRanges(),e.addRange(r)}function T(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",E),t.removeEventListener("beforeinput",v),t.removeEventListener("paste",C),t.removeEventListener("drop",L),t.removeEventListener("focusout",A),t.removeEventListener("input",x),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),i===e&&(i=null)}function y(){let e=i;if(!e||e.done)return;let t=S(e.element);T(e),c({type:"formfeed:edit-commit",token:e.token,items:t})}function h(e){let t=i;if(!t||t.done)return;let n=t.element;for(;n.firstChild;)n.removeChild(n.firstChild);for(let r of t.saved)n.appendChild(r);T(t),e&&c({type:"formfeed:edit-cancel",token:t.token})}}function S(s){let l=[],g=o=>{let i=l[l.length-1];i?.kind==="text"?i.text+=o:o&&l.push({kind:"text",text:o})},p=o=>{for(let i of Array.from(o.childNodes))if(i.nodeType===3)g(i.data);else if(i.nodeType===1){let a=i,c=a.getAttribute("data-ff-chip");c!==null?l.push({kind:"chip",index:Number(c)}):a.tagName==="BR"?l.push({kind:"break"}):((a.tagName==="DIV"||a.tagName==="P")&&l.length&&l.push({kind:"break"}),p(a))}};for(p(s);l[l.length-1]?.kind==="break";)l.pop();return l}R(window);})();';
|
|
5373
|
+
var fitRuntime = '"use strict";(()=>{function a(t,e,n){if(e===t)return t.scrollHeight<=t.clientHeight+.5&&t.scrollWidth<=t.clientWidth+.5;let o=n.getComputedStyle(t),i=t.clientHeight-(parseFloat(o.paddingTop)||0)-(parseFloat(o.paddingBottom)||0);return e.scrollHeight<=i+.5&&e.scrollWidth<=e.clientWidth+.5}function m(t,e){let n=t.firstElementChild??t;n.style.fontSize="";let o=parseFloat(e.getComputedStyle(n).fontSize)||16,i=Math.min(o,parseFloat(t.getAttribute("data-ff-fit-min")??"")||o/2);if(a(t,n,e))return o;let l=i,s=o;for(let d=0;d<14;d++){let r=(l+s)/2;n.style.fontSize=`${r}px`,a(t,n,e)?l=r:s=r}let f=Math.floor(l*100)/100;return n.style.fontSize=`${f}px`,f}function c(t){let e=()=>{for(let o of Array.from(t.document.querySelectorAll(\'[data-ff-fit="shrink"]\')))m(o,t);t.document.documentElement.setAttribute("data-ff-fit","done")};t.formfeedFit=e;let n=t.document.fonts;n?n.ready.then(e):t.addEventListener("load",e)}c(window);})();';
|
|
5374
|
+
var stageRuntime = '"use strict";(()=>{var I=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function D(a,l={}){let f=l.clicks!==!1,c=l.live===!0,o=a.document,n=null,s=null,u=e=>{try{a.parent.postMessage(e,"*")}catch{}},p=o.createElement("style");p.setAttribute("data-formfeed","edit"),p.setAttribute("data-pagedjs-ignore",""),p.textContent=I,(o.head??o.documentElement).appendChild(p),f&&o.documentElement.classList.add("ff-edit-mode");let d=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!f)return;if(n&&!n.done&&n.element.contains(e.target)){e.stopImmediatePropagation();return}let t=d(e.target);if(!t)return;e.preventDefault(),e.stopImmediatePropagation(),n&&!n.done&&v();let r=t.getAttribute("data-ff-src")??"",i=t.getBoundingClientRect();s={src:r,element:t,x:e.clientX,y:e.clientY},u({type:"formfeed:edit-request",src:r,edit:t.getAttribute("data-ff-edit"),text:t.textContent??"",count:o.querySelectorAll(`[data-ff-src="${r.replace(/"/g,"")}"]`).length,split:t.hasAttribute("data-split-from")||t.hasAttribute("data-split-to"),rect:{left:i.left,top:i.top,width:i.width,height:i.height}})},!0),o.addEventListener("keydown",e=>{if(!f||n&&!n.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),u({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),a.addEventListener("message",e=>{if(e.source!==a.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?b(t):(t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end")&&(s=null,n&&!n.done&&L(!1)))});function b(e){n&&!n.done&&L(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):s&&s.src===e.src?s.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),r=e.at,i=e.layer?r&&Number.isFinite(r.x)&&Number.isFinite(r.y)?{x:Number(r.x),y:Number(r.y)}:null:s&&s.src===e.src?s:null;if(s=null,!t||!Array.isArray(e.items)){u({type:"formfeed:edit-cancel",token:String(e.token)});return}let x=Array.from(t.childNodes),g=o.createDocumentFragment();for(let y of e.items)if(y.kind==="text")g.appendChild(o.createTextNode(String(y.text)));else if(y.kind==="break")g.appendChild(o.createElement("br"));else if(y.kind==="chip"){let m=o.createElement("span");m.className="ff-chip",m.setAttribute("data-ff-chip",String(y.index)),m.setAttribute("contenteditable","false"),y.title&&(m.title=String(y.title)),m.textContent=String(y.text),g.appendChild(m)}for(let y of x)t.removeChild(y);t.appendChild(g),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,n={token:String(e.token),element:t,saved:x,allowBreaks:e.allowBreaks===!0,badge:e.note?E(t,String(e.note)):null,done:!1},t.addEventListener("keydown",C),t.addEventListener("beforeinput",S),t.addEventListener("paste",A),t.addEventListener("drop",T),t.addEventListener("focusout",N),c&&t.addEventListener("input",k),t.focus(),e.select==="all"?H(t):M(t,i),u({type:"formfeed:edit-started",token:n.token}),typeof e.insert=="string"&&e.insert&&h(e.insert.replace(/\\r\\n?|\\n/g," "))}function h(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=a.getSelection(),r=t&&t.rangeCount?t.getRangeAt(0):null;r&&(r.deleteContents(),r.insertNode(o.createTextNode(e)),r.collapse(!1),c&&k())}function k(){n&&!n.done&&u({type:"formfeed:edit-input",token:n.token,items:w(n.element)})}function E(e,t){let r=o.createElement("div");r.className="ff-edit-badge",r.textContent=t;let i=e.getBoundingClientRect();return r.style.left=`${i.left+a.scrollX}px`,r.style.top=`${Math.max(0,i.top+a.scrollY-22)}px`,o.body.appendChild(r),r}function H(e){let t=a.getSelection();if(!t)return;let r=o.createRange();r.selectNodeContents(e),t.removeAllRanges(),t.addRange(r)}function M(e,t){let r=a.getSelection();if(!r)return;let i=null,x=o;if(t){if(x.caretRangeFromPoint)i=x.caretRangeFromPoint(t.x,t.y);else if(x.caretPositionFromPoint){let g=x.caretPositionFromPoint(t.x,t.y);g&&(i=o.createRange(),i.setStart(g.offsetNode,g.offset))}}if(!i||!e.contains(i.startContainer))i=o.createRange(),i.selectNodeContents(e),i.collapse(!1);else if(t){let g=i.startContainer,m=(g.nodeType===1?g:g.parentElement)?.closest("[data-ff-chip]")??null;if(m&&m!==e&&e.contains(m)){let F=m.getBoundingClientRect();i=o.createRange(),t.x<F.left+F.width/2?i.setStartBefore(m):i.setStartAfter(m),i.collapse(!0)}}r.removeAllRanges(),r.addRange(i)}function C(e){let t=e.key;t==="Escape"?(e.preventDefault(),L(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),v()):t==="Enter"?(e.preventDefault(),n?.allowBreaks&&P()):t==="Tab"?(e.preventDefault(),v()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),c&&n&&!e.altKey&&u({type:"formfeed:edit-style",token:n.token,key:t.toLowerCase()}))}function S(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function A(e){e.preventDefault(),h((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function T(e){e.preventDefault()}function N(){a.setTimeout(()=>{n&&!n.done&&(!o.hasFocus()||!n.element.contains(o.activeElement))&&v()},0)}function P(){let e=a.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let r=o.createElement("br");t.insertNode(r),r.nextSibling||r.parentNode?.appendChild(o.createElement("br"));let i=o.createRange();i.setStartAfter(r),i.collapse(!0),e.removeAllRanges(),e.addRange(i)}function R(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",C),t.removeEventListener("beforeinput",S),t.removeEventListener("paste",A),t.removeEventListener("drop",T),t.removeEventListener("focusout",N),t.removeEventListener("input",k),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),n===e&&(n=null)}function v(){let e=n;if(!e||e.done)return;let t=w(e.element);R(e),u({type:"formfeed:edit-commit",token:e.token,items:t})}function L(e){let t=n;if(!t||t.done)return;let r=t.element;for(;r.firstChild;)r.removeChild(r.firstChild);for(let i of t.saved)r.appendChild(i);R(t),e&&u({type:"formfeed:edit-cancel",token:t.token})}}function w(a){let l=[],f=o=>{let n=l[l.length-1];n?.kind==="text"?n.text+=o:o&&l.push({kind:"text",text:o})},c=o=>{for(let n of Array.from(o.childNodes))if(n.nodeType===3)f(n.data);else if(n.nodeType===1){let s=n,u=s.getAttribute("data-ff-chip");u!==null?l.push({kind:"chip",index:Number(u)}):s.tagName==="BR"?l.push({kind:"break"}):((s.tagName==="DIV"||s.tagName==="P")&&l.length&&l.push({kind:"break"}),c(s))}};for(c(a);l[l.length-1]?.kind==="break";)l.pop();return l}function K(a,l){return Array.from(a.querySelectorAll("[data-ff-layer]")).map(f=>{let c=f.querySelector(":scope > .ff-text"),o=!1;if(c&&f.style.height!=="auto"){let n=l.getComputedStyle(f),s=f.clientHeight-(parseFloat(n.paddingTop)||0)-(parseFloat(n.paddingBottom)||0);o=n.overflow==="hidden"&&(c.scrollHeight>s+1||c.scrollWidth>c.clientWidth+1)}return{id:f.getAttribute("data-ff-layer")??"",width:f.offsetWidth,height:f.offsetHeight,overflow:o}})}function B(a){let l=a.document,f=p=>{try{a.parent.postMessage(p,"*")}catch{}},c=p=>typeof p=="string"?l.querySelector(`[data-ff-layer="${p.replace(/"/g,"")}"]`):null,o=()=>{for(let p of Array.from(l.querySelectorAll("[data-ff-layer]")))for(let d of["left","top","width","height","transform"])p.style.removeProperty(d)},n=()=>{a.formfeedFit?.(),f({type:"formfeed:ready",layers:K(l,a)})};a.addEventListener("message",p=>{if(p.source!==a.parent)return;let d=p.data;if(!(!d||typeof d.type!="string")){if(d.type==="formfeed:layer-style"){let b=c(d.id);if(!b)return;let h=(k,E)=>{typeof E=="number"&&Number.isFinite(E)&&b.style.setProperty(k,`${E}px`)};h("left",d.left),h("top",d.top),h("width",d.width),h("height",d.height),typeof d.rotation=="number"&&Number.isFinite(d.rotation)&&b.style.setProperty("transform",d.rotation?`rotate(${d.rotation}deg)`:"none")}else if(d.type==="formfeed:layer-style-clear")o();else if(d.type==="formfeed:css"&&typeof d.css=="string"){let b=l.querySelector(\'style[data-formfeed="template"]\');b&&(b.textContent=d.css),o(),n()}}}),D(a,{clicks:!1,live:!0});let s=l.fonts,u=()=>s?void s.ready.then(n):n();l.readyState==="complete"?u():a.addEventListener("load",u)}B(window);})();';
|
|
5375
|
+
|
|
5274
5376
|
// packages/engine/src/lib/assemble.ts
|
|
5275
5377
|
var DEFAULT_CHROME_PADDING = "0 10mm";
|
|
5276
5378
|
function defaultSettings() {
|
|
@@ -5370,9 +5472,12 @@ function chartScripts(html, vendor) {
|
|
|
5370
5472
|
if (!vendor?.chartJs || !html.includes("data-ff-chart"))
|
|
5371
5473
|
return { head: "", body: "" };
|
|
5372
5474
|
const lib = "inline" in vendor.chartJs ? `<script>${vendor.chartJs.inline}</script>` : `<script src="${escapeHtml(vendor.chartJs.src)}"></script>`;
|
|
5373
|
-
return {
|
|
5374
|
-
|
|
5375
|
-
|
|
5475
|
+
return {
|
|
5476
|
+
head: `${lib}
|
|
5477
|
+
`,
|
|
5478
|
+
body: `${chartInitScript()}
|
|
5479
|
+
`
|
|
5480
|
+
};
|
|
5376
5481
|
}
|
|
5377
5482
|
function assembleDocument(input) {
|
|
5378
5483
|
const settings = mergeSettings(input.settings);
|
|
@@ -5399,6 +5504,8 @@ ${brandFontFamilies(input.brand)}`
|
|
|
5399
5504
|
` : "" : input.extraCss ? `<style data-formfeed="extra">${input.extraCss}</style>
|
|
5400
5505
|
` : "";
|
|
5401
5506
|
const charts = chartScripts(input.html, input.vendor);
|
|
5507
|
+
const fit = input.html.includes("data-ff-fit") ? `<script>${fitRuntime.replace(/<\/script/gi, "<\\/script")}</script>
|
|
5508
|
+
` : "";
|
|
5402
5509
|
return `<!doctype html>
|
|
5403
5510
|
<html lang="${escapeHtml(locale.split("_")[0] ?? locale)}">
|
|
5404
5511
|
<head>
|
|
@@ -5410,7 +5517,7 @@ ${tailwind}<style data-formfeed="template">${input.css ?? ""}</style>
|
|
|
5410
5517
|
${charts.head}</head>
|
|
5411
5518
|
<body class="formfeed-body formfeed-${kind}${input.mode === "preview" ? " formfeed-preview" : ""}">
|
|
5412
5519
|
${input.html}
|
|
5413
|
-
${charts.body}</body>
|
|
5520
|
+
${charts.body}${fit}</body>
|
|
5414
5521
|
</html>
|
|
5415
5522
|
`;
|
|
5416
5523
|
}
|
|
@@ -5485,7 +5592,10 @@ function paperOf(settings) {
|
|
|
5485
5592
|
}
|
|
5486
5593
|
var pageNumberSpans = (html, page, total) => {
|
|
5487
5594
|
const fill = (source, name, value) => source.replace(
|
|
5488
|
-
new RegExp(
|
|
5595
|
+
new RegExp(
|
|
5596
|
+
`<([a-z][a-z0-9]*)(\\s[^>]*?\\bclass=(["'])(?:[^"']*\\s)?${name}(?:\\s[^"']*)?\\3[^>]*)>[^<]*</\\1>`,
|
|
5597
|
+
"gi"
|
|
5598
|
+
),
|
|
5489
5599
|
(_match, tag, attrs) => `<${tag}${attrs}>${value}</${tag}>`
|
|
5490
5600
|
);
|
|
5491
5601
|
return fill(fill(html, "pageNumber", page), "totalPages", total);
|
|
@@ -5508,6 +5618,7 @@ document.addEventListener('click', function (event) {
|
|
|
5508
5618
|
} catch (e) {}
|
|
5509
5619
|
}, true);
|
|
5510
5620
|
</script>`;
|
|
5621
|
+
var textEditScript = `<script>${textEditRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
|
|
5511
5622
|
var pageNavScript = `<script>
|
|
5512
5623
|
(function () {
|
|
5513
5624
|
var pages = function () { return document.querySelectorAll('.pagedjs_page'); };
|
|
@@ -5609,8 +5720,11 @@ var zoomGestureScript = `<script>
|
|
|
5609
5720
|
})();
|
|
5610
5721
|
</script>`;
|
|
5611
5722
|
function intoHead(document, markup) {
|
|
5612
|
-
return document.replace(
|
|
5613
|
-
|
|
5723
|
+
return document.replace(
|
|
5724
|
+
/<head[^>]*>(?:\s*<meta\b[^>]*>)*/i,
|
|
5725
|
+
(head) => `${head}
|
|
5726
|
+
${markup}`
|
|
5727
|
+
);
|
|
5614
5728
|
}
|
|
5615
5729
|
function chromeBoxStyle(settings, which) {
|
|
5616
5730
|
const margin = settings.margin ?? {};
|
|
@@ -5625,7 +5739,7 @@ function flowChromeStyle(settings, which) {
|
|
|
5625
5739
|
const height = settings[which]?.height;
|
|
5626
5740
|
return `position:absolute;${which === "header" ? "top" : "bottom"}:0;left:0;width:100%;box-sizing:border-box;padding:${padding};${height ? `height:${height};` : ""}`;
|
|
5627
5741
|
}
|
|
5628
|
-
function flowDocument(draft) {
|
|
5742
|
+
function flowDocument(draft, options = {}) {
|
|
5629
5743
|
const { settings, kind, headerHtml, footerHtml } = draft;
|
|
5630
5744
|
const margin = settings.margin ?? {};
|
|
5631
5745
|
const paper = paperOf(settings);
|
|
@@ -5637,7 +5751,15 @@ body.formfeed-preview { position: relative; box-sizing: border-box; width: ${pap
|
|
|
5637
5751
|
</style>` : `<style data-formfeed="preview">html { background: #e5e7eb; } body.formfeed-preview { margin: 24px auto; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2); }</style>`;
|
|
5638
5752
|
const header = headerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "header") : `${chromeBoxStyle(settings, "header")}margin-bottom:8px`}">${headerHtml}</div>` : "";
|
|
5639
5753
|
const footer = footerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "footer") : `${chromeBoxStyle(settings, "footer")}margin-top:8px`}">${pageNumberSpans(footerHtml, "1", "1")}</div>` : "";
|
|
5640
|
-
return intoHead(
|
|
5754
|
+
return intoHead(
|
|
5755
|
+
draft.document,
|
|
5756
|
+
`${chrome}${options.editing ? textEditScript : ""}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}`
|
|
5757
|
+
).replace(/(<body[^>]*>)/, `$1${header}`).replace("</body>", `${footer}</body>`);
|
|
5758
|
+
}
|
|
5759
|
+
function canvasDocument(draft) {
|
|
5760
|
+
const chrome = `<style data-formfeed="stage">html, body { background: transparent !important; margin: 0 !important; }</style>`;
|
|
5761
|
+
const runtime = `<script>${stageRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
|
|
5762
|
+
return intoHead(draft.document, `${chrome}${runtime}${zoomGestureScript}`);
|
|
5641
5763
|
}
|
|
5642
5764
|
function pagedDocument(draft, options) {
|
|
5643
5765
|
const { headerHtml, footerHtml, settings } = draft;
|
|
@@ -5659,82 +5781,17 @@ html { background: #e5e7eb; }
|
|
|
5659
5781
|
`;
|
|
5660
5782
|
const selectorGuard = `<script>(function(){var q=DocumentFragment.prototype.querySelectorAll;DocumentFragment.prototype.querySelectorAll=function(s){try{return q.call(this,s);}catch(e){console.warn('formfeed paged preview: skipped a selector Paged.js could not use:',s);return q.call(this,':not(*)');}};})();</script>`;
|
|
5661
5783
|
const boxSizingGuard = `<script>(function(){var rule='.pagedjs_pagebox *';var o=new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(n.nodeName==='STYLE'&&n.textContent&&n.textContent.indexOf(rule)>=0){n.textContent=n.textContent.split(rule).join('.pagedjs_pagebox [class*="pagedjs_"]');o.disconnect();}});});});o.observe(document.documentElement,{childList:true,subtree:true});})();</script>`;
|
|
5662
|
-
const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); } };</script>`;
|
|
5784
|
+
const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { if (window.formfeedFit) window.formfeedFit(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); } };</script>`;
|
|
5663
5785
|
const script = `<script src="${options.pagedScriptUrl}"></script>`;
|
|
5664
5786
|
const header = headerHtml ? `<div class="ff-running-header">${pageNumberSpans(headerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
|
|
5665
5787
|
const footer = footerHtml ? `<div class="ff-running-footer">${pageNumberSpans(footerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
|
|
5666
5788
|
return intoHead(
|
|
5667
5789
|
draft.document,
|
|
5668
|
-
`<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${inspectScript}${pageNavScript}${zoomGestureScript}`
|
|
5790
|
+
`<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${options.editing ? textEditScript : ""}${inspectScript}${pageNavScript}${zoomGestureScript}`
|
|
5669
5791
|
).replace(/(<body[^>]*>)/, `$1${header}${footer}`);
|
|
5670
5792
|
}
|
|
5671
5793
|
|
|
5672
|
-
// packages/engine/src/lib/source-
|
|
5673
|
-
var sourceAttribute = "data-ff-src";
|
|
5674
|
-
function parseSourceAttribute(value) {
|
|
5675
|
-
const match = /^(body|header|footer):(\d+):(\d+)$/.exec(value ?? "");
|
|
5676
|
-
if (!match) return null;
|
|
5677
|
-
const line = Number(match[2]);
|
|
5678
|
-
const column = Number(match[3]);
|
|
5679
|
-
if (line < 1 || column < 1) return null;
|
|
5680
|
-
return { file: match[1], line, column };
|
|
5681
|
-
}
|
|
5682
|
-
function annotateSourcePositions(source, engine, file) {
|
|
5683
|
-
const scanner = new Scanner(source, engine);
|
|
5684
|
-
const lines = lineStarts(source);
|
|
5685
|
-
const out = [];
|
|
5686
|
-
let copied = 0;
|
|
5687
|
-
const n = source.length;
|
|
5688
|
-
let i = 0;
|
|
5689
|
-
while (i < n) {
|
|
5690
|
-
const ch = source[i];
|
|
5691
|
-
if (ch === "{") {
|
|
5692
|
-
i = scanner.skipTemplate(i) ?? i + 1;
|
|
5693
|
-
continue;
|
|
5694
|
-
}
|
|
5695
|
-
if (ch !== "<") {
|
|
5696
|
-
i++;
|
|
5697
|
-
continue;
|
|
5698
|
-
}
|
|
5699
|
-
if (source.startsWith("<!--", i)) {
|
|
5700
|
-
i = scanner.skipTo(i + 4, "-->");
|
|
5701
|
-
continue;
|
|
5702
|
-
}
|
|
5703
|
-
if (source.startsWith("<![CDATA[", i)) {
|
|
5704
|
-
i = scanner.skipTo(i + 9, "]]>");
|
|
5705
|
-
continue;
|
|
5706
|
-
}
|
|
5707
|
-
const next = source[i + 1];
|
|
5708
|
-
if (next === "!" || next === "?") {
|
|
5709
|
-
i = scanner.tagEnd(i + 2);
|
|
5710
|
-
continue;
|
|
5711
|
-
}
|
|
5712
|
-
if (next === "/") {
|
|
5713
|
-
i += 2;
|
|
5714
|
-
continue;
|
|
5715
|
-
}
|
|
5716
|
-
tagName.lastIndex = i + 1;
|
|
5717
|
-
const name = tagName.exec(source)?.[0];
|
|
5718
|
-
if (!name) {
|
|
5719
|
-
i++;
|
|
5720
|
-
continue;
|
|
5721
|
-
}
|
|
5722
|
-
const nameEnd = i + 1 + name.length;
|
|
5723
|
-
const end = scanner.tagEnd(nameEnd);
|
|
5724
|
-
const after = source[nameEnd];
|
|
5725
|
-
const plainName = after === void 0 || after === ">" || after === "/" || /\s/.test(after);
|
|
5726
|
-
if (plainName && !source.slice(nameEnd, end).toLowerCase().includes(sourceAttribute)) {
|
|
5727
|
-
const { line, column } = locate(lines, i);
|
|
5728
|
-
out.push(source.slice(copied, nameEnd), ` ${sourceAttribute}="${file}:${line}:${column}"`);
|
|
5729
|
-
copied = nameEnd;
|
|
5730
|
-
}
|
|
5731
|
-
i = end;
|
|
5732
|
-
if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
|
|
5733
|
-
i = scanner.skipToClosingTag(end, name);
|
|
5734
|
-
}
|
|
5735
|
-
out.push(source.slice(copied));
|
|
5736
|
-
return out.join("");
|
|
5737
|
-
}
|
|
5794
|
+
// packages/engine/src/lib/source-scan.ts
|
|
5738
5795
|
var tagName = /[A-Za-z][^\s/>{}<"'=]*/y;
|
|
5739
5796
|
var rawTextElements = /* @__PURE__ */ new Set([
|
|
5740
5797
|
"script",
|
|
@@ -5775,6 +5832,38 @@ var Scanner = class {
|
|
|
5775
5832
|
return this.blockEnd(tag.end, tag.keyword);
|
|
5776
5833
|
return tag.end;
|
|
5777
5834
|
}
|
|
5835
|
+
/** The construct starting at `at` and what kind it is, or null when `{` starts none. */
|
|
5836
|
+
classify(at) {
|
|
5837
|
+
const s = this.source;
|
|
5838
|
+
const tag = this.templateTag(at);
|
|
5839
|
+
if (!tag) return null;
|
|
5840
|
+
if (this.engine === "handlebars") {
|
|
5841
|
+
if (s.startsWith("{{{{", at)) return { type: "statement", end: tag.end };
|
|
5842
|
+
if (s.startsWith("{{!", at)) return { type: "comment", end: tag.end };
|
|
5843
|
+
if (s.startsWith("{{{", at))
|
|
5844
|
+
return {
|
|
5845
|
+
type: "output",
|
|
5846
|
+
end: tag.end,
|
|
5847
|
+
inner: s.slice(at + 3, tag.end - 3)
|
|
5848
|
+
};
|
|
5849
|
+
const inner = s.slice(at + 2, tag.end - 2);
|
|
5850
|
+
const head = inner.replace(/^~?\s*/, "");
|
|
5851
|
+
if (/^[#/^>*]/.test(head) || /^else(?:\s|~?$)/.test(head))
|
|
5852
|
+
return { type: "statement", end: tag.end };
|
|
5853
|
+
return { type: "output", end: tag.end, inner };
|
|
5854
|
+
}
|
|
5855
|
+
if (s.startsWith("{#", at)) return { type: "comment", end: tag.end };
|
|
5856
|
+
if (s.startsWith("{{", at))
|
|
5857
|
+
return {
|
|
5858
|
+
type: "output",
|
|
5859
|
+
end: tag.end,
|
|
5860
|
+
inner: s.slice(at + 2, tag.end - 2)
|
|
5861
|
+
};
|
|
5862
|
+
if (/^\{%[-+~]?\s*#/.test(s.slice(at, at + 64)))
|
|
5863
|
+
return { type: "comment", end: tag.end };
|
|
5864
|
+
const end = tag.keyword && this.captures(tag.keyword, tag.rest ?? "") ? this.blockEnd(tag.end, tag.keyword) : tag.end;
|
|
5865
|
+
return { type: "statement", end, keyword: tag.keyword };
|
|
5866
|
+
}
|
|
5778
5867
|
/** Index after `close`, stepping over template constructs; the end of the source when missing. */
|
|
5779
5868
|
skipTo(from, close) {
|
|
5780
5869
|
let j = from;
|
|
@@ -5839,7 +5928,8 @@ var Scanner = class {
|
|
|
5839
5928
|
j++;
|
|
5840
5929
|
continue;
|
|
5841
5930
|
}
|
|
5842
|
-
if (tag.keyword === keyword && this.captures(keyword, tag.rest ?? ""))
|
|
5931
|
+
if (tag.keyword === keyword && this.captures(keyword, tag.rest ?? ""))
|
|
5932
|
+
depth++;
|
|
5843
5933
|
else if (tag.keyword === `end${keyword}` && --depth === 0) return tag.end;
|
|
5844
5934
|
j = tag.end;
|
|
5845
5935
|
}
|
|
@@ -5850,22 +5940,32 @@ var Scanner = class {
|
|
|
5850
5940
|
if (s[at] !== "{") return null;
|
|
5851
5941
|
if (this.engine === "handlebars") {
|
|
5852
5942
|
if (s.startsWith("{{{{", at)) return { end: this.handlebarsRawBlock(at) };
|
|
5853
|
-
if (s.startsWith("{{!--", at))
|
|
5854
|
-
|
|
5855
|
-
if (s.startsWith("{{
|
|
5856
|
-
|
|
5943
|
+
if (s.startsWith("{{!--", at))
|
|
5944
|
+
return { end: this.indexAfter(at + 5, "--}}") };
|
|
5945
|
+
if (s.startsWith("{{!", at))
|
|
5946
|
+
return { end: this.indexAfter(at + 3, "}}") };
|
|
5947
|
+
if (s.startsWith("{{{", at))
|
|
5948
|
+
return { end: this.expressionEnd(at + 3, "}}}") };
|
|
5949
|
+
if (s.startsWith("{{", at))
|
|
5950
|
+
return { end: this.expressionEnd(at + 2, "}}") };
|
|
5857
5951
|
return null;
|
|
5858
5952
|
}
|
|
5859
5953
|
if (this.engine === "jinja2" && s.startsWith("{#", at))
|
|
5860
5954
|
return { end: this.indexAfter(at + 2, "#}") };
|
|
5861
|
-
if (s.startsWith("{{", at))
|
|
5955
|
+
if (s.startsWith("{{", at))
|
|
5956
|
+
return { end: this.expressionEnd(at + 2, "}}") };
|
|
5862
5957
|
if (!s.startsWith("{%", at)) return null;
|
|
5863
5958
|
const end = /^[-+~]?\s*#/.test(s.slice(at + 2, at + 64)) ? this.indexAfter(at + 2, "%}") : this.expressionEnd(at + 2, "%}");
|
|
5864
|
-
const inner = /^[-+~]?\s*(\w+)([\s\S]*)$/.exec(
|
|
5959
|
+
const inner = /^[-+~]?\s*(\w+)([\s\S]*)$/.exec(
|
|
5960
|
+
s.slice(at + 2, Math.max(at + 2, end - 2))
|
|
5961
|
+
);
|
|
5865
5962
|
const keyword = inner?.[1];
|
|
5866
5963
|
if (!keyword) return { end };
|
|
5867
5964
|
if (literalBlocks[this.engine].has(keyword)) {
|
|
5868
|
-
const close = new RegExp(
|
|
5965
|
+
const close = new RegExp(
|
|
5966
|
+
`\\{%[-+~]?\\s*end${keyword}\\s*[-+~]?%\\}`,
|
|
5967
|
+
"g"
|
|
5968
|
+
);
|
|
5869
5969
|
close.lastIndex = end;
|
|
5870
5970
|
const match = close.exec(s);
|
|
5871
5971
|
return { end: match ? match.index + match[0].length : this.n, keyword };
|
|
@@ -5920,125 +6020,2110 @@ function locate(starts, offset) {
|
|
|
5920
6020
|
}
|
|
5921
6021
|
return { line: low + 1, column: offset - (starts[low] ?? 0) + 1 };
|
|
5922
6022
|
}
|
|
6023
|
+
function offsetOf(starts, sourceLength, line, column) {
|
|
6024
|
+
const start = starts[line - 1];
|
|
6025
|
+
if (start === void 0 || column < 1) return null;
|
|
6026
|
+
const offset = start + column - 1;
|
|
6027
|
+
const next = starts[line] ?? sourceLength + 1;
|
|
6028
|
+
return offset < next ? offset : null;
|
|
6029
|
+
}
|
|
5923
6030
|
|
|
5924
|
-
// packages/engine/src/lib/
|
|
5925
|
-
var
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
maxFileBytes: 20 * 1024 * 1024
|
|
5942
|
-
};
|
|
5943
|
-
|
|
5944
|
-
// node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
|
|
5945
|
-
var u8 = Uint8Array;
|
|
5946
|
-
var u16 = Uint16Array;
|
|
5947
|
-
var i32 = Int32Array;
|
|
5948
|
-
var fleb = new u8([
|
|
5949
|
-
0,
|
|
5950
|
-
0,
|
|
5951
|
-
0,
|
|
5952
|
-
0,
|
|
5953
|
-
0,
|
|
5954
|
-
0,
|
|
5955
|
-
0,
|
|
5956
|
-
0,
|
|
5957
|
-
1,
|
|
5958
|
-
1,
|
|
5959
|
-
1,
|
|
5960
|
-
1,
|
|
5961
|
-
2,
|
|
5962
|
-
2,
|
|
5963
|
-
2,
|
|
5964
|
-
2,
|
|
5965
|
-
3,
|
|
5966
|
-
3,
|
|
5967
|
-
3,
|
|
5968
|
-
3,
|
|
5969
|
-
4,
|
|
5970
|
-
4,
|
|
5971
|
-
4,
|
|
5972
|
-
4,
|
|
5973
|
-
5,
|
|
5974
|
-
5,
|
|
5975
|
-
5,
|
|
5976
|
-
5,
|
|
5977
|
-
0,
|
|
5978
|
-
/* unused */
|
|
5979
|
-
0,
|
|
5980
|
-
0,
|
|
5981
|
-
/* impossible */
|
|
5982
|
-
0
|
|
6031
|
+
// packages/engine/src/lib/source-edit.ts
|
|
6032
|
+
var MAX_REGION = 2e4;
|
|
6033
|
+
var voidElements = /* @__PURE__ */ new Set([
|
|
6034
|
+
"area",
|
|
6035
|
+
"base",
|
|
6036
|
+
"br",
|
|
6037
|
+
"col",
|
|
6038
|
+
"embed",
|
|
6039
|
+
"hr",
|
|
6040
|
+
"img",
|
|
6041
|
+
"input",
|
|
6042
|
+
"link",
|
|
6043
|
+
"meta",
|
|
6044
|
+
"param",
|
|
6045
|
+
"source",
|
|
6046
|
+
"track",
|
|
6047
|
+
"wbr"
|
|
5983
6048
|
]);
|
|
5984
|
-
var
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
0,
|
|
6017
|
-
0
|
|
6049
|
+
var phrasingElements = /* @__PURE__ */ new Set([
|
|
6050
|
+
"a",
|
|
6051
|
+
"abbr",
|
|
6052
|
+
"b",
|
|
6053
|
+
"bdi",
|
|
6054
|
+
"bdo",
|
|
6055
|
+
"cite",
|
|
6056
|
+
"code",
|
|
6057
|
+
"data",
|
|
6058
|
+
"del",
|
|
6059
|
+
"dfn",
|
|
6060
|
+
"em",
|
|
6061
|
+
"font",
|
|
6062
|
+
"i",
|
|
6063
|
+
"img",
|
|
6064
|
+
"ins",
|
|
6065
|
+
"kbd",
|
|
6066
|
+
"label",
|
|
6067
|
+
"mark",
|
|
6068
|
+
"q",
|
|
6069
|
+
"s",
|
|
6070
|
+
"samp",
|
|
6071
|
+
"small",
|
|
6072
|
+
"span",
|
|
6073
|
+
"strong",
|
|
6074
|
+
"sub",
|
|
6075
|
+
"sup",
|
|
6076
|
+
"svg",
|
|
6077
|
+
"time",
|
|
6078
|
+
"u",
|
|
6079
|
+
"var",
|
|
6080
|
+
"wbr"
|
|
6018
6081
|
]);
|
|
6019
|
-
var
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6082
|
+
var namedEntities = {
|
|
6083
|
+
amp: 38,
|
|
6084
|
+
lt: 60,
|
|
6085
|
+
gt: 62,
|
|
6086
|
+
quot: 34,
|
|
6087
|
+
apos: 39,
|
|
6088
|
+
nbsp: 160,
|
|
6089
|
+
shy: 173,
|
|
6090
|
+
ensp: 8194,
|
|
6091
|
+
emsp: 8195,
|
|
6092
|
+
thinsp: 8201,
|
|
6093
|
+
zwnj: 8204,
|
|
6094
|
+
zwj: 8205,
|
|
6095
|
+
ndash: 8211,
|
|
6096
|
+
mdash: 8212,
|
|
6097
|
+
lsquo: 8216,
|
|
6098
|
+
rsquo: 8217,
|
|
6099
|
+
sbquo: 8218,
|
|
6100
|
+
ldquo: 8220,
|
|
6101
|
+
rdquo: 8221,
|
|
6102
|
+
bdquo: 8222,
|
|
6103
|
+
laquo: 171,
|
|
6104
|
+
raquo: 187,
|
|
6105
|
+
lsaquo: 8249,
|
|
6106
|
+
rsaquo: 8250,
|
|
6107
|
+
hellip: 8230,
|
|
6108
|
+
bull: 8226,
|
|
6109
|
+
middot: 183,
|
|
6110
|
+
euro: 8364,
|
|
6111
|
+
pound: 163,
|
|
6112
|
+
yen: 165,
|
|
6113
|
+
cent: 162,
|
|
6114
|
+
copy: 169,
|
|
6115
|
+
reg: 174,
|
|
6116
|
+
trade: 8482,
|
|
6117
|
+
deg: 176,
|
|
6118
|
+
sect: 167,
|
|
6119
|
+
para: 182,
|
|
6120
|
+
times: 215,
|
|
6121
|
+
divide: 247,
|
|
6122
|
+
plusmn: 177,
|
|
6123
|
+
micro: 181,
|
|
6124
|
+
frac12: 189,
|
|
6125
|
+
frac14: 188,
|
|
6126
|
+
frac34: 190,
|
|
6127
|
+
sup2: 178,
|
|
6128
|
+
sup3: 179,
|
|
6129
|
+
dagger: 8224,
|
|
6130
|
+
Dagger: 8225,
|
|
6131
|
+
larr: 8592,
|
|
6132
|
+
rarr: 8594,
|
|
6133
|
+
uarr: 8593,
|
|
6134
|
+
darr: 8595,
|
|
6135
|
+
auml: 228,
|
|
6136
|
+
ouml: 246,
|
|
6137
|
+
uuml: 252,
|
|
6138
|
+
Auml: 196,
|
|
6139
|
+
Ouml: 214,
|
|
6140
|
+
Uuml: 220,
|
|
6141
|
+
szlig: 223,
|
|
6142
|
+
eacute: 233,
|
|
6143
|
+
egrave: 232,
|
|
6144
|
+
agrave: 224,
|
|
6145
|
+
aacute: 225,
|
|
6146
|
+
ccedil: 231,
|
|
6147
|
+
oacute: 243,
|
|
6148
|
+
iacute: 237,
|
|
6149
|
+
uacute: 250,
|
|
6150
|
+
ntilde: 241
|
|
6151
|
+
};
|
|
6152
|
+
var entityPattern = /&(#[0-9]{1,7}|#[xX][0-9a-fA-F]{1,6}|[A-Za-z][A-Za-z0-9]{0,31});/y;
|
|
6153
|
+
var NBSP = String.fromCharCode(160);
|
|
6154
|
+
var BREAK = String.fromCharCode(57344);
|
|
6155
|
+
var REMOVED = String.fromCharCode(57345);
|
|
6156
|
+
var CHIP_OPEN = String.fromCharCode(57346);
|
|
6157
|
+
var CHIP_CLOSE = String.fromCharCode(57347);
|
|
6158
|
+
function editableRegion(source, engine, location) {
|
|
6159
|
+
const offset = offsetOf(
|
|
6160
|
+
lineStarts(source),
|
|
6161
|
+
source.length,
|
|
6162
|
+
location.line,
|
|
6163
|
+
location.column
|
|
6164
|
+
);
|
|
6165
|
+
return offset === null ? { reason: "not-an-element" } : editableRegionAt(source, engine, offset);
|
|
6166
|
+
}
|
|
6167
|
+
function editableRegionAt(source, engine, at) {
|
|
6168
|
+
const scanned = scanRegion(source, engine, at);
|
|
6169
|
+
if ("reason" in scanned) return scanned;
|
|
6170
|
+
const region = { kind: "text", ...scanned };
|
|
6171
|
+
const visible = region.parts.filter(
|
|
6172
|
+
(p) => !(p.kind === "text" && !source.slice(p.start, p.end).trim())
|
|
6173
|
+
);
|
|
6174
|
+
const only = visible.length === 1 ? visible[0] : void 0;
|
|
6175
|
+
if (only?.kind === "chip" && only.chip === "expression") {
|
|
6176
|
+
const call = translationCall(engine, only.inner ?? "");
|
|
6177
|
+
if (call)
|
|
6178
|
+
return { ...region, kind: "t", key: call.key, params: call.params };
|
|
6179
|
+
}
|
|
6180
|
+
const hasText = region.parts.some(
|
|
6181
|
+
(p) => p.kind === "text" && decodeText2(source.slice(p.start, p.end)).text.trim() !== ""
|
|
6182
|
+
);
|
|
6183
|
+
return hasText ? region : { reason: "no-text" };
|
|
6184
|
+
}
|
|
6185
|
+
function findRegionAgain(source, engine, outer) {
|
|
6186
|
+
const first = source.indexOf(outer);
|
|
6187
|
+
if (first < 0 || source.indexOf(outer, first + 1) >= 0) return null;
|
|
6188
|
+
const found = editableRegionAt(source, engine, first);
|
|
6189
|
+
return "reason" in found ? null : found;
|
|
6190
|
+
}
|
|
6191
|
+
function regionChips(source, region) {
|
|
6192
|
+
const chips = [];
|
|
6193
|
+
for (const part of region.parts)
|
|
6194
|
+
if (part.kind === "chip")
|
|
6195
|
+
chips.push({
|
|
6196
|
+
index: chips.length,
|
|
6197
|
+
kind: part.chip,
|
|
6198
|
+
label: part.label,
|
|
6199
|
+
source: source.slice(part.start, part.end)
|
|
6200
|
+
});
|
|
6201
|
+
return chips;
|
|
6202
|
+
}
|
|
6203
|
+
function regionItems(source, region) {
|
|
6204
|
+
const items = [];
|
|
6205
|
+
let chip = 0;
|
|
6206
|
+
for (const part of region.parts) {
|
|
6207
|
+
if (part.kind === "text")
|
|
6208
|
+
items.push({
|
|
6209
|
+
kind: "text",
|
|
6210
|
+
text: decodeText2(source.slice(part.start, part.end)).text
|
|
6211
|
+
});
|
|
6212
|
+
else if (part.kind === "break") items.push({ kind: "break" });
|
|
6213
|
+
else items.push({ kind: "chip", index: chip++ });
|
|
6024
6214
|
}
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6215
|
+
return items;
|
|
6216
|
+
}
|
|
6217
|
+
function applyRegionEdit(source, engine, region, items) {
|
|
6218
|
+
const chipParts = region.parts.filter(
|
|
6219
|
+
(p) => p.kind === "chip"
|
|
6220
|
+
);
|
|
6221
|
+
const kept = [];
|
|
6222
|
+
for (const item of items)
|
|
6223
|
+
if (item.kind === "chip") {
|
|
6224
|
+
if (!Number.isInteger(item.index) || item.index < 0 || item.index >= chipParts.length)
|
|
6225
|
+
return { error: "chips" };
|
|
6226
|
+
if (kept.length && item.index <= (kept[kept.length - 1] ?? -1))
|
|
6227
|
+
return { error: "chips" };
|
|
6228
|
+
kept.push(item.index);
|
|
6229
|
+
}
|
|
6230
|
+
const breakSpelling = source.slice(...spanOf(region.parts.find((p) => p.kind === "break"))) || "<br>";
|
|
6231
|
+
const newSegments = [""];
|
|
6232
|
+
for (const item of items) {
|
|
6233
|
+
if (item.kind === "chip") newSegments.push("");
|
|
6234
|
+
else
|
|
6235
|
+
newSegments[newSegments.length - 1] += item.kind === "break" ? BREAK : item.text.replace(/[\uE000-\uE003]/g, "");
|
|
6236
|
+
}
|
|
6237
|
+
const pieces = [];
|
|
6238
|
+
let segStart = region.start;
|
|
6239
|
+
let partIndex = 0;
|
|
6240
|
+
for (let k = 0; k <= kept.length; k++) {
|
|
6241
|
+
const boundary = k < kept.length ? chipParts[kept[k] ?? 0] : void 0;
|
|
6242
|
+
const segEnd = boundary ? boundary.start : region.end;
|
|
6243
|
+
const segParts = [];
|
|
6244
|
+
while (partIndex < region.parts.length && (region.parts[partIndex]?.start ?? Infinity) < segEnd)
|
|
6245
|
+
segParts.push(region.parts[partIndex++]);
|
|
6246
|
+
if (boundary) partIndex++;
|
|
6247
|
+
pieces.push(
|
|
6248
|
+
rewriteSegment(
|
|
6249
|
+
source,
|
|
6250
|
+
segStart,
|
|
6251
|
+
segEnd,
|
|
6252
|
+
segParts,
|
|
6253
|
+
newSegments[k] ?? "",
|
|
6254
|
+
breakSpelling
|
|
6255
|
+
)
|
|
6256
|
+
);
|
|
6257
|
+
if (boundary) {
|
|
6258
|
+
pieces.push(source.slice(boundary.start, boundary.end));
|
|
6259
|
+
segStart = boundary.end;
|
|
6029
6260
|
}
|
|
6030
6261
|
}
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6262
|
+
const expected = flatExpected(
|
|
6263
|
+
items,
|
|
6264
|
+
chipParts.map((c) => source.slice(c.start, c.end))
|
|
6265
|
+
);
|
|
6266
|
+
const attempt = source.slice(0, region.start) + pieces.join("") + source.slice(region.end);
|
|
6267
|
+
if (readsBack(attempt, engine, region.tagStart, expected))
|
|
6268
|
+
return { source: attempt };
|
|
6269
|
+
const whole = items.map(
|
|
6270
|
+
(item) => item.kind === "chip" ? source.slice(chipParts[item.index]?.start, chipParts[item.index]?.end) : item.kind === "break" ? breakSpelling : encodeText(item.text, true, true)
|
|
6271
|
+
).join("");
|
|
6272
|
+
const rewritten = source.slice(0, region.start) + whole + source.slice(region.end);
|
|
6273
|
+
return readsBack(rewritten, engine, region.tagStart, expected) ? { source: rewritten } : { error: "invalid" };
|
|
6274
|
+
}
|
|
6275
|
+
function translationItems(text2) {
|
|
6276
|
+
const items = [];
|
|
6277
|
+
const names = [];
|
|
6278
|
+
let last = 0;
|
|
6279
|
+
for (const match of text2.matchAll(/\{(\w+)\}/g)) {
|
|
6280
|
+
if (match.index > last)
|
|
6281
|
+
items.push({ kind: "text", text: text2.slice(last, match.index) });
|
|
6282
|
+
items.push({ kind: "chip", index: names.length });
|
|
6283
|
+
names.push(match[1] ?? "");
|
|
6284
|
+
last = match.index + match[0].length;
|
|
6285
|
+
}
|
|
6286
|
+
if (last < text2.length) items.push({ kind: "text", text: text2.slice(last) });
|
|
6287
|
+
return { items, names };
|
|
6288
|
+
}
|
|
6289
|
+
function translationText(items, names) {
|
|
6290
|
+
return items.map(
|
|
6291
|
+
(item) => item.kind === "chip" ? `{${names[item.index] ?? ""}}` : item.kind === "break" ? " " : item.text.replace(new RegExp(NBSP, "g"), " ")
|
|
6292
|
+
).join("");
|
|
6293
|
+
}
|
|
6294
|
+
function chipValues(rendered, items) {
|
|
6295
|
+
const slots = [""];
|
|
6296
|
+
for (const item of items) {
|
|
6297
|
+
if (item.kind === "chip") slots.push("");
|
|
6298
|
+
else if (item.kind === "text") slots[slots.length - 1] += item.text;
|
|
6299
|
+
}
|
|
6300
|
+
const chips = slots.length - 1;
|
|
6301
|
+
if (!rendered.startsWith(slots[0] ?? "")) return null;
|
|
6302
|
+
let found = null;
|
|
6303
|
+
let solutions = 0;
|
|
6304
|
+
let steps = 0;
|
|
6305
|
+
const values = [];
|
|
6306
|
+
const place = (k, pos) => {
|
|
6307
|
+
if (solutions > 1 || ++steps > 1e4) return;
|
|
6308
|
+
if (k === chips) {
|
|
6309
|
+
if (pos === rendered.length) {
|
|
6310
|
+
solutions++;
|
|
6311
|
+
found = [...values];
|
|
6312
|
+
}
|
|
6313
|
+
return;
|
|
6314
|
+
}
|
|
6315
|
+
const next = slots[k + 1] ?? "";
|
|
6316
|
+
const last = k + 1 === chips;
|
|
6317
|
+
for (let q = pos; q <= rendered.length; q++) {
|
|
6318
|
+
q = rendered.indexOf(next, q);
|
|
6319
|
+
if (q < 0) return;
|
|
6320
|
+
if (!last || q + next.length === rendered.length) {
|
|
6321
|
+
values[k] = rendered.slice(pos, q);
|
|
6322
|
+
place(k + 1, q + next.length);
|
|
6323
|
+
if (solutions > 1) return;
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
};
|
|
6327
|
+
place(0, (slots[0] ?? "").length);
|
|
6328
|
+
return solutions === 1 && steps <= 1e4 ? found : null;
|
|
6329
|
+
}
|
|
6330
|
+
function scanRegion(source, engine, at) {
|
|
6331
|
+
if (source[at] !== "<") return { reason: "not-an-element" };
|
|
6332
|
+
tagName.lastIndex = at + 1;
|
|
6333
|
+
const name = tagName.exec(source)?.[0];
|
|
6334
|
+
if (!name) return { reason: "not-an-element" };
|
|
6335
|
+
const nameEnd = at + 1 + name.length;
|
|
6336
|
+
if (!plainNameEnd(source[nameEnd])) return { reason: "dynamic-tag" };
|
|
6337
|
+
const tag = name.toLowerCase();
|
|
6338
|
+
const scanner = new Scanner(source, engine);
|
|
6339
|
+
const start = scanner.tagEnd(nameEnd);
|
|
6340
|
+
if (voidElements.has(tag) || source[start - 2] === "/")
|
|
6341
|
+
return { reason: "void" };
|
|
6342
|
+
if (rawTextElements.has(tag)) return { reason: "raw-text" };
|
|
6343
|
+
const parts = [];
|
|
6344
|
+
let textStart = start;
|
|
6345
|
+
const flush = (to) => {
|
|
6346
|
+
if (to > textStart) parts.push({ kind: "text", start: textStart, end: to });
|
|
6347
|
+
};
|
|
6348
|
+
let i = start;
|
|
6349
|
+
while (i < source.length) {
|
|
6350
|
+
if (i - start > MAX_REGION) return { reason: "too-large" };
|
|
6351
|
+
const ch = source[i];
|
|
6352
|
+
if (ch === "{") {
|
|
6353
|
+
const construct = scanner.classify(i);
|
|
6354
|
+
if (!construct) {
|
|
6355
|
+
i++;
|
|
6356
|
+
continue;
|
|
6357
|
+
}
|
|
6358
|
+
if (construct.type === "statement") return { reason: "control-flow" };
|
|
6359
|
+
flush(i);
|
|
6360
|
+
parts.push(
|
|
6361
|
+
construct.type === "output" ? {
|
|
6362
|
+
kind: "chip",
|
|
6363
|
+
chip: "expression",
|
|
6364
|
+
label: expressionLabel(construct.inner),
|
|
6365
|
+
inner: construct.inner,
|
|
6366
|
+
start: i,
|
|
6367
|
+
end: construct.end
|
|
6368
|
+
} : {
|
|
6369
|
+
kind: "chip",
|
|
6370
|
+
chip: "comment",
|
|
6371
|
+
label: "comment",
|
|
6372
|
+
start: i,
|
|
6373
|
+
end: construct.end
|
|
6374
|
+
}
|
|
6375
|
+
);
|
|
6376
|
+
i = textStart = construct.end;
|
|
6377
|
+
continue;
|
|
6378
|
+
}
|
|
6379
|
+
if (ch === "&") {
|
|
6380
|
+
entityPattern.lastIndex = i;
|
|
6381
|
+
const match = entityPattern.exec(source);
|
|
6382
|
+
if (match && entityChar(match[1] ?? "") === null && !(match[1] ?? "").startsWith("#")) {
|
|
6383
|
+
flush(i);
|
|
6384
|
+
parts.push({
|
|
6385
|
+
kind: "chip",
|
|
6386
|
+
chip: "entity",
|
|
6387
|
+
label: match[0],
|
|
6388
|
+
start: i,
|
|
6389
|
+
end: i + match[0].length
|
|
6390
|
+
});
|
|
6391
|
+
i = textStart = i + match[0].length;
|
|
6392
|
+
continue;
|
|
6393
|
+
}
|
|
6394
|
+
i++;
|
|
6395
|
+
continue;
|
|
6396
|
+
}
|
|
6397
|
+
if (ch !== "<") {
|
|
6398
|
+
i++;
|
|
6399
|
+
continue;
|
|
6400
|
+
}
|
|
6401
|
+
if (source.startsWith("<!--", i)) {
|
|
6402
|
+
const end = scanner.skipTo(i + 4, "-->");
|
|
6403
|
+
flush(i);
|
|
6404
|
+
parts.push({
|
|
6405
|
+
kind: "chip",
|
|
6406
|
+
chip: "comment",
|
|
6407
|
+
label: "comment",
|
|
6408
|
+
start: i,
|
|
6409
|
+
end
|
|
6410
|
+
});
|
|
6411
|
+
i = textStart = end;
|
|
6412
|
+
continue;
|
|
6413
|
+
}
|
|
6414
|
+
const next = source[i + 1];
|
|
6415
|
+
if (next === "/") {
|
|
6416
|
+
tagName.lastIndex = i + 2;
|
|
6417
|
+
const closing = tagName.exec(source)?.[0];
|
|
6418
|
+
if (closing?.toLowerCase() !== tag) return { reason: "unclosed" };
|
|
6419
|
+
flush(i);
|
|
6420
|
+
return {
|
|
6421
|
+
tag,
|
|
6422
|
+
tagStart: at,
|
|
6423
|
+
start,
|
|
6424
|
+
end: i,
|
|
6425
|
+
outerEnd: scanner.tagEnd(i + 2 + closing.length),
|
|
6426
|
+
parts
|
|
6427
|
+
};
|
|
6428
|
+
}
|
|
6429
|
+
if (next === "!" || next === "?") return { reason: "block-child" };
|
|
6430
|
+
tagName.lastIndex = i + 1;
|
|
6431
|
+
const child2 = tagName.exec(source)?.[0];
|
|
6432
|
+
if (!child2) {
|
|
6433
|
+
i++;
|
|
6434
|
+
continue;
|
|
6435
|
+
}
|
|
6436
|
+
const childNameEnd = i + 1 + child2.length;
|
|
6437
|
+
if (!plainNameEnd(source[childNameEnd])) return { reason: "dynamic-tag" };
|
|
6438
|
+
const childTag = child2.toLowerCase();
|
|
6439
|
+
const childStartEnd = scanner.tagEnd(childNameEnd);
|
|
6440
|
+
if (childTag === "br") {
|
|
6441
|
+
flush(i);
|
|
6442
|
+
parts.push({ kind: "break", start: i, end: childStartEnd });
|
|
6443
|
+
i = textStart = childStartEnd;
|
|
6444
|
+
continue;
|
|
6445
|
+
}
|
|
6446
|
+
if (rawTextElements.has(childTag)) return { reason: "raw-text" };
|
|
6447
|
+
if (!phrasingElements.has(childTag)) return { reason: "block-child" };
|
|
6448
|
+
let childEnd = childStartEnd;
|
|
6449
|
+
if (!voidElements.has(childTag) && source[childStartEnd - 2] !== "/") {
|
|
6450
|
+
childEnd = matchingEnd(source, scanner, childStartEnd, childTag);
|
|
6451
|
+
if (childEnd === null) return { reason: "unclosed" };
|
|
6452
|
+
}
|
|
6453
|
+
flush(i);
|
|
6454
|
+
parts.push({
|
|
6455
|
+
kind: "chip",
|
|
6456
|
+
chip: "element",
|
|
6457
|
+
label: `<${childTag}>`,
|
|
6458
|
+
start: i,
|
|
6459
|
+
end: childEnd
|
|
6460
|
+
});
|
|
6461
|
+
i = textStart = childEnd;
|
|
6462
|
+
}
|
|
6463
|
+
return { reason: "unclosed" };
|
|
6464
|
+
}
|
|
6465
|
+
function plainNameEnd(after) {
|
|
6466
|
+
return after === void 0 || after === ">" || after === "/" || /\s/.test(after);
|
|
6467
|
+
}
|
|
6468
|
+
function matchingEnd(source, scanner, from, name) {
|
|
6469
|
+
let depth = 1;
|
|
6470
|
+
let j = from;
|
|
6471
|
+
while (j < source.length) {
|
|
6472
|
+
const ch = source[j];
|
|
6473
|
+
if (ch === "{") {
|
|
6474
|
+
j = scanner.skipTemplate(j) ?? j + 1;
|
|
6475
|
+
continue;
|
|
6476
|
+
}
|
|
6477
|
+
if (ch !== "<") {
|
|
6478
|
+
j++;
|
|
6479
|
+
continue;
|
|
6480
|
+
}
|
|
6481
|
+
if (source.startsWith("<!--", j)) {
|
|
6482
|
+
j = scanner.skipTo(j + 4, "-->");
|
|
6483
|
+
continue;
|
|
6484
|
+
}
|
|
6485
|
+
if (source[j + 1] === "/") {
|
|
6486
|
+
tagName.lastIndex = j + 2;
|
|
6487
|
+
const closing = tagName.exec(source)?.[0];
|
|
6488
|
+
const end2 = scanner.tagEnd(j + 2);
|
|
6489
|
+
if (closing?.toLowerCase() === name && --depth === 0) return end2;
|
|
6490
|
+
j = end2;
|
|
6491
|
+
continue;
|
|
6492
|
+
}
|
|
6493
|
+
tagName.lastIndex = j + 1;
|
|
6494
|
+
const opening = tagName.exec(source)?.[0];
|
|
6495
|
+
if (!opening) {
|
|
6496
|
+
j++;
|
|
6497
|
+
continue;
|
|
6498
|
+
}
|
|
6499
|
+
const end = scanner.tagEnd(j + 1 + opening.length);
|
|
6500
|
+
const lower = opening.toLowerCase();
|
|
6501
|
+
if (lower === name && source[end - 2] !== "/") depth++;
|
|
6502
|
+
j = rawTextElements.has(lower) ? scanner.skipToClosingTag(end, lower) : end;
|
|
6503
|
+
}
|
|
6504
|
+
return null;
|
|
6505
|
+
}
|
|
6506
|
+
function expressionLabel(inner) {
|
|
6507
|
+
const label2 = inner.replace(/^[-~+&]?\s*/, "").replace(/\s*[-~+]?$/, "").replace(/\s+/g, " ");
|
|
6508
|
+
return label2.length > 60 ? `${label2.slice(0, 59)}\u2026` : label2;
|
|
6509
|
+
}
|
|
6510
|
+
function translationCall(engine, inner) {
|
|
6511
|
+
const expression = inner.replace(/^[-~+]?\s*/, "").replace(/\s*[-~+]?$/, "");
|
|
6512
|
+
let match;
|
|
6513
|
+
if (engine === "jinja2") {
|
|
6514
|
+
match = /^(?:t|translate)\(\s*(['"])((?:(?!\1).)*)\1\s*(?:,\s*\{([\s\S]*)\}\s*)?\)$/.exec(
|
|
6515
|
+
expression
|
|
6516
|
+
);
|
|
6517
|
+
return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", ":", ",") } : null;
|
|
6518
|
+
}
|
|
6519
|
+
if (engine === "liquid") {
|
|
6520
|
+
match = /^(['"])((?:(?!\1).)*)\1\s*\|\s*(?:t|translate)\b\s*(?::\s*([\s\S]*))?$/.exec(
|
|
6521
|
+
expression
|
|
6522
|
+
);
|
|
6523
|
+
return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", ":", ",") } : null;
|
|
6524
|
+
}
|
|
6525
|
+
match = /^(?:t|translate)\s+(['"])((?:(?!\1).)*)\1([\s\S]*)$/.exec(
|
|
6526
|
+
expression
|
|
6527
|
+
);
|
|
6528
|
+
return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", "=", " ") } : null;
|
|
6529
|
+
}
|
|
6530
|
+
function parseParams(text2, assign, separator) {
|
|
6531
|
+
const params = {};
|
|
6532
|
+
for (const piece of splitTopLevel(text2, separator)) {
|
|
6533
|
+
const at = piece.indexOf(assign);
|
|
6534
|
+
if (at <= 0) continue;
|
|
6535
|
+
const name = piece.slice(0, at).trim().replace(/^['"]|['"]$/g, "");
|
|
6536
|
+
if (/^[\w-]+$/.test(name)) params[name] = piece.slice(at + 1).trim();
|
|
6537
|
+
}
|
|
6538
|
+
return params;
|
|
6539
|
+
}
|
|
6540
|
+
function splitTopLevel(text2, separator) {
|
|
6541
|
+
const pieces = [];
|
|
6542
|
+
let depth = 0;
|
|
6543
|
+
let quote2 = null;
|
|
6544
|
+
let current = "";
|
|
6545
|
+
for (const ch of text2) {
|
|
6546
|
+
if (quote2) {
|
|
6547
|
+
if (ch === quote2) quote2 = null;
|
|
6548
|
+
} else if (ch === '"' || ch === "'") quote2 = ch;
|
|
6549
|
+
else if ("([{".includes(ch)) depth++;
|
|
6550
|
+
else if (")]}".includes(ch)) depth--;
|
|
6551
|
+
else if (depth === 0 && (separator === " " ? /\s/.test(ch) : ch === separator)) {
|
|
6552
|
+
if (current.trim()) pieces.push(current);
|
|
6553
|
+
current = "";
|
|
6554
|
+
continue;
|
|
6555
|
+
}
|
|
6556
|
+
current += ch;
|
|
6557
|
+
}
|
|
6558
|
+
if (current.trim()) pieces.push(current);
|
|
6559
|
+
return pieces;
|
|
6560
|
+
}
|
|
6561
|
+
function entityChar(reference) {
|
|
6562
|
+
if (reference.startsWith("#")) {
|
|
6563
|
+
const code2 = reference[1] === "x" || reference[1] === "X" ? parseInt(reference.slice(2), 16) : parseInt(reference.slice(1), 10);
|
|
6564
|
+
return Number.isFinite(code2) && code2 > 0 && code2 <= 1114111 && !(code2 >= 55296 && code2 <= 57343) ? String.fromCodePoint(code2) : null;
|
|
6565
|
+
}
|
|
6566
|
+
const code = namedEntities[reference];
|
|
6567
|
+
return code === void 0 ? null : String.fromCharCode(code);
|
|
6568
|
+
}
|
|
6569
|
+
function decodeText2(raw) {
|
|
6570
|
+
let text2 = "";
|
|
6571
|
+
const map = [];
|
|
6572
|
+
let i = 0;
|
|
6573
|
+
while (i < raw.length) {
|
|
6574
|
+
if (raw[i] === "&") {
|
|
6575
|
+
entityPattern.lastIndex = i;
|
|
6576
|
+
const match = entityPattern.exec(raw);
|
|
6577
|
+
const decoded = match ? entityChar(match[1] ?? "") : null;
|
|
6578
|
+
if (match && decoded !== null) {
|
|
6579
|
+
for (let u = 0; u < decoded.length; u++) map.push(i);
|
|
6580
|
+
text2 += decoded;
|
|
6581
|
+
i += match[0].length;
|
|
6582
|
+
continue;
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
text2 += raw[i];
|
|
6586
|
+
map.push(i);
|
|
6587
|
+
i++;
|
|
6588
|
+
}
|
|
6589
|
+
map.push(raw.length);
|
|
6590
|
+
return { text: text2, map };
|
|
6591
|
+
}
|
|
6592
|
+
function encodeText(text2, keepNbsp, allBraces = false) {
|
|
6593
|
+
let out = "";
|
|
6594
|
+
for (let k = 0; k < text2.length; k++) {
|
|
6595
|
+
const ch = text2[k] ?? "";
|
|
6596
|
+
const next = text2[k + 1];
|
|
6597
|
+
if (ch === "&") out += "&";
|
|
6598
|
+
else if (ch === "<") out += "<";
|
|
6599
|
+
else if (ch === ">") out += ">";
|
|
6600
|
+
else if (ch === NBSP) out += keepNbsp ? " " : " ";
|
|
6601
|
+
else if (ch === "{" && (allBraces || next === void 0 || next === "{" || next === "%" || next === "#"))
|
|
6602
|
+
out += "{";
|
|
6603
|
+
else if (ch === "#" && next === "}") out += "#";
|
|
6604
|
+
else out += ch;
|
|
6605
|
+
}
|
|
6606
|
+
return out;
|
|
6607
|
+
}
|
|
6608
|
+
function encodeTemplateText(text2) {
|
|
6609
|
+
return encodeText(text2, true);
|
|
6610
|
+
}
|
|
6611
|
+
function spanOf(part) {
|
|
6612
|
+
return part ? [part.start, part.end] : [0, 0];
|
|
6613
|
+
}
|
|
6614
|
+
function rewriteSegment(source, segStart, segEnd, parts, next, breakSpelling) {
|
|
6615
|
+
let old = "";
|
|
6616
|
+
const map = [];
|
|
6617
|
+
for (const part of parts) {
|
|
6618
|
+
if (part.kind === "text") {
|
|
6619
|
+
const decoded = decodeText2(source.slice(part.start, part.end));
|
|
6620
|
+
old += decoded.text;
|
|
6621
|
+
for (let k = 0; k < decoded.text.length; k++)
|
|
6622
|
+
map.push(part.start + (decoded.map[k] ?? 0));
|
|
6623
|
+
} else {
|
|
6624
|
+
old += part.kind === "break" ? BREAK : REMOVED;
|
|
6625
|
+
map.push(part.start);
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
map.push(segEnd);
|
|
6629
|
+
if (old === next) return source.slice(segStart, segEnd);
|
|
6630
|
+
let prefix = 0;
|
|
6631
|
+
const limit = Math.min(old.length, next.length);
|
|
6632
|
+
while (prefix < limit && old[prefix] === next[prefix]) prefix++;
|
|
6633
|
+
let suffix = 0;
|
|
6634
|
+
while (suffix < limit - prefix && old[old.length - 1 - suffix] === next[next.length - 1 - suffix])
|
|
6635
|
+
suffix++;
|
|
6636
|
+
if (prefix > 0 && isHighSurrogate(old.charCodeAt(prefix - 1))) prefix--;
|
|
6637
|
+
if (suffix > 0 && isLowSurrogate(old.charCodeAt(old.length - suffix)))
|
|
6638
|
+
suffix--;
|
|
6639
|
+
const from = map[prefix] ?? segEnd;
|
|
6640
|
+
const to = map[old.length - suffix] ?? segEnd;
|
|
6641
|
+
const middle = next.slice(prefix, next.length - suffix);
|
|
6642
|
+
const keepNbsp = old.slice(prefix, old.length - suffix).includes(NBSP);
|
|
6643
|
+
const written = middle.split(BREAK).map((text2) => encodeText(text2, keepNbsp)).join(breakSpelling);
|
|
6644
|
+
return source.slice(segStart, from) + written + source.slice(to, segEnd);
|
|
6645
|
+
}
|
|
6646
|
+
var isHighSurrogate = (code) => code >= 55296 && code <= 56319;
|
|
6647
|
+
var isLowSurrogate = (code) => code >= 56320 && code <= 57343;
|
|
6648
|
+
function flatExpected(items, chipSources) {
|
|
6649
|
+
return items.map(
|
|
6650
|
+
(item) => item.kind === "chip" ? CHIP_OPEN + (chipSources[item.index] ?? "") + CHIP_CLOSE : item.kind === "break" ? BREAK : item.text.replace(/[\uE000-\uE003]/g, "")
|
|
6651
|
+
).join("").replace(new RegExp(NBSP, "g"), " ");
|
|
6652
|
+
}
|
|
6653
|
+
function readsBack(source, engine, tagStart, expected) {
|
|
6654
|
+
const scanned = scanRegion(source, engine, tagStart);
|
|
6655
|
+
if ("reason" in scanned) return false;
|
|
6656
|
+
if (engine === "jinja2" && scanned.parts.some(
|
|
6657
|
+
(p) => p.kind === "text" && source.slice(p.start, p.end).includes("#}")
|
|
6658
|
+
))
|
|
6659
|
+
return false;
|
|
6660
|
+
const flat = scanned.parts.map(
|
|
6661
|
+
(part) => part.kind === "text" ? decodeText2(source.slice(part.start, part.end)).text : part.kind === "break" ? BREAK : CHIP_OPEN + source.slice(part.start, part.end) + CHIP_CLOSE
|
|
6662
|
+
).join("").replace(new RegExp(NBSP, "g"), " ");
|
|
6663
|
+
return flat === expected;
|
|
6664
|
+
}
|
|
6665
|
+
|
|
6666
|
+
// packages/engine/src/lib/source-positions.ts
|
|
6667
|
+
var sourceAttribute = "data-ff-src";
|
|
6668
|
+
var editAttribute = "data-ff-edit";
|
|
6669
|
+
function parseSourceAttribute(value) {
|
|
6670
|
+
const match = /^(body|header|footer):(\d+):(\d+)$/.exec(value ?? "");
|
|
6671
|
+
if (!match) return null;
|
|
6672
|
+
const line = Number(match[2]);
|
|
6673
|
+
const column = Number(match[3]);
|
|
6674
|
+
if (line < 1 || column < 1) return null;
|
|
6675
|
+
return { file: match[1], line, column };
|
|
6676
|
+
}
|
|
6677
|
+
function annotateSourcePositions(source, engine, file, options = {}) {
|
|
6678
|
+
const scanner = new Scanner(source, engine);
|
|
6679
|
+
const lines = lineStarts(source);
|
|
6680
|
+
const out = [];
|
|
6681
|
+
let copied = 0;
|
|
6682
|
+
const n = source.length;
|
|
6683
|
+
let i = 0;
|
|
6684
|
+
while (i < n) {
|
|
6685
|
+
const ch = source[i];
|
|
6686
|
+
if (ch === "{") {
|
|
6687
|
+
i = scanner.skipTemplate(i) ?? i + 1;
|
|
6688
|
+
continue;
|
|
6689
|
+
}
|
|
6690
|
+
if (ch !== "<") {
|
|
6691
|
+
i++;
|
|
6692
|
+
continue;
|
|
6693
|
+
}
|
|
6694
|
+
if (source.startsWith("<!--", i)) {
|
|
6695
|
+
i = scanner.skipTo(i + 4, "-->");
|
|
6696
|
+
continue;
|
|
6697
|
+
}
|
|
6698
|
+
if (source.startsWith("<![CDATA[", i)) {
|
|
6699
|
+
i = scanner.skipTo(i + 9, "]]>");
|
|
6700
|
+
continue;
|
|
6701
|
+
}
|
|
6702
|
+
const next = source[i + 1];
|
|
6703
|
+
if (next === "!" || next === "?") {
|
|
6704
|
+
i = scanner.tagEnd(i + 2);
|
|
6705
|
+
continue;
|
|
6706
|
+
}
|
|
6707
|
+
if (next === "/") {
|
|
6708
|
+
i += 2;
|
|
6709
|
+
continue;
|
|
6710
|
+
}
|
|
6711
|
+
tagName.lastIndex = i + 1;
|
|
6712
|
+
const name = tagName.exec(source)?.[0];
|
|
6713
|
+
if (!name) {
|
|
6714
|
+
i++;
|
|
6715
|
+
continue;
|
|
6716
|
+
}
|
|
6717
|
+
const nameEnd = i + 1 + name.length;
|
|
6718
|
+
const end = scanner.tagEnd(nameEnd);
|
|
6719
|
+
const after = source[nameEnd];
|
|
6720
|
+
const plainName = after === void 0 || after === ">" || after === "/" || /\s/.test(after);
|
|
6721
|
+
if (plainName && !source.slice(nameEnd, end).toLowerCase().includes(sourceAttribute)) {
|
|
6722
|
+
const { line, column } = locate(lines, i);
|
|
6723
|
+
const region = options.editable ? editableRegionAt(source, engine, i) : null;
|
|
6724
|
+
const edit = region && !("reason" in region) ? ` ${editAttribute}="${region.kind}"` : "";
|
|
6725
|
+
out.push(
|
|
6726
|
+
source.slice(copied, nameEnd),
|
|
6727
|
+
` ${sourceAttribute}="${file}:${line}:${column}"${edit}`
|
|
6728
|
+
);
|
|
6729
|
+
copied = nameEnd;
|
|
6730
|
+
}
|
|
6731
|
+
i = end;
|
|
6732
|
+
if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
|
|
6733
|
+
i = scanner.skipToClosingTag(end, name);
|
|
6734
|
+
}
|
|
6735
|
+
out.push(source.slice(copied));
|
|
6736
|
+
return out.join("");
|
|
6737
|
+
}
|
|
6738
|
+
|
|
6739
|
+
// packages/engine/src/lib/design/types.ts
|
|
6740
|
+
var DESIGN_VERSION = 1;
|
|
6741
|
+
|
|
6742
|
+
// packages/engine/src/lib/design/parse.ts
|
|
6743
|
+
var DESIGN_LIMITS = {
|
|
6744
|
+
layers: 200,
|
|
6745
|
+
textPerLayer: 5e3,
|
|
6746
|
+
bytes: 256 * 1024
|
|
6747
|
+
};
|
|
6748
|
+
var layerIdPattern = /^[a-z][a-z0-9-]{0,31}$/;
|
|
6749
|
+
var dataPathPattern = /^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*$/;
|
|
6750
|
+
var colourPattern = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
6751
|
+
var tokenPattern = /^[a-z][a-z0-9-]{0,31}$/;
|
|
6752
|
+
var symbologies = [
|
|
6753
|
+
"code128",
|
|
6754
|
+
"ean13",
|
|
6755
|
+
"ean8",
|
|
6756
|
+
"upca",
|
|
6757
|
+
"itf14",
|
|
6758
|
+
"code39"
|
|
6759
|
+
];
|
|
6760
|
+
var Reader = class {
|
|
6761
|
+
constructor(at) {
|
|
6762
|
+
this.at = at;
|
|
6763
|
+
}
|
|
6764
|
+
problems = [];
|
|
6765
|
+
problem(message) {
|
|
6766
|
+
this.problems.push(`${this.at}: ${message}`);
|
|
6767
|
+
}
|
|
6768
|
+
number(value, name, min, max2, fallback) {
|
|
6769
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6770
|
+
if (fallback !== void 0 && value === void 0) return fallback;
|
|
6771
|
+
this.problem(`${name} must be a number`);
|
|
6772
|
+
return fallback ?? min;
|
|
6773
|
+
}
|
|
6774
|
+
return Math.min(max2, Math.max(min, value));
|
|
6775
|
+
}
|
|
6776
|
+
string(value, name, max2, pattern) {
|
|
6777
|
+
if (typeof value !== "string" || value.length > max2 || pattern && !pattern.test(value)) {
|
|
6778
|
+
this.problem(`${name} is not valid`);
|
|
6779
|
+
return "";
|
|
6780
|
+
}
|
|
6781
|
+
return value;
|
|
6782
|
+
}
|
|
6783
|
+
oneOf(value, name, options, fallback) {
|
|
6784
|
+
if (value === void 0 && fallback !== void 0) return fallback;
|
|
6785
|
+
if (typeof value === "string" && options.includes(value))
|
|
6786
|
+
return value;
|
|
6787
|
+
this.problem(`${name} must be one of ${options.join(", ")}`);
|
|
6788
|
+
return options[0];
|
|
6789
|
+
}
|
|
6790
|
+
bool(value, fallback) {
|
|
6791
|
+
return typeof value === "boolean" ? value : fallback;
|
|
6792
|
+
}
|
|
6793
|
+
paint(value, name) {
|
|
6794
|
+
const v = value;
|
|
6795
|
+
if (v && typeof v["color"] === "string" && colourPattern.test(v["color"]))
|
|
6796
|
+
return { color: v["color"] };
|
|
6797
|
+
if (v && typeof v["brand"] === "string" && tokenPattern.test(v["brand"]) && typeof v["fallback"] === "string" && colourPattern.test(v["fallback"]))
|
|
6798
|
+
return { brand: v["brand"], fallback: v["fallback"] };
|
|
6799
|
+
this.problem(`${name} is not a colour`);
|
|
6800
|
+
return { color: "#000000" };
|
|
6801
|
+
}
|
|
6802
|
+
paintOrNull(value, name) {
|
|
6803
|
+
return value === null || value === void 0 ? null : this.paint(value, name);
|
|
6804
|
+
}
|
|
6805
|
+
path(value, name) {
|
|
6806
|
+
return this.string(value, name, 200, dataPathPattern);
|
|
6807
|
+
}
|
|
6808
|
+
};
|
|
6809
|
+
function designBytes(value) {
|
|
6810
|
+
return new TextEncoder().encode(JSON.stringify(value) ?? "").length;
|
|
6811
|
+
}
|
|
6812
|
+
function parseDesign(value) {
|
|
6813
|
+
const root = new Reader("design");
|
|
6814
|
+
const v = value;
|
|
6815
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
6816
|
+
return { design: null, problems: ["design: not an object"] };
|
|
6817
|
+
if (designBytes(v) > DESIGN_LIMITS.bytes)
|
|
6818
|
+
return {
|
|
6819
|
+
design: null,
|
|
6820
|
+
problems: [`design: larger than ${DESIGN_LIMITS.bytes / 1024} kB`]
|
|
6821
|
+
};
|
|
6822
|
+
if (v["version"] !== DESIGN_VERSION)
|
|
6823
|
+
root.problem(`version must be ${DESIGN_VERSION}`);
|
|
6824
|
+
const output = typeof v["output"] === "string" ? v["output"].slice(0, 64) : "";
|
|
6825
|
+
const background = root.paintOrNull(v["background"], "background");
|
|
6826
|
+
const rawLayers = Array.isArray(v["layers"]) ? v["layers"] : null;
|
|
6827
|
+
if (!rawLayers) root.problem("layers must be a list");
|
|
6828
|
+
if (rawLayers && rawLayers.length > DESIGN_LIMITS.layers)
|
|
6829
|
+
root.problem(`at most ${DESIGN_LIMITS.layers} layers`);
|
|
6830
|
+
const problems = [...root.problems];
|
|
6831
|
+
const layers = [];
|
|
6832
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6833
|
+
for (const [index, raw] of (rawLayers ?? []).slice(0, DESIGN_LIMITS.layers).entries()) {
|
|
6834
|
+
const id = raw?.["id"];
|
|
6835
|
+
const reader = new Reader(
|
|
6836
|
+
`layer ${typeof id === "string" ? id : index + 1}`
|
|
6837
|
+
);
|
|
6838
|
+
const layer = readLayer(reader, raw);
|
|
6839
|
+
if (layer && ids.has(layer.id)) reader.problem("id is used twice");
|
|
6840
|
+
if (layer) ids.add(layer.id);
|
|
6841
|
+
problems.push(...reader.problems);
|
|
6842
|
+
if (layer) layers.push(layer);
|
|
6843
|
+
}
|
|
6844
|
+
let guides;
|
|
6845
|
+
const g = v["guides"];
|
|
6846
|
+
if (g && typeof g === "object") {
|
|
6847
|
+
const list2 = (x) => (Array.isArray(x) ? x : []).filter((n) => typeof n === "number" && Number.isFinite(n)).slice(0, 100);
|
|
6848
|
+
guides = { x: list2(g["x"]), y: list2(g["y"]) };
|
|
6849
|
+
}
|
|
6850
|
+
if (problems.length) return { design: null, problems };
|
|
6851
|
+
return {
|
|
6852
|
+
design: {
|
|
6853
|
+
version: DESIGN_VERSION,
|
|
6854
|
+
output,
|
|
6855
|
+
background,
|
|
6856
|
+
layers,
|
|
6857
|
+
...guides ? { guides } : {}
|
|
6858
|
+
},
|
|
6859
|
+
problems: []
|
|
6860
|
+
};
|
|
6861
|
+
}
|
|
6862
|
+
function readLayer(r, raw) {
|
|
6863
|
+
const v = raw;
|
|
6864
|
+
if (!v || typeof v !== "object") {
|
|
6865
|
+
r.problem("not an object");
|
|
6866
|
+
return null;
|
|
6867
|
+
}
|
|
6868
|
+
const base = {
|
|
6869
|
+
id: r.string(v["id"], "id", 32, layerIdPattern),
|
|
6870
|
+
name: typeof v["name"] === "string" && v["name"].trim() ? v["name"].slice(0, 100) : String(v["id"] ?? ""),
|
|
6871
|
+
x: r.number(v["x"], "x", -1e4, 1e4),
|
|
6872
|
+
y: r.number(v["y"], "y", -1e4, 1e4),
|
|
6873
|
+
width: r.number(v["width"], "width", 1, 1e4),
|
|
6874
|
+
height: r.number(v["height"], "height", 1, 1e4),
|
|
6875
|
+
rotation: r.number(v["rotation"], "rotation", -360, 360, 0),
|
|
6876
|
+
opacity: r.number(v["opacity"], "opacity", 0, 1, 1),
|
|
6877
|
+
visible: r.bool(v["visible"], true),
|
|
6878
|
+
...v["locked"] === true ? { locked: true } : {}
|
|
6879
|
+
};
|
|
6880
|
+
const showWhen = v["showWhen"];
|
|
6881
|
+
if (showWhen) base.showWhen = { path: r.path(showWhen["path"], "showWhen") };
|
|
6882
|
+
if (v["radius"] !== void 0)
|
|
6883
|
+
base.radius = r.number(v["radius"], "radius", 0, 5e3);
|
|
6884
|
+
if (v["shadow"]) base.shadow = readShadow(r, v["shadow"]);
|
|
6885
|
+
if (v["stroke"]) base.stroke = readStroke(r, v["stroke"]);
|
|
6886
|
+
switch (v["type"]) {
|
|
6887
|
+
case "text": {
|
|
6888
|
+
const style2 = readStyle(r, v["style"]);
|
|
6889
|
+
const layer = {
|
|
6890
|
+
...base,
|
|
6891
|
+
type: "text",
|
|
6892
|
+
content: readContent(r, v["content"]),
|
|
6893
|
+
style: style2,
|
|
6894
|
+
sizing: r.oneOf(
|
|
6895
|
+
v["sizing"],
|
|
6896
|
+
"sizing",
|
|
6897
|
+
["fixed", "shrink", "grow"],
|
|
6898
|
+
"fixed"
|
|
6899
|
+
),
|
|
6900
|
+
valign: r.oneOf(
|
|
6901
|
+
v["valign"],
|
|
6902
|
+
"valign",
|
|
6903
|
+
["top", "middle", "bottom"],
|
|
6904
|
+
"top"
|
|
6905
|
+
)
|
|
6906
|
+
};
|
|
6907
|
+
if (v["minFontSize"] !== void 0)
|
|
6908
|
+
layer.minFontSize = r.number(
|
|
6909
|
+
v["minFontSize"],
|
|
6910
|
+
"minFontSize",
|
|
6911
|
+
1,
|
|
6912
|
+
style2.size
|
|
6913
|
+
);
|
|
6914
|
+
if (v["maxLines"] !== void 0)
|
|
6915
|
+
layer.maxLines = Math.round(
|
|
6916
|
+
r.number(v["maxLines"], "maxLines", 1, 100)
|
|
6917
|
+
);
|
|
6918
|
+
if (v["fill"] !== void 0)
|
|
6919
|
+
layer.fill = r.paintOrNull(v["fill"], "fill");
|
|
6920
|
+
if (v["padding"] !== void 0)
|
|
6921
|
+
layer.padding = r.number(v["padding"], "padding", 0, 1e3);
|
|
6922
|
+
return layer;
|
|
6923
|
+
}
|
|
6924
|
+
case "image": {
|
|
6925
|
+
const layer = {
|
|
6926
|
+
...base,
|
|
6927
|
+
type: "image",
|
|
6928
|
+
source: readImageSource(r, v["source"]),
|
|
6929
|
+
fit: r.oneOf(
|
|
6930
|
+
v["fit"],
|
|
6931
|
+
"fit",
|
|
6932
|
+
["cover", "contain", "fill"],
|
|
6933
|
+
"cover"
|
|
6934
|
+
)
|
|
6935
|
+
};
|
|
6936
|
+
const focus = v["focus"];
|
|
6937
|
+
if (focus)
|
|
6938
|
+
layer.focus = {
|
|
6939
|
+
x: r.number(focus["x"], "focus", 0, 100),
|
|
6940
|
+
y: r.number(focus["y"], "focus", 0, 100)
|
|
6941
|
+
};
|
|
6942
|
+
return layer;
|
|
6943
|
+
}
|
|
6944
|
+
case "shape":
|
|
6945
|
+
return {
|
|
6946
|
+
...base,
|
|
6947
|
+
type: "shape",
|
|
6948
|
+
shape: r.oneOf(v["shape"], "shape", [
|
|
6949
|
+
"rectangle",
|
|
6950
|
+
"ellipse",
|
|
6951
|
+
"line"
|
|
6952
|
+
]),
|
|
6953
|
+
fill: r.paintOrNull(v["fill"], "fill")
|
|
6954
|
+
};
|
|
6955
|
+
case "qr":
|
|
6956
|
+
return {
|
|
6957
|
+
...base,
|
|
6958
|
+
type: "qr",
|
|
6959
|
+
value: readBinding(r, v["value"]),
|
|
6960
|
+
ecc: r.oneOf(v["ecc"], "ecc", ["L", "M", "Q", "H"], "M"),
|
|
6961
|
+
color: r.paint(v["color"] ?? { color: "#000000" }, "color"),
|
|
6962
|
+
background: r.paintOrNull(v["background"], "background")
|
|
6963
|
+
};
|
|
6964
|
+
case "barcode":
|
|
6965
|
+
return {
|
|
6966
|
+
...base,
|
|
6967
|
+
type: "barcode",
|
|
6968
|
+
value: readBinding(r, v["value"]),
|
|
6969
|
+
symbology: r.oneOf(v["symbology"], "symbology", symbologies, "code128"),
|
|
6970
|
+
showText: r.bool(v["showText"], true)
|
|
6971
|
+
};
|
|
6972
|
+
default:
|
|
6973
|
+
r.problem("unknown layer type");
|
|
6974
|
+
return null;
|
|
6975
|
+
}
|
|
6976
|
+
}
|
|
6977
|
+
function readShadow(r, raw) {
|
|
6978
|
+
const v = raw ?? {};
|
|
6979
|
+
const color = r.string(v["color"], "shadow colour", 9, colourPattern) || "#00000040";
|
|
6980
|
+
return {
|
|
6981
|
+
x: r.number(v["x"], "shadow x", -500, 500, 0),
|
|
6982
|
+
y: r.number(v["y"], "shadow y", -500, 500, 0),
|
|
6983
|
+
blur: r.number(v["blur"], "shadow blur", 0, 500, 0),
|
|
6984
|
+
color
|
|
6985
|
+
};
|
|
6986
|
+
}
|
|
6987
|
+
function readStroke(r, raw) {
|
|
6988
|
+
const v = raw ?? {};
|
|
6989
|
+
return {
|
|
6990
|
+
width: r.number(v["width"], "stroke width", 0, 200),
|
|
6991
|
+
paint: r.paint(v["paint"], "stroke colour")
|
|
6992
|
+
};
|
|
6993
|
+
}
|
|
6994
|
+
function readStyle(r, raw) {
|
|
6995
|
+
const v = raw ?? {};
|
|
6996
|
+
const style2 = {
|
|
6997
|
+
font: readFont(r, v["font"]),
|
|
6998
|
+
weight: Math.round(r.number(v["weight"], "weight", 100, 900, 400) / 100) * 100,
|
|
6999
|
+
size: r.number(v["size"], "size", 1, 2e3),
|
|
7000
|
+
lineHeight: r.number(v["lineHeight"], "lineHeight", 0.5, 5, 1.2),
|
|
7001
|
+
letterSpacing: r.number(v["letterSpacing"], "letterSpacing", -50, 200, 0),
|
|
7002
|
+
color: r.paint(v["color"], "colour"),
|
|
7003
|
+
align: r.oneOf(
|
|
7004
|
+
v["align"],
|
|
7005
|
+
"align",
|
|
7006
|
+
["left", "center", "right", "justify"],
|
|
7007
|
+
"left"
|
|
7008
|
+
)
|
|
7009
|
+
};
|
|
7010
|
+
if (v["italic"] === true) style2.italic = true;
|
|
7011
|
+
if (v["uppercase"] === true) style2.uppercase = true;
|
|
7012
|
+
if (v["underline"] === true) style2.underline = true;
|
|
7013
|
+
if (v["strike"] === true) style2.strike = true;
|
|
7014
|
+
return style2;
|
|
7015
|
+
}
|
|
7016
|
+
function readFont(r, raw) {
|
|
7017
|
+
const v = raw ?? {};
|
|
7018
|
+
if (v["brand"] === "heading" || v["brand"] === "body")
|
|
7019
|
+
return { brand: v["brand"] };
|
|
7020
|
+
const family = r.string(v["family"], "font", 100, /^[^"'\\<>\n\r;{}]+$/);
|
|
7021
|
+
return {
|
|
7022
|
+
family,
|
|
7023
|
+
source: r.oneOf(
|
|
7024
|
+
v["source"],
|
|
7025
|
+
"font source",
|
|
7026
|
+
["google", "organisation", "system"],
|
|
7027
|
+
"system"
|
|
7028
|
+
)
|
|
7029
|
+
};
|
|
7030
|
+
}
|
|
7031
|
+
function readContent(r, raw) {
|
|
7032
|
+
if (!Array.isArray(raw)) {
|
|
7033
|
+
r.problem("content must be a list");
|
|
7034
|
+
return [];
|
|
7035
|
+
}
|
|
7036
|
+
let length = 0;
|
|
7037
|
+
const items = [];
|
|
7038
|
+
for (const item of raw) {
|
|
7039
|
+
if (item && typeof item["text"] === "string") {
|
|
7040
|
+
length += item["text"].length;
|
|
7041
|
+
items.push({ text: item["text"] });
|
|
7042
|
+
} else if (item && item["break"] === true) items.push({ break: true });
|
|
7043
|
+
else if (item && typeof item["path"] === "string") {
|
|
7044
|
+
const path = r.path(item["path"], "content path");
|
|
7045
|
+
const format = item["format"] === void 0 ? void 0 : readFormat(r, item["format"]);
|
|
7046
|
+
items.push(format ? { path, format } : { path });
|
|
7047
|
+
} else
|
|
7048
|
+
r.problem("content holds something that is not text, a path or a break");
|
|
7049
|
+
}
|
|
7050
|
+
if (length > 5e3) r.problem("at most 5000 characters of text");
|
|
7051
|
+
return items;
|
|
7052
|
+
}
|
|
7053
|
+
function readFormat(r, raw) {
|
|
7054
|
+
const v = raw ?? {};
|
|
7055
|
+
switch (v["helper"]) {
|
|
7056
|
+
case "upper":
|
|
7057
|
+
case "lower":
|
|
7058
|
+
case "title":
|
|
7059
|
+
case "money":
|
|
7060
|
+
return { helper: v["helper"] };
|
|
7061
|
+
case "number":
|
|
7062
|
+
return {
|
|
7063
|
+
helper: "number",
|
|
7064
|
+
decimals: Math.round(r.number(v["decimals"], "decimals", 0, 10, 2))
|
|
7065
|
+
};
|
|
7066
|
+
case "date":
|
|
7067
|
+
return {
|
|
7068
|
+
helper: "date",
|
|
7069
|
+
pattern: r.string(v["pattern"], "date pattern", 64, /^[^{}\n\r]+$/)
|
|
7070
|
+
};
|
|
7071
|
+
case "truncate":
|
|
7072
|
+
return {
|
|
7073
|
+
helper: "truncate",
|
|
7074
|
+
length: Math.round(r.number(v["length"], "length", 1, 5e3))
|
|
7075
|
+
};
|
|
7076
|
+
case "default":
|
|
7077
|
+
return {
|
|
7078
|
+
helper: "default",
|
|
7079
|
+
value: r.string(v["value"], "default value", 500, /^[^{}\n\r]*$/)
|
|
7080
|
+
};
|
|
7081
|
+
default:
|
|
7082
|
+
r.problem("unknown format");
|
|
7083
|
+
return void 0;
|
|
7084
|
+
}
|
|
7085
|
+
}
|
|
7086
|
+
function readImageSource(r, raw) {
|
|
7087
|
+
const v = raw ?? {};
|
|
7088
|
+
if (v["url"] === "") return { url: "" };
|
|
7089
|
+
if (v["asset"] === "") return { asset: "" };
|
|
7090
|
+
if (v["path"] === "") return { path: "" };
|
|
7091
|
+
if (typeof v["url"] === "string")
|
|
7092
|
+
return {
|
|
7093
|
+
url: r.string(v["url"], "image url", 2e3, /^https?:\/\/[^\s"'<>]+$/)
|
|
7094
|
+
};
|
|
7095
|
+
if (typeof v["asset"] === "string")
|
|
7096
|
+
return {
|
|
7097
|
+
asset: r.string(v["asset"], "file name", 200, /^[^"\\<>\n\r{}]+$/)
|
|
7098
|
+
};
|
|
7099
|
+
if (typeof v["path"] === "string")
|
|
7100
|
+
return { path: r.path(v["path"], "image path") };
|
|
7101
|
+
if (v["brandLogo"] !== void 0)
|
|
7102
|
+
return {
|
|
7103
|
+
brandLogo: r.oneOf(v["brandLogo"], "brand logo", [
|
|
7104
|
+
"primary",
|
|
7105
|
+
"inverse",
|
|
7106
|
+
"mark"
|
|
7107
|
+
])
|
|
7108
|
+
};
|
|
7109
|
+
r.problem("the image has no source");
|
|
7110
|
+
return { url: "" };
|
|
7111
|
+
}
|
|
7112
|
+
function readBinding(r, raw) {
|
|
7113
|
+
const v = raw ?? {};
|
|
7114
|
+
if (typeof v["path"] === "string")
|
|
7115
|
+
return { path: r.path(v["path"], "value path") };
|
|
7116
|
+
if (typeof v["text"] === "string")
|
|
7117
|
+
return { text: r.string(v["text"], "value", 2e3, /^[^\n\r{}]*$/) };
|
|
7118
|
+
r.problem("the code has no value");
|
|
7119
|
+
return { text: "" };
|
|
7120
|
+
}
|
|
7121
|
+
|
|
7122
|
+
// packages/engine/src/lib/design/fonts.ts
|
|
7123
|
+
var range = (from, to) => Array.from({ length: (to - from) / 100 + 1 }, (_, i) => from + i * 100);
|
|
7124
|
+
var googleFonts = [
|
|
7125
|
+
{
|
|
7126
|
+
family: "Inter",
|
|
7127
|
+
category: "sans-serif",
|
|
7128
|
+
weights: range(100, 900),
|
|
7129
|
+
italic: true
|
|
7130
|
+
},
|
|
7131
|
+
{
|
|
7132
|
+
family: "Roboto",
|
|
7133
|
+
category: "sans-serif",
|
|
7134
|
+
weights: range(100, 900),
|
|
7135
|
+
italic: true
|
|
7136
|
+
},
|
|
7137
|
+
{
|
|
7138
|
+
family: "Open Sans",
|
|
7139
|
+
category: "sans-serif",
|
|
7140
|
+
weights: range(300, 800),
|
|
7141
|
+
italic: true
|
|
7142
|
+
},
|
|
7143
|
+
{
|
|
7144
|
+
family: "Lato",
|
|
7145
|
+
category: "sans-serif",
|
|
7146
|
+
weights: [100, 300, 400, 700, 900],
|
|
7147
|
+
italic: true
|
|
7148
|
+
},
|
|
7149
|
+
{
|
|
7150
|
+
family: "Montserrat",
|
|
7151
|
+
category: "sans-serif",
|
|
7152
|
+
weights: range(100, 900),
|
|
7153
|
+
italic: true
|
|
7154
|
+
},
|
|
7155
|
+
{
|
|
7156
|
+
family: "Poppins",
|
|
7157
|
+
category: "sans-serif",
|
|
7158
|
+
weights: range(100, 900),
|
|
7159
|
+
italic: true
|
|
7160
|
+
},
|
|
7161
|
+
{
|
|
7162
|
+
family: "Source Sans 3",
|
|
7163
|
+
category: "sans-serif",
|
|
7164
|
+
weights: range(200, 900),
|
|
7165
|
+
italic: true
|
|
7166
|
+
},
|
|
7167
|
+
{
|
|
7168
|
+
family: "Nunito",
|
|
7169
|
+
category: "sans-serif",
|
|
7170
|
+
weights: range(200, 900),
|
|
7171
|
+
italic: true
|
|
7172
|
+
},
|
|
7173
|
+
{
|
|
7174
|
+
family: "Raleway",
|
|
7175
|
+
category: "sans-serif",
|
|
7176
|
+
weights: range(100, 900),
|
|
7177
|
+
italic: true
|
|
7178
|
+
},
|
|
7179
|
+
{
|
|
7180
|
+
family: "Work Sans",
|
|
7181
|
+
category: "sans-serif",
|
|
7182
|
+
weights: range(100, 900),
|
|
7183
|
+
italic: true
|
|
7184
|
+
},
|
|
7185
|
+
{
|
|
7186
|
+
family: "DM Sans",
|
|
7187
|
+
category: "sans-serif",
|
|
7188
|
+
weights: range(100, 900),
|
|
7189
|
+
italic: true
|
|
7190
|
+
},
|
|
7191
|
+
{
|
|
7192
|
+
family: "Manrope",
|
|
7193
|
+
category: "sans-serif",
|
|
7194
|
+
weights: range(200, 800),
|
|
7195
|
+
italic: false
|
|
7196
|
+
},
|
|
7197
|
+
{
|
|
7198
|
+
family: "Rubik",
|
|
7199
|
+
category: "sans-serif",
|
|
7200
|
+
weights: range(300, 900),
|
|
7201
|
+
italic: true
|
|
7202
|
+
},
|
|
7203
|
+
{
|
|
7204
|
+
family: "Noto Sans",
|
|
7205
|
+
category: "sans-serif",
|
|
7206
|
+
weights: range(100, 900),
|
|
7207
|
+
italic: true
|
|
7208
|
+
},
|
|
7209
|
+
{
|
|
7210
|
+
family: "IBM Plex Sans",
|
|
7211
|
+
category: "sans-serif",
|
|
7212
|
+
weights: range(100, 700),
|
|
7213
|
+
italic: true
|
|
7214
|
+
},
|
|
7215
|
+
{
|
|
7216
|
+
family: "Space Grotesk",
|
|
7217
|
+
category: "sans-serif",
|
|
7218
|
+
weights: range(300, 700),
|
|
7219
|
+
italic: false
|
|
7220
|
+
},
|
|
7221
|
+
{
|
|
7222
|
+
family: "Outfit",
|
|
7223
|
+
category: "sans-serif",
|
|
7224
|
+
weights: range(100, 900),
|
|
7225
|
+
italic: false
|
|
7226
|
+
},
|
|
7227
|
+
{
|
|
7228
|
+
family: "Plus Jakarta Sans",
|
|
7229
|
+
category: "sans-serif",
|
|
7230
|
+
weights: range(200, 800),
|
|
7231
|
+
italic: true
|
|
7232
|
+
},
|
|
7233
|
+
{
|
|
7234
|
+
family: "Figtree",
|
|
7235
|
+
category: "sans-serif",
|
|
7236
|
+
weights: range(300, 900),
|
|
7237
|
+
italic: true
|
|
7238
|
+
},
|
|
7239
|
+
{
|
|
7240
|
+
family: "Karla",
|
|
7241
|
+
category: "sans-serif",
|
|
7242
|
+
weights: range(200, 800),
|
|
7243
|
+
italic: true
|
|
7244
|
+
},
|
|
7245
|
+
{
|
|
7246
|
+
family: "Barlow",
|
|
7247
|
+
category: "sans-serif",
|
|
7248
|
+
weights: range(100, 900),
|
|
7249
|
+
italic: true
|
|
7250
|
+
},
|
|
7251
|
+
{
|
|
7252
|
+
family: "Archivo",
|
|
7253
|
+
category: "sans-serif",
|
|
7254
|
+
weights: range(100, 900),
|
|
7255
|
+
italic: true
|
|
7256
|
+
},
|
|
7257
|
+
{
|
|
7258
|
+
family: "Oswald",
|
|
7259
|
+
category: "display",
|
|
7260
|
+
weights: range(200, 700),
|
|
7261
|
+
italic: false
|
|
7262
|
+
},
|
|
7263
|
+
{ family: "Bebas Neue", category: "display", weights: [400], italic: false },
|
|
7264
|
+
{ family: "Anton", category: "display", weights: [400], italic: false },
|
|
7265
|
+
{
|
|
7266
|
+
family: "Playfair Display",
|
|
7267
|
+
category: "serif",
|
|
7268
|
+
weights: range(400, 900),
|
|
7269
|
+
italic: true
|
|
7270
|
+
},
|
|
7271
|
+
{
|
|
7272
|
+
family: "Merriweather",
|
|
7273
|
+
category: "serif",
|
|
7274
|
+
weights: range(300, 900),
|
|
7275
|
+
italic: true
|
|
7276
|
+
},
|
|
7277
|
+
{ family: "Lora", category: "serif", weights: range(400, 700), italic: true },
|
|
7278
|
+
{
|
|
7279
|
+
family: "Libre Baskerville",
|
|
7280
|
+
category: "serif",
|
|
7281
|
+
weights: [400, 700],
|
|
7282
|
+
italic: true
|
|
7283
|
+
},
|
|
7284
|
+
{
|
|
7285
|
+
family: "Source Serif 4",
|
|
7286
|
+
category: "serif",
|
|
7287
|
+
weights: range(200, 900),
|
|
7288
|
+
italic: true
|
|
7289
|
+
},
|
|
7290
|
+
{
|
|
7291
|
+
family: "EB Garamond",
|
|
7292
|
+
category: "serif",
|
|
7293
|
+
weights: range(400, 800),
|
|
7294
|
+
italic: true
|
|
7295
|
+
},
|
|
7296
|
+
{
|
|
7297
|
+
family: "Cormorant Garamond",
|
|
7298
|
+
category: "serif",
|
|
7299
|
+
weights: range(300, 700),
|
|
7300
|
+
italic: true
|
|
7301
|
+
},
|
|
7302
|
+
{
|
|
7303
|
+
family: "DM Serif Display",
|
|
7304
|
+
category: "serif",
|
|
7305
|
+
weights: [400],
|
|
7306
|
+
italic: true
|
|
7307
|
+
},
|
|
7308
|
+
{
|
|
7309
|
+
family: "Fraunces",
|
|
7310
|
+
category: "serif",
|
|
7311
|
+
weights: range(100, 900),
|
|
7312
|
+
italic: true
|
|
7313
|
+
},
|
|
7314
|
+
{
|
|
7315
|
+
family: "Roboto Slab",
|
|
7316
|
+
category: "serif",
|
|
7317
|
+
weights: range(100, 900),
|
|
7318
|
+
italic: false
|
|
7319
|
+
},
|
|
7320
|
+
{
|
|
7321
|
+
family: "Zilla Slab",
|
|
7322
|
+
category: "serif",
|
|
7323
|
+
weights: [300, 400, 500, 600, 700],
|
|
7324
|
+
italic: true
|
|
7325
|
+
},
|
|
7326
|
+
{
|
|
7327
|
+
family: "JetBrains Mono",
|
|
7328
|
+
category: "monospace",
|
|
7329
|
+
weights: range(100, 800),
|
|
7330
|
+
italic: true
|
|
7331
|
+
},
|
|
7332
|
+
{
|
|
7333
|
+
family: "IBM Plex Mono",
|
|
7334
|
+
category: "monospace",
|
|
7335
|
+
weights: range(100, 700),
|
|
7336
|
+
italic: true
|
|
7337
|
+
},
|
|
7338
|
+
{
|
|
7339
|
+
family: "Space Mono",
|
|
7340
|
+
category: "monospace",
|
|
7341
|
+
weights: [400, 700],
|
|
7342
|
+
italic: true
|
|
7343
|
+
},
|
|
7344
|
+
{
|
|
7345
|
+
family: "Caveat",
|
|
7346
|
+
category: "handwriting",
|
|
7347
|
+
weights: range(400, 700),
|
|
7348
|
+
italic: false
|
|
7349
|
+
},
|
|
7350
|
+
{
|
|
7351
|
+
family: "Pacifico",
|
|
7352
|
+
category: "handwriting",
|
|
7353
|
+
weights: [400],
|
|
7354
|
+
italic: false
|
|
7355
|
+
},
|
|
7356
|
+
{
|
|
7357
|
+
family: "Dancing Script",
|
|
7358
|
+
category: "handwriting",
|
|
7359
|
+
weights: range(400, 700),
|
|
7360
|
+
italic: false
|
|
7361
|
+
}
|
|
7362
|
+
];
|
|
7363
|
+
var byFamily = new Map(googleFonts.map((font) => [font.family, font]));
|
|
7364
|
+
function googleFont(family) {
|
|
7365
|
+
return byFamily.get(family);
|
|
7366
|
+
}
|
|
7367
|
+
function genericFamily(font) {
|
|
7368
|
+
if (!font) return "sans-serif";
|
|
7369
|
+
return font.category === "serif" ? "serif" : font.category === "monospace" ? "monospace" : font.category === "handwriting" ? "cursive" : "sans-serif";
|
|
7370
|
+
}
|
|
7371
|
+
function nearestWeight(font, weight) {
|
|
7372
|
+
return font.weights.reduce(
|
|
7373
|
+
(best, w) => Math.abs(w - weight) < Math.abs(best - weight) || Math.abs(w - weight) === Math.abs(best - weight) && w > best ? w : best,
|
|
7374
|
+
font.weights[0] ?? 400
|
|
7375
|
+
);
|
|
7376
|
+
}
|
|
7377
|
+
function googleFontsUrl(uses) {
|
|
7378
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
7379
|
+
for (const use of uses) {
|
|
7380
|
+
const font = byFamily.get(use.family);
|
|
7381
|
+
if (!font) continue;
|
|
7382
|
+
const italic = use.italic && font.italic ? 1 : 0;
|
|
7383
|
+
const set = wanted.get(font.family) ?? /* @__PURE__ */ new Set();
|
|
7384
|
+
set.add(`${italic},${nearestWeight(font, use.weight)}`);
|
|
7385
|
+
wanted.set(font.family, set);
|
|
7386
|
+
}
|
|
7387
|
+
if (!wanted.size) return null;
|
|
7388
|
+
const families = [...wanted.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([family, set]) => {
|
|
7389
|
+
const tuples = [...set].map((t) => t.split(",").map(Number)).sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
7390
|
+
const name = family.replace(/ /g, "+");
|
|
7391
|
+
return tuples.some(([italic]) => italic) ? `family=${name}:ital,wght@${tuples.map(([i, w]) => `${i},${w}`).join(";")}` : `family=${name}:wght@${tuples.map(([, w]) => w).join(";")}`;
|
|
7392
|
+
});
|
|
7393
|
+
return `https://fonts.googleapis.com/css2?${families.join("&")}&display=block`;
|
|
7394
|
+
}
|
|
7395
|
+
|
|
7396
|
+
// packages/engine/src/lib/design/hash.ts
|
|
7397
|
+
function designOutputHash(html, css2) {
|
|
7398
|
+
const text2 = `${html}\0${css2}`;
|
|
7399
|
+
let a = 2166136261;
|
|
7400
|
+
let b = 16777619 ^ 2654435769;
|
|
7401
|
+
for (let i = 0; i < text2.length; i++) {
|
|
7402
|
+
const code = text2.charCodeAt(i);
|
|
7403
|
+
if (code === 13) continue;
|
|
7404
|
+
a = Math.imul(a ^ code, 16777619) >>> 0;
|
|
7405
|
+
b = Math.imul(b ^ code, 16777619) >>> 0;
|
|
7406
|
+
b = b + (a >>> 13) >>> 0;
|
|
7407
|
+
}
|
|
7408
|
+
return a.toString(16).padStart(8, "0") + b.toString(16).padStart(8, "0");
|
|
7409
|
+
}
|
|
7410
|
+
|
|
7411
|
+
// packages/engine/src/lib/design/layers.ts
|
|
7412
|
+
function imageIsEmpty(source) {
|
|
7413
|
+
if ("url" in source) return source.url === "";
|
|
7414
|
+
if ("asset" in source) return source.asset === "";
|
|
7415
|
+
if ("path" in source) return source.path === "";
|
|
7416
|
+
return false;
|
|
7417
|
+
}
|
|
7418
|
+
function emptyDesign() {
|
|
7419
|
+
return {
|
|
7420
|
+
version: DESIGN_VERSION,
|
|
7421
|
+
output: "",
|
|
7422
|
+
background: { color: "#ffffff" },
|
|
7423
|
+
layers: []
|
|
7424
|
+
};
|
|
7425
|
+
}
|
|
7426
|
+
function nextLayerId(design2, type) {
|
|
7427
|
+
const taken = new Set(design2.layers.map((layer) => layer.id));
|
|
7428
|
+
for (let n = 1; ; n++) {
|
|
7429
|
+
const id = `${type}-${n}`;
|
|
7430
|
+
if (!taken.has(id)) return id;
|
|
7431
|
+
}
|
|
7432
|
+
}
|
|
7433
|
+
var baseText = {
|
|
7434
|
+
type: "text",
|
|
7435
|
+
rotation: 0,
|
|
7436
|
+
opacity: 1,
|
|
7437
|
+
visible: true,
|
|
7438
|
+
content: [{ text: "Text" }],
|
|
7439
|
+
style: {
|
|
7440
|
+
font: { family: "Inter", source: "google" },
|
|
7441
|
+
// regular: the editor's Bold is a toggle, and a new text that was already bold confused it
|
|
7442
|
+
weight: 400,
|
|
7443
|
+
size: 48,
|
|
7444
|
+
lineHeight: 1.2,
|
|
7445
|
+
letterSpacing: 0,
|
|
7446
|
+
color: { color: "#111827" },
|
|
7447
|
+
align: "left"
|
|
7448
|
+
},
|
|
7449
|
+
sizing: "fixed",
|
|
7450
|
+
valign: "top"
|
|
7451
|
+
};
|
|
7452
|
+
function defaultLayer(type, id, name, canvas) {
|
|
7453
|
+
const centred = (width, height) => ({
|
|
7454
|
+
x: Math.round((canvas.width - width) / 2),
|
|
7455
|
+
y: Math.round((canvas.height - height) / 2),
|
|
7456
|
+
width,
|
|
7457
|
+
height
|
|
7458
|
+
});
|
|
7459
|
+
const common = { id, name, rotation: 0, opacity: 1, visible: true };
|
|
7460
|
+
switch (type) {
|
|
7461
|
+
case "text":
|
|
7462
|
+
return {
|
|
7463
|
+
...baseText,
|
|
7464
|
+
...common,
|
|
7465
|
+
...centred(Math.min(canvas.width - 80, 600), 80)
|
|
7466
|
+
};
|
|
7467
|
+
case "image":
|
|
7468
|
+
return {
|
|
7469
|
+
...common,
|
|
7470
|
+
type: "image",
|
|
7471
|
+
...centred(Math.min(canvas.width, 400), Math.min(canvas.height, 300)),
|
|
7472
|
+
// not chosen yet (`imageIsEmpty`), and the file library first: that is where an upload goes
|
|
7473
|
+
source: { asset: "" },
|
|
7474
|
+
fit: "cover"
|
|
7475
|
+
};
|
|
7476
|
+
case "shape":
|
|
7477
|
+
return {
|
|
7478
|
+
...common,
|
|
7479
|
+
type: "shape",
|
|
7480
|
+
...centred(240, 160),
|
|
7481
|
+
shape: "rectangle",
|
|
7482
|
+
fill: { color: "#e5e7eb" }
|
|
7483
|
+
};
|
|
7484
|
+
case "qr":
|
|
7485
|
+
return {
|
|
7486
|
+
...common,
|
|
7487
|
+
type: "qr",
|
|
7488
|
+
...centred(240, 240),
|
|
7489
|
+
value: { text: "https://example.test" },
|
|
7490
|
+
ecc: "M",
|
|
7491
|
+
color: { color: "#000000" },
|
|
7492
|
+
background: { color: "#ffffff" }
|
|
7493
|
+
};
|
|
7494
|
+
case "barcode":
|
|
7495
|
+
return {
|
|
7496
|
+
...common,
|
|
7497
|
+
type: "barcode",
|
|
7498
|
+
...centred(400, 140),
|
|
7499
|
+
value: { text: "4006381333931" },
|
|
7500
|
+
symbology: "ean13",
|
|
7501
|
+
showText: true
|
|
7502
|
+
};
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
|
|
7506
|
+
// packages/engine/src/lib/design/emit.ts
|
|
7507
|
+
var colourPattern2 = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
7508
|
+
var tokenPattern2 = /^[a-z][a-z0-9-]{0,31}$/;
|
|
7509
|
+
function emitDesign(design2, engine, size) {
|
|
7510
|
+
const lines = ['<div class="ff-design">'];
|
|
7511
|
+
const map = {};
|
|
7512
|
+
const rules = [];
|
|
7513
|
+
for (const layer of design2.layers) {
|
|
7514
|
+
if (!layer.visible) continue;
|
|
7515
|
+
const markup = layerHtml(layer, engine);
|
|
7516
|
+
lines.push(
|
|
7517
|
+
` ${layer.showWhen?.path ? ifBlock(engine, layer.showWhen.path, markup) : markup}`
|
|
7518
|
+
);
|
|
7519
|
+
map[layer.id] = { start: lines.length, end: lines.length };
|
|
7520
|
+
rules.push(...layerCss(layer));
|
|
7521
|
+
}
|
|
7522
|
+
lines.push("</div>");
|
|
7523
|
+
const html = `${lines.join("\n")}
|
|
7524
|
+
`;
|
|
7525
|
+
const fonts = googleFontsUrl(
|
|
7526
|
+
design2.layers.filter(
|
|
7527
|
+
(l) => l.visible && l.type === "text" && "family" in l.style.font && l.style.font.source === "google"
|
|
7528
|
+
).map((l) => ({
|
|
7529
|
+
family: l.style.font.family,
|
|
7530
|
+
weight: l.style.weight,
|
|
7531
|
+
italic: l.style.italic === true
|
|
7532
|
+
}))
|
|
7533
|
+
);
|
|
7534
|
+
const head = [
|
|
7535
|
+
...fonts ? [`@import url("${fonts}");`] : [],
|
|
7536
|
+
`.ff-design { ${decl({
|
|
7537
|
+
position: "relative",
|
|
7538
|
+
width: px(size.width),
|
|
7539
|
+
height: px(size.height),
|
|
7540
|
+
overflow: "hidden",
|
|
7541
|
+
background: design2.background ? paint(design2.background) : void 0
|
|
7542
|
+
})} }`,
|
|
7543
|
+
".ff-layer { position: absolute; box-sizing: border-box; margin: 0; }"
|
|
7544
|
+
];
|
|
7545
|
+
const css2 = `${[...head, ...rules].join("\n")}
|
|
7546
|
+
`;
|
|
7547
|
+
return { html, css: css2, map, output: designOutputHash(html, css2) };
|
|
7548
|
+
}
|
|
7549
|
+
function open(layer, tag, extra = "") {
|
|
7550
|
+
return `<${tag} class="ff-layer l-${layer.id}" data-ff-layer="${layer.id}"${extra}`;
|
|
7551
|
+
}
|
|
7552
|
+
function layerHtml(layer, engine) {
|
|
7553
|
+
switch (layer.type) {
|
|
7554
|
+
case "text": {
|
|
7555
|
+
const fit = layer.sizing === "shrink" ? ` data-ff-fit="shrink" data-ff-fit-min="${num(layer.minFontSize ?? Math.max(8, Math.round(layer.style.size / 3)))}"` : "";
|
|
7556
|
+
return `${open(layer, "div", fit)}><p class="ff-text">${textHtml(layer.content, engine)}</p></div>`;
|
|
7557
|
+
}
|
|
7558
|
+
case "image":
|
|
7559
|
+
if (imageIsEmpty(layer.source)) return `${open(layer, "div")}></div>`;
|
|
7560
|
+
return `${open(layer, "img", ` src="${imageSource(layer.source, engine)}" alt=""`)}>`;
|
|
7561
|
+
case "shape":
|
|
7562
|
+
return `${open(layer, "div")}></div>`;
|
|
7563
|
+
case "qr":
|
|
7564
|
+
return `${open(
|
|
7565
|
+
layer,
|
|
7566
|
+
"img",
|
|
7567
|
+
` src="${helperCall(engine, "qrcode", binding(layer.value, engine), {
|
|
7568
|
+
ecc: stringLiteral(engine, layer.ecc),
|
|
7569
|
+
margin: "0",
|
|
7570
|
+
color: stringLiteral(engine, literalColour(layer.color)),
|
|
7571
|
+
background: stringLiteral(
|
|
7572
|
+
engine,
|
|
7573
|
+
layer.background ? literalColour(layer.background) : "#ffffff00"
|
|
7574
|
+
)
|
|
7575
|
+
})}" alt=""`
|
|
7576
|
+
)}>`;
|
|
7577
|
+
case "barcode":
|
|
7578
|
+
return `${open(
|
|
7579
|
+
layer,
|
|
7580
|
+
"img",
|
|
7581
|
+
` src="${helperCall(engine, "barcode", binding(layer.value, engine), {
|
|
7582
|
+
type: stringLiteral(engine, layer.symbology),
|
|
7583
|
+
text: layer.showText ? "true" : "false"
|
|
7584
|
+
})}" alt=""`
|
|
7585
|
+
)}>`;
|
|
7586
|
+
}
|
|
7587
|
+
}
|
|
7588
|
+
function textHtml(items, engine) {
|
|
7589
|
+
return items.map((item) => {
|
|
7590
|
+
if ("break" in item) return "<br>";
|
|
7591
|
+
if ("text" in item)
|
|
7592
|
+
return encodeTemplateText(item.text.replace(/[\r\n]+/g, " "));
|
|
7593
|
+
return valueHtml(item.path, item.format, engine);
|
|
7594
|
+
}).join("");
|
|
7595
|
+
}
|
|
7596
|
+
function valueHtml(path, format, engine) {
|
|
7597
|
+
if (!format) return `{{ ${path} }}`;
|
|
7598
|
+
switch (format.helper) {
|
|
7599
|
+
case "upper":
|
|
7600
|
+
case "lower":
|
|
7601
|
+
case "title":
|
|
7602
|
+
case "money":
|
|
7603
|
+
return filterCall(engine, path, format.helper);
|
|
7604
|
+
case "number":
|
|
7605
|
+
return filterCall(engine, path, "number", [String(format.decimals)]);
|
|
7606
|
+
case "date":
|
|
7607
|
+
return filterCall(engine, path, "date", [
|
|
7608
|
+
stringLiteral(engine, format.pattern)
|
|
7609
|
+
]);
|
|
7610
|
+
case "truncate":
|
|
7611
|
+
return filterCall(engine, path, "truncate", [String(format.length)]);
|
|
7612
|
+
case "default":
|
|
7613
|
+
return filterCall(engine, path, "default", [
|
|
7614
|
+
stringLiteral(engine, format.value)
|
|
7615
|
+
]);
|
|
7616
|
+
}
|
|
7617
|
+
}
|
|
7618
|
+
function binding(value, engine) {
|
|
7619
|
+
return "path" in value ? value.path : stringLiteral(engine, value.text);
|
|
7620
|
+
}
|
|
7621
|
+
function imageSource(source, engine) {
|
|
7622
|
+
if ("url" in source) return escapeAttribute(source.url);
|
|
7623
|
+
if ("asset" in source)
|
|
7624
|
+
return helperCall(engine, "asset", stringLiteral(engine, source.asset));
|
|
7625
|
+
if ("path" in source) return `{{ ${source.path} }}`;
|
|
7626
|
+
return `{{ brand.logo.${source.brandLogo} }}`;
|
|
7627
|
+
}
|
|
7628
|
+
function layerCss(layer) {
|
|
7629
|
+
const text2 = layer.type === "text" ? layer : null;
|
|
7630
|
+
const box = {
|
|
7631
|
+
left: px(layer.x),
|
|
7632
|
+
top: px(layer.y),
|
|
7633
|
+
width: px(layer.width),
|
|
7634
|
+
height: text2?.sizing === "grow" ? void 0 : px(layer.height),
|
|
7635
|
+
opacity: layer.opacity < 1 ? num(layer.opacity) : void 0,
|
|
7636
|
+
transform: layer.rotation ? `rotate(${num(layer.rotation)}deg)` : void 0,
|
|
7637
|
+
"border-radius": layer.type === "shape" && layer.shape === "ellipse" ? "50%" : layer.radius ? px(layer.radius) : void 0,
|
|
7638
|
+
border: layer.stroke && layer.stroke.width > 0 ? `${px(layer.stroke.width)} solid ${paint(layer.stroke.paint)}` : void 0,
|
|
7639
|
+
"box-shadow": layer.shadow && !text2 ? shadow(layer.shadow) : void 0
|
|
7640
|
+
};
|
|
7641
|
+
if (text2) {
|
|
7642
|
+
Object.assign(box, {
|
|
7643
|
+
display: "flex",
|
|
7644
|
+
"flex-direction": "column",
|
|
7645
|
+
"justify-content": text2.valign === "middle" ? "center" : text2.valign === "bottom" ? "flex-end" : "flex-start",
|
|
7646
|
+
overflow: text2.sizing === "grow" ? void 0 : "hidden",
|
|
7647
|
+
padding: text2.padding ? px(text2.padding) : void 0,
|
|
7648
|
+
background: text2.fill ? paint(text2.fill) : void 0
|
|
7649
|
+
});
|
|
7650
|
+
const s = text2.style;
|
|
7651
|
+
const clamp = text2.maxLines ? {
|
|
7652
|
+
display: "-webkit-box",
|
|
7653
|
+
"-webkit-box-orient": "vertical",
|
|
7654
|
+
"-webkit-line-clamp": String(text2.maxLines),
|
|
7655
|
+
overflow: "hidden"
|
|
7656
|
+
} : {};
|
|
7657
|
+
const inner = decl({
|
|
7658
|
+
margin: "0",
|
|
7659
|
+
width: "100%",
|
|
7660
|
+
"font-family": fontFamily(s.font),
|
|
7661
|
+
"font-weight": String(servedWeight(s.font, s.weight)),
|
|
7662
|
+
"font-size": px(s.size),
|
|
7663
|
+
"line-height": num(s.lineHeight),
|
|
7664
|
+
"letter-spacing": s.letterSpacing ? px(s.letterSpacing) : void 0,
|
|
7665
|
+
color: paint(s.color),
|
|
7666
|
+
"text-align": s.align,
|
|
7667
|
+
"font-style": s.italic ? "italic" : void 0,
|
|
7668
|
+
"text-transform": s.uppercase ? "uppercase" : void 0,
|
|
7669
|
+
"text-decoration": [s.underline && "underline", s.strike && "line-through"].filter(Boolean).join(" ") || void 0,
|
|
7670
|
+
"text-shadow": text2.shadow ? shadow(text2.shadow) : void 0,
|
|
7671
|
+
"overflow-wrap": "break-word",
|
|
7672
|
+
...clamp
|
|
7673
|
+
});
|
|
7674
|
+
return [
|
|
7675
|
+
`.l-${layer.id} { ${decl(box)} }`,
|
|
7676
|
+
`.l-${layer.id} .ff-text { ${inner} }`
|
|
7677
|
+
];
|
|
7678
|
+
}
|
|
7679
|
+
if (layer.type === "image")
|
|
7680
|
+
Object.assign(box, {
|
|
7681
|
+
display: "block",
|
|
7682
|
+
"object-fit": layer.fit,
|
|
7683
|
+
"object-position": layer.focus ? `${num(layer.focus.x)}% ${num(layer.focus.y)}%` : void 0
|
|
7684
|
+
});
|
|
7685
|
+
if (layer.type === "qr" || layer.type === "barcode")
|
|
7686
|
+
Object.assign(box, { display: "block", "object-fit": "contain" });
|
|
7687
|
+
if (layer.type === "shape")
|
|
7688
|
+
box["background"] = layer.fill ? paint(layer.fill) : void 0;
|
|
7689
|
+
return [`.l-${layer.id} { ${decl(box)} }`];
|
|
7690
|
+
}
|
|
7691
|
+
function decl(properties) {
|
|
7692
|
+
return Object.entries(properties).filter(([, value]) => value !== void 0).map(([name, value]) => `${name}: ${value};`).join(" ");
|
|
7693
|
+
}
|
|
7694
|
+
function num(n) {
|
|
7695
|
+
return String(Math.round(n * 100) / 100);
|
|
7696
|
+
}
|
|
7697
|
+
function px(n) {
|
|
7698
|
+
const value = num(n);
|
|
7699
|
+
return value === "0" ? "0" : `${value}px`;
|
|
7700
|
+
}
|
|
7701
|
+
function safeColour(value) {
|
|
7702
|
+
return colourPattern2.test(value) ? value : "#000000";
|
|
7703
|
+
}
|
|
7704
|
+
function paint(p) {
|
|
7705
|
+
if ("brand" in p)
|
|
7706
|
+
return tokenPattern2.test(p.brand) ? `var(--brand-color-${p.brand}, ${safeColour(p.fallback)})` : safeColour(p.fallback);
|
|
7707
|
+
return safeColour(p.color);
|
|
7708
|
+
}
|
|
7709
|
+
function literalColour(p) {
|
|
7710
|
+
return safeColour("brand" in p ? p.fallback : p.color);
|
|
7711
|
+
}
|
|
7712
|
+
function shadow(s) {
|
|
7713
|
+
return `${px(s.x)} ${px(s.y)} ${px(s.blur)} ${safeColour(s.color)}`;
|
|
7714
|
+
}
|
|
7715
|
+
function servedWeight(font, weight) {
|
|
7716
|
+
const google = "family" in font && font.source === "google" ? googleFont(font.family) : void 0;
|
|
7717
|
+
return google ? nearestWeight(google, weight) : weight;
|
|
7718
|
+
}
|
|
7719
|
+
function fontFamily(font) {
|
|
7720
|
+
if ("brand" in font) return `var(--brand-font-${font.brand}, sans-serif)`;
|
|
7721
|
+
const family = font.family.replace(/["'\\<>\n\r;{}]/g, "");
|
|
7722
|
+
const generic = font.source === "google" ? genericFamily(googleFont(family)) : "sans-serif";
|
|
7723
|
+
return `"${family}", ${generic}`;
|
|
7724
|
+
}
|
|
7725
|
+
function escapeAttribute(value) {
|
|
7726
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/\{/g, "%7B").replace(/\}/g, "%7D");
|
|
7727
|
+
}
|
|
7728
|
+
|
|
7729
|
+
// packages/engine/src/lib/design/starters.ts
|
|
7730
|
+
var style = (overrides) => ({
|
|
7731
|
+
font: { family: "Inter", source: "google" },
|
|
7732
|
+
weight: 600,
|
|
7733
|
+
size: 40,
|
|
7734
|
+
lineHeight: 1.2,
|
|
7735
|
+
letterSpacing: 0,
|
|
7736
|
+
color: { color: "#111827" },
|
|
7737
|
+
align: "left",
|
|
7738
|
+
...overrides
|
|
7739
|
+
});
|
|
7740
|
+
var text = (layer) => ({
|
|
7741
|
+
type: "text",
|
|
7742
|
+
rotation: 0,
|
|
7743
|
+
opacity: 1,
|
|
7744
|
+
visible: true,
|
|
7745
|
+
sizing: "fixed",
|
|
7746
|
+
valign: "top",
|
|
7747
|
+
...layer
|
|
7748
|
+
});
|
|
7749
|
+
var design = (background, layers) => ({
|
|
7750
|
+
version: DESIGN_VERSION,
|
|
7751
|
+
output: "",
|
|
7752
|
+
background,
|
|
7753
|
+
layers
|
|
7754
|
+
});
|
|
7755
|
+
var designStarters = [
|
|
7756
|
+
{
|
|
7757
|
+
id: "blank",
|
|
7758
|
+
title: "Blank",
|
|
7759
|
+
size: { width: 1200, height: 630 },
|
|
7760
|
+
design: design({ color: "#ffffff" }, []),
|
|
7761
|
+
sampleData: {}
|
|
7762
|
+
},
|
|
7763
|
+
{
|
|
7764
|
+
id: "article",
|
|
7765
|
+
title: "Article card",
|
|
7766
|
+
size: { width: 1200, height: 630 },
|
|
7767
|
+
design: design({ brand: "primary", fallback: "#0f172a" }, [
|
|
7768
|
+
text({
|
|
7769
|
+
id: "site",
|
|
7770
|
+
name: "Site",
|
|
7771
|
+
x: 80,
|
|
7772
|
+
y: 64,
|
|
7773
|
+
width: 1040,
|
|
7774
|
+
height: 40,
|
|
7775
|
+
content: [{ path: "site.name" }],
|
|
7776
|
+
style: style({
|
|
7777
|
+
size: 26,
|
|
7778
|
+
weight: 600,
|
|
7779
|
+
letterSpacing: 2,
|
|
7780
|
+
uppercase: true,
|
|
7781
|
+
color: { color: "#cbd5e1" }
|
|
7782
|
+
})
|
|
7783
|
+
}),
|
|
7784
|
+
text({
|
|
7785
|
+
id: "title",
|
|
7786
|
+
name: "Title",
|
|
7787
|
+
x: 80,
|
|
7788
|
+
y: 130,
|
|
7789
|
+
width: 1040,
|
|
7790
|
+
height: 320,
|
|
7791
|
+
content: [{ path: "post.title" }],
|
|
7792
|
+
sizing: "shrink",
|
|
7793
|
+
minFontSize: 40,
|
|
7794
|
+
valign: "middle",
|
|
7795
|
+
style: style({
|
|
7796
|
+
size: 76,
|
|
7797
|
+
weight: 800,
|
|
7798
|
+
lineHeight: 1.1,
|
|
7799
|
+
color: { color: "#ffffff" }
|
|
7800
|
+
})
|
|
7801
|
+
}),
|
|
7802
|
+
{
|
|
7803
|
+
id: "rule",
|
|
7804
|
+
name: "Rule",
|
|
7805
|
+
type: "shape",
|
|
7806
|
+
shape: "rectangle",
|
|
7807
|
+
x: 80,
|
|
7808
|
+
y: 478,
|
|
7809
|
+
width: 120,
|
|
7810
|
+
height: 6,
|
|
7811
|
+
rotation: 0,
|
|
7812
|
+
opacity: 1,
|
|
7813
|
+
visible: true,
|
|
7814
|
+
fill: { brand: "accent", fallback: "#38bdf8" }
|
|
7815
|
+
},
|
|
7816
|
+
text({
|
|
7817
|
+
id: "byline",
|
|
7818
|
+
name: "Byline",
|
|
7819
|
+
x: 80,
|
|
7820
|
+
y: 508,
|
|
7821
|
+
width: 1040,
|
|
7822
|
+
height: 50,
|
|
7823
|
+
content: [{ text: "By " }, { path: "post.author" }],
|
|
7824
|
+
showWhen: { path: "post.author" },
|
|
7825
|
+
style: style({ size: 30, weight: 500, color: { color: "#cbd5e1" } })
|
|
7826
|
+
})
|
|
7827
|
+
]),
|
|
7828
|
+
sampleData: {
|
|
7829
|
+
site: { name: "Fennlor Studio" },
|
|
7830
|
+
post: {
|
|
7831
|
+
title: "How we cut our invoice run from an hour to four minutes",
|
|
7832
|
+
author: "Max Mustermann"
|
|
7833
|
+
}
|
|
7834
|
+
}
|
|
7835
|
+
},
|
|
7836
|
+
{
|
|
7837
|
+
id: "quote",
|
|
7838
|
+
title: "Quote card",
|
|
7839
|
+
size: { width: 1080, height: 1080 },
|
|
7840
|
+
design: design({ color: "#f5efe6" }, [
|
|
7841
|
+
text({
|
|
7842
|
+
id: "mark",
|
|
7843
|
+
name: "Quotation mark",
|
|
7844
|
+
x: 96,
|
|
7845
|
+
y: 60,
|
|
7846
|
+
width: 240,
|
|
7847
|
+
height: 220,
|
|
7848
|
+
content: [{ text: "\u201C" }],
|
|
7849
|
+
style: style({
|
|
7850
|
+
font: { family: "Playfair Display", source: "google" },
|
|
7851
|
+
size: 220,
|
|
7852
|
+
weight: 700,
|
|
7853
|
+
lineHeight: 1,
|
|
7854
|
+
color: { color: "#d97706" }
|
|
7855
|
+
})
|
|
7856
|
+
}),
|
|
7857
|
+
text({
|
|
7858
|
+
id: "quote",
|
|
7859
|
+
name: "Quote",
|
|
7860
|
+
x: 96,
|
|
7861
|
+
y: 260,
|
|
7862
|
+
width: 888,
|
|
7863
|
+
height: 520,
|
|
7864
|
+
content: [{ path: "review.quote" }],
|
|
7865
|
+
sizing: "shrink",
|
|
7866
|
+
minFontSize: 32,
|
|
7867
|
+
valign: "middle",
|
|
7868
|
+
style: style({
|
|
7869
|
+
font: { family: "Playfair Display", source: "google" },
|
|
7870
|
+
size: 60,
|
|
7871
|
+
weight: 600,
|
|
7872
|
+
lineHeight: 1.22,
|
|
7873
|
+
color: { color: "#1c1917" }
|
|
7874
|
+
})
|
|
7875
|
+
}),
|
|
7876
|
+
text({
|
|
7877
|
+
id: "author",
|
|
7878
|
+
name: "Author",
|
|
7879
|
+
x: 96,
|
|
7880
|
+
y: 836,
|
|
7881
|
+
width: 888,
|
|
7882
|
+
height: 50,
|
|
7883
|
+
content: [{ path: "review.author" }],
|
|
7884
|
+
style: style({ size: 36, weight: 600, color: { color: "#1c1917" } })
|
|
7885
|
+
}),
|
|
7886
|
+
text({
|
|
7887
|
+
id: "role",
|
|
7888
|
+
name: "Role",
|
|
7889
|
+
x: 96,
|
|
7890
|
+
y: 896,
|
|
7891
|
+
width: 888,
|
|
7892
|
+
height: 44,
|
|
7893
|
+
content: [{ path: "review.role" }],
|
|
7894
|
+
showWhen: { path: "review.role" },
|
|
7895
|
+
style: style({ size: 28, weight: 400, color: { color: "#78716c" } })
|
|
7896
|
+
})
|
|
7897
|
+
]),
|
|
7898
|
+
sampleData: {
|
|
7899
|
+
review: {
|
|
7900
|
+
quote: "Delivery took two days, and the one shade that arrived dented was replaced before we had finished the call.",
|
|
7901
|
+
author: "Erika Mustermann",
|
|
7902
|
+
role: "Office manager, Olvarest GmbH"
|
|
7903
|
+
}
|
|
7904
|
+
}
|
|
7905
|
+
},
|
|
7906
|
+
{
|
|
7907
|
+
id: "event",
|
|
7908
|
+
title: "Event story",
|
|
7909
|
+
size: { width: 1080, height: 1920 },
|
|
7910
|
+
design: design({ color: "#111827" }, [
|
|
7911
|
+
text({
|
|
7912
|
+
id: "label",
|
|
7913
|
+
name: "Label",
|
|
7914
|
+
x: 96,
|
|
7915
|
+
y: 200,
|
|
7916
|
+
width: 888,
|
|
7917
|
+
height: 50,
|
|
7918
|
+
content: [{ text: "Event" }],
|
|
7919
|
+
style: style({
|
|
7920
|
+
size: 36,
|
|
7921
|
+
weight: 700,
|
|
7922
|
+
letterSpacing: 6,
|
|
7923
|
+
uppercase: true,
|
|
7924
|
+
color: { color: "#fbbf24" }
|
|
7925
|
+
})
|
|
7926
|
+
}),
|
|
7927
|
+
text({
|
|
7928
|
+
id: "title",
|
|
7929
|
+
name: "Title",
|
|
7930
|
+
x: 96,
|
|
7931
|
+
y: 290,
|
|
7932
|
+
width: 888,
|
|
7933
|
+
height: 620,
|
|
7934
|
+
content: [{ path: "event.title" }],
|
|
7935
|
+
sizing: "shrink",
|
|
7936
|
+
minFontSize: 56,
|
|
7937
|
+
valign: "bottom",
|
|
7938
|
+
style: style({
|
|
7939
|
+
size: 112,
|
|
7940
|
+
weight: 800,
|
|
7941
|
+
lineHeight: 1.05,
|
|
7942
|
+
color: { color: "#ffffff" }
|
|
7943
|
+
})
|
|
7944
|
+
}),
|
|
7945
|
+
text({
|
|
7946
|
+
id: "date",
|
|
7947
|
+
name: "Date",
|
|
7948
|
+
x: 96,
|
|
7949
|
+
y: 980,
|
|
7950
|
+
width: 888,
|
|
7951
|
+
height: 70,
|
|
7952
|
+
content: [
|
|
7953
|
+
{
|
|
7954
|
+
path: "event.date",
|
|
7955
|
+
format: { helper: "date", pattern: "d MMMM yyyy" }
|
|
7956
|
+
}
|
|
7957
|
+
],
|
|
7958
|
+
style: style({ size: 52, weight: 600, color: { color: "#e5e7eb" } })
|
|
7959
|
+
}),
|
|
7960
|
+
text({
|
|
7961
|
+
id: "place",
|
|
7962
|
+
name: "Place",
|
|
7963
|
+
x: 96,
|
|
7964
|
+
y: 1060,
|
|
7965
|
+
width: 888,
|
|
7966
|
+
height: 60,
|
|
7967
|
+
content: [{ path: "event.place" }],
|
|
7968
|
+
style: style({ size: 38, weight: 400, color: { color: "#9ca3af" } })
|
|
7969
|
+
}),
|
|
7970
|
+
{
|
|
7971
|
+
id: "qr",
|
|
7972
|
+
name: "QR code",
|
|
7973
|
+
type: "qr",
|
|
7974
|
+
x: 96,
|
|
7975
|
+
y: 1440,
|
|
7976
|
+
width: 300,
|
|
7977
|
+
height: 300,
|
|
7978
|
+
rotation: 0,
|
|
7979
|
+
opacity: 1,
|
|
7980
|
+
visible: true,
|
|
7981
|
+
radius: 16,
|
|
7982
|
+
value: { path: "event.url" },
|
|
7983
|
+
ecc: "M",
|
|
7984
|
+
color: { color: "#111827" },
|
|
7985
|
+
background: { color: "#ffffff" }
|
|
7986
|
+
},
|
|
7987
|
+
text({
|
|
7988
|
+
id: "scan",
|
|
7989
|
+
name: "Scan note",
|
|
7990
|
+
x: 440,
|
|
7991
|
+
y: 1560,
|
|
7992
|
+
width: 544,
|
|
7993
|
+
height: 60,
|
|
7994
|
+
content: [{ text: "Scan for your ticket" }],
|
|
7995
|
+
style: style({ size: 40, weight: 600, color: { color: "#ffffff" } })
|
|
7996
|
+
})
|
|
7997
|
+
]),
|
|
7998
|
+
sampleData: {
|
|
7999
|
+
event: {
|
|
8000
|
+
title: "Formfeed meetup: documents as code",
|
|
8001
|
+
date: "2026-11-12",
|
|
8002
|
+
place: "Musterstra\xDFe 1 \xB7 12345 Musterstadt",
|
|
8003
|
+
url: "https://fennlor.test/events/meetup"
|
|
8004
|
+
}
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
];
|
|
8008
|
+
|
|
8009
|
+
// packages/engine/src/lib/office/errors.ts
|
|
8010
|
+
var OfficeError = class extends Error {
|
|
8011
|
+
constructor(code, message, details = {}) {
|
|
8012
|
+
super(message);
|
|
8013
|
+
this.code = code;
|
|
8014
|
+
this.details = details;
|
|
8015
|
+
this.name = "OfficeError";
|
|
8016
|
+
}
|
|
8017
|
+
};
|
|
8018
|
+
var officeLimits = {
|
|
8019
|
+
/** Entries in one container. */
|
|
8020
|
+
maxEntries: 2e3,
|
|
8021
|
+
/** Sum of the uncompressed sizes the central directory declares. */
|
|
8022
|
+
maxDeclaredBytes: 200 * 1024 * 1024,
|
|
8023
|
+
/** One part we inflate; enforced while inflating, because the declared size can lie. */
|
|
8024
|
+
maxPartBytes: 20 * 1024 * 1024,
|
|
8025
|
+
/** A conversion upload or a template file. */
|
|
8026
|
+
maxFileBytes: 20 * 1024 * 1024
|
|
8027
|
+
};
|
|
8028
|
+
|
|
8029
|
+
// node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
|
|
8030
|
+
var u8 = Uint8Array;
|
|
8031
|
+
var u16 = Uint16Array;
|
|
8032
|
+
var i32 = Int32Array;
|
|
8033
|
+
var fleb = new u8([
|
|
8034
|
+
0,
|
|
8035
|
+
0,
|
|
8036
|
+
0,
|
|
8037
|
+
0,
|
|
8038
|
+
0,
|
|
8039
|
+
0,
|
|
8040
|
+
0,
|
|
8041
|
+
0,
|
|
8042
|
+
1,
|
|
8043
|
+
1,
|
|
8044
|
+
1,
|
|
8045
|
+
1,
|
|
8046
|
+
2,
|
|
8047
|
+
2,
|
|
8048
|
+
2,
|
|
8049
|
+
2,
|
|
8050
|
+
3,
|
|
8051
|
+
3,
|
|
8052
|
+
3,
|
|
8053
|
+
3,
|
|
8054
|
+
4,
|
|
8055
|
+
4,
|
|
8056
|
+
4,
|
|
8057
|
+
4,
|
|
8058
|
+
5,
|
|
8059
|
+
5,
|
|
8060
|
+
5,
|
|
8061
|
+
5,
|
|
8062
|
+
0,
|
|
8063
|
+
/* unused */
|
|
8064
|
+
0,
|
|
8065
|
+
0,
|
|
8066
|
+
/* impossible */
|
|
8067
|
+
0
|
|
8068
|
+
]);
|
|
8069
|
+
var fdeb = new u8([
|
|
8070
|
+
0,
|
|
8071
|
+
0,
|
|
8072
|
+
0,
|
|
8073
|
+
0,
|
|
8074
|
+
1,
|
|
8075
|
+
1,
|
|
8076
|
+
2,
|
|
8077
|
+
2,
|
|
8078
|
+
3,
|
|
8079
|
+
3,
|
|
8080
|
+
4,
|
|
8081
|
+
4,
|
|
8082
|
+
5,
|
|
8083
|
+
5,
|
|
8084
|
+
6,
|
|
8085
|
+
6,
|
|
8086
|
+
7,
|
|
8087
|
+
7,
|
|
8088
|
+
8,
|
|
8089
|
+
8,
|
|
8090
|
+
9,
|
|
8091
|
+
9,
|
|
8092
|
+
10,
|
|
8093
|
+
10,
|
|
8094
|
+
11,
|
|
8095
|
+
11,
|
|
8096
|
+
12,
|
|
8097
|
+
12,
|
|
8098
|
+
13,
|
|
8099
|
+
13,
|
|
8100
|
+
/* unused */
|
|
8101
|
+
0,
|
|
8102
|
+
0
|
|
8103
|
+
]);
|
|
8104
|
+
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
8105
|
+
var freb = function(eb, start) {
|
|
8106
|
+
var b = new u16(31);
|
|
8107
|
+
for (var i = 0; i < 31; ++i) {
|
|
8108
|
+
b[i] = start += 1 << eb[i - 1];
|
|
8109
|
+
}
|
|
8110
|
+
var r = new i32(b[30]);
|
|
8111
|
+
for (var i = 1; i < 30; ++i) {
|
|
8112
|
+
for (var j = b[i]; j < b[i + 1]; ++j) {
|
|
8113
|
+
r[j] = j - b[i] << 5 | i;
|
|
8114
|
+
}
|
|
8115
|
+
}
|
|
8116
|
+
return { b, r };
|
|
8117
|
+
};
|
|
8118
|
+
var _a = freb(fleb, 2);
|
|
8119
|
+
var fl = _a.b;
|
|
8120
|
+
var revfl = _a.r;
|
|
8121
|
+
fl[28] = 258, revfl[258] = 28;
|
|
8122
|
+
var _b = freb(fdeb, 0);
|
|
8123
|
+
var fd = _b.b;
|
|
8124
|
+
var revfd = _b.r;
|
|
8125
|
+
var rev = new u16(32768);
|
|
8126
|
+
for (i = 0; i < 32768; ++i) {
|
|
6042
8127
|
x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
6043
8128
|
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
6044
8129
|
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
@@ -7223,8 +9308,8 @@ function assertWellFormed(tokens, part) {
|
|
|
7223
9308
|
break;
|
|
7224
9309
|
}
|
|
7225
9310
|
case "close": {
|
|
7226
|
-
const
|
|
7227
|
-
if (
|
|
9311
|
+
const open2 = stack.pop();
|
|
9312
|
+
if (open2 !== token2.name) throw invalid2(`</${token2.name}> closes <${open2 ?? "nothing"}>`, part);
|
|
7228
9313
|
break;
|
|
7229
9314
|
}
|
|
7230
9315
|
case "text":
|
|
@@ -7240,12 +9325,12 @@ function assertWellFormed(tokens, part) {
|
|
|
7240
9325
|
}
|
|
7241
9326
|
var FORBIDDEN = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
7242
9327
|
var ENTITY = /&(?:#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/y;
|
|
7243
|
-
function checkText(
|
|
7244
|
-
if (FORBIDDEN.test(
|
|
7245
|
-
if (
|
|
7246
|
-
for (let i =
|
|
9328
|
+
function checkText(text2, part) {
|
|
9329
|
+
if (FORBIDDEN.test(text2)) throw invalid2("a character XML does not allow", part);
|
|
9330
|
+
if (text2.includes("<")) throw invalid2("unescaped < in text", part);
|
|
9331
|
+
for (let i = text2.indexOf("&"); i !== -1; i = text2.indexOf("&", i + 1)) {
|
|
7247
9332
|
ENTITY.lastIndex = i;
|
|
7248
|
-
if (!ENTITY.test(
|
|
9333
|
+
if (!ENTITY.test(text2)) throw invalid2(`invalid entity reference near "${text2.slice(i, i + 12)}"`, part);
|
|
7249
9334
|
}
|
|
7250
9335
|
}
|
|
7251
9336
|
function checkAttributes(raw, part) {
|
|
@@ -7260,7 +9345,7 @@ function checkAttributes(raw, part) {
|
|
|
7260
9345
|
});
|
|
7261
9346
|
if (rest.trim() !== "") throw invalid2(`malformed attributes in ${raw.slice(0, 40)}`, part);
|
|
7262
9347
|
}
|
|
7263
|
-
function
|
|
9348
|
+
function decodeText3(raw) {
|
|
7264
9349
|
return raw.replace(/&(#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/g, (whole, name) => {
|
|
7265
9350
|
switch (name) {
|
|
7266
9351
|
case "amp":
|
|
@@ -7280,13 +9365,13 @@ function decodeText2(raw) {
|
|
|
7280
9365
|
}
|
|
7281
9366
|
});
|
|
7282
9367
|
}
|
|
7283
|
-
function escapeText2(
|
|
7284
|
-
return
|
|
9368
|
+
function escapeText2(text2) {
|
|
9369
|
+
return text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
7285
9370
|
}
|
|
7286
9371
|
var FORBIDDEN_GLOBAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
|
|
7287
|
-
function stripForbidden(
|
|
9372
|
+
function stripForbidden(text2) {
|
|
7288
9373
|
let removed = 0;
|
|
7289
|
-
const out =
|
|
9374
|
+
const out = text2.replace(FORBIDDEN_GLOBAL, () => {
|
|
7290
9375
|
removed++;
|
|
7291
9376
|
return "";
|
|
7292
9377
|
});
|
|
@@ -7537,13 +9622,13 @@ function placeDrawings(xml2, pattern, markupFor) {
|
|
|
7537
9622
|
pattern.lastIndex = 0;
|
|
7538
9623
|
if (pattern.test(t.raw)) {
|
|
7539
9624
|
const segments = t.raw.split(pattern);
|
|
7540
|
-
let
|
|
9625
|
+
let text2 = segments[0];
|
|
7541
9626
|
for (let s = 1; s < segments.length; s += 2) {
|
|
7542
9627
|
const index = Number(segments[s]);
|
|
7543
|
-
|
|
9628
|
+
text2 += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
|
|
7544
9629
|
}
|
|
7545
9630
|
out[textOpen] = withPreserve(out[textOpen]);
|
|
7546
|
-
out.push(
|
|
9631
|
+
out.push(text2);
|
|
7547
9632
|
continue;
|
|
7548
9633
|
}
|
|
7549
9634
|
}
|
|
@@ -7566,13 +9651,13 @@ function withRelationships(rels2, relationships) {
|
|
|
7566
9651
|
return rels2.slice(0, end) + added + rels2.slice(end);
|
|
7567
9652
|
}
|
|
7568
9653
|
function relationshipIds(rels2) {
|
|
7569
|
-
return new Set([...(rels2 ?? "").matchAll(/\sId="([^"]+)"/g)].map((m) =>
|
|
9654
|
+
return new Set([...(rels2 ?? "").matchAll(/\sId="([^"]+)"/g)].map((m) => decodeText3(m[1])));
|
|
7570
9655
|
}
|
|
7571
9656
|
function withImageTypes(contentTypes, extensions) {
|
|
7572
9657
|
let out = contentTypes;
|
|
7573
9658
|
for (const ext of extensions) {
|
|
7574
9659
|
if (new RegExp(`<Default\\b[^>]*Extension="${ext}"`, "i").test(out)) continue;
|
|
7575
|
-
out = out.replace(/<Types\b[^>]*>/, (
|
|
9660
|
+
out = out.replace(/<Types\b[^>]*>/, (open2) => `${open2}<Default Extension="${ext}" ContentType="image/${ext}"/>`);
|
|
7576
9661
|
}
|
|
7577
9662
|
return out;
|
|
7578
9663
|
}
|
|
@@ -7594,7 +9679,7 @@ var THEME_REFERENCES = /^\+(mj|mn)-/;
|
|
|
7594
9679
|
function documentFonts(archive) {
|
|
7595
9680
|
const fonts = /* @__PURE__ */ new Map();
|
|
7596
9681
|
const add2 = (name, embedded = false) => {
|
|
7597
|
-
const clean =
|
|
9682
|
+
const clean = decodeText3(name).trim();
|
|
7598
9683
|
if (!clean || THEME_REFERENCES.test(clean)) return;
|
|
7599
9684
|
const key = clean.toLowerCase();
|
|
7600
9685
|
const known = fonts.get(key);
|
|
@@ -7775,7 +9860,7 @@ function toTemplateSource(xml2, flavour, nonce, part) {
|
|
|
7775
9860
|
for (const token2 of tokens) {
|
|
7776
9861
|
if (token2.kind === "text" && inText) {
|
|
7777
9862
|
flush();
|
|
7778
|
-
source +=
|
|
9863
|
+
source += decodeText3(token2.raw);
|
|
7779
9864
|
continue;
|
|
7780
9865
|
}
|
|
7781
9866
|
pending.raw += token2.raw;
|
|
@@ -7798,11 +9883,11 @@ function fromTemplateOutput(output, template, part) {
|
|
|
7798
9883
|
let last = 0;
|
|
7799
9884
|
let inText = false;
|
|
7800
9885
|
const emit = (segment) => {
|
|
7801
|
-
const { text, removed } = stripForbidden(segment);
|
|
9886
|
+
const { text: text2, removed } = stripForbidden(segment);
|
|
7802
9887
|
removedCharacters += removed;
|
|
7803
|
-
if (!inText &&
|
|
7804
|
-
throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${
|
|
7805
|
-
xml2 += escapeText2(
|
|
9888
|
+
if (!inText && text2.trim() !== "")
|
|
9889
|
+
throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text2.trim().slice(0, 40)}"`);
|
|
9890
|
+
xml2 += escapeText2(text2);
|
|
7806
9891
|
};
|
|
7807
9892
|
for (const m of output.matchAll(placeholder)) {
|
|
7808
9893
|
emit(output.slice(last, m.index));
|
|
@@ -7844,15 +9929,15 @@ function layoutText(xml2, flavour, part) {
|
|
|
7844
9929
|
out.push(token2.raw);
|
|
7845
9930
|
continue;
|
|
7846
9931
|
}
|
|
7847
|
-
const
|
|
9932
|
+
const text2 = token2.raw.replace(/\r\n?/g, "\n");
|
|
7848
9933
|
if (flavour === "wordprocessing") {
|
|
7849
|
-
const pieces =
|
|
7850
|
-
if (pieces.length > 1 || /^\s|\s$/.test(
|
|
9934
|
+
const pieces = text2.split(/(\n|\t)/);
|
|
9935
|
+
if (pieces.length > 1 || /^\s|\s$/.test(text2)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
|
|
7851
9936
|
out.push(
|
|
7852
9937
|
pieces.map((p) => p === "\n" ? '</w:t><w:br/><w:t xml:space="preserve">' : p === " " ? '</w:t><w:tab/><w:t xml:space="preserve">' : p).join("")
|
|
7853
9938
|
);
|
|
7854
9939
|
} else {
|
|
7855
|
-
out.push(
|
|
9940
|
+
out.push(text2.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
|
|
7856
9941
|
}
|
|
7857
9942
|
}
|
|
7858
9943
|
return out.join("");
|
|
@@ -7885,17 +9970,17 @@ var AUTOCORRECTED = [
|
|
|
7885
9970
|
[/\u200B|\u200C|\u200D|\uFEFF/g, ""]
|
|
7886
9971
|
];
|
|
7887
9972
|
function normaliseTags(template, part) {
|
|
7888
|
-
const { text, origin, placeholders } = stripPlaceholders(template);
|
|
9973
|
+
const { text: text2, origin, placeholders } = stripPlaceholders(template);
|
|
7889
9974
|
const sourceRange = (from, to) => [
|
|
7890
9975
|
origin[from] ?? template.source.length,
|
|
7891
9976
|
(origin[to - 1] ?? template.source.length - 1) + 1
|
|
7892
9977
|
];
|
|
7893
9978
|
const diagnostics = [];
|
|
7894
9979
|
const edits = [];
|
|
7895
|
-
for (const tag of findTags(
|
|
9980
|
+
for (const tag of findTags(text2)) {
|
|
7896
9981
|
const inside = placeholders.filter((p) => p.at > tag.start && p.at < tag.end);
|
|
7897
9982
|
const paragraph2 = paragraphOf(template, placeholders, tag.start);
|
|
7898
|
-
const raw =
|
|
9983
|
+
const raw = text2.slice(tag.start, tag.end);
|
|
7899
9984
|
const unsafe = inside.filter((p) => {
|
|
7900
9985
|
const piece = template.markup[p.index];
|
|
7901
9986
|
return !piece || piece.paragraphBoundary || piece.containerBoundary;
|
|
@@ -7949,8 +10034,8 @@ function normaliseTags(template, part) {
|
|
|
7949
10034
|
return { template: { ...template, source: applyEdits(template.source, edits) }, diagnostics };
|
|
7950
10035
|
}
|
|
7951
10036
|
var ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
|
|
7952
|
-
function decodeEntities(
|
|
7953
|
-
return
|
|
10037
|
+
function decodeEntities(text2) {
|
|
10038
|
+
return text2.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
|
|
7954
10039
|
if (name[0] !== "#") return ENTITIES[name.toLowerCase()] ?? whole;
|
|
7955
10040
|
const code = name[1] === "x" || name[1] === "X" ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);
|
|
7956
10041
|
return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
|
|
@@ -7958,28 +10043,28 @@ function decodeEntities(text) {
|
|
|
7958
10043
|
}
|
|
7959
10044
|
function stripPlaceholders(template) {
|
|
7960
10045
|
const pattern = placeholderPattern(template.nonce);
|
|
7961
|
-
let
|
|
10046
|
+
let text2 = "";
|
|
7962
10047
|
const origin = [];
|
|
7963
10048
|
const placeholders = [];
|
|
7964
10049
|
let last = 0;
|
|
7965
10050
|
for (const m of template.source.matchAll(pattern)) {
|
|
7966
10051
|
for (let i = last; i < (m.index ?? 0); i++) {
|
|
7967
|
-
|
|
10052
|
+
text2 += template.source[i];
|
|
7968
10053
|
origin.push(i);
|
|
7969
10054
|
}
|
|
7970
|
-
placeholders.push({ index: Number(m[1]), at:
|
|
10055
|
+
placeholders.push({ index: Number(m[1]), at: text2.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
|
|
7971
10056
|
last = (m.index ?? 0) + m[0].length;
|
|
7972
10057
|
}
|
|
7973
10058
|
for (let i = last; i < template.source.length; i++) {
|
|
7974
|
-
|
|
10059
|
+
text2 += template.source[i];
|
|
7975
10060
|
origin.push(i);
|
|
7976
10061
|
}
|
|
7977
|
-
return { text, origin, placeholders };
|
|
10062
|
+
return { text: text2, origin, placeholders };
|
|
7978
10063
|
}
|
|
7979
10064
|
function listTags(template, part) {
|
|
7980
|
-
const { text, placeholders } = stripPlaceholders(template);
|
|
7981
|
-
return findTags(
|
|
7982
|
-
const raw =
|
|
10065
|
+
const { text: text2, placeholders } = stripPlaceholders(template);
|
|
10066
|
+
return findTags(text2).map((tag) => {
|
|
10067
|
+
const raw = text2.slice(tag.start, tag.end);
|
|
7983
10068
|
const kind = raw.startsWith("{#") ? "comment" : raw.startsWith("{%") || /^\{\{~?\s*[#/^]/.test(raw) || /^\{\{~?\s*else\b/.test(raw) ? "block" : "output";
|
|
7984
10069
|
return { part, paragraph: paragraphOf(template, placeholders, tag.start), text: raw.replace(/\s+/g, " ").trim(), kind };
|
|
7985
10070
|
});
|
|
@@ -8000,15 +10085,15 @@ function undoAutocorrect(tag) {
|
|
|
8000
10085
|
for (const [pattern, replacement] of AUTOCORRECTED) out = out.replace(pattern, replacement);
|
|
8001
10086
|
return out;
|
|
8002
10087
|
}
|
|
8003
|
-
function findTags(
|
|
10088
|
+
function findTags(text2) {
|
|
8004
10089
|
const tags = [];
|
|
8005
|
-
for (let i = 0; i <
|
|
8006
|
-
const opener = OPENERS.find((o) =>
|
|
10090
|
+
for (let i = 0; i < text2.length - 1; i++) {
|
|
10091
|
+
const opener = OPENERS.find((o) => text2.startsWith(o, i));
|
|
8007
10092
|
if (!opener) continue;
|
|
8008
10093
|
const closer = CLOSERS[opener];
|
|
8009
|
-
const end =
|
|
10094
|
+
const end = text2.indexOf(closer, i + opener.length);
|
|
8010
10095
|
if (end === -1) continue;
|
|
8011
|
-
const stop = end + closer.length + (opener === "{{" &&
|
|
10096
|
+
const stop = end + closer.length + (opener === "{{" && text2[end + closer.length] === "}" ? 1 : 0);
|
|
8012
10097
|
tags.push({ start: i, end: stop });
|
|
8013
10098
|
i = stop - 1;
|
|
8014
10099
|
}
|
|
@@ -8038,8 +10123,8 @@ var CONTENT = /* @__PURE__ */ new Set(["w:drawing", "w:pict", "w:object", "w:fld
|
|
|
8038
10123
|
var JINJA_OPEN = /^(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
|
|
8039
10124
|
var JINJA_CLOSE = /^end(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
|
|
8040
10125
|
var JINJA_MIDDLE = /^(else|elif|elsif|when)\b/;
|
|
8041
|
-
function blockRole(
|
|
8042
|
-
const t =
|
|
10126
|
+
function blockRole(text2) {
|
|
10127
|
+
const t = text2.trim();
|
|
8043
10128
|
let m = /^\{%-?\s*([\s\S]*?)\s*-?%\}$/.exec(t);
|
|
8044
10129
|
if (m && !t.slice(2, -2).includes("%}")) {
|
|
8045
10130
|
const body = m[1] ?? "";
|
|
@@ -8071,14 +10156,14 @@ function dropTaggedFallbacks(xml2, part) {
|
|
|
8071
10156
|
if (t.kind === "open" && t.name === "mc:Fallback" && !t.selfClosing) {
|
|
8072
10157
|
let depth = 1;
|
|
8073
10158
|
let j = i + 1;
|
|
8074
|
-
let
|
|
10159
|
+
let text2 = "";
|
|
8075
10160
|
for (; j < tokens.length && depth > 0; j++) {
|
|
8076
10161
|
const u = tokens[j];
|
|
8077
10162
|
if (u.kind === "open" && u.name === "mc:Fallback" && !u.selfClosing) depth++;
|
|
8078
10163
|
else if (u.kind === "close" && u.name === "mc:Fallback") depth--;
|
|
8079
|
-
else if (u.kind === "text")
|
|
10164
|
+
else if (u.kind === "text") text2 += decodeText3(u.raw);
|
|
8080
10165
|
}
|
|
8081
|
-
if (/\{\{|\{%|\{#/.test(
|
|
10166
|
+
if (/\{\{|\{%|\{#/.test(text2)) {
|
|
8082
10167
|
i = j - 1;
|
|
8083
10168
|
continue;
|
|
8084
10169
|
}
|
|
@@ -8119,25 +10204,25 @@ function applyStructure(xml2, part) {
|
|
|
8119
10204
|
if (s.role === "open")
|
|
8120
10205
|
stack.push({ keyword: s.keyword, parent, paragraph: s.info.number, tag: s.tag, after: unitOf(span).end });
|
|
8121
10206
|
else if (s.role === "close" || s.role === "middle") {
|
|
8122
|
-
const
|
|
8123
|
-
if (
|
|
8124
|
-
const body = tokens.slice(
|
|
10207
|
+
const open2 = s.role === "close" ? stack.pop() : stack.at(-1);
|
|
10208
|
+
if (open2 && s.role === "close" && LOOPS.has(open2.keyword)) {
|
|
10209
|
+
const body = tokens.slice(open2.after, unitOf(span).start);
|
|
8125
10210
|
const reference = body.find((t) => t.kind === "open" && REFERENCES.has(t.name));
|
|
8126
10211
|
if (reference && reference.kind === "open")
|
|
8127
10212
|
diagnostics.push({
|
|
8128
10213
|
severity: "error",
|
|
8129
10214
|
code: "office-reference-in-loop",
|
|
8130
|
-
message: `The loop from paragraph ${
|
|
10215
|
+
message: `The loop from paragraph ${open2.paragraph} repeats a ${reference.name.includes("comment") ? "comment" : "footnote or endnote"}, which cannot be copied. Move it out of the loop.`,
|
|
8131
10216
|
part,
|
|
8132
|
-
paragraph:
|
|
8133
|
-
text: short2(
|
|
10217
|
+
paragraph: open2.paragraph,
|
|
10218
|
+
text: short2(open2.tag)
|
|
8134
10219
|
});
|
|
8135
10220
|
}
|
|
8136
|
-
if (
|
|
10221
|
+
if (open2 && open2.parent !== parent)
|
|
8137
10222
|
diagnostics.push({
|
|
8138
10223
|
severity: "error",
|
|
8139
10224
|
code: "office-block-unbalanced",
|
|
8140
|
-
message: `${short2(
|
|
10225
|
+
message: `${short2(open2.tag)} (paragraph ${open2.paragraph}) and ${short2(s.tag)} are in different places: one in a table cell or text box, the other outside it. Put both in the same place.`,
|
|
8141
10226
|
part,
|
|
8142
10227
|
paragraph: s.info.number,
|
|
8143
10228
|
text: short2(s.tag)
|
|
@@ -8151,9 +10236,9 @@ function applyStructure(xml2, part) {
|
|
|
8151
10236
|
for (const [span, s] of structural) {
|
|
8152
10237
|
if (rowOf.has(span)) continue;
|
|
8153
10238
|
const keep = s.info.sectionProperties;
|
|
8154
|
-
let
|
|
8155
|
-
if (keep)
|
|
8156
|
-
replacements.push({ start: span.start, end: span.end, text });
|
|
10239
|
+
let text2 = element(s.tag);
|
|
10240
|
+
if (keep) text2 = s.role === "open" ? `<w:p>${keep}</w:p>${text2}` : `${text2}<w:p>${keep}</w:p>`;
|
|
10241
|
+
replacements.push({ start: span.start, end: span.end, text: text2 });
|
|
8157
10242
|
const holder = span.parent && BODIES.has(span.parent.name) ? span.parent : null;
|
|
8158
10243
|
if (holder && !keep) cellsEmptied.set(holder, (cellsEmptied.get(holder) ?? 0) + 1);
|
|
8159
10244
|
}
|
|
@@ -8207,7 +10292,7 @@ function collectParagraphs(tokens, root) {
|
|
|
8207
10292
|
const out = [];
|
|
8208
10293
|
const walk3 = (span) => {
|
|
8209
10294
|
if (PARAGRAPH.has(span.name)) {
|
|
8210
|
-
let
|
|
10295
|
+
let text2 = "";
|
|
8211
10296
|
let hasContent = false;
|
|
8212
10297
|
let sectionProperties = null;
|
|
8213
10298
|
let inText = false;
|
|
@@ -8218,10 +10303,10 @@ function collectParagraphs(tokens, root) {
|
|
|
8218
10303
|
else if (CONTENT.has(t.name)) hasContent = true;
|
|
8219
10304
|
else if (t.name === "w:sectPr") sectionProperties = "";
|
|
8220
10305
|
} else if (t.kind === "close" && TEXT.has(t.name)) inText = false;
|
|
8221
|
-
else if (t.kind === "text" && inText)
|
|
10306
|
+
else if (t.kind === "text" && inText) text2 += decodeText3(t.raw);
|
|
8222
10307
|
}
|
|
8223
10308
|
if (sectionProperties !== null) sectionProperties = propertiesOf(tokens, span);
|
|
8224
|
-
out.push({ span, number: out.length + 1, text, hasContent, sectionProperties });
|
|
10309
|
+
out.push({ span, number: out.length + 1, text: text2, hasContent, sectionProperties });
|
|
8225
10310
|
}
|
|
8226
10311
|
for (const child2 of span.children) walk3(child2);
|
|
8227
10312
|
};
|
|
@@ -8329,7 +10414,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8329
10414
|
for (const container of containers) {
|
|
8330
10415
|
const indexes = [];
|
|
8331
10416
|
const edits = [];
|
|
8332
|
-
let
|
|
10417
|
+
let text2 = "";
|
|
8333
10418
|
let inText = false;
|
|
8334
10419
|
for (let i = container.start + 1; i < container.end; i++) {
|
|
8335
10420
|
const t = tokens[i];
|
|
@@ -8338,7 +10423,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8338
10423
|
else if (t.kind === "text" && inText) {
|
|
8339
10424
|
pattern.lastIndex = 0;
|
|
8340
10425
|
if (!pattern.test(t.raw)) {
|
|
8341
|
-
|
|
10426
|
+
text2 += decodeText3(t.raw);
|
|
8342
10427
|
continue;
|
|
8343
10428
|
}
|
|
8344
10429
|
const segments = t.raw.split(pattern);
|
|
@@ -8347,7 +10432,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8347
10432
|
if (s % 2 === 1) indexes.push(Number(segments[s]));
|
|
8348
10433
|
else kept += segments[s];
|
|
8349
10434
|
}
|
|
8350
|
-
|
|
10435
|
+
text2 += decodeText3(kept);
|
|
8351
10436
|
edits.push({ at: i, raw: kept });
|
|
8352
10437
|
}
|
|
8353
10438
|
}
|
|
@@ -8366,7 +10451,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8366
10451
|
}
|
|
8367
10452
|
const autoFit = tokens.slice(container.start, container.end).some((t) => t.kind === "open" && t.name === "a:spAutoFit");
|
|
8368
10453
|
const placed = layoutPictures(pictures, box, autoFit).map((b, i) => pictureMarkup(pictures[i], b)).join("");
|
|
8369
|
-
const onlyPictures = container.name === "p:sp" &&
|
|
10454
|
+
const onlyPictures = container.name === "p:sp" && text2.trim() === "";
|
|
8370
10455
|
if (onlyPictures) replacements.push({ start: container.start, end: container.end, text: placed });
|
|
8371
10456
|
else {
|
|
8372
10457
|
for (const edit of edits) replacements.push({ start: edit.at, end: edit.at, text: edit.raw });
|
|
@@ -8639,8 +10724,8 @@ function starterTags(engine) {
|
|
|
8639
10724
|
codeFit: engine === "liquid" ? "{{ offer.url | qrcode }}" : "{{ qrcode(offer.url) }}"
|
|
8640
10725
|
};
|
|
8641
10726
|
}
|
|
8642
|
-
var paragraph = (
|
|
8643
|
-
var cell = (
|
|
10727
|
+
var paragraph = (text2, style2, bold = false) => `<w:p>${style2 ? `<w:pPr><w:pStyle w:val="${style2}"/></w:pPr>` : ""}<w:r>${bold ? "<w:rPr><w:b/></w:rPr>" : ""}<w:t xml:space="preserve">${escapeText2(text2)}</w:t></w:r></w:p>`;
|
|
10728
|
+
var cell = (text2, width, bold = false) => `<w:tc><w:tcPr><w:tcW w:w="${width}" w:type="dxa"/></w:tcPr>${paragraph(text2, void 0, bold)}</w:tc>`;
|
|
8644
10729
|
var row = (cells) => `<w:tr>${cells}</w:tr>`;
|
|
8645
10730
|
function starterDocument(engine) {
|
|
8646
10731
|
const t = starterTags(engine);
|
|
@@ -8683,7 +10768,7 @@ function starterDocument(engine) {
|
|
|
8683
10768
|
["word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="${W}"><w:body>${body}</w:body></w:document>`]
|
|
8684
10769
|
];
|
|
8685
10770
|
return {
|
|
8686
|
-
bytes: writeZip(files.map(([name,
|
|
10771
|
+
bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
|
|
8687
10772
|
sampleData: starterSampleData()
|
|
8688
10773
|
};
|
|
8689
10774
|
}
|
|
@@ -8711,15 +10796,15 @@ var CM = 36e4;
|
|
|
8711
10796
|
var FONT = "Carlito";
|
|
8712
10797
|
var ACCENT = "0F766E";
|
|
8713
10798
|
var emptyTree = '<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr/>';
|
|
8714
|
-
var run = (
|
|
8715
|
-
var para = (
|
|
10799
|
+
var run = (text2, size, options = {}) => `<a:r><a:rPr lang="en-GB" sz="${size * 100}"${options.bold ? ' b="1"' : ""} dirty="0">${options.color ? `<a:solidFill><a:srgbClr val="${options.color}"/></a:solidFill>` : ""}<a:latin typeface="${FONT}"/></a:rPr><a:t>${escapeText2(text2)}</a:t></a:r>`;
|
|
10800
|
+
var para = (text2, size, options = {}) => `<a:p>${run(text2, size, options)}</a:p>`;
|
|
8716
10801
|
function textBox(id, name, x, y, cx, cy, paragraphs) {
|
|
8717
10802
|
return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="${name}"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:txBody><a:bodyPr wrap="square" rtlCol="0"><a:noAutofit/></a:bodyPr><a:lstStyle/>${paragraphs}</p:txBody></p:sp>`;
|
|
8718
10803
|
}
|
|
8719
|
-
function cell2(
|
|
10804
|
+
function cell2(text2, header) {
|
|
8720
10805
|
const fill = header ? `<a:solidFill><a:srgbClr val="${ACCENT}"/></a:solidFill>` : "<a:noFill/>";
|
|
8721
10806
|
const line = `<a:lnB w="6350"><a:solidFill><a:srgbClr val="BFBFBF"/></a:solidFill></a:lnB>`;
|
|
8722
|
-
return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(
|
|
10807
|
+
return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(text2, 14, header ? { bold: true, color: "FFFFFF" } : {})}</a:txBody><a:tcPr marL="91440" marR="91440" marT="45720" marB="45720">${line}${fill}</a:tcPr></a:tc>`;
|
|
8723
10808
|
}
|
|
8724
10809
|
var tableRow = (a, b, header = false) => `<a:tr h="${Math.round(0.9 * CM)}">${cell2(a, header)}${cell2(b, header)}</a:tr>`;
|
|
8725
10810
|
function table2(id, x, y, rows) {
|
|
@@ -8809,12 +10894,14 @@ function starterPresentation(engine) {
|
|
|
8809
10894
|
];
|
|
8810
10895
|
const encoder2 = new TextEncoder();
|
|
8811
10896
|
return {
|
|
8812
|
-
bytes: writeZip(files.map(([name,
|
|
10897
|
+
bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
|
|
8813
10898
|
sampleData: starterSampleData()
|
|
8814
10899
|
};
|
|
8815
10900
|
}
|
|
8816
10901
|
export {
|
|
8817
10902
|
DEFAULT_CHROME_PADDING,
|
|
10903
|
+
DESIGN_LIMITS,
|
|
10904
|
+
DESIGN_VERSION,
|
|
8818
10905
|
DefaultHelperRegistry,
|
|
8819
10906
|
EPC_MAX_BYTES,
|
|
8820
10907
|
EngineSyntaxError,
|
|
@@ -8828,32 +10915,52 @@ export {
|
|
|
8828
10915
|
analyzeOffice,
|
|
8829
10916
|
annotateSourcePositions,
|
|
8830
10917
|
apitemplateRegions,
|
|
10918
|
+
applyRegionEdit,
|
|
8831
10919
|
assembleDocument,
|
|
8832
10920
|
brandCss,
|
|
8833
10921
|
builtinHelpers,
|
|
10922
|
+
canvasDocument,
|
|
8834
10923
|
chartInitScript,
|
|
8835
10924
|
chartMarkup,
|
|
8836
10925
|
chartPayload,
|
|
10926
|
+
chipValues,
|
|
8837
10927
|
convertScss,
|
|
8838
10928
|
createHelperRegistry,
|
|
10929
|
+
dataPathPattern,
|
|
8839
10930
|
defaultHelpers,
|
|
10931
|
+
defaultLayer,
|
|
8840
10932
|
defaultLimits,
|
|
8841
10933
|
defaultOutput,
|
|
8842
10934
|
defaultSettings,
|
|
8843
10935
|
depthGuard,
|
|
10936
|
+
designBytes,
|
|
10937
|
+
designOutputHash,
|
|
10938
|
+
designStarters,
|
|
8844
10939
|
diffSchemas,
|
|
10940
|
+
editAttribute,
|
|
10941
|
+
editableRegion,
|
|
10942
|
+
editableRegionAt,
|
|
10943
|
+
emitDesign,
|
|
8845
10944
|
emptyBrand,
|
|
10945
|
+
emptyDesign,
|
|
10946
|
+
encodeTemplateText,
|
|
8846
10947
|
enforceLimits,
|
|
8847
10948
|
engineIds,
|
|
8848
10949
|
engines,
|
|
8849
10950
|
epcPayload,
|
|
8850
10951
|
escapeHtml,
|
|
10952
|
+
findRegionAgain,
|
|
8851
10953
|
flowDocument,
|
|
8852
10954
|
fontFaceCss,
|
|
10955
|
+
genericFamily,
|
|
8853
10956
|
getEngine,
|
|
10957
|
+
googleFont,
|
|
10958
|
+
googleFonts,
|
|
10959
|
+
googleFontsUrl,
|
|
8854
10960
|
handlebarsEngine,
|
|
8855
10961
|
helperDocs,
|
|
8856
10962
|
ibanChecksum,
|
|
10963
|
+
imageIsEmpty,
|
|
8857
10964
|
imageMarkup,
|
|
8858
10965
|
imageOutput,
|
|
8859
10966
|
importApitemplate,
|
|
@@ -8870,6 +10977,7 @@ export {
|
|
|
8870
10977
|
jinja2Engine,
|
|
8871
10978
|
jinjaTags,
|
|
8872
10979
|
jsreportTemplates,
|
|
10980
|
+
layerIdPattern,
|
|
8873
10981
|
layeredPartials,
|
|
8874
10982
|
liquidBuiltinFilters,
|
|
8875
10983
|
liquidEngine,
|
|
@@ -8878,6 +10986,8 @@ export {
|
|
|
8878
10986
|
mergeSettings,
|
|
8879
10987
|
mergeSnippetCss,
|
|
8880
10988
|
mergeSnippetI18n,
|
|
10989
|
+
nearestWeight,
|
|
10990
|
+
nextLayerId,
|
|
8881
10991
|
normaliseIban,
|
|
8882
10992
|
normalisePartialName,
|
|
8883
10993
|
nunjucksBuiltinFilters,
|
|
@@ -8890,14 +11000,18 @@ export {
|
|
|
8890
11000
|
pageSize,
|
|
8891
11001
|
pagedDocument,
|
|
8892
11002
|
paperOf,
|
|
11003
|
+
parseDesign,
|
|
8893
11004
|
parseSourceAttribute,
|
|
8894
11005
|
partialInclude,
|
|
8895
11006
|
printReset,
|
|
8896
11007
|
qrSvg,
|
|
8897
11008
|
readJsreportExport,
|
|
11009
|
+
regionChips,
|
|
11010
|
+
regionItems,
|
|
8898
11011
|
renderOffice,
|
|
8899
11012
|
renderVersion,
|
|
8900
11013
|
resolvePath,
|
|
11014
|
+
resolveTranslation,
|
|
8901
11015
|
scanBlocks,
|
|
8902
11016
|
schemaPaths,
|
|
8903
11017
|
slugFromName,
|
|
@@ -8912,6 +11026,9 @@ export {
|
|
|
8912
11026
|
svgDataUri,
|
|
8913
11027
|
toDate,
|
|
8914
11028
|
toNumber,
|
|
11029
|
+
translationItems,
|
|
11030
|
+
translationTarget,
|
|
11031
|
+
translationText,
|
|
8915
11032
|
validateEpc,
|
|
8916
11033
|
zoomGestureScript
|
|
8917
11034
|
};
|