@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
package/dist/esm/af.common.js
CHANGED
|
@@ -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 \0@oxc-project+runtime@0.151.0/helpers/esm/typeof.js
|
|
24
|
+
// \0@oxc-project+runtime@0.157.0/helpers/esm/typeof.js
|
|
26
25
|
function _typeof(o) {
|
|
27
26
|
"@babel/helpers - typeof";
|
|
28
27
|
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
@@ -32,28 +31,25 @@ function _typeof(o) {
|
|
|
32
31
|
}, _typeof(o);
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
function toPrimitive(t, r) {
|
|
34
|
+
// \0@oxc-project+runtime@0.157.0/helpers/esm/toPrimitive.js
|
|
35
|
+
function toPrimitive(t, e) {
|
|
38
36
|
if ("object" != _typeof(t) || !t) return t;
|
|
39
|
-
var
|
|
40
|
-
if (void 0 !==
|
|
41
|
-
var i =
|
|
37
|
+
var r;
|
|
38
|
+
if ("undefined" != typeof Symbol && void 0 !== (r = t[Symbol.toPrimitive])) {
|
|
39
|
+
var i = r.call(t, e || "default");
|
|
42
40
|
if ("object" != _typeof(i)) return i;
|
|
43
41
|
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
44
42
|
}
|
|
45
|
-
return ("string" ===
|
|
43
|
+
return ("string" === e ? String : Number)(t);
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
//#region \0@oxc-project+runtime@0.151.0/helpers/esm/toPropertyKey.js
|
|
46
|
+
// \0@oxc-project+runtime@0.157.0/helpers/esm/toPropertyKey.js
|
|
50
47
|
function toPropertyKey(t) {
|
|
51
48
|
var i = toPrimitive(t, "string");
|
|
52
49
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
//#region \0@oxc-project+runtime@0.151.0/helpers/esm/defineProperty.js
|
|
52
|
+
// \0@oxc-project+runtime@0.157.0/helpers/esm/defineProperty.js
|
|
57
53
|
function _defineProperty(e, r, t) {
|
|
58
54
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
59
55
|
value: t,
|
|
@@ -63,8 +59,7 @@ function _defineProperty(e, r, t) {
|
|
|
63
59
|
}) : e[r] = t, e;
|
|
64
60
|
}
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
//#region src/SessionHandler.ts
|
|
62
|
+
// src/SessionHandler.ts
|
|
68
63
|
var SessionHandler = class {
|
|
69
64
|
constructor() {
|
|
70
65
|
_defineProperty(this, "eventHandler", mitt_default());
|
|
@@ -111,8 +106,7 @@ var SessionHandler = class {
|
|
|
111
106
|
}
|
|
112
107
|
};
|
|
113
108
|
|
|
114
|
-
|
|
115
|
-
//#region src/UserSession.ts
|
|
109
|
+
// src/UserSession.ts
|
|
116
110
|
var UserSession = class extends SessionHandler {
|
|
117
111
|
constructor(userData) {
|
|
118
112
|
super();
|
|
@@ -184,8 +178,7 @@ var UserSession = class extends SessionHandler {
|
|
|
184
178
|
}
|
|
185
179
|
};
|
|
186
180
|
|
|
187
|
-
|
|
188
|
-
//#region src/log.ts
|
|
181
|
+
// src/log.ts
|
|
189
182
|
const article = window.af?.article?.id ?? window.window?.location.pathname.substring(1).split("/")[0];
|
|
190
183
|
/**
|
|
191
184
|
* Logs an activity to the website activity log
|
|
@@ -330,15 +323,13 @@ function getSessionData() {
|
|
|
330
323
|
};
|
|
331
324
|
}
|
|
332
325
|
|
|
333
|
-
|
|
334
|
-
//#region src/errorHandler.ts
|
|
326
|
+
// src/errorHandler.ts
|
|
335
327
|
window.window?.addEventListener("error", (event) => {
|
|
336
328
|
if (typeof event === "object" && "error" in event) logError(event.error);
|
|
337
329
|
else if (typeof event === "object" && "message" in event) logError(event);
|
|
338
330
|
});
|
|
339
331
|
|
|
340
|
-
|
|
341
|
-
//#region src/localStorage.ts
|
|
332
|
+
// src/localStorage.ts
|
|
342
333
|
function getLocalKey(key) {
|
|
343
334
|
return [
|
|
344
335
|
af?.article?.id || "",
|
|
@@ -375,8 +366,7 @@ var LocalStorage = class {
|
|
|
375
366
|
};
|
|
376
367
|
var localStorage_default = new LocalStorage();
|
|
377
368
|
|
|
378
|
-
|
|
379
|
-
//#region src/date.ts
|
|
369
|
+
// src/date.ts
|
|
380
370
|
function toLocalISODate(date) {
|
|
381
371
|
return date ? toLocalISODateTime(date).substring(0, 10) : null;
|
|
382
372
|
}
|
|
@@ -399,8 +389,7 @@ function toLocalDateTime(date) {
|
|
|
399
389
|
return date ? date.toISOString().substring(0, 16) : "";
|
|
400
390
|
}
|
|
401
391
|
|
|
402
|
-
|
|
403
|
-
//#region src/downloadBlob.ts
|
|
392
|
+
// src/downloadBlob.ts
|
|
404
393
|
function downloadBlob(blob, fileName) {
|
|
405
394
|
return new Promise((resolve, reject) => {
|
|
406
395
|
try {
|
|
@@ -422,8 +411,7 @@ function downloadBlob(blob, fileName) {
|
|
|
422
411
|
});
|
|
423
412
|
}
|
|
424
413
|
|
|
425
|
-
|
|
426
|
-
//#region src/invariant.ts
|
|
414
|
+
// src/invariant.ts
|
|
427
415
|
/**
|
|
428
416
|
* Throws an error if the condition is falsy. The provided message is used as the error message.
|
|
429
417
|
* A function can be provided to lazily generate the message.
|
|
@@ -438,8 +426,7 @@ function invariant(condition, message) {
|
|
|
438
426
|
throw new Error(provided ?? "Invariant failed");
|
|
439
427
|
}
|
|
440
428
|
|
|
441
|
-
|
|
442
|
-
//#region src/downloadRequest.ts
|
|
429
|
+
// src/downloadRequest.ts
|
|
443
430
|
async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
444
431
|
let bytesReceived = 0;
|
|
445
432
|
onProgress?.({ status: "requested" });
|
|
@@ -493,8 +480,7 @@ async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
|
493
480
|
}
|
|
494
481
|
}
|
|
495
482
|
|
|
496
|
-
|
|
497
|
-
//#region src/parseFunc.ts
|
|
483
|
+
// src/parseFunc.ts
|
|
498
484
|
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)*)\}$/;
|
|
499
485
|
const arrowBlockRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*\{([\s\S]*)\}$/;
|
|
500
486
|
const arrowExpressionRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*([\s\S]+)$/;
|
|
@@ -528,13 +514,11 @@ function parseFunc(func) {
|
|
|
528
514
|
};
|
|
529
515
|
}
|
|
530
516
|
|
|
531
|
-
|
|
532
|
-
//#region src/Namespace.ts
|
|
517
|
+
// src/Namespace.ts
|
|
533
518
|
function Namespace() {}
|
|
534
519
|
Namespace.prototype = Object.create(null);
|
|
535
520
|
|
|
536
|
-
|
|
537
|
-
//#region src/requireNamespace.ts
|
|
521
|
+
// src/requireNamespace.ts
|
|
538
522
|
function requireNamespace(root, namespace) {
|
|
539
523
|
if (typeof root === "object") {
|
|
540
524
|
if (typeof namespace === "string") return requireNamespace(root, namespace.split("."));
|
|
@@ -546,8 +530,7 @@ function requireNamespace(root, namespace) {
|
|
|
546
530
|
}
|
|
547
531
|
}
|
|
548
532
|
|
|
549
|
-
|
|
550
|
-
//#region src/expose.ts
|
|
533
|
+
// src/expose.ts
|
|
551
534
|
/**
|
|
552
535
|
* Expose a value to a potentially deeply nested global variable
|
|
553
536
|
* @param name {string | object} Either a dot-separated path to the global variable, or an object to expose the value on
|
|
@@ -577,8 +560,7 @@ function expose(name, value, options = {}) {
|
|
|
577
560
|
}
|
|
578
561
|
}
|
|
579
562
|
|
|
580
|
-
|
|
581
|
-
//#region src/getLocalizedString.ts
|
|
563
|
+
// src/getLocalizedString.ts
|
|
582
564
|
async function fetchLocalizedString(text, project) {
|
|
583
565
|
try {
|
|
584
566
|
let response = await fetch(`/api/user/localize/new/${encodeURIComponent(project)}`, {
|
|
@@ -667,8 +649,7 @@ function createLocalizer(project) {
|
|
|
667
649
|
};
|
|
668
650
|
}
|
|
669
651
|
|
|
670
|
-
|
|
671
|
-
//#region src/filter/operators.ts
|
|
652
|
+
// src/filter/operators.ts
|
|
672
653
|
/**
|
|
673
654
|
* Operators that take no value at all.
|
|
674
655
|
*
|
|
@@ -682,6 +663,26 @@ const PARAMETERLESS_OPERATORS = [
|
|
|
682
663
|
"isblank",
|
|
683
664
|
"isnotblank"
|
|
684
665
|
];
|
|
666
|
+
/**
|
|
667
|
+
* Whole-period checks for dates, such as "Is last week", in the order of the
|
|
668
|
+
* Windows filter editor. They take no value. The framework has no operator for
|
|
669
|
+
* them, so a filter using one is sent as a filter string (`needsFilterString`).
|
|
670
|
+
*/
|
|
671
|
+
const PERIOD_OPERATORS = [
|
|
672
|
+
"isbeyondthisyear",
|
|
673
|
+
"islaterthisyear",
|
|
674
|
+
"islaterthismonth",
|
|
675
|
+
"isnextweek",
|
|
676
|
+
"islaterthisweek",
|
|
677
|
+
"istomorrow",
|
|
678
|
+
"istoday",
|
|
679
|
+
"isyesterday",
|
|
680
|
+
"isearlierthisweek",
|
|
681
|
+
"islastweek",
|
|
682
|
+
"isearlierthismonth",
|
|
683
|
+
"isearlierthisyear",
|
|
684
|
+
"ispriorthisyear"
|
|
685
|
+
];
|
|
685
686
|
/** Operators whose value is a two element `[from, to]` array. */
|
|
686
687
|
const PAIR_OPERATORS = [
|
|
687
688
|
"between",
|
|
@@ -692,7 +693,7 @@ const PAIR_OPERATORS = [
|
|
|
692
693
|
/** Operators whose value is an array of values. */
|
|
693
694
|
const LIST_OPERATORS = ["inlist", "notinlist"];
|
|
694
695
|
function getOperatorArity(operator) {
|
|
695
|
-
if (PARAMETERLESS_OPERATORS.includes(operator)) return "none";
|
|
696
|
+
if (PARAMETERLESS_OPERATORS.includes(operator) || PERIOD_OPERATORS.includes(operator)) return "none";
|
|
696
697
|
if (PAIR_OPERATORS.includes(operator)) return "pair";
|
|
697
698
|
if (LIST_OPERATORS.includes(operator)) return "list";
|
|
698
699
|
return "single";
|
|
@@ -735,7 +736,20 @@ const OPERATOR_LABELS = {
|
|
|
735
736
|
datelessthan: "Before",
|
|
736
737
|
datelessthanorequal: "Up to",
|
|
737
738
|
datebetween: "Between",
|
|
738
|
-
datenotbetween: "Not between"
|
|
739
|
+
datenotbetween: "Not between",
|
|
740
|
+
isbeyondthisyear: "Is beyond this year",
|
|
741
|
+
islaterthisyear: "Is later this year",
|
|
742
|
+
islaterthismonth: "Is later this month",
|
|
743
|
+
isnextweek: "Is next week",
|
|
744
|
+
islaterthisweek: "Is later this week",
|
|
745
|
+
istomorrow: "Is tomorrow",
|
|
746
|
+
istoday: "Is today",
|
|
747
|
+
isyesterday: "Is yesterday",
|
|
748
|
+
isearlierthisweek: "Is earlier this week",
|
|
749
|
+
islastweek: "Is last week",
|
|
750
|
+
isearlierthismonth: "Is earlier this month",
|
|
751
|
+
isearlierthisyear: "Is earlier this year",
|
|
752
|
+
ispriorthisyear: "Is prior to this year"
|
|
739
753
|
};
|
|
740
754
|
/** Comparison operator labels used for date and time fields. */
|
|
741
755
|
const DATE_OPERATOR_LABELS = {
|
|
@@ -780,13 +794,15 @@ const OPERATORS_BY_TYPE = {
|
|
|
780
794
|
...EQUALITY,
|
|
781
795
|
...LIST_CHECKS,
|
|
782
796
|
...RANGE,
|
|
783
|
-
...COMPARISONS
|
|
797
|
+
...COMPARISONS,
|
|
798
|
+
...PERIOD_OPERATORS
|
|
784
799
|
],
|
|
785
800
|
datetime: [
|
|
786
801
|
...EQUALITY,
|
|
787
802
|
...LIST_CHECKS,
|
|
788
803
|
...RANGE,
|
|
789
|
-
...COMPARISONS
|
|
804
|
+
...COMPARISONS,
|
|
805
|
+
...PERIOD_OPERATORS
|
|
790
806
|
],
|
|
791
807
|
number: [
|
|
792
808
|
...EQUALITY,
|
|
@@ -859,7 +875,10 @@ function getValueType(field, operator) {
|
|
|
859
875
|
const RELATIVE_DATE_VALUES = [
|
|
860
876
|
"Yesterday",
|
|
861
877
|
"Today",
|
|
878
|
+
"Now",
|
|
862
879
|
"Tomorrow",
|
|
880
|
+
"DayAfterTomorrow",
|
|
881
|
+
"TwoWeeksAway",
|
|
863
882
|
"ThisWeek",
|
|
864
883
|
"ThisMonth",
|
|
865
884
|
"ThisYear",
|
|
@@ -874,7 +893,10 @@ const DYNAMIC_VALUE_LABELS = {
|
|
|
874
893
|
CurrentUser: "Current user",
|
|
875
894
|
Yesterday: "Yesterday",
|
|
876
895
|
Today: "Today",
|
|
896
|
+
Now: "Now",
|
|
877
897
|
Tomorrow: "Tomorrow",
|
|
898
|
+
DayAfterTomorrow: "Day after tomorrow",
|
|
899
|
+
TwoWeeksAway: "Two weeks away",
|
|
878
900
|
ThisWeek: "This week",
|
|
879
901
|
ThisMonth: "This month",
|
|
880
902
|
ThisYear: "This year",
|
|
@@ -910,6 +932,30 @@ function getDynamicValuesForField(field) {
|
|
|
910
932
|
if (field.type === "string") return ["CurrentUser"];
|
|
911
933
|
return [];
|
|
912
934
|
}
|
|
935
|
+
/** Operators that can compare a field with another column. */
|
|
936
|
+
const COLUMN_OPERATORS = [
|
|
937
|
+
...EQUALITY,
|
|
938
|
+
...RANGE,
|
|
939
|
+
...COMPARISONS
|
|
940
|
+
];
|
|
941
|
+
/** Whether an operator can take another column as its value. */
|
|
942
|
+
function supportsColumnValue(operator) {
|
|
943
|
+
return COLUMN_OPERATORS.includes(operator);
|
|
944
|
+
}
|
|
945
|
+
/** Dates and datetimes compare with each other; other types only with their own. */
|
|
946
|
+
function getComparableType(type) {
|
|
947
|
+
return type === "datetime" ? "date" : type;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* The columns a field can be compared with: the other fields of the same type,
|
|
951
|
+
* as in the Windows filter editor. Booleans have only true and false checks,
|
|
952
|
+
* so they get none.
|
|
953
|
+
*/
|
|
954
|
+
function getColumnsForField(field, fields) {
|
|
955
|
+
if (field.type === "boolean") return [];
|
|
956
|
+
let type = getComparableType(field.type);
|
|
957
|
+
return fields.filter((other) => other.name !== field.name && !other.excludeFromFilter && getComparableType(other.type) === type);
|
|
958
|
+
}
|
|
913
959
|
/**
|
|
914
960
|
* Aliases for names `eOperators.ToString()` can produce that are not operators
|
|
915
961
|
* in their own right. `Not` is the bare negation flag (i.e. "not equal to"),
|
|
@@ -933,8 +979,7 @@ function normalizeOperator(operator) {
|
|
|
933
979
|
return OPERATOR_ALIASES[normalized] ?? normalized;
|
|
934
980
|
}
|
|
935
981
|
|
|
936
|
-
|
|
937
|
-
//#region src/filter/tree.ts
|
|
982
|
+
// src/filter/tree.ts
|
|
938
983
|
/**
|
|
939
984
|
* Creates the default empty filter: a single `and` group with no conditions.
|
|
940
985
|
*/
|
|
@@ -1021,10 +1066,40 @@ function isCompleteExpression(item) {
|
|
|
1021
1066
|
let arity = getOperatorArity(normalizeOperator(item.operator));
|
|
1022
1067
|
if (arity === "none") return true;
|
|
1023
1068
|
if (item.value === null || item.value === void 0 || item.value === "") return false;
|
|
1024
|
-
if (arity === "pair") return Array.isArray(item.value) && item.value.length === 2 && item.value.every((v) =>
|
|
1069
|
+
if (arity === "pair") return Array.isArray(item.value) && item.value.length === 2 && item.value.every((v) => {
|
|
1070
|
+
let end = isRangeEnd(v) ? v.value : v;
|
|
1071
|
+
return end !== "" && end != null;
|
|
1072
|
+
});
|
|
1025
1073
|
if (arity === "list") return Array.isArray(item.value) && item.value.length > 0;
|
|
1026
1074
|
return true;
|
|
1027
1075
|
}
|
|
1076
|
+
/** Whether a range value end is a `FilterRangeEnd` rather than a plain value. */
|
|
1077
|
+
function isRangeEnd(value) {
|
|
1078
|
+
return typeof value === "object" && value !== null && !(value instanceof Date) && "valueType" in value;
|
|
1079
|
+
}
|
|
1080
|
+
/** Dynamic values the framework has no `eDynamicValue` member for. */
|
|
1081
|
+
const DYNAMIC_VALUES_WITHOUT_FRAMEWORK_SUPPORT = /* @__PURE__ */ new Set([
|
|
1082
|
+
"Now",
|
|
1083
|
+
"DayAfterTomorrow",
|
|
1084
|
+
"TwoWeeksAway"
|
|
1085
|
+
]);
|
|
1086
|
+
function needsConversion(valueType, value) {
|
|
1087
|
+
if (valueType === "column") return true;
|
|
1088
|
+
return valueType === "special" && typeof value === "string" && DYNAMIC_VALUES_WITHOUT_FRAMEWORK_SUPPORT.has(value);
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* True when the filter holds something the framework's data API cannot read
|
|
1092
|
+
* from a filter object: another column, a range end that is not a plain value,
|
|
1093
|
+
* one of the dates only the Windows editor offers, or a whole-period check. Such a filter must be
|
|
1094
|
+
* converted with `convertFilter` and sent as a filter string.
|
|
1095
|
+
*/
|
|
1096
|
+
function needsFilterString(filter) {
|
|
1097
|
+
if (!filter) return false;
|
|
1098
|
+
if (isGroup(filter)) return filter.items.some(needsFilterString);
|
|
1099
|
+
if (PERIOD_OPERATORS.includes(normalizeOperator(filter.operator))) return true;
|
|
1100
|
+
if (needsConversion(filter.valueType, filter.value)) return true;
|
|
1101
|
+
return Array.isArray(filter.value) && filter.value.some((item) => isRangeEnd(item));
|
|
1102
|
+
}
|
|
1028
1103
|
/**
|
|
1029
1104
|
* Creates an expression for a field, using the field's default operator and an
|
|
1030
1105
|
* empty value of the right shape.
|
|
@@ -1055,8 +1130,9 @@ function changeOperator(expression, operator, field) {
|
|
|
1055
1130
|
let previousArity = getOperatorArity(normalizeOperator(expression.operator));
|
|
1056
1131
|
let arity = getOperatorArity(operator);
|
|
1057
1132
|
let value = expression.value;
|
|
1058
|
-
|
|
1059
|
-
|
|
1133
|
+
let holdsColumn = expression.valueType === "column" || Array.isArray(value) && value.some((item) => isRangeEnd(item) && item.valueType === "column");
|
|
1134
|
+
if (arity !== previousArity || holdsColumn && !supportsColumnValue(operator)) value = createEmptyValue(operator);
|
|
1135
|
+
let valueType = (expression.valueType === "special" || expression.valueType === "column") && value === expression.value ? expression.valueType : getValueType(field, operator);
|
|
1060
1136
|
return {
|
|
1061
1137
|
...expression,
|
|
1062
1138
|
operator,
|
|
@@ -1122,8 +1198,7 @@ function addGroupAtPath(filter, path = []) {
|
|
|
1122
1198
|
return insertNodeAtPath(filter, path, createEmptyGroup(parent && isGroup(parent) && parent.mode === "and" ? "or" : "and"));
|
|
1123
1199
|
}
|
|
1124
1200
|
|
|
1125
|
-
|
|
1126
|
-
//#region src/filter/convertFilter.ts
|
|
1201
|
+
// src/filter/convertFilter.ts
|
|
1127
1202
|
const CONVERT_URL = "/api/filter/convert";
|
|
1128
1203
|
/**
|
|
1129
1204
|
* Converts between a structured filter and a DevExpress filter string.
|
|
@@ -1148,8 +1223,7 @@ async function convertFilter(request, signal) {
|
|
|
1148
1223
|
};
|
|
1149
1224
|
}
|
|
1150
1225
|
|
|
1151
|
-
|
|
1152
|
-
//#region src/filter/describeFilter.ts
|
|
1226
|
+
// src/filter/describeFilter.ts
|
|
1153
1227
|
/**
|
|
1154
1228
|
* Builds a human-readable description of a filter, without contacting the
|
|
1155
1229
|
* server. Use this for read-only summaries; the authoritative filter string is
|
|
@@ -1177,18 +1251,19 @@ function describeExpression(expression, fields) {
|
|
|
1177
1251
|
let label = getOperatorLabel(operator, field?.type);
|
|
1178
1252
|
let arity = getOperatorArity(operator);
|
|
1179
1253
|
if (arity === "none") return `[${caption}] ${label}`;
|
|
1180
|
-
return `[${caption}] ${label} ${formatValue(expression, arity)}`;
|
|
1254
|
+
return `[${caption}] ${label} ${formatValue(expression, arity, fields)}`;
|
|
1181
1255
|
}
|
|
1182
|
-
function formatValue(expression, arity) {
|
|
1256
|
+
function formatValue(expression, arity, fields) {
|
|
1183
1257
|
let { value, valueType } = expression;
|
|
1184
|
-
if (valueType === "special" && typeof value === "string") return getDynamicValueLabel(value);
|
|
1185
1258
|
if (Array.isArray(value)) {
|
|
1186
|
-
let formatted = value.map((item) => formatScalar(item, valueType));
|
|
1259
|
+
let formatted = value.map((item) => isRangeEnd(item) ? formatScalar(item.value, item.valueType, fields) : formatScalar(item, valueType, fields));
|
|
1187
1260
|
return arity === "pair" ? formatted.join(" - ") : `(${formatted.join(", ")})`;
|
|
1188
1261
|
}
|
|
1189
|
-
return formatScalar(value, valueType);
|
|
1262
|
+
return formatScalar(value, valueType, fields);
|
|
1190
1263
|
}
|
|
1191
|
-
function formatScalar(value, valueType) {
|
|
1264
|
+
function formatScalar(value, valueType, fields) {
|
|
1265
|
+
if (valueType === "special" && typeof value === "string") return getDynamicValueLabel(value);
|
|
1266
|
+
if (valueType === "column" && typeof value === "string") return `[${fields.get(value.toLowerCase())?.caption ?? value}]`;
|
|
1192
1267
|
if (value === null || value === void 0) return "";
|
|
1193
1268
|
if (value instanceof Date) return value.toISOString().slice(0, 10);
|
|
1194
1269
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
@@ -1196,8 +1271,7 @@ function formatScalar(value, valueType) {
|
|
|
1196
1271
|
return `'${String(value)}'`;
|
|
1197
1272
|
}
|
|
1198
1273
|
|
|
1199
|
-
|
|
1200
|
-
//#region src/filter/fields.ts
|
|
1274
|
+
// src/filter/fields.ts
|
|
1201
1275
|
const FIELD_TYPES = [
|
|
1202
1276
|
"string",
|
|
1203
1277
|
"number",
|
|
@@ -1232,8 +1306,7 @@ function toFieldMetadataList(records) {
|
|
|
1232
1306
|
return records.map(toFieldMetadata).filter((field) => field.name !== "" && !field.excludeFromFilter).sort((a, b) => a.name.localeCompare(b.name));
|
|
1233
1307
|
}
|
|
1234
1308
|
|
|
1235
|
-
|
|
1236
|
-
//#region src/filter/standardLayout.ts
|
|
1309
|
+
// src/filter/standardLayout.ts
|
|
1237
1310
|
/**
|
|
1238
1311
|
* The "standard layout" is the simplified shape the Windows filter editor can
|
|
1239
1312
|
* render in its flow view: an outer `or` group whose items are `and` groups of
|
|
@@ -1315,8 +1388,7 @@ function collectExpressions(item, into) {
|
|
|
1315
1388
|
for (let child of item.items) collectExpressions(child, into);
|
|
1316
1389
|
}
|
|
1317
1390
|
|
|
1318
|
-
|
|
1319
|
-
//#region src/filter/savedFilters.ts
|
|
1391
|
+
// src/filter/savedFilters.ts
|
|
1320
1392
|
/**
|
|
1321
1393
|
* `sviw_Database_MyFilters` unions in a synthetic "No Filter" row so the Windows
|
|
1322
1394
|
* tree can render a "no filter" entry. It is not a real filter and must be
|
|
@@ -1415,8 +1487,7 @@ const canDeleteSavedFilter = canRenameSavedFilter;
|
|
|
1415
1487
|
*/
|
|
1416
1488
|
const canSetCrossDomain = canRenameSavedFilter;
|
|
1417
1489
|
|
|
1418
|
-
|
|
1419
|
-
//#region src/getUserImage.ts
|
|
1490
|
+
// src/getUserImage.ts
|
|
1420
1491
|
const sizeMap = {
|
|
1421
1492
|
xs: 64,
|
|
1422
1493
|
sm: 128,
|
|
@@ -1431,8 +1502,7 @@ function getUserImage(primKeyOrOptions, login, hash, size = "xs") {
|
|
|
1431
1502
|
return `/api/data/blob/EmployeesLookup/${primKeyOrOptions}/Image/${encodeURIComponent(login ?? "")}${login ? "." : ""}${encodeURIComponent(hash)}.jpg${sizePx ? `?width=${sizePx}` : ""}`;
|
|
1432
1503
|
}
|
|
1433
1504
|
|
|
1434
|
-
|
|
1435
|
-
//#region src/localize.ts
|
|
1505
|
+
// src/localize.ts
|
|
1436
1506
|
/**
|
|
1437
1507
|
* Localization function for tagged templates. Replaces the arguments
|
|
1438
1508
|
* with $n, where n is the index, before asking Appframe to translate
|
|
@@ -1456,8 +1526,7 @@ function localize(stringParts, ...args) {
|
|
|
1456
1526
|
return localized;
|
|
1457
1527
|
}
|
|
1458
1528
|
|
|
1459
|
-
|
|
1460
|
-
//#region src/report/recordSource.ts
|
|
1529
|
+
// src/report/recordSource.ts
|
|
1461
1530
|
const XML_ENTITIES = {
|
|
1462
1531
|
amp: "&",
|
|
1463
1532
|
lt: "<",
|
|
@@ -1523,14 +1592,26 @@ function readSelectColumns(xml) {
|
|
|
1523
1592
|
return columns;
|
|
1524
1593
|
}
|
|
1525
1594
|
/**
|
|
1595
|
+
* The record source a document holds. Parameter lookups store one `<afRecordSource>`; a report
|
|
1596
|
+
* stores its record sources as `<ArrayOfAfReportRecordSource>` with one `<afReportRecordSource>`
|
|
1597
|
+
* per data source, of which the one with the ID `Master` feeds the report (else the first).
|
|
1598
|
+
*/
|
|
1599
|
+
function pickRecordSource(document) {
|
|
1600
|
+
if (!document) return null;
|
|
1601
|
+
let sources = [...document.matchAll(/<(afRecordSource|afReportRecordSource)(?:\s[^>]*)?>([\s\S]*?)<\/\1>/g)].map((match) => match[2]);
|
|
1602
|
+
if (sources.length === 0) return null;
|
|
1603
|
+
return sources.find((source) => readElement(source, "ID") === "Master") ?? sources[0];
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1526
1606
|
* Reads the parts of an `afRecordSource` a web client can act on: the view, the
|
|
1527
1607
|
* selected columns and the design-time filter.
|
|
1528
1608
|
*
|
|
1529
1609
|
* Returns `null` for anything that is not a record source, so a missing or
|
|
1530
1610
|
* empty `RecordSource` column simply means "no lookup".
|
|
1531
1611
|
*/
|
|
1532
|
-
function parseRecordSource(
|
|
1533
|
-
|
|
1612
|
+
function parseRecordSource(document) {
|
|
1613
|
+
let xml = pickRecordSource(document);
|
|
1614
|
+
if (!xml) return null;
|
|
1534
1615
|
let viewName = readElement(xml, "ViewName");
|
|
1535
1616
|
if (!viewName) return null;
|
|
1536
1617
|
let maxRecords = readElement(xml, "MaxRecords");
|
|
@@ -1543,8 +1624,7 @@ function parseRecordSource(xml) {
|
|
|
1543
1624
|
};
|
|
1544
1625
|
}
|
|
1545
1626
|
|
|
1546
|
-
|
|
1547
|
-
//#region src/report/parameters.ts
|
|
1627
|
+
// src/report/parameters.ts
|
|
1548
1628
|
/**
|
|
1549
1629
|
* Report parameters: mapping the metadata records onto something renderable,
|
|
1550
1630
|
* and the XML the parameter values are stored as on a saved filter.
|
|
@@ -1697,8 +1777,7 @@ function restoreParameterValues(parameters, xml) {
|
|
|
1697
1777
|
return values;
|
|
1698
1778
|
}
|
|
1699
1779
|
|
|
1700
|
-
|
|
1701
|
-
//#region src/report/parameterDefaults.ts
|
|
1780
|
+
// src/report/parameterDefaults.ts
|
|
1702
1781
|
/**
|
|
1703
1782
|
* Resolving report parameter defaults that name a stored procedure.
|
|
1704
1783
|
*
|
|
@@ -1760,8 +1839,7 @@ function firstValue(result) {
|
|
|
1760
1839
|
return row;
|
|
1761
1840
|
}
|
|
1762
1841
|
|
|
1763
|
-
|
|
1764
|
-
//#region src/report/render.ts
|
|
1842
|
+
// src/report/render.ts
|
|
1765
1843
|
/**
|
|
1766
1844
|
* Rendering a report through the framework's report route.
|
|
1767
1845
|
*
|
|
@@ -1965,8 +2043,7 @@ function toParameterEntries(parameters) {
|
|
|
1965
2043
|
}));
|
|
1966
2044
|
}
|
|
1967
2045
|
|
|
1968
|
-
|
|
1969
|
-
//#region src/report/report.ts
|
|
2046
|
+
// src/report/report.ts
|
|
1970
2047
|
/**
|
|
1971
2048
|
* Mapping a `sviw_WinClient_MyReports` record onto the report model, including
|
|
1972
2049
|
* which output formats the report offers.
|
|
@@ -2010,8 +2087,7 @@ function toReport(record) {
|
|
|
2010
2087
|
};
|
|
2011
2088
|
}
|
|
2012
2089
|
|
|
2013
|
-
|
|
2014
|
-
//#region src/alert.ts
|
|
2090
|
+
// src/alert.ts
|
|
2015
2091
|
const template = `
|
|
2016
2092
|
<dialog class="alert-dialog">
|
|
2017
2093
|
<form method="dialog">
|
|
@@ -2122,8 +2198,7 @@ async function alert(prompt, options = {}) {
|
|
|
2122
2198
|
});
|
|
2123
2199
|
}
|
|
2124
2200
|
|
|
2125
|
-
|
|
2126
|
-
//#region src/settings.ts
|
|
2201
|
+
// src/settings.ts
|
|
2127
2202
|
function getKey(article) {
|
|
2128
2203
|
return `${article ?? window.af?.article?.id ?? window.window?.location.pathname.substring(1).split("/")[0]}.${window.af?.userSession.login ?? "anonymous"}`;
|
|
2129
2204
|
}
|
|
@@ -2143,9 +2218,7 @@ function saveSettings(setting, value, article) {
|
|
|
2143
2218
|
localStorage.setItem(key, JSON.stringify(userSettings));
|
|
2144
2219
|
}
|
|
2145
2220
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
const version = "2.15.2";
|
|
2221
|
+
// src/index.ts
|
|
2222
|
+
const version = "2.16.1";
|
|
2149
2223
|
|
|
2150
|
-
|
|
2151
|
-
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 };
|
|
2224
|
+
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 };
|