@olenbetong/appframe-core 2.15.1 → 2.16.0
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 +174 -88
- package/dist/esm/af.common.legacy.js +170 -80
- 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 +178 -87
- package/dist/iife/af.common.legacy.js +174 -79
- 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/log.js +32 -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/log.ts +34 -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
|
|
@@ -219,6 +216,30 @@ async function logError(error) {
|
|
|
219
216
|
function getSessionId() {
|
|
220
217
|
return crypto.randomUUID?.();
|
|
221
218
|
}
|
|
219
|
+
/** The version in the first URL served under `/lib/@olenbetong/<name>/<version>/`. */
|
|
220
|
+
function libVersion(name, urls) {
|
|
221
|
+
let pattern = new RegExp(`/lib/@olenbetong/${name}/([^/]+)/`);
|
|
222
|
+
for (let url of urls) {
|
|
223
|
+
let match = typeof url === "string" ? pattern.exec(url) : null;
|
|
224
|
+
if (match) return match[1];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The synergi-chrome and synergi-platform versions the page loads on the 2026 site templates,
|
|
229
|
+
* read from the chrome's script tag and the platform import map rather than from the chrome
|
|
230
|
+
* itself, so they are known even when the chrome failed to start. Empty on other pages.
|
|
231
|
+
*/
|
|
232
|
+
function getPlatformVersions() {
|
|
233
|
+
let versions = {};
|
|
234
|
+
try {
|
|
235
|
+
let importMap = document.querySelector("script[type=\"importmap\"]");
|
|
236
|
+
let platform = importMap ? libVersion("synergi-platform", Object.values(JSON.parse(importMap.textContent ?? "{}").imports ?? {})) : void 0;
|
|
237
|
+
if (platform) versions["synergi-platform"] = platform;
|
|
238
|
+
} catch {}
|
|
239
|
+
let chrome = libVersion("synergi-chrome", Array.from(document.scripts, (script) => script.src)) ?? window.ob?.chrome?.version;
|
|
240
|
+
if (chrome) versions["synergi-chrome"] = chrome;
|
|
241
|
+
return versions;
|
|
242
|
+
}
|
|
222
243
|
let sessionStart = Date.now();
|
|
223
244
|
let sessionId = getSessionId();
|
|
224
245
|
let deviceId = "";
|
|
@@ -227,7 +248,8 @@ if ("document" in window) {
|
|
|
227
248
|
let startSession = () => logActivity("AfSessionInit", {
|
|
228
249
|
"appframe-core": window.af?.common?.version,
|
|
229
250
|
"appframe-data": window.af?.data?.version,
|
|
230
|
-
"app-version": window.af?.article?.version
|
|
251
|
+
"app-version": window.af?.article?.version,
|
|
252
|
+
...getPlatformVersions()
|
|
231
253
|
});
|
|
232
254
|
document.addEventListener("DOMContentLoaded", () => {
|
|
233
255
|
deviceId = localStorage.getItem("_afDeviceId");
|
|
@@ -261,15 +283,13 @@ function getSessionData() {
|
|
|
261
283
|
};
|
|
262
284
|
}
|
|
263
285
|
|
|
264
|
-
|
|
265
|
-
//#region src/errorHandler.ts
|
|
286
|
+
// src/errorHandler.ts
|
|
266
287
|
window.window?.addEventListener("error", (event) => {
|
|
267
288
|
if (typeof event === "object" && "error" in event) logError(event.error);
|
|
268
289
|
else if (typeof event === "object" && "message" in event) logError(event);
|
|
269
290
|
});
|
|
270
291
|
|
|
271
|
-
|
|
272
|
-
//#region src/localStorage.ts
|
|
292
|
+
// src/localStorage.ts
|
|
273
293
|
function getLocalKey(key) {
|
|
274
294
|
return [
|
|
275
295
|
af?.article?.id || "",
|
|
@@ -304,8 +324,7 @@ var LocalStorage = class {
|
|
|
304
324
|
};
|
|
305
325
|
var localStorage_default = new LocalStorage();
|
|
306
326
|
|
|
307
|
-
|
|
308
|
-
//#region src/date.ts
|
|
327
|
+
// src/date.ts
|
|
309
328
|
function toLocalISODate(date) {
|
|
310
329
|
return date ? toLocalISODateTime(date).substring(0, 10) : null;
|
|
311
330
|
}
|
|
@@ -328,8 +347,7 @@ function toLocalDateTime(date) {
|
|
|
328
347
|
return date ? date.toISOString().substring(0, 16) : "";
|
|
329
348
|
}
|
|
330
349
|
|
|
331
|
-
|
|
332
|
-
//#region src/downloadBlob.ts
|
|
350
|
+
// src/downloadBlob.ts
|
|
333
351
|
function downloadBlob(blob, fileName) {
|
|
334
352
|
return new Promise((resolve, reject) => {
|
|
335
353
|
try {
|
|
@@ -351,8 +369,7 @@ function downloadBlob(blob, fileName) {
|
|
|
351
369
|
});
|
|
352
370
|
}
|
|
353
371
|
|
|
354
|
-
|
|
355
|
-
//#region src/invariant.ts
|
|
372
|
+
// src/invariant.ts
|
|
356
373
|
/**
|
|
357
374
|
* Throws an error if the condition is falsy. The provided message is used as the error message.
|
|
358
375
|
* A function can be provided to lazily generate the message.
|
|
@@ -367,8 +384,7 @@ function invariant(condition, message) {
|
|
|
367
384
|
throw new Error(provided ?? "Invariant failed");
|
|
368
385
|
}
|
|
369
386
|
|
|
370
|
-
|
|
371
|
-
//#region src/downloadRequest.ts
|
|
387
|
+
// src/downloadRequest.ts
|
|
372
388
|
async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
373
389
|
let bytesReceived = 0;
|
|
374
390
|
onProgress?.({ status: "requested" });
|
|
@@ -422,8 +438,7 @@ async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
|
422
438
|
}
|
|
423
439
|
}
|
|
424
440
|
|
|
425
|
-
|
|
426
|
-
//#region src/parseFunc.ts
|
|
441
|
+
// src/parseFunc.ts
|
|
427
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)*)\}$/;
|
|
428
443
|
const arrowBlockRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*\{([\s\S]*)\}$/;
|
|
429
444
|
const arrowExpressionRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*([\s\S]+)$/;
|
|
@@ -457,13 +472,11 @@ function parseFunc(func) {
|
|
|
457
472
|
};
|
|
458
473
|
}
|
|
459
474
|
|
|
460
|
-
|
|
461
|
-
//#region src/Namespace.ts
|
|
475
|
+
// src/Namespace.ts
|
|
462
476
|
function Namespace() {}
|
|
463
477
|
Namespace.prototype = Object.create(null);
|
|
464
478
|
|
|
465
|
-
|
|
466
|
-
//#region src/requireNamespace.ts
|
|
479
|
+
// src/requireNamespace.ts
|
|
467
480
|
function requireNamespace(root, namespace) {
|
|
468
481
|
if (typeof root === "object") {
|
|
469
482
|
if (typeof namespace === "string") return requireNamespace(root, namespace.split("."));
|
|
@@ -475,8 +488,7 @@ function requireNamespace(root, namespace) {
|
|
|
475
488
|
}
|
|
476
489
|
}
|
|
477
490
|
|
|
478
|
-
|
|
479
|
-
//#region src/expose.ts
|
|
491
|
+
// src/expose.ts
|
|
480
492
|
/**
|
|
481
493
|
* Expose a value to a potentially deeply nested global variable
|
|
482
494
|
* @param name {string | object} Either a dot-separated path to the global variable, or an object to expose the value on
|
|
@@ -506,8 +518,7 @@ function expose(name, value, options = {}) {
|
|
|
506
518
|
}
|
|
507
519
|
}
|
|
508
520
|
|
|
509
|
-
|
|
510
|
-
//#region src/getLocalizedString.ts
|
|
521
|
+
// src/getLocalizedString.ts
|
|
511
522
|
async function fetchLocalizedString(text, project) {
|
|
512
523
|
try {
|
|
513
524
|
let response = await fetch(`/api/user/localize/new/${encodeURIComponent(project)}`, {
|
|
@@ -596,8 +607,7 @@ function createLocalizer(project) {
|
|
|
596
607
|
};
|
|
597
608
|
}
|
|
598
609
|
|
|
599
|
-
|
|
600
|
-
//#region src/filter/operators.ts
|
|
610
|
+
// src/filter/operators.ts
|
|
601
611
|
/**
|
|
602
612
|
* Operators that take no value at all.
|
|
603
613
|
*
|
|
@@ -611,6 +621,26 @@ const PARAMETERLESS_OPERATORS = [
|
|
|
611
621
|
"isblank",
|
|
612
622
|
"isnotblank"
|
|
613
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
|
+
];
|
|
614
644
|
/** Operators whose value is a two element `[from, to]` array. */
|
|
615
645
|
const PAIR_OPERATORS = [
|
|
616
646
|
"between",
|
|
@@ -621,7 +651,7 @@ const PAIR_OPERATORS = [
|
|
|
621
651
|
/** Operators whose value is an array of values. */
|
|
622
652
|
const LIST_OPERATORS = ["inlist", "notinlist"];
|
|
623
653
|
function getOperatorArity(operator) {
|
|
624
|
-
if (PARAMETERLESS_OPERATORS.includes(operator)) return "none";
|
|
654
|
+
if (PARAMETERLESS_OPERATORS.includes(operator) || PERIOD_OPERATORS.includes(operator)) return "none";
|
|
625
655
|
if (PAIR_OPERATORS.includes(operator)) return "pair";
|
|
626
656
|
if (LIST_OPERATORS.includes(operator)) return "list";
|
|
627
657
|
return "single";
|
|
@@ -664,7 +694,20 @@ const OPERATOR_LABELS = {
|
|
|
664
694
|
datelessthan: "Before",
|
|
665
695
|
datelessthanorequal: "Up to",
|
|
666
696
|
datebetween: "Between",
|
|
667
|
-
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"
|
|
668
711
|
};
|
|
669
712
|
/** Comparison operator labels used for date and time fields. */
|
|
670
713
|
const DATE_OPERATOR_LABELS = {
|
|
@@ -709,13 +752,15 @@ const OPERATORS_BY_TYPE = {
|
|
|
709
752
|
...EQUALITY,
|
|
710
753
|
...LIST_CHECKS,
|
|
711
754
|
...RANGE,
|
|
712
|
-
...COMPARISONS
|
|
755
|
+
...COMPARISONS,
|
|
756
|
+
...PERIOD_OPERATORS
|
|
713
757
|
],
|
|
714
758
|
datetime: [
|
|
715
759
|
...EQUALITY,
|
|
716
760
|
...LIST_CHECKS,
|
|
717
761
|
...RANGE,
|
|
718
|
-
...COMPARISONS
|
|
762
|
+
...COMPARISONS,
|
|
763
|
+
...PERIOD_OPERATORS
|
|
719
764
|
],
|
|
720
765
|
number: [
|
|
721
766
|
...EQUALITY,
|
|
@@ -788,7 +833,10 @@ function getValueType(field, operator) {
|
|
|
788
833
|
const RELATIVE_DATE_VALUES = [
|
|
789
834
|
"Yesterday",
|
|
790
835
|
"Today",
|
|
836
|
+
"Now",
|
|
791
837
|
"Tomorrow",
|
|
838
|
+
"DayAfterTomorrow",
|
|
839
|
+
"TwoWeeksAway",
|
|
792
840
|
"ThisWeek",
|
|
793
841
|
"ThisMonth",
|
|
794
842
|
"ThisYear",
|
|
@@ -803,7 +851,10 @@ const DYNAMIC_VALUE_LABELS = {
|
|
|
803
851
|
CurrentUser: "Current user",
|
|
804
852
|
Yesterday: "Yesterday",
|
|
805
853
|
Today: "Today",
|
|
854
|
+
Now: "Now",
|
|
806
855
|
Tomorrow: "Tomorrow",
|
|
856
|
+
DayAfterTomorrow: "Day after tomorrow",
|
|
857
|
+
TwoWeeksAway: "Two weeks away",
|
|
807
858
|
ThisWeek: "This week",
|
|
808
859
|
ThisMonth: "This month",
|
|
809
860
|
ThisYear: "This year",
|
|
@@ -839,6 +890,30 @@ function getDynamicValuesForField(field) {
|
|
|
839
890
|
if (field.type === "string") return ["CurrentUser"];
|
|
840
891
|
return [];
|
|
841
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
|
+
}
|
|
842
917
|
/**
|
|
843
918
|
* Aliases for names `eOperators.ToString()` can produce that are not operators
|
|
844
919
|
* in their own right. `Not` is the bare negation flag (i.e. "not equal to"),
|
|
@@ -862,8 +937,7 @@ function normalizeOperator(operator) {
|
|
|
862
937
|
return OPERATOR_ALIASES[normalized] ?? normalized;
|
|
863
938
|
}
|
|
864
939
|
|
|
865
|
-
|
|
866
|
-
//#region src/filter/tree.ts
|
|
940
|
+
// src/filter/tree.ts
|
|
867
941
|
/**
|
|
868
942
|
* Creates the default empty filter: a single `and` group with no conditions.
|
|
869
943
|
*/
|
|
@@ -950,10 +1024,40 @@ function isCompleteExpression(item) {
|
|
|
950
1024
|
let arity = getOperatorArity(normalizeOperator(item.operator));
|
|
951
1025
|
if (arity === "none") return true;
|
|
952
1026
|
if (item.value === null || item.value === void 0 || item.value === "") return false;
|
|
953
|
-
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
|
+
});
|
|
954
1031
|
if (arity === "list") return Array.isArray(item.value) && item.value.length > 0;
|
|
955
1032
|
return true;
|
|
956
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
|
+
}
|
|
957
1061
|
/**
|
|
958
1062
|
* Creates an expression for a field, using the field's default operator and an
|
|
959
1063
|
* empty value of the right shape.
|
|
@@ -984,8 +1088,9 @@ function changeOperator(expression, operator, field) {
|
|
|
984
1088
|
let previousArity = getOperatorArity(normalizeOperator(expression.operator));
|
|
985
1089
|
let arity = getOperatorArity(operator);
|
|
986
1090
|
let value = expression.value;
|
|
987
|
-
|
|
988
|
-
|
|
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);
|
|
989
1094
|
return {
|
|
990
1095
|
...expression,
|
|
991
1096
|
operator,
|
|
@@ -1051,8 +1156,7 @@ function addGroupAtPath(filter, path = []) {
|
|
|
1051
1156
|
return insertNodeAtPath(filter, path, createEmptyGroup(parent && isGroup(parent) && parent.mode === "and" ? "or" : "and"));
|
|
1052
1157
|
}
|
|
1053
1158
|
|
|
1054
|
-
|
|
1055
|
-
//#region src/filter/convertFilter.ts
|
|
1159
|
+
// src/filter/convertFilter.ts
|
|
1056
1160
|
const CONVERT_URL = "/api/filter/convert";
|
|
1057
1161
|
/**
|
|
1058
1162
|
* Converts between a structured filter and a DevExpress filter string.
|
|
@@ -1077,8 +1181,7 @@ async function convertFilter(request, signal) {
|
|
|
1077
1181
|
};
|
|
1078
1182
|
}
|
|
1079
1183
|
|
|
1080
|
-
|
|
1081
|
-
//#region src/filter/describeFilter.ts
|
|
1184
|
+
// src/filter/describeFilter.ts
|
|
1082
1185
|
/**
|
|
1083
1186
|
* Builds a human-readable description of a filter, without contacting the
|
|
1084
1187
|
* server. Use this for read-only summaries; the authoritative filter string is
|
|
@@ -1106,18 +1209,19 @@ function describeExpression(expression, fields) {
|
|
|
1106
1209
|
let label = getOperatorLabel(operator, field?.type);
|
|
1107
1210
|
let arity = getOperatorArity(operator);
|
|
1108
1211
|
if (arity === "none") return `[${caption}] ${label}`;
|
|
1109
|
-
return `[${caption}] ${label} ${formatValue(expression, arity)}`;
|
|
1212
|
+
return `[${caption}] ${label} ${formatValue(expression, arity, fields)}`;
|
|
1110
1213
|
}
|
|
1111
|
-
function formatValue(expression, arity) {
|
|
1214
|
+
function formatValue(expression, arity, fields) {
|
|
1112
1215
|
let { value, valueType } = expression;
|
|
1113
|
-
if (valueType === "special" && typeof value === "string") return getDynamicValueLabel(value);
|
|
1114
1216
|
if (Array.isArray(value)) {
|
|
1115
|
-
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));
|
|
1116
1218
|
return arity === "pair" ? formatted.join(" - ") : `(${formatted.join(", ")})`;
|
|
1117
1219
|
}
|
|
1118
|
-
return formatScalar(value, valueType);
|
|
1220
|
+
return formatScalar(value, valueType, fields);
|
|
1119
1221
|
}
|
|
1120
|
-
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}]`;
|
|
1121
1225
|
if (value === null || value === void 0) return "";
|
|
1122
1226
|
if (value instanceof Date) return value.toISOString().slice(0, 10);
|
|
1123
1227
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
@@ -1125,8 +1229,7 @@ function formatScalar(value, valueType) {
|
|
|
1125
1229
|
return `'${String(value)}'`;
|
|
1126
1230
|
}
|
|
1127
1231
|
|
|
1128
|
-
|
|
1129
|
-
//#region src/filter/fields.ts
|
|
1232
|
+
// src/filter/fields.ts
|
|
1130
1233
|
const FIELD_TYPES = [
|
|
1131
1234
|
"string",
|
|
1132
1235
|
"number",
|
|
@@ -1161,8 +1264,7 @@ function toFieldMetadataList(records) {
|
|
|
1161
1264
|
return records.map(toFieldMetadata).filter((field) => field.name !== "" && !field.excludeFromFilter).sort((a, b) => a.name.localeCompare(b.name));
|
|
1162
1265
|
}
|
|
1163
1266
|
|
|
1164
|
-
|
|
1165
|
-
//#region src/filter/standardLayout.ts
|
|
1267
|
+
// src/filter/standardLayout.ts
|
|
1166
1268
|
/**
|
|
1167
1269
|
* The "standard layout" is the simplified shape the Windows filter editor can
|
|
1168
1270
|
* render in its flow view: an outer `or` group whose items are `and` groups of
|
|
@@ -1244,8 +1346,7 @@ function collectExpressions(item, into) {
|
|
|
1244
1346
|
for (let child of item.items) collectExpressions(child, into);
|
|
1245
1347
|
}
|
|
1246
1348
|
|
|
1247
|
-
|
|
1248
|
-
//#region src/filter/savedFilters.ts
|
|
1349
|
+
// src/filter/savedFilters.ts
|
|
1249
1350
|
/**
|
|
1250
1351
|
* `sviw_Database_MyFilters` unions in a synthetic "No Filter" row so the Windows
|
|
1251
1352
|
* tree can render a "no filter" entry. It is not a real filter and must be
|
|
@@ -1344,8 +1445,7 @@ const canDeleteSavedFilter = canRenameSavedFilter;
|
|
|
1344
1445
|
*/
|
|
1345
1446
|
const canSetCrossDomain = canRenameSavedFilter;
|
|
1346
1447
|
|
|
1347
|
-
|
|
1348
|
-
//#region src/getUserImage.ts
|
|
1448
|
+
// src/getUserImage.ts
|
|
1349
1449
|
const sizeMap = {
|
|
1350
1450
|
xs: 64,
|
|
1351
1451
|
sm: 128,
|
|
@@ -1360,8 +1460,7 @@ function getUserImage(primKeyOrOptions, login, hash, size = "xs") {
|
|
|
1360
1460
|
return `/api/data/blob/EmployeesLookup/${primKeyOrOptions}/Image/${encodeURIComponent(login ?? "")}${login ? "." : ""}${encodeURIComponent(hash)}.jpg${sizePx ? `?width=${sizePx}` : ""}`;
|
|
1361
1461
|
}
|
|
1362
1462
|
|
|
1363
|
-
|
|
1364
|
-
//#region src/localize.ts
|
|
1463
|
+
// src/localize.ts
|
|
1365
1464
|
/**
|
|
1366
1465
|
* Localization function for tagged templates. Replaces the arguments
|
|
1367
1466
|
* with $n, where n is the index, before asking Appframe to translate
|
|
@@ -1385,8 +1484,7 @@ function localize(stringParts, ...args) {
|
|
|
1385
1484
|
return localized;
|
|
1386
1485
|
}
|
|
1387
1486
|
|
|
1388
|
-
|
|
1389
|
-
//#region src/report/recordSource.ts
|
|
1487
|
+
// src/report/recordSource.ts
|
|
1390
1488
|
const XML_ENTITIES = {
|
|
1391
1489
|
amp: "&",
|
|
1392
1490
|
lt: "<",
|
|
@@ -1472,8 +1570,7 @@ function parseRecordSource(xml) {
|
|
|
1472
1570
|
};
|
|
1473
1571
|
}
|
|
1474
1572
|
|
|
1475
|
-
|
|
1476
|
-
//#region src/report/parameters.ts
|
|
1573
|
+
// src/report/parameters.ts
|
|
1477
1574
|
/**
|
|
1478
1575
|
* Report parameters: mapping the metadata records onto something renderable,
|
|
1479
1576
|
* and the XML the parameter values are stored as on a saved filter.
|
|
@@ -1626,8 +1723,7 @@ function restoreParameterValues(parameters, xml) {
|
|
|
1626
1723
|
return values;
|
|
1627
1724
|
}
|
|
1628
1725
|
|
|
1629
|
-
|
|
1630
|
-
//#region src/report/parameterDefaults.ts
|
|
1726
|
+
// src/report/parameterDefaults.ts
|
|
1631
1727
|
/**
|
|
1632
1728
|
* Resolving report parameter defaults that name a stored procedure.
|
|
1633
1729
|
*
|
|
@@ -1689,8 +1785,7 @@ function firstValue(result) {
|
|
|
1689
1785
|
return row;
|
|
1690
1786
|
}
|
|
1691
1787
|
|
|
1692
|
-
|
|
1693
|
-
//#region src/report/render.ts
|
|
1788
|
+
// src/report/render.ts
|
|
1694
1789
|
/**
|
|
1695
1790
|
* Rendering a report through the framework's report route.
|
|
1696
1791
|
*
|
|
@@ -1894,8 +1989,7 @@ function toParameterEntries(parameters) {
|
|
|
1894
1989
|
}));
|
|
1895
1990
|
}
|
|
1896
1991
|
|
|
1897
|
-
|
|
1898
|
-
//#region src/report/report.ts
|
|
1992
|
+
// src/report/report.ts
|
|
1899
1993
|
/**
|
|
1900
1994
|
* Mapping a `sviw_WinClient_MyReports` record onto the report model, including
|
|
1901
1995
|
* which output formats the report offers.
|
|
@@ -1939,8 +2033,7 @@ function toReport(record) {
|
|
|
1939
2033
|
};
|
|
1940
2034
|
}
|
|
1941
2035
|
|
|
1942
|
-
|
|
1943
|
-
//#region src/alert.ts
|
|
2036
|
+
// src/alert.ts
|
|
1944
2037
|
const template = `
|
|
1945
2038
|
<dialog class="alert-dialog">
|
|
1946
2039
|
<form method="dialog">
|
|
@@ -2051,8 +2144,7 @@ async function alert(prompt, options = {}) {
|
|
|
2051
2144
|
});
|
|
2052
2145
|
}
|
|
2053
2146
|
|
|
2054
|
-
|
|
2055
|
-
//#region src/settings.ts
|
|
2147
|
+
// src/settings.ts
|
|
2056
2148
|
function getKey(article) {
|
|
2057
2149
|
return `${article ?? window.af?.article?.id ?? window.window?.location.pathname.substring(1).split("/")[0]}.${window.af?.userSession.login ?? "anonymous"}`;
|
|
2058
2150
|
}
|
|
@@ -2072,9 +2164,7 @@ function saveSettings(setting, value, article) {
|
|
|
2072
2164
|
localStorage.setItem(key, JSON.stringify(userSettings));
|
|
2073
2165
|
}
|
|
2074
2166
|
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
const version = "2.15.1";
|
|
2167
|
+
// src/index.ts
|
|
2168
|
+
const version = "2.15.2";
|
|
2078
2169
|
|
|
2079
|
-
|
|
2080
|
-
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 };
|
|
2170
|
+
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 };
|