@olenbetong/appframe-core 2.15.2 → 2.16.1
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/esm/af.common.js +167 -94
- package/dist/esm/af.common.legacy.js +158 -81
- package/dist/esm/af.common.legacy.min.js +3 -3
- package/dist/esm/af.common.legacy.min.js.map +1 -1
- package/dist/esm/af.common.min.js +3 -3
- package/dist/esm/af.common.min.js.map +1 -1
- package/dist/iife/af.common.js +171 -93
- package/dist/iife/af.common.legacy.js +162 -80
- package/dist/iife/af.common.legacy.min.js +3 -3
- package/dist/iife/af.common.legacy.min.js.map +1 -1
- package/dist/iife/af.common.min.js +3 -3
- package/dist/iife/af.common.min.js.map +1 -1
- package/es/filter/columnValues.test.d.ts +1 -0
- package/es/filter/columnValues.test.js +119 -0
- package/es/filter/describeFilter.js +11 -9
- package/es/filter/index.d.ts +3 -3
- package/es/filter/index.js +2 -2
- package/es/filter/operators.d.ts +14 -0
- package/es/filter/operators.js +63 -3
- package/es/filter/tree.d.ts +10 -1
- package/es/filter/tree.js +43 -5
- package/es/filter/types.d.ts +27 -6
- package/es/index.js +1 -1
- package/es/report/recordSource.d.ts +1 -1
- package/es/report/recordSource.js +16 -2
- package/es/report/report.test.js +36 -0
- package/package.json +6 -4
- package/src/filter/columnValues.test.ts +164 -0
- package/src/filter/describeFilter.ts +16 -10
- package/src/filter/index.ts +6 -0
- package/src/filter/operators.ts +74 -3
- package/src/filter/tree.ts +64 -7
- package/src/filter/types.ts +43 -4
- package/src/report/recordSource.ts +17 -2
- package/src/report/report.test.ts +39 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// ../../node_modules/.pnpm/mitt@3.0.1/node_modules/mitt/dist/mitt.mjs
|
|
2
2
|
function mitt_default(n) {
|
|
3
3
|
return {
|
|
4
4
|
all: n = n || /* @__PURE__ */ new Map(),
|
|
@@ -21,8 +21,7 @@ function mitt_default(n) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
//#region src/SessionHandler.ts
|
|
24
|
+
// src/SessionHandler.ts
|
|
26
25
|
var SessionHandler = class {
|
|
27
26
|
eventHandler = mitt_default();
|
|
28
27
|
authenticated() {
|
|
@@ -67,8 +66,7 @@ var SessionHandler = class {
|
|
|
67
66
|
}
|
|
68
67
|
};
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
//#region src/UserSession.ts
|
|
69
|
+
// src/UserSession.ts
|
|
72
70
|
var UserSession = class extends SessionHandler {
|
|
73
71
|
amDesignator = "a.m.";
|
|
74
72
|
dayNames = [
|
|
@@ -140,8 +138,7 @@ var UserSession = class extends SessionHandler {
|
|
|
140
138
|
}
|
|
141
139
|
};
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
//#region src/log.ts
|
|
141
|
+
// src/log.ts
|
|
145
142
|
const article = window.af?.article?.id ?? window.window?.location.pathname.substring(1).split("/")[0];
|
|
146
143
|
/**
|
|
147
144
|
* Logs an activity to the website activity log
|
|
@@ -286,15 +283,13 @@ function getSessionData() {
|
|
|
286
283
|
};
|
|
287
284
|
}
|
|
288
285
|
|
|
289
|
-
|
|
290
|
-
//#region src/errorHandler.ts
|
|
286
|
+
// src/errorHandler.ts
|
|
291
287
|
window.window?.addEventListener("error", (event) => {
|
|
292
288
|
if (typeof event === "object" && "error" in event) logError(event.error);
|
|
293
289
|
else if (typeof event === "object" && "message" in event) logError(event);
|
|
294
290
|
});
|
|
295
291
|
|
|
296
|
-
|
|
297
|
-
//#region src/localStorage.ts
|
|
292
|
+
// src/localStorage.ts
|
|
298
293
|
function getLocalKey(key) {
|
|
299
294
|
return [
|
|
300
295
|
af?.article?.id || "",
|
|
@@ -329,8 +324,7 @@ var LocalStorage = class {
|
|
|
329
324
|
};
|
|
330
325
|
var localStorage_default = new LocalStorage();
|
|
331
326
|
|
|
332
|
-
|
|
333
|
-
//#region src/date.ts
|
|
327
|
+
// src/date.ts
|
|
334
328
|
function toLocalISODate(date) {
|
|
335
329
|
return date ? toLocalISODateTime(date).substring(0, 10) : null;
|
|
336
330
|
}
|
|
@@ -353,8 +347,7 @@ function toLocalDateTime(date) {
|
|
|
353
347
|
return date ? date.toISOString().substring(0, 16) : "";
|
|
354
348
|
}
|
|
355
349
|
|
|
356
|
-
|
|
357
|
-
//#region src/downloadBlob.ts
|
|
350
|
+
// src/downloadBlob.ts
|
|
358
351
|
function downloadBlob(blob, fileName) {
|
|
359
352
|
return new Promise((resolve, reject) => {
|
|
360
353
|
try {
|
|
@@ -376,8 +369,7 @@ function downloadBlob(blob, fileName) {
|
|
|
376
369
|
});
|
|
377
370
|
}
|
|
378
371
|
|
|
379
|
-
|
|
380
|
-
//#region src/invariant.ts
|
|
372
|
+
// src/invariant.ts
|
|
381
373
|
/**
|
|
382
374
|
* Throws an error if the condition is falsy. The provided message is used as the error message.
|
|
383
375
|
* A function can be provided to lazily generate the message.
|
|
@@ -392,8 +384,7 @@ function invariant(condition, message) {
|
|
|
392
384
|
throw new Error(provided ?? "Invariant failed");
|
|
393
385
|
}
|
|
394
386
|
|
|
395
|
-
|
|
396
|
-
//#region src/downloadRequest.ts
|
|
387
|
+
// src/downloadRequest.ts
|
|
397
388
|
async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
398
389
|
let bytesReceived = 0;
|
|
399
390
|
onProgress?.({ status: "requested" });
|
|
@@ -447,8 +438,7 @@ async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
|
447
438
|
}
|
|
448
439
|
}
|
|
449
440
|
|
|
450
|
-
|
|
451
|
-
//#region src/parseFunc.ts
|
|
441
|
+
// src/parseFunc.ts
|
|
452
442
|
const funcRegex = /^function(?:\s+([a-zA-Z$_][a-zA-Z0-9$_]*))?\s*\(((?:(?:\s*[a-zA-Z$_][a-zA-Z0-9$_]*)(?:\s*,\s*[a-zA-Z$_][a-zA-Z0-9$_]*)*)?)\s*\)\s*\{((?:.|\n|\r)*)\}$/;
|
|
453
443
|
const arrowBlockRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*\{([\s\S]*)\}$/;
|
|
454
444
|
const arrowExpressionRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*([\s\S]+)$/;
|
|
@@ -482,13 +472,11 @@ function parseFunc(func) {
|
|
|
482
472
|
};
|
|
483
473
|
}
|
|
484
474
|
|
|
485
|
-
|
|
486
|
-
//#region src/Namespace.ts
|
|
475
|
+
// src/Namespace.ts
|
|
487
476
|
function Namespace() {}
|
|
488
477
|
Namespace.prototype = Object.create(null);
|
|
489
478
|
|
|
490
|
-
|
|
491
|
-
//#region src/requireNamespace.ts
|
|
479
|
+
// src/requireNamespace.ts
|
|
492
480
|
function requireNamespace(root, namespace) {
|
|
493
481
|
if (typeof root === "object") {
|
|
494
482
|
if (typeof namespace === "string") return requireNamespace(root, namespace.split("."));
|
|
@@ -500,8 +488,7 @@ function requireNamespace(root, namespace) {
|
|
|
500
488
|
}
|
|
501
489
|
}
|
|
502
490
|
|
|
503
|
-
|
|
504
|
-
//#region src/expose.ts
|
|
491
|
+
// src/expose.ts
|
|
505
492
|
/**
|
|
506
493
|
* Expose a value to a potentially deeply nested global variable
|
|
507
494
|
* @param name {string | object} Either a dot-separated path to the global variable, or an object to expose the value on
|
|
@@ -531,8 +518,7 @@ function expose(name, value, options = {}) {
|
|
|
531
518
|
}
|
|
532
519
|
}
|
|
533
520
|
|
|
534
|
-
|
|
535
|
-
//#region src/getLocalizedString.ts
|
|
521
|
+
// src/getLocalizedString.ts
|
|
536
522
|
async function fetchLocalizedString(text, project) {
|
|
537
523
|
try {
|
|
538
524
|
let response = await fetch(`/api/user/localize/new/${encodeURIComponent(project)}`, {
|
|
@@ -621,8 +607,7 @@ function createLocalizer(project) {
|
|
|
621
607
|
};
|
|
622
608
|
}
|
|
623
609
|
|
|
624
|
-
|
|
625
|
-
//#region src/filter/operators.ts
|
|
610
|
+
// src/filter/operators.ts
|
|
626
611
|
/**
|
|
627
612
|
* Operators that take no value at all.
|
|
628
613
|
*
|
|
@@ -636,6 +621,26 @@ const PARAMETERLESS_OPERATORS = [
|
|
|
636
621
|
"isblank",
|
|
637
622
|
"isnotblank"
|
|
638
623
|
];
|
|
624
|
+
/**
|
|
625
|
+
* Whole-period checks for dates, such as "Is last week", in the order of the
|
|
626
|
+
* Windows filter editor. They take no value. The framework has no operator for
|
|
627
|
+
* them, so a filter using one is sent as a filter string (`needsFilterString`).
|
|
628
|
+
*/
|
|
629
|
+
const PERIOD_OPERATORS = [
|
|
630
|
+
"isbeyondthisyear",
|
|
631
|
+
"islaterthisyear",
|
|
632
|
+
"islaterthismonth",
|
|
633
|
+
"isnextweek",
|
|
634
|
+
"islaterthisweek",
|
|
635
|
+
"istomorrow",
|
|
636
|
+
"istoday",
|
|
637
|
+
"isyesterday",
|
|
638
|
+
"isearlierthisweek",
|
|
639
|
+
"islastweek",
|
|
640
|
+
"isearlierthismonth",
|
|
641
|
+
"isearlierthisyear",
|
|
642
|
+
"ispriorthisyear"
|
|
643
|
+
];
|
|
639
644
|
/** Operators whose value is a two element `[from, to]` array. */
|
|
640
645
|
const PAIR_OPERATORS = [
|
|
641
646
|
"between",
|
|
@@ -646,7 +651,7 @@ const PAIR_OPERATORS = [
|
|
|
646
651
|
/** Operators whose value is an array of values. */
|
|
647
652
|
const LIST_OPERATORS = ["inlist", "notinlist"];
|
|
648
653
|
function getOperatorArity(operator) {
|
|
649
|
-
if (PARAMETERLESS_OPERATORS.includes(operator)) return "none";
|
|
654
|
+
if (PARAMETERLESS_OPERATORS.includes(operator) || PERIOD_OPERATORS.includes(operator)) return "none";
|
|
650
655
|
if (PAIR_OPERATORS.includes(operator)) return "pair";
|
|
651
656
|
if (LIST_OPERATORS.includes(operator)) return "list";
|
|
652
657
|
return "single";
|
|
@@ -689,7 +694,20 @@ const OPERATOR_LABELS = {
|
|
|
689
694
|
datelessthan: "Before",
|
|
690
695
|
datelessthanorequal: "Up to",
|
|
691
696
|
datebetween: "Between",
|
|
692
|
-
datenotbetween: "Not between"
|
|
697
|
+
datenotbetween: "Not between",
|
|
698
|
+
isbeyondthisyear: "Is beyond this year",
|
|
699
|
+
islaterthisyear: "Is later this year",
|
|
700
|
+
islaterthismonth: "Is later this month",
|
|
701
|
+
isnextweek: "Is next week",
|
|
702
|
+
islaterthisweek: "Is later this week",
|
|
703
|
+
istomorrow: "Is tomorrow",
|
|
704
|
+
istoday: "Is today",
|
|
705
|
+
isyesterday: "Is yesterday",
|
|
706
|
+
isearlierthisweek: "Is earlier this week",
|
|
707
|
+
islastweek: "Is last week",
|
|
708
|
+
isearlierthismonth: "Is earlier this month",
|
|
709
|
+
isearlierthisyear: "Is earlier this year",
|
|
710
|
+
ispriorthisyear: "Is prior to this year"
|
|
693
711
|
};
|
|
694
712
|
/** Comparison operator labels used for date and time fields. */
|
|
695
713
|
const DATE_OPERATOR_LABELS = {
|
|
@@ -734,13 +752,15 @@ const OPERATORS_BY_TYPE = {
|
|
|
734
752
|
...EQUALITY,
|
|
735
753
|
...LIST_CHECKS,
|
|
736
754
|
...RANGE,
|
|
737
|
-
...COMPARISONS
|
|
755
|
+
...COMPARISONS,
|
|
756
|
+
...PERIOD_OPERATORS
|
|
738
757
|
],
|
|
739
758
|
datetime: [
|
|
740
759
|
...EQUALITY,
|
|
741
760
|
...LIST_CHECKS,
|
|
742
761
|
...RANGE,
|
|
743
|
-
...COMPARISONS
|
|
762
|
+
...COMPARISONS,
|
|
763
|
+
...PERIOD_OPERATORS
|
|
744
764
|
],
|
|
745
765
|
number: [
|
|
746
766
|
...EQUALITY,
|
|
@@ -813,7 +833,10 @@ function getValueType(field, operator) {
|
|
|
813
833
|
const RELATIVE_DATE_VALUES = [
|
|
814
834
|
"Yesterday",
|
|
815
835
|
"Today",
|
|
836
|
+
"Now",
|
|
816
837
|
"Tomorrow",
|
|
838
|
+
"DayAfterTomorrow",
|
|
839
|
+
"TwoWeeksAway",
|
|
817
840
|
"ThisWeek",
|
|
818
841
|
"ThisMonth",
|
|
819
842
|
"ThisYear",
|
|
@@ -828,7 +851,10 @@ const DYNAMIC_VALUE_LABELS = {
|
|
|
828
851
|
CurrentUser: "Current user",
|
|
829
852
|
Yesterday: "Yesterday",
|
|
830
853
|
Today: "Today",
|
|
854
|
+
Now: "Now",
|
|
831
855
|
Tomorrow: "Tomorrow",
|
|
856
|
+
DayAfterTomorrow: "Day after tomorrow",
|
|
857
|
+
TwoWeeksAway: "Two weeks away",
|
|
832
858
|
ThisWeek: "This week",
|
|
833
859
|
ThisMonth: "This month",
|
|
834
860
|
ThisYear: "This year",
|
|
@@ -864,6 +890,30 @@ function getDynamicValuesForField(field) {
|
|
|
864
890
|
if (field.type === "string") return ["CurrentUser"];
|
|
865
891
|
return [];
|
|
866
892
|
}
|
|
893
|
+
/** Operators that can compare a field with another column. */
|
|
894
|
+
const COLUMN_OPERATORS = [
|
|
895
|
+
...EQUALITY,
|
|
896
|
+
...RANGE,
|
|
897
|
+
...COMPARISONS
|
|
898
|
+
];
|
|
899
|
+
/** Whether an operator can take another column as its value. */
|
|
900
|
+
function supportsColumnValue(operator) {
|
|
901
|
+
return COLUMN_OPERATORS.includes(operator);
|
|
902
|
+
}
|
|
903
|
+
/** Dates and datetimes compare with each other; other types only with their own. */
|
|
904
|
+
function getComparableType(type) {
|
|
905
|
+
return type === "datetime" ? "date" : type;
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* The columns a field can be compared with: the other fields of the same type,
|
|
909
|
+
* as in the Windows filter editor. Booleans have only true and false checks,
|
|
910
|
+
* so they get none.
|
|
911
|
+
*/
|
|
912
|
+
function getColumnsForField(field, fields) {
|
|
913
|
+
if (field.type === "boolean") return [];
|
|
914
|
+
let type = getComparableType(field.type);
|
|
915
|
+
return fields.filter((other) => other.name !== field.name && !other.excludeFromFilter && getComparableType(other.type) === type);
|
|
916
|
+
}
|
|
867
917
|
/**
|
|
868
918
|
* Aliases for names `eOperators.ToString()` can produce that are not operators
|
|
869
919
|
* in their own right. `Not` is the bare negation flag (i.e. "not equal to"),
|
|
@@ -887,8 +937,7 @@ function normalizeOperator(operator) {
|
|
|
887
937
|
return OPERATOR_ALIASES[normalized] ?? normalized;
|
|
888
938
|
}
|
|
889
939
|
|
|
890
|
-
|
|
891
|
-
//#region src/filter/tree.ts
|
|
940
|
+
// src/filter/tree.ts
|
|
892
941
|
/**
|
|
893
942
|
* Creates the default empty filter: a single `and` group with no conditions.
|
|
894
943
|
*/
|
|
@@ -975,10 +1024,40 @@ function isCompleteExpression(item) {
|
|
|
975
1024
|
let arity = getOperatorArity(normalizeOperator(item.operator));
|
|
976
1025
|
if (arity === "none") return true;
|
|
977
1026
|
if (item.value === null || item.value === void 0 || item.value === "") return false;
|
|
978
|
-
if (arity === "pair") return Array.isArray(item.value) && item.value.length === 2 && item.value.every((v) =>
|
|
1027
|
+
if (arity === "pair") return Array.isArray(item.value) && item.value.length === 2 && item.value.every((v) => {
|
|
1028
|
+
let end = isRangeEnd(v) ? v.value : v;
|
|
1029
|
+
return end !== "" && end != null;
|
|
1030
|
+
});
|
|
979
1031
|
if (arity === "list") return Array.isArray(item.value) && item.value.length > 0;
|
|
980
1032
|
return true;
|
|
981
1033
|
}
|
|
1034
|
+
/** Whether a range value end is a `FilterRangeEnd` rather than a plain value. */
|
|
1035
|
+
function isRangeEnd(value) {
|
|
1036
|
+
return typeof value === "object" && value !== null && !(value instanceof Date) && "valueType" in value;
|
|
1037
|
+
}
|
|
1038
|
+
/** Dynamic values the framework has no `eDynamicValue` member for. */
|
|
1039
|
+
const DYNAMIC_VALUES_WITHOUT_FRAMEWORK_SUPPORT = /* @__PURE__ */ new Set([
|
|
1040
|
+
"Now",
|
|
1041
|
+
"DayAfterTomorrow",
|
|
1042
|
+
"TwoWeeksAway"
|
|
1043
|
+
]);
|
|
1044
|
+
function needsConversion(valueType, value) {
|
|
1045
|
+
if (valueType === "column") return true;
|
|
1046
|
+
return valueType === "special" && typeof value === "string" && DYNAMIC_VALUES_WITHOUT_FRAMEWORK_SUPPORT.has(value);
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* True when the filter holds something the framework's data API cannot read
|
|
1050
|
+
* from a filter object: another column, a range end that is not a plain value,
|
|
1051
|
+
* one of the dates only the Windows editor offers, or a whole-period check. Such a filter must be
|
|
1052
|
+
* converted with `convertFilter` and sent as a filter string.
|
|
1053
|
+
*/
|
|
1054
|
+
function needsFilterString(filter) {
|
|
1055
|
+
if (!filter) return false;
|
|
1056
|
+
if (isGroup(filter)) return filter.items.some(needsFilterString);
|
|
1057
|
+
if (PERIOD_OPERATORS.includes(normalizeOperator(filter.operator))) return true;
|
|
1058
|
+
if (needsConversion(filter.valueType, filter.value)) return true;
|
|
1059
|
+
return Array.isArray(filter.value) && filter.value.some((item) => isRangeEnd(item));
|
|
1060
|
+
}
|
|
982
1061
|
/**
|
|
983
1062
|
* Creates an expression for a field, using the field's default operator and an
|
|
984
1063
|
* empty value of the right shape.
|
|
@@ -1009,8 +1088,9 @@ function changeOperator(expression, operator, field) {
|
|
|
1009
1088
|
let previousArity = getOperatorArity(normalizeOperator(expression.operator));
|
|
1010
1089
|
let arity = getOperatorArity(operator);
|
|
1011
1090
|
let value = expression.value;
|
|
1012
|
-
|
|
1013
|
-
|
|
1091
|
+
let holdsColumn = expression.valueType === "column" || Array.isArray(value) && value.some((item) => isRangeEnd(item) && item.valueType === "column");
|
|
1092
|
+
if (arity !== previousArity || holdsColumn && !supportsColumnValue(operator)) value = createEmptyValue(operator);
|
|
1093
|
+
let valueType = (expression.valueType === "special" || expression.valueType === "column") && value === expression.value ? expression.valueType : getValueType(field, operator);
|
|
1014
1094
|
return {
|
|
1015
1095
|
...expression,
|
|
1016
1096
|
operator,
|
|
@@ -1076,8 +1156,7 @@ function addGroupAtPath(filter, path = []) {
|
|
|
1076
1156
|
return insertNodeAtPath(filter, path, createEmptyGroup(parent && isGroup(parent) && parent.mode === "and" ? "or" : "and"));
|
|
1077
1157
|
}
|
|
1078
1158
|
|
|
1079
|
-
|
|
1080
|
-
//#region src/filter/convertFilter.ts
|
|
1159
|
+
// src/filter/convertFilter.ts
|
|
1081
1160
|
const CONVERT_URL = "/api/filter/convert";
|
|
1082
1161
|
/**
|
|
1083
1162
|
* Converts between a structured filter and a DevExpress filter string.
|
|
@@ -1102,8 +1181,7 @@ async function convertFilter(request, signal) {
|
|
|
1102
1181
|
};
|
|
1103
1182
|
}
|
|
1104
1183
|
|
|
1105
|
-
|
|
1106
|
-
//#region src/filter/describeFilter.ts
|
|
1184
|
+
// src/filter/describeFilter.ts
|
|
1107
1185
|
/**
|
|
1108
1186
|
* Builds a human-readable description of a filter, without contacting the
|
|
1109
1187
|
* server. Use this for read-only summaries; the authoritative filter string is
|
|
@@ -1131,18 +1209,19 @@ function describeExpression(expression, fields) {
|
|
|
1131
1209
|
let label = getOperatorLabel(operator, field?.type);
|
|
1132
1210
|
let arity = getOperatorArity(operator);
|
|
1133
1211
|
if (arity === "none") return `[${caption}] ${label}`;
|
|
1134
|
-
return `[${caption}] ${label} ${formatValue(expression, arity)}`;
|
|
1212
|
+
return `[${caption}] ${label} ${formatValue(expression, arity, fields)}`;
|
|
1135
1213
|
}
|
|
1136
|
-
function formatValue(expression, arity) {
|
|
1214
|
+
function formatValue(expression, arity, fields) {
|
|
1137
1215
|
let { value, valueType } = expression;
|
|
1138
|
-
if (valueType === "special" && typeof value === "string") return getDynamicValueLabel(value);
|
|
1139
1216
|
if (Array.isArray(value)) {
|
|
1140
|
-
let formatted = value.map((item) => formatScalar(item, valueType));
|
|
1217
|
+
let formatted = value.map((item) => isRangeEnd(item) ? formatScalar(item.value, item.valueType, fields) : formatScalar(item, valueType, fields));
|
|
1141
1218
|
return arity === "pair" ? formatted.join(" - ") : `(${formatted.join(", ")})`;
|
|
1142
1219
|
}
|
|
1143
|
-
return formatScalar(value, valueType);
|
|
1220
|
+
return formatScalar(value, valueType, fields);
|
|
1144
1221
|
}
|
|
1145
|
-
function formatScalar(value, valueType) {
|
|
1222
|
+
function formatScalar(value, valueType, fields) {
|
|
1223
|
+
if (valueType === "special" && typeof value === "string") return getDynamicValueLabel(value);
|
|
1224
|
+
if (valueType === "column" && typeof value === "string") return `[${fields.get(value.toLowerCase())?.caption ?? value}]`;
|
|
1146
1225
|
if (value === null || value === void 0) return "";
|
|
1147
1226
|
if (value instanceof Date) return value.toISOString().slice(0, 10);
|
|
1148
1227
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
@@ -1150,8 +1229,7 @@ function formatScalar(value, valueType) {
|
|
|
1150
1229
|
return `'${String(value)}'`;
|
|
1151
1230
|
}
|
|
1152
1231
|
|
|
1153
|
-
|
|
1154
|
-
//#region src/filter/fields.ts
|
|
1232
|
+
// src/filter/fields.ts
|
|
1155
1233
|
const FIELD_TYPES = [
|
|
1156
1234
|
"string",
|
|
1157
1235
|
"number",
|
|
@@ -1186,8 +1264,7 @@ function toFieldMetadataList(records) {
|
|
|
1186
1264
|
return records.map(toFieldMetadata).filter((field) => field.name !== "" && !field.excludeFromFilter).sort((a, b) => a.name.localeCompare(b.name));
|
|
1187
1265
|
}
|
|
1188
1266
|
|
|
1189
|
-
|
|
1190
|
-
//#region src/filter/standardLayout.ts
|
|
1267
|
+
// src/filter/standardLayout.ts
|
|
1191
1268
|
/**
|
|
1192
1269
|
* The "standard layout" is the simplified shape the Windows filter editor can
|
|
1193
1270
|
* render in its flow view: an outer `or` group whose items are `and` groups of
|
|
@@ -1269,8 +1346,7 @@ function collectExpressions(item, into) {
|
|
|
1269
1346
|
for (let child of item.items) collectExpressions(child, into);
|
|
1270
1347
|
}
|
|
1271
1348
|
|
|
1272
|
-
|
|
1273
|
-
//#region src/filter/savedFilters.ts
|
|
1349
|
+
// src/filter/savedFilters.ts
|
|
1274
1350
|
/**
|
|
1275
1351
|
* `sviw_Database_MyFilters` unions in a synthetic "No Filter" row so the Windows
|
|
1276
1352
|
* tree can render a "no filter" entry. It is not a real filter and must be
|
|
@@ -1369,8 +1445,7 @@ const canDeleteSavedFilter = canRenameSavedFilter;
|
|
|
1369
1445
|
*/
|
|
1370
1446
|
const canSetCrossDomain = canRenameSavedFilter;
|
|
1371
1447
|
|
|
1372
|
-
|
|
1373
|
-
//#region src/getUserImage.ts
|
|
1448
|
+
// src/getUserImage.ts
|
|
1374
1449
|
const sizeMap = {
|
|
1375
1450
|
xs: 64,
|
|
1376
1451
|
sm: 128,
|
|
@@ -1385,8 +1460,7 @@ function getUserImage(primKeyOrOptions, login, hash, size = "xs") {
|
|
|
1385
1460
|
return `/api/data/blob/EmployeesLookup/${primKeyOrOptions}/Image/${encodeURIComponent(login ?? "")}${login ? "." : ""}${encodeURIComponent(hash)}.jpg${sizePx ? `?width=${sizePx}` : ""}`;
|
|
1386
1461
|
}
|
|
1387
1462
|
|
|
1388
|
-
|
|
1389
|
-
//#region src/localize.ts
|
|
1463
|
+
// src/localize.ts
|
|
1390
1464
|
/**
|
|
1391
1465
|
* Localization function for tagged templates. Replaces the arguments
|
|
1392
1466
|
* with $n, where n is the index, before asking Appframe to translate
|
|
@@ -1410,8 +1484,7 @@ function localize(stringParts, ...args) {
|
|
|
1410
1484
|
return localized;
|
|
1411
1485
|
}
|
|
1412
1486
|
|
|
1413
|
-
|
|
1414
|
-
//#region src/report/recordSource.ts
|
|
1487
|
+
// src/report/recordSource.ts
|
|
1415
1488
|
const XML_ENTITIES = {
|
|
1416
1489
|
amp: "&",
|
|
1417
1490
|
lt: "<",
|
|
@@ -1477,14 +1550,26 @@ function readSelectColumns(xml) {
|
|
|
1477
1550
|
return columns;
|
|
1478
1551
|
}
|
|
1479
1552
|
/**
|
|
1553
|
+
* The record source a document holds. Parameter lookups store one `<afRecordSource>`; a report
|
|
1554
|
+
* stores its record sources as `<ArrayOfAfReportRecordSource>` with one `<afReportRecordSource>`
|
|
1555
|
+
* per data source, of which the one with the ID `Master` feeds the report (else the first).
|
|
1556
|
+
*/
|
|
1557
|
+
function pickRecordSource(document) {
|
|
1558
|
+
if (!document) return null;
|
|
1559
|
+
let sources = [...document.matchAll(/<(afRecordSource|afReportRecordSource)(?:\s[^>]*)?>([\s\S]*?)<\/\1>/g)].map((match) => match[2]);
|
|
1560
|
+
if (sources.length === 0) return null;
|
|
1561
|
+
return sources.find((source) => readElement(source, "ID") === "Master") ?? sources[0];
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1480
1564
|
* Reads the parts of an `afRecordSource` a web client can act on: the view, the
|
|
1481
1565
|
* selected columns and the design-time filter.
|
|
1482
1566
|
*
|
|
1483
1567
|
* Returns `null` for anything that is not a record source, so a missing or
|
|
1484
1568
|
* empty `RecordSource` column simply means "no lookup".
|
|
1485
1569
|
*/
|
|
1486
|
-
function parseRecordSource(
|
|
1487
|
-
|
|
1570
|
+
function parseRecordSource(document) {
|
|
1571
|
+
let xml = pickRecordSource(document);
|
|
1572
|
+
if (!xml) return null;
|
|
1488
1573
|
let viewName = readElement(xml, "ViewName");
|
|
1489
1574
|
if (!viewName) return null;
|
|
1490
1575
|
let maxRecords = readElement(xml, "MaxRecords");
|
|
@@ -1497,8 +1582,7 @@ function parseRecordSource(xml) {
|
|
|
1497
1582
|
};
|
|
1498
1583
|
}
|
|
1499
1584
|
|
|
1500
|
-
|
|
1501
|
-
//#region src/report/parameters.ts
|
|
1585
|
+
// src/report/parameters.ts
|
|
1502
1586
|
/**
|
|
1503
1587
|
* Report parameters: mapping the metadata records onto something renderable,
|
|
1504
1588
|
* and the XML the parameter values are stored as on a saved filter.
|
|
@@ -1651,8 +1735,7 @@ function restoreParameterValues(parameters, xml) {
|
|
|
1651
1735
|
return values;
|
|
1652
1736
|
}
|
|
1653
1737
|
|
|
1654
|
-
|
|
1655
|
-
//#region src/report/parameterDefaults.ts
|
|
1738
|
+
// src/report/parameterDefaults.ts
|
|
1656
1739
|
/**
|
|
1657
1740
|
* Resolving report parameter defaults that name a stored procedure.
|
|
1658
1741
|
*
|
|
@@ -1714,8 +1797,7 @@ function firstValue(result) {
|
|
|
1714
1797
|
return row;
|
|
1715
1798
|
}
|
|
1716
1799
|
|
|
1717
|
-
|
|
1718
|
-
//#region src/report/render.ts
|
|
1800
|
+
// src/report/render.ts
|
|
1719
1801
|
/**
|
|
1720
1802
|
* Rendering a report through the framework's report route.
|
|
1721
1803
|
*
|
|
@@ -1919,8 +2001,7 @@ function toParameterEntries(parameters) {
|
|
|
1919
2001
|
}));
|
|
1920
2002
|
}
|
|
1921
2003
|
|
|
1922
|
-
|
|
1923
|
-
//#region src/report/report.ts
|
|
2004
|
+
// src/report/report.ts
|
|
1924
2005
|
/**
|
|
1925
2006
|
* Mapping a `sviw_WinClient_MyReports` record onto the report model, including
|
|
1926
2007
|
* which output formats the report offers.
|
|
@@ -1964,8 +2045,7 @@ function toReport(record) {
|
|
|
1964
2045
|
};
|
|
1965
2046
|
}
|
|
1966
2047
|
|
|
1967
|
-
|
|
1968
|
-
//#region src/alert.ts
|
|
2048
|
+
// src/alert.ts
|
|
1969
2049
|
const template = `
|
|
1970
2050
|
<dialog class="alert-dialog">
|
|
1971
2051
|
<form method="dialog">
|
|
@@ -2076,8 +2156,7 @@ async function alert(prompt, options = {}) {
|
|
|
2076
2156
|
});
|
|
2077
2157
|
}
|
|
2078
2158
|
|
|
2079
|
-
|
|
2080
|
-
//#region src/settings.ts
|
|
2159
|
+
// src/settings.ts
|
|
2081
2160
|
function getKey(article) {
|
|
2082
2161
|
return `${article ?? window.af?.article?.id ?? window.window?.location.pathname.substring(1).split("/")[0]}.${window.af?.userSession.login ?? "anonymous"}`;
|
|
2083
2162
|
}
|
|
@@ -2097,9 +2176,7 @@ function saveSettings(setting, value, article) {
|
|
|
2097
2176
|
localStorage.setItem(key, JSON.stringify(userSettings));
|
|
2098
2177
|
}
|
|
2099
2178
|
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
const version = "2.15.2";
|
|
2179
|
+
// src/index.ts
|
|
2180
|
+
const version = "2.16.1";
|
|
2103
2181
|
|
|
2104
|
-
|
|
2105
|
-
export { ConfirmButtons, NO_FILTER_ID, RELATIVE_DATE_VALUES, SessionHandler, UserSession, addGroupAtPath, alert, canDeleteSavedFilter, canEditSavedFilter, canRenameSavedFilter, canSetCrossDomain, canShareSavedFilter, changeOperator, coerceParameterValue, coerceToStandardLayout, confirm, convertFilter, countExpressions, createEmptyFilter, createEmptyGroup, createExpression, createLocalizer, decodeXml, describeFilter, downloadBlob, downloadReport, downloadRequest, encodeXml, expose, fetchParameterDefault, findSavedFilter, getDefaultOperator, getDefaultParameterValues, getDynamicValueLabel, getDynamicValuesForField, getLocalizedString, getNodeAtPath, getOperatorArity, getOperatorLabel, getOperatorsForField, getReportExtension, getReportFileName, getReportFormats, getReportMediaType, getReportRequest, getSessionData, getSettings, getUserImage, getValueType, insertNodeAtPath, invariant, isCompleteExpression, isEmptyFilter, isExpression, isGroup, isProcedureDefault, isStandardLayout, localStorage_default as localStorage, localize, logActivity, logError, mitt_default as mitt, normalizeOperator, openAlertDialog, openReport, parseParameterItems, parseParameters, parseRecordSource, partitionSavedFilters, printReport, pruneFilter, readElement, readElements, removeNodeAtPath, renderReport, resolveParameterDefaults, restoreParameterValues, saveSettings, serializeParameters, setGroupMode, startActivity, time, toFieldMetadata, toFieldMetadataList, toFilterFieldType, toFilterTree, toLocalDateTime, toLocalISODate, toLocalISODateTime, toReport, toReportParameter, toReportParameters, toSavedFilter, toSavedFilters, unwrapSingleAndGroup, updateNodeAtPath, version, waitForDialog };
|
|
2182
|
+
export { ConfirmButtons, NO_FILTER_ID, PERIOD_OPERATORS, RELATIVE_DATE_VALUES, SessionHandler, UserSession, addGroupAtPath, alert, canDeleteSavedFilter, canEditSavedFilter, canRenameSavedFilter, canSetCrossDomain, canShareSavedFilter, changeOperator, coerceParameterValue, coerceToStandardLayout, confirm, convertFilter, countExpressions, createEmptyFilter, createEmptyGroup, createExpression, createLocalizer, decodeXml, describeFilter, downloadBlob, downloadReport, downloadRequest, encodeXml, expose, fetchParameterDefault, findSavedFilter, getColumnsForField, getDefaultOperator, getDefaultParameterValues, getDynamicValueLabel, getDynamicValuesForField, getLocalizedString, getNodeAtPath, getOperatorArity, getOperatorLabel, getOperatorsForField, getReportExtension, getReportFileName, getReportFormats, getReportMediaType, getReportRequest, getSessionData, getSettings, getUserImage, getValueType, insertNodeAtPath, invariant, isCompleteExpression, isEmptyFilter, isExpression, isGroup, isProcedureDefault, isRangeEnd, isStandardLayout, localStorage_default as localStorage, localize, logActivity, logError, mitt_default as mitt, needsFilterString, normalizeOperator, openAlertDialog, openReport, parseParameterItems, parseParameters, parseRecordSource, partitionSavedFilters, printReport, pruneFilter, readElement, readElements, removeNodeAtPath, renderReport, resolveParameterDefaults, restoreParameterValues, saveSettings, serializeParameters, setGroupMode, startActivity, supportsColumnValue, time, toFieldMetadata, toFieldMetadataList, toFilterFieldType, toFilterTree, toLocalDateTime, toLocalISODate, toLocalISODateTime, toReport, toReportParameter, toReportParameters, toSavedFilter, toSavedFilters, unwrapSingleAndGroup, updateNodeAtPath, version, waitForDialog };
|