@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
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.150.0/helpers/esm/typeof.js
|
|
24
|
+
// \0@oxc-project+runtime@0.155.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,8 +31,7 @@ function _typeof(o) {
|
|
|
32
31
|
}, _typeof(o);
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
//#region \0@oxc-project+runtime@0.150.0/helpers/esm/toPrimitive.js
|
|
34
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPrimitive.js
|
|
37
35
|
function toPrimitive(t, r) {
|
|
38
36
|
if ("object" != _typeof(t) || !t) return t;
|
|
39
37
|
var e = t[Symbol.toPrimitive];
|
|
@@ -45,15 +43,13 @@ function toPrimitive(t, r) {
|
|
|
45
43
|
return ("string" === r ? String : Number)(t);
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
//#region \0@oxc-project+runtime@0.150.0/helpers/esm/toPropertyKey.js
|
|
46
|
+
// \0@oxc-project+runtime@0.155.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.150.0/helpers/esm/defineProperty.js
|
|
52
|
+
// \0@oxc-project+runtime@0.155.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
|
|
@@ -263,6 +256,30 @@ async function logError(error) {
|
|
|
263
256
|
function getSessionId() {
|
|
264
257
|
return crypto.randomUUID?.();
|
|
265
258
|
}
|
|
259
|
+
/** The version in the first URL served under `/lib/@olenbetong/<name>/<version>/`. */
|
|
260
|
+
function libVersion(name, urls) {
|
|
261
|
+
let pattern = new RegExp(`/lib/@olenbetong/${name}/([^/]+)/`);
|
|
262
|
+
for (let url of urls) {
|
|
263
|
+
let match = typeof url === "string" ? pattern.exec(url) : null;
|
|
264
|
+
if (match) return match[1];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The synergi-chrome and synergi-platform versions the page loads on the 2026 site templates,
|
|
269
|
+
* read from the chrome's script tag and the platform import map rather than from the chrome
|
|
270
|
+
* itself, so they are known even when the chrome failed to start. Empty on other pages.
|
|
271
|
+
*/
|
|
272
|
+
function getPlatformVersions() {
|
|
273
|
+
let versions = {};
|
|
274
|
+
try {
|
|
275
|
+
let importMap = document.querySelector("script[type=\"importmap\"]");
|
|
276
|
+
let platform = importMap ? libVersion("synergi-platform", Object.values(JSON.parse(importMap.textContent ?? "{}").imports ?? {})) : void 0;
|
|
277
|
+
if (platform) versions["synergi-platform"] = platform;
|
|
278
|
+
} catch {}
|
|
279
|
+
let chrome = libVersion("synergi-chrome", Array.from(document.scripts, (script) => script.src)) ?? window.ob?.chrome?.version;
|
|
280
|
+
if (chrome) versions["synergi-chrome"] = chrome;
|
|
281
|
+
return versions;
|
|
282
|
+
}
|
|
266
283
|
let sessionStart = Date.now();
|
|
267
284
|
let sessionId = getSessionId();
|
|
268
285
|
let deviceId = "";
|
|
@@ -271,7 +288,8 @@ if ("document" in window) {
|
|
|
271
288
|
let startSession = () => logActivity("AfSessionInit", {
|
|
272
289
|
"appframe-core": window.af?.common?.version,
|
|
273
290
|
"appframe-data": window.af?.data?.version,
|
|
274
|
-
"app-version": window.af?.article?.version
|
|
291
|
+
"app-version": window.af?.article?.version,
|
|
292
|
+
...getPlatformVersions()
|
|
275
293
|
});
|
|
276
294
|
document.addEventListener("DOMContentLoaded", () => {
|
|
277
295
|
deviceId = localStorage.getItem("_afDeviceId");
|
|
@@ -305,15 +323,13 @@ function getSessionData() {
|
|
|
305
323
|
};
|
|
306
324
|
}
|
|
307
325
|
|
|
308
|
-
|
|
309
|
-
//#region src/errorHandler.ts
|
|
326
|
+
// src/errorHandler.ts
|
|
310
327
|
window.window?.addEventListener("error", (event) => {
|
|
311
328
|
if (typeof event === "object" && "error" in event) logError(event.error);
|
|
312
329
|
else if (typeof event === "object" && "message" in event) logError(event);
|
|
313
330
|
});
|
|
314
331
|
|
|
315
|
-
|
|
316
|
-
//#region src/localStorage.ts
|
|
332
|
+
// src/localStorage.ts
|
|
317
333
|
function getLocalKey(key) {
|
|
318
334
|
return [
|
|
319
335
|
af?.article?.id || "",
|
|
@@ -350,8 +366,7 @@ var LocalStorage = class {
|
|
|
350
366
|
};
|
|
351
367
|
var localStorage_default = new LocalStorage();
|
|
352
368
|
|
|
353
|
-
|
|
354
|
-
//#region src/date.ts
|
|
369
|
+
// src/date.ts
|
|
355
370
|
function toLocalISODate(date) {
|
|
356
371
|
return date ? toLocalISODateTime(date).substring(0, 10) : null;
|
|
357
372
|
}
|
|
@@ -374,8 +389,7 @@ function toLocalDateTime(date) {
|
|
|
374
389
|
return date ? date.toISOString().substring(0, 16) : "";
|
|
375
390
|
}
|
|
376
391
|
|
|
377
|
-
|
|
378
|
-
//#region src/downloadBlob.ts
|
|
392
|
+
// src/downloadBlob.ts
|
|
379
393
|
function downloadBlob(blob, fileName) {
|
|
380
394
|
return new Promise((resolve, reject) => {
|
|
381
395
|
try {
|
|
@@ -397,8 +411,7 @@ function downloadBlob(blob, fileName) {
|
|
|
397
411
|
});
|
|
398
412
|
}
|
|
399
413
|
|
|
400
|
-
|
|
401
|
-
//#region src/invariant.ts
|
|
414
|
+
// src/invariant.ts
|
|
402
415
|
/**
|
|
403
416
|
* Throws an error if the condition is falsy. The provided message is used as the error message.
|
|
404
417
|
* A function can be provided to lazily generate the message.
|
|
@@ -413,8 +426,7 @@ function invariant(condition, message) {
|
|
|
413
426
|
throw new Error(provided ?? "Invariant failed");
|
|
414
427
|
}
|
|
415
428
|
|
|
416
|
-
|
|
417
|
-
//#region src/downloadRequest.ts
|
|
429
|
+
// src/downloadRequest.ts
|
|
418
430
|
async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
419
431
|
let bytesReceived = 0;
|
|
420
432
|
onProgress?.({ status: "requested" });
|
|
@@ -468,8 +480,7 @@ async function downloadRequest(request, { fileName, signal, onProgress }) {
|
|
|
468
480
|
}
|
|
469
481
|
}
|
|
470
482
|
|
|
471
|
-
|
|
472
|
-
//#region src/parseFunc.ts
|
|
483
|
+
// src/parseFunc.ts
|
|
473
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)*)\}$/;
|
|
474
485
|
const arrowBlockRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*\{([\s\S]*)\}$/;
|
|
475
486
|
const arrowExpressionRegex = /^(?:\(([^)]*)\)|([a-zA-Z$_][a-zA-Z0-9$_]*))\s*=>\s*([\s\S]+)$/;
|
|
@@ -503,13 +514,11 @@ function parseFunc(func) {
|
|
|
503
514
|
};
|
|
504
515
|
}
|
|
505
516
|
|
|
506
|
-
|
|
507
|
-
//#region src/Namespace.ts
|
|
517
|
+
// src/Namespace.ts
|
|
508
518
|
function Namespace() {}
|
|
509
519
|
Namespace.prototype = Object.create(null);
|
|
510
520
|
|
|
511
|
-
|
|
512
|
-
//#region src/requireNamespace.ts
|
|
521
|
+
// src/requireNamespace.ts
|
|
513
522
|
function requireNamespace(root, namespace) {
|
|
514
523
|
if (typeof root === "object") {
|
|
515
524
|
if (typeof namespace === "string") return requireNamespace(root, namespace.split("."));
|
|
@@ -521,8 +530,7 @@ function requireNamespace(root, namespace) {
|
|
|
521
530
|
}
|
|
522
531
|
}
|
|
523
532
|
|
|
524
|
-
|
|
525
|
-
//#region src/expose.ts
|
|
533
|
+
// src/expose.ts
|
|
526
534
|
/**
|
|
527
535
|
* Expose a value to a potentially deeply nested global variable
|
|
528
536
|
* @param name {string | object} Either a dot-separated path to the global variable, or an object to expose the value on
|
|
@@ -552,8 +560,7 @@ function expose(name, value, options = {}) {
|
|
|
552
560
|
}
|
|
553
561
|
}
|
|
554
562
|
|
|
555
|
-
|
|
556
|
-
//#region src/getLocalizedString.ts
|
|
563
|
+
// src/getLocalizedString.ts
|
|
557
564
|
async function fetchLocalizedString(text, project) {
|
|
558
565
|
try {
|
|
559
566
|
let response = await fetch(`/api/user/localize/new/${encodeURIComponent(project)}`, {
|
|
@@ -642,8 +649,7 @@ function createLocalizer(project) {
|
|
|
642
649
|
};
|
|
643
650
|
}
|
|
644
651
|
|
|
645
|
-
|
|
646
|
-
//#region src/filter/operators.ts
|
|
652
|
+
// src/filter/operators.ts
|
|
647
653
|
/**
|
|
648
654
|
* Operators that take no value at all.
|
|
649
655
|
*
|
|
@@ -657,6 +663,26 @@ const PARAMETERLESS_OPERATORS = [
|
|
|
657
663
|
"isblank",
|
|
658
664
|
"isnotblank"
|
|
659
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
|
+
];
|
|
660
686
|
/** Operators whose value is a two element `[from, to]` array. */
|
|
661
687
|
const PAIR_OPERATORS = [
|
|
662
688
|
"between",
|
|
@@ -667,7 +693,7 @@ const PAIR_OPERATORS = [
|
|
|
667
693
|
/** Operators whose value is an array of values. */
|
|
668
694
|
const LIST_OPERATORS = ["inlist", "notinlist"];
|
|
669
695
|
function getOperatorArity(operator) {
|
|
670
|
-
if (PARAMETERLESS_OPERATORS.includes(operator)) return "none";
|
|
696
|
+
if (PARAMETERLESS_OPERATORS.includes(operator) || PERIOD_OPERATORS.includes(operator)) return "none";
|
|
671
697
|
if (PAIR_OPERATORS.includes(operator)) return "pair";
|
|
672
698
|
if (LIST_OPERATORS.includes(operator)) return "list";
|
|
673
699
|
return "single";
|
|
@@ -710,7 +736,20 @@ const OPERATOR_LABELS = {
|
|
|
710
736
|
datelessthan: "Before",
|
|
711
737
|
datelessthanorequal: "Up to",
|
|
712
738
|
datebetween: "Between",
|
|
713
|
-
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"
|
|
714
753
|
};
|
|
715
754
|
/** Comparison operator labels used for date and time fields. */
|
|
716
755
|
const DATE_OPERATOR_LABELS = {
|
|
@@ -755,13 +794,15 @@ const OPERATORS_BY_TYPE = {
|
|
|
755
794
|
...EQUALITY,
|
|
756
795
|
...LIST_CHECKS,
|
|
757
796
|
...RANGE,
|
|
758
|
-
...COMPARISONS
|
|
797
|
+
...COMPARISONS,
|
|
798
|
+
...PERIOD_OPERATORS
|
|
759
799
|
],
|
|
760
800
|
datetime: [
|
|
761
801
|
...EQUALITY,
|
|
762
802
|
...LIST_CHECKS,
|
|
763
803
|
...RANGE,
|
|
764
|
-
...COMPARISONS
|
|
804
|
+
...COMPARISONS,
|
|
805
|
+
...PERIOD_OPERATORS
|
|
765
806
|
],
|
|
766
807
|
number: [
|
|
767
808
|
...EQUALITY,
|
|
@@ -834,7 +875,10 @@ function getValueType(field, operator) {
|
|
|
834
875
|
const RELATIVE_DATE_VALUES = [
|
|
835
876
|
"Yesterday",
|
|
836
877
|
"Today",
|
|
878
|
+
"Now",
|
|
837
879
|
"Tomorrow",
|
|
880
|
+
"DayAfterTomorrow",
|
|
881
|
+
"TwoWeeksAway",
|
|
838
882
|
"ThisWeek",
|
|
839
883
|
"ThisMonth",
|
|
840
884
|
"ThisYear",
|
|
@@ -849,7 +893,10 @@ const DYNAMIC_VALUE_LABELS = {
|
|
|
849
893
|
CurrentUser: "Current user",
|
|
850
894
|
Yesterday: "Yesterday",
|
|
851
895
|
Today: "Today",
|
|
896
|
+
Now: "Now",
|
|
852
897
|
Tomorrow: "Tomorrow",
|
|
898
|
+
DayAfterTomorrow: "Day after tomorrow",
|
|
899
|
+
TwoWeeksAway: "Two weeks away",
|
|
853
900
|
ThisWeek: "This week",
|
|
854
901
|
ThisMonth: "This month",
|
|
855
902
|
ThisYear: "This year",
|
|
@@ -885,6 +932,30 @@ function getDynamicValuesForField(field) {
|
|
|
885
932
|
if (field.type === "string") return ["CurrentUser"];
|
|
886
933
|
return [];
|
|
887
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
|
+
}
|
|
888
959
|
/**
|
|
889
960
|
* Aliases for names `eOperators.ToString()` can produce that are not operators
|
|
890
961
|
* in their own right. `Not` is the bare negation flag (i.e. "not equal to"),
|
|
@@ -908,8 +979,7 @@ function normalizeOperator(operator) {
|
|
|
908
979
|
return OPERATOR_ALIASES[normalized] ?? normalized;
|
|
909
980
|
}
|
|
910
981
|
|
|
911
|
-
|
|
912
|
-
//#region src/filter/tree.ts
|
|
982
|
+
// src/filter/tree.ts
|
|
913
983
|
/**
|
|
914
984
|
* Creates the default empty filter: a single `and` group with no conditions.
|
|
915
985
|
*/
|
|
@@ -996,10 +1066,40 @@ function isCompleteExpression(item) {
|
|
|
996
1066
|
let arity = getOperatorArity(normalizeOperator(item.operator));
|
|
997
1067
|
if (arity === "none") return true;
|
|
998
1068
|
if (item.value === null || item.value === void 0 || item.value === "") return false;
|
|
999
|
-
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
|
+
});
|
|
1000
1073
|
if (arity === "list") return Array.isArray(item.value) && item.value.length > 0;
|
|
1001
1074
|
return true;
|
|
1002
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
|
+
}
|
|
1003
1103
|
/**
|
|
1004
1104
|
* Creates an expression for a field, using the field's default operator and an
|
|
1005
1105
|
* empty value of the right shape.
|
|
@@ -1030,8 +1130,9 @@ function changeOperator(expression, operator, field) {
|
|
|
1030
1130
|
let previousArity = getOperatorArity(normalizeOperator(expression.operator));
|
|
1031
1131
|
let arity = getOperatorArity(operator);
|
|
1032
1132
|
let value = expression.value;
|
|
1033
|
-
|
|
1034
|
-
|
|
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);
|
|
1035
1136
|
return {
|
|
1036
1137
|
...expression,
|
|
1037
1138
|
operator,
|
|
@@ -1097,8 +1198,7 @@ function addGroupAtPath(filter, path = []) {
|
|
|
1097
1198
|
return insertNodeAtPath(filter, path, createEmptyGroup(parent && isGroup(parent) && parent.mode === "and" ? "or" : "and"));
|
|
1098
1199
|
}
|
|
1099
1200
|
|
|
1100
|
-
|
|
1101
|
-
//#region src/filter/convertFilter.ts
|
|
1201
|
+
// src/filter/convertFilter.ts
|
|
1102
1202
|
const CONVERT_URL = "/api/filter/convert";
|
|
1103
1203
|
/**
|
|
1104
1204
|
* Converts between a structured filter and a DevExpress filter string.
|
|
@@ -1123,8 +1223,7 @@ async function convertFilter(request, signal) {
|
|
|
1123
1223
|
};
|
|
1124
1224
|
}
|
|
1125
1225
|
|
|
1126
|
-
|
|
1127
|
-
//#region src/filter/describeFilter.ts
|
|
1226
|
+
// src/filter/describeFilter.ts
|
|
1128
1227
|
/**
|
|
1129
1228
|
* Builds a human-readable description of a filter, without contacting the
|
|
1130
1229
|
* server. Use this for read-only summaries; the authoritative filter string is
|
|
@@ -1152,18 +1251,19 @@ function describeExpression(expression, fields) {
|
|
|
1152
1251
|
let label = getOperatorLabel(operator, field?.type);
|
|
1153
1252
|
let arity = getOperatorArity(operator);
|
|
1154
1253
|
if (arity === "none") return `[${caption}] ${label}`;
|
|
1155
|
-
return `[${caption}] ${label} ${formatValue(expression, arity)}`;
|
|
1254
|
+
return `[${caption}] ${label} ${formatValue(expression, arity, fields)}`;
|
|
1156
1255
|
}
|
|
1157
|
-
function formatValue(expression, arity) {
|
|
1256
|
+
function formatValue(expression, arity, fields) {
|
|
1158
1257
|
let { value, valueType } = expression;
|
|
1159
|
-
if (valueType === "special" && typeof value === "string") return getDynamicValueLabel(value);
|
|
1160
1258
|
if (Array.isArray(value)) {
|
|
1161
|
-
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));
|
|
1162
1260
|
return arity === "pair" ? formatted.join(" - ") : `(${formatted.join(", ")})`;
|
|
1163
1261
|
}
|
|
1164
|
-
return formatScalar(value, valueType);
|
|
1262
|
+
return formatScalar(value, valueType, fields);
|
|
1165
1263
|
}
|
|
1166
|
-
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}]`;
|
|
1167
1267
|
if (value === null || value === void 0) return "";
|
|
1168
1268
|
if (value instanceof Date) return value.toISOString().slice(0, 10);
|
|
1169
1269
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
@@ -1171,8 +1271,7 @@ function formatScalar(value, valueType) {
|
|
|
1171
1271
|
return `'${String(value)}'`;
|
|
1172
1272
|
}
|
|
1173
1273
|
|
|
1174
|
-
|
|
1175
|
-
//#region src/filter/fields.ts
|
|
1274
|
+
// src/filter/fields.ts
|
|
1176
1275
|
const FIELD_TYPES = [
|
|
1177
1276
|
"string",
|
|
1178
1277
|
"number",
|
|
@@ -1207,8 +1306,7 @@ function toFieldMetadataList(records) {
|
|
|
1207
1306
|
return records.map(toFieldMetadata).filter((field) => field.name !== "" && !field.excludeFromFilter).sort((a, b) => a.name.localeCompare(b.name));
|
|
1208
1307
|
}
|
|
1209
1308
|
|
|
1210
|
-
|
|
1211
|
-
//#region src/filter/standardLayout.ts
|
|
1309
|
+
// src/filter/standardLayout.ts
|
|
1212
1310
|
/**
|
|
1213
1311
|
* The "standard layout" is the simplified shape the Windows filter editor can
|
|
1214
1312
|
* render in its flow view: an outer `or` group whose items are `and` groups of
|
|
@@ -1290,8 +1388,7 @@ function collectExpressions(item, into) {
|
|
|
1290
1388
|
for (let child of item.items) collectExpressions(child, into);
|
|
1291
1389
|
}
|
|
1292
1390
|
|
|
1293
|
-
|
|
1294
|
-
//#region src/filter/savedFilters.ts
|
|
1391
|
+
// src/filter/savedFilters.ts
|
|
1295
1392
|
/**
|
|
1296
1393
|
* `sviw_Database_MyFilters` unions in a synthetic "No Filter" row so the Windows
|
|
1297
1394
|
* tree can render a "no filter" entry. It is not a real filter and must be
|
|
@@ -1390,8 +1487,7 @@ const canDeleteSavedFilter = canRenameSavedFilter;
|
|
|
1390
1487
|
*/
|
|
1391
1488
|
const canSetCrossDomain = canRenameSavedFilter;
|
|
1392
1489
|
|
|
1393
|
-
|
|
1394
|
-
//#region src/getUserImage.ts
|
|
1490
|
+
// src/getUserImage.ts
|
|
1395
1491
|
const sizeMap = {
|
|
1396
1492
|
xs: 64,
|
|
1397
1493
|
sm: 128,
|
|
@@ -1406,8 +1502,7 @@ function getUserImage(primKeyOrOptions, login, hash, size = "xs") {
|
|
|
1406
1502
|
return `/api/data/blob/EmployeesLookup/${primKeyOrOptions}/Image/${encodeURIComponent(login ?? "")}${login ? "." : ""}${encodeURIComponent(hash)}.jpg${sizePx ? `?width=${sizePx}` : ""}`;
|
|
1407
1503
|
}
|
|
1408
1504
|
|
|
1409
|
-
|
|
1410
|
-
//#region src/localize.ts
|
|
1505
|
+
// src/localize.ts
|
|
1411
1506
|
/**
|
|
1412
1507
|
* Localization function for tagged templates. Replaces the arguments
|
|
1413
1508
|
* with $n, where n is the index, before asking Appframe to translate
|
|
@@ -1431,8 +1526,7 @@ function localize(stringParts, ...args) {
|
|
|
1431
1526
|
return localized;
|
|
1432
1527
|
}
|
|
1433
1528
|
|
|
1434
|
-
|
|
1435
|
-
//#region src/report/recordSource.ts
|
|
1529
|
+
// src/report/recordSource.ts
|
|
1436
1530
|
const XML_ENTITIES = {
|
|
1437
1531
|
amp: "&",
|
|
1438
1532
|
lt: "<",
|
|
@@ -1518,8 +1612,7 @@ function parseRecordSource(xml) {
|
|
|
1518
1612
|
};
|
|
1519
1613
|
}
|
|
1520
1614
|
|
|
1521
|
-
|
|
1522
|
-
//#region src/report/parameters.ts
|
|
1615
|
+
// src/report/parameters.ts
|
|
1523
1616
|
/**
|
|
1524
1617
|
* Report parameters: mapping the metadata records onto something renderable,
|
|
1525
1618
|
* and the XML the parameter values are stored as on a saved filter.
|
|
@@ -1672,8 +1765,7 @@ function restoreParameterValues(parameters, xml) {
|
|
|
1672
1765
|
return values;
|
|
1673
1766
|
}
|
|
1674
1767
|
|
|
1675
|
-
|
|
1676
|
-
//#region src/report/parameterDefaults.ts
|
|
1768
|
+
// src/report/parameterDefaults.ts
|
|
1677
1769
|
/**
|
|
1678
1770
|
* Resolving report parameter defaults that name a stored procedure.
|
|
1679
1771
|
*
|
|
@@ -1735,8 +1827,7 @@ function firstValue(result) {
|
|
|
1735
1827
|
return row;
|
|
1736
1828
|
}
|
|
1737
1829
|
|
|
1738
|
-
|
|
1739
|
-
//#region src/report/render.ts
|
|
1830
|
+
// src/report/render.ts
|
|
1740
1831
|
/**
|
|
1741
1832
|
* Rendering a report through the framework's report route.
|
|
1742
1833
|
*
|
|
@@ -1940,8 +2031,7 @@ function toParameterEntries(parameters) {
|
|
|
1940
2031
|
}));
|
|
1941
2032
|
}
|
|
1942
2033
|
|
|
1943
|
-
|
|
1944
|
-
//#region src/report/report.ts
|
|
2034
|
+
// src/report/report.ts
|
|
1945
2035
|
/**
|
|
1946
2036
|
* Mapping a `sviw_WinClient_MyReports` record onto the report model, including
|
|
1947
2037
|
* which output formats the report offers.
|
|
@@ -1985,8 +2075,7 @@ function toReport(record) {
|
|
|
1985
2075
|
};
|
|
1986
2076
|
}
|
|
1987
2077
|
|
|
1988
|
-
|
|
1989
|
-
//#region src/alert.ts
|
|
2078
|
+
// src/alert.ts
|
|
1990
2079
|
const template = `
|
|
1991
2080
|
<dialog class="alert-dialog">
|
|
1992
2081
|
<form method="dialog">
|
|
@@ -2097,8 +2186,7 @@ async function alert(prompt, options = {}) {
|
|
|
2097
2186
|
});
|
|
2098
2187
|
}
|
|
2099
2188
|
|
|
2100
|
-
|
|
2101
|
-
//#region src/settings.ts
|
|
2189
|
+
// src/settings.ts
|
|
2102
2190
|
function getKey(article) {
|
|
2103
2191
|
return `${article ?? window.af?.article?.id ?? window.window?.location.pathname.substring(1).split("/")[0]}.${window.af?.userSession.login ?? "anonymous"}`;
|
|
2104
2192
|
}
|
|
@@ -2118,9 +2206,7 @@ function saveSettings(setting, value, article) {
|
|
|
2118
2206
|
localStorage.setItem(key, JSON.stringify(userSettings));
|
|
2119
2207
|
}
|
|
2120
2208
|
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
const version = "2.15.1";
|
|
2209
|
+
// src/index.ts
|
|
2210
|
+
const version = "2.15.2";
|
|
2124
2211
|
|
|
2125
|
-
|
|
2126
|
-
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 };
|
|
2212
|
+
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 };
|