@embedpdf/core-acrojs 3.0.0-next.13 → 3.0.0-next.15
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 +141 -128
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -26
- package/dist/index.d.ts +30 -26
- package/dist/index.js +141 -128
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
//#region src/actions.ts
|
|
2
2
|
/**
|
|
3
3
|
* Return executable JavaScript in PDF action order (root, then `/Next`
|
|
4
|
-
* depth-first). A poisoned native snapshot is never partially executed.
|
|
4
|
+
* depth-first). A poisoned native snapshot is never partially executed. Any
|
|
5
|
+
* kind of destination will do: only the scripts are read.
|
|
5
6
|
*/
|
|
6
7
|
function javaScriptSourcesFromActionTree(tree) {
|
|
7
8
|
if (tree.incomplete) throw new Error("Refusing to execute an incomplete PDF action tree");
|
|
@@ -22,7 +23,7 @@ function javaScriptProgramFromActionTree(tree) {
|
|
|
22
23
|
/**
|
|
23
24
|
* Host-side Acrobat color-array conversions — the commit sinks and world
|
|
24
25
|
* builders cross the Acrobat-array ↔ renderer-color boundary through these.
|
|
25
|
-
* The
|
|
26
|
+
* The prelude inlines its own identical formulas (it must stay
|
|
26
27
|
* self-contained); `prelude.test.ts` pins the two implementations together.
|
|
27
28
|
*
|
|
28
29
|
* Conversion vectors (the documented Acrobat semantics):
|
|
@@ -46,11 +47,11 @@ function scriptColorToRgb(color) {
|
|
|
46
47
|
b: color[3]
|
|
47
48
|
};
|
|
48
49
|
case "CMYK": {
|
|
49
|
-
const [,
|
|
50
|
+
const [, cyan, magenta, yellow, black] = color;
|
|
50
51
|
return {
|
|
51
|
-
r: 1 - Math.min(1,
|
|
52
|
-
g: 1 - Math.min(1,
|
|
53
|
-
b: 1 - Math.min(1,
|
|
52
|
+
r: 1 - Math.min(1, cyan + black),
|
|
53
|
+
g: 1 - Math.min(1, magenta + black),
|
|
54
|
+
b: 1 - Math.min(1, yellow + black)
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
}
|
|
@@ -91,10 +92,10 @@ function isScriptColorArray(value) {
|
|
|
91
92
|
//#endregion
|
|
92
93
|
//#region src/types.ts
|
|
93
94
|
/**
|
|
94
|
-
* The writable-property validity matrix, keyed by engine subtype — the
|
|
95
|
+
* The writable-property validity matrix, keyed by engine subtype — the one
|
|
95
96
|
* exported table (the prelude inlines an identical copy; a parity test pins
|
|
96
97
|
* them together, and plugin-annotation drift-guards this against the kind
|
|
97
|
-
* registry's
|
|
98
|
+
* registry's FieldSpecs). Flags + contents are writable everywhere
|
|
98
99
|
* script-addressable; appearance keys vary by kind.
|
|
99
100
|
*/
|
|
100
101
|
const ANNOT_WRITABLE_KEYS = Object.freeze({
|
|
@@ -218,16 +219,16 @@ function seedFrom(documentId, sequence) {
|
|
|
218
219
|
}
|
|
219
220
|
return hash >>> 0;
|
|
220
221
|
}
|
|
221
|
-
/**
|
|
222
|
-
* standard `ScriptHostOptions.identity` builder. */
|
|
222
|
+
/** The session's identity, as Acrobat JavaScript's `identity` object, under
|
|
223
|
+
* embedder overrides — the standard `ScriptHostOptions.identity` builder. */
|
|
223
224
|
function resolveScriptIdentity(doc, overrides) {
|
|
224
|
-
const
|
|
225
|
+
const identity = doc.security.identity;
|
|
225
226
|
const supplied = typeof overrides === "function" ? overrides() : overrides ?? {};
|
|
226
227
|
return {
|
|
227
|
-
name: supplied.name ??
|
|
228
|
-
loginName: supplied.loginName ??
|
|
229
|
-
corporation: supplied.corporation ??
|
|
230
|
-
email: supplied.email ?? ""
|
|
228
|
+
name: supplied.name ?? identity?.displayName ?? identity?.userId ?? "",
|
|
229
|
+
loginName: supplied.loginName ?? identity?.userId ?? "",
|
|
230
|
+
corporation: supplied.corporation ?? identity?.organization ?? "",
|
|
231
|
+
email: supplied.email ?? identity?.email ?? ""
|
|
231
232
|
};
|
|
232
233
|
}
|
|
233
234
|
function createScriptHost(options) {
|
|
@@ -261,7 +262,7 @@ function createScriptHost(options) {
|
|
|
261
262
|
...world.annotsCoverDocument !== void 0 ? { annotsCoverDocument: world.annotsCoverDocument } : {},
|
|
262
263
|
event: world.event
|
|
263
264
|
});
|
|
264
|
-
/** A resource fault poisons the realm: rebuild + re-boot lazily
|
|
265
|
+
/** A resource fault poisons the realm: rebuild + re-boot lazily next time. */
|
|
265
266
|
const noteFault = (sandbox) => {
|
|
266
267
|
if (!sandbox.disposed) return;
|
|
267
268
|
sandboxPromise = null;
|
|
@@ -345,25 +346,42 @@ function valueFromEntry(entry) {
|
|
|
345
346
|
case "unsupported": return null;
|
|
346
347
|
}
|
|
347
348
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
349
|
+
/**
|
|
350
|
+
* A field value as scripts see it. A checkbox or radio group shows the export
|
|
351
|
+
* value of its checked widget, as Acrobat's `field.value` does, or `'Off'`.
|
|
352
|
+
*/
|
|
353
|
+
function fieldValueFromEntry(field, entry) {
|
|
354
|
+
if (field.family !== "checkbox" && field.family !== "radio") return valueFromEntry(entry);
|
|
355
|
+
if (entry.kind !== "scalar") return "Off";
|
|
356
|
+
return field.widgets.find((widget) => widget.onState === entry.value)?.exportValue ?? entry.value;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* How a widget shows, as Acrobat's `field.display` reads its flags: hidden;
|
|
360
|
+
* on screen but not printed; printed but not on screen; or visible.
|
|
361
|
+
*/
|
|
362
|
+
function displayOf(row) {
|
|
363
|
+
if (!row) return "visible";
|
|
364
|
+
if (row.hidden) return "hidden";
|
|
365
|
+
if (row.noView) return "noView";
|
|
366
|
+
return row.print ? "visible" : "noPrint";
|
|
351
367
|
}
|
|
352
368
|
/** Build the VM's detached field view from the engine's lossless snapshot. */
|
|
353
369
|
function scriptFieldsFromSnapshot(snapshot) {
|
|
370
|
+
const rows = new Map(snapshot.widgets.map((row) => [row.ref.kind === "objectNumber" ? row.ref.objectNumber : 0, row]));
|
|
354
371
|
return snapshot.fields.map((field) => ({
|
|
355
372
|
ref: field.ref,
|
|
356
373
|
name: field.name,
|
|
357
374
|
family: field.family,
|
|
358
|
-
value: fieldValueFromEntry(field
|
|
359
|
-
defaultValue: fieldValueFromEntry(field
|
|
360
|
-
display:
|
|
361
|
-
readOnly: field.
|
|
362
|
-
required: field.
|
|
375
|
+
value: fieldValueFromEntry(field, field.valueEntry),
|
|
376
|
+
defaultValue: fieldValueFromEntry(field, field.defaultValueEntry),
|
|
377
|
+
display: displayOf(rows.get(field.widgets?.[0]?.objectNumber ?? -1)),
|
|
378
|
+
readOnly: field.readOnly,
|
|
379
|
+
required: field.required,
|
|
363
380
|
..."options" in field ? { options: field.options.map((option) => ({
|
|
364
381
|
label: option.label,
|
|
365
382
|
value: option.value
|
|
366
|
-
})) } : {}
|
|
383
|
+
})) } : {},
|
|
384
|
+
...field.family === "checkbox" || field.family === "radio" ? { exportValues: field.widgets.map((widget) => widget.exportValue) } : {}
|
|
367
385
|
}));
|
|
368
386
|
}
|
|
369
387
|
//#endregion
|
|
@@ -375,18 +393,18 @@ function scriptFieldsFromSnapshot(snapshot) {
|
|
|
375
393
|
* values. The sandbox ships `installAcroJs.toString()` into QuickJS, while
|
|
376
394
|
* tests execute the same source in a Node VM. Types disappear at build time.
|
|
377
395
|
*/
|
|
378
|
-
function installAcroJs(
|
|
379
|
-
const host =
|
|
396
|
+
function installAcroJs(globalObject) {
|
|
397
|
+
const host = globalObject;
|
|
380
398
|
const NativeDate = Date;
|
|
381
399
|
let state = null;
|
|
382
400
|
const cloneValue = (value) => Array.isArray(value) ? value.map((item) => String(item)) : value;
|
|
383
|
-
const sameValue = (
|
|
384
|
-
if (Array.isArray(
|
|
385
|
-
return
|
|
401
|
+
const sameValue = (left, right) => {
|
|
402
|
+
if (Array.isArray(left) && Array.isArray(right)) return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
403
|
+
return left === right;
|
|
386
404
|
};
|
|
387
405
|
const refKey = (ref) => {
|
|
388
406
|
const value = ref;
|
|
389
|
-
return value?.kind === "objectNumber" ? `obj:${String(value.
|
|
407
|
+
return value?.kind === "objectNumber" ? `obj:${String(value.objectNumber)}` : `fqn:${String(value?.name ?? "")}`;
|
|
390
408
|
};
|
|
391
409
|
const diagnostic = (code, message) => {
|
|
392
410
|
state?.diagnostics.push({
|
|
@@ -583,19 +601,19 @@ function installAcroJs(g) {
|
|
|
583
601
|
Number(color[2]),
|
|
584
602
|
Number(color[3])
|
|
585
603
|
];
|
|
586
|
-
const
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
const
|
|
604
|
+
const cyan = Number(color[1]);
|
|
605
|
+
const magenta = Number(color[2]);
|
|
606
|
+
const yellow = Number(color[3]);
|
|
607
|
+
const black = Number(color[4]);
|
|
590
608
|
return [
|
|
591
|
-
1 - Math.min(1,
|
|
592
|
-
1 - Math.min(1,
|
|
593
|
-
1 - Math.min(1,
|
|
609
|
+
1 - Math.min(1, cyan + black),
|
|
610
|
+
1 - Math.min(1, magenta + black),
|
|
611
|
+
1 - Math.min(1, yellow + black)
|
|
594
612
|
];
|
|
595
613
|
};
|
|
596
|
-
const sameAnnotValue = (
|
|
597
|
-
if (Array.isArray(
|
|
598
|
-
return
|
|
614
|
+
const sameAnnotValue = (left, right) => {
|
|
615
|
+
if (Array.isArray(left) && Array.isArray(right)) return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
616
|
+
return left === right;
|
|
599
617
|
};
|
|
600
618
|
const cloneAnnotValue = (value) => Array.isArray(value) ? [...value] : value;
|
|
601
619
|
const makeAnnot = (record) => {
|
|
@@ -623,8 +641,8 @@ function installAcroJs(g) {
|
|
|
623
641
|
get: () => cloneAnnotValue(record.current[key]),
|
|
624
642
|
set: (value) => stage(key, value, validate)
|
|
625
643
|
});
|
|
626
|
-
const isNum = (
|
|
627
|
-
const isBool = (
|
|
644
|
+
const isNum = (value) => typeof value === "number" && isFinite(value);
|
|
645
|
+
const isBool = (value) => typeof value === "boolean";
|
|
628
646
|
Object.defineProperties(wrapper, {
|
|
629
647
|
name: {
|
|
630
648
|
enumerable: true,
|
|
@@ -648,12 +666,12 @@ function installAcroJs(g) {
|
|
|
648
666
|
},
|
|
649
667
|
strokeColor: prop("strokeColor", isColorArray),
|
|
650
668
|
fillColor: prop("fillColor", isColorArray),
|
|
651
|
-
opacity: prop("opacity", (
|
|
652
|
-
width: prop("width", (
|
|
653
|
-
style: prop("borderStyle", (
|
|
654
|
-
dash: prop("dash", (
|
|
655
|
-
rect: prop("rect", (
|
|
656
|
-
contents: prop("contents", (
|
|
669
|
+
opacity: prop("opacity", (value) => isNum(value) && Number(value) >= 0 && Number(value) <= 1),
|
|
670
|
+
width: prop("width", (value) => isNum(value) && Number(value) >= 0),
|
|
671
|
+
style: prop("borderStyle", (value) => value === "S" || value === "D"),
|
|
672
|
+
dash: prop("dash", (value) => Array.isArray(value) && value.every(isNum)),
|
|
673
|
+
rect: prop("rect", (value) => Array.isArray(value) && value.length === 4 && value.every(isNum)),
|
|
674
|
+
contents: prop("contents", (value) => typeof value === "string"),
|
|
657
675
|
hidden: prop("hidden", isBool),
|
|
658
676
|
print: prop("print", isBool),
|
|
659
677
|
readOnly: prop("readOnly", isBool),
|
|
@@ -871,30 +889,30 @@ function installAcroJs(g) {
|
|
|
871
889
|
});
|
|
872
890
|
const fieldsByName = new Map(fields.map((field) => [String(field.input.name), field]));
|
|
873
891
|
const fieldsByRef = new Map(fields.map((field) => [refKey(field.input.ref), field]));
|
|
874
|
-
const annots = (input.annots ?? []).map((
|
|
892
|
+
const annots = (input.annots ?? []).map((annotation) => {
|
|
875
893
|
const writableSnapshot = () => ({
|
|
876
|
-
strokeColor: cloneAnnotValue(
|
|
877
|
-
fillColor: cloneAnnotValue(
|
|
878
|
-
opacity: Number(
|
|
879
|
-
width: Number(
|
|
880
|
-
borderStyle: String(
|
|
881
|
-
dash: cloneAnnotValue(
|
|
882
|
-
rect: cloneAnnotValue(
|
|
894
|
+
strokeColor: cloneAnnotValue(annotation.strokeColor ?? ["T"]),
|
|
895
|
+
fillColor: cloneAnnotValue(annotation.fillColor ?? ["T"]),
|
|
896
|
+
opacity: Number(annotation.opacity ?? 1),
|
|
897
|
+
width: Number(annotation.width ?? 1),
|
|
898
|
+
borderStyle: String(annotation.borderStyle ?? "S"),
|
|
899
|
+
dash: cloneAnnotValue(annotation.dash ?? []),
|
|
900
|
+
rect: cloneAnnotValue(annotation.rect ?? [
|
|
883
901
|
0,
|
|
884
902
|
0,
|
|
885
903
|
0,
|
|
886
904
|
0
|
|
887
905
|
]),
|
|
888
|
-
contents: String(
|
|
889
|
-
hidden: Boolean(
|
|
890
|
-
print: Boolean(
|
|
891
|
-
readOnly: Boolean(
|
|
892
|
-
locked: Boolean(
|
|
893
|
-
noView: Boolean(
|
|
894
|
-
toggleNoView: Boolean(
|
|
906
|
+
contents: String(annotation.contents ?? ""),
|
|
907
|
+
hidden: Boolean(annotation.hidden),
|
|
908
|
+
print: Boolean(annotation.print),
|
|
909
|
+
readOnly: Boolean(annotation.readOnly),
|
|
910
|
+
locked: Boolean(annotation.locked),
|
|
911
|
+
noView: Boolean(annotation.noView),
|
|
912
|
+
toggleNoView: Boolean(annotation.toggleNoView)
|
|
895
913
|
});
|
|
896
914
|
const record = {
|
|
897
|
-
input:
|
|
915
|
+
input: annotation,
|
|
898
916
|
original: writableSnapshot(),
|
|
899
917
|
current: writableSnapshot(),
|
|
900
918
|
wrapper: {}
|
|
@@ -951,22 +969,17 @@ function installAcroJs(g) {
|
|
|
951
969
|
};
|
|
952
970
|
const formValue = (field, value) => {
|
|
953
971
|
const family = String(field.input.family);
|
|
954
|
-
if (family === "text") return {
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
};
|
|
966
|
-
if (family === "listbox") return {
|
|
967
|
-
type: "choice",
|
|
968
|
-
values: Array.isArray(value) ? value.map(String) : [String(value ?? "")]
|
|
969
|
-
};
|
|
972
|
+
if (family === "text") return { value: String(value ?? "") };
|
|
973
|
+
if (family === "checkbox" || family === "radio") return { value: value === null || value === void 0 || String(value) === "Off" ? null : String(value) };
|
|
974
|
+
if (family === "combobox") {
|
|
975
|
+
const selected = Array.isArray(value) ? value[0] : value;
|
|
976
|
+
const text = selected === null || selected === void 0 ? "" : String(selected);
|
|
977
|
+
return { value: text === "" ? null : text };
|
|
978
|
+
}
|
|
979
|
+
if (family === "listbox") {
|
|
980
|
+
if (Array.isArray(value)) return { selectedValues: value.map(String) };
|
|
981
|
+
return { selectedValues: value === null || value === void 0 || value === "" ? [] : [String(value)] };
|
|
982
|
+
}
|
|
970
983
|
diagnostic("invalid-field-value", `field '${String(field.input.name)}' cannot accept a scripted value`);
|
|
971
984
|
return null;
|
|
972
985
|
};
|
|
@@ -1157,23 +1170,23 @@ function installAcroJs(g) {
|
|
|
1157
1170
|
rgb[2]
|
|
1158
1171
|
];
|
|
1159
1172
|
if (target === "CMYK") {
|
|
1160
|
-
const
|
|
1173
|
+
const black = 1 - Math.max(rgb[0], rgb[1], rgb[2]);
|
|
1161
1174
|
return [
|
|
1162
1175
|
"CMYK",
|
|
1163
|
-
1 - rgb[0] -
|
|
1164
|
-
1 - rgb[1] -
|
|
1165
|
-
1 - rgb[2] -
|
|
1166
|
-
|
|
1176
|
+
1 - rgb[0] - black,
|
|
1177
|
+
1 - rgb[1] - black,
|
|
1178
|
+
1 - rgb[2] - black,
|
|
1179
|
+
black
|
|
1167
1180
|
];
|
|
1168
1181
|
}
|
|
1169
1182
|
return ["T"];
|
|
1170
1183
|
},
|
|
1171
|
-
equal: (
|
|
1172
|
-
if (!isColorArray(
|
|
1173
|
-
const
|
|
1174
|
-
const
|
|
1175
|
-
if (
|
|
1176
|
-
return
|
|
1184
|
+
equal: (left, right) => {
|
|
1185
|
+
if (!isColorArray(left) || !isColorArray(right)) return false;
|
|
1186
|
+
const leftRgb = colorToRgbTriple(left);
|
|
1187
|
+
const rightRgb = colorToRgbTriple(right);
|
|
1188
|
+
if (leftRgb === null || rightRgb === null) return leftRgb === rightRgb;
|
|
1189
|
+
return leftRgb.every((component, index) => Math.abs(component - rightRgb[index]) < 1e-6);
|
|
1177
1190
|
}
|
|
1178
1191
|
});
|
|
1179
1192
|
host.getField = getField;
|
|
@@ -1221,7 +1234,7 @@ function installAcroJs(g) {
|
|
|
1221
1234
|
host.AFSimple_Calculate = (operation, names) => {
|
|
1222
1235
|
const values = (Array.isArray(names) ? names : String(names).split(",")).map((name) => numberValue(getField(String(name).trim())?.value));
|
|
1223
1236
|
const op = String(operation).toUpperCase();
|
|
1224
|
-
const value = values.length === 0 ? 0 : op === "PRD" ? values.reduce((
|
|
1237
|
+
const value = values.length === 0 ? 0 : op === "PRD" ? values.reduce((left, right) => left * right, 1) : op === "AVG" ? values.reduce((left, right) => left + right, 0) / values.length : op === "MIN" ? Math.min(...values) : op === "MAX" ? Math.max(...values) : values.reduce((left, right) => left + right, 0);
|
|
1225
1238
|
if (state) state.event.value = value;
|
|
1226
1239
|
};
|
|
1227
1240
|
host.AFNumber_Format = (decimals, separatorStyle, _negativeStyle, _currencyStyle, currency, prepend) => {
|
|
@@ -1274,12 +1287,12 @@ function installAcroJs(g) {
|
|
|
1274
1287
|
const formatMismatch = () => rejectWithAlert(`The value entered does not match the format of the field [ ${String(state?.event.targetName ?? "")} ]`);
|
|
1275
1288
|
const dateMismatch = (format) => rejectWithAlert(`Invalid date/time: please ensure that the date/time exists. Field [ ${String(state?.event.targetName ?? "")} ] should match format ${String(format ?? "")}`);
|
|
1276
1289
|
const mergeChange = (eventObject) => {
|
|
1277
|
-
const
|
|
1278
|
-
const value = String(
|
|
1279
|
-
if (
|
|
1280
|
-
const start = Math.max(0, Math.trunc(Number(
|
|
1281
|
-
const end = Math.max(start, Math.trunc(Number(
|
|
1282
|
-
return value.slice(0, start) + String(
|
|
1290
|
+
const event = eventObject ?? state?.event ?? {};
|
|
1291
|
+
const value = String(event.value ?? "");
|
|
1292
|
+
if (event.willCommit) return value;
|
|
1293
|
+
const start = Math.max(0, Math.trunc(Number(event.selStart ?? 0)));
|
|
1294
|
+
const end = Math.max(start, Math.trunc(Number(event.selEnd ?? 0)));
|
|
1295
|
+
return value.slice(0, start) + String(event.change ?? "") + value.slice(end);
|
|
1283
1296
|
};
|
|
1284
1297
|
host.AFMergeChange = mergeChange;
|
|
1285
1298
|
const makeNumber = (value) => {
|
|
@@ -1374,17 +1387,17 @@ function installAcroJs(g) {
|
|
|
1374
1387
|
break;
|
|
1375
1388
|
}
|
|
1376
1389
|
const timeMatch = /(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(am|pm)?/i.exec(text);
|
|
1377
|
-
let
|
|
1378
|
-
let
|
|
1379
|
-
let
|
|
1390
|
+
let hours = 0;
|
|
1391
|
+
let minutes = 0;
|
|
1392
|
+
let seconds = 0;
|
|
1380
1393
|
if (timeMatch) {
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1394
|
+
hours = Number(timeMatch[1]);
|
|
1395
|
+
minutes = Number(timeMatch[2]);
|
|
1396
|
+
seconds = Number(timeMatch[3] ?? 0);
|
|
1384
1397
|
const marker = String(timeMatch[4] ?? "").toLowerCase();
|
|
1385
|
-
if (marker === "pm" &&
|
|
1386
|
-
if (marker === "am" &&
|
|
1387
|
-
if (
|
|
1398
|
+
if (marker === "pm" && hours < 12) hours += 12;
|
|
1399
|
+
if (marker === "am" && hours === 12) hours = 0;
|
|
1400
|
+
if (hours > 23 || minutes > 59 || seconds > 59) return null;
|
|
1388
1401
|
}
|
|
1389
1402
|
const numbers = ((timeMatch ? text.slice(0, timeMatch.index) + text.slice(timeMatch.index + timeMatch[0].length) : text).match(/\d+/g) ?? []).map((token) => Number(token));
|
|
1390
1403
|
const order = [];
|
|
@@ -1392,35 +1405,35 @@ function installAcroJs(g) {
|
|
|
1392
1405
|
const kind = token[0] === "y" ? "y" : token[0] === "m" ? "m" : "d";
|
|
1393
1406
|
if (order.indexOf(kind) < 0) order.push(kind);
|
|
1394
1407
|
}
|
|
1395
|
-
let
|
|
1396
|
-
let
|
|
1397
|
-
let
|
|
1408
|
+
let year = null;
|
|
1409
|
+
let month = namedMonth;
|
|
1410
|
+
let day = null;
|
|
1398
1411
|
let cursor = 0;
|
|
1399
1412
|
for (const kind of order) {
|
|
1400
1413
|
if (kind === "m" && namedMonth !== null) continue;
|
|
1401
1414
|
if (cursor >= numbers.length) break;
|
|
1402
1415
|
const value = numbers[cursor];
|
|
1403
1416
|
cursor += 1;
|
|
1404
|
-
if (kind === "y")
|
|
1405
|
-
else if (kind === "m")
|
|
1406
|
-
else
|
|
1417
|
+
if (kind === "y") year = value;
|
|
1418
|
+
else if (kind === "m") month = value;
|
|
1419
|
+
else day = value;
|
|
1407
1420
|
}
|
|
1408
|
-
if (
|
|
1421
|
+
if (year === null && month === null && day === null && !timeMatch) return null;
|
|
1409
1422
|
const today = nowWall();
|
|
1410
|
-
if (
|
|
1411
|
-
else if (
|
|
1412
|
-
if (
|
|
1413
|
-
if (
|
|
1414
|
-
if (
|
|
1415
|
-
const probe = new NativeDate(NativeDate.UTC(
|
|
1416
|
-
if (probe.getUTCFullYear() !==
|
|
1423
|
+
if (year === null) year = today.y;
|
|
1424
|
+
else if (year < 100) year = year < 50 ? 2e3 + year : 1900 + year;
|
|
1425
|
+
if (month === null) month = today.m;
|
|
1426
|
+
if (day === null) day = 1;
|
|
1427
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) return null;
|
|
1428
|
+
const probe = new NativeDate(NativeDate.UTC(year, month - 1, day));
|
|
1429
|
+
if (probe.getUTCFullYear() !== year || probe.getUTCMonth() !== month - 1 || probe.getUTCDate() !== day) return null;
|
|
1417
1430
|
return {
|
|
1418
|
-
y,
|
|
1419
|
-
m,
|
|
1420
|
-
d,
|
|
1421
|
-
H,
|
|
1422
|
-
M,
|
|
1423
|
-
S
|
|
1431
|
+
y: year,
|
|
1432
|
+
m: month,
|
|
1433
|
+
d: day,
|
|
1434
|
+
H: hours,
|
|
1435
|
+
M: minutes,
|
|
1436
|
+
S: seconds
|
|
1424
1437
|
};
|
|
1425
1438
|
};
|
|
1426
1439
|
const afDateFormatEx = (format) => {
|