@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.
@@ -1,4 +1,4 @@
1
- //#region ../../node_modules/.pnpm/mitt@3.0.1/node_modules/mitt/dist/mitt.mjs
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
507
- //#region src/Namespace.ts
517
+ // src/Namespace.ts
508
518
  function Namespace() {}
509
519
  Namespace.prototype = Object.create(null);
510
520
 
511
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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) => v !== "" && v != null);
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
- if (arity !== previousArity) value = createEmptyValue(operator);
1034
- let valueType = expression.valueType === "special" && arity === previousArity ? "special" : getValueType(field, operator);
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
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
- //#endregion
2122
- //#region src/index.ts
2123
- const version = "2.15.1";
2209
+ // src/index.ts
2210
+ const version = "2.15.2";
2124
2211
 
2125
- //#endregion
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 };