@embedpdf/core-acrojs 3.0.0-next.1 → 3.0.0-next.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/dist/index.cjs +954 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +241 -16
- package/dist/index.d.ts +241 -16
- package/dist/index.js +947 -48
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -8,7 +8,7 @@ function javaScriptSourcesFromActionTree(tree) {
|
|
|
8
8
|
if (tree.incomplete) throw new Error("Refusing to execute an incomplete PDF action tree");
|
|
9
9
|
const sources = [];
|
|
10
10
|
const visit = (node) => {
|
|
11
|
-
if (node.type === "javascript"
|
|
11
|
+
if (node.type === "javascript") sources.push(node.script);
|
|
12
12
|
for (const child of node.next) visit(child);
|
|
13
13
|
};
|
|
14
14
|
if (tree.root) visit(tree.root);
|
|
@@ -19,6 +19,324 @@ function javaScriptProgramFromActionTree(tree) {
|
|
|
19
19
|
return javaScriptSourcesFromActionTree(tree).join("\n;\n");
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
|
+
//#region src/color.ts
|
|
23
|
+
/**
|
|
24
|
+
* Host-side Acrobat color-array conversions — the commit sinks and world
|
|
25
|
+
* builders cross the Acrobat-array ↔ renderer-color boundary through these.
|
|
26
|
+
* The PRELUDE inlines its own identical formulas (it must stay
|
|
27
|
+
* self-contained); `prelude.test.ts` pins the two implementations together.
|
|
28
|
+
*
|
|
29
|
+
* Conversion vectors (the documented Acrobat semantics):
|
|
30
|
+
* G → RGB: replicate the gray component.
|
|
31
|
+
* CMYK → RGB: component = 1 − min(1, channel + k).
|
|
32
|
+
* RGB → G: luminosity 0.3r + 0.59g + 0.11b.
|
|
33
|
+
* RGB → CMYK: k = 1 − max(r,g,b); channels = 1 − component − k.
|
|
34
|
+
* T (transparent) converts to itself only.
|
|
35
|
+
*/
|
|
36
|
+
function scriptColorToRgb(color) {
|
|
37
|
+
switch (color[0]) {
|
|
38
|
+
case "T": return null;
|
|
39
|
+
case "G": return {
|
|
40
|
+
r: color[1],
|
|
41
|
+
g: color[1],
|
|
42
|
+
b: color[1]
|
|
43
|
+
};
|
|
44
|
+
case "RGB": return {
|
|
45
|
+
r: color[1],
|
|
46
|
+
g: color[2],
|
|
47
|
+
b: color[3]
|
|
48
|
+
};
|
|
49
|
+
case "CMYK": {
|
|
50
|
+
const [, c, m, y, k] = color;
|
|
51
|
+
return {
|
|
52
|
+
r: 1 - Math.min(1, c + k),
|
|
53
|
+
g: 1 - Math.min(1, m + k),
|
|
54
|
+
b: 1 - Math.min(1, y + k)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const clamp01 = (value) => Math.min(1, Math.max(0, value));
|
|
60
|
+
const hex = (value) => Math.round(clamp01(value) * 255).toString(16).padStart(2, "0");
|
|
61
|
+
/** Acrobat array → CSS hex, or null for transparent. */
|
|
62
|
+
function scriptColorToCss(color) {
|
|
63
|
+
const rgb = scriptColorToRgb(color);
|
|
64
|
+
return rgb === null ? null : `#${hex(rgb.r)}${hex(rgb.g)}${hex(rgb.b)}`;
|
|
65
|
+
}
|
|
66
|
+
/** CSS hex (#rgb/#rrggbb) → Acrobat RGB array; null/undefined → transparent. */
|
|
67
|
+
function cssToScriptColor(css) {
|
|
68
|
+
if (!css) return ["T"];
|
|
69
|
+
const raw = css.trim().replace(/^#/, "");
|
|
70
|
+
const long = raw.length === 3 ? raw.split("").map((ch) => ch + ch).join("") : raw;
|
|
71
|
+
if (!/^[0-9a-fA-F]{6}$/.test(long)) return ["T"];
|
|
72
|
+
const channel = (offset) => parseInt(long.slice(offset, offset + 2), 16) / 255;
|
|
73
|
+
return [
|
|
74
|
+
"RGB",
|
|
75
|
+
channel(0),
|
|
76
|
+
channel(2),
|
|
77
|
+
channel(4)
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
/** Structural validation for values scripts assign. */
|
|
81
|
+
function isScriptColorArray(value) {
|
|
82
|
+
if (!Array.isArray(value) || value.length === 0) return false;
|
|
83
|
+
const [space, ...parts] = value;
|
|
84
|
+
const expected = {
|
|
85
|
+
T: 0,
|
|
86
|
+
G: 1,
|
|
87
|
+
RGB: 3,
|
|
88
|
+
CMYK: 4
|
|
89
|
+
}[String(space)];
|
|
90
|
+
return expected !== void 0 && parts.length === expected && parts.every((part) => typeof part === "number" && Number.isFinite(part));
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/types.ts
|
|
94
|
+
/**
|
|
95
|
+
* The writable-property validity matrix, keyed by engine subtype — the ONE
|
|
96
|
+
* exported table (the prelude inlines an identical copy; a parity test pins
|
|
97
|
+
* them together, and plugin-annotation drift-guards this against the kind
|
|
98
|
+
* registry's PropSpecs). Flags + contents are writable everywhere
|
|
99
|
+
* script-addressable; appearance keys vary by kind.
|
|
100
|
+
*/
|
|
101
|
+
const ANNOT_WRITABLE_KEYS = Object.freeze({
|
|
102
|
+
square: [
|
|
103
|
+
"strokeColor",
|
|
104
|
+
"fillColor",
|
|
105
|
+
"opacity",
|
|
106
|
+
"width",
|
|
107
|
+
"borderStyle",
|
|
108
|
+
"dash",
|
|
109
|
+
"rect"
|
|
110
|
+
],
|
|
111
|
+
circle: [
|
|
112
|
+
"strokeColor",
|
|
113
|
+
"fillColor",
|
|
114
|
+
"opacity",
|
|
115
|
+
"width",
|
|
116
|
+
"borderStyle",
|
|
117
|
+
"dash",
|
|
118
|
+
"rect"
|
|
119
|
+
],
|
|
120
|
+
polygon: [
|
|
121
|
+
"strokeColor",
|
|
122
|
+
"fillColor",
|
|
123
|
+
"opacity",
|
|
124
|
+
"width",
|
|
125
|
+
"borderStyle",
|
|
126
|
+
"dash",
|
|
127
|
+
"rect"
|
|
128
|
+
],
|
|
129
|
+
polyline: [
|
|
130
|
+
"strokeColor",
|
|
131
|
+
"fillColor",
|
|
132
|
+
"opacity",
|
|
133
|
+
"width",
|
|
134
|
+
"borderStyle",
|
|
135
|
+
"dash",
|
|
136
|
+
"rect"
|
|
137
|
+
],
|
|
138
|
+
line: [
|
|
139
|
+
"strokeColor",
|
|
140
|
+
"fillColor",
|
|
141
|
+
"opacity",
|
|
142
|
+
"width",
|
|
143
|
+
"borderStyle",
|
|
144
|
+
"dash",
|
|
145
|
+
"rect"
|
|
146
|
+
],
|
|
147
|
+
ink: [
|
|
148
|
+
"strokeColor",
|
|
149
|
+
"opacity",
|
|
150
|
+
"width",
|
|
151
|
+
"rect"
|
|
152
|
+
],
|
|
153
|
+
"free-text": [
|
|
154
|
+
"strokeColor",
|
|
155
|
+
"fillColor",
|
|
156
|
+
"opacity",
|
|
157
|
+
"rect"
|
|
158
|
+
],
|
|
159
|
+
highlight: ["strokeColor", "opacity"],
|
|
160
|
+
underline: ["strokeColor", "opacity"],
|
|
161
|
+
squiggly: ["strokeColor", "opacity"],
|
|
162
|
+
strikeout: ["strokeColor", "opacity"],
|
|
163
|
+
caret: ["strokeColor", "opacity"],
|
|
164
|
+
text: [
|
|
165
|
+
"strokeColor",
|
|
166
|
+
"opacity",
|
|
167
|
+
"rect"
|
|
168
|
+
],
|
|
169
|
+
stamp: ["rect"],
|
|
170
|
+
"file-attachment": ["strokeColor", "opacity"]
|
|
171
|
+
});
|
|
172
|
+
const DEFAULT_SCRIPT_BUDGET = Object.freeze({
|
|
173
|
+
maxExecutionMs: 50,
|
|
174
|
+
maxMemoryBytes: 16 * 1024 * 1024,
|
|
175
|
+
maxStackBytes: 512 * 1024,
|
|
176
|
+
maxEffects: 256,
|
|
177
|
+
maxOutputBytes: 1024 * 1024
|
|
178
|
+
});
|
|
179
|
+
const DEFAULT_SCRIPT_SECURITY_POLICY = Object.freeze({
|
|
180
|
+
enabled: false,
|
|
181
|
+
executionOwner: "originating-client-only",
|
|
182
|
+
nameTreeBoot: "lazy-first-transaction",
|
|
183
|
+
submitForm: "sink-chain",
|
|
184
|
+
openAction: "execute-lifecycle",
|
|
185
|
+
pageActions: "execute-lifecycle",
|
|
186
|
+
catalogLifecycleActions: "execute-on-verb",
|
|
187
|
+
annotationActions: "execute-full-matrix"
|
|
188
|
+
});
|
|
189
|
+
/** The shipped dispatch matrix; extracted actions outside it remain data
|
|
190
|
+
* only. (Historical note: v1 froze everything beyond K/V/C/F as
|
|
191
|
+
* preserve-only; phases 1–4 executed the rest.) */
|
|
192
|
+
const SCRIPT_EVENT_MATRIX = Object.freeze({
|
|
193
|
+
nameTree: "lazy-first-originating-field-transaction",
|
|
194
|
+
field: Object.freeze({
|
|
195
|
+
keystroke: "execute-on-commit",
|
|
196
|
+
validate: "execute",
|
|
197
|
+
calculate: "execute-in-calculation-order",
|
|
198
|
+
format: "execute-after-value-calculation"
|
|
199
|
+
}),
|
|
200
|
+
openAction: "execute-lifecycle",
|
|
201
|
+
page: "execute-lifecycle",
|
|
202
|
+
catalogLifecycle: "execute-on-verb",
|
|
203
|
+
annotation: Object.freeze({
|
|
204
|
+
widgetActivate: "execute-on-originating-client",
|
|
205
|
+
other: "execute-full-matrix"
|
|
206
|
+
}),
|
|
207
|
+
submitForm: "sink-chain"
|
|
208
|
+
});
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/host.ts
|
|
211
|
+
/** FNV-1a over `documentId:sequence` — the deterministic default random seed
|
|
212
|
+
* (ported verbatim from the form controller; exported for callers building
|
|
213
|
+
* `ScriptHostOptions.environment`). */
|
|
214
|
+
function seedFrom(documentId, sequence) {
|
|
215
|
+
let hash = 2166136261;
|
|
216
|
+
for (const char of `${documentId}:${sequence}`) {
|
|
217
|
+
hash ^= char.charCodeAt(0);
|
|
218
|
+
hash = Math.imul(hash, 16777619);
|
|
219
|
+
}
|
|
220
|
+
return hash >>> 0;
|
|
221
|
+
}
|
|
222
|
+
/** Engine JWT/session identity composed under embedder overrides — the
|
|
223
|
+
* standard `ScriptHostOptions.identity` builder. */
|
|
224
|
+
function resolveScriptIdentity(doc, overrides) {
|
|
225
|
+
const claims = doc.security.identity;
|
|
226
|
+
const supplied = typeof overrides === "function" ? overrides() : overrides ?? {};
|
|
227
|
+
return {
|
|
228
|
+
name: supplied.name ?? claims?.display_name ?? claims?.user_id ?? "",
|
|
229
|
+
loginName: supplied.loginName ?? claims?.user_id ?? "",
|
|
230
|
+
corporation: supplied.corporation ?? claims?.group_id ?? "",
|
|
231
|
+
email: supplied.email ?? ""
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function createScriptHost(options) {
|
|
235
|
+
const budget = options.budget ?? DEFAULT_SCRIPT_BUDGET;
|
|
236
|
+
let tail = Promise.resolve();
|
|
237
|
+
const enqueue = (operation) => {
|
|
238
|
+
const result = tail.then(operation, operation);
|
|
239
|
+
tail = result.then(() => void 0, () => void 0);
|
|
240
|
+
return result;
|
|
241
|
+
};
|
|
242
|
+
let sandboxPromise = null;
|
|
243
|
+
let booted = false;
|
|
244
|
+
let pendingBoot = null;
|
|
245
|
+
let bootSourcesCache = null;
|
|
246
|
+
let sequence = 0;
|
|
247
|
+
let disposed = false;
|
|
248
|
+
const ensureSandbox = () => {
|
|
249
|
+
sandboxPromise ?? (sandboxPromise = options.sandboxFactory());
|
|
250
|
+
return sandboxPromise;
|
|
251
|
+
};
|
|
252
|
+
const inputFor = (world) => ({
|
|
253
|
+
document: {
|
|
254
|
+
...options.document(),
|
|
255
|
+
...world.pageNumber !== void 0 ? { pageNumber: world.pageNumber } : {}
|
|
256
|
+
},
|
|
257
|
+
identity: options.identity(),
|
|
258
|
+
environment: options.environment(sequence++),
|
|
259
|
+
fields: world.fields,
|
|
260
|
+
...world.annots ? { annots: world.annots } : {},
|
|
261
|
+
...world.annotPages ? { annotPages: world.annotPages } : {},
|
|
262
|
+
...world.annotsCoverDocument !== void 0 ? { annotsCoverDocument: world.annotsCoverDocument } : {},
|
|
263
|
+
event: world.event
|
|
264
|
+
});
|
|
265
|
+
/** A resource fault poisons the realm: rebuild + re-boot lazily NEXT time. */
|
|
266
|
+
const noteFault = (sandbox) => {
|
|
267
|
+
if (!sandbox.disposed) return;
|
|
268
|
+
sandboxPromise = null;
|
|
269
|
+
booted = false;
|
|
270
|
+
pendingBoot = null;
|
|
271
|
+
};
|
|
272
|
+
const bootNow = async (world, runBudget) => {
|
|
273
|
+
if (booted) return null;
|
|
274
|
+
booted = true;
|
|
275
|
+
const sandbox = await ensureSandbox();
|
|
276
|
+
bootSourcesCache ?? (bootSourcesCache = options.bootSources());
|
|
277
|
+
let sources = [];
|
|
278
|
+
try {
|
|
279
|
+
sources = await bootSourcesCache;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
bootSourcesCache = null;
|
|
282
|
+
return {
|
|
283
|
+
event: {
|
|
284
|
+
rc: true,
|
|
285
|
+
value: null,
|
|
286
|
+
change: "",
|
|
287
|
+
selStart: 0,
|
|
288
|
+
selEnd: 0
|
|
289
|
+
},
|
|
290
|
+
formEffects: [],
|
|
291
|
+
annotEffects: [],
|
|
292
|
+
uiEffects: [],
|
|
293
|
+
diagnostics: [{
|
|
294
|
+
code: "script-error",
|
|
295
|
+
message: `Document boot script failed (continuing without it): ${error instanceof Error ? error.message : String(error)}`
|
|
296
|
+
}]
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
if (sources.length === 0) return null;
|
|
300
|
+
const output = sandbox.boot(sources, inputFor({
|
|
301
|
+
...world,
|
|
302
|
+
event: { kind: "name-tree-boot" }
|
|
303
|
+
}), runBudget ?? budget);
|
|
304
|
+
noteFault(sandbox);
|
|
305
|
+
return output;
|
|
306
|
+
};
|
|
307
|
+
const runNow = async (program, world, runBudget) => {
|
|
308
|
+
if (!booted) pendingBoot = await bootNow(world, runBudget);
|
|
309
|
+
const sandbox = await ensureSandbox();
|
|
310
|
+
const output = sandbox.run(program, inputFor(world), runBudget ?? budget);
|
|
311
|
+
noteFault(sandbox);
|
|
312
|
+
return output;
|
|
313
|
+
};
|
|
314
|
+
return {
|
|
315
|
+
transaction(body) {
|
|
316
|
+
if (disposed) return Promise.reject(/* @__PURE__ */ new Error("script host is disposed"));
|
|
317
|
+
return enqueue(() => body({
|
|
318
|
+
boot: async (world, runBudget) => {
|
|
319
|
+
if (pendingBoot) {
|
|
320
|
+
const output = pendingBoot;
|
|
321
|
+
pendingBoot = null;
|
|
322
|
+
return output;
|
|
323
|
+
}
|
|
324
|
+
return bootNow(world, runBudget);
|
|
325
|
+
},
|
|
326
|
+
run: (program, world, runBudget) => runNow(program, world, runBudget)
|
|
327
|
+
}));
|
|
328
|
+
},
|
|
329
|
+
dispose() {
|
|
330
|
+
disposed = true;
|
|
331
|
+
const dying = sandboxPromise;
|
|
332
|
+
sandboxPromise = null;
|
|
333
|
+
booted = false;
|
|
334
|
+
pendingBoot = null;
|
|
335
|
+
dying?.then((sandbox) => sandbox.dispose()).catch(() => void 0);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
//#endregion
|
|
22
340
|
//#region src/input.ts
|
|
23
341
|
function valueFromEntry(entry) {
|
|
24
342
|
switch (entry.kind) {
|
|
@@ -146,6 +464,242 @@ function installAcroJs(g) {
|
|
|
146
464
|
return wrapper;
|
|
147
465
|
};
|
|
148
466
|
const getField = (name) => state?.fieldsByName.get(String(name))?.wrapper ?? null;
|
|
467
|
+
const ANNOT_WRITABLE = {
|
|
468
|
+
square: [
|
|
469
|
+
"strokeColor",
|
|
470
|
+
"fillColor",
|
|
471
|
+
"opacity",
|
|
472
|
+
"width",
|
|
473
|
+
"borderStyle",
|
|
474
|
+
"dash",
|
|
475
|
+
"rect"
|
|
476
|
+
],
|
|
477
|
+
circle: [
|
|
478
|
+
"strokeColor",
|
|
479
|
+
"fillColor",
|
|
480
|
+
"opacity",
|
|
481
|
+
"width",
|
|
482
|
+
"borderStyle",
|
|
483
|
+
"dash",
|
|
484
|
+
"rect"
|
|
485
|
+
],
|
|
486
|
+
polygon: [
|
|
487
|
+
"strokeColor",
|
|
488
|
+
"fillColor",
|
|
489
|
+
"opacity",
|
|
490
|
+
"width",
|
|
491
|
+
"borderStyle",
|
|
492
|
+
"dash",
|
|
493
|
+
"rect"
|
|
494
|
+
],
|
|
495
|
+
polyline: [
|
|
496
|
+
"strokeColor",
|
|
497
|
+
"fillColor",
|
|
498
|
+
"opacity",
|
|
499
|
+
"width",
|
|
500
|
+
"borderStyle",
|
|
501
|
+
"dash",
|
|
502
|
+
"rect"
|
|
503
|
+
],
|
|
504
|
+
line: [
|
|
505
|
+
"strokeColor",
|
|
506
|
+
"fillColor",
|
|
507
|
+
"opacity",
|
|
508
|
+
"width",
|
|
509
|
+
"borderStyle",
|
|
510
|
+
"dash",
|
|
511
|
+
"rect"
|
|
512
|
+
],
|
|
513
|
+
ink: [
|
|
514
|
+
"strokeColor",
|
|
515
|
+
"opacity",
|
|
516
|
+
"width",
|
|
517
|
+
"rect"
|
|
518
|
+
],
|
|
519
|
+
"free-text": [
|
|
520
|
+
"strokeColor",
|
|
521
|
+
"fillColor",
|
|
522
|
+
"opacity",
|
|
523
|
+
"rect"
|
|
524
|
+
],
|
|
525
|
+
highlight: ["strokeColor", "opacity"],
|
|
526
|
+
underline: ["strokeColor", "opacity"],
|
|
527
|
+
squiggly: ["strokeColor", "opacity"],
|
|
528
|
+
strikeout: ["strokeColor", "opacity"],
|
|
529
|
+
caret: ["strokeColor", "opacity"],
|
|
530
|
+
text: [
|
|
531
|
+
"strokeColor",
|
|
532
|
+
"opacity",
|
|
533
|
+
"rect"
|
|
534
|
+
],
|
|
535
|
+
stamp: ["rect"],
|
|
536
|
+
"file-attachment": ["strokeColor", "opacity"]
|
|
537
|
+
};
|
|
538
|
+
const ANNOT_FLAG_KEYS = [
|
|
539
|
+
"hidden",
|
|
540
|
+
"print",
|
|
541
|
+
"readOnly",
|
|
542
|
+
"locked",
|
|
543
|
+
"noView",
|
|
544
|
+
"toggleNoView"
|
|
545
|
+
];
|
|
546
|
+
const ACROBAT_TYPE = {
|
|
547
|
+
square: "Square",
|
|
548
|
+
circle: "Circle",
|
|
549
|
+
line: "Line",
|
|
550
|
+
polygon: "Polygon",
|
|
551
|
+
polyline: "PolyLine",
|
|
552
|
+
ink: "Ink",
|
|
553
|
+
"free-text": "FreeText",
|
|
554
|
+
highlight: "Highlight",
|
|
555
|
+
underline: "Underline",
|
|
556
|
+
squiggly: "Squiggly",
|
|
557
|
+
strikeout: "StrikeOut",
|
|
558
|
+
text: "Text",
|
|
559
|
+
stamp: "Stamp",
|
|
560
|
+
caret: "Caret",
|
|
561
|
+
"file-attachment": "FileAttachment"
|
|
562
|
+
};
|
|
563
|
+
const isColorArray = (value) => {
|
|
564
|
+
if (!Array.isArray(value) || value.length === 0) return false;
|
|
565
|
+
const expected = {
|
|
566
|
+
T: 0,
|
|
567
|
+
G: 1,
|
|
568
|
+
RGB: 3,
|
|
569
|
+
CMYK: 4
|
|
570
|
+
}[String(value[0])];
|
|
571
|
+
return expected !== void 0 && value.length === expected + 1 && value.slice(1).every((part) => typeof part === "number" && isFinite(Number(part)));
|
|
572
|
+
};
|
|
573
|
+
const colorToRgbTriple = (value) => {
|
|
574
|
+
const color = value;
|
|
575
|
+
const space = String(color[0]);
|
|
576
|
+
if (space === "T") return null;
|
|
577
|
+
if (space === "G") return [
|
|
578
|
+
Number(color[1]),
|
|
579
|
+
Number(color[1]),
|
|
580
|
+
Number(color[1])
|
|
581
|
+
];
|
|
582
|
+
if (space === "RGB") return [
|
|
583
|
+
Number(color[1]),
|
|
584
|
+
Number(color[2]),
|
|
585
|
+
Number(color[3])
|
|
586
|
+
];
|
|
587
|
+
const c = Number(color[1]);
|
|
588
|
+
const m = Number(color[2]);
|
|
589
|
+
const y = Number(color[3]);
|
|
590
|
+
const k = Number(color[4]);
|
|
591
|
+
return [
|
|
592
|
+
1 - Math.min(1, c + k),
|
|
593
|
+
1 - Math.min(1, m + k),
|
|
594
|
+
1 - Math.min(1, y + k)
|
|
595
|
+
];
|
|
596
|
+
};
|
|
597
|
+
const sameAnnotValue = (a, b) => {
|
|
598
|
+
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((item, index) => item === b[index]);
|
|
599
|
+
return a === b;
|
|
600
|
+
};
|
|
601
|
+
const cloneAnnotValue = (value) => Array.isArray(value) ? [...value] : value;
|
|
602
|
+
const makeAnnot = (record) => {
|
|
603
|
+
const input = record.input;
|
|
604
|
+
const subtype = String(input.subtype);
|
|
605
|
+
const writable = ANNOT_WRITABLE[subtype] ?? [];
|
|
606
|
+
const wrapper = {};
|
|
607
|
+
const stage = (key, value, validate) => {
|
|
608
|
+
if (key !== "contents" && ANNOT_FLAG_KEYS.indexOf(key) < 0 && writable.indexOf(key) < 0) {
|
|
609
|
+
diagnostic("unsupported-api", `Annot.${key} is not writable on subtype '${subtype}' (write ignored)`);
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (input.opaqueBody && ANNOT_FLAG_KEYS.indexOf(key) < 0 && key !== "contents") {
|
|
613
|
+
diagnostic("unsupported-api", `Annot.${key}: '${subtype}' has an opaque appearance (write ignored)`);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (!validate(value)) {
|
|
617
|
+
diagnostic("invalid-field-value", `Annot.${key}: invalid value (write ignored)`);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
record.current[key] = cloneAnnotValue(value);
|
|
621
|
+
};
|
|
622
|
+
const prop = (key, validate) => ({
|
|
623
|
+
enumerable: true,
|
|
624
|
+
get: () => cloneAnnotValue(record.current[key]),
|
|
625
|
+
set: (value) => stage(key, value, validate)
|
|
626
|
+
});
|
|
627
|
+
const isNum = (v) => typeof v === "number" && isFinite(v);
|
|
628
|
+
const isBool = (v) => typeof v === "boolean";
|
|
629
|
+
Object.defineProperties(wrapper, {
|
|
630
|
+
name: {
|
|
631
|
+
enumerable: true,
|
|
632
|
+
get: () => String(input.name ?? "")
|
|
633
|
+
},
|
|
634
|
+
type: {
|
|
635
|
+
enumerable: true,
|
|
636
|
+
get: () => ACROBAT_TYPE[subtype] ?? subtype
|
|
637
|
+
},
|
|
638
|
+
page: {
|
|
639
|
+
enumerable: true,
|
|
640
|
+
get: () => Number(input.page ?? 0)
|
|
641
|
+
},
|
|
642
|
+
author: {
|
|
643
|
+
enumerable: true,
|
|
644
|
+
get: () => String(input.author ?? "")
|
|
645
|
+
},
|
|
646
|
+
subject: {
|
|
647
|
+
enumerable: true,
|
|
648
|
+
get: () => String(input.subject ?? "")
|
|
649
|
+
},
|
|
650
|
+
strokeColor: prop("strokeColor", isColorArray),
|
|
651
|
+
fillColor: prop("fillColor", isColorArray),
|
|
652
|
+
opacity: prop("opacity", (v) => isNum(v) && Number(v) >= 0 && Number(v) <= 1),
|
|
653
|
+
width: prop("width", (v) => isNum(v) && Number(v) >= 0),
|
|
654
|
+
style: prop("borderStyle", (v) => v === "S" || v === "D"),
|
|
655
|
+
dash: prop("dash", (v) => Array.isArray(v) && v.every(isNum)),
|
|
656
|
+
rect: prop("rect", (v) => Array.isArray(v) && v.length === 4 && v.every(isNum)),
|
|
657
|
+
contents: prop("contents", (v) => typeof v === "string"),
|
|
658
|
+
hidden: prop("hidden", isBool),
|
|
659
|
+
print: prop("print", isBool),
|
|
660
|
+
readOnly: prop("readOnly", isBool),
|
|
661
|
+
lock: prop("locked", isBool),
|
|
662
|
+
noView: prop("noView", isBool),
|
|
663
|
+
toggleNoView: prop("toggleNoView", isBool),
|
|
664
|
+
doc: {
|
|
665
|
+
enumerable: false,
|
|
666
|
+
get: () => doc
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
wrapper.getProps = () => {
|
|
670
|
+
const out = {};
|
|
671
|
+
for (const key of Object.keys(record.current)) out[key] = cloneAnnotValue(record.current[key]);
|
|
672
|
+
return out;
|
|
673
|
+
};
|
|
674
|
+
wrapper.setProps = (props) => {
|
|
675
|
+
const source = props ?? {};
|
|
676
|
+
for (const key of Object.keys(source)) wrapper[key] = source[key];
|
|
677
|
+
};
|
|
678
|
+
return wrapper;
|
|
679
|
+
};
|
|
680
|
+
const getAnnots = (spec) => {
|
|
681
|
+
if (!state) return null;
|
|
682
|
+
const nPage = spec?.nPage;
|
|
683
|
+
if (nPage === void 0 || nPage === null) {
|
|
684
|
+
if (!state.annotsCoverDocument) diagnostic("unsupported-api", "getAnnots: whole-document search is limited to the prefetched pages (compatibility deviation)");
|
|
685
|
+
return state.annots.length ? state.annots.map((record) => record.wrapper) : null;
|
|
686
|
+
}
|
|
687
|
+
const page = Math.trunc(Number(nPage));
|
|
688
|
+
if (!state.annotPages.has(page)) {
|
|
689
|
+
diagnostic("unsupported-api", `getAnnots: page ${page} is outside the prefetched annots plane (compatibility deviation)`);
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
const records = state.annotsByPage.get(page) ?? [];
|
|
693
|
+
return records.length ? records.map((record) => record.wrapper) : null;
|
|
694
|
+
};
|
|
695
|
+
const getAnnot = (nPage, name) => {
|
|
696
|
+
const records = getAnnots({ nPage });
|
|
697
|
+
if (!records) return null;
|
|
698
|
+
for (const wrapper of records) if (String(wrapper.name) === String(name)) return wrapper;
|
|
699
|
+
return null;
|
|
700
|
+
};
|
|
701
|
+
doc.getAnnots = getAnnots;
|
|
702
|
+
doc.getAnnot = getAnnot;
|
|
149
703
|
Object.defineProperties(doc, {
|
|
150
704
|
documentFileName: {
|
|
151
705
|
enumerable: true,
|
|
@@ -182,7 +736,26 @@ function installAcroJs(g) {
|
|
|
182
736
|
}
|
|
183
737
|
};
|
|
184
738
|
doc.print = () => state?.uiEffects.push({ kind: "print" });
|
|
185
|
-
doc.submitForm = () =>
|
|
739
|
+
doc.submitForm = (cUrlOrArgs, bFDF, bEmpty, aFields) => {
|
|
740
|
+
if (!state) return;
|
|
741
|
+
const args = cUrlOrArgs !== null && typeof cUrlOrArgs === "object" ? cUrlOrArgs : {
|
|
742
|
+
cURL: cUrlOrArgs,
|
|
743
|
+
bFDF,
|
|
744
|
+
bEmpty,
|
|
745
|
+
aFields
|
|
746
|
+
};
|
|
747
|
+
const names = Array.isArray(args.aFields) ? args.aFields.map((name) => String(name)) : null;
|
|
748
|
+
const submitAs = args.cSubmitAs === void 0 ? void 0 : String(args.cSubmitAs).toUpperCase();
|
|
749
|
+
const format = submitAs === "FDF" ? "fdf" : submitAs === "XFDF" ? "xfdf" : submitAs === "HTML" ? "html" : submitAs === "PDF" ? "pdf" : args.bFDF === false ? "html" : void 0;
|
|
750
|
+
state.uiEffects.push({
|
|
751
|
+
kind: "submitForm",
|
|
752
|
+
url: args.cURL === void 0 || args.cURL === null ? null : String(args.cURL),
|
|
753
|
+
fieldNames: names,
|
|
754
|
+
includeEmpty: Boolean(args.bEmpty),
|
|
755
|
+
...format === void 0 ? {} : { format },
|
|
756
|
+
...args.bGet === void 0 ? {} : { method: args.bGet ? "get" : "post" }
|
|
757
|
+
});
|
|
758
|
+
};
|
|
186
759
|
doc.mailDoc = () => blocked("mailDoc");
|
|
187
760
|
doc.getURL = () => blocked("getURL");
|
|
188
761
|
const app = {
|
|
@@ -250,6 +823,7 @@ function installAcroJs(g) {
|
|
|
250
823
|
const hour = local.getUTCHours();
|
|
251
824
|
const minute = local.getUTCMinutes();
|
|
252
825
|
const second = local.getUTCSeconds();
|
|
826
|
+
const hour12 = hour % 12 === 0 ? 12 : hour % 12;
|
|
253
827
|
const tokens = {
|
|
254
828
|
yyyy: String(year),
|
|
255
829
|
yy: pad(year % 100),
|
|
@@ -260,12 +834,15 @@ function installAcroJs(g) {
|
|
|
260
834
|
dd: pad(day),
|
|
261
835
|
d: String(day),
|
|
262
836
|
HH: pad(hour),
|
|
837
|
+
hh: pad(hour12),
|
|
838
|
+
h: String(hour12),
|
|
263
839
|
H: String(hour),
|
|
264
840
|
MM: pad(minute),
|
|
265
841
|
M: String(minute),
|
|
266
|
-
ss: pad(second)
|
|
842
|
+
ss: pad(second),
|
|
843
|
+
tt: hour < 12 ? "am" : "pm"
|
|
267
844
|
};
|
|
268
|
-
return String(format).replace(/yyyy|mmmm|mmm|yy|mm|dd|HH|MM|ss|m|d|H|M/g, (token) => tokens[token]);
|
|
845
|
+
return String(format).replace(/yyyy|mmmm|mmm|yy|mm|dd|HH|hh|MM|ss|tt|m|d|H|h|M/g, (token) => tokens[token]);
|
|
269
846
|
};
|
|
270
847
|
const util = {
|
|
271
848
|
printd,
|
|
@@ -295,14 +872,53 @@ function installAcroJs(g) {
|
|
|
295
872
|
});
|
|
296
873
|
const fieldsByName = new Map(fields.map((field) => [String(field.input.name), field]));
|
|
297
874
|
const fieldsByRef = new Map(fields.map((field) => [refKey(field.input.ref), field]));
|
|
875
|
+
const annots = (input.annots ?? []).map((annot) => {
|
|
876
|
+
const writableSnapshot = () => ({
|
|
877
|
+
strokeColor: cloneAnnotValue(annot.strokeColor ?? ["T"]),
|
|
878
|
+
fillColor: cloneAnnotValue(annot.fillColor ?? ["T"]),
|
|
879
|
+
opacity: Number(annot.opacity ?? 1),
|
|
880
|
+
width: Number(annot.width ?? 1),
|
|
881
|
+
borderStyle: String(annot.borderStyle ?? "S"),
|
|
882
|
+
dash: cloneAnnotValue(annot.dash ?? []),
|
|
883
|
+
rect: cloneAnnotValue(annot.rect ?? [
|
|
884
|
+
0,
|
|
885
|
+
0,
|
|
886
|
+
0,
|
|
887
|
+
0
|
|
888
|
+
]),
|
|
889
|
+
contents: String(annot.contents ?? ""),
|
|
890
|
+
hidden: Boolean(annot.hidden),
|
|
891
|
+
print: Boolean(annot.print),
|
|
892
|
+
readOnly: Boolean(annot.readOnly),
|
|
893
|
+
locked: Boolean(annot.locked),
|
|
894
|
+
noView: Boolean(annot.noView),
|
|
895
|
+
toggleNoView: Boolean(annot.toggleNoView)
|
|
896
|
+
});
|
|
897
|
+
const record = {
|
|
898
|
+
input: annot,
|
|
899
|
+
original: writableSnapshot(),
|
|
900
|
+
current: writableSnapshot(),
|
|
901
|
+
wrapper: {}
|
|
902
|
+
};
|
|
903
|
+
record.wrapper = makeAnnot(record);
|
|
904
|
+
return record;
|
|
905
|
+
});
|
|
906
|
+
const annotsByPage = /* @__PURE__ */ new Map();
|
|
907
|
+
for (const record of annots) {
|
|
908
|
+
const page = Number(record.input.page ?? 0);
|
|
909
|
+
const bucket = annotsByPage.get(page);
|
|
910
|
+
if (bucket) bucket.push(record);
|
|
911
|
+
else annotsByPage.set(page, [record]);
|
|
912
|
+
}
|
|
298
913
|
const eventInput = input.event ?? {};
|
|
299
914
|
const target = fieldsByRef.get(refKey(eventInput.target));
|
|
300
915
|
const source = fieldsByRef.get(refKey(eventInput.source));
|
|
301
916
|
const initialValue = eventInput.value !== void 0 ? cloneValue(eventInput.value) : cloneValue(target?.value ?? null);
|
|
917
|
+
const eventType = eventInput.type !== void 0 ? String(eventInput.type) : eventInput.kind === "name-tree-boot" ? "Doc" : "Field";
|
|
302
918
|
const event = {
|
|
303
|
-
name: eventName(String(eventInput.kind ?? "")),
|
|
304
|
-
type:
|
|
305
|
-
target: target?.wrapper ?? null,
|
|
919
|
+
name: eventInput.name !== void 0 ? String(eventInput.name) : eventName(String(eventInput.kind ?? "")),
|
|
920
|
+
type: eventType,
|
|
921
|
+
target: target?.wrapper ?? (eventType === "Doc" ? doc : null),
|
|
306
922
|
targetName: String(target?.input.name ?? ""),
|
|
307
923
|
source: source?.wrapper ?? { source: doc },
|
|
308
924
|
value: initialValue,
|
|
@@ -319,6 +935,10 @@ function installAcroJs(g) {
|
|
|
319
935
|
fields,
|
|
320
936
|
fieldsByName,
|
|
321
937
|
fieldsByRef,
|
|
938
|
+
annots,
|
|
939
|
+
annotsByPage,
|
|
940
|
+
annotPages: new Set((input.annotPages ?? []).map((page) => Math.trunc(Number(page)))),
|
|
941
|
+
annotsCoverDocument: Boolean(input.annotsCoverDocument),
|
|
322
942
|
event,
|
|
323
943
|
resetRefs: [],
|
|
324
944
|
resetKeys: /* @__PURE__ */ new Set(),
|
|
@@ -391,10 +1011,26 @@ function installAcroJs(g) {
|
|
|
391
1011
|
ref: target.input.ref,
|
|
392
1012
|
text: String(state.event.value ?? "")
|
|
393
1013
|
});
|
|
394
|
-
const
|
|
1014
|
+
const annotEffects = [];
|
|
1015
|
+
for (const record of state.annots) {
|
|
1016
|
+
const patch = {};
|
|
1017
|
+
const flags = {};
|
|
1018
|
+
for (const key of Object.keys(record.current)) {
|
|
1019
|
+
if (sameAnnotValue(record.current[key], record.original[key])) continue;
|
|
1020
|
+
if (ANNOT_FLAG_KEYS.indexOf(key) >= 0) flags[key] = record.current[key];
|
|
1021
|
+
else patch[key] = cloneAnnotValue(record.current[key]);
|
|
1022
|
+
}
|
|
1023
|
+
if (Object.keys(flags).length > 0) patch.flags = flags;
|
|
1024
|
+
if (Object.keys(patch).length > 0) annotEffects.push({
|
|
1025
|
+
ref: record.input.ref,
|
|
1026
|
+
patch
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
const totalEffects = formEffects.length + annotEffects.length + state.uiEffects.length;
|
|
395
1030
|
if (totalEffects > state.maxEffects) return {
|
|
396
1031
|
event: eventOutput(),
|
|
397
1032
|
formEffects: [],
|
|
1033
|
+
annotEffects: [],
|
|
398
1034
|
uiEffects: [],
|
|
399
1035
|
diagnostics: [...state.diagnostics],
|
|
400
1036
|
error: {
|
|
@@ -405,6 +1041,7 @@ function installAcroJs(g) {
|
|
|
405
1041
|
return {
|
|
406
1042
|
event: eventOutput(),
|
|
407
1043
|
formEffects,
|
|
1044
|
+
annotEffects,
|
|
408
1045
|
uiEffects: [...state.uiEffects],
|
|
409
1046
|
diagnostics: [...state.diagnostics]
|
|
410
1047
|
};
|
|
@@ -412,6 +1049,7 @@ function installAcroJs(g) {
|
|
|
412
1049
|
const failed = (error) => ({
|
|
413
1050
|
event: eventOutput(),
|
|
414
1051
|
formEffects: [],
|
|
1052
|
+
annotEffects: [],
|
|
415
1053
|
uiEffects: [],
|
|
416
1054
|
diagnostics: [...state?.diagnostics ?? []],
|
|
417
1055
|
error: {
|
|
@@ -461,7 +1099,87 @@ function installAcroJs(g) {
|
|
|
461
1099
|
noPrint: 2,
|
|
462
1100
|
noView: 3
|
|
463
1101
|
});
|
|
1102
|
+
host.color = Object.freeze({
|
|
1103
|
+
transparent: Object.freeze(["T"]),
|
|
1104
|
+
black: Object.freeze(["G", 0]),
|
|
1105
|
+
white: Object.freeze(["G", 1]),
|
|
1106
|
+
gray: Object.freeze(["G", .5]),
|
|
1107
|
+
ltGray: Object.freeze(["G", .75]),
|
|
1108
|
+
dkGray: Object.freeze(["G", .25]),
|
|
1109
|
+
red: Object.freeze([
|
|
1110
|
+
"RGB",
|
|
1111
|
+
1,
|
|
1112
|
+
0,
|
|
1113
|
+
0
|
|
1114
|
+
]),
|
|
1115
|
+
green: Object.freeze([
|
|
1116
|
+
"RGB",
|
|
1117
|
+
0,
|
|
1118
|
+
1,
|
|
1119
|
+
0
|
|
1120
|
+
]),
|
|
1121
|
+
blue: Object.freeze([
|
|
1122
|
+
"RGB",
|
|
1123
|
+
0,
|
|
1124
|
+
0,
|
|
1125
|
+
1
|
|
1126
|
+
]),
|
|
1127
|
+
cyan: Object.freeze([
|
|
1128
|
+
"CMYK",
|
|
1129
|
+
1,
|
|
1130
|
+
0,
|
|
1131
|
+
0,
|
|
1132
|
+
0
|
|
1133
|
+
]),
|
|
1134
|
+
magenta: Object.freeze([
|
|
1135
|
+
"CMYK",
|
|
1136
|
+
0,
|
|
1137
|
+
1,
|
|
1138
|
+
0,
|
|
1139
|
+
0
|
|
1140
|
+
]),
|
|
1141
|
+
yellow: Object.freeze([
|
|
1142
|
+
"CMYK",
|
|
1143
|
+
0,
|
|
1144
|
+
0,
|
|
1145
|
+
1,
|
|
1146
|
+
0
|
|
1147
|
+
]),
|
|
1148
|
+
convert: (value, space) => {
|
|
1149
|
+
if (!isColorArray(value)) return ["T"];
|
|
1150
|
+
const target = String(space);
|
|
1151
|
+
const rgb = colorToRgbTriple(value);
|
|
1152
|
+
if (target === "T" || rgb === null) return ["T"];
|
|
1153
|
+
if (target === "G") return ["G", .3 * rgb[0] + .59 * rgb[1] + .11 * rgb[2]];
|
|
1154
|
+
if (target === "RGB") return [
|
|
1155
|
+
"RGB",
|
|
1156
|
+
rgb[0],
|
|
1157
|
+
rgb[1],
|
|
1158
|
+
rgb[2]
|
|
1159
|
+
];
|
|
1160
|
+
if (target === "CMYK") {
|
|
1161
|
+
const k = 1 - Math.max(rgb[0], rgb[1], rgb[2]);
|
|
1162
|
+
return [
|
|
1163
|
+
"CMYK",
|
|
1164
|
+
1 - rgb[0] - k,
|
|
1165
|
+
1 - rgb[1] - k,
|
|
1166
|
+
1 - rgb[2] - k,
|
|
1167
|
+
k
|
|
1168
|
+
];
|
|
1169
|
+
}
|
|
1170
|
+
return ["T"];
|
|
1171
|
+
},
|
|
1172
|
+
equal: (a, b) => {
|
|
1173
|
+
if (!isColorArray(a) || !isColorArray(b)) return false;
|
|
1174
|
+
const left = colorToRgbTriple(a);
|
|
1175
|
+
const right = colorToRgbTriple(b);
|
|
1176
|
+
if (left === null || right === null) return left === right;
|
|
1177
|
+
return left.every((component, index) => Math.abs(component - right[index]) < 1e-6);
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
464
1180
|
host.getField = getField;
|
|
1181
|
+
host.getAnnots = getAnnots;
|
|
1182
|
+
host.getAnnot = getAnnot;
|
|
465
1183
|
host.resetForm = doc.resetForm;
|
|
466
1184
|
host.print = doc.print;
|
|
467
1185
|
host.submitForm = doc.submitForm;
|
|
@@ -523,8 +1241,226 @@ function installAcroJs(g) {
|
|
|
523
1241
|
const places = Math.max(0, Math.min(10, Math.trunc(Number(decimals))));
|
|
524
1242
|
state.event.value = `${(numberValue(state.event.value) * 100).toFixed(places)}%`;
|
|
525
1243
|
};
|
|
526
|
-
|
|
527
|
-
|
|
1244
|
+
const DATE_FORMATS = [
|
|
1245
|
+
"m/d",
|
|
1246
|
+
"m/d/yy",
|
|
1247
|
+
"mm/dd/yy",
|
|
1248
|
+
"mm/yy",
|
|
1249
|
+
"d-mmm",
|
|
1250
|
+
"d-mmm-yy",
|
|
1251
|
+
"dd-mmm-yy",
|
|
1252
|
+
"yy-mm-dd",
|
|
1253
|
+
"mmm-yy",
|
|
1254
|
+
"mmmm-yy",
|
|
1255
|
+
"mmm d, yyyy",
|
|
1256
|
+
"mmmm d, yyyy",
|
|
1257
|
+
"m/d/yy h:MM tt",
|
|
1258
|
+
"m/d/yy HH:MM"
|
|
1259
|
+
];
|
|
1260
|
+
const TIME_FORMATS = [
|
|
1261
|
+
"HH:MM",
|
|
1262
|
+
"h:MM tt",
|
|
1263
|
+
"HH:MM:ss",
|
|
1264
|
+
"h:MM:ss tt"
|
|
1265
|
+
];
|
|
1266
|
+
const rejectWithAlert = (message) => {
|
|
1267
|
+
if (!state) return;
|
|
1268
|
+
state.uiEffects.push({
|
|
1269
|
+
kind: "alert",
|
|
1270
|
+
message,
|
|
1271
|
+
icon: 0
|
|
1272
|
+
});
|
|
1273
|
+
state.event.rc = false;
|
|
1274
|
+
};
|
|
1275
|
+
const formatMismatch = () => rejectWithAlert(`The value entered does not match the format of the field [ ${String(state?.event.targetName ?? "")} ]`);
|
|
1276
|
+
const dateMismatch = (format) => rejectWithAlert(`Invalid date/time: please ensure that the date/time exists. Field [ ${String(state?.event.targetName ?? "")} ] should match format ${String(format ?? "")}`);
|
|
1277
|
+
const mergeChange = (eventObject) => {
|
|
1278
|
+
const e = eventObject ?? state?.event ?? {};
|
|
1279
|
+
const value = String(e.value ?? "");
|
|
1280
|
+
if (e.willCommit) return value;
|
|
1281
|
+
const start = Math.max(0, Math.trunc(Number(e.selStart ?? 0)));
|
|
1282
|
+
const end = Math.max(start, Math.trunc(Number(e.selEnd ?? 0)));
|
|
1283
|
+
return value.slice(0, start) + String(e.change ?? "") + value.slice(end);
|
|
1284
|
+
};
|
|
1285
|
+
host.AFMergeChange = mergeChange;
|
|
1286
|
+
const makeNumber = (value) => {
|
|
1287
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
1288
|
+
let text = String(value ?? "").trim();
|
|
1289
|
+
if (text === "") return null;
|
|
1290
|
+
text = text.replace(/[^0-9,.\-+eE]/g, "");
|
|
1291
|
+
if (text.includes(",") && text.includes(".")) text = text.lastIndexOf(",") > text.lastIndexOf(".") ? text.replace(/\./g, "").replace(",", ".") : text.replace(/,/g, "");
|
|
1292
|
+
else if (text.includes(",")) text = (text.match(/,/g) ?? []).length === 1 ? text.replace(",", ".") : text.replace(/,/g, "");
|
|
1293
|
+
const parsed = Number(text);
|
|
1294
|
+
return text !== "" && Number.isFinite(parsed) ? parsed : null;
|
|
1295
|
+
};
|
|
1296
|
+
host.AFMakeNumber = makeNumber;
|
|
1297
|
+
host.AFExtractNums = (value) => {
|
|
1298
|
+
const matches = String(value ?? "").match(/\d+/g);
|
|
1299
|
+
return matches && matches.length > 0 ? [...matches] : null;
|
|
1300
|
+
};
|
|
1301
|
+
const validNumberText = (text, separatorStyle) => {
|
|
1302
|
+
const trimmed = text.trim();
|
|
1303
|
+
if (trimmed === "") return true;
|
|
1304
|
+
return (separatorStyle === 2 || separatorStyle === 3 ? /^[+-]?(\d+|\d{1,3}(\.\d{3})+)?(,\d*)?$/ : /^[+-]?(\d+|\d{1,3}(,\d{3})+)?(\.\d*)?$/).test(trimmed) && /\d/.test(trimmed);
|
|
1305
|
+
};
|
|
1306
|
+
host.AFNumber_Keystroke = (_decimals, separatorStyle, _negativeStyle, _currencyStyle, currency) => {
|
|
1307
|
+
if (!state || !state.event.willCommit) return;
|
|
1308
|
+
const proposed = mergeChange(state.event);
|
|
1309
|
+
const cleaned = currency ? proposed.split(String(currency)).join("") : proposed;
|
|
1310
|
+
if (!validNumberText(cleaned, Math.trunc(Number(separatorStyle)))) formatMismatch();
|
|
1311
|
+
};
|
|
1312
|
+
host.AFPercent_Keystroke = (_decimals, separatorStyle) => {
|
|
1313
|
+
if (!state || !state.event.willCommit) return;
|
|
1314
|
+
const cleaned = mergeChange(state.event).replace(/%\s*$/, "");
|
|
1315
|
+
if (!validNumberText(cleaned, Math.trunc(Number(separatorStyle)))) formatMismatch();
|
|
1316
|
+
};
|
|
1317
|
+
const digitsOf = (value) => String(value ?? "").replace(/\D/g, "");
|
|
1318
|
+
const afSpecialFormat = (psf) => {
|
|
1319
|
+
if (!state) return;
|
|
1320
|
+
const digits = digitsOf(state.event.value);
|
|
1321
|
+
const kind = Math.trunc(Number(psf));
|
|
1322
|
+
let formatted = String(state.event.value ?? "");
|
|
1323
|
+
if (kind === 0) formatted = digits.slice(0, 5);
|
|
1324
|
+
else if (kind === 1) formatted = `${digits.slice(0, 5)}-${digits.slice(5, 9)}`;
|
|
1325
|
+
else if (kind === 2) formatted = digits.length >= 10 ? `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6, 10)}` : `${digits.slice(0, 3)}-${digits.slice(3, 7)}`;
|
|
1326
|
+
else if (kind === 3) formatted = `${digits.slice(0, 3)}-${digits.slice(3, 5)}-${digits.slice(5, 9)}`;
|
|
1327
|
+
state.event.value = formatted;
|
|
1328
|
+
};
|
|
1329
|
+
host.AFSpecial_Format = afSpecialFormat;
|
|
1330
|
+
host.AFSpecial_Keystroke = (psf) => {
|
|
1331
|
+
if (!state || !state.event.willCommit) return;
|
|
1332
|
+
const merged = mergeChange(state.event);
|
|
1333
|
+
if (merged.trim() === "") return;
|
|
1334
|
+
const digits = digitsOf(merged).length;
|
|
1335
|
+
const kind = Math.trunc(Number(psf));
|
|
1336
|
+
if (!(kind === 0 ? digits === 5 : kind === 1 ? digits === 9 : kind === 2 ? digits === 7 || digits === 10 : digits === 9)) formatMismatch();
|
|
1337
|
+
};
|
|
1338
|
+
host.AFRange_Validate = (useGreater, greaterThan, useLess, lessThan) => {
|
|
1339
|
+
if (!state) return;
|
|
1340
|
+
const value = makeNumber(state.event.value);
|
|
1341
|
+
if (value === null) return;
|
|
1342
|
+
const hasGreater = Boolean(useGreater);
|
|
1343
|
+
const hasLess = Boolean(useLess);
|
|
1344
|
+
const low = Number(greaterThan);
|
|
1345
|
+
const high = Number(lessThan);
|
|
1346
|
+
if ((!hasGreater || value >= low) && (!hasLess || value <= high)) return;
|
|
1347
|
+
rejectWithAlert(hasGreater && hasLess ? `Invalid value: must be greater than or equal to ${low} and less than or equal to ${high}.` : hasGreater ? `Invalid value: must be greater than or equal to ${low}.` : `Invalid value: must be less than or equal to ${high}.`);
|
|
1348
|
+
};
|
|
1349
|
+
const nowWall = () => {
|
|
1350
|
+
const env = state?.input.environment ?? {};
|
|
1351
|
+
const local = new NativeDate(Number(env.nowMs ?? 0) + Number(env.utcOffsetMinutes ?? 0) * 6e4);
|
|
1352
|
+
return {
|
|
1353
|
+
y: local.getUTCFullYear(),
|
|
1354
|
+
m: local.getUTCMonth() + 1,
|
|
1355
|
+
d: local.getUTCDate(),
|
|
1356
|
+
H: 0,
|
|
1357
|
+
M: 0,
|
|
1358
|
+
S: 0
|
|
1359
|
+
};
|
|
1360
|
+
};
|
|
1361
|
+
/** printd adds the environment offset itself, so a wall time converts back. */
|
|
1362
|
+
const wallDate = (parts) => {
|
|
1363
|
+
const env = state?.input.environment ?? {};
|
|
1364
|
+
return new NativeDate(NativeDate.UTC(parts.y, parts.m - 1, parts.d, parts.H, parts.M, parts.S) - Number(env.utcOffsetMinutes ?? 0) * 6e4);
|
|
1365
|
+
};
|
|
1366
|
+
/** Lenient scand-style parse: numbers mapped by the format's token order,
|
|
1367
|
+
* month names accepted, missing parts defaulted like Acrobat's scand. */
|
|
1368
|
+
const parseDateLoose = (raw, format) => {
|
|
1369
|
+
const text = String(raw ?? "").trim();
|
|
1370
|
+
if (text === "") return null;
|
|
1371
|
+
const lower = text.toLowerCase();
|
|
1372
|
+
let namedMonth = null;
|
|
1373
|
+
for (let index = 0; index < 12; index += 1) if (lower.indexOf(monthShort[index].toLowerCase()) >= 0) {
|
|
1374
|
+
namedMonth = index + 1;
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
const timeMatch = /(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(am|pm)?/i.exec(text);
|
|
1378
|
+
let H = 0;
|
|
1379
|
+
let M = 0;
|
|
1380
|
+
let S = 0;
|
|
1381
|
+
if (timeMatch) {
|
|
1382
|
+
H = Number(timeMatch[1]);
|
|
1383
|
+
M = Number(timeMatch[2]);
|
|
1384
|
+
S = Number(timeMatch[3] ?? 0);
|
|
1385
|
+
const marker = String(timeMatch[4] ?? "").toLowerCase();
|
|
1386
|
+
if (marker === "pm" && H < 12) H += 12;
|
|
1387
|
+
if (marker === "am" && H === 12) H = 0;
|
|
1388
|
+
if (H > 23 || M > 59 || S > 59) return null;
|
|
1389
|
+
}
|
|
1390
|
+
const numbers = ((timeMatch ? text.slice(0, timeMatch.index) + text.slice(timeMatch.index + timeMatch[0].length) : text).match(/\d+/g) ?? []).map((token) => Number(token));
|
|
1391
|
+
const order = [];
|
|
1392
|
+
for (const token of String(format ?? "").match(/yyyy|mmmm|mmm|yy|mm|dd|m|d/g) ?? []) {
|
|
1393
|
+
const kind = token[0] === "y" ? "y" : token[0] === "m" ? "m" : "d";
|
|
1394
|
+
if (order.indexOf(kind) < 0) order.push(kind);
|
|
1395
|
+
}
|
|
1396
|
+
let y = null;
|
|
1397
|
+
let m = namedMonth;
|
|
1398
|
+
let d = null;
|
|
1399
|
+
let cursor = 0;
|
|
1400
|
+
for (const kind of order) {
|
|
1401
|
+
if (kind === "m" && namedMonth !== null) continue;
|
|
1402
|
+
if (cursor >= numbers.length) break;
|
|
1403
|
+
const value = numbers[cursor];
|
|
1404
|
+
cursor += 1;
|
|
1405
|
+
if (kind === "y") y = value;
|
|
1406
|
+
else if (kind === "m") m = value;
|
|
1407
|
+
else d = value;
|
|
1408
|
+
}
|
|
1409
|
+
if (y === null && m === null && d === null && !timeMatch) return null;
|
|
1410
|
+
const today = nowWall();
|
|
1411
|
+
if (y === null) y = today.y;
|
|
1412
|
+
else if (y < 100) y = y < 50 ? 2e3 + y : 1900 + y;
|
|
1413
|
+
if (m === null) m = today.m;
|
|
1414
|
+
if (d === null) d = 1;
|
|
1415
|
+
if (m < 1 || m > 12 || d < 1 || d > 31) return null;
|
|
1416
|
+
const probe = new NativeDate(NativeDate.UTC(y, m - 1, d));
|
|
1417
|
+
if (probe.getUTCFullYear() !== y || probe.getUTCMonth() !== m - 1 || probe.getUTCDate() !== d) return null;
|
|
1418
|
+
return {
|
|
1419
|
+
y,
|
|
1420
|
+
m,
|
|
1421
|
+
d,
|
|
1422
|
+
H,
|
|
1423
|
+
M,
|
|
1424
|
+
S
|
|
1425
|
+
};
|
|
1426
|
+
};
|
|
1427
|
+
const afDateFormatEx = (format) => {
|
|
1428
|
+
if (!state) return;
|
|
1429
|
+
const raw = String(state.event.value ?? "");
|
|
1430
|
+
if (raw.trim() === "") return;
|
|
1431
|
+
let date = new FixedDate(raw);
|
|
1432
|
+
if (Number.isNaN(date.getTime())) {
|
|
1433
|
+
const parts = parseDateLoose(raw, format);
|
|
1434
|
+
if (!parts) return;
|
|
1435
|
+
date = wallDate(parts);
|
|
1436
|
+
}
|
|
1437
|
+
state.event.value = printd(format, date);
|
|
1438
|
+
};
|
|
1439
|
+
host.AFDate_FormatEx = afDateFormatEx;
|
|
1440
|
+
host.AFDate_Format = (index) => afDateFormatEx(DATE_FORMATS[Math.trunc(Number(index))] ?? "m/d/yy");
|
|
1441
|
+
const afDateKeystrokeEx = (format) => {
|
|
1442
|
+
if (!state || !state.event.willCommit) return;
|
|
1443
|
+
const merged = mergeChange(state.event);
|
|
1444
|
+
if (merged.trim() === "") return;
|
|
1445
|
+
if (!parseDateLoose(merged, format)) dateMismatch(format);
|
|
1446
|
+
};
|
|
1447
|
+
host.AFDate_KeystrokeEx = afDateKeystrokeEx;
|
|
1448
|
+
host.AFDate_Keystroke = (index) => afDateKeystrokeEx(DATE_FORMATS[Math.trunc(Number(index))] ?? "m/d/yy");
|
|
1449
|
+
const afTimeFormatEx = (format) => {
|
|
1450
|
+
if (!state) return;
|
|
1451
|
+
const raw = String(state.event.value ?? "");
|
|
1452
|
+
if (raw.trim() === "") return;
|
|
1453
|
+
const parts = parseDateLoose(raw, format);
|
|
1454
|
+
if (!parts) return;
|
|
1455
|
+
state.event.value = printd(format, wallDate(parts));
|
|
1456
|
+
};
|
|
1457
|
+
host.AFTime_FormatEx = afTimeFormatEx;
|
|
1458
|
+
host.AFTime_Format = (index) => afTimeFormatEx(TIME_FORMATS[Math.trunc(Number(index))] ?? "HH:MM");
|
|
1459
|
+
host.AFTime_Keystroke = (index) => {
|
|
1460
|
+
if (!state || !state.event.willCommit) return;
|
|
1461
|
+
const merged = mergeChange(state.event);
|
|
1462
|
+
if (merged.trim() === "") return;
|
|
1463
|
+
if (!parseDateLoose(merged, TIME_FORMATS[Math.trunc(Number(index))] ?? "HH:MM")) dateMismatch(TIME_FORMATS[Math.trunc(Number(index))] ?? "HH:MM");
|
|
528
1464
|
};
|
|
529
1465
|
host.fetch = void 0;
|
|
530
1466
|
host.XMLHttpRequest = void 0;
|
|
@@ -533,50 +1469,21 @@ function installAcroJs(g) {
|
|
|
533
1469
|
/** The exact self-contained source evaluated in Node parity tests and QuickJS. */
|
|
534
1470
|
const PRELUDE_SOURCE = `(${installAcroJs.toString()})(globalThis);`;
|
|
535
1471
|
//#endregion
|
|
536
|
-
|
|
537
|
-
const DEFAULT_SCRIPT_BUDGET = Object.freeze({
|
|
538
|
-
maxExecutionMs: 50,
|
|
539
|
-
maxMemoryBytes: 16 * 1024 * 1024,
|
|
540
|
-
maxStackBytes: 512 * 1024,
|
|
541
|
-
maxEffects: 256,
|
|
542
|
-
maxOutputBytes: 1024 * 1024
|
|
543
|
-
});
|
|
544
|
-
const DEFAULT_SCRIPT_SECURITY_POLICY = Object.freeze({
|
|
545
|
-
enabled: false,
|
|
546
|
-
executionOwner: "originating-client-only",
|
|
547
|
-
nameTreeBoot: "lazy-first-transaction",
|
|
548
|
-
submitForm: "blocked",
|
|
549
|
-
openAction: "preserve-only",
|
|
550
|
-
pageActions: "preserve-only",
|
|
551
|
-
catalogLifecycleActions: "preserve-only",
|
|
552
|
-
annotationActions: "widget-activate-only"
|
|
553
|
-
});
|
|
554
|
-
/** Frozen V1 dispatch matrix; extracted actions outside it remain data only. */
|
|
555
|
-
const SCRIPT_EVENT_MATRIX = Object.freeze({
|
|
556
|
-
nameTree: "lazy-first-originating-field-transaction",
|
|
557
|
-
field: Object.freeze({
|
|
558
|
-
keystroke: "execute-on-commit",
|
|
559
|
-
validate: "execute",
|
|
560
|
-
calculate: "execute-in-calculation-order",
|
|
561
|
-
format: "execute-after-value-calculation"
|
|
562
|
-
}),
|
|
563
|
-
openAction: "preserve-only",
|
|
564
|
-
page: "preserve-only",
|
|
565
|
-
catalogLifecycle: "preserve-only",
|
|
566
|
-
annotation: Object.freeze({
|
|
567
|
-
widgetActivate: "execute-on-originating-client",
|
|
568
|
-
other: "preserve-only"
|
|
569
|
-
}),
|
|
570
|
-
submitForm: "blocked"
|
|
571
|
-
});
|
|
572
|
-
//#endregion
|
|
1472
|
+
exports.ANNOT_WRITABLE_KEYS = ANNOT_WRITABLE_KEYS;
|
|
573
1473
|
exports.DEFAULT_SCRIPT_BUDGET = DEFAULT_SCRIPT_BUDGET;
|
|
574
1474
|
exports.DEFAULT_SCRIPT_SECURITY_POLICY = DEFAULT_SCRIPT_SECURITY_POLICY;
|
|
575
1475
|
exports.PRELUDE_SOURCE = PRELUDE_SOURCE;
|
|
576
1476
|
exports.SCRIPT_EVENT_MATRIX = SCRIPT_EVENT_MATRIX;
|
|
1477
|
+
exports.createScriptHost = createScriptHost;
|
|
1478
|
+
exports.cssToScriptColor = cssToScriptColor;
|
|
577
1479
|
exports.installAcroJs = installAcroJs;
|
|
1480
|
+
exports.isScriptColorArray = isScriptColorArray;
|
|
578
1481
|
exports.javaScriptProgramFromActionTree = javaScriptProgramFromActionTree;
|
|
579
1482
|
exports.javaScriptSourcesFromActionTree = javaScriptSourcesFromActionTree;
|
|
1483
|
+
exports.resolveScriptIdentity = resolveScriptIdentity;
|
|
1484
|
+
exports.scriptColorToCss = scriptColorToCss;
|
|
1485
|
+
exports.scriptColorToRgb = scriptColorToRgb;
|
|
580
1486
|
exports.scriptFieldsFromSnapshot = scriptFieldsFromSnapshot;
|
|
1487
|
+
exports.seedFrom = seedFrom;
|
|
581
1488
|
|
|
582
1489
|
//# sourceMappingURL=index.cjs.map
|