@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.cjs
CHANGED
|
@@ -31,6 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DEFAULT_CHROME_PADDING: () => DEFAULT_CHROME_PADDING,
|
|
34
|
+
DESIGN_LIMITS: () => DESIGN_LIMITS,
|
|
35
|
+
DESIGN_VERSION: () => DESIGN_VERSION,
|
|
34
36
|
DefaultHelperRegistry: () => DefaultHelperRegistry,
|
|
35
37
|
EPC_MAX_BYTES: () => EPC_MAX_BYTES,
|
|
36
38
|
EngineSyntaxError: () => EngineSyntaxError,
|
|
@@ -44,32 +46,52 @@ __export(index_exports, {
|
|
|
44
46
|
analyzeOffice: () => analyzeOffice,
|
|
45
47
|
annotateSourcePositions: () => annotateSourcePositions,
|
|
46
48
|
apitemplateRegions: () => apitemplateRegions,
|
|
49
|
+
applyRegionEdit: () => applyRegionEdit,
|
|
47
50
|
assembleDocument: () => assembleDocument,
|
|
48
51
|
brandCss: () => brandCss,
|
|
49
52
|
builtinHelpers: () => builtinHelpers,
|
|
53
|
+
canvasDocument: () => canvasDocument,
|
|
50
54
|
chartInitScript: () => chartInitScript,
|
|
51
55
|
chartMarkup: () => chartMarkup,
|
|
52
56
|
chartPayload: () => chartPayload,
|
|
57
|
+
chipValues: () => chipValues,
|
|
53
58
|
convertScss: () => convertScss,
|
|
54
59
|
createHelperRegistry: () => createHelperRegistry,
|
|
60
|
+
dataPathPattern: () => dataPathPattern,
|
|
55
61
|
defaultHelpers: () => defaultHelpers,
|
|
62
|
+
defaultLayer: () => defaultLayer,
|
|
56
63
|
defaultLimits: () => defaultLimits,
|
|
57
64
|
defaultOutput: () => defaultOutput,
|
|
58
65
|
defaultSettings: () => defaultSettings,
|
|
59
66
|
depthGuard: () => depthGuard,
|
|
67
|
+
designBytes: () => designBytes,
|
|
68
|
+
designOutputHash: () => designOutputHash,
|
|
69
|
+
designStarters: () => designStarters,
|
|
60
70
|
diffSchemas: () => diffSchemas,
|
|
71
|
+
editAttribute: () => editAttribute,
|
|
72
|
+
editableRegion: () => editableRegion,
|
|
73
|
+
editableRegionAt: () => editableRegionAt,
|
|
74
|
+
emitDesign: () => emitDesign,
|
|
61
75
|
emptyBrand: () => emptyBrand,
|
|
76
|
+
emptyDesign: () => emptyDesign,
|
|
77
|
+
encodeTemplateText: () => encodeTemplateText,
|
|
62
78
|
enforceLimits: () => enforceLimits,
|
|
63
79
|
engineIds: () => engineIds,
|
|
64
80
|
engines: () => engines,
|
|
65
81
|
epcPayload: () => epcPayload,
|
|
66
82
|
escapeHtml: () => escapeHtml,
|
|
83
|
+
findRegionAgain: () => findRegionAgain,
|
|
67
84
|
flowDocument: () => flowDocument,
|
|
68
85
|
fontFaceCss: () => fontFaceCss,
|
|
86
|
+
genericFamily: () => genericFamily,
|
|
69
87
|
getEngine: () => getEngine,
|
|
88
|
+
googleFont: () => googleFont,
|
|
89
|
+
googleFonts: () => googleFonts,
|
|
90
|
+
googleFontsUrl: () => googleFontsUrl,
|
|
70
91
|
handlebarsEngine: () => handlebarsEngine,
|
|
71
92
|
helperDocs: () => helperDocs,
|
|
72
93
|
ibanChecksum: () => ibanChecksum,
|
|
94
|
+
imageIsEmpty: () => imageIsEmpty,
|
|
73
95
|
imageMarkup: () => imageMarkup,
|
|
74
96
|
imageOutput: () => imageOutput,
|
|
75
97
|
importApitemplate: () => importApitemplate,
|
|
@@ -86,6 +108,7 @@ __export(index_exports, {
|
|
|
86
108
|
jinja2Engine: () => jinja2Engine,
|
|
87
109
|
jinjaTags: () => jinjaTags,
|
|
88
110
|
jsreportTemplates: () => jsreportTemplates,
|
|
111
|
+
layerIdPattern: () => layerIdPattern,
|
|
89
112
|
layeredPartials: () => layeredPartials,
|
|
90
113
|
liquidBuiltinFilters: () => liquidBuiltinFilters,
|
|
91
114
|
liquidEngine: () => liquidEngine,
|
|
@@ -94,6 +117,8 @@ __export(index_exports, {
|
|
|
94
117
|
mergeSettings: () => mergeSettings,
|
|
95
118
|
mergeSnippetCss: () => mergeSnippetCss,
|
|
96
119
|
mergeSnippetI18n: () => mergeSnippetI18n,
|
|
120
|
+
nearestWeight: () => nearestWeight,
|
|
121
|
+
nextLayerId: () => nextLayerId,
|
|
97
122
|
normaliseIban: () => normaliseIban,
|
|
98
123
|
normalisePartialName: () => normalisePartialName,
|
|
99
124
|
nunjucksBuiltinFilters: () => nunjucksBuiltinFilters,
|
|
@@ -106,14 +131,18 @@ __export(index_exports, {
|
|
|
106
131
|
pageSize: () => pageSize,
|
|
107
132
|
pagedDocument: () => pagedDocument,
|
|
108
133
|
paperOf: () => paperOf,
|
|
134
|
+
parseDesign: () => parseDesign,
|
|
109
135
|
parseSourceAttribute: () => parseSourceAttribute,
|
|
110
136
|
partialInclude: () => partialInclude,
|
|
111
137
|
printReset: () => printReset,
|
|
112
138
|
qrSvg: () => qrSvg,
|
|
113
139
|
readJsreportExport: () => readJsreportExport,
|
|
140
|
+
regionChips: () => regionChips,
|
|
141
|
+
regionItems: () => regionItems,
|
|
114
142
|
renderOffice: () => renderOffice,
|
|
115
143
|
renderVersion: () => renderVersion,
|
|
116
144
|
resolvePath: () => resolvePath,
|
|
145
|
+
resolveTranslation: () => resolveTranslation,
|
|
117
146
|
scanBlocks: () => scanBlocks,
|
|
118
147
|
schemaPaths: () => schemaPaths,
|
|
119
148
|
slugFromName: () => slugFromName,
|
|
@@ -128,6 +157,9 @@ __export(index_exports, {
|
|
|
128
157
|
svgDataUri: () => svgDataUri,
|
|
129
158
|
toDate: () => toDate,
|
|
130
159
|
toNumber: () => toNumber,
|
|
160
|
+
translationItems: () => translationItems,
|
|
161
|
+
translationTarget: () => translationTarget,
|
|
162
|
+
translationText: () => translationText,
|
|
131
163
|
validateEpc: () => validateEpc,
|
|
132
164
|
zoomGestureScript: () => zoomGestureScript
|
|
133
165
|
});
|
|
@@ -317,11 +349,11 @@ function validateEpc(input) {
|
|
|
317
349
|
const purpose = String(input.purpose ?? "");
|
|
318
350
|
if (purpose.length > 4) problems.push({ field: "purpose", code: "too_long", max: 4, actual: purpose.length });
|
|
319
351
|
const reference = String(input.reference ?? "").trim();
|
|
320
|
-
const
|
|
321
|
-
if (reference &&
|
|
352
|
+
const text2 = String(input.text ?? "").trim();
|
|
353
|
+
if (reference && text2) problems.push({ field: "reference", code: "reference_or_text" });
|
|
322
354
|
if (reference.length > 35)
|
|
323
355
|
problems.push({ field: "reference", code: "too_long", max: 35, actual: reference.length });
|
|
324
|
-
if (
|
|
356
|
+
if (text2.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text2.length });
|
|
325
357
|
const bytes = byteLength(epcPayload(input));
|
|
326
358
|
if (bytes > EPC_MAX_BYTES)
|
|
327
359
|
problems.push({ field: "payload", code: "too_long", max: EPC_MAX_BYTES, actual: bytes });
|
|
@@ -357,6 +389,12 @@ var barcodeTypes = {
|
|
|
357
389
|
datamatrix: "datamatrix",
|
|
358
390
|
pdf417: "pdf417"
|
|
359
391
|
};
|
|
392
|
+
var ownTextLayout = /* @__PURE__ */ new Set(["ean13", "ean8", "upca"]);
|
|
393
|
+
function codeText(helper, value) {
|
|
394
|
+
const text2 = value === void 0 || value === null ? "" : String(value);
|
|
395
|
+
if (text2 === "") throw new Error(`${helper}: nothing to encode, the value is empty or missing from the data`);
|
|
396
|
+
return text2;
|
|
397
|
+
}
|
|
360
398
|
var codeHelpers = [
|
|
361
399
|
{
|
|
362
400
|
name: "qrcode",
|
|
@@ -373,7 +411,7 @@ var codeHelpers = [
|
|
|
373
411
|
},
|
|
374
412
|
fn: (ctx, value, options) => {
|
|
375
413
|
const o = options ?? {};
|
|
376
|
-
const svg = qrSvg(
|
|
414
|
+
const svg = qrSvg(codeText("qrcode", value), o);
|
|
377
415
|
if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "QR code" });
|
|
378
416
|
return svgDataUri(svg);
|
|
379
417
|
}
|
|
@@ -393,16 +431,17 @@ var codeHelpers = [
|
|
|
393
431
|
fn: (ctx, value, options) => {
|
|
394
432
|
const o = options ?? {};
|
|
395
433
|
const bcid = barcodeTypes[String(o.type ?? "code128").toLowerCase()] ?? "code128";
|
|
434
|
+
const text2 = codeText("barcode", value);
|
|
396
435
|
const svg = (0, import_bwip_js.toSVG)({
|
|
397
436
|
bcid,
|
|
398
|
-
text:
|
|
437
|
+
text: text2,
|
|
399
438
|
height: o.height ?? 12,
|
|
400
439
|
...o.width === void 0 ? {} : { width: o.width },
|
|
401
440
|
scale: o.scale ?? 2,
|
|
402
441
|
includetext: o.text !== false,
|
|
403
|
-
textxalign: "center"
|
|
442
|
+
...ownTextLayout.has(bcid) ? {} : { textxalign: "center" }
|
|
404
443
|
});
|
|
405
|
-
if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${
|
|
444
|
+
if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text2}` });
|
|
406
445
|
return svgDataUri(svg);
|
|
407
446
|
}
|
|
408
447
|
},
|
|
@@ -453,6 +492,22 @@ var import_fr = require("n2words/fr");
|
|
|
453
492
|
var import_it = require("n2words/it");
|
|
454
493
|
var import_nl = require("n2words/nl");
|
|
455
494
|
|
|
495
|
+
// packages/engine/src/lib/translation.ts
|
|
496
|
+
function resolveTranslation(dictionaries, locale, key) {
|
|
497
|
+
const lang = locale.split("-")[0] ?? locale;
|
|
498
|
+
for (const candidate of [locale, lang, "en"]) {
|
|
499
|
+
const text2 = dictionaries?.[candidate]?.[key];
|
|
500
|
+
if (typeof text2 === "string") return { text: text2, from: candidate };
|
|
501
|
+
}
|
|
502
|
+
return { text: key, from: null };
|
|
503
|
+
}
|
|
504
|
+
function translationTarget(dictionaries, locale, key) {
|
|
505
|
+
const lang = locale.split("-")[0] ?? locale;
|
|
506
|
+
const { from } = resolveTranslation(dictionaries, locale, key);
|
|
507
|
+
if (from === locale || from === lang) return from;
|
|
508
|
+
return dictionaries?.[locale] ? locale : lang;
|
|
509
|
+
}
|
|
510
|
+
|
|
456
511
|
// packages/engine/src/lib/helpers/office.ts
|
|
457
512
|
var officeUnsupportedHelpers = /* @__PURE__ */ new Set(["chart", "markdown"]);
|
|
458
513
|
function officeUnsupported(name) {
|
|
@@ -497,7 +552,9 @@ var str = (v) => v === void 0 || v === null ? "" : String(v);
|
|
|
497
552
|
var opt = (v, fallback) => v === void 0 || v === null || v === "" ? fallback : v;
|
|
498
553
|
function requireFinite(helper, value) {
|
|
499
554
|
if (typeof value === "number" && !Number.isFinite(value))
|
|
500
|
-
throw new Error(
|
|
555
|
+
throw new Error(
|
|
556
|
+
`${helper}: got ${value}, the result of a calculation with a value that is missing or not a number`
|
|
557
|
+
);
|
|
501
558
|
}
|
|
502
559
|
var wordsByLang = {
|
|
503
560
|
de: import_de.toCardinal,
|
|
@@ -675,13 +732,20 @@ var formatHelpers = [
|
|
|
675
732
|
if (Number.isNaN(n)) return "";
|
|
676
733
|
const named = args.find(isNamedArgs) ?? {};
|
|
677
734
|
const [decimals, method] = args.filter((a) => !isNamedArgs(a));
|
|
678
|
-
const places = Number(
|
|
735
|
+
const places = Number(
|
|
736
|
+
opt(decimals, opt(named["precision"], opt(named["decimals"], 0)))
|
|
737
|
+
);
|
|
679
738
|
const how = String(opt(method, opt(named["method"], "common")));
|
|
680
739
|
const f = 10 ** places;
|
|
681
740
|
const scaled = Number((Math.abs(n) * f).toPrecision(15));
|
|
682
|
-
if (how === "floor")
|
|
683
|
-
|
|
684
|
-
if (how
|
|
741
|
+
if (how === "floor")
|
|
742
|
+
return (n < 0 ? -Math.ceil(scaled) : Math.floor(scaled)) / f;
|
|
743
|
+
if (how === "ceil")
|
|
744
|
+
return (n < 0 ? -Math.floor(scaled) : Math.ceil(scaled)) / f;
|
|
745
|
+
if (how !== "common")
|
|
746
|
+
throw new Error(
|
|
747
|
+
`round: method must be 'common', 'floor' or 'ceil', not '${how}'`
|
|
748
|
+
);
|
|
685
749
|
return Math.round(scaled + Number.EPSILON) / f * Math.sign(n);
|
|
686
750
|
}
|
|
687
751
|
},
|
|
@@ -913,11 +977,9 @@ var formatHelpers = [
|
|
|
913
977
|
category: "text"
|
|
914
978
|
},
|
|
915
979
|
fn: (ctx, key, params) => {
|
|
916
|
-
const
|
|
917
|
-
const lang = ctx.locale.split("-")[0] ?? ctx.locale;
|
|
918
|
-
const text = dictionaries[ctx.locale]?.[str(key)] ?? dictionaries[lang]?.[str(key)] ?? dictionaries["en"]?.[str(key)] ?? str(key);
|
|
980
|
+
const { text: text2 } = resolveTranslation(ctx.i18n, ctx.locale, str(key));
|
|
919
981
|
const p = params ?? {};
|
|
920
|
-
return
|
|
982
|
+
return text2.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
|
|
921
983
|
}
|
|
922
984
|
},
|
|
923
985
|
{
|
|
@@ -1242,8 +1304,8 @@ function imageMarkup(url, options = {}) {
|
|
|
1242
1304
|
options.fit ? `object-fit:${options.fit}` : ""
|
|
1243
1305
|
].filter(Boolean).join(";");
|
|
1244
1306
|
const cls = options.class ? ` class="${escapeHtml(options.class)}"` : "";
|
|
1245
|
-
const
|
|
1246
|
-
return `<img src="${escapeHtml(url)}" alt="${escapeHtml(options.alt ?? "")}"${cls}${
|
|
1307
|
+
const style2 = styles ? ` style="${styles}"` : "";
|
|
1308
|
+
return `<img src="${escapeHtml(url)}" alt="${escapeHtml(options.alt ?? "")}"${cls}${style2}>`;
|
|
1247
1309
|
}
|
|
1248
1310
|
function resolveAsset(base, url) {
|
|
1249
1311
|
if (!base || /^[a-z][a-z0-9+.-]*:/i.test(url)) return url;
|
|
@@ -1531,11 +1593,11 @@ function scanBlocks(source, spec) {
|
|
|
1531
1593
|
while (m = pattern.exec(source)) {
|
|
1532
1594
|
const keyword = m[1] ?? "";
|
|
1533
1595
|
const rest = m[2] ?? "";
|
|
1534
|
-
const
|
|
1596
|
+
const range2 = rangeOf(source, m.index, m.index + m[0].length);
|
|
1535
1597
|
if (inRaw) {
|
|
1536
1598
|
if (spec.closers[keyword] === inRaw) {
|
|
1537
|
-
const
|
|
1538
|
-
if (
|
|
1599
|
+
const open2 = stack.pop();
|
|
1600
|
+
if (open2) open2.block.close = range2;
|
|
1539
1601
|
inRaw = null;
|
|
1540
1602
|
}
|
|
1541
1603
|
continue;
|
|
@@ -1543,7 +1605,7 @@ function scanBlocks(source, spec) {
|
|
|
1543
1605
|
if (isBlockOpener(keyword, rest)) {
|
|
1544
1606
|
const block = {
|
|
1545
1607
|
type: spec.openers[keyword] ?? "other",
|
|
1546
|
-
open:
|
|
1608
|
+
open: range2
|
|
1547
1609
|
};
|
|
1548
1610
|
blocks.push(block);
|
|
1549
1611
|
stack.push({ keyword, block });
|
|
@@ -1557,7 +1619,7 @@ function scanBlocks(source, spec) {
|
|
|
1557
1619
|
code: "unexpected-end-tag",
|
|
1558
1620
|
message: `Unexpected {% ${keyword} %} without an open block`,
|
|
1559
1621
|
args: { keyword },
|
|
1560
|
-
range
|
|
1622
|
+
range: range2
|
|
1561
1623
|
});
|
|
1562
1624
|
} else if (top.keyword !== expected) {
|
|
1563
1625
|
diagnostics.push({
|
|
@@ -1565,16 +1627,16 @@ function scanBlocks(source, spec) {
|
|
|
1565
1627
|
code: "mismatched-end-tag",
|
|
1566
1628
|
message: `Expected {% end${top.keyword} %} to close the {% ${top.keyword} %} at line ${top.block.open.start.line}, found {% ${keyword} %}`,
|
|
1567
1629
|
args: { open: top.keyword, line: top.block.open.start.line, found: keyword },
|
|
1568
|
-
range
|
|
1630
|
+
range: range2
|
|
1569
1631
|
});
|
|
1570
1632
|
const idx = stack.map((s) => s.keyword).lastIndexOf(expected);
|
|
1571
1633
|
if (idx >= 0) {
|
|
1572
1634
|
const closed = stack.splice(idx, 1)[0];
|
|
1573
|
-
if (closed) closed.block.close =
|
|
1635
|
+
if (closed) closed.block.close = range2;
|
|
1574
1636
|
}
|
|
1575
1637
|
} else {
|
|
1576
1638
|
stack.pop();
|
|
1577
|
-
top.block.close =
|
|
1639
|
+
top.block.close = range2;
|
|
1578
1640
|
}
|
|
1579
1641
|
} else if (spec.continuations.includes(keyword) && !stack.length) {
|
|
1580
1642
|
diagnostics.push({
|
|
@@ -1582,17 +1644,17 @@ function scanBlocks(source, spec) {
|
|
|
1582
1644
|
code: "unexpected-tag",
|
|
1583
1645
|
message: `{% ${keyword} %} outside of a block`,
|
|
1584
1646
|
args: { keyword },
|
|
1585
|
-
range
|
|
1647
|
+
range: range2
|
|
1586
1648
|
});
|
|
1587
1649
|
}
|
|
1588
1650
|
}
|
|
1589
|
-
for (const
|
|
1651
|
+
for (const open2 of stack) {
|
|
1590
1652
|
diagnostics.push({
|
|
1591
1653
|
severity: "error",
|
|
1592
1654
|
code: "unclosed-block",
|
|
1593
|
-
message: `{% ${
|
|
1594
|
-
args: { keyword:
|
|
1595
|
-
range:
|
|
1655
|
+
message: `{% ${open2.keyword} %} is never closed; add {% end${open2.keyword} %}`,
|
|
1656
|
+
args: { keyword: open2.keyword },
|
|
1657
|
+
range: open2.block.open
|
|
1596
1658
|
});
|
|
1597
1659
|
}
|
|
1598
1660
|
return { blocks, diagnostics };
|
|
@@ -2024,8 +2086,8 @@ var SPEC = /^(?:([\s\S])?([<>=^]))?([+\- ])?(#)?(0)?(\d+)?([,_])?(?:\.(\d+))?([b
|
|
|
2024
2086
|
function group(digits, separator) {
|
|
2025
2087
|
return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
2026
2088
|
}
|
|
2027
|
-
function pythonExponent(
|
|
2028
|
-
return
|
|
2089
|
+
function pythonExponent(text2) {
|
|
2090
|
+
return text2.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
|
|
2029
2091
|
}
|
|
2030
2092
|
function formatNumber(value, type, precision, alternate) {
|
|
2031
2093
|
if (!Number.isFinite(value)) return Number.isNaN(value) ? "nan" : "inf";
|
|
@@ -2038,20 +2100,20 @@ function formatNumber(value, type, precision, alternate) {
|
|
|
2038
2100
|
return `${(abs * 100).toFixed(precision ?? 6)}%`;
|
|
2039
2101
|
case "e":
|
|
2040
2102
|
case "E": {
|
|
2041
|
-
const
|
|
2042
|
-
return type === "E" ?
|
|
2103
|
+
const text2 = pythonExponent(abs.toExponential(precision ?? 6));
|
|
2104
|
+
return type === "E" ? text2.toUpperCase() : text2;
|
|
2043
2105
|
}
|
|
2044
2106
|
case "g":
|
|
2045
2107
|
case "G": {
|
|
2046
2108
|
const p = precision === 0 ? 1 : precision ?? 6;
|
|
2047
2109
|
const exp = abs === 0 ? 0 : Math.floor(Math.log10(abs));
|
|
2048
|
-
let
|
|
2110
|
+
let text2 = exp < -4 || exp >= p ? pythonExponent(abs.toExponential(p - 1)) : abs.toFixed(Math.max(0, p - 1 - exp));
|
|
2049
2111
|
if (!alternate) {
|
|
2050
|
-
const [mantissa = "", exponent] =
|
|
2112
|
+
const [mantissa = "", exponent] = text2.split("e");
|
|
2051
2113
|
const trimmed = mantissa.includes(".") ? mantissa.replace(/0+$/, "").replace(/\.$/, "") : mantissa;
|
|
2052
|
-
|
|
2114
|
+
text2 = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
|
|
2053
2115
|
}
|
|
2054
|
-
return type === "G" ?
|
|
2116
|
+
return type === "G" ? text2.toUpperCase() : text2;
|
|
2055
2117
|
}
|
|
2056
2118
|
case "d":
|
|
2057
2119
|
case "n":
|
|
@@ -2120,35 +2182,35 @@ function lookupField(name, positional, named, next) {
|
|
|
2120
2182
|
function pythonFormat(template, positional, named = {}) {
|
|
2121
2183
|
let auto = 0;
|
|
2122
2184
|
const next = () => auto++;
|
|
2123
|
-
const render = (
|
|
2185
|
+
const render = (text2) => {
|
|
2124
2186
|
let out = "";
|
|
2125
|
-
for (let i = 0; i <
|
|
2126
|
-
const ch =
|
|
2187
|
+
for (let i = 0; i < text2.length; i++) {
|
|
2188
|
+
const ch = text2[i];
|
|
2127
2189
|
if (ch === "}") {
|
|
2128
2190
|
out += "}";
|
|
2129
|
-
if (
|
|
2191
|
+
if (text2[i + 1] === "}") i++;
|
|
2130
2192
|
continue;
|
|
2131
2193
|
}
|
|
2132
2194
|
if (ch !== "{") {
|
|
2133
2195
|
out += ch;
|
|
2134
2196
|
continue;
|
|
2135
2197
|
}
|
|
2136
|
-
if (
|
|
2198
|
+
if (text2[i + 1] === "{") {
|
|
2137
2199
|
out += "{";
|
|
2138
2200
|
i++;
|
|
2139
2201
|
continue;
|
|
2140
2202
|
}
|
|
2141
2203
|
let depth = 1;
|
|
2142
2204
|
let j = i + 1;
|
|
2143
|
-
for (; j <
|
|
2144
|
-
if (
|
|
2145
|
-
else if (
|
|
2205
|
+
for (; j < text2.length && depth > 0; j++) {
|
|
2206
|
+
if (text2[j] === "{") depth++;
|
|
2207
|
+
else if (text2[j] === "}") depth--;
|
|
2146
2208
|
}
|
|
2147
2209
|
if (depth > 0) {
|
|
2148
|
-
out +=
|
|
2210
|
+
out += text2.slice(i);
|
|
2149
2211
|
break;
|
|
2150
2212
|
}
|
|
2151
|
-
const field =
|
|
2213
|
+
const field = text2.slice(i + 1, j - 1);
|
|
2152
2214
|
const colon = field.indexOf(":");
|
|
2153
2215
|
const head = colon < 0 ? field : field.slice(0, colon);
|
|
2154
2216
|
const name = head.replace(/![rsa]$/, "");
|
|
@@ -3670,7 +3732,7 @@ function layeredPartials(own, shared2, onShared) {
|
|
|
3670
3732
|
};
|
|
3671
3733
|
}
|
|
3672
3734
|
|
|
3673
|
-
// packages/engine/src/lib/
|
|
3735
|
+
// packages/engine/src/lib/dialect.ts
|
|
3674
3736
|
var loop = (engine, item, list2, body) => engine === "handlebars" ? `{{#each ${list2}}}
|
|
3675
3737
|
${body.replace(new RegExp(`(?<![\\w.])${item}\\.`, "g"), "")}
|
|
3676
3738
|
{{/each}}` : `{% for ${item} in ${list2} %}
|
|
@@ -3691,6 +3753,32 @@ var tr = (engine, key, params = {}) => {
|
|
|
3691
3753
|
return `{{ '${key}' | t${entries.length ? ": " + entries.map(([k, v]) => `${k}: ${v}`).join(", ") : ""} }}`;
|
|
3692
3754
|
return `{{ t('${key}'${entries.length ? `, { ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")} }` : ""}) }}`;
|
|
3693
3755
|
};
|
|
3756
|
+
var ifBlock = (engine, cond, body) => engine === "handlebars" ? `{{#if ${cond}}}${body}{{/if}}` : `{% if ${cond} %}${body}{% endif %}`;
|
|
3757
|
+
function stringLiteral(engine, value) {
|
|
3758
|
+
if (engine === "liquid") {
|
|
3759
|
+
if (!value.includes("'")) return `'${value}'`;
|
|
3760
|
+
if (!value.includes('"')) return `"${value}"`;
|
|
3761
|
+
return `'${value.replace(/'/g, "\u2019")}'`;
|
|
3762
|
+
}
|
|
3763
|
+
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
3764
|
+
}
|
|
3765
|
+
function filterCall(engine, input, name, args = []) {
|
|
3766
|
+
if (engine === "handlebars")
|
|
3767
|
+
return `{{${name} ${input}${args.map((a) => ` ${a}`).join("")}}}`;
|
|
3768
|
+
if (engine === "liquid")
|
|
3769
|
+
return `{{ ${input} | ${name}${args.length ? `: ${args.join(", ")}` : ""} }}`;
|
|
3770
|
+
return `{{ ${input} | ${name}${args.length ? `(${args.join(", ")})` : ""} }}`;
|
|
3771
|
+
}
|
|
3772
|
+
function helperCall(engine, name, input, options = {}) {
|
|
3773
|
+
const entries = Object.entries(options);
|
|
3774
|
+
if (engine === "handlebars")
|
|
3775
|
+
return `{{${name} ${input}${entries.map(([k, v]) => ` ${k}=${v}`).join("")}}}`;
|
|
3776
|
+
if (engine === "liquid")
|
|
3777
|
+
return `{{ ${input} | ${name}${entries.length ? `: ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")}` : ""} }}`;
|
|
3778
|
+
return `{{ ${name}(${input}${entries.length ? `, { ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")} }` : ""}) }}`;
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
// packages/engine/src/lib/snippets.ts
|
|
3694
3782
|
function forAll(build) {
|
|
3695
3783
|
return {
|
|
3696
3784
|
jinja2: build("jinja2"),
|
|
@@ -3731,8 +3819,18 @@ var snippets = [
|
|
|
3731
3819
|
</div>$0`
|
|
3732
3820
|
),
|
|
3733
3821
|
sampleData: {
|
|
3734
|
-
company: {
|
|
3735
|
-
|
|
3822
|
+
company: {
|
|
3823
|
+
name: "Fennlor Studio GmbH",
|
|
3824
|
+
street: "Musterstra\xDFe 1",
|
|
3825
|
+
zip: "12345",
|
|
3826
|
+
city: "Musterstadt"
|
|
3827
|
+
},
|
|
3828
|
+
customer: {
|
|
3829
|
+
name: "Olvarest GmbH",
|
|
3830
|
+
street: "Beispielweg 2",
|
|
3831
|
+
zip: "54321",
|
|
3832
|
+
city: "Beispielstadt"
|
|
3833
|
+
}
|
|
3736
3834
|
},
|
|
3737
3835
|
// Form B puts the field 45 mm below the top edge and 20 mm from the left; the page margins decide where it lands
|
|
3738
3836
|
css: `.ff-window { width: 85mm; height: 45mm; box-sizing: border-box; padding: 0 5mm; overflow: hidden; }
|
|
@@ -3759,8 +3857,16 @@ var snippets = [
|
|
|
3759
3857
|
.ff-reference th { text-align: left; font-weight: normal; color: #555; padding: 1px 12px 1px 0; }
|
|
3760
3858
|
.ff-reference td { text-align: right; padding: 1px 0; }`,
|
|
3761
3859
|
i18n: {
|
|
3762
|
-
en: {
|
|
3763
|
-
|
|
3860
|
+
en: {
|
|
3861
|
+
"reference.customer": "Customer no.",
|
|
3862
|
+
"reference.invoice": "Invoice no.",
|
|
3863
|
+
"reference.date": "Date"
|
|
3864
|
+
},
|
|
3865
|
+
de: {
|
|
3866
|
+
"reference.customer": "Kundennummer",
|
|
3867
|
+
"reference.invoice": "Rechnungsnummer",
|
|
3868
|
+
"reference.date": "Datum"
|
|
3869
|
+
}
|
|
3764
3870
|
}
|
|
3765
3871
|
},
|
|
3766
3872
|
{
|
|
@@ -3775,14 +3881,24 @@ var snippets = [
|
|
|
3775
3881
|
</div>$0`
|
|
3776
3882
|
),
|
|
3777
3883
|
sampleData: {
|
|
3778
|
-
invoice: {
|
|
3884
|
+
invoice: {
|
|
3885
|
+
number: "2026-0042",
|
|
3886
|
+
period_start: "2026-09-01",
|
|
3887
|
+
period_end: "2026-09-30"
|
|
3888
|
+
}
|
|
3779
3889
|
},
|
|
3780
3890
|
css: `.ff-heading { margin: 24px 0 16px; }
|
|
3781
3891
|
.ff-heading h1 { font-family: var(--brand-font-heading, inherit); font-size: 18pt; color: var(--brand-color-primary, inherit); margin: 0 0 4px; }
|
|
3782
3892
|
.ff-heading p { margin: 0; color: #555; }`,
|
|
3783
3893
|
i18n: {
|
|
3784
|
-
en: {
|
|
3785
|
-
|
|
3894
|
+
en: {
|
|
3895
|
+
"heading.title": "Invoice {number}",
|
|
3896
|
+
"heading.period": "Service period:"
|
|
3897
|
+
},
|
|
3898
|
+
de: {
|
|
3899
|
+
"heading.title": "Rechnung {number}",
|
|
3900
|
+
"heading.period": "Leistungszeitraum:"
|
|
3901
|
+
}
|
|
3786
3902
|
}
|
|
3787
3903
|
},
|
|
3788
3904
|
{
|
|
@@ -3909,11 +4025,17 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
|
|
|
3909
4025
|
group: "invoice",
|
|
3910
4026
|
title: "Small business VAT note",
|
|
3911
4027
|
description: "States that no VAT is charged under \xA7 19 UStG (Kleinunternehmerregelung).",
|
|
3912
|
-
source: forAll(
|
|
4028
|
+
source: forAll(
|
|
4029
|
+
(e) => `<p class="ff-note">${tr(e, "vat.small_business")}</p>$0`
|
|
4030
|
+
),
|
|
3913
4031
|
css: `.ff-note { margin-top: 12px; font-size: 9pt; color: #444; }`,
|
|
3914
4032
|
i18n: {
|
|
3915
|
-
en: {
|
|
3916
|
-
|
|
4033
|
+
en: {
|
|
4034
|
+
"vat.small_business": "No VAT is charged under the small business scheme of \xA7 19 UStG."
|
|
4035
|
+
},
|
|
4036
|
+
de: {
|
|
4037
|
+
"vat.small_business": "Gem\xE4\xDF \xA7 19 UStG wird keine Umsatzsteuer berechnet."
|
|
4038
|
+
}
|
|
3917
4039
|
}
|
|
3918
4040
|
},
|
|
3919
4041
|
{
|
|
@@ -3974,7 +4096,9 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
|
|
|
3974
4096
|
<span>${tr(e, "footer.page")} <span class="pageNumber"></span> ${tr(e, "footer.of")} <span class="totalPages"></span></span>
|
|
3975
4097
|
</div>$0`
|
|
3976
4098
|
),
|
|
3977
|
-
sampleData: {
|
|
4099
|
+
sampleData: {
|
|
4100
|
+
company: { name: "Fennlor Studio GmbH", email: "hello@fennlor.example" }
|
|
4101
|
+
},
|
|
3978
4102
|
i18n: {
|
|
3979
4103
|
en: { "footer.page": "Page", "footer.of": "of" },
|
|
3980
4104
|
de: { "footer.page": "Seite", "footer.of": "von" }
|
|
@@ -4059,7 +4183,10 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
|
|
|
4059
4183
|
},
|
|
4060
4184
|
i18n: {
|
|
4061
4185
|
en: { "qr.alt": "Payment QR code", "qr.caption": "Scan to pay" },
|
|
4062
|
-
de: {
|
|
4186
|
+
de: {
|
|
4187
|
+
"qr.alt": "QR-Code zur Zahlung",
|
|
4188
|
+
"qr.caption": "Zum Bezahlen scannen"
|
|
4189
|
+
}
|
|
4063
4190
|
},
|
|
4064
4191
|
sampleData: {
|
|
4065
4192
|
payment: {
|
|
@@ -4178,7 +4305,9 @@ var selectorKey = (prelude) => (prelude.split(";").pop() ?? "").trim().replace(/
|
|
|
4178
4305
|
function mergeSnippetCss(css2, snippetCss) {
|
|
4179
4306
|
if (!snippetCss?.trim()) return css2;
|
|
4180
4307
|
const defined = new Set(
|
|
4181
|
-
[...stripComments(css2).matchAll(/([^{}]+)\{/g)].map(
|
|
4308
|
+
[...stripComments(css2).matchAll(/([^{}]+)\{/g)].map(
|
|
4309
|
+
(m) => selectorKey(m[1] ?? "")
|
|
4310
|
+
)
|
|
4182
4311
|
);
|
|
4183
4312
|
const missing = [...stripComments(snippetCss).matchAll(/([^{}]+)\{[^{}]*\}/g)].filter((m) => !defined.has(selectorKey(m[1] ?? ""))).map((m) => m[0].trim());
|
|
4184
4313
|
if (!missing.length) return css2;
|
|
@@ -4226,9 +4355,9 @@ function pick(input, ...keys) {
|
|
|
4226
4355
|
function toLength(value) {
|
|
4227
4356
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
4228
4357
|
if (typeof value === "number") return `${value}mm`;
|
|
4229
|
-
const
|
|
4230
|
-
if (/^-?\d+(\.\d+)?$/.test(
|
|
4231
|
-
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(
|
|
4358
|
+
const text2 = String(value).trim();
|
|
4359
|
+
if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
|
|
4360
|
+
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
|
|
4232
4361
|
return void 0;
|
|
4233
4362
|
}
|
|
4234
4363
|
function toMarginLength(value) {
|
|
@@ -4248,7 +4377,7 @@ function convertHeaderFooter(html) {
|
|
|
4248
4377
|
if (typeof html !== "string" || !html.trim()) return void 0;
|
|
4249
4378
|
return chromePlaceholders.reduce((out, [pattern, span]) => out.replace(pattern, span), html);
|
|
4250
4379
|
}
|
|
4251
|
-
var escapeText = (
|
|
4380
|
+
var escapeText = (text2) => text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4252
4381
|
var visibleMarkup = (html) => html.replace(/<style[\s\S]*?<\/style>/gi, "").trim();
|
|
4253
4382
|
function apitemplateChrome(take, kind) {
|
|
4254
4383
|
const custom = take(`custom_${kind}`);
|
|
@@ -4262,7 +4391,7 @@ function apitemplateChrome(take, kind) {
|
|
|
4262
4391
|
if (!slots.some(Boolean)) return { fromSlots: false };
|
|
4263
4392
|
const sizeText = size === void 0 ? "" : String(size).trim();
|
|
4264
4393
|
const fontSize2 = /^\d+(\.\d+)?$/.test(sizeText) ? `${sizeText}px` : /^\d+(\.\d+)?(px|pt|mm)$/.test(sizeText) ? sizeText : "9px";
|
|
4265
|
-
const cells = slots.map((
|
|
4394
|
+
const cells = slots.map((text2, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text2)}</span>`).join("");
|
|
4266
4395
|
const styles = customHtml.match(/<style[\s\S]*?<\/style>/gi)?.join("") ?? "";
|
|
4267
4396
|
return {
|
|
4268
4397
|
html: convertHeaderFooter(`${styles}<div style="display:flex;gap:4mm;font-size:${fontSize2}">${cells}</div>`),
|
|
@@ -4271,8 +4400,8 @@ function apitemplateChrome(take, kind) {
|
|
|
4271
4400
|
}
|
|
4272
4401
|
function clearsChromePadding(html, kind) {
|
|
4273
4402
|
if (!html) return false;
|
|
4274
|
-
for (const
|
|
4275
|
-
for (const rule2 of (
|
|
4403
|
+
for (const style2 of html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi))
|
|
4404
|
+
for (const rule2 of (style2[1] ?? "").matchAll(/([^{}]+)\{([^}]*)\}/g))
|
|
4276
4405
|
if (new RegExp(`#${kind}\\b`).test(rule2[1] ?? "") && /(^|;)\s*padding\s*:\s*0(px|mm)?\s*(!important\s*)?(;|$)/i.test((rule2[2] ?? "").trim()))
|
|
4277
4406
|
return true;
|
|
4278
4407
|
return false;
|
|
@@ -4423,9 +4552,9 @@ function checkTemplate(engineId, html, sampleData, name, docs) {
|
|
|
4423
4552
|
function lengthMm(value) {
|
|
4424
4553
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
4425
4554
|
if (typeof value === "number" && Number.isFinite(value)) return `${value}mm`;
|
|
4426
|
-
const
|
|
4427
|
-
if (/^-?\d+(\.\d+)?$/.test(
|
|
4428
|
-
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(
|
|
4555
|
+
const text2 = String(value).trim();
|
|
4556
|
+
if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
|
|
4557
|
+
if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
|
|
4429
4558
|
return void 0;
|
|
4430
4559
|
}
|
|
4431
4560
|
function toBase64(value) {
|
|
@@ -4828,7 +4957,7 @@ var unsupportedFilters = {
|
|
|
4828
4957
|
in_time_zone: 'pass the zone to `date` as its second argument: `date: "dd.MM.yyyy", "Europe/Berlin"`'
|
|
4829
4958
|
};
|
|
4830
4959
|
function mapLiquid(source, fn) {
|
|
4831
|
-
return source.replace(/(\{\{-?|\{%-?)([\s\S]*?)(-?\}\}|-?%\})/g, (_m,
|
|
4960
|
+
return source.replace(/(\{\{-?|\{%-?)([\s\S]*?)(-?\}\}|-?%\})/g, (_m, open2, inner, close) => `${open2}${fn(inner)}${close}`);
|
|
4832
4961
|
}
|
|
4833
4962
|
function convertDialect(source, changes) {
|
|
4834
4963
|
const errors = [];
|
|
@@ -4942,11 +5071,11 @@ function inlineIncludes(source, blocks, errors, changes) {
|
|
|
4942
5071
|
}
|
|
4943
5072
|
var cssPaperFormats = { a3: "A3", a4: "A4", a5: "A5", letter: "Letter" };
|
|
4944
5073
|
var isoSizes = { a0: [841, 1189], a1: [594, 841], a2: [420, 594], a6: [105, 148] };
|
|
4945
|
-
var escapeHtml2 = (
|
|
5074
|
+
var escapeHtml2 = (text2) => text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4946
5075
|
function simpleHeaderFooter(part, margin) {
|
|
4947
5076
|
const cells = ["left", "center", "right"].map((side) => part[side] ?? "");
|
|
4948
5077
|
if (!cells.some((c) => c.trim())) return void 0;
|
|
4949
|
-
const cell3 = (
|
|
5078
|
+
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>`;
|
|
4950
5079
|
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>`;
|
|
4951
5080
|
}
|
|
4952
5081
|
function mapSettings(input, kind, warnings, changes) {
|
|
@@ -5115,11 +5244,11 @@ var DOCS3 = "/migrate/jsreport";
|
|
|
5115
5244
|
var COLLECTIONS = ["templates", "data", "assets", "components", "scripts", "folders"];
|
|
5116
5245
|
function readJsreportExport(files) {
|
|
5117
5246
|
const bundle = { templates: [], data: [], assets: [], components: [], scripts: [], folders: [] };
|
|
5118
|
-
for (const [path,
|
|
5247
|
+
for (const [path, text2] of Object.entries(files)) {
|
|
5119
5248
|
const normalized = path.replace(/\\/g, "/").replace(/^\.?\//, "");
|
|
5120
5249
|
if (normalized === "metadata.json") {
|
|
5121
5250
|
try {
|
|
5122
|
-
bundle.version = String(JSON.parse(
|
|
5251
|
+
bundle.version = String(JSON.parse(text2).reporterVersion ?? "");
|
|
5123
5252
|
} catch {
|
|
5124
5253
|
}
|
|
5125
5254
|
continue;
|
|
@@ -5127,7 +5256,7 @@ function readJsreportExport(files) {
|
|
|
5127
5256
|
const collection = normalized.split("/")[0];
|
|
5128
5257
|
if (!COLLECTIONS.includes(collection) || !normalized.endsWith(".json")) continue;
|
|
5129
5258
|
try {
|
|
5130
|
-
bundle[collection].push(JSON.parse(
|
|
5259
|
+
bundle[collection].push(JSON.parse(text2));
|
|
5131
5260
|
} catch {
|
|
5132
5261
|
}
|
|
5133
5262
|
}
|
|
@@ -5169,7 +5298,7 @@ function assetBase64(content) {
|
|
|
5169
5298
|
}
|
|
5170
5299
|
return toBase64(content);
|
|
5171
5300
|
}
|
|
5172
|
-
var escapeMustaches = (
|
|
5301
|
+
var escapeMustaches = (text2) => text2.replace(/\{\{/g, "\\{{");
|
|
5173
5302
|
function resolveAssets(source, bundle, options, warnings, changes) {
|
|
5174
5303
|
const v3 = /\{\{\{?\s*asset\s+(["'])([^"']+)\1(?:\s+(["'])([^"']+)\3)?\s*\}?\}\}/g;
|
|
5175
5304
|
const v2 = /\{#asset\s+([^\s}]+)(?:\s+@encoding=(\w+))?\s*\}/g;
|
|
@@ -5406,6 +5535,11 @@ function importJsreport(bundle, ref, options = {}) {
|
|
|
5406
5535
|
};
|
|
5407
5536
|
}
|
|
5408
5537
|
|
|
5538
|
+
// packages/engine/src/lib/generated/frames.ts
|
|
5539
|
+
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);})();';
|
|
5540
|
+
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);})();';
|
|
5541
|
+
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);})();';
|
|
5542
|
+
|
|
5409
5543
|
// packages/engine/src/lib/assemble.ts
|
|
5410
5544
|
var DEFAULT_CHROME_PADDING = "0 10mm";
|
|
5411
5545
|
function defaultSettings() {
|
|
@@ -5505,9 +5639,12 @@ function chartScripts(html, vendor) {
|
|
|
5505
5639
|
if (!vendor?.chartJs || !html.includes("data-ff-chart"))
|
|
5506
5640
|
return { head: "", body: "" };
|
|
5507
5641
|
const lib = "inline" in vendor.chartJs ? `<script>${vendor.chartJs.inline}</script>` : `<script src="${escapeHtml(vendor.chartJs.src)}"></script>`;
|
|
5508
|
-
return {
|
|
5509
|
-
|
|
5510
|
-
|
|
5642
|
+
return {
|
|
5643
|
+
head: `${lib}
|
|
5644
|
+
`,
|
|
5645
|
+
body: `${chartInitScript()}
|
|
5646
|
+
`
|
|
5647
|
+
};
|
|
5511
5648
|
}
|
|
5512
5649
|
function assembleDocument(input) {
|
|
5513
5650
|
const settings = mergeSettings(input.settings);
|
|
@@ -5534,6 +5671,8 @@ ${brandFontFamilies(input.brand)}`
|
|
|
5534
5671
|
` : "" : input.extraCss ? `<style data-formfeed="extra">${input.extraCss}</style>
|
|
5535
5672
|
` : "";
|
|
5536
5673
|
const charts = chartScripts(input.html, input.vendor);
|
|
5674
|
+
const fit = input.html.includes("data-ff-fit") ? `<script>${fitRuntime.replace(/<\/script/gi, "<\\/script")}</script>
|
|
5675
|
+
` : "";
|
|
5537
5676
|
return `<!doctype html>
|
|
5538
5677
|
<html lang="${escapeHtml(locale.split("_")[0] ?? locale)}">
|
|
5539
5678
|
<head>
|
|
@@ -5545,7 +5684,7 @@ ${tailwind}<style data-formfeed="template">${input.css ?? ""}</style>
|
|
|
5545
5684
|
${charts.head}</head>
|
|
5546
5685
|
<body class="formfeed-body formfeed-${kind}${input.mode === "preview" ? " formfeed-preview" : ""}">
|
|
5547
5686
|
${input.html}
|
|
5548
|
-
${charts.body}</body>
|
|
5687
|
+
${charts.body}${fit}</body>
|
|
5549
5688
|
</html>
|
|
5550
5689
|
`;
|
|
5551
5690
|
}
|
|
@@ -5620,7 +5759,10 @@ function paperOf(settings) {
|
|
|
5620
5759
|
}
|
|
5621
5760
|
var pageNumberSpans = (html, page, total) => {
|
|
5622
5761
|
const fill = (source, name, value) => source.replace(
|
|
5623
|
-
new RegExp(
|
|
5762
|
+
new RegExp(
|
|
5763
|
+
`<([a-z][a-z0-9]*)(\\s[^>]*?\\bclass=(["'])(?:[^"']*\\s)?${name}(?:\\s[^"']*)?\\3[^>]*)>[^<]*</\\1>`,
|
|
5764
|
+
"gi"
|
|
5765
|
+
),
|
|
5624
5766
|
(_match, tag, attrs) => `<${tag}${attrs}>${value}</${tag}>`
|
|
5625
5767
|
);
|
|
5626
5768
|
return fill(fill(html, "pageNumber", page), "totalPages", total);
|
|
@@ -5643,6 +5785,7 @@ document.addEventListener('click', function (event) {
|
|
|
5643
5785
|
} catch (e) {}
|
|
5644
5786
|
}, true);
|
|
5645
5787
|
</script>`;
|
|
5788
|
+
var textEditScript = `<script>${textEditRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
|
|
5646
5789
|
var pageNavScript = `<script>
|
|
5647
5790
|
(function () {
|
|
5648
5791
|
var pages = function () { return document.querySelectorAll('.pagedjs_page'); };
|
|
@@ -5744,8 +5887,11 @@ var zoomGestureScript = `<script>
|
|
|
5744
5887
|
})();
|
|
5745
5888
|
</script>`;
|
|
5746
5889
|
function intoHead(document, markup) {
|
|
5747
|
-
return document.replace(
|
|
5748
|
-
|
|
5890
|
+
return document.replace(
|
|
5891
|
+
/<head[^>]*>(?:\s*<meta\b[^>]*>)*/i,
|
|
5892
|
+
(head) => `${head}
|
|
5893
|
+
${markup}`
|
|
5894
|
+
);
|
|
5749
5895
|
}
|
|
5750
5896
|
function chromeBoxStyle(settings, which) {
|
|
5751
5897
|
const margin = settings.margin ?? {};
|
|
@@ -5760,7 +5906,7 @@ function flowChromeStyle(settings, which) {
|
|
|
5760
5906
|
const height = settings[which]?.height;
|
|
5761
5907
|
return `position:absolute;${which === "header" ? "top" : "bottom"}:0;left:0;width:100%;box-sizing:border-box;padding:${padding};${height ? `height:${height};` : ""}`;
|
|
5762
5908
|
}
|
|
5763
|
-
function flowDocument(draft) {
|
|
5909
|
+
function flowDocument(draft, options = {}) {
|
|
5764
5910
|
const { settings, kind, headerHtml, footerHtml } = draft;
|
|
5765
5911
|
const margin = settings.margin ?? {};
|
|
5766
5912
|
const paper = paperOf(settings);
|
|
@@ -5772,7 +5918,15 @@ body.formfeed-preview { position: relative; box-sizing: border-box; width: ${pap
|
|
|
5772
5918
|
</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>`;
|
|
5773
5919
|
const header = headerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "header") : `${chromeBoxStyle(settings, "header")}margin-bottom:8px`}">${headerHtml}</div>` : "";
|
|
5774
5920
|
const footer = footerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "footer") : `${chromeBoxStyle(settings, "footer")}margin-top:8px`}">${pageNumberSpans(footerHtml, "1", "1")}</div>` : "";
|
|
5775
|
-
return intoHead(
|
|
5921
|
+
return intoHead(
|
|
5922
|
+
draft.document,
|
|
5923
|
+
`${chrome}${options.editing ? textEditScript : ""}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}`
|
|
5924
|
+
).replace(/(<body[^>]*>)/, `$1${header}`).replace("</body>", `${footer}</body>`);
|
|
5925
|
+
}
|
|
5926
|
+
function canvasDocument(draft) {
|
|
5927
|
+
const chrome = `<style data-formfeed="stage">html, body { background: transparent !important; margin: 0 !important; }</style>`;
|
|
5928
|
+
const runtime = `<script>${stageRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
|
|
5929
|
+
return intoHead(draft.document, `${chrome}${runtime}${zoomGestureScript}`);
|
|
5776
5930
|
}
|
|
5777
5931
|
function pagedDocument(draft, options) {
|
|
5778
5932
|
const { headerHtml, footerHtml, settings } = draft;
|
|
@@ -5794,82 +5948,17 @@ html { background: #e5e7eb; }
|
|
|
5794
5948
|
`;
|
|
5795
5949
|
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>`;
|
|
5796
5950
|
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>`;
|
|
5797
|
-
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>`;
|
|
5951
|
+
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>`;
|
|
5798
5952
|
const script = `<script src="${options.pagedScriptUrl}"></script>`;
|
|
5799
5953
|
const header = headerHtml ? `<div class="ff-running-header">${pageNumberSpans(headerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
|
|
5800
5954
|
const footer = footerHtml ? `<div class="ff-running-footer">${pageNumberSpans(footerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
|
|
5801
5955
|
return intoHead(
|
|
5802
5956
|
draft.document,
|
|
5803
|
-
`<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${inspectScript}${pageNavScript}${zoomGestureScript}`
|
|
5957
|
+
`<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${options.editing ? textEditScript : ""}${inspectScript}${pageNavScript}${zoomGestureScript}`
|
|
5804
5958
|
).replace(/(<body[^>]*>)/, `$1${header}${footer}`);
|
|
5805
5959
|
}
|
|
5806
5960
|
|
|
5807
|
-
// packages/engine/src/lib/source-
|
|
5808
|
-
var sourceAttribute = "data-ff-src";
|
|
5809
|
-
function parseSourceAttribute(value) {
|
|
5810
|
-
const match = /^(body|header|footer):(\d+):(\d+)$/.exec(value ?? "");
|
|
5811
|
-
if (!match) return null;
|
|
5812
|
-
const line = Number(match[2]);
|
|
5813
|
-
const column = Number(match[3]);
|
|
5814
|
-
if (line < 1 || column < 1) return null;
|
|
5815
|
-
return { file: match[1], line, column };
|
|
5816
|
-
}
|
|
5817
|
-
function annotateSourcePositions(source, engine, file) {
|
|
5818
|
-
const scanner = new Scanner(source, engine);
|
|
5819
|
-
const lines = lineStarts(source);
|
|
5820
|
-
const out = [];
|
|
5821
|
-
let copied = 0;
|
|
5822
|
-
const n = source.length;
|
|
5823
|
-
let i = 0;
|
|
5824
|
-
while (i < n) {
|
|
5825
|
-
const ch = source[i];
|
|
5826
|
-
if (ch === "{") {
|
|
5827
|
-
i = scanner.skipTemplate(i) ?? i + 1;
|
|
5828
|
-
continue;
|
|
5829
|
-
}
|
|
5830
|
-
if (ch !== "<") {
|
|
5831
|
-
i++;
|
|
5832
|
-
continue;
|
|
5833
|
-
}
|
|
5834
|
-
if (source.startsWith("<!--", i)) {
|
|
5835
|
-
i = scanner.skipTo(i + 4, "-->");
|
|
5836
|
-
continue;
|
|
5837
|
-
}
|
|
5838
|
-
if (source.startsWith("<![CDATA[", i)) {
|
|
5839
|
-
i = scanner.skipTo(i + 9, "]]>");
|
|
5840
|
-
continue;
|
|
5841
|
-
}
|
|
5842
|
-
const next = source[i + 1];
|
|
5843
|
-
if (next === "!" || next === "?") {
|
|
5844
|
-
i = scanner.tagEnd(i + 2);
|
|
5845
|
-
continue;
|
|
5846
|
-
}
|
|
5847
|
-
if (next === "/") {
|
|
5848
|
-
i += 2;
|
|
5849
|
-
continue;
|
|
5850
|
-
}
|
|
5851
|
-
tagName.lastIndex = i + 1;
|
|
5852
|
-
const name = tagName.exec(source)?.[0];
|
|
5853
|
-
if (!name) {
|
|
5854
|
-
i++;
|
|
5855
|
-
continue;
|
|
5856
|
-
}
|
|
5857
|
-
const nameEnd = i + 1 + name.length;
|
|
5858
|
-
const end = scanner.tagEnd(nameEnd);
|
|
5859
|
-
const after = source[nameEnd];
|
|
5860
|
-
const plainName = after === void 0 || after === ">" || after === "/" || /\s/.test(after);
|
|
5861
|
-
if (plainName && !source.slice(nameEnd, end).toLowerCase().includes(sourceAttribute)) {
|
|
5862
|
-
const { line, column } = locate(lines, i);
|
|
5863
|
-
out.push(source.slice(copied, nameEnd), ` ${sourceAttribute}="${file}:${line}:${column}"`);
|
|
5864
|
-
copied = nameEnd;
|
|
5865
|
-
}
|
|
5866
|
-
i = end;
|
|
5867
|
-
if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
|
|
5868
|
-
i = scanner.skipToClosingTag(end, name);
|
|
5869
|
-
}
|
|
5870
|
-
out.push(source.slice(copied));
|
|
5871
|
-
return out.join("");
|
|
5872
|
-
}
|
|
5961
|
+
// packages/engine/src/lib/source-scan.ts
|
|
5873
5962
|
var tagName = /[A-Za-z][^\s/>{}<"'=]*/y;
|
|
5874
5963
|
var rawTextElements = /* @__PURE__ */ new Set([
|
|
5875
5964
|
"script",
|
|
@@ -5910,6 +5999,38 @@ var Scanner = class {
|
|
|
5910
5999
|
return this.blockEnd(tag.end, tag.keyword);
|
|
5911
6000
|
return tag.end;
|
|
5912
6001
|
}
|
|
6002
|
+
/** The construct starting at `at` and what kind it is, or null when `{` starts none. */
|
|
6003
|
+
classify(at) {
|
|
6004
|
+
const s = this.source;
|
|
6005
|
+
const tag = this.templateTag(at);
|
|
6006
|
+
if (!tag) return null;
|
|
6007
|
+
if (this.engine === "handlebars") {
|
|
6008
|
+
if (s.startsWith("{{{{", at)) return { type: "statement", end: tag.end };
|
|
6009
|
+
if (s.startsWith("{{!", at)) return { type: "comment", end: tag.end };
|
|
6010
|
+
if (s.startsWith("{{{", at))
|
|
6011
|
+
return {
|
|
6012
|
+
type: "output",
|
|
6013
|
+
end: tag.end,
|
|
6014
|
+
inner: s.slice(at + 3, tag.end - 3)
|
|
6015
|
+
};
|
|
6016
|
+
const inner = s.slice(at + 2, tag.end - 2);
|
|
6017
|
+
const head = inner.replace(/^~?\s*/, "");
|
|
6018
|
+
if (/^[#/^>*]/.test(head) || /^else(?:\s|~?$)/.test(head))
|
|
6019
|
+
return { type: "statement", end: tag.end };
|
|
6020
|
+
return { type: "output", end: tag.end, inner };
|
|
6021
|
+
}
|
|
6022
|
+
if (s.startsWith("{#", at)) return { type: "comment", end: tag.end };
|
|
6023
|
+
if (s.startsWith("{{", at))
|
|
6024
|
+
return {
|
|
6025
|
+
type: "output",
|
|
6026
|
+
end: tag.end,
|
|
6027
|
+
inner: s.slice(at + 2, tag.end - 2)
|
|
6028
|
+
};
|
|
6029
|
+
if (/^\{%[-+~]?\s*#/.test(s.slice(at, at + 64)))
|
|
6030
|
+
return { type: "comment", end: tag.end };
|
|
6031
|
+
const end = tag.keyword && this.captures(tag.keyword, tag.rest ?? "") ? this.blockEnd(tag.end, tag.keyword) : tag.end;
|
|
6032
|
+
return { type: "statement", end, keyword: tag.keyword };
|
|
6033
|
+
}
|
|
5913
6034
|
/** Index after `close`, stepping over template constructs; the end of the source when missing. */
|
|
5914
6035
|
skipTo(from, close) {
|
|
5915
6036
|
let j = from;
|
|
@@ -5974,7 +6095,8 @@ var Scanner = class {
|
|
|
5974
6095
|
j++;
|
|
5975
6096
|
continue;
|
|
5976
6097
|
}
|
|
5977
|
-
if (tag.keyword === keyword && this.captures(keyword, tag.rest ?? ""))
|
|
6098
|
+
if (tag.keyword === keyword && this.captures(keyword, tag.rest ?? ""))
|
|
6099
|
+
depth++;
|
|
5978
6100
|
else if (tag.keyword === `end${keyword}` && --depth === 0) return tag.end;
|
|
5979
6101
|
j = tag.end;
|
|
5980
6102
|
}
|
|
@@ -5985,22 +6107,32 @@ var Scanner = class {
|
|
|
5985
6107
|
if (s[at] !== "{") return null;
|
|
5986
6108
|
if (this.engine === "handlebars") {
|
|
5987
6109
|
if (s.startsWith("{{{{", at)) return { end: this.handlebarsRawBlock(at) };
|
|
5988
|
-
if (s.startsWith("{{!--", at))
|
|
5989
|
-
|
|
5990
|
-
if (s.startsWith("{{
|
|
5991
|
-
|
|
6110
|
+
if (s.startsWith("{{!--", at))
|
|
6111
|
+
return { end: this.indexAfter(at + 5, "--}}") };
|
|
6112
|
+
if (s.startsWith("{{!", at))
|
|
6113
|
+
return { end: this.indexAfter(at + 3, "}}") };
|
|
6114
|
+
if (s.startsWith("{{{", at))
|
|
6115
|
+
return { end: this.expressionEnd(at + 3, "}}}") };
|
|
6116
|
+
if (s.startsWith("{{", at))
|
|
6117
|
+
return { end: this.expressionEnd(at + 2, "}}") };
|
|
5992
6118
|
return null;
|
|
5993
6119
|
}
|
|
5994
6120
|
if (this.engine === "jinja2" && s.startsWith("{#", at))
|
|
5995
6121
|
return { end: this.indexAfter(at + 2, "#}") };
|
|
5996
|
-
if (s.startsWith("{{", at))
|
|
6122
|
+
if (s.startsWith("{{", at))
|
|
6123
|
+
return { end: this.expressionEnd(at + 2, "}}") };
|
|
5997
6124
|
if (!s.startsWith("{%", at)) return null;
|
|
5998
6125
|
const end = /^[-+~]?\s*#/.test(s.slice(at + 2, at + 64)) ? this.indexAfter(at + 2, "%}") : this.expressionEnd(at + 2, "%}");
|
|
5999
|
-
const inner = /^[-+~]?\s*(\w+)([\s\S]*)$/.exec(
|
|
6126
|
+
const inner = /^[-+~]?\s*(\w+)([\s\S]*)$/.exec(
|
|
6127
|
+
s.slice(at + 2, Math.max(at + 2, end - 2))
|
|
6128
|
+
);
|
|
6000
6129
|
const keyword = inner?.[1];
|
|
6001
6130
|
if (!keyword) return { end };
|
|
6002
6131
|
if (literalBlocks[this.engine].has(keyword)) {
|
|
6003
|
-
const close = new RegExp(
|
|
6132
|
+
const close = new RegExp(
|
|
6133
|
+
`\\{%[-+~]?\\s*end${keyword}\\s*[-+~]?%\\}`,
|
|
6134
|
+
"g"
|
|
6135
|
+
);
|
|
6004
6136
|
close.lastIndex = end;
|
|
6005
6137
|
const match = close.exec(s);
|
|
6006
6138
|
return { end: match ? match.index + match[0].length : this.n, keyword };
|
|
@@ -6055,125 +6187,2110 @@ function locate(starts, offset) {
|
|
|
6055
6187
|
}
|
|
6056
6188
|
return { line: low + 1, column: offset - (starts[low] ?? 0) + 1 };
|
|
6057
6189
|
}
|
|
6190
|
+
function offsetOf(starts, sourceLength, line, column) {
|
|
6191
|
+
const start = starts[line - 1];
|
|
6192
|
+
if (start === void 0 || column < 1) return null;
|
|
6193
|
+
const offset = start + column - 1;
|
|
6194
|
+
const next = starts[line] ?? sourceLength + 1;
|
|
6195
|
+
return offset < next ? offset : null;
|
|
6196
|
+
}
|
|
6058
6197
|
|
|
6059
|
-
// packages/engine/src/lib/
|
|
6060
|
-
var
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
maxFileBytes: 20 * 1024 * 1024
|
|
6077
|
-
};
|
|
6078
|
-
|
|
6079
|
-
// node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
|
|
6080
|
-
var u8 = Uint8Array;
|
|
6081
|
-
var u16 = Uint16Array;
|
|
6082
|
-
var i32 = Int32Array;
|
|
6083
|
-
var fleb = new u8([
|
|
6084
|
-
0,
|
|
6085
|
-
0,
|
|
6086
|
-
0,
|
|
6087
|
-
0,
|
|
6088
|
-
0,
|
|
6089
|
-
0,
|
|
6090
|
-
0,
|
|
6091
|
-
0,
|
|
6092
|
-
1,
|
|
6093
|
-
1,
|
|
6094
|
-
1,
|
|
6095
|
-
1,
|
|
6096
|
-
2,
|
|
6097
|
-
2,
|
|
6098
|
-
2,
|
|
6099
|
-
2,
|
|
6100
|
-
3,
|
|
6101
|
-
3,
|
|
6102
|
-
3,
|
|
6103
|
-
3,
|
|
6104
|
-
4,
|
|
6105
|
-
4,
|
|
6106
|
-
4,
|
|
6107
|
-
4,
|
|
6108
|
-
5,
|
|
6109
|
-
5,
|
|
6110
|
-
5,
|
|
6111
|
-
5,
|
|
6112
|
-
0,
|
|
6113
|
-
/* unused */
|
|
6114
|
-
0,
|
|
6115
|
-
0,
|
|
6116
|
-
/* impossible */
|
|
6117
|
-
0
|
|
6198
|
+
// packages/engine/src/lib/source-edit.ts
|
|
6199
|
+
var MAX_REGION = 2e4;
|
|
6200
|
+
var voidElements = /* @__PURE__ */ new Set([
|
|
6201
|
+
"area",
|
|
6202
|
+
"base",
|
|
6203
|
+
"br",
|
|
6204
|
+
"col",
|
|
6205
|
+
"embed",
|
|
6206
|
+
"hr",
|
|
6207
|
+
"img",
|
|
6208
|
+
"input",
|
|
6209
|
+
"link",
|
|
6210
|
+
"meta",
|
|
6211
|
+
"param",
|
|
6212
|
+
"source",
|
|
6213
|
+
"track",
|
|
6214
|
+
"wbr"
|
|
6118
6215
|
]);
|
|
6119
|
-
var
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
0,
|
|
6152
|
-
0
|
|
6216
|
+
var phrasingElements = /* @__PURE__ */ new Set([
|
|
6217
|
+
"a",
|
|
6218
|
+
"abbr",
|
|
6219
|
+
"b",
|
|
6220
|
+
"bdi",
|
|
6221
|
+
"bdo",
|
|
6222
|
+
"cite",
|
|
6223
|
+
"code",
|
|
6224
|
+
"data",
|
|
6225
|
+
"del",
|
|
6226
|
+
"dfn",
|
|
6227
|
+
"em",
|
|
6228
|
+
"font",
|
|
6229
|
+
"i",
|
|
6230
|
+
"img",
|
|
6231
|
+
"ins",
|
|
6232
|
+
"kbd",
|
|
6233
|
+
"label",
|
|
6234
|
+
"mark",
|
|
6235
|
+
"q",
|
|
6236
|
+
"s",
|
|
6237
|
+
"samp",
|
|
6238
|
+
"small",
|
|
6239
|
+
"span",
|
|
6240
|
+
"strong",
|
|
6241
|
+
"sub",
|
|
6242
|
+
"sup",
|
|
6243
|
+
"svg",
|
|
6244
|
+
"time",
|
|
6245
|
+
"u",
|
|
6246
|
+
"var",
|
|
6247
|
+
"wbr"
|
|
6153
6248
|
]);
|
|
6154
|
-
var
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6249
|
+
var namedEntities = {
|
|
6250
|
+
amp: 38,
|
|
6251
|
+
lt: 60,
|
|
6252
|
+
gt: 62,
|
|
6253
|
+
quot: 34,
|
|
6254
|
+
apos: 39,
|
|
6255
|
+
nbsp: 160,
|
|
6256
|
+
shy: 173,
|
|
6257
|
+
ensp: 8194,
|
|
6258
|
+
emsp: 8195,
|
|
6259
|
+
thinsp: 8201,
|
|
6260
|
+
zwnj: 8204,
|
|
6261
|
+
zwj: 8205,
|
|
6262
|
+
ndash: 8211,
|
|
6263
|
+
mdash: 8212,
|
|
6264
|
+
lsquo: 8216,
|
|
6265
|
+
rsquo: 8217,
|
|
6266
|
+
sbquo: 8218,
|
|
6267
|
+
ldquo: 8220,
|
|
6268
|
+
rdquo: 8221,
|
|
6269
|
+
bdquo: 8222,
|
|
6270
|
+
laquo: 171,
|
|
6271
|
+
raquo: 187,
|
|
6272
|
+
lsaquo: 8249,
|
|
6273
|
+
rsaquo: 8250,
|
|
6274
|
+
hellip: 8230,
|
|
6275
|
+
bull: 8226,
|
|
6276
|
+
middot: 183,
|
|
6277
|
+
euro: 8364,
|
|
6278
|
+
pound: 163,
|
|
6279
|
+
yen: 165,
|
|
6280
|
+
cent: 162,
|
|
6281
|
+
copy: 169,
|
|
6282
|
+
reg: 174,
|
|
6283
|
+
trade: 8482,
|
|
6284
|
+
deg: 176,
|
|
6285
|
+
sect: 167,
|
|
6286
|
+
para: 182,
|
|
6287
|
+
times: 215,
|
|
6288
|
+
divide: 247,
|
|
6289
|
+
plusmn: 177,
|
|
6290
|
+
micro: 181,
|
|
6291
|
+
frac12: 189,
|
|
6292
|
+
frac14: 188,
|
|
6293
|
+
frac34: 190,
|
|
6294
|
+
sup2: 178,
|
|
6295
|
+
sup3: 179,
|
|
6296
|
+
dagger: 8224,
|
|
6297
|
+
Dagger: 8225,
|
|
6298
|
+
larr: 8592,
|
|
6299
|
+
rarr: 8594,
|
|
6300
|
+
uarr: 8593,
|
|
6301
|
+
darr: 8595,
|
|
6302
|
+
auml: 228,
|
|
6303
|
+
ouml: 246,
|
|
6304
|
+
uuml: 252,
|
|
6305
|
+
Auml: 196,
|
|
6306
|
+
Ouml: 214,
|
|
6307
|
+
Uuml: 220,
|
|
6308
|
+
szlig: 223,
|
|
6309
|
+
eacute: 233,
|
|
6310
|
+
egrave: 232,
|
|
6311
|
+
agrave: 224,
|
|
6312
|
+
aacute: 225,
|
|
6313
|
+
ccedil: 231,
|
|
6314
|
+
oacute: 243,
|
|
6315
|
+
iacute: 237,
|
|
6316
|
+
uacute: 250,
|
|
6317
|
+
ntilde: 241
|
|
6318
|
+
};
|
|
6319
|
+
var entityPattern = /&(#[0-9]{1,7}|#[xX][0-9a-fA-F]{1,6}|[A-Za-z][A-Za-z0-9]{0,31});/y;
|
|
6320
|
+
var NBSP = String.fromCharCode(160);
|
|
6321
|
+
var BREAK = String.fromCharCode(57344);
|
|
6322
|
+
var REMOVED = String.fromCharCode(57345);
|
|
6323
|
+
var CHIP_OPEN = String.fromCharCode(57346);
|
|
6324
|
+
var CHIP_CLOSE = String.fromCharCode(57347);
|
|
6325
|
+
function editableRegion(source, engine, location) {
|
|
6326
|
+
const offset = offsetOf(
|
|
6327
|
+
lineStarts(source),
|
|
6328
|
+
source.length,
|
|
6329
|
+
location.line,
|
|
6330
|
+
location.column
|
|
6331
|
+
);
|
|
6332
|
+
return offset === null ? { reason: "not-an-element" } : editableRegionAt(source, engine, offset);
|
|
6333
|
+
}
|
|
6334
|
+
function editableRegionAt(source, engine, at) {
|
|
6335
|
+
const scanned = scanRegion(source, engine, at);
|
|
6336
|
+
if ("reason" in scanned) return scanned;
|
|
6337
|
+
const region = { kind: "text", ...scanned };
|
|
6338
|
+
const visible = region.parts.filter(
|
|
6339
|
+
(p) => !(p.kind === "text" && !source.slice(p.start, p.end).trim())
|
|
6340
|
+
);
|
|
6341
|
+
const only = visible.length === 1 ? visible[0] : void 0;
|
|
6342
|
+
if (only?.kind === "chip" && only.chip === "expression") {
|
|
6343
|
+
const call = translationCall(engine, only.inner ?? "");
|
|
6344
|
+
if (call)
|
|
6345
|
+
return { ...region, kind: "t", key: call.key, params: call.params };
|
|
6346
|
+
}
|
|
6347
|
+
const hasText = region.parts.some(
|
|
6348
|
+
(p) => p.kind === "text" && decodeText2(source.slice(p.start, p.end)).text.trim() !== ""
|
|
6349
|
+
);
|
|
6350
|
+
return hasText ? region : { reason: "no-text" };
|
|
6351
|
+
}
|
|
6352
|
+
function findRegionAgain(source, engine, outer) {
|
|
6353
|
+
const first = source.indexOf(outer);
|
|
6354
|
+
if (first < 0 || source.indexOf(outer, first + 1) >= 0) return null;
|
|
6355
|
+
const found = editableRegionAt(source, engine, first);
|
|
6356
|
+
return "reason" in found ? null : found;
|
|
6357
|
+
}
|
|
6358
|
+
function regionChips(source, region) {
|
|
6359
|
+
const chips = [];
|
|
6360
|
+
for (const part of region.parts)
|
|
6361
|
+
if (part.kind === "chip")
|
|
6362
|
+
chips.push({
|
|
6363
|
+
index: chips.length,
|
|
6364
|
+
kind: part.chip,
|
|
6365
|
+
label: part.label,
|
|
6366
|
+
source: source.slice(part.start, part.end)
|
|
6367
|
+
});
|
|
6368
|
+
return chips;
|
|
6369
|
+
}
|
|
6370
|
+
function regionItems(source, region) {
|
|
6371
|
+
const items = [];
|
|
6372
|
+
let chip = 0;
|
|
6373
|
+
for (const part of region.parts) {
|
|
6374
|
+
if (part.kind === "text")
|
|
6375
|
+
items.push({
|
|
6376
|
+
kind: "text",
|
|
6377
|
+
text: decodeText2(source.slice(part.start, part.end)).text
|
|
6378
|
+
});
|
|
6379
|
+
else if (part.kind === "break") items.push({ kind: "break" });
|
|
6380
|
+
else items.push({ kind: "chip", index: chip++ });
|
|
6159
6381
|
}
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6382
|
+
return items;
|
|
6383
|
+
}
|
|
6384
|
+
function applyRegionEdit(source, engine, region, items) {
|
|
6385
|
+
const chipParts = region.parts.filter(
|
|
6386
|
+
(p) => p.kind === "chip"
|
|
6387
|
+
);
|
|
6388
|
+
const kept = [];
|
|
6389
|
+
for (const item of items)
|
|
6390
|
+
if (item.kind === "chip") {
|
|
6391
|
+
if (!Number.isInteger(item.index) || item.index < 0 || item.index >= chipParts.length)
|
|
6392
|
+
return { error: "chips" };
|
|
6393
|
+
if (kept.length && item.index <= (kept[kept.length - 1] ?? -1))
|
|
6394
|
+
return { error: "chips" };
|
|
6395
|
+
kept.push(item.index);
|
|
6396
|
+
}
|
|
6397
|
+
const breakSpelling = source.slice(...spanOf(region.parts.find((p) => p.kind === "break"))) || "<br>";
|
|
6398
|
+
const newSegments = [""];
|
|
6399
|
+
for (const item of items) {
|
|
6400
|
+
if (item.kind === "chip") newSegments.push("");
|
|
6401
|
+
else
|
|
6402
|
+
newSegments[newSegments.length - 1] += item.kind === "break" ? BREAK : item.text.replace(/[\uE000-\uE003]/g, "");
|
|
6403
|
+
}
|
|
6404
|
+
const pieces = [];
|
|
6405
|
+
let segStart = region.start;
|
|
6406
|
+
let partIndex = 0;
|
|
6407
|
+
for (let k = 0; k <= kept.length; k++) {
|
|
6408
|
+
const boundary = k < kept.length ? chipParts[kept[k] ?? 0] : void 0;
|
|
6409
|
+
const segEnd = boundary ? boundary.start : region.end;
|
|
6410
|
+
const segParts = [];
|
|
6411
|
+
while (partIndex < region.parts.length && (region.parts[partIndex]?.start ?? Infinity) < segEnd)
|
|
6412
|
+
segParts.push(region.parts[partIndex++]);
|
|
6413
|
+
if (boundary) partIndex++;
|
|
6414
|
+
pieces.push(
|
|
6415
|
+
rewriteSegment(
|
|
6416
|
+
source,
|
|
6417
|
+
segStart,
|
|
6418
|
+
segEnd,
|
|
6419
|
+
segParts,
|
|
6420
|
+
newSegments[k] ?? "",
|
|
6421
|
+
breakSpelling
|
|
6422
|
+
)
|
|
6423
|
+
);
|
|
6424
|
+
if (boundary) {
|
|
6425
|
+
pieces.push(source.slice(boundary.start, boundary.end));
|
|
6426
|
+
segStart = boundary.end;
|
|
6164
6427
|
}
|
|
6165
6428
|
}
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6429
|
+
const expected = flatExpected(
|
|
6430
|
+
items,
|
|
6431
|
+
chipParts.map((c) => source.slice(c.start, c.end))
|
|
6432
|
+
);
|
|
6433
|
+
const attempt = source.slice(0, region.start) + pieces.join("") + source.slice(region.end);
|
|
6434
|
+
if (readsBack(attempt, engine, region.tagStart, expected))
|
|
6435
|
+
return { source: attempt };
|
|
6436
|
+
const whole = items.map(
|
|
6437
|
+
(item) => item.kind === "chip" ? source.slice(chipParts[item.index]?.start, chipParts[item.index]?.end) : item.kind === "break" ? breakSpelling : encodeText(item.text, true, true)
|
|
6438
|
+
).join("");
|
|
6439
|
+
const rewritten = source.slice(0, region.start) + whole + source.slice(region.end);
|
|
6440
|
+
return readsBack(rewritten, engine, region.tagStart, expected) ? { source: rewritten } : { error: "invalid" };
|
|
6441
|
+
}
|
|
6442
|
+
function translationItems(text2) {
|
|
6443
|
+
const items = [];
|
|
6444
|
+
const names = [];
|
|
6445
|
+
let last = 0;
|
|
6446
|
+
for (const match of text2.matchAll(/\{(\w+)\}/g)) {
|
|
6447
|
+
if (match.index > last)
|
|
6448
|
+
items.push({ kind: "text", text: text2.slice(last, match.index) });
|
|
6449
|
+
items.push({ kind: "chip", index: names.length });
|
|
6450
|
+
names.push(match[1] ?? "");
|
|
6451
|
+
last = match.index + match[0].length;
|
|
6452
|
+
}
|
|
6453
|
+
if (last < text2.length) items.push({ kind: "text", text: text2.slice(last) });
|
|
6454
|
+
return { items, names };
|
|
6455
|
+
}
|
|
6456
|
+
function translationText(items, names) {
|
|
6457
|
+
return items.map(
|
|
6458
|
+
(item) => item.kind === "chip" ? `{${names[item.index] ?? ""}}` : item.kind === "break" ? " " : item.text.replace(new RegExp(NBSP, "g"), " ")
|
|
6459
|
+
).join("");
|
|
6460
|
+
}
|
|
6461
|
+
function chipValues(rendered, items) {
|
|
6462
|
+
const slots = [""];
|
|
6463
|
+
for (const item of items) {
|
|
6464
|
+
if (item.kind === "chip") slots.push("");
|
|
6465
|
+
else if (item.kind === "text") slots[slots.length - 1] += item.text;
|
|
6466
|
+
}
|
|
6467
|
+
const chips = slots.length - 1;
|
|
6468
|
+
if (!rendered.startsWith(slots[0] ?? "")) return null;
|
|
6469
|
+
let found = null;
|
|
6470
|
+
let solutions = 0;
|
|
6471
|
+
let steps = 0;
|
|
6472
|
+
const values = [];
|
|
6473
|
+
const place = (k, pos) => {
|
|
6474
|
+
if (solutions > 1 || ++steps > 1e4) return;
|
|
6475
|
+
if (k === chips) {
|
|
6476
|
+
if (pos === rendered.length) {
|
|
6477
|
+
solutions++;
|
|
6478
|
+
found = [...values];
|
|
6479
|
+
}
|
|
6480
|
+
return;
|
|
6481
|
+
}
|
|
6482
|
+
const next = slots[k + 1] ?? "";
|
|
6483
|
+
const last = k + 1 === chips;
|
|
6484
|
+
for (let q = pos; q <= rendered.length; q++) {
|
|
6485
|
+
q = rendered.indexOf(next, q);
|
|
6486
|
+
if (q < 0) return;
|
|
6487
|
+
if (!last || q + next.length === rendered.length) {
|
|
6488
|
+
values[k] = rendered.slice(pos, q);
|
|
6489
|
+
place(k + 1, q + next.length);
|
|
6490
|
+
if (solutions > 1) return;
|
|
6491
|
+
}
|
|
6492
|
+
}
|
|
6493
|
+
};
|
|
6494
|
+
place(0, (slots[0] ?? "").length);
|
|
6495
|
+
return solutions === 1 && steps <= 1e4 ? found : null;
|
|
6496
|
+
}
|
|
6497
|
+
function scanRegion(source, engine, at) {
|
|
6498
|
+
if (source[at] !== "<") return { reason: "not-an-element" };
|
|
6499
|
+
tagName.lastIndex = at + 1;
|
|
6500
|
+
const name = tagName.exec(source)?.[0];
|
|
6501
|
+
if (!name) return { reason: "not-an-element" };
|
|
6502
|
+
const nameEnd = at + 1 + name.length;
|
|
6503
|
+
if (!plainNameEnd(source[nameEnd])) return { reason: "dynamic-tag" };
|
|
6504
|
+
const tag = name.toLowerCase();
|
|
6505
|
+
const scanner = new Scanner(source, engine);
|
|
6506
|
+
const start = scanner.tagEnd(nameEnd);
|
|
6507
|
+
if (voidElements.has(tag) || source[start - 2] === "/")
|
|
6508
|
+
return { reason: "void" };
|
|
6509
|
+
if (rawTextElements.has(tag)) return { reason: "raw-text" };
|
|
6510
|
+
const parts = [];
|
|
6511
|
+
let textStart = start;
|
|
6512
|
+
const flush = (to) => {
|
|
6513
|
+
if (to > textStart) parts.push({ kind: "text", start: textStart, end: to });
|
|
6514
|
+
};
|
|
6515
|
+
let i = start;
|
|
6516
|
+
while (i < source.length) {
|
|
6517
|
+
if (i - start > MAX_REGION) return { reason: "too-large" };
|
|
6518
|
+
const ch = source[i];
|
|
6519
|
+
if (ch === "{") {
|
|
6520
|
+
const construct = scanner.classify(i);
|
|
6521
|
+
if (!construct) {
|
|
6522
|
+
i++;
|
|
6523
|
+
continue;
|
|
6524
|
+
}
|
|
6525
|
+
if (construct.type === "statement") return { reason: "control-flow" };
|
|
6526
|
+
flush(i);
|
|
6527
|
+
parts.push(
|
|
6528
|
+
construct.type === "output" ? {
|
|
6529
|
+
kind: "chip",
|
|
6530
|
+
chip: "expression",
|
|
6531
|
+
label: expressionLabel(construct.inner),
|
|
6532
|
+
inner: construct.inner,
|
|
6533
|
+
start: i,
|
|
6534
|
+
end: construct.end
|
|
6535
|
+
} : {
|
|
6536
|
+
kind: "chip",
|
|
6537
|
+
chip: "comment",
|
|
6538
|
+
label: "comment",
|
|
6539
|
+
start: i,
|
|
6540
|
+
end: construct.end
|
|
6541
|
+
}
|
|
6542
|
+
);
|
|
6543
|
+
i = textStart = construct.end;
|
|
6544
|
+
continue;
|
|
6545
|
+
}
|
|
6546
|
+
if (ch === "&") {
|
|
6547
|
+
entityPattern.lastIndex = i;
|
|
6548
|
+
const match = entityPattern.exec(source);
|
|
6549
|
+
if (match && entityChar(match[1] ?? "") === null && !(match[1] ?? "").startsWith("#")) {
|
|
6550
|
+
flush(i);
|
|
6551
|
+
parts.push({
|
|
6552
|
+
kind: "chip",
|
|
6553
|
+
chip: "entity",
|
|
6554
|
+
label: match[0],
|
|
6555
|
+
start: i,
|
|
6556
|
+
end: i + match[0].length
|
|
6557
|
+
});
|
|
6558
|
+
i = textStart = i + match[0].length;
|
|
6559
|
+
continue;
|
|
6560
|
+
}
|
|
6561
|
+
i++;
|
|
6562
|
+
continue;
|
|
6563
|
+
}
|
|
6564
|
+
if (ch !== "<") {
|
|
6565
|
+
i++;
|
|
6566
|
+
continue;
|
|
6567
|
+
}
|
|
6568
|
+
if (source.startsWith("<!--", i)) {
|
|
6569
|
+
const end = scanner.skipTo(i + 4, "-->");
|
|
6570
|
+
flush(i);
|
|
6571
|
+
parts.push({
|
|
6572
|
+
kind: "chip",
|
|
6573
|
+
chip: "comment",
|
|
6574
|
+
label: "comment",
|
|
6575
|
+
start: i,
|
|
6576
|
+
end
|
|
6577
|
+
});
|
|
6578
|
+
i = textStart = end;
|
|
6579
|
+
continue;
|
|
6580
|
+
}
|
|
6581
|
+
const next = source[i + 1];
|
|
6582
|
+
if (next === "/") {
|
|
6583
|
+
tagName.lastIndex = i + 2;
|
|
6584
|
+
const closing = tagName.exec(source)?.[0];
|
|
6585
|
+
if (closing?.toLowerCase() !== tag) return { reason: "unclosed" };
|
|
6586
|
+
flush(i);
|
|
6587
|
+
return {
|
|
6588
|
+
tag,
|
|
6589
|
+
tagStart: at,
|
|
6590
|
+
start,
|
|
6591
|
+
end: i,
|
|
6592
|
+
outerEnd: scanner.tagEnd(i + 2 + closing.length),
|
|
6593
|
+
parts
|
|
6594
|
+
};
|
|
6595
|
+
}
|
|
6596
|
+
if (next === "!" || next === "?") return { reason: "block-child" };
|
|
6597
|
+
tagName.lastIndex = i + 1;
|
|
6598
|
+
const child2 = tagName.exec(source)?.[0];
|
|
6599
|
+
if (!child2) {
|
|
6600
|
+
i++;
|
|
6601
|
+
continue;
|
|
6602
|
+
}
|
|
6603
|
+
const childNameEnd = i + 1 + child2.length;
|
|
6604
|
+
if (!plainNameEnd(source[childNameEnd])) return { reason: "dynamic-tag" };
|
|
6605
|
+
const childTag = child2.toLowerCase();
|
|
6606
|
+
const childStartEnd = scanner.tagEnd(childNameEnd);
|
|
6607
|
+
if (childTag === "br") {
|
|
6608
|
+
flush(i);
|
|
6609
|
+
parts.push({ kind: "break", start: i, end: childStartEnd });
|
|
6610
|
+
i = textStart = childStartEnd;
|
|
6611
|
+
continue;
|
|
6612
|
+
}
|
|
6613
|
+
if (rawTextElements.has(childTag)) return { reason: "raw-text" };
|
|
6614
|
+
if (!phrasingElements.has(childTag)) return { reason: "block-child" };
|
|
6615
|
+
let childEnd = childStartEnd;
|
|
6616
|
+
if (!voidElements.has(childTag) && source[childStartEnd - 2] !== "/") {
|
|
6617
|
+
childEnd = matchingEnd(source, scanner, childStartEnd, childTag);
|
|
6618
|
+
if (childEnd === null) return { reason: "unclosed" };
|
|
6619
|
+
}
|
|
6620
|
+
flush(i);
|
|
6621
|
+
parts.push({
|
|
6622
|
+
kind: "chip",
|
|
6623
|
+
chip: "element",
|
|
6624
|
+
label: `<${childTag}>`,
|
|
6625
|
+
start: i,
|
|
6626
|
+
end: childEnd
|
|
6627
|
+
});
|
|
6628
|
+
i = textStart = childEnd;
|
|
6629
|
+
}
|
|
6630
|
+
return { reason: "unclosed" };
|
|
6631
|
+
}
|
|
6632
|
+
function plainNameEnd(after) {
|
|
6633
|
+
return after === void 0 || after === ">" || after === "/" || /\s/.test(after);
|
|
6634
|
+
}
|
|
6635
|
+
function matchingEnd(source, scanner, from, name) {
|
|
6636
|
+
let depth = 1;
|
|
6637
|
+
let j = from;
|
|
6638
|
+
while (j < source.length) {
|
|
6639
|
+
const ch = source[j];
|
|
6640
|
+
if (ch === "{") {
|
|
6641
|
+
j = scanner.skipTemplate(j) ?? j + 1;
|
|
6642
|
+
continue;
|
|
6643
|
+
}
|
|
6644
|
+
if (ch !== "<") {
|
|
6645
|
+
j++;
|
|
6646
|
+
continue;
|
|
6647
|
+
}
|
|
6648
|
+
if (source.startsWith("<!--", j)) {
|
|
6649
|
+
j = scanner.skipTo(j + 4, "-->");
|
|
6650
|
+
continue;
|
|
6651
|
+
}
|
|
6652
|
+
if (source[j + 1] === "/") {
|
|
6653
|
+
tagName.lastIndex = j + 2;
|
|
6654
|
+
const closing = tagName.exec(source)?.[0];
|
|
6655
|
+
const end2 = scanner.tagEnd(j + 2);
|
|
6656
|
+
if (closing?.toLowerCase() === name && --depth === 0) return end2;
|
|
6657
|
+
j = end2;
|
|
6658
|
+
continue;
|
|
6659
|
+
}
|
|
6660
|
+
tagName.lastIndex = j + 1;
|
|
6661
|
+
const opening = tagName.exec(source)?.[0];
|
|
6662
|
+
if (!opening) {
|
|
6663
|
+
j++;
|
|
6664
|
+
continue;
|
|
6665
|
+
}
|
|
6666
|
+
const end = scanner.tagEnd(j + 1 + opening.length);
|
|
6667
|
+
const lower = opening.toLowerCase();
|
|
6668
|
+
if (lower === name && source[end - 2] !== "/") depth++;
|
|
6669
|
+
j = rawTextElements.has(lower) ? scanner.skipToClosingTag(end, lower) : end;
|
|
6670
|
+
}
|
|
6671
|
+
return null;
|
|
6672
|
+
}
|
|
6673
|
+
function expressionLabel(inner) {
|
|
6674
|
+
const label2 = inner.replace(/^[-~+&]?\s*/, "").replace(/\s*[-~+]?$/, "").replace(/\s+/g, " ");
|
|
6675
|
+
return label2.length > 60 ? `${label2.slice(0, 59)}\u2026` : label2;
|
|
6676
|
+
}
|
|
6677
|
+
function translationCall(engine, inner) {
|
|
6678
|
+
const expression = inner.replace(/^[-~+]?\s*/, "").replace(/\s*[-~+]?$/, "");
|
|
6679
|
+
let match;
|
|
6680
|
+
if (engine === "jinja2") {
|
|
6681
|
+
match = /^(?:t|translate)\(\s*(['"])((?:(?!\1).)*)\1\s*(?:,\s*\{([\s\S]*)\}\s*)?\)$/.exec(
|
|
6682
|
+
expression
|
|
6683
|
+
);
|
|
6684
|
+
return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", ":", ",") } : null;
|
|
6685
|
+
}
|
|
6686
|
+
if (engine === "liquid") {
|
|
6687
|
+
match = /^(['"])((?:(?!\1).)*)\1\s*\|\s*(?:t|translate)\b\s*(?::\s*([\s\S]*))?$/.exec(
|
|
6688
|
+
expression
|
|
6689
|
+
);
|
|
6690
|
+
return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", ":", ",") } : null;
|
|
6691
|
+
}
|
|
6692
|
+
match = /^(?:t|translate)\s+(['"])((?:(?!\1).)*)\1([\s\S]*)$/.exec(
|
|
6693
|
+
expression
|
|
6694
|
+
);
|
|
6695
|
+
return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", "=", " ") } : null;
|
|
6696
|
+
}
|
|
6697
|
+
function parseParams(text2, assign, separator) {
|
|
6698
|
+
const params = {};
|
|
6699
|
+
for (const piece of splitTopLevel(text2, separator)) {
|
|
6700
|
+
const at = piece.indexOf(assign);
|
|
6701
|
+
if (at <= 0) continue;
|
|
6702
|
+
const name = piece.slice(0, at).trim().replace(/^['"]|['"]$/g, "");
|
|
6703
|
+
if (/^[\w-]+$/.test(name)) params[name] = piece.slice(at + 1).trim();
|
|
6704
|
+
}
|
|
6705
|
+
return params;
|
|
6706
|
+
}
|
|
6707
|
+
function splitTopLevel(text2, separator) {
|
|
6708
|
+
const pieces = [];
|
|
6709
|
+
let depth = 0;
|
|
6710
|
+
let quote2 = null;
|
|
6711
|
+
let current = "";
|
|
6712
|
+
for (const ch of text2) {
|
|
6713
|
+
if (quote2) {
|
|
6714
|
+
if (ch === quote2) quote2 = null;
|
|
6715
|
+
} else if (ch === '"' || ch === "'") quote2 = ch;
|
|
6716
|
+
else if ("([{".includes(ch)) depth++;
|
|
6717
|
+
else if (")]}".includes(ch)) depth--;
|
|
6718
|
+
else if (depth === 0 && (separator === " " ? /\s/.test(ch) : ch === separator)) {
|
|
6719
|
+
if (current.trim()) pieces.push(current);
|
|
6720
|
+
current = "";
|
|
6721
|
+
continue;
|
|
6722
|
+
}
|
|
6723
|
+
current += ch;
|
|
6724
|
+
}
|
|
6725
|
+
if (current.trim()) pieces.push(current);
|
|
6726
|
+
return pieces;
|
|
6727
|
+
}
|
|
6728
|
+
function entityChar(reference) {
|
|
6729
|
+
if (reference.startsWith("#")) {
|
|
6730
|
+
const code2 = reference[1] === "x" || reference[1] === "X" ? parseInt(reference.slice(2), 16) : parseInt(reference.slice(1), 10);
|
|
6731
|
+
return Number.isFinite(code2) && code2 > 0 && code2 <= 1114111 && !(code2 >= 55296 && code2 <= 57343) ? String.fromCodePoint(code2) : null;
|
|
6732
|
+
}
|
|
6733
|
+
const code = namedEntities[reference];
|
|
6734
|
+
return code === void 0 ? null : String.fromCharCode(code);
|
|
6735
|
+
}
|
|
6736
|
+
function decodeText2(raw) {
|
|
6737
|
+
let text2 = "";
|
|
6738
|
+
const map = [];
|
|
6739
|
+
let i = 0;
|
|
6740
|
+
while (i < raw.length) {
|
|
6741
|
+
if (raw[i] === "&") {
|
|
6742
|
+
entityPattern.lastIndex = i;
|
|
6743
|
+
const match = entityPattern.exec(raw);
|
|
6744
|
+
const decoded = match ? entityChar(match[1] ?? "") : null;
|
|
6745
|
+
if (match && decoded !== null) {
|
|
6746
|
+
for (let u = 0; u < decoded.length; u++) map.push(i);
|
|
6747
|
+
text2 += decoded;
|
|
6748
|
+
i += match[0].length;
|
|
6749
|
+
continue;
|
|
6750
|
+
}
|
|
6751
|
+
}
|
|
6752
|
+
text2 += raw[i];
|
|
6753
|
+
map.push(i);
|
|
6754
|
+
i++;
|
|
6755
|
+
}
|
|
6756
|
+
map.push(raw.length);
|
|
6757
|
+
return { text: text2, map };
|
|
6758
|
+
}
|
|
6759
|
+
function encodeText(text2, keepNbsp, allBraces = false) {
|
|
6760
|
+
let out = "";
|
|
6761
|
+
for (let k = 0; k < text2.length; k++) {
|
|
6762
|
+
const ch = text2[k] ?? "";
|
|
6763
|
+
const next = text2[k + 1];
|
|
6764
|
+
if (ch === "&") out += "&";
|
|
6765
|
+
else if (ch === "<") out += "<";
|
|
6766
|
+
else if (ch === ">") out += ">";
|
|
6767
|
+
else if (ch === NBSP) out += keepNbsp ? " " : " ";
|
|
6768
|
+
else if (ch === "{" && (allBraces || next === void 0 || next === "{" || next === "%" || next === "#"))
|
|
6769
|
+
out += "{";
|
|
6770
|
+
else if (ch === "#" && next === "}") out += "#";
|
|
6771
|
+
else out += ch;
|
|
6772
|
+
}
|
|
6773
|
+
return out;
|
|
6774
|
+
}
|
|
6775
|
+
function encodeTemplateText(text2) {
|
|
6776
|
+
return encodeText(text2, true);
|
|
6777
|
+
}
|
|
6778
|
+
function spanOf(part) {
|
|
6779
|
+
return part ? [part.start, part.end] : [0, 0];
|
|
6780
|
+
}
|
|
6781
|
+
function rewriteSegment(source, segStart, segEnd, parts, next, breakSpelling) {
|
|
6782
|
+
let old = "";
|
|
6783
|
+
const map = [];
|
|
6784
|
+
for (const part of parts) {
|
|
6785
|
+
if (part.kind === "text") {
|
|
6786
|
+
const decoded = decodeText2(source.slice(part.start, part.end));
|
|
6787
|
+
old += decoded.text;
|
|
6788
|
+
for (let k = 0; k < decoded.text.length; k++)
|
|
6789
|
+
map.push(part.start + (decoded.map[k] ?? 0));
|
|
6790
|
+
} else {
|
|
6791
|
+
old += part.kind === "break" ? BREAK : REMOVED;
|
|
6792
|
+
map.push(part.start);
|
|
6793
|
+
}
|
|
6794
|
+
}
|
|
6795
|
+
map.push(segEnd);
|
|
6796
|
+
if (old === next) return source.slice(segStart, segEnd);
|
|
6797
|
+
let prefix = 0;
|
|
6798
|
+
const limit = Math.min(old.length, next.length);
|
|
6799
|
+
while (prefix < limit && old[prefix] === next[prefix]) prefix++;
|
|
6800
|
+
let suffix = 0;
|
|
6801
|
+
while (suffix < limit - prefix && old[old.length - 1 - suffix] === next[next.length - 1 - suffix])
|
|
6802
|
+
suffix++;
|
|
6803
|
+
if (prefix > 0 && isHighSurrogate(old.charCodeAt(prefix - 1))) prefix--;
|
|
6804
|
+
if (suffix > 0 && isLowSurrogate(old.charCodeAt(old.length - suffix)))
|
|
6805
|
+
suffix--;
|
|
6806
|
+
const from = map[prefix] ?? segEnd;
|
|
6807
|
+
const to = map[old.length - suffix] ?? segEnd;
|
|
6808
|
+
const middle = next.slice(prefix, next.length - suffix);
|
|
6809
|
+
const keepNbsp = old.slice(prefix, old.length - suffix).includes(NBSP);
|
|
6810
|
+
const written = middle.split(BREAK).map((text2) => encodeText(text2, keepNbsp)).join(breakSpelling);
|
|
6811
|
+
return source.slice(segStart, from) + written + source.slice(to, segEnd);
|
|
6812
|
+
}
|
|
6813
|
+
var isHighSurrogate = (code) => code >= 55296 && code <= 56319;
|
|
6814
|
+
var isLowSurrogate = (code) => code >= 56320 && code <= 57343;
|
|
6815
|
+
function flatExpected(items, chipSources) {
|
|
6816
|
+
return items.map(
|
|
6817
|
+
(item) => item.kind === "chip" ? CHIP_OPEN + (chipSources[item.index] ?? "") + CHIP_CLOSE : item.kind === "break" ? BREAK : item.text.replace(/[\uE000-\uE003]/g, "")
|
|
6818
|
+
).join("").replace(new RegExp(NBSP, "g"), " ");
|
|
6819
|
+
}
|
|
6820
|
+
function readsBack(source, engine, tagStart, expected) {
|
|
6821
|
+
const scanned = scanRegion(source, engine, tagStart);
|
|
6822
|
+
if ("reason" in scanned) return false;
|
|
6823
|
+
if (engine === "jinja2" && scanned.parts.some(
|
|
6824
|
+
(p) => p.kind === "text" && source.slice(p.start, p.end).includes("#}")
|
|
6825
|
+
))
|
|
6826
|
+
return false;
|
|
6827
|
+
const flat = scanned.parts.map(
|
|
6828
|
+
(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
|
|
6829
|
+
).join("").replace(new RegExp(NBSP, "g"), " ");
|
|
6830
|
+
return flat === expected;
|
|
6831
|
+
}
|
|
6832
|
+
|
|
6833
|
+
// packages/engine/src/lib/source-positions.ts
|
|
6834
|
+
var sourceAttribute = "data-ff-src";
|
|
6835
|
+
var editAttribute = "data-ff-edit";
|
|
6836
|
+
function parseSourceAttribute(value) {
|
|
6837
|
+
const match = /^(body|header|footer):(\d+):(\d+)$/.exec(value ?? "");
|
|
6838
|
+
if (!match) return null;
|
|
6839
|
+
const line = Number(match[2]);
|
|
6840
|
+
const column = Number(match[3]);
|
|
6841
|
+
if (line < 1 || column < 1) return null;
|
|
6842
|
+
return { file: match[1], line, column };
|
|
6843
|
+
}
|
|
6844
|
+
function annotateSourcePositions(source, engine, file, options = {}) {
|
|
6845
|
+
const scanner = new Scanner(source, engine);
|
|
6846
|
+
const lines = lineStarts(source);
|
|
6847
|
+
const out = [];
|
|
6848
|
+
let copied = 0;
|
|
6849
|
+
const n = source.length;
|
|
6850
|
+
let i = 0;
|
|
6851
|
+
while (i < n) {
|
|
6852
|
+
const ch = source[i];
|
|
6853
|
+
if (ch === "{") {
|
|
6854
|
+
i = scanner.skipTemplate(i) ?? i + 1;
|
|
6855
|
+
continue;
|
|
6856
|
+
}
|
|
6857
|
+
if (ch !== "<") {
|
|
6858
|
+
i++;
|
|
6859
|
+
continue;
|
|
6860
|
+
}
|
|
6861
|
+
if (source.startsWith("<!--", i)) {
|
|
6862
|
+
i = scanner.skipTo(i + 4, "-->");
|
|
6863
|
+
continue;
|
|
6864
|
+
}
|
|
6865
|
+
if (source.startsWith("<![CDATA[", i)) {
|
|
6866
|
+
i = scanner.skipTo(i + 9, "]]>");
|
|
6867
|
+
continue;
|
|
6868
|
+
}
|
|
6869
|
+
const next = source[i + 1];
|
|
6870
|
+
if (next === "!" || next === "?") {
|
|
6871
|
+
i = scanner.tagEnd(i + 2);
|
|
6872
|
+
continue;
|
|
6873
|
+
}
|
|
6874
|
+
if (next === "/") {
|
|
6875
|
+
i += 2;
|
|
6876
|
+
continue;
|
|
6877
|
+
}
|
|
6878
|
+
tagName.lastIndex = i + 1;
|
|
6879
|
+
const name = tagName.exec(source)?.[0];
|
|
6880
|
+
if (!name) {
|
|
6881
|
+
i++;
|
|
6882
|
+
continue;
|
|
6883
|
+
}
|
|
6884
|
+
const nameEnd = i + 1 + name.length;
|
|
6885
|
+
const end = scanner.tagEnd(nameEnd);
|
|
6886
|
+
const after = source[nameEnd];
|
|
6887
|
+
const plainName = after === void 0 || after === ">" || after === "/" || /\s/.test(after);
|
|
6888
|
+
if (plainName && !source.slice(nameEnd, end).toLowerCase().includes(sourceAttribute)) {
|
|
6889
|
+
const { line, column } = locate(lines, i);
|
|
6890
|
+
const region = options.editable ? editableRegionAt(source, engine, i) : null;
|
|
6891
|
+
const edit = region && !("reason" in region) ? ` ${editAttribute}="${region.kind}"` : "";
|
|
6892
|
+
out.push(
|
|
6893
|
+
source.slice(copied, nameEnd),
|
|
6894
|
+
` ${sourceAttribute}="${file}:${line}:${column}"${edit}`
|
|
6895
|
+
);
|
|
6896
|
+
copied = nameEnd;
|
|
6897
|
+
}
|
|
6898
|
+
i = end;
|
|
6899
|
+
if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
|
|
6900
|
+
i = scanner.skipToClosingTag(end, name);
|
|
6901
|
+
}
|
|
6902
|
+
out.push(source.slice(copied));
|
|
6903
|
+
return out.join("");
|
|
6904
|
+
}
|
|
6905
|
+
|
|
6906
|
+
// packages/engine/src/lib/design/types.ts
|
|
6907
|
+
var DESIGN_VERSION = 1;
|
|
6908
|
+
|
|
6909
|
+
// packages/engine/src/lib/design/parse.ts
|
|
6910
|
+
var DESIGN_LIMITS = {
|
|
6911
|
+
layers: 200,
|
|
6912
|
+
textPerLayer: 5e3,
|
|
6913
|
+
bytes: 256 * 1024
|
|
6914
|
+
};
|
|
6915
|
+
var layerIdPattern = /^[a-z][a-z0-9-]{0,31}$/;
|
|
6916
|
+
var dataPathPattern = /^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*$/;
|
|
6917
|
+
var colourPattern = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
6918
|
+
var tokenPattern = /^[a-z][a-z0-9-]{0,31}$/;
|
|
6919
|
+
var symbologies = [
|
|
6920
|
+
"code128",
|
|
6921
|
+
"ean13",
|
|
6922
|
+
"ean8",
|
|
6923
|
+
"upca",
|
|
6924
|
+
"itf14",
|
|
6925
|
+
"code39"
|
|
6926
|
+
];
|
|
6927
|
+
var Reader = class {
|
|
6928
|
+
constructor(at) {
|
|
6929
|
+
this.at = at;
|
|
6930
|
+
}
|
|
6931
|
+
problems = [];
|
|
6932
|
+
problem(message) {
|
|
6933
|
+
this.problems.push(`${this.at}: ${message}`);
|
|
6934
|
+
}
|
|
6935
|
+
number(value, name, min, max2, fallback) {
|
|
6936
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6937
|
+
if (fallback !== void 0 && value === void 0) return fallback;
|
|
6938
|
+
this.problem(`${name} must be a number`);
|
|
6939
|
+
return fallback ?? min;
|
|
6940
|
+
}
|
|
6941
|
+
return Math.min(max2, Math.max(min, value));
|
|
6942
|
+
}
|
|
6943
|
+
string(value, name, max2, pattern) {
|
|
6944
|
+
if (typeof value !== "string" || value.length > max2 || pattern && !pattern.test(value)) {
|
|
6945
|
+
this.problem(`${name} is not valid`);
|
|
6946
|
+
return "";
|
|
6947
|
+
}
|
|
6948
|
+
return value;
|
|
6949
|
+
}
|
|
6950
|
+
oneOf(value, name, options, fallback) {
|
|
6951
|
+
if (value === void 0 && fallback !== void 0) return fallback;
|
|
6952
|
+
if (typeof value === "string" && options.includes(value))
|
|
6953
|
+
return value;
|
|
6954
|
+
this.problem(`${name} must be one of ${options.join(", ")}`);
|
|
6955
|
+
return options[0];
|
|
6956
|
+
}
|
|
6957
|
+
bool(value, fallback) {
|
|
6958
|
+
return typeof value === "boolean" ? value : fallback;
|
|
6959
|
+
}
|
|
6960
|
+
paint(value, name) {
|
|
6961
|
+
const v = value;
|
|
6962
|
+
if (v && typeof v["color"] === "string" && colourPattern.test(v["color"]))
|
|
6963
|
+
return { color: v["color"] };
|
|
6964
|
+
if (v && typeof v["brand"] === "string" && tokenPattern.test(v["brand"]) && typeof v["fallback"] === "string" && colourPattern.test(v["fallback"]))
|
|
6965
|
+
return { brand: v["brand"], fallback: v["fallback"] };
|
|
6966
|
+
this.problem(`${name} is not a colour`);
|
|
6967
|
+
return { color: "#000000" };
|
|
6968
|
+
}
|
|
6969
|
+
paintOrNull(value, name) {
|
|
6970
|
+
return value === null || value === void 0 ? null : this.paint(value, name);
|
|
6971
|
+
}
|
|
6972
|
+
path(value, name) {
|
|
6973
|
+
return this.string(value, name, 200, dataPathPattern);
|
|
6974
|
+
}
|
|
6975
|
+
};
|
|
6976
|
+
function designBytes(value) {
|
|
6977
|
+
return new TextEncoder().encode(JSON.stringify(value) ?? "").length;
|
|
6978
|
+
}
|
|
6979
|
+
function parseDesign(value) {
|
|
6980
|
+
const root = new Reader("design");
|
|
6981
|
+
const v = value;
|
|
6982
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
6983
|
+
return { design: null, problems: ["design: not an object"] };
|
|
6984
|
+
if (designBytes(v) > DESIGN_LIMITS.bytes)
|
|
6985
|
+
return {
|
|
6986
|
+
design: null,
|
|
6987
|
+
problems: [`design: larger than ${DESIGN_LIMITS.bytes / 1024} kB`]
|
|
6988
|
+
};
|
|
6989
|
+
if (v["version"] !== DESIGN_VERSION)
|
|
6990
|
+
root.problem(`version must be ${DESIGN_VERSION}`);
|
|
6991
|
+
const output = typeof v["output"] === "string" ? v["output"].slice(0, 64) : "";
|
|
6992
|
+
const background = root.paintOrNull(v["background"], "background");
|
|
6993
|
+
const rawLayers = Array.isArray(v["layers"]) ? v["layers"] : null;
|
|
6994
|
+
if (!rawLayers) root.problem("layers must be a list");
|
|
6995
|
+
if (rawLayers && rawLayers.length > DESIGN_LIMITS.layers)
|
|
6996
|
+
root.problem(`at most ${DESIGN_LIMITS.layers} layers`);
|
|
6997
|
+
const problems = [...root.problems];
|
|
6998
|
+
const layers = [];
|
|
6999
|
+
const ids = /* @__PURE__ */ new Set();
|
|
7000
|
+
for (const [index, raw] of (rawLayers ?? []).slice(0, DESIGN_LIMITS.layers).entries()) {
|
|
7001
|
+
const id = raw?.["id"];
|
|
7002
|
+
const reader = new Reader(
|
|
7003
|
+
`layer ${typeof id === "string" ? id : index + 1}`
|
|
7004
|
+
);
|
|
7005
|
+
const layer = readLayer(reader, raw);
|
|
7006
|
+
if (layer && ids.has(layer.id)) reader.problem("id is used twice");
|
|
7007
|
+
if (layer) ids.add(layer.id);
|
|
7008
|
+
problems.push(...reader.problems);
|
|
7009
|
+
if (layer) layers.push(layer);
|
|
7010
|
+
}
|
|
7011
|
+
let guides;
|
|
7012
|
+
const g = v["guides"];
|
|
7013
|
+
if (g && typeof g === "object") {
|
|
7014
|
+
const list2 = (x) => (Array.isArray(x) ? x : []).filter((n) => typeof n === "number" && Number.isFinite(n)).slice(0, 100);
|
|
7015
|
+
guides = { x: list2(g["x"]), y: list2(g["y"]) };
|
|
7016
|
+
}
|
|
7017
|
+
if (problems.length) return { design: null, problems };
|
|
7018
|
+
return {
|
|
7019
|
+
design: {
|
|
7020
|
+
version: DESIGN_VERSION,
|
|
7021
|
+
output,
|
|
7022
|
+
background,
|
|
7023
|
+
layers,
|
|
7024
|
+
...guides ? { guides } : {}
|
|
7025
|
+
},
|
|
7026
|
+
problems: []
|
|
7027
|
+
};
|
|
7028
|
+
}
|
|
7029
|
+
function readLayer(r, raw) {
|
|
7030
|
+
const v = raw;
|
|
7031
|
+
if (!v || typeof v !== "object") {
|
|
7032
|
+
r.problem("not an object");
|
|
7033
|
+
return null;
|
|
7034
|
+
}
|
|
7035
|
+
const base = {
|
|
7036
|
+
id: r.string(v["id"], "id", 32, layerIdPattern),
|
|
7037
|
+
name: typeof v["name"] === "string" && v["name"].trim() ? v["name"].slice(0, 100) : String(v["id"] ?? ""),
|
|
7038
|
+
x: r.number(v["x"], "x", -1e4, 1e4),
|
|
7039
|
+
y: r.number(v["y"], "y", -1e4, 1e4),
|
|
7040
|
+
width: r.number(v["width"], "width", 1, 1e4),
|
|
7041
|
+
height: r.number(v["height"], "height", 1, 1e4),
|
|
7042
|
+
rotation: r.number(v["rotation"], "rotation", -360, 360, 0),
|
|
7043
|
+
opacity: r.number(v["opacity"], "opacity", 0, 1, 1),
|
|
7044
|
+
visible: r.bool(v["visible"], true),
|
|
7045
|
+
...v["locked"] === true ? { locked: true } : {}
|
|
7046
|
+
};
|
|
7047
|
+
const showWhen = v["showWhen"];
|
|
7048
|
+
if (showWhen) base.showWhen = { path: r.path(showWhen["path"], "showWhen") };
|
|
7049
|
+
if (v["radius"] !== void 0)
|
|
7050
|
+
base.radius = r.number(v["radius"], "radius", 0, 5e3);
|
|
7051
|
+
if (v["shadow"]) base.shadow = readShadow(r, v["shadow"]);
|
|
7052
|
+
if (v["stroke"]) base.stroke = readStroke(r, v["stroke"]);
|
|
7053
|
+
switch (v["type"]) {
|
|
7054
|
+
case "text": {
|
|
7055
|
+
const style2 = readStyle(r, v["style"]);
|
|
7056
|
+
const layer = {
|
|
7057
|
+
...base,
|
|
7058
|
+
type: "text",
|
|
7059
|
+
content: readContent(r, v["content"]),
|
|
7060
|
+
style: style2,
|
|
7061
|
+
sizing: r.oneOf(
|
|
7062
|
+
v["sizing"],
|
|
7063
|
+
"sizing",
|
|
7064
|
+
["fixed", "shrink", "grow"],
|
|
7065
|
+
"fixed"
|
|
7066
|
+
),
|
|
7067
|
+
valign: r.oneOf(
|
|
7068
|
+
v["valign"],
|
|
7069
|
+
"valign",
|
|
7070
|
+
["top", "middle", "bottom"],
|
|
7071
|
+
"top"
|
|
7072
|
+
)
|
|
7073
|
+
};
|
|
7074
|
+
if (v["minFontSize"] !== void 0)
|
|
7075
|
+
layer.minFontSize = r.number(
|
|
7076
|
+
v["minFontSize"],
|
|
7077
|
+
"minFontSize",
|
|
7078
|
+
1,
|
|
7079
|
+
style2.size
|
|
7080
|
+
);
|
|
7081
|
+
if (v["maxLines"] !== void 0)
|
|
7082
|
+
layer.maxLines = Math.round(
|
|
7083
|
+
r.number(v["maxLines"], "maxLines", 1, 100)
|
|
7084
|
+
);
|
|
7085
|
+
if (v["fill"] !== void 0)
|
|
7086
|
+
layer.fill = r.paintOrNull(v["fill"], "fill");
|
|
7087
|
+
if (v["padding"] !== void 0)
|
|
7088
|
+
layer.padding = r.number(v["padding"], "padding", 0, 1e3);
|
|
7089
|
+
return layer;
|
|
7090
|
+
}
|
|
7091
|
+
case "image": {
|
|
7092
|
+
const layer = {
|
|
7093
|
+
...base,
|
|
7094
|
+
type: "image",
|
|
7095
|
+
source: readImageSource(r, v["source"]),
|
|
7096
|
+
fit: r.oneOf(
|
|
7097
|
+
v["fit"],
|
|
7098
|
+
"fit",
|
|
7099
|
+
["cover", "contain", "fill"],
|
|
7100
|
+
"cover"
|
|
7101
|
+
)
|
|
7102
|
+
};
|
|
7103
|
+
const focus = v["focus"];
|
|
7104
|
+
if (focus)
|
|
7105
|
+
layer.focus = {
|
|
7106
|
+
x: r.number(focus["x"], "focus", 0, 100),
|
|
7107
|
+
y: r.number(focus["y"], "focus", 0, 100)
|
|
7108
|
+
};
|
|
7109
|
+
return layer;
|
|
7110
|
+
}
|
|
7111
|
+
case "shape":
|
|
7112
|
+
return {
|
|
7113
|
+
...base,
|
|
7114
|
+
type: "shape",
|
|
7115
|
+
shape: r.oneOf(v["shape"], "shape", [
|
|
7116
|
+
"rectangle",
|
|
7117
|
+
"ellipse",
|
|
7118
|
+
"line"
|
|
7119
|
+
]),
|
|
7120
|
+
fill: r.paintOrNull(v["fill"], "fill")
|
|
7121
|
+
};
|
|
7122
|
+
case "qr":
|
|
7123
|
+
return {
|
|
7124
|
+
...base,
|
|
7125
|
+
type: "qr",
|
|
7126
|
+
value: readBinding(r, v["value"]),
|
|
7127
|
+
ecc: r.oneOf(v["ecc"], "ecc", ["L", "M", "Q", "H"], "M"),
|
|
7128
|
+
color: r.paint(v["color"] ?? { color: "#000000" }, "color"),
|
|
7129
|
+
background: r.paintOrNull(v["background"], "background")
|
|
7130
|
+
};
|
|
7131
|
+
case "barcode":
|
|
7132
|
+
return {
|
|
7133
|
+
...base,
|
|
7134
|
+
type: "barcode",
|
|
7135
|
+
value: readBinding(r, v["value"]),
|
|
7136
|
+
symbology: r.oneOf(v["symbology"], "symbology", symbologies, "code128"),
|
|
7137
|
+
showText: r.bool(v["showText"], true)
|
|
7138
|
+
};
|
|
7139
|
+
default:
|
|
7140
|
+
r.problem("unknown layer type");
|
|
7141
|
+
return null;
|
|
7142
|
+
}
|
|
7143
|
+
}
|
|
7144
|
+
function readShadow(r, raw) {
|
|
7145
|
+
const v = raw ?? {};
|
|
7146
|
+
const color = r.string(v["color"], "shadow colour", 9, colourPattern) || "#00000040";
|
|
7147
|
+
return {
|
|
7148
|
+
x: r.number(v["x"], "shadow x", -500, 500, 0),
|
|
7149
|
+
y: r.number(v["y"], "shadow y", -500, 500, 0),
|
|
7150
|
+
blur: r.number(v["blur"], "shadow blur", 0, 500, 0),
|
|
7151
|
+
color
|
|
7152
|
+
};
|
|
7153
|
+
}
|
|
7154
|
+
function readStroke(r, raw) {
|
|
7155
|
+
const v = raw ?? {};
|
|
7156
|
+
return {
|
|
7157
|
+
width: r.number(v["width"], "stroke width", 0, 200),
|
|
7158
|
+
paint: r.paint(v["paint"], "stroke colour")
|
|
7159
|
+
};
|
|
7160
|
+
}
|
|
7161
|
+
function readStyle(r, raw) {
|
|
7162
|
+
const v = raw ?? {};
|
|
7163
|
+
const style2 = {
|
|
7164
|
+
font: readFont(r, v["font"]),
|
|
7165
|
+
weight: Math.round(r.number(v["weight"], "weight", 100, 900, 400) / 100) * 100,
|
|
7166
|
+
size: r.number(v["size"], "size", 1, 2e3),
|
|
7167
|
+
lineHeight: r.number(v["lineHeight"], "lineHeight", 0.5, 5, 1.2),
|
|
7168
|
+
letterSpacing: r.number(v["letterSpacing"], "letterSpacing", -50, 200, 0),
|
|
7169
|
+
color: r.paint(v["color"], "colour"),
|
|
7170
|
+
align: r.oneOf(
|
|
7171
|
+
v["align"],
|
|
7172
|
+
"align",
|
|
7173
|
+
["left", "center", "right", "justify"],
|
|
7174
|
+
"left"
|
|
7175
|
+
)
|
|
7176
|
+
};
|
|
7177
|
+
if (v["italic"] === true) style2.italic = true;
|
|
7178
|
+
if (v["uppercase"] === true) style2.uppercase = true;
|
|
7179
|
+
if (v["underline"] === true) style2.underline = true;
|
|
7180
|
+
if (v["strike"] === true) style2.strike = true;
|
|
7181
|
+
return style2;
|
|
7182
|
+
}
|
|
7183
|
+
function readFont(r, raw) {
|
|
7184
|
+
const v = raw ?? {};
|
|
7185
|
+
if (v["brand"] === "heading" || v["brand"] === "body")
|
|
7186
|
+
return { brand: v["brand"] };
|
|
7187
|
+
const family = r.string(v["family"], "font", 100, /^[^"'\\<>\n\r;{}]+$/);
|
|
7188
|
+
return {
|
|
7189
|
+
family,
|
|
7190
|
+
source: r.oneOf(
|
|
7191
|
+
v["source"],
|
|
7192
|
+
"font source",
|
|
7193
|
+
["google", "organisation", "system"],
|
|
7194
|
+
"system"
|
|
7195
|
+
)
|
|
7196
|
+
};
|
|
7197
|
+
}
|
|
7198
|
+
function readContent(r, raw) {
|
|
7199
|
+
if (!Array.isArray(raw)) {
|
|
7200
|
+
r.problem("content must be a list");
|
|
7201
|
+
return [];
|
|
7202
|
+
}
|
|
7203
|
+
let length = 0;
|
|
7204
|
+
const items = [];
|
|
7205
|
+
for (const item of raw) {
|
|
7206
|
+
if (item && typeof item["text"] === "string") {
|
|
7207
|
+
length += item["text"].length;
|
|
7208
|
+
items.push({ text: item["text"] });
|
|
7209
|
+
} else if (item && item["break"] === true) items.push({ break: true });
|
|
7210
|
+
else if (item && typeof item["path"] === "string") {
|
|
7211
|
+
const path = r.path(item["path"], "content path");
|
|
7212
|
+
const format = item["format"] === void 0 ? void 0 : readFormat(r, item["format"]);
|
|
7213
|
+
items.push(format ? { path, format } : { path });
|
|
7214
|
+
} else
|
|
7215
|
+
r.problem("content holds something that is not text, a path or a break");
|
|
7216
|
+
}
|
|
7217
|
+
if (length > 5e3) r.problem("at most 5000 characters of text");
|
|
7218
|
+
return items;
|
|
7219
|
+
}
|
|
7220
|
+
function readFormat(r, raw) {
|
|
7221
|
+
const v = raw ?? {};
|
|
7222
|
+
switch (v["helper"]) {
|
|
7223
|
+
case "upper":
|
|
7224
|
+
case "lower":
|
|
7225
|
+
case "title":
|
|
7226
|
+
case "money":
|
|
7227
|
+
return { helper: v["helper"] };
|
|
7228
|
+
case "number":
|
|
7229
|
+
return {
|
|
7230
|
+
helper: "number",
|
|
7231
|
+
decimals: Math.round(r.number(v["decimals"], "decimals", 0, 10, 2))
|
|
7232
|
+
};
|
|
7233
|
+
case "date":
|
|
7234
|
+
return {
|
|
7235
|
+
helper: "date",
|
|
7236
|
+
pattern: r.string(v["pattern"], "date pattern", 64, /^[^{}\n\r]+$/)
|
|
7237
|
+
};
|
|
7238
|
+
case "truncate":
|
|
7239
|
+
return {
|
|
7240
|
+
helper: "truncate",
|
|
7241
|
+
length: Math.round(r.number(v["length"], "length", 1, 5e3))
|
|
7242
|
+
};
|
|
7243
|
+
case "default":
|
|
7244
|
+
return {
|
|
7245
|
+
helper: "default",
|
|
7246
|
+
value: r.string(v["value"], "default value", 500, /^[^{}\n\r]*$/)
|
|
7247
|
+
};
|
|
7248
|
+
default:
|
|
7249
|
+
r.problem("unknown format");
|
|
7250
|
+
return void 0;
|
|
7251
|
+
}
|
|
7252
|
+
}
|
|
7253
|
+
function readImageSource(r, raw) {
|
|
7254
|
+
const v = raw ?? {};
|
|
7255
|
+
if (v["url"] === "") return { url: "" };
|
|
7256
|
+
if (v["asset"] === "") return { asset: "" };
|
|
7257
|
+
if (v["path"] === "") return { path: "" };
|
|
7258
|
+
if (typeof v["url"] === "string")
|
|
7259
|
+
return {
|
|
7260
|
+
url: r.string(v["url"], "image url", 2e3, /^https?:\/\/[^\s"'<>]+$/)
|
|
7261
|
+
};
|
|
7262
|
+
if (typeof v["asset"] === "string")
|
|
7263
|
+
return {
|
|
7264
|
+
asset: r.string(v["asset"], "file name", 200, /^[^"\\<>\n\r{}]+$/)
|
|
7265
|
+
};
|
|
7266
|
+
if (typeof v["path"] === "string")
|
|
7267
|
+
return { path: r.path(v["path"], "image path") };
|
|
7268
|
+
if (v["brandLogo"] !== void 0)
|
|
7269
|
+
return {
|
|
7270
|
+
brandLogo: r.oneOf(v["brandLogo"], "brand logo", [
|
|
7271
|
+
"primary",
|
|
7272
|
+
"inverse",
|
|
7273
|
+
"mark"
|
|
7274
|
+
])
|
|
7275
|
+
};
|
|
7276
|
+
r.problem("the image has no source");
|
|
7277
|
+
return { url: "" };
|
|
7278
|
+
}
|
|
7279
|
+
function readBinding(r, raw) {
|
|
7280
|
+
const v = raw ?? {};
|
|
7281
|
+
if (typeof v["path"] === "string")
|
|
7282
|
+
return { path: r.path(v["path"], "value path") };
|
|
7283
|
+
if (typeof v["text"] === "string")
|
|
7284
|
+
return { text: r.string(v["text"], "value", 2e3, /^[^\n\r{}]*$/) };
|
|
7285
|
+
r.problem("the code has no value");
|
|
7286
|
+
return { text: "" };
|
|
7287
|
+
}
|
|
7288
|
+
|
|
7289
|
+
// packages/engine/src/lib/design/fonts.ts
|
|
7290
|
+
var range = (from, to) => Array.from({ length: (to - from) / 100 + 1 }, (_, i) => from + i * 100);
|
|
7291
|
+
var googleFonts = [
|
|
7292
|
+
{
|
|
7293
|
+
family: "Inter",
|
|
7294
|
+
category: "sans-serif",
|
|
7295
|
+
weights: range(100, 900),
|
|
7296
|
+
italic: true
|
|
7297
|
+
},
|
|
7298
|
+
{
|
|
7299
|
+
family: "Roboto",
|
|
7300
|
+
category: "sans-serif",
|
|
7301
|
+
weights: range(100, 900),
|
|
7302
|
+
italic: true
|
|
7303
|
+
},
|
|
7304
|
+
{
|
|
7305
|
+
family: "Open Sans",
|
|
7306
|
+
category: "sans-serif",
|
|
7307
|
+
weights: range(300, 800),
|
|
7308
|
+
italic: true
|
|
7309
|
+
},
|
|
7310
|
+
{
|
|
7311
|
+
family: "Lato",
|
|
7312
|
+
category: "sans-serif",
|
|
7313
|
+
weights: [100, 300, 400, 700, 900],
|
|
7314
|
+
italic: true
|
|
7315
|
+
},
|
|
7316
|
+
{
|
|
7317
|
+
family: "Montserrat",
|
|
7318
|
+
category: "sans-serif",
|
|
7319
|
+
weights: range(100, 900),
|
|
7320
|
+
italic: true
|
|
7321
|
+
},
|
|
7322
|
+
{
|
|
7323
|
+
family: "Poppins",
|
|
7324
|
+
category: "sans-serif",
|
|
7325
|
+
weights: range(100, 900),
|
|
7326
|
+
italic: true
|
|
7327
|
+
},
|
|
7328
|
+
{
|
|
7329
|
+
family: "Source Sans 3",
|
|
7330
|
+
category: "sans-serif",
|
|
7331
|
+
weights: range(200, 900),
|
|
7332
|
+
italic: true
|
|
7333
|
+
},
|
|
7334
|
+
{
|
|
7335
|
+
family: "Nunito",
|
|
7336
|
+
category: "sans-serif",
|
|
7337
|
+
weights: range(200, 900),
|
|
7338
|
+
italic: true
|
|
7339
|
+
},
|
|
7340
|
+
{
|
|
7341
|
+
family: "Raleway",
|
|
7342
|
+
category: "sans-serif",
|
|
7343
|
+
weights: range(100, 900),
|
|
7344
|
+
italic: true
|
|
7345
|
+
},
|
|
7346
|
+
{
|
|
7347
|
+
family: "Work Sans",
|
|
7348
|
+
category: "sans-serif",
|
|
7349
|
+
weights: range(100, 900),
|
|
7350
|
+
italic: true
|
|
7351
|
+
},
|
|
7352
|
+
{
|
|
7353
|
+
family: "DM Sans",
|
|
7354
|
+
category: "sans-serif",
|
|
7355
|
+
weights: range(100, 900),
|
|
7356
|
+
italic: true
|
|
7357
|
+
},
|
|
7358
|
+
{
|
|
7359
|
+
family: "Manrope",
|
|
7360
|
+
category: "sans-serif",
|
|
7361
|
+
weights: range(200, 800),
|
|
7362
|
+
italic: false
|
|
7363
|
+
},
|
|
7364
|
+
{
|
|
7365
|
+
family: "Rubik",
|
|
7366
|
+
category: "sans-serif",
|
|
7367
|
+
weights: range(300, 900),
|
|
7368
|
+
italic: true
|
|
7369
|
+
},
|
|
7370
|
+
{
|
|
7371
|
+
family: "Noto Sans",
|
|
7372
|
+
category: "sans-serif",
|
|
7373
|
+
weights: range(100, 900),
|
|
7374
|
+
italic: true
|
|
7375
|
+
},
|
|
7376
|
+
{
|
|
7377
|
+
family: "IBM Plex Sans",
|
|
7378
|
+
category: "sans-serif",
|
|
7379
|
+
weights: range(100, 700),
|
|
7380
|
+
italic: true
|
|
7381
|
+
},
|
|
7382
|
+
{
|
|
7383
|
+
family: "Space Grotesk",
|
|
7384
|
+
category: "sans-serif",
|
|
7385
|
+
weights: range(300, 700),
|
|
7386
|
+
italic: false
|
|
7387
|
+
},
|
|
7388
|
+
{
|
|
7389
|
+
family: "Outfit",
|
|
7390
|
+
category: "sans-serif",
|
|
7391
|
+
weights: range(100, 900),
|
|
7392
|
+
italic: false
|
|
7393
|
+
},
|
|
7394
|
+
{
|
|
7395
|
+
family: "Plus Jakarta Sans",
|
|
7396
|
+
category: "sans-serif",
|
|
7397
|
+
weights: range(200, 800),
|
|
7398
|
+
italic: true
|
|
7399
|
+
},
|
|
7400
|
+
{
|
|
7401
|
+
family: "Figtree",
|
|
7402
|
+
category: "sans-serif",
|
|
7403
|
+
weights: range(300, 900),
|
|
7404
|
+
italic: true
|
|
7405
|
+
},
|
|
7406
|
+
{
|
|
7407
|
+
family: "Karla",
|
|
7408
|
+
category: "sans-serif",
|
|
7409
|
+
weights: range(200, 800),
|
|
7410
|
+
italic: true
|
|
7411
|
+
},
|
|
7412
|
+
{
|
|
7413
|
+
family: "Barlow",
|
|
7414
|
+
category: "sans-serif",
|
|
7415
|
+
weights: range(100, 900),
|
|
7416
|
+
italic: true
|
|
7417
|
+
},
|
|
7418
|
+
{
|
|
7419
|
+
family: "Archivo",
|
|
7420
|
+
category: "sans-serif",
|
|
7421
|
+
weights: range(100, 900),
|
|
7422
|
+
italic: true
|
|
7423
|
+
},
|
|
7424
|
+
{
|
|
7425
|
+
family: "Oswald",
|
|
7426
|
+
category: "display",
|
|
7427
|
+
weights: range(200, 700),
|
|
7428
|
+
italic: false
|
|
7429
|
+
},
|
|
7430
|
+
{ family: "Bebas Neue", category: "display", weights: [400], italic: false },
|
|
7431
|
+
{ family: "Anton", category: "display", weights: [400], italic: false },
|
|
7432
|
+
{
|
|
7433
|
+
family: "Playfair Display",
|
|
7434
|
+
category: "serif",
|
|
7435
|
+
weights: range(400, 900),
|
|
7436
|
+
italic: true
|
|
7437
|
+
},
|
|
7438
|
+
{
|
|
7439
|
+
family: "Merriweather",
|
|
7440
|
+
category: "serif",
|
|
7441
|
+
weights: range(300, 900),
|
|
7442
|
+
italic: true
|
|
7443
|
+
},
|
|
7444
|
+
{ family: "Lora", category: "serif", weights: range(400, 700), italic: true },
|
|
7445
|
+
{
|
|
7446
|
+
family: "Libre Baskerville",
|
|
7447
|
+
category: "serif",
|
|
7448
|
+
weights: [400, 700],
|
|
7449
|
+
italic: true
|
|
7450
|
+
},
|
|
7451
|
+
{
|
|
7452
|
+
family: "Source Serif 4",
|
|
7453
|
+
category: "serif",
|
|
7454
|
+
weights: range(200, 900),
|
|
7455
|
+
italic: true
|
|
7456
|
+
},
|
|
7457
|
+
{
|
|
7458
|
+
family: "EB Garamond",
|
|
7459
|
+
category: "serif",
|
|
7460
|
+
weights: range(400, 800),
|
|
7461
|
+
italic: true
|
|
7462
|
+
},
|
|
7463
|
+
{
|
|
7464
|
+
family: "Cormorant Garamond",
|
|
7465
|
+
category: "serif",
|
|
7466
|
+
weights: range(300, 700),
|
|
7467
|
+
italic: true
|
|
7468
|
+
},
|
|
7469
|
+
{
|
|
7470
|
+
family: "DM Serif Display",
|
|
7471
|
+
category: "serif",
|
|
7472
|
+
weights: [400],
|
|
7473
|
+
italic: true
|
|
7474
|
+
},
|
|
7475
|
+
{
|
|
7476
|
+
family: "Fraunces",
|
|
7477
|
+
category: "serif",
|
|
7478
|
+
weights: range(100, 900),
|
|
7479
|
+
italic: true
|
|
7480
|
+
},
|
|
7481
|
+
{
|
|
7482
|
+
family: "Roboto Slab",
|
|
7483
|
+
category: "serif",
|
|
7484
|
+
weights: range(100, 900),
|
|
7485
|
+
italic: false
|
|
7486
|
+
},
|
|
7487
|
+
{
|
|
7488
|
+
family: "Zilla Slab",
|
|
7489
|
+
category: "serif",
|
|
7490
|
+
weights: [300, 400, 500, 600, 700],
|
|
7491
|
+
italic: true
|
|
7492
|
+
},
|
|
7493
|
+
{
|
|
7494
|
+
family: "JetBrains Mono",
|
|
7495
|
+
category: "monospace",
|
|
7496
|
+
weights: range(100, 800),
|
|
7497
|
+
italic: true
|
|
7498
|
+
},
|
|
7499
|
+
{
|
|
7500
|
+
family: "IBM Plex Mono",
|
|
7501
|
+
category: "monospace",
|
|
7502
|
+
weights: range(100, 700),
|
|
7503
|
+
italic: true
|
|
7504
|
+
},
|
|
7505
|
+
{
|
|
7506
|
+
family: "Space Mono",
|
|
7507
|
+
category: "monospace",
|
|
7508
|
+
weights: [400, 700],
|
|
7509
|
+
italic: true
|
|
7510
|
+
},
|
|
7511
|
+
{
|
|
7512
|
+
family: "Caveat",
|
|
7513
|
+
category: "handwriting",
|
|
7514
|
+
weights: range(400, 700),
|
|
7515
|
+
italic: false
|
|
7516
|
+
},
|
|
7517
|
+
{
|
|
7518
|
+
family: "Pacifico",
|
|
7519
|
+
category: "handwriting",
|
|
7520
|
+
weights: [400],
|
|
7521
|
+
italic: false
|
|
7522
|
+
},
|
|
7523
|
+
{
|
|
7524
|
+
family: "Dancing Script",
|
|
7525
|
+
category: "handwriting",
|
|
7526
|
+
weights: range(400, 700),
|
|
7527
|
+
italic: false
|
|
7528
|
+
}
|
|
7529
|
+
];
|
|
7530
|
+
var byFamily = new Map(googleFonts.map((font) => [font.family, font]));
|
|
7531
|
+
function googleFont(family) {
|
|
7532
|
+
return byFamily.get(family);
|
|
7533
|
+
}
|
|
7534
|
+
function genericFamily(font) {
|
|
7535
|
+
if (!font) return "sans-serif";
|
|
7536
|
+
return font.category === "serif" ? "serif" : font.category === "monospace" ? "monospace" : font.category === "handwriting" ? "cursive" : "sans-serif";
|
|
7537
|
+
}
|
|
7538
|
+
function nearestWeight(font, weight) {
|
|
7539
|
+
return font.weights.reduce(
|
|
7540
|
+
(best, w) => Math.abs(w - weight) < Math.abs(best - weight) || Math.abs(w - weight) === Math.abs(best - weight) && w > best ? w : best,
|
|
7541
|
+
font.weights[0] ?? 400
|
|
7542
|
+
);
|
|
7543
|
+
}
|
|
7544
|
+
function googleFontsUrl(uses) {
|
|
7545
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
7546
|
+
for (const use of uses) {
|
|
7547
|
+
const font = byFamily.get(use.family);
|
|
7548
|
+
if (!font) continue;
|
|
7549
|
+
const italic = use.italic && font.italic ? 1 : 0;
|
|
7550
|
+
const set = wanted.get(font.family) ?? /* @__PURE__ */ new Set();
|
|
7551
|
+
set.add(`${italic},${nearestWeight(font, use.weight)}`);
|
|
7552
|
+
wanted.set(font.family, set);
|
|
7553
|
+
}
|
|
7554
|
+
if (!wanted.size) return null;
|
|
7555
|
+
const families = [...wanted.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([family, set]) => {
|
|
7556
|
+
const tuples = [...set].map((t) => t.split(",").map(Number)).sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
7557
|
+
const name = family.replace(/ /g, "+");
|
|
7558
|
+
return tuples.some(([italic]) => italic) ? `family=${name}:ital,wght@${tuples.map(([i, w]) => `${i},${w}`).join(";")}` : `family=${name}:wght@${tuples.map(([, w]) => w).join(";")}`;
|
|
7559
|
+
});
|
|
7560
|
+
return `https://fonts.googleapis.com/css2?${families.join("&")}&display=block`;
|
|
7561
|
+
}
|
|
7562
|
+
|
|
7563
|
+
// packages/engine/src/lib/design/hash.ts
|
|
7564
|
+
function designOutputHash(html, css2) {
|
|
7565
|
+
const text2 = `${html}\0${css2}`;
|
|
7566
|
+
let a = 2166136261;
|
|
7567
|
+
let b = 16777619 ^ 2654435769;
|
|
7568
|
+
for (let i = 0; i < text2.length; i++) {
|
|
7569
|
+
const code = text2.charCodeAt(i);
|
|
7570
|
+
if (code === 13) continue;
|
|
7571
|
+
a = Math.imul(a ^ code, 16777619) >>> 0;
|
|
7572
|
+
b = Math.imul(b ^ code, 16777619) >>> 0;
|
|
7573
|
+
b = b + (a >>> 13) >>> 0;
|
|
7574
|
+
}
|
|
7575
|
+
return a.toString(16).padStart(8, "0") + b.toString(16).padStart(8, "0");
|
|
7576
|
+
}
|
|
7577
|
+
|
|
7578
|
+
// packages/engine/src/lib/design/layers.ts
|
|
7579
|
+
function imageIsEmpty(source) {
|
|
7580
|
+
if ("url" in source) return source.url === "";
|
|
7581
|
+
if ("asset" in source) return source.asset === "";
|
|
7582
|
+
if ("path" in source) return source.path === "";
|
|
7583
|
+
return false;
|
|
7584
|
+
}
|
|
7585
|
+
function emptyDesign() {
|
|
7586
|
+
return {
|
|
7587
|
+
version: DESIGN_VERSION,
|
|
7588
|
+
output: "",
|
|
7589
|
+
background: { color: "#ffffff" },
|
|
7590
|
+
layers: []
|
|
7591
|
+
};
|
|
7592
|
+
}
|
|
7593
|
+
function nextLayerId(design2, type) {
|
|
7594
|
+
const taken = new Set(design2.layers.map((layer) => layer.id));
|
|
7595
|
+
for (let n = 1; ; n++) {
|
|
7596
|
+
const id = `${type}-${n}`;
|
|
7597
|
+
if (!taken.has(id)) return id;
|
|
7598
|
+
}
|
|
7599
|
+
}
|
|
7600
|
+
var baseText = {
|
|
7601
|
+
type: "text",
|
|
7602
|
+
rotation: 0,
|
|
7603
|
+
opacity: 1,
|
|
7604
|
+
visible: true,
|
|
7605
|
+
content: [{ text: "Text" }],
|
|
7606
|
+
style: {
|
|
7607
|
+
font: { family: "Inter", source: "google" },
|
|
7608
|
+
// regular: the editor's Bold is a toggle, and a new text that was already bold confused it
|
|
7609
|
+
weight: 400,
|
|
7610
|
+
size: 48,
|
|
7611
|
+
lineHeight: 1.2,
|
|
7612
|
+
letterSpacing: 0,
|
|
7613
|
+
color: { color: "#111827" },
|
|
7614
|
+
align: "left"
|
|
7615
|
+
},
|
|
7616
|
+
sizing: "fixed",
|
|
7617
|
+
valign: "top"
|
|
7618
|
+
};
|
|
7619
|
+
function defaultLayer(type, id, name, canvas) {
|
|
7620
|
+
const centred = (width, height) => ({
|
|
7621
|
+
x: Math.round((canvas.width - width) / 2),
|
|
7622
|
+
y: Math.round((canvas.height - height) / 2),
|
|
7623
|
+
width,
|
|
7624
|
+
height
|
|
7625
|
+
});
|
|
7626
|
+
const common = { id, name, rotation: 0, opacity: 1, visible: true };
|
|
7627
|
+
switch (type) {
|
|
7628
|
+
case "text":
|
|
7629
|
+
return {
|
|
7630
|
+
...baseText,
|
|
7631
|
+
...common,
|
|
7632
|
+
...centred(Math.min(canvas.width - 80, 600), 80)
|
|
7633
|
+
};
|
|
7634
|
+
case "image":
|
|
7635
|
+
return {
|
|
7636
|
+
...common,
|
|
7637
|
+
type: "image",
|
|
7638
|
+
...centred(Math.min(canvas.width, 400), Math.min(canvas.height, 300)),
|
|
7639
|
+
// not chosen yet (`imageIsEmpty`), and the file library first: that is where an upload goes
|
|
7640
|
+
source: { asset: "" },
|
|
7641
|
+
fit: "cover"
|
|
7642
|
+
};
|
|
7643
|
+
case "shape":
|
|
7644
|
+
return {
|
|
7645
|
+
...common,
|
|
7646
|
+
type: "shape",
|
|
7647
|
+
...centred(240, 160),
|
|
7648
|
+
shape: "rectangle",
|
|
7649
|
+
fill: { color: "#e5e7eb" }
|
|
7650
|
+
};
|
|
7651
|
+
case "qr":
|
|
7652
|
+
return {
|
|
7653
|
+
...common,
|
|
7654
|
+
type: "qr",
|
|
7655
|
+
...centred(240, 240),
|
|
7656
|
+
value: { text: "https://example.test" },
|
|
7657
|
+
ecc: "M",
|
|
7658
|
+
color: { color: "#000000" },
|
|
7659
|
+
background: { color: "#ffffff" }
|
|
7660
|
+
};
|
|
7661
|
+
case "barcode":
|
|
7662
|
+
return {
|
|
7663
|
+
...common,
|
|
7664
|
+
type: "barcode",
|
|
7665
|
+
...centred(400, 140),
|
|
7666
|
+
value: { text: "4006381333931" },
|
|
7667
|
+
symbology: "ean13",
|
|
7668
|
+
showText: true
|
|
7669
|
+
};
|
|
7670
|
+
}
|
|
7671
|
+
}
|
|
7672
|
+
|
|
7673
|
+
// packages/engine/src/lib/design/emit.ts
|
|
7674
|
+
var colourPattern2 = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
7675
|
+
var tokenPattern2 = /^[a-z][a-z0-9-]{0,31}$/;
|
|
7676
|
+
function emitDesign(design2, engine, size) {
|
|
7677
|
+
const lines = ['<div class="ff-design">'];
|
|
7678
|
+
const map = {};
|
|
7679
|
+
const rules = [];
|
|
7680
|
+
for (const layer of design2.layers) {
|
|
7681
|
+
if (!layer.visible) continue;
|
|
7682
|
+
const markup = layerHtml(layer, engine);
|
|
7683
|
+
lines.push(
|
|
7684
|
+
` ${layer.showWhen?.path ? ifBlock(engine, layer.showWhen.path, markup) : markup}`
|
|
7685
|
+
);
|
|
7686
|
+
map[layer.id] = { start: lines.length, end: lines.length };
|
|
7687
|
+
rules.push(...layerCss(layer));
|
|
7688
|
+
}
|
|
7689
|
+
lines.push("</div>");
|
|
7690
|
+
const html = `${lines.join("\n")}
|
|
7691
|
+
`;
|
|
7692
|
+
const fonts = googleFontsUrl(
|
|
7693
|
+
design2.layers.filter(
|
|
7694
|
+
(l) => l.visible && l.type === "text" && "family" in l.style.font && l.style.font.source === "google"
|
|
7695
|
+
).map((l) => ({
|
|
7696
|
+
family: l.style.font.family,
|
|
7697
|
+
weight: l.style.weight,
|
|
7698
|
+
italic: l.style.italic === true
|
|
7699
|
+
}))
|
|
7700
|
+
);
|
|
7701
|
+
const head = [
|
|
7702
|
+
...fonts ? [`@import url("${fonts}");`] : [],
|
|
7703
|
+
`.ff-design { ${decl({
|
|
7704
|
+
position: "relative",
|
|
7705
|
+
width: px(size.width),
|
|
7706
|
+
height: px(size.height),
|
|
7707
|
+
overflow: "hidden",
|
|
7708
|
+
background: design2.background ? paint(design2.background) : void 0
|
|
7709
|
+
})} }`,
|
|
7710
|
+
".ff-layer { position: absolute; box-sizing: border-box; margin: 0; }"
|
|
7711
|
+
];
|
|
7712
|
+
const css2 = `${[...head, ...rules].join("\n")}
|
|
7713
|
+
`;
|
|
7714
|
+
return { html, css: css2, map, output: designOutputHash(html, css2) };
|
|
7715
|
+
}
|
|
7716
|
+
function open(layer, tag, extra = "") {
|
|
7717
|
+
return `<${tag} class="ff-layer l-${layer.id}" data-ff-layer="${layer.id}"${extra}`;
|
|
7718
|
+
}
|
|
7719
|
+
function layerHtml(layer, engine) {
|
|
7720
|
+
switch (layer.type) {
|
|
7721
|
+
case "text": {
|
|
7722
|
+
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)))}"` : "";
|
|
7723
|
+
return `${open(layer, "div", fit)}><p class="ff-text">${textHtml(layer.content, engine)}</p></div>`;
|
|
7724
|
+
}
|
|
7725
|
+
case "image":
|
|
7726
|
+
if (imageIsEmpty(layer.source)) return `${open(layer, "div")}></div>`;
|
|
7727
|
+
return `${open(layer, "img", ` src="${imageSource(layer.source, engine)}" alt=""`)}>`;
|
|
7728
|
+
case "shape":
|
|
7729
|
+
return `${open(layer, "div")}></div>`;
|
|
7730
|
+
case "qr":
|
|
7731
|
+
return `${open(
|
|
7732
|
+
layer,
|
|
7733
|
+
"img",
|
|
7734
|
+
` src="${helperCall(engine, "qrcode", binding(layer.value, engine), {
|
|
7735
|
+
ecc: stringLiteral(engine, layer.ecc),
|
|
7736
|
+
margin: "0",
|
|
7737
|
+
color: stringLiteral(engine, literalColour(layer.color)),
|
|
7738
|
+
background: stringLiteral(
|
|
7739
|
+
engine,
|
|
7740
|
+
layer.background ? literalColour(layer.background) : "#ffffff00"
|
|
7741
|
+
)
|
|
7742
|
+
})}" alt=""`
|
|
7743
|
+
)}>`;
|
|
7744
|
+
case "barcode":
|
|
7745
|
+
return `${open(
|
|
7746
|
+
layer,
|
|
7747
|
+
"img",
|
|
7748
|
+
` src="${helperCall(engine, "barcode", binding(layer.value, engine), {
|
|
7749
|
+
type: stringLiteral(engine, layer.symbology),
|
|
7750
|
+
text: layer.showText ? "true" : "false"
|
|
7751
|
+
})}" alt=""`
|
|
7752
|
+
)}>`;
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
function textHtml(items, engine) {
|
|
7756
|
+
return items.map((item) => {
|
|
7757
|
+
if ("break" in item) return "<br>";
|
|
7758
|
+
if ("text" in item)
|
|
7759
|
+
return encodeTemplateText(item.text.replace(/[\r\n]+/g, " "));
|
|
7760
|
+
return valueHtml(item.path, item.format, engine);
|
|
7761
|
+
}).join("");
|
|
7762
|
+
}
|
|
7763
|
+
function valueHtml(path, format, engine) {
|
|
7764
|
+
if (!format) return `{{ ${path} }}`;
|
|
7765
|
+
switch (format.helper) {
|
|
7766
|
+
case "upper":
|
|
7767
|
+
case "lower":
|
|
7768
|
+
case "title":
|
|
7769
|
+
case "money":
|
|
7770
|
+
return filterCall(engine, path, format.helper);
|
|
7771
|
+
case "number":
|
|
7772
|
+
return filterCall(engine, path, "number", [String(format.decimals)]);
|
|
7773
|
+
case "date":
|
|
7774
|
+
return filterCall(engine, path, "date", [
|
|
7775
|
+
stringLiteral(engine, format.pattern)
|
|
7776
|
+
]);
|
|
7777
|
+
case "truncate":
|
|
7778
|
+
return filterCall(engine, path, "truncate", [String(format.length)]);
|
|
7779
|
+
case "default":
|
|
7780
|
+
return filterCall(engine, path, "default", [
|
|
7781
|
+
stringLiteral(engine, format.value)
|
|
7782
|
+
]);
|
|
7783
|
+
}
|
|
7784
|
+
}
|
|
7785
|
+
function binding(value, engine) {
|
|
7786
|
+
return "path" in value ? value.path : stringLiteral(engine, value.text);
|
|
7787
|
+
}
|
|
7788
|
+
function imageSource(source, engine) {
|
|
7789
|
+
if ("url" in source) return escapeAttribute(source.url);
|
|
7790
|
+
if ("asset" in source)
|
|
7791
|
+
return helperCall(engine, "asset", stringLiteral(engine, source.asset));
|
|
7792
|
+
if ("path" in source) return `{{ ${source.path} }}`;
|
|
7793
|
+
return `{{ brand.logo.${source.brandLogo} }}`;
|
|
7794
|
+
}
|
|
7795
|
+
function layerCss(layer) {
|
|
7796
|
+
const text2 = layer.type === "text" ? layer : null;
|
|
7797
|
+
const box = {
|
|
7798
|
+
left: px(layer.x),
|
|
7799
|
+
top: px(layer.y),
|
|
7800
|
+
width: px(layer.width),
|
|
7801
|
+
height: text2?.sizing === "grow" ? void 0 : px(layer.height),
|
|
7802
|
+
opacity: layer.opacity < 1 ? num(layer.opacity) : void 0,
|
|
7803
|
+
transform: layer.rotation ? `rotate(${num(layer.rotation)}deg)` : void 0,
|
|
7804
|
+
"border-radius": layer.type === "shape" && layer.shape === "ellipse" ? "50%" : layer.radius ? px(layer.radius) : void 0,
|
|
7805
|
+
border: layer.stroke && layer.stroke.width > 0 ? `${px(layer.stroke.width)} solid ${paint(layer.stroke.paint)}` : void 0,
|
|
7806
|
+
"box-shadow": layer.shadow && !text2 ? shadow(layer.shadow) : void 0
|
|
7807
|
+
};
|
|
7808
|
+
if (text2) {
|
|
7809
|
+
Object.assign(box, {
|
|
7810
|
+
display: "flex",
|
|
7811
|
+
"flex-direction": "column",
|
|
7812
|
+
"justify-content": text2.valign === "middle" ? "center" : text2.valign === "bottom" ? "flex-end" : "flex-start",
|
|
7813
|
+
overflow: text2.sizing === "grow" ? void 0 : "hidden",
|
|
7814
|
+
padding: text2.padding ? px(text2.padding) : void 0,
|
|
7815
|
+
background: text2.fill ? paint(text2.fill) : void 0
|
|
7816
|
+
});
|
|
7817
|
+
const s = text2.style;
|
|
7818
|
+
const clamp = text2.maxLines ? {
|
|
7819
|
+
display: "-webkit-box",
|
|
7820
|
+
"-webkit-box-orient": "vertical",
|
|
7821
|
+
"-webkit-line-clamp": String(text2.maxLines),
|
|
7822
|
+
overflow: "hidden"
|
|
7823
|
+
} : {};
|
|
7824
|
+
const inner = decl({
|
|
7825
|
+
margin: "0",
|
|
7826
|
+
width: "100%",
|
|
7827
|
+
"font-family": fontFamily(s.font),
|
|
7828
|
+
"font-weight": String(servedWeight(s.font, s.weight)),
|
|
7829
|
+
"font-size": px(s.size),
|
|
7830
|
+
"line-height": num(s.lineHeight),
|
|
7831
|
+
"letter-spacing": s.letterSpacing ? px(s.letterSpacing) : void 0,
|
|
7832
|
+
color: paint(s.color),
|
|
7833
|
+
"text-align": s.align,
|
|
7834
|
+
"font-style": s.italic ? "italic" : void 0,
|
|
7835
|
+
"text-transform": s.uppercase ? "uppercase" : void 0,
|
|
7836
|
+
"text-decoration": [s.underline && "underline", s.strike && "line-through"].filter(Boolean).join(" ") || void 0,
|
|
7837
|
+
"text-shadow": text2.shadow ? shadow(text2.shadow) : void 0,
|
|
7838
|
+
"overflow-wrap": "break-word",
|
|
7839
|
+
...clamp
|
|
7840
|
+
});
|
|
7841
|
+
return [
|
|
7842
|
+
`.l-${layer.id} { ${decl(box)} }`,
|
|
7843
|
+
`.l-${layer.id} .ff-text { ${inner} }`
|
|
7844
|
+
];
|
|
7845
|
+
}
|
|
7846
|
+
if (layer.type === "image")
|
|
7847
|
+
Object.assign(box, {
|
|
7848
|
+
display: "block",
|
|
7849
|
+
"object-fit": layer.fit,
|
|
7850
|
+
"object-position": layer.focus ? `${num(layer.focus.x)}% ${num(layer.focus.y)}%` : void 0
|
|
7851
|
+
});
|
|
7852
|
+
if (layer.type === "qr" || layer.type === "barcode")
|
|
7853
|
+
Object.assign(box, { display: "block", "object-fit": "contain" });
|
|
7854
|
+
if (layer.type === "shape")
|
|
7855
|
+
box["background"] = layer.fill ? paint(layer.fill) : void 0;
|
|
7856
|
+
return [`.l-${layer.id} { ${decl(box)} }`];
|
|
7857
|
+
}
|
|
7858
|
+
function decl(properties) {
|
|
7859
|
+
return Object.entries(properties).filter(([, value]) => value !== void 0).map(([name, value]) => `${name}: ${value};`).join(" ");
|
|
7860
|
+
}
|
|
7861
|
+
function num(n) {
|
|
7862
|
+
return String(Math.round(n * 100) / 100);
|
|
7863
|
+
}
|
|
7864
|
+
function px(n) {
|
|
7865
|
+
const value = num(n);
|
|
7866
|
+
return value === "0" ? "0" : `${value}px`;
|
|
7867
|
+
}
|
|
7868
|
+
function safeColour(value) {
|
|
7869
|
+
return colourPattern2.test(value) ? value : "#000000";
|
|
7870
|
+
}
|
|
7871
|
+
function paint(p) {
|
|
7872
|
+
if ("brand" in p)
|
|
7873
|
+
return tokenPattern2.test(p.brand) ? `var(--brand-color-${p.brand}, ${safeColour(p.fallback)})` : safeColour(p.fallback);
|
|
7874
|
+
return safeColour(p.color);
|
|
7875
|
+
}
|
|
7876
|
+
function literalColour(p) {
|
|
7877
|
+
return safeColour("brand" in p ? p.fallback : p.color);
|
|
7878
|
+
}
|
|
7879
|
+
function shadow(s) {
|
|
7880
|
+
return `${px(s.x)} ${px(s.y)} ${px(s.blur)} ${safeColour(s.color)}`;
|
|
7881
|
+
}
|
|
7882
|
+
function servedWeight(font, weight) {
|
|
7883
|
+
const google = "family" in font && font.source === "google" ? googleFont(font.family) : void 0;
|
|
7884
|
+
return google ? nearestWeight(google, weight) : weight;
|
|
7885
|
+
}
|
|
7886
|
+
function fontFamily(font) {
|
|
7887
|
+
if ("brand" in font) return `var(--brand-font-${font.brand}, sans-serif)`;
|
|
7888
|
+
const family = font.family.replace(/["'\\<>\n\r;{}]/g, "");
|
|
7889
|
+
const generic = font.source === "google" ? genericFamily(googleFont(family)) : "sans-serif";
|
|
7890
|
+
return `"${family}", ${generic}`;
|
|
7891
|
+
}
|
|
7892
|
+
function escapeAttribute(value) {
|
|
7893
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/\{/g, "%7B").replace(/\}/g, "%7D");
|
|
7894
|
+
}
|
|
7895
|
+
|
|
7896
|
+
// packages/engine/src/lib/design/starters.ts
|
|
7897
|
+
var style = (overrides) => ({
|
|
7898
|
+
font: { family: "Inter", source: "google" },
|
|
7899
|
+
weight: 600,
|
|
7900
|
+
size: 40,
|
|
7901
|
+
lineHeight: 1.2,
|
|
7902
|
+
letterSpacing: 0,
|
|
7903
|
+
color: { color: "#111827" },
|
|
7904
|
+
align: "left",
|
|
7905
|
+
...overrides
|
|
7906
|
+
});
|
|
7907
|
+
var text = (layer) => ({
|
|
7908
|
+
type: "text",
|
|
7909
|
+
rotation: 0,
|
|
7910
|
+
opacity: 1,
|
|
7911
|
+
visible: true,
|
|
7912
|
+
sizing: "fixed",
|
|
7913
|
+
valign: "top",
|
|
7914
|
+
...layer
|
|
7915
|
+
});
|
|
7916
|
+
var design = (background, layers) => ({
|
|
7917
|
+
version: DESIGN_VERSION,
|
|
7918
|
+
output: "",
|
|
7919
|
+
background,
|
|
7920
|
+
layers
|
|
7921
|
+
});
|
|
7922
|
+
var designStarters = [
|
|
7923
|
+
{
|
|
7924
|
+
id: "blank",
|
|
7925
|
+
title: "Blank",
|
|
7926
|
+
size: { width: 1200, height: 630 },
|
|
7927
|
+
design: design({ color: "#ffffff" }, []),
|
|
7928
|
+
sampleData: {}
|
|
7929
|
+
},
|
|
7930
|
+
{
|
|
7931
|
+
id: "article",
|
|
7932
|
+
title: "Article card",
|
|
7933
|
+
size: { width: 1200, height: 630 },
|
|
7934
|
+
design: design({ brand: "primary", fallback: "#0f172a" }, [
|
|
7935
|
+
text({
|
|
7936
|
+
id: "site",
|
|
7937
|
+
name: "Site",
|
|
7938
|
+
x: 80,
|
|
7939
|
+
y: 64,
|
|
7940
|
+
width: 1040,
|
|
7941
|
+
height: 40,
|
|
7942
|
+
content: [{ path: "site.name" }],
|
|
7943
|
+
style: style({
|
|
7944
|
+
size: 26,
|
|
7945
|
+
weight: 600,
|
|
7946
|
+
letterSpacing: 2,
|
|
7947
|
+
uppercase: true,
|
|
7948
|
+
color: { color: "#cbd5e1" }
|
|
7949
|
+
})
|
|
7950
|
+
}),
|
|
7951
|
+
text({
|
|
7952
|
+
id: "title",
|
|
7953
|
+
name: "Title",
|
|
7954
|
+
x: 80,
|
|
7955
|
+
y: 130,
|
|
7956
|
+
width: 1040,
|
|
7957
|
+
height: 320,
|
|
7958
|
+
content: [{ path: "post.title" }],
|
|
7959
|
+
sizing: "shrink",
|
|
7960
|
+
minFontSize: 40,
|
|
7961
|
+
valign: "middle",
|
|
7962
|
+
style: style({
|
|
7963
|
+
size: 76,
|
|
7964
|
+
weight: 800,
|
|
7965
|
+
lineHeight: 1.1,
|
|
7966
|
+
color: { color: "#ffffff" }
|
|
7967
|
+
})
|
|
7968
|
+
}),
|
|
7969
|
+
{
|
|
7970
|
+
id: "rule",
|
|
7971
|
+
name: "Rule",
|
|
7972
|
+
type: "shape",
|
|
7973
|
+
shape: "rectangle",
|
|
7974
|
+
x: 80,
|
|
7975
|
+
y: 478,
|
|
7976
|
+
width: 120,
|
|
7977
|
+
height: 6,
|
|
7978
|
+
rotation: 0,
|
|
7979
|
+
opacity: 1,
|
|
7980
|
+
visible: true,
|
|
7981
|
+
fill: { brand: "accent", fallback: "#38bdf8" }
|
|
7982
|
+
},
|
|
7983
|
+
text({
|
|
7984
|
+
id: "byline",
|
|
7985
|
+
name: "Byline",
|
|
7986
|
+
x: 80,
|
|
7987
|
+
y: 508,
|
|
7988
|
+
width: 1040,
|
|
7989
|
+
height: 50,
|
|
7990
|
+
content: [{ text: "By " }, { path: "post.author" }],
|
|
7991
|
+
showWhen: { path: "post.author" },
|
|
7992
|
+
style: style({ size: 30, weight: 500, color: { color: "#cbd5e1" } })
|
|
7993
|
+
})
|
|
7994
|
+
]),
|
|
7995
|
+
sampleData: {
|
|
7996
|
+
site: { name: "Fennlor Studio" },
|
|
7997
|
+
post: {
|
|
7998
|
+
title: "How we cut our invoice run from an hour to four minutes",
|
|
7999
|
+
author: "Max Mustermann"
|
|
8000
|
+
}
|
|
8001
|
+
}
|
|
8002
|
+
},
|
|
8003
|
+
{
|
|
8004
|
+
id: "quote",
|
|
8005
|
+
title: "Quote card",
|
|
8006
|
+
size: { width: 1080, height: 1080 },
|
|
8007
|
+
design: design({ color: "#f5efe6" }, [
|
|
8008
|
+
text({
|
|
8009
|
+
id: "mark",
|
|
8010
|
+
name: "Quotation mark",
|
|
8011
|
+
x: 96,
|
|
8012
|
+
y: 60,
|
|
8013
|
+
width: 240,
|
|
8014
|
+
height: 220,
|
|
8015
|
+
content: [{ text: "\u201C" }],
|
|
8016
|
+
style: style({
|
|
8017
|
+
font: { family: "Playfair Display", source: "google" },
|
|
8018
|
+
size: 220,
|
|
8019
|
+
weight: 700,
|
|
8020
|
+
lineHeight: 1,
|
|
8021
|
+
color: { color: "#d97706" }
|
|
8022
|
+
})
|
|
8023
|
+
}),
|
|
8024
|
+
text({
|
|
8025
|
+
id: "quote",
|
|
8026
|
+
name: "Quote",
|
|
8027
|
+
x: 96,
|
|
8028
|
+
y: 260,
|
|
8029
|
+
width: 888,
|
|
8030
|
+
height: 520,
|
|
8031
|
+
content: [{ path: "review.quote" }],
|
|
8032
|
+
sizing: "shrink",
|
|
8033
|
+
minFontSize: 32,
|
|
8034
|
+
valign: "middle",
|
|
8035
|
+
style: style({
|
|
8036
|
+
font: { family: "Playfair Display", source: "google" },
|
|
8037
|
+
size: 60,
|
|
8038
|
+
weight: 600,
|
|
8039
|
+
lineHeight: 1.22,
|
|
8040
|
+
color: { color: "#1c1917" }
|
|
8041
|
+
})
|
|
8042
|
+
}),
|
|
8043
|
+
text({
|
|
8044
|
+
id: "author",
|
|
8045
|
+
name: "Author",
|
|
8046
|
+
x: 96,
|
|
8047
|
+
y: 836,
|
|
8048
|
+
width: 888,
|
|
8049
|
+
height: 50,
|
|
8050
|
+
content: [{ path: "review.author" }],
|
|
8051
|
+
style: style({ size: 36, weight: 600, color: { color: "#1c1917" } })
|
|
8052
|
+
}),
|
|
8053
|
+
text({
|
|
8054
|
+
id: "role",
|
|
8055
|
+
name: "Role",
|
|
8056
|
+
x: 96,
|
|
8057
|
+
y: 896,
|
|
8058
|
+
width: 888,
|
|
8059
|
+
height: 44,
|
|
8060
|
+
content: [{ path: "review.role" }],
|
|
8061
|
+
showWhen: { path: "review.role" },
|
|
8062
|
+
style: style({ size: 28, weight: 400, color: { color: "#78716c" } })
|
|
8063
|
+
})
|
|
8064
|
+
]),
|
|
8065
|
+
sampleData: {
|
|
8066
|
+
review: {
|
|
8067
|
+
quote: "Delivery took two days, and the one shade that arrived dented was replaced before we had finished the call.",
|
|
8068
|
+
author: "Erika Mustermann",
|
|
8069
|
+
role: "Office manager, Olvarest GmbH"
|
|
8070
|
+
}
|
|
8071
|
+
}
|
|
8072
|
+
},
|
|
8073
|
+
{
|
|
8074
|
+
id: "event",
|
|
8075
|
+
title: "Event story",
|
|
8076
|
+
size: { width: 1080, height: 1920 },
|
|
8077
|
+
design: design({ color: "#111827" }, [
|
|
8078
|
+
text({
|
|
8079
|
+
id: "label",
|
|
8080
|
+
name: "Label",
|
|
8081
|
+
x: 96,
|
|
8082
|
+
y: 200,
|
|
8083
|
+
width: 888,
|
|
8084
|
+
height: 50,
|
|
8085
|
+
content: [{ text: "Event" }],
|
|
8086
|
+
style: style({
|
|
8087
|
+
size: 36,
|
|
8088
|
+
weight: 700,
|
|
8089
|
+
letterSpacing: 6,
|
|
8090
|
+
uppercase: true,
|
|
8091
|
+
color: { color: "#fbbf24" }
|
|
8092
|
+
})
|
|
8093
|
+
}),
|
|
8094
|
+
text({
|
|
8095
|
+
id: "title",
|
|
8096
|
+
name: "Title",
|
|
8097
|
+
x: 96,
|
|
8098
|
+
y: 290,
|
|
8099
|
+
width: 888,
|
|
8100
|
+
height: 620,
|
|
8101
|
+
content: [{ path: "event.title" }],
|
|
8102
|
+
sizing: "shrink",
|
|
8103
|
+
minFontSize: 56,
|
|
8104
|
+
valign: "bottom",
|
|
8105
|
+
style: style({
|
|
8106
|
+
size: 112,
|
|
8107
|
+
weight: 800,
|
|
8108
|
+
lineHeight: 1.05,
|
|
8109
|
+
color: { color: "#ffffff" }
|
|
8110
|
+
})
|
|
8111
|
+
}),
|
|
8112
|
+
text({
|
|
8113
|
+
id: "date",
|
|
8114
|
+
name: "Date",
|
|
8115
|
+
x: 96,
|
|
8116
|
+
y: 980,
|
|
8117
|
+
width: 888,
|
|
8118
|
+
height: 70,
|
|
8119
|
+
content: [
|
|
8120
|
+
{
|
|
8121
|
+
path: "event.date",
|
|
8122
|
+
format: { helper: "date", pattern: "d MMMM yyyy" }
|
|
8123
|
+
}
|
|
8124
|
+
],
|
|
8125
|
+
style: style({ size: 52, weight: 600, color: { color: "#e5e7eb" } })
|
|
8126
|
+
}),
|
|
8127
|
+
text({
|
|
8128
|
+
id: "place",
|
|
8129
|
+
name: "Place",
|
|
8130
|
+
x: 96,
|
|
8131
|
+
y: 1060,
|
|
8132
|
+
width: 888,
|
|
8133
|
+
height: 60,
|
|
8134
|
+
content: [{ path: "event.place" }],
|
|
8135
|
+
style: style({ size: 38, weight: 400, color: { color: "#9ca3af" } })
|
|
8136
|
+
}),
|
|
8137
|
+
{
|
|
8138
|
+
id: "qr",
|
|
8139
|
+
name: "QR code",
|
|
8140
|
+
type: "qr",
|
|
8141
|
+
x: 96,
|
|
8142
|
+
y: 1440,
|
|
8143
|
+
width: 300,
|
|
8144
|
+
height: 300,
|
|
8145
|
+
rotation: 0,
|
|
8146
|
+
opacity: 1,
|
|
8147
|
+
visible: true,
|
|
8148
|
+
radius: 16,
|
|
8149
|
+
value: { path: "event.url" },
|
|
8150
|
+
ecc: "M",
|
|
8151
|
+
color: { color: "#111827" },
|
|
8152
|
+
background: { color: "#ffffff" }
|
|
8153
|
+
},
|
|
8154
|
+
text({
|
|
8155
|
+
id: "scan",
|
|
8156
|
+
name: "Scan note",
|
|
8157
|
+
x: 440,
|
|
8158
|
+
y: 1560,
|
|
8159
|
+
width: 544,
|
|
8160
|
+
height: 60,
|
|
8161
|
+
content: [{ text: "Scan for your ticket" }],
|
|
8162
|
+
style: style({ size: 40, weight: 600, color: { color: "#ffffff" } })
|
|
8163
|
+
})
|
|
8164
|
+
]),
|
|
8165
|
+
sampleData: {
|
|
8166
|
+
event: {
|
|
8167
|
+
title: "Formfeed meetup: documents as code",
|
|
8168
|
+
date: "2026-11-12",
|
|
8169
|
+
place: "Musterstra\xDFe 1 \xB7 12345 Musterstadt",
|
|
8170
|
+
url: "https://fennlor.test/events/meetup"
|
|
8171
|
+
}
|
|
8172
|
+
}
|
|
8173
|
+
}
|
|
8174
|
+
];
|
|
8175
|
+
|
|
8176
|
+
// packages/engine/src/lib/office/errors.ts
|
|
8177
|
+
var OfficeError = class extends Error {
|
|
8178
|
+
constructor(code, message, details = {}) {
|
|
8179
|
+
super(message);
|
|
8180
|
+
this.code = code;
|
|
8181
|
+
this.details = details;
|
|
8182
|
+
this.name = "OfficeError";
|
|
8183
|
+
}
|
|
8184
|
+
};
|
|
8185
|
+
var officeLimits = {
|
|
8186
|
+
/** Entries in one container. */
|
|
8187
|
+
maxEntries: 2e3,
|
|
8188
|
+
/** Sum of the uncompressed sizes the central directory declares. */
|
|
8189
|
+
maxDeclaredBytes: 200 * 1024 * 1024,
|
|
8190
|
+
/** One part we inflate; enforced while inflating, because the declared size can lie. */
|
|
8191
|
+
maxPartBytes: 20 * 1024 * 1024,
|
|
8192
|
+
/** A conversion upload or a template file. */
|
|
8193
|
+
maxFileBytes: 20 * 1024 * 1024
|
|
8194
|
+
};
|
|
8195
|
+
|
|
8196
|
+
// node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
|
|
8197
|
+
var u8 = Uint8Array;
|
|
8198
|
+
var u16 = Uint16Array;
|
|
8199
|
+
var i32 = Int32Array;
|
|
8200
|
+
var fleb = new u8([
|
|
8201
|
+
0,
|
|
8202
|
+
0,
|
|
8203
|
+
0,
|
|
8204
|
+
0,
|
|
8205
|
+
0,
|
|
8206
|
+
0,
|
|
8207
|
+
0,
|
|
8208
|
+
0,
|
|
8209
|
+
1,
|
|
8210
|
+
1,
|
|
8211
|
+
1,
|
|
8212
|
+
1,
|
|
8213
|
+
2,
|
|
8214
|
+
2,
|
|
8215
|
+
2,
|
|
8216
|
+
2,
|
|
8217
|
+
3,
|
|
8218
|
+
3,
|
|
8219
|
+
3,
|
|
8220
|
+
3,
|
|
8221
|
+
4,
|
|
8222
|
+
4,
|
|
8223
|
+
4,
|
|
8224
|
+
4,
|
|
8225
|
+
5,
|
|
8226
|
+
5,
|
|
8227
|
+
5,
|
|
8228
|
+
5,
|
|
8229
|
+
0,
|
|
8230
|
+
/* unused */
|
|
8231
|
+
0,
|
|
8232
|
+
0,
|
|
8233
|
+
/* impossible */
|
|
8234
|
+
0
|
|
8235
|
+
]);
|
|
8236
|
+
var fdeb = new u8([
|
|
8237
|
+
0,
|
|
8238
|
+
0,
|
|
8239
|
+
0,
|
|
8240
|
+
0,
|
|
8241
|
+
1,
|
|
8242
|
+
1,
|
|
8243
|
+
2,
|
|
8244
|
+
2,
|
|
8245
|
+
3,
|
|
8246
|
+
3,
|
|
8247
|
+
4,
|
|
8248
|
+
4,
|
|
8249
|
+
5,
|
|
8250
|
+
5,
|
|
8251
|
+
6,
|
|
8252
|
+
6,
|
|
8253
|
+
7,
|
|
8254
|
+
7,
|
|
8255
|
+
8,
|
|
8256
|
+
8,
|
|
8257
|
+
9,
|
|
8258
|
+
9,
|
|
8259
|
+
10,
|
|
8260
|
+
10,
|
|
8261
|
+
11,
|
|
8262
|
+
11,
|
|
8263
|
+
12,
|
|
8264
|
+
12,
|
|
8265
|
+
13,
|
|
8266
|
+
13,
|
|
8267
|
+
/* unused */
|
|
8268
|
+
0,
|
|
8269
|
+
0
|
|
8270
|
+
]);
|
|
8271
|
+
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
8272
|
+
var freb = function(eb, start) {
|
|
8273
|
+
var b = new u16(31);
|
|
8274
|
+
for (var i = 0; i < 31; ++i) {
|
|
8275
|
+
b[i] = start += 1 << eb[i - 1];
|
|
8276
|
+
}
|
|
8277
|
+
var r = new i32(b[30]);
|
|
8278
|
+
for (var i = 1; i < 30; ++i) {
|
|
8279
|
+
for (var j = b[i]; j < b[i + 1]; ++j) {
|
|
8280
|
+
r[j] = j - b[i] << 5 | i;
|
|
8281
|
+
}
|
|
8282
|
+
}
|
|
8283
|
+
return { b, r };
|
|
8284
|
+
};
|
|
8285
|
+
var _a = freb(fleb, 2);
|
|
8286
|
+
var fl = _a.b;
|
|
8287
|
+
var revfl = _a.r;
|
|
8288
|
+
fl[28] = 258, revfl[258] = 28;
|
|
8289
|
+
var _b = freb(fdeb, 0);
|
|
8290
|
+
var fd = _b.b;
|
|
8291
|
+
var revfd = _b.r;
|
|
8292
|
+
var rev = new u16(32768);
|
|
8293
|
+
for (i = 0; i < 32768; ++i) {
|
|
6177
8294
|
x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
6178
8295
|
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
6179
8296
|
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
@@ -7358,8 +9475,8 @@ function assertWellFormed(tokens, part) {
|
|
|
7358
9475
|
break;
|
|
7359
9476
|
}
|
|
7360
9477
|
case "close": {
|
|
7361
|
-
const
|
|
7362
|
-
if (
|
|
9478
|
+
const open2 = stack.pop();
|
|
9479
|
+
if (open2 !== token2.name) throw invalid2(`</${token2.name}> closes <${open2 ?? "nothing"}>`, part);
|
|
7363
9480
|
break;
|
|
7364
9481
|
}
|
|
7365
9482
|
case "text":
|
|
@@ -7375,12 +9492,12 @@ function assertWellFormed(tokens, part) {
|
|
|
7375
9492
|
}
|
|
7376
9493
|
var FORBIDDEN = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
7377
9494
|
var ENTITY = /&(?:#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/y;
|
|
7378
|
-
function checkText(
|
|
7379
|
-
if (FORBIDDEN.test(
|
|
7380
|
-
if (
|
|
7381
|
-
for (let i =
|
|
9495
|
+
function checkText(text2, part) {
|
|
9496
|
+
if (FORBIDDEN.test(text2)) throw invalid2("a character XML does not allow", part);
|
|
9497
|
+
if (text2.includes("<")) throw invalid2("unescaped < in text", part);
|
|
9498
|
+
for (let i = text2.indexOf("&"); i !== -1; i = text2.indexOf("&", i + 1)) {
|
|
7382
9499
|
ENTITY.lastIndex = i;
|
|
7383
|
-
if (!ENTITY.test(
|
|
9500
|
+
if (!ENTITY.test(text2)) throw invalid2(`invalid entity reference near "${text2.slice(i, i + 12)}"`, part);
|
|
7384
9501
|
}
|
|
7385
9502
|
}
|
|
7386
9503
|
function checkAttributes(raw, part) {
|
|
@@ -7395,7 +9512,7 @@ function checkAttributes(raw, part) {
|
|
|
7395
9512
|
});
|
|
7396
9513
|
if (rest.trim() !== "") throw invalid2(`malformed attributes in ${raw.slice(0, 40)}`, part);
|
|
7397
9514
|
}
|
|
7398
|
-
function
|
|
9515
|
+
function decodeText3(raw) {
|
|
7399
9516
|
return raw.replace(/&(#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/g, (whole, name) => {
|
|
7400
9517
|
switch (name) {
|
|
7401
9518
|
case "amp":
|
|
@@ -7415,13 +9532,13 @@ function decodeText2(raw) {
|
|
|
7415
9532
|
}
|
|
7416
9533
|
});
|
|
7417
9534
|
}
|
|
7418
|
-
function escapeText2(
|
|
7419
|
-
return
|
|
9535
|
+
function escapeText2(text2) {
|
|
9536
|
+
return text2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
7420
9537
|
}
|
|
7421
9538
|
var FORBIDDEN_GLOBAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
|
|
7422
|
-
function stripForbidden(
|
|
9539
|
+
function stripForbidden(text2) {
|
|
7423
9540
|
let removed = 0;
|
|
7424
|
-
const out =
|
|
9541
|
+
const out = text2.replace(FORBIDDEN_GLOBAL, () => {
|
|
7425
9542
|
removed++;
|
|
7426
9543
|
return "";
|
|
7427
9544
|
});
|
|
@@ -7672,13 +9789,13 @@ function placeDrawings(xml2, pattern, markupFor) {
|
|
|
7672
9789
|
pattern.lastIndex = 0;
|
|
7673
9790
|
if (pattern.test(t.raw)) {
|
|
7674
9791
|
const segments = t.raw.split(pattern);
|
|
7675
|
-
let
|
|
9792
|
+
let text2 = segments[0];
|
|
7676
9793
|
for (let s = 1; s < segments.length; s += 2) {
|
|
7677
9794
|
const index = Number(segments[s]);
|
|
7678
|
-
|
|
9795
|
+
text2 += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
|
|
7679
9796
|
}
|
|
7680
9797
|
out[textOpen] = withPreserve(out[textOpen]);
|
|
7681
|
-
out.push(
|
|
9798
|
+
out.push(text2);
|
|
7682
9799
|
continue;
|
|
7683
9800
|
}
|
|
7684
9801
|
}
|
|
@@ -7701,13 +9818,13 @@ function withRelationships(rels2, relationships) {
|
|
|
7701
9818
|
return rels2.slice(0, end) + added + rels2.slice(end);
|
|
7702
9819
|
}
|
|
7703
9820
|
function relationshipIds(rels2) {
|
|
7704
|
-
return new Set([...(rels2 ?? "").matchAll(/\sId="([^"]+)"/g)].map((m) =>
|
|
9821
|
+
return new Set([...(rels2 ?? "").matchAll(/\sId="([^"]+)"/g)].map((m) => decodeText3(m[1])));
|
|
7705
9822
|
}
|
|
7706
9823
|
function withImageTypes(contentTypes, extensions) {
|
|
7707
9824
|
let out = contentTypes;
|
|
7708
9825
|
for (const ext of extensions) {
|
|
7709
9826
|
if (new RegExp(`<Default\\b[^>]*Extension="${ext}"`, "i").test(out)) continue;
|
|
7710
|
-
out = out.replace(/<Types\b[^>]*>/, (
|
|
9827
|
+
out = out.replace(/<Types\b[^>]*>/, (open2) => `${open2}<Default Extension="${ext}" ContentType="image/${ext}"/>`);
|
|
7711
9828
|
}
|
|
7712
9829
|
return out;
|
|
7713
9830
|
}
|
|
@@ -7729,7 +9846,7 @@ var THEME_REFERENCES = /^\+(mj|mn)-/;
|
|
|
7729
9846
|
function documentFonts(archive) {
|
|
7730
9847
|
const fonts = /* @__PURE__ */ new Map();
|
|
7731
9848
|
const add2 = (name, embedded = false) => {
|
|
7732
|
-
const clean =
|
|
9849
|
+
const clean = decodeText3(name).trim();
|
|
7733
9850
|
if (!clean || THEME_REFERENCES.test(clean)) return;
|
|
7734
9851
|
const key = clean.toLowerCase();
|
|
7735
9852
|
const known = fonts.get(key);
|
|
@@ -7910,7 +10027,7 @@ function toTemplateSource(xml2, flavour, nonce, part) {
|
|
|
7910
10027
|
for (const token2 of tokens) {
|
|
7911
10028
|
if (token2.kind === "text" && inText) {
|
|
7912
10029
|
flush();
|
|
7913
|
-
source +=
|
|
10030
|
+
source += decodeText3(token2.raw);
|
|
7914
10031
|
continue;
|
|
7915
10032
|
}
|
|
7916
10033
|
pending.raw += token2.raw;
|
|
@@ -7933,11 +10050,11 @@ function fromTemplateOutput(output, template, part) {
|
|
|
7933
10050
|
let last = 0;
|
|
7934
10051
|
let inText = false;
|
|
7935
10052
|
const emit = (segment) => {
|
|
7936
|
-
const { text, removed } = stripForbidden(segment);
|
|
10053
|
+
const { text: text2, removed } = stripForbidden(segment);
|
|
7937
10054
|
removedCharacters += removed;
|
|
7938
|
-
if (!inText &&
|
|
7939
|
-
throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${
|
|
7940
|
-
xml2 += escapeText2(
|
|
10055
|
+
if (!inText && text2.trim() !== "")
|
|
10056
|
+
throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text2.trim().slice(0, 40)}"`);
|
|
10057
|
+
xml2 += escapeText2(text2);
|
|
7941
10058
|
};
|
|
7942
10059
|
for (const m of output.matchAll(placeholder)) {
|
|
7943
10060
|
emit(output.slice(last, m.index));
|
|
@@ -7979,15 +10096,15 @@ function layoutText(xml2, flavour, part) {
|
|
|
7979
10096
|
out.push(token2.raw);
|
|
7980
10097
|
continue;
|
|
7981
10098
|
}
|
|
7982
|
-
const
|
|
10099
|
+
const text2 = token2.raw.replace(/\r\n?/g, "\n");
|
|
7983
10100
|
if (flavour === "wordprocessing") {
|
|
7984
|
-
const pieces =
|
|
7985
|
-
if (pieces.length > 1 || /^\s|\s$/.test(
|
|
10101
|
+
const pieces = text2.split(/(\n|\t)/);
|
|
10102
|
+
if (pieces.length > 1 || /^\s|\s$/.test(text2)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
|
|
7986
10103
|
out.push(
|
|
7987
10104
|
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("")
|
|
7988
10105
|
);
|
|
7989
10106
|
} else {
|
|
7990
|
-
out.push(
|
|
10107
|
+
out.push(text2.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
|
|
7991
10108
|
}
|
|
7992
10109
|
}
|
|
7993
10110
|
return out.join("");
|
|
@@ -8020,17 +10137,17 @@ var AUTOCORRECTED = [
|
|
|
8020
10137
|
[/\u200B|\u200C|\u200D|\uFEFF/g, ""]
|
|
8021
10138
|
];
|
|
8022
10139
|
function normaliseTags(template, part) {
|
|
8023
|
-
const { text, origin, placeholders } = stripPlaceholders(template);
|
|
10140
|
+
const { text: text2, origin, placeholders } = stripPlaceholders(template);
|
|
8024
10141
|
const sourceRange = (from, to) => [
|
|
8025
10142
|
origin[from] ?? template.source.length,
|
|
8026
10143
|
(origin[to - 1] ?? template.source.length - 1) + 1
|
|
8027
10144
|
];
|
|
8028
10145
|
const diagnostics = [];
|
|
8029
10146
|
const edits = [];
|
|
8030
|
-
for (const tag of findTags(
|
|
10147
|
+
for (const tag of findTags(text2)) {
|
|
8031
10148
|
const inside = placeholders.filter((p) => p.at > tag.start && p.at < tag.end);
|
|
8032
10149
|
const paragraph2 = paragraphOf(template, placeholders, tag.start);
|
|
8033
|
-
const raw =
|
|
10150
|
+
const raw = text2.slice(tag.start, tag.end);
|
|
8034
10151
|
const unsafe = inside.filter((p) => {
|
|
8035
10152
|
const piece = template.markup[p.index];
|
|
8036
10153
|
return !piece || piece.paragraphBoundary || piece.containerBoundary;
|
|
@@ -8084,8 +10201,8 @@ function normaliseTags(template, part) {
|
|
|
8084
10201
|
return { template: { ...template, source: applyEdits(template.source, edits) }, diagnostics };
|
|
8085
10202
|
}
|
|
8086
10203
|
var ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
|
|
8087
|
-
function decodeEntities(
|
|
8088
|
-
return
|
|
10204
|
+
function decodeEntities(text2) {
|
|
10205
|
+
return text2.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
|
|
8089
10206
|
if (name[0] !== "#") return ENTITIES[name.toLowerCase()] ?? whole;
|
|
8090
10207
|
const code = name[1] === "x" || name[1] === "X" ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);
|
|
8091
10208
|
return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
|
|
@@ -8093,28 +10210,28 @@ function decodeEntities(text) {
|
|
|
8093
10210
|
}
|
|
8094
10211
|
function stripPlaceholders(template) {
|
|
8095
10212
|
const pattern = placeholderPattern(template.nonce);
|
|
8096
|
-
let
|
|
10213
|
+
let text2 = "";
|
|
8097
10214
|
const origin = [];
|
|
8098
10215
|
const placeholders = [];
|
|
8099
10216
|
let last = 0;
|
|
8100
10217
|
for (const m of template.source.matchAll(pattern)) {
|
|
8101
10218
|
for (let i = last; i < (m.index ?? 0); i++) {
|
|
8102
|
-
|
|
10219
|
+
text2 += template.source[i];
|
|
8103
10220
|
origin.push(i);
|
|
8104
10221
|
}
|
|
8105
|
-
placeholders.push({ index: Number(m[1]), at:
|
|
10222
|
+
placeholders.push({ index: Number(m[1]), at: text2.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
|
|
8106
10223
|
last = (m.index ?? 0) + m[0].length;
|
|
8107
10224
|
}
|
|
8108
10225
|
for (let i = last; i < template.source.length; i++) {
|
|
8109
|
-
|
|
10226
|
+
text2 += template.source[i];
|
|
8110
10227
|
origin.push(i);
|
|
8111
10228
|
}
|
|
8112
|
-
return { text, origin, placeholders };
|
|
10229
|
+
return { text: text2, origin, placeholders };
|
|
8113
10230
|
}
|
|
8114
10231
|
function listTags(template, part) {
|
|
8115
|
-
const { text, placeholders } = stripPlaceholders(template);
|
|
8116
|
-
return findTags(
|
|
8117
|
-
const raw =
|
|
10232
|
+
const { text: text2, placeholders } = stripPlaceholders(template);
|
|
10233
|
+
return findTags(text2).map((tag) => {
|
|
10234
|
+
const raw = text2.slice(tag.start, tag.end);
|
|
8118
10235
|
const kind = raw.startsWith("{#") ? "comment" : raw.startsWith("{%") || /^\{\{~?\s*[#/^]/.test(raw) || /^\{\{~?\s*else\b/.test(raw) ? "block" : "output";
|
|
8119
10236
|
return { part, paragraph: paragraphOf(template, placeholders, tag.start), text: raw.replace(/\s+/g, " ").trim(), kind };
|
|
8120
10237
|
});
|
|
@@ -8135,15 +10252,15 @@ function undoAutocorrect(tag) {
|
|
|
8135
10252
|
for (const [pattern, replacement] of AUTOCORRECTED) out = out.replace(pattern, replacement);
|
|
8136
10253
|
return out;
|
|
8137
10254
|
}
|
|
8138
|
-
function findTags(
|
|
10255
|
+
function findTags(text2) {
|
|
8139
10256
|
const tags = [];
|
|
8140
|
-
for (let i = 0; i <
|
|
8141
|
-
const opener = OPENERS.find((o) =>
|
|
10257
|
+
for (let i = 0; i < text2.length - 1; i++) {
|
|
10258
|
+
const opener = OPENERS.find((o) => text2.startsWith(o, i));
|
|
8142
10259
|
if (!opener) continue;
|
|
8143
10260
|
const closer = CLOSERS[opener];
|
|
8144
|
-
const end =
|
|
10261
|
+
const end = text2.indexOf(closer, i + opener.length);
|
|
8145
10262
|
if (end === -1) continue;
|
|
8146
|
-
const stop = end + closer.length + (opener === "{{" &&
|
|
10263
|
+
const stop = end + closer.length + (opener === "{{" && text2[end + closer.length] === "}" ? 1 : 0);
|
|
8147
10264
|
tags.push({ start: i, end: stop });
|
|
8148
10265
|
i = stop - 1;
|
|
8149
10266
|
}
|
|
@@ -8173,8 +10290,8 @@ var CONTENT = /* @__PURE__ */ new Set(["w:drawing", "w:pict", "w:object", "w:fld
|
|
|
8173
10290
|
var JINJA_OPEN = /^(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
|
|
8174
10291
|
var JINJA_CLOSE = /^end(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
|
|
8175
10292
|
var JINJA_MIDDLE = /^(else|elif|elsif|when)\b/;
|
|
8176
|
-
function blockRole(
|
|
8177
|
-
const t =
|
|
10293
|
+
function blockRole(text2) {
|
|
10294
|
+
const t = text2.trim();
|
|
8178
10295
|
let m = /^\{%-?\s*([\s\S]*?)\s*-?%\}$/.exec(t);
|
|
8179
10296
|
if (m && !t.slice(2, -2).includes("%}")) {
|
|
8180
10297
|
const body = m[1] ?? "";
|
|
@@ -8206,14 +10323,14 @@ function dropTaggedFallbacks(xml2, part) {
|
|
|
8206
10323
|
if (t.kind === "open" && t.name === "mc:Fallback" && !t.selfClosing) {
|
|
8207
10324
|
let depth = 1;
|
|
8208
10325
|
let j = i + 1;
|
|
8209
|
-
let
|
|
10326
|
+
let text2 = "";
|
|
8210
10327
|
for (; j < tokens.length && depth > 0; j++) {
|
|
8211
10328
|
const u = tokens[j];
|
|
8212
10329
|
if (u.kind === "open" && u.name === "mc:Fallback" && !u.selfClosing) depth++;
|
|
8213
10330
|
else if (u.kind === "close" && u.name === "mc:Fallback") depth--;
|
|
8214
|
-
else if (u.kind === "text")
|
|
10331
|
+
else if (u.kind === "text") text2 += decodeText3(u.raw);
|
|
8215
10332
|
}
|
|
8216
|
-
if (/\{\{|\{%|\{#/.test(
|
|
10333
|
+
if (/\{\{|\{%|\{#/.test(text2)) {
|
|
8217
10334
|
i = j - 1;
|
|
8218
10335
|
continue;
|
|
8219
10336
|
}
|
|
@@ -8254,25 +10371,25 @@ function applyStructure(xml2, part) {
|
|
|
8254
10371
|
if (s.role === "open")
|
|
8255
10372
|
stack.push({ keyword: s.keyword, parent, paragraph: s.info.number, tag: s.tag, after: unitOf(span).end });
|
|
8256
10373
|
else if (s.role === "close" || s.role === "middle") {
|
|
8257
|
-
const
|
|
8258
|
-
if (
|
|
8259
|
-
const body = tokens.slice(
|
|
10374
|
+
const open2 = s.role === "close" ? stack.pop() : stack.at(-1);
|
|
10375
|
+
if (open2 && s.role === "close" && LOOPS.has(open2.keyword)) {
|
|
10376
|
+
const body = tokens.slice(open2.after, unitOf(span).start);
|
|
8260
10377
|
const reference = body.find((t) => t.kind === "open" && REFERENCES.has(t.name));
|
|
8261
10378
|
if (reference && reference.kind === "open")
|
|
8262
10379
|
diagnostics.push({
|
|
8263
10380
|
severity: "error",
|
|
8264
10381
|
code: "office-reference-in-loop",
|
|
8265
|
-
message: `The loop from paragraph ${
|
|
10382
|
+
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.`,
|
|
8266
10383
|
part,
|
|
8267
|
-
paragraph:
|
|
8268
|
-
text: short2(
|
|
10384
|
+
paragraph: open2.paragraph,
|
|
10385
|
+
text: short2(open2.tag)
|
|
8269
10386
|
});
|
|
8270
10387
|
}
|
|
8271
|
-
if (
|
|
10388
|
+
if (open2 && open2.parent !== parent)
|
|
8272
10389
|
diagnostics.push({
|
|
8273
10390
|
severity: "error",
|
|
8274
10391
|
code: "office-block-unbalanced",
|
|
8275
|
-
message: `${short2(
|
|
10392
|
+
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.`,
|
|
8276
10393
|
part,
|
|
8277
10394
|
paragraph: s.info.number,
|
|
8278
10395
|
text: short2(s.tag)
|
|
@@ -8286,9 +10403,9 @@ function applyStructure(xml2, part) {
|
|
|
8286
10403
|
for (const [span, s] of structural) {
|
|
8287
10404
|
if (rowOf.has(span)) continue;
|
|
8288
10405
|
const keep = s.info.sectionProperties;
|
|
8289
|
-
let
|
|
8290
|
-
if (keep)
|
|
8291
|
-
replacements.push({ start: span.start, end: span.end, text });
|
|
10406
|
+
let text2 = element(s.tag);
|
|
10407
|
+
if (keep) text2 = s.role === "open" ? `<w:p>${keep}</w:p>${text2}` : `${text2}<w:p>${keep}</w:p>`;
|
|
10408
|
+
replacements.push({ start: span.start, end: span.end, text: text2 });
|
|
8292
10409
|
const holder = span.parent && BODIES.has(span.parent.name) ? span.parent : null;
|
|
8293
10410
|
if (holder && !keep) cellsEmptied.set(holder, (cellsEmptied.get(holder) ?? 0) + 1);
|
|
8294
10411
|
}
|
|
@@ -8342,7 +10459,7 @@ function collectParagraphs(tokens, root) {
|
|
|
8342
10459
|
const out = [];
|
|
8343
10460
|
const walk3 = (span) => {
|
|
8344
10461
|
if (PARAGRAPH.has(span.name)) {
|
|
8345
|
-
let
|
|
10462
|
+
let text2 = "";
|
|
8346
10463
|
let hasContent = false;
|
|
8347
10464
|
let sectionProperties = null;
|
|
8348
10465
|
let inText = false;
|
|
@@ -8353,10 +10470,10 @@ function collectParagraphs(tokens, root) {
|
|
|
8353
10470
|
else if (CONTENT.has(t.name)) hasContent = true;
|
|
8354
10471
|
else if (t.name === "w:sectPr") sectionProperties = "";
|
|
8355
10472
|
} else if (t.kind === "close" && TEXT.has(t.name)) inText = false;
|
|
8356
|
-
else if (t.kind === "text" && inText)
|
|
10473
|
+
else if (t.kind === "text" && inText) text2 += decodeText3(t.raw);
|
|
8357
10474
|
}
|
|
8358
10475
|
if (sectionProperties !== null) sectionProperties = propertiesOf(tokens, span);
|
|
8359
|
-
out.push({ span, number: out.length + 1, text, hasContent, sectionProperties });
|
|
10476
|
+
out.push({ span, number: out.length + 1, text: text2, hasContent, sectionProperties });
|
|
8360
10477
|
}
|
|
8361
10478
|
for (const child2 of span.children) walk3(child2);
|
|
8362
10479
|
};
|
|
@@ -8464,7 +10581,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8464
10581
|
for (const container of containers) {
|
|
8465
10582
|
const indexes = [];
|
|
8466
10583
|
const edits = [];
|
|
8467
|
-
let
|
|
10584
|
+
let text2 = "";
|
|
8468
10585
|
let inText = false;
|
|
8469
10586
|
for (let i = container.start + 1; i < container.end; i++) {
|
|
8470
10587
|
const t = tokens[i];
|
|
@@ -8473,7 +10590,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8473
10590
|
else if (t.kind === "text" && inText) {
|
|
8474
10591
|
pattern.lastIndex = 0;
|
|
8475
10592
|
if (!pattern.test(t.raw)) {
|
|
8476
|
-
|
|
10593
|
+
text2 += decodeText3(t.raw);
|
|
8477
10594
|
continue;
|
|
8478
10595
|
}
|
|
8479
10596
|
const segments = t.raw.split(pattern);
|
|
@@ -8482,7 +10599,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8482
10599
|
if (s % 2 === 1) indexes.push(Number(segments[s]));
|
|
8483
10600
|
else kept += segments[s];
|
|
8484
10601
|
}
|
|
8485
|
-
|
|
10602
|
+
text2 += decodeText3(kept);
|
|
8486
10603
|
edits.push({ at: i, raw: kept });
|
|
8487
10604
|
}
|
|
8488
10605
|
}
|
|
@@ -8501,7 +10618,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
|
|
|
8501
10618
|
}
|
|
8502
10619
|
const autoFit = tokens.slice(container.start, container.end).some((t) => t.kind === "open" && t.name === "a:spAutoFit");
|
|
8503
10620
|
const placed = layoutPictures(pictures, box, autoFit).map((b, i) => pictureMarkup(pictures[i], b)).join("");
|
|
8504
|
-
const onlyPictures = container.name === "p:sp" &&
|
|
10621
|
+
const onlyPictures = container.name === "p:sp" && text2.trim() === "";
|
|
8505
10622
|
if (onlyPictures) replacements.push({ start: container.start, end: container.end, text: placed });
|
|
8506
10623
|
else {
|
|
8507
10624
|
for (const edit of edits) replacements.push({ start: edit.at, end: edit.at, text: edit.raw });
|
|
@@ -8774,8 +10891,8 @@ function starterTags(engine) {
|
|
|
8774
10891
|
codeFit: engine === "liquid" ? "{{ offer.url | qrcode }}" : "{{ qrcode(offer.url) }}"
|
|
8775
10892
|
};
|
|
8776
10893
|
}
|
|
8777
|
-
var paragraph = (
|
|
8778
|
-
var cell = (
|
|
10894
|
+
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>`;
|
|
10895
|
+
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>`;
|
|
8779
10896
|
var row = (cells) => `<w:tr>${cells}</w:tr>`;
|
|
8780
10897
|
function starterDocument(engine) {
|
|
8781
10898
|
const t = starterTags(engine);
|
|
@@ -8818,7 +10935,7 @@ function starterDocument(engine) {
|
|
|
8818
10935
|
["word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="${W}"><w:body>${body}</w:body></w:document>`]
|
|
8819
10936
|
];
|
|
8820
10937
|
return {
|
|
8821
|
-
bytes: writeZip(files.map(([name,
|
|
10938
|
+
bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
|
|
8822
10939
|
sampleData: starterSampleData()
|
|
8823
10940
|
};
|
|
8824
10941
|
}
|
|
@@ -8846,15 +10963,15 @@ var CM = 36e4;
|
|
|
8846
10963
|
var FONT = "Carlito";
|
|
8847
10964
|
var ACCENT = "0F766E";
|
|
8848
10965
|
var emptyTree = '<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr/>';
|
|
8849
|
-
var run = (
|
|
8850
|
-
var para = (
|
|
10966
|
+
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>`;
|
|
10967
|
+
var para = (text2, size, options = {}) => `<a:p>${run(text2, size, options)}</a:p>`;
|
|
8851
10968
|
function textBox(id, name, x, y, cx, cy, paragraphs) {
|
|
8852
10969
|
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>`;
|
|
8853
10970
|
}
|
|
8854
|
-
function cell2(
|
|
10971
|
+
function cell2(text2, header) {
|
|
8855
10972
|
const fill = header ? `<a:solidFill><a:srgbClr val="${ACCENT}"/></a:solidFill>` : "<a:noFill/>";
|
|
8856
10973
|
const line = `<a:lnB w="6350"><a:solidFill><a:srgbClr val="BFBFBF"/></a:solidFill></a:lnB>`;
|
|
8857
|
-
return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(
|
|
10974
|
+
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>`;
|
|
8858
10975
|
}
|
|
8859
10976
|
var tableRow = (a, b, header = false) => `<a:tr h="${Math.round(0.9 * CM)}">${cell2(a, header)}${cell2(b, header)}</a:tr>`;
|
|
8860
10977
|
function table2(id, x, y, rows) {
|
|
@@ -8944,7 +11061,7 @@ function starterPresentation(engine) {
|
|
|
8944
11061
|
];
|
|
8945
11062
|
const encoder2 = new TextEncoder();
|
|
8946
11063
|
return {
|
|
8947
|
-
bytes: writeZip(files.map(([name,
|
|
11064
|
+
bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
|
|
8948
11065
|
sampleData: starterSampleData()
|
|
8949
11066
|
};
|
|
8950
11067
|
}
|