@schukai/monster 3.92.3 → 3.93.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.
@@ -12,27 +12,28 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import { getLocaleOfDocument } from "../dom/locale.mjs";
16
- import { Base } from "../types/base.mjs";
17
- import { getGlobal, getGlobalObject } from "../types/global.mjs";
18
- import { ID } from "../types/id.mjs";
19
- import { isArray, isObject, isString, isPrimitive } from "../types/is.mjs";
15
+ import {getLocaleOfDocument} from "../dom/locale.mjs";
16
+ import {Base} from "../types/base.mjs";
17
+ import {getGlobal, getGlobalObject} from "../types/global.mjs";
18
+ import {ID} from "../types/id.mjs";
19
+ import {isArray, isObject, isString, isPrimitive} from "../types/is.mjs";
20
20
  import {
21
- getDocumentTranslations,
22
- Translations,
21
+ getDocumentTranslations,
22
+ Translations,
23
23
  } from "../i18n/translations.mjs";
24
24
  import {
25
- validateFunction,
26
- validateInteger,
27
- validateObject,
28
- validatePrimitive,
29
- validateString,
30
- validateBoolean,
25
+ validateFunction,
26
+ validateInteger,
27
+ validateObject,
28
+ validatePrimitive,
29
+ validateString,
30
+ validateBoolean,
31
31
  } from "../types/validate.mjs";
32
- import { clone } from "../util/clone.mjs";
33
- import { Pathfinder } from "./pathfinder.mjs";
32
+ import {clone} from "../util/clone.mjs";
33
+ import {Pathfinder} from "./pathfinder.mjs";
34
+ import {formatTimeAgo} from "../i18n/time-ago.mjs";
34
35
 
35
- export { Transformer };
36
+ export {Transformer};
36
37
 
37
38
  /**
38
39
  * The transformer class is a swiss army knife for manipulating values.
@@ -52,53 +53,53 @@ export { Transformer };
52
53
  * @summary The transformer class is a swiss army knife for manipulating values. especially in combination with the pipe, processing chains can be built up.
53
54
  */
54
55
  class Transformer extends Base {
55
- /**
56
- *
57
- * @param {string} definition
58
- */
59
- constructor(definition) {
60
- super();
61
- this.args = disassemble(definition);
62
- this.command = this.args.shift();
63
- this.callbacks = new Map();
64
- }
65
-
66
- /**
67
- *
68
- * @param {string} name
69
- * @param {function} callback
70
- * @param {object} context
71
- * @return {Transformer}
72
- * @throws {TypeError} value is not a string
73
- * @throws {TypeError} value is not a function
74
- */
75
- setCallback(name, callback, context) {
76
- validateString(name);
77
- validateFunction(callback);
78
-
79
- if (context !== undefined) {
80
- validateObject(context);
81
- }
82
-
83
- this.callbacks.set(name, {
84
- callback: callback,
85
- context: context,
86
- });
87
-
88
- return this;
89
- }
90
-
91
- /**
92
- *
93
- * @param {*} value
94
- * @return {*}
95
- * @throws {Error} unknown command
96
- * @throws {TypeError} unsupported type
97
- * @throws {Error} type not supported
98
- */
99
- run(value) {
100
- return transform.apply(this, [value]);
101
- }
56
+ /**
57
+ *
58
+ * @param {string} definition
59
+ */
60
+ constructor(definition) {
61
+ super();
62
+ this.args = disassemble(definition);
63
+ this.command = this.args.shift();
64
+ this.callbacks = new Map();
65
+ }
66
+
67
+ /**
68
+ *
69
+ * @param {string} name
70
+ * @param {function} callback
71
+ * @param {object} context
72
+ * @return {Transformer}
73
+ * @throws {TypeError} value is not a string
74
+ * @throws {TypeError} value is not a function
75
+ */
76
+ setCallback(name, callback, context) {
77
+ validateString(name);
78
+ validateFunction(callback);
79
+
80
+ if (context !== undefined) {
81
+ validateObject(context);
82
+ }
83
+
84
+ this.callbacks.set(name, {
85
+ callback: callback,
86
+ context: context,
87
+ });
88
+
89
+ return this;
90
+ }
91
+
92
+ /**
93
+ *
94
+ * @param {*} value
95
+ * @return {*}
96
+ * @throws {Error} unknown command
97
+ * @throws {TypeError} unsupported type
98
+ * @throws {Error} type not supported
99
+ */
100
+ run(value) {
101
+ return transform.apply(this, [value]);
102
+ }
102
103
  }
103
104
 
104
105
  /**
@@ -108,41 +109,41 @@ class Transformer extends Base {
108
109
  * @private
109
110
  */
110
111
  function disassemble(command) {
111
- validateString(command);
112
-
113
- const placeholder = new Map();
114
- const regex = /((?<pattern>\\(?<char>.)){1})/gim;
115
-
116
- // The separator for args must be escaped
117
- // undefined string which should not occur normally and is also not a regex
118
- const result = command.matchAll(regex);
119
-
120
- for (const m of result) {
121
- const g = m?.["groups"];
122
- if (!isObject(g)) {
123
- continue;
124
- }
125
-
126
- const p = g?.["pattern"];
127
- const c = g?.["char"];
128
-
129
- if (p && c) {
130
- const r = `__${new ID().toString()}__`;
131
- placeholder.set(r, c);
132
- command = command.replace(p, r);
133
- }
134
- }
135
- let parts = command.split(":");
136
-
137
- parts = parts.map(function (value) {
138
- let v = value.trim();
139
- for (const k of placeholder) {
140
- v = v.replace(k[0], k[1]);
141
- }
142
- return v;
143
- });
144
-
145
- return parts;
112
+ validateString(command);
113
+
114
+ const placeholder = new Map();
115
+ const regex = /((?<pattern>\\(?<char>.)){1})/gim;
116
+
117
+ // The separator for args must be escaped
118
+ // undefined string which should not occur normally and is also not a regex
119
+ const result = command.matchAll(regex);
120
+
121
+ for (const m of result) {
122
+ const g = m?.["groups"];
123
+ if (!isObject(g)) {
124
+ continue;
125
+ }
126
+
127
+ const p = g?.["pattern"];
128
+ const c = g?.["char"];
129
+
130
+ if (p && c) {
131
+ const r = `__${new ID().toString()}__`;
132
+ placeholder.set(r, c);
133
+ command = command.replace(p, r);
134
+ }
135
+ }
136
+ let parts = command.split(":");
137
+
138
+ parts = parts.map(function (value) {
139
+ let v = value.trim();
140
+ for (const k of placeholder) {
141
+ v = v.replace(k[0], k[1]);
142
+ }
143
+ return v;
144
+ });
145
+
146
+ return parts;
146
147
  }
147
148
 
148
149
  /**
@@ -153,12 +154,12 @@ function disassemble(command) {
153
154
  * @private
154
155
  */
155
156
  function convertToString(value) {
156
- if (isObject(value) && value.hasOwnProperty("toString")) {
157
- value = value.toString();
158
- }
157
+ if (isObject(value) && value.hasOwnProperty("toString")) {
158
+ value = value.toString();
159
+ }
159
160
 
160
- validateString(value);
161
- return value;
161
+ validateString(value);
162
+ return value;
162
163
  }
163
164
 
164
165
  /**
@@ -172,667 +173,680 @@ function convertToString(value) {
172
173
  * @throws {Error} missing key parameter
173
174
  */
174
175
  function transform(value) {
175
- const console = getGlobalObject("console");
176
-
177
- const args = clone(this.args);
178
- let key;
179
- let defaultValue;
180
-
181
- let translations;
182
- let date;
183
- let locale;
184
- let timestamp;
185
- let map;
186
- let keyValue;
187
-
188
- switch (this.command) {
189
- case "static":
190
- return this.args.join(":");
191
-
192
- case "tolower":
193
- case "strtolower":
194
- case "tolowercase":
195
- validateString(value);
196
- return value.toLowerCase();
197
-
198
- case "contains":
199
- if (isString(value)) {
200
- return value.includes(args[0]);
201
- }
202
-
203
- if (isArray(value)) {
204
- return value.includes(args[0]);
205
- }
206
-
207
- if (isObject(value)) {
208
- return value.hasOwnProperty(args[0]);
209
- }
210
-
211
- return false;
212
-
213
- case "has-entries":
214
- case "hasentries":
215
- if (isObject(value)) {
216
- return Object.keys(value).length > 0;
217
- }
218
-
219
- if (isArray(value)) {
220
- return value.length > 0;
221
- }
222
-
223
- return false;
224
-
225
- case "isundefined":
226
- case "is-undefined":
227
- return value === undefined;
228
-
229
- case "isnull":
230
- case "is-null":
231
- return value === null;
232
-
233
- case "isset":
234
- case "is-set":
235
- return value !== undefined && value !== null;
236
-
237
- case "isnumber":
238
- case "is-number":
239
- return isPrimitive(value) && !isNaN(value);
240
-
241
- case "isinteger":
242
- case "is-integer":
243
- return isPrimitive(value) && !isNaN(value) && value % 1 === 0;
244
-
245
- case "isfloat":
246
- case "is-float":
247
- return isPrimitive(value) && !isNaN(value) && value % 1 !== 0;
248
-
249
- case "isobject":
250
- case "is-object":
251
- return isObject(value);
252
-
253
- case "isarray":
254
- case "is-array":
255
- return Array.isArray(value);
256
-
257
- case "not":
258
- validateBoolean(value);
259
- return !value;
260
-
261
- case "toupper":
262
- case "strtoupper":
263
- case "touppercase":
264
- validateString(value);
265
- return value.toUpperCase();
266
-
267
- case "tostring":
268
- return `${value}`;
269
-
270
- case "tointeger":
271
- const n = parseInt(value);
272
- validateInteger(n);
273
- return n;
274
-
275
- case "to-json":
276
- case "tojson":
277
- return JSON.stringify(value);
278
-
279
- case "from-json":
280
- case "fromjson":
281
- return JSON.parse(value);
282
-
283
- case "trim":
284
- validateString(value);
285
- return value.trim();
286
-
287
- case "rawurlencode":
288
- validateString(value);
289
- return encodeURIComponent(value)
290
- .replace(/!/g, "%21")
291
- .replace(/'/g, "%27")
292
- .replace(/\(/g, "%28")
293
- .replace(/\)/g, "%29")
294
- .replace(/\*/g, "%2A");
295
-
296
- case "call":
297
- /**
298
- * callback-definition
299
- * function callback(value, ...args) {
300
- * return value;
301
- * }
302
- */
303
-
304
- let callback;
305
- const callbackName = args.shift();
306
- let context = getGlobal();
307
-
308
- if (isObject(value) && value.hasOwnProperty(callbackName)) {
309
- callback = value[callbackName];
310
- } else if (this.callbacks.has(callbackName)) {
311
- const s = this.callbacks.get(callbackName);
312
- callback = s?.["callback"];
313
- context = s?.["context"];
314
- } else if (
315
- typeof window === "object" &&
316
- window.hasOwnProperty(callbackName)
317
- ) {
318
- callback = window[callbackName];
319
- }
320
- validateFunction(callback);
321
-
322
- args.unshift(value);
323
- return callback.call(context, ...args);
324
-
325
- case "plain":
326
- case "plaintext":
327
- validateString(value);
328
- const doc = new DOMParser().parseFromString(value, "text/html");
329
- return doc.body.textContent || "";
330
-
331
- case "if":
332
- case "?":
333
- validatePrimitive(value);
334
-
335
- let trueStatement = args.shift() || undefined;
336
- let falseStatement = args.shift() || undefined;
337
-
338
- trueStatement = convertSpecialStrings(trueStatement, value);
339
- falseStatement = convertSpecialStrings(falseStatement, value);
340
-
341
- const condition = evaluateCondition(value);
342
- return condition ? trueStatement : falseStatement;
343
-
344
- case "ucfirst":
345
- validateString(value);
346
-
347
- const firstchar = value.charAt(0).toUpperCase();
348
- return firstchar + value.substr(1);
349
- case "ucwords":
350
- validateString(value);
351
-
352
- return value.replace(
353
- /^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g,
354
- function (v) {
355
- return v.toUpperCase();
356
- },
357
- );
358
-
359
- case "count":
360
- case "length":
361
- if (
362
- (isString(value) || isObject(value) || isArray(value)) &&
363
- value.hasOwnProperty("length")
364
- ) {
365
- return value.length;
366
- }
367
-
368
- throw new TypeError(`unsupported type ${typeof value}`);
369
-
370
- case "to-base64":
371
- case "btoa":
372
- case "base64":
373
- return btoa(convertToString(value));
374
-
375
- case "atob":
376
- case "from-base64":
377
- return atob(convertToString(value));
378
-
379
- case "empty":
380
- return "";
381
-
382
- case "undefined":
383
- return undefined;
384
-
385
- case "debug":
386
- if (isObject(console)) {
387
- console.log(value);
388
- }
389
-
390
- return value;
391
-
392
- case "prefix":
393
- validateString(value);
394
- const prefix = args?.[0];
395
- return prefix + value;
396
-
397
- case "suffix":
398
- validateString(value);
399
- const suffix = args?.[0];
400
- return value + suffix;
401
-
402
- case "uniqid":
403
- return new ID().toString();
404
-
405
- case "first-key":
406
- case "last-key":
407
- case "nth-last-key":
408
- case "nth-key":
409
- if (!isObject(value)) {
410
- throw new Error("type not supported");
411
- }
412
-
413
- const keys = Object.keys(value).sort();
414
-
415
- if (this.command === "first-key") {
416
- key = 0;
417
- } else if (this.command === "last-key") {
418
- key = keys.length - 1;
419
- } else {
420
- key = validateInteger(parseInt(args.shift()));
421
-
422
- if (this.command === "nth-last-key") {
423
- key = keys.length - key - 1;
424
- }
425
- }
426
-
427
- defaultValue = args.shift() || "";
428
-
429
- const useKey = keys?.[key];
430
-
431
- if (value?.[useKey]) {
432
- return value?.[useKey];
433
- }
434
-
435
- return defaultValue;
436
-
437
- case "key":
438
- case "property":
439
- case "index":
440
- key = args.shift() || undefined;
441
-
442
- if (key === undefined) {
443
- throw new Error("missing key parameter");
444
- }
445
-
446
- defaultValue = args.shift() || undefined;
447
-
448
- if (value instanceof Map) {
449
- if (!value.has(key)) {
450
- return defaultValue;
451
- }
452
- return value.get(key);
453
- }
454
-
455
- if (isObject(value) || isArray(value)) {
456
- if (value?.[key]) {
457
- return value?.[key];
458
- }
459
-
460
- return defaultValue;
461
- }
462
-
463
- throw new Error("type not supported");
464
-
465
- case "path-exists":
466
- key = args.shift();
467
- if (key === undefined) {
468
- throw new Error("missing key parameter");
469
- }
470
-
471
- return new Pathfinder(value).exists(key);
472
-
473
- case "concat":
474
- const pf2 = new Pathfinder(value);
475
- let concat = "";
476
- while (args.length > 0) {
477
- key = args.shift();
478
- if (key === undefined) {
479
- throw new Error("missing key parameter");
480
- }
481
-
482
- // add empty strings
483
- if (isString(key) && key.trim() === "") {
484
- concat += key;
485
- continue;
486
- }
487
-
488
- if (!pf2.exists(key)) {
489
- concat += key;
490
- continue;
491
- }
492
- const v = pf2.getVia(key);
493
- if (!isPrimitive(v)) {
494
- throw new Error("value is not primitive");
495
- }
496
-
497
- concat += v;
498
- }
499
-
500
- return concat;
501
- case "path":
502
- key = args.shift();
503
- if (key === undefined) {
504
- throw new Error("missing key parameter");
505
- }
506
-
507
- const pf = new Pathfinder(value);
508
-
509
- if (!pf.exists(key)) {
510
- return undefined;
511
- }
512
-
513
- return pf.getVia(key);
514
-
515
- case "substring":
516
- validateString(value);
517
-
518
- const start = parseInt(args[0]) || 0;
519
- const end = (parseInt(args[1]) || 0) + start;
520
-
521
- return value.substring(start, end);
522
-
523
- case "nop":
524
- return value;
525
-
526
- case "??":
527
- case "default":
528
- if (value !== undefined && value !== null) {
529
- return value;
530
- }
531
-
532
- defaultValue = args.shift();
533
- let defaultType = args.shift();
534
- if (defaultType === undefined) {
535
- defaultType = "string";
536
- }
537
-
538
- switch (defaultType) {
539
- case "int":
540
- case "integer":
541
- return parseInt(defaultValue);
542
- case "float":
543
- return parseFloat(defaultValue);
544
- case "undefined":
545
- return undefined;
546
- case "bool":
547
- case "boolean":
548
- defaultValue = defaultValue.toLowerCase();
549
- return (
550
- (defaultValue !== "undefined" &&
551
- defaultValue !== "" &&
552
- defaultValue !== "off" &&
553
- defaultValue !== "false" &&
554
- defaultValue !== "false") ||
555
- defaultValue === "on" ||
556
- defaultValue === "true" ||
557
- defaultValue === "true"
558
- );
559
- case "string":
560
- return `${defaultValue}`;
561
- case "object":
562
- return JSON.parse(atob(defaultValue));
563
- }
564
-
565
- throw new Error("type not supported");
566
-
567
- case "map":
568
- map = new Map();
569
- while (args.length > 0) {
570
- keyValue = args.shift();
571
- if (keyValue === undefined) {
572
- throw new Error("missing key parameter");
573
- }
574
-
575
- keyValue = keyValue.split("=");
576
- map.set(keyValue[0], keyValue[1]);
577
- }
578
-
579
- return map.get(value);
580
-
581
- case "equals":
582
- if (args.length === 0) {
583
- throw new Error("missing value parameter");
584
- }
585
-
586
- validatePrimitive(value);
587
-
588
- const equalsValue = args.shift();
589
-
590
- /**
591
- * The history of “typeof null”
592
- * https://2ality.com/2013/10/typeof-null.html
593
- * In JavaScript, typeof null is 'object', which incorrectly suggests
594
- * that null is an object.
595
- */
596
- if (value === null) {
597
- return equalsValue === "null";
598
- }
599
-
600
- const typeOfValue = typeof value;
601
-
602
- switch (typeOfValue) {
603
- case "string":
604
- return value === equalsValue;
605
- case "number":
606
- return value === parseFloat(equalsValue);
607
- case "boolean":
608
- return value === (equalsValue === "true" || equalsValue === "on");
609
- case "undefined":
610
- return equalsValue === "undefined";
611
- default:
612
- throw new Error("type not supported");
613
- }
614
-
615
- case "money":
616
- case "currency":
617
- try {
618
- locale = getLocaleOfDocument();
619
- } catch (e) {
620
- throw new Error(`unsupported locale or missing format (${e.message})`);
621
- }
622
-
623
- // Verwenden von RegExp, um Währung und Betrag zu extrahieren
624
- const match = value.match(/^([A-Z]{3})[\s-]*(\d+(\.\d+)?)$/);
625
- if (!match) {
626
- throw new Error("invalid currency format");
627
- }
628
-
629
- const currency = match[1];
630
- const amount = match[2];
631
-
632
- const maximumFractionDigits = args?.[0] || 2;
633
- const roundingIncrement = args?.[1] || 5;
634
-
635
- const nf = new Intl.NumberFormat(locale.toString(), {
636
- style: "currency",
637
- currency: currency,
638
- maximumFractionDigits: maximumFractionDigits,
639
- roundingIncrement: roundingIncrement,
640
- });
641
-
642
- return nf.format(amount);
643
-
644
- case "timestamp":
645
- date = new Date(value);
646
- timestamp = date.getTime();
647
- if (isNaN(timestamp)) {
648
- throw new Error("invalid date");
649
- }
650
- return timestamp;
651
-
652
- case "time":
653
- date = new Date(value);
654
- if (isNaN(date.getTime())) {
655
- throw new Error("invalid date");
656
- }
657
-
658
- try {
659
- locale = getLocaleOfDocument();
660
- return date.toLocaleTimeString(locale.toString(), {
661
- hour12: false,
662
- });
663
- } catch (e) {
664
- throw new Error(`unsupported locale or missing format (${e.message})`);
665
- }
666
-
667
- case "datetimeformat":
668
- date = new Date(value);
669
- if (isNaN(date.getTime())) {
670
- throw new Error("invalid date");
671
- }
672
-
673
- const options = {
674
- dateStyle: "medium",
675
- timeStyle: "medium",
676
- hour12: false,
677
- };
678
-
679
- if (args.length > 0) {
680
- options.dateStyle = args.shift();
681
- }
682
-
683
- if (args.length > 0) {
684
- options.timeStyle = args.shift();
685
- }
686
-
687
- try {
688
- locale = getLocaleOfDocument().toString();
689
- return new Intl.DateTimeFormat(locale, options).format(date);
690
- } catch (e) {
691
- throw new Error(`unsupported locale or missing format (${e.message})`);
692
- }
693
-
694
- case "datetime":
695
- date = new Date(value);
696
- if (isNaN(date.getTime())) {
697
- throw new Error("invalid date");
698
- }
699
-
700
- try {
701
- locale = getLocaleOfDocument();
702
- return date.toLocaleString(locale.toString(), {
703
- hour12: false,
704
- });
705
- } catch (e) {
706
- throw new Error(`unsupported locale or missing format (${e.message})`);
707
- }
708
-
709
- case "date":
710
- date = new Date(value);
711
- if (isNaN(date.getTime())) {
712
- throw new Error("invalid date");
713
- }
714
-
715
- try {
716
- locale = getLocaleOfDocument();
717
- return date.toLocaleDateString(locale.toString(), {
718
- year: "numeric",
719
- month: "2-digit",
720
- day: "2-digit",
721
- });
722
- } catch (e) {
723
- throw new Error(`unsupported locale or missing format (${e.message})`);
724
- }
725
-
726
- case "year":
727
- date = new Date(value);
728
- if (isNaN(date.getTime())) {
729
- throw new Error("invalid date");
730
- }
731
-
732
- return date.getFullYear();
733
-
734
- case "month":
735
- date = new Date(value);
736
- if (isNaN(date.getTime())) {
737
- throw new Error("invalid date");
738
- }
739
-
740
- return date.getMonth() + 1;
741
-
742
- case "day":
743
- date = new Date(value);
744
- if (isNaN(date.getTime())) {
745
- throw new Error("invalid date");
746
- }
747
-
748
- return date.getDate();
749
-
750
- case "weekday":
751
- date = new Date(value);
752
- if (isNaN(date.getTime())) {
753
- throw new Error("invalid date");
754
- }
755
-
756
- return date.getDay();
757
-
758
- case "hour":
759
- case "hours":
760
- date = new Date(value);
761
- if (isNaN(date.getTime())) {
762
- throw new Error("invalid date");
763
- }
764
-
765
- return date.getHours();
766
-
767
- case "minute":
768
- case "minutes":
769
- date = new Date(value);
770
- if (isNaN(date.getTime())) {
771
- throw new Error("invalid date");
772
- }
773
-
774
- return date.getMinutes();
775
-
776
- case "second":
777
- case "seconds":
778
- date = new Date(value);
779
- if (isNaN(date.getTime())) {
780
- throw new Error("invalid date");
781
- }
782
-
783
- return date.getSeconds();
784
-
785
- case "i18n":
786
- case "translation":
787
- translations = getDocumentTranslations();
788
- if (!(translations instanceof Translations)) {
789
- throw new Error("missing translations");
790
- }
791
-
792
- key = args.shift() || undefined;
793
- if (key === undefined) {
794
- key = value;
795
- }
796
-
797
- defaultValue = args.shift() || undefined;
798
-
799
- defaultValue = convertSpecialStrings(defaultValue, value);
800
-
801
- return translations.getText(key, defaultValue);
802
-
803
- case "set-toggle":
804
- case "set-set":
805
- case "set-remove":
806
- const modifier = args.shift();
807
- let delimiter = args.shift();
808
- if (delimiter === undefined) {
809
- delimiter = " ";
810
- }
811
-
812
- const set = new Set(value.split(delimiter));
813
- const toggle = new Set(modifier.split(delimiter));
814
- if (this.command === "set-toggle") {
815
- for (const t of toggle) {
816
- if (set.has(t)) {
817
- set.delete(t);
818
- } else {
819
- set.add(t);
820
- }
821
- }
822
- } else if (this.command === "set-set") {
823
- for (const t of toggle) {
824
- set.add(t);
825
- }
826
- } else if (this.command === "set-remove") {
827
- for (const t of toggle) {
828
- set.delete(t);
829
- }
830
- }
831
- return Array.from(set).join(delimiter);
832
-
833
- default:
834
- throw new Error(`unknown command ${this.command}`);
835
- }
176
+ const console = getGlobalObject("console");
177
+
178
+ const args = clone(this.args);
179
+ let key;
180
+ let defaultValue;
181
+
182
+ let translations;
183
+ let date;
184
+ let locale;
185
+ let timestamp;
186
+ let map;
187
+ let keyValue;
188
+
189
+ switch (this.command) {
190
+ case "static":
191
+ return this.args.join(":");
192
+
193
+ case "tolower":
194
+ case "strtolower":
195
+ case "tolowercase":
196
+ validateString(value);
197
+ return value.toLowerCase();
198
+
199
+ case "contains":
200
+ if (isString(value)) {
201
+ return value.includes(args[0]);
202
+ }
203
+
204
+ if (isArray(value)) {
205
+ return value.includes(args[0]);
206
+ }
207
+
208
+ if (isObject(value)) {
209
+ return value.hasOwnProperty(args[0]);
210
+ }
211
+
212
+ return false;
213
+
214
+ case "has-entries":
215
+ case "hasentries":
216
+ if (isObject(value)) {
217
+ return Object.keys(value).length > 0;
218
+ }
219
+
220
+ if (isArray(value)) {
221
+ return value.length > 0;
222
+ }
223
+
224
+ return false;
225
+
226
+ case "isundefined":
227
+ case "is-undefined":
228
+ return value === undefined;
229
+
230
+ case "isnull":
231
+ case "is-null":
232
+ return value === null;
233
+
234
+ case "isset":
235
+ case "is-set":
236
+ return value !== undefined && value !== null;
237
+
238
+ case "isnumber":
239
+ case "is-number":
240
+ return isPrimitive(value) && !isNaN(value);
241
+
242
+ case "isinteger":
243
+ case "is-integer":
244
+ return isPrimitive(value) && !isNaN(value) && value % 1 === 0;
245
+
246
+ case "isfloat":
247
+ case "is-float":
248
+ return isPrimitive(value) && !isNaN(value) && value % 1 !== 0;
249
+
250
+ case "isobject":
251
+ case "is-object":
252
+ return isObject(value);
253
+
254
+ case "isarray":
255
+ case "is-array":
256
+ return Array.isArray(value);
257
+
258
+ case "not":
259
+ validateBoolean(value);
260
+ return !value;
261
+
262
+ case "toupper":
263
+ case "strtoupper":
264
+ case "touppercase":
265
+ validateString(value);
266
+ return value.toUpperCase();
267
+
268
+ case "tostring":
269
+ return `${value}`;
270
+
271
+ case "tointeger":
272
+ const n = parseInt(value);
273
+ validateInteger(n);
274
+ return n;
275
+
276
+ case "to-json":
277
+ case "tojson":
278
+ return JSON.stringify(value);
279
+
280
+ case "from-json":
281
+ case "fromjson":
282
+ return JSON.parse(value);
283
+
284
+ case "trim":
285
+ validateString(value);
286
+ return value.trim();
287
+
288
+ case "rawurlencode":
289
+ validateString(value);
290
+ return encodeURIComponent(value)
291
+ .replace(/!/g, "%21")
292
+ .replace(/'/g, "%27")
293
+ .replace(/\(/g, "%28")
294
+ .replace(/\)/g, "%29")
295
+ .replace(/\*/g, "%2A");
296
+
297
+ case "call":
298
+ /**
299
+ * callback-definition
300
+ * function callback(value, ...args) {
301
+ * return value;
302
+ * }
303
+ */
304
+
305
+ let callback;
306
+ const callbackName = args.shift();
307
+ let context = getGlobal();
308
+
309
+ if (isObject(value) && value.hasOwnProperty(callbackName)) {
310
+ callback = value[callbackName];
311
+ } else if (this.callbacks.has(callbackName)) {
312
+ const s = this.callbacks.get(callbackName);
313
+ callback = s?.["callback"];
314
+ context = s?.["context"];
315
+ } else if (
316
+ typeof window === "object" &&
317
+ window.hasOwnProperty(callbackName)
318
+ ) {
319
+ callback = window[callbackName];
320
+ }
321
+ validateFunction(callback);
322
+
323
+ args.unshift(value);
324
+ return callback.call(context, ...args);
325
+
326
+ case "plain":
327
+ case "plaintext":
328
+ validateString(value);
329
+ const doc = new DOMParser().parseFromString(value, "text/html");
330
+ return doc.body.textContent || "";
331
+
332
+ case "if":
333
+ case "?":
334
+ validatePrimitive(value);
335
+
336
+ let trueStatement = args.shift() || undefined;
337
+ let falseStatement = args.shift() || undefined;
338
+
339
+ trueStatement = convertSpecialStrings(trueStatement, value);
340
+ falseStatement = convertSpecialStrings(falseStatement, value);
341
+
342
+ const condition = evaluateCondition(value);
343
+ return condition ? trueStatement : falseStatement;
344
+
345
+ case "ucfirst":
346
+ validateString(value);
347
+
348
+ const firstchar = value.charAt(0).toUpperCase();
349
+ return firstchar + value.substr(1);
350
+ case "ucwords":
351
+ validateString(value);
352
+
353
+ return value.replace(
354
+ /^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g,
355
+ function (v) {
356
+ return v.toUpperCase();
357
+ },
358
+ );
359
+
360
+ case "count":
361
+ case "length":
362
+ if (
363
+ (isString(value) || isObject(value) || isArray(value)) &&
364
+ value.hasOwnProperty("length")
365
+ ) {
366
+ return value.length;
367
+ }
368
+
369
+ throw new TypeError(`unsupported type ${typeof value}`);
370
+
371
+ case "to-base64":
372
+ case "btoa":
373
+ case "base64":
374
+ return btoa(convertToString(value));
375
+
376
+ case "atob":
377
+ case "from-base64":
378
+ return atob(convertToString(value));
379
+
380
+ case "empty":
381
+ return "";
382
+
383
+ case "undefined":
384
+ return undefined;
385
+
386
+ case "debug":
387
+ if (isObject(console)) {
388
+ console.log(value);
389
+ }
390
+
391
+ return value;
392
+
393
+ case "prefix":
394
+ validateString(value);
395
+ const prefix = args?.[0];
396
+ return prefix + value;
397
+
398
+ case "suffix":
399
+ validateString(value);
400
+ const suffix = args?.[0];
401
+ return value + suffix;
402
+
403
+ case "uniqid":
404
+ return new ID().toString();
405
+
406
+ case "first-key":
407
+ case "last-key":
408
+ case "nth-last-key":
409
+ case "nth-key":
410
+ if (!isObject(value)) {
411
+ throw new Error("type not supported");
412
+ }
413
+
414
+ const keys = Object.keys(value).sort();
415
+
416
+ if (this.command === "first-key") {
417
+ key = 0;
418
+ } else if (this.command === "last-key") {
419
+ key = keys.length - 1;
420
+ } else {
421
+ key = validateInteger(parseInt(args.shift()));
422
+
423
+ if (this.command === "nth-last-key") {
424
+ key = keys.length - key - 1;
425
+ }
426
+ }
427
+
428
+ defaultValue = args.shift() || "";
429
+
430
+ const useKey = keys?.[key];
431
+
432
+ if (value?.[useKey]) {
433
+ return value?.[useKey];
434
+ }
435
+
436
+ return defaultValue;
437
+
438
+ case "key":
439
+ case "property":
440
+ case "index":
441
+ key = args.shift() || undefined;
442
+
443
+ if (key === undefined) {
444
+ throw new Error("missing key parameter");
445
+ }
446
+
447
+ defaultValue = args.shift() || undefined;
448
+
449
+ if (value instanceof Map) {
450
+ if (!value.has(key)) {
451
+ return defaultValue;
452
+ }
453
+ return value.get(key);
454
+ }
455
+
456
+ if (isObject(value) || isArray(value)) {
457
+ if (value?.[key]) {
458
+ return value?.[key];
459
+ }
460
+
461
+ return defaultValue;
462
+ }
463
+
464
+ throw new Error("type not supported");
465
+
466
+ case "path-exists":
467
+ key = args.shift();
468
+ if (key === undefined) {
469
+ throw new Error("missing key parameter");
470
+ }
471
+
472
+ return new Pathfinder(value).exists(key);
473
+
474
+ case "concat":
475
+ const pf2 = new Pathfinder(value);
476
+ let concat = "";
477
+ while (args.length > 0) {
478
+ key = args.shift();
479
+ if (key === undefined) {
480
+ throw new Error("missing key parameter");
481
+ }
482
+
483
+ // add empty strings
484
+ if (isString(key) && key.trim() === "") {
485
+ concat += key;
486
+ continue;
487
+ }
488
+
489
+ if (!pf2.exists(key)) {
490
+ concat += key;
491
+ continue;
492
+ }
493
+ const v = pf2.getVia(key);
494
+ if (!isPrimitive(v)) {
495
+ throw new Error("value is not primitive");
496
+ }
497
+
498
+ concat += v;
499
+ }
500
+
501
+ return concat;
502
+ case "path":
503
+ key = args.shift();
504
+ if (key === undefined) {
505
+ throw new Error("missing key parameter");
506
+ }
507
+
508
+ const pf = new Pathfinder(value);
509
+
510
+ if (!pf.exists(key)) {
511
+ return undefined;
512
+ }
513
+
514
+ return pf.getVia(key);
515
+
516
+ case "substring":
517
+ validateString(value);
518
+
519
+ const start = parseInt(args[0]) || 0;
520
+ const end = (parseInt(args[1]) || 0) + start;
521
+
522
+ return value.substring(start, end);
523
+
524
+ case "nop":
525
+ return value;
526
+
527
+ case "??":
528
+ case "default":
529
+ if (value !== undefined && value !== null) {
530
+ return value;
531
+ }
532
+
533
+ defaultValue = args.shift();
534
+ let defaultType = args.shift();
535
+ if (defaultType === undefined) {
536
+ defaultType = "string";
537
+ }
538
+
539
+ switch (defaultType) {
540
+ case "int":
541
+ case "integer":
542
+ return parseInt(defaultValue);
543
+ case "float":
544
+ return parseFloat(defaultValue);
545
+ case "undefined":
546
+ return undefined;
547
+ case "bool":
548
+ case "boolean":
549
+ defaultValue = defaultValue.toLowerCase();
550
+ return (
551
+ (defaultValue !== "undefined" &&
552
+ defaultValue !== "" &&
553
+ defaultValue !== "off" &&
554
+ defaultValue !== "false" &&
555
+ defaultValue !== "false") ||
556
+ defaultValue === "on" ||
557
+ defaultValue === "true" ||
558
+ defaultValue === "true"
559
+ );
560
+ case "string":
561
+ return `${defaultValue}`;
562
+ case "object":
563
+ return JSON.parse(atob(defaultValue));
564
+ }
565
+
566
+ throw new Error("type not supported");
567
+
568
+ case "map":
569
+ map = new Map();
570
+ while (args.length > 0) {
571
+ keyValue = args.shift();
572
+ if (keyValue === undefined) {
573
+ throw new Error("missing key parameter");
574
+ }
575
+
576
+ keyValue = keyValue.split("=");
577
+ map.set(keyValue[0], keyValue[1]);
578
+ }
579
+
580
+ return map.get(value);
581
+
582
+ case "equals":
583
+ if (args.length === 0) {
584
+ throw new Error("missing value parameter");
585
+ }
586
+
587
+ validatePrimitive(value);
588
+
589
+ const equalsValue = args.shift();
590
+
591
+ /**
592
+ * The history of “typeof null”
593
+ * https://2ality.com/2013/10/typeof-null.html
594
+ * In JavaScript, typeof null is 'object', which incorrectly suggests
595
+ * that null is an object.
596
+ */
597
+ if (value === null) {
598
+ return equalsValue === "null";
599
+ }
600
+
601
+ const typeOfValue = typeof value;
602
+
603
+ switch (typeOfValue) {
604
+ case "string":
605
+ return value === equalsValue;
606
+ case "number":
607
+ return value === parseFloat(equalsValue);
608
+ case "boolean":
609
+ return value === (equalsValue === "true" || equalsValue === "on");
610
+ case "undefined":
611
+ return equalsValue === "undefined";
612
+ default:
613
+ throw new Error("type not supported");
614
+ }
615
+
616
+ case "money":
617
+ case "currency":
618
+ try {
619
+ locale = getLocaleOfDocument();
620
+ } catch (e) {
621
+ throw new Error(`unsupported locale or missing format (${e.message})`);
622
+ }
623
+
624
+ // Verwenden von RegExp, um Währung und Betrag zu extrahieren
625
+ const match = value.match(/^([A-Z]{3})[\s-]*(\d+(\.\d+)?)$/);
626
+ if (!match) {
627
+ throw new Error("invalid currency format");
628
+ }
629
+
630
+ const currency = match[1];
631
+ const amount = match[2];
632
+
633
+ const maximumFractionDigits = args?.[0] || 2;
634
+ const roundingIncrement = args?.[1] || 5;
635
+
636
+ const nf = new Intl.NumberFormat(locale.toString(), {
637
+ style: "currency",
638
+ currency: currency,
639
+ maximumFractionDigits: maximumFractionDigits,
640
+ roundingIncrement: roundingIncrement,
641
+ });
642
+
643
+ return nf.format(amount);
644
+
645
+ case "timestamp":
646
+ date = new Date(value);
647
+ timestamp = date.getTime();
648
+ if (isNaN(timestamp)) {
649
+ throw new Error("invalid date");
650
+ }
651
+ return timestamp;
652
+
653
+ case "time":
654
+ date = new Date(value);
655
+ if (isNaN(date.getTime())) {
656
+ throw new Error("invalid date");
657
+ }
658
+
659
+ try {
660
+ locale = getLocaleOfDocument();
661
+ return date.toLocaleTimeString(locale.toString(), {
662
+ hour12: false,
663
+ });
664
+ } catch (e) {
665
+ throw new Error(`unsupported locale or missing format (${e.message})`);
666
+ }
667
+
668
+ case "datetimeformat":
669
+ date = new Date(value);
670
+ if (isNaN(date.getTime())) {
671
+ throw new Error("invalid date");
672
+ }
673
+
674
+ const options = {
675
+ dateStyle: "medium",
676
+ timeStyle: "medium",
677
+ hour12: false,
678
+ };
679
+
680
+ if (args.length > 0) {
681
+ options.dateStyle = args.shift();
682
+ }
683
+
684
+ if (args.length > 0) {
685
+ options.timeStyle = args.shift();
686
+ }
687
+
688
+ try {
689
+ locale = getLocaleOfDocument().toString();
690
+ return new Intl.DateTimeFormat(locale, options).format(date);
691
+ } catch (e) {
692
+ throw new Error(`unsupported locale or missing format (${e.message})`);
693
+ }
694
+
695
+ case "datetime":
696
+ date = new Date(value);
697
+ if (isNaN(date.getTime())) {
698
+ throw new Error("invalid date");
699
+ }
700
+
701
+ try {
702
+ locale = getLocaleOfDocument();
703
+ return date.toLocaleString(locale.toString(), {
704
+ hour12: false,
705
+ });
706
+ } catch (e) {
707
+ throw new Error(`unsupported locale or missing format (${e.message})`);
708
+ }
709
+
710
+ case "date":
711
+ date = new Date(value);
712
+ if (isNaN(date.getTime())) {
713
+ throw new Error("invalid date");
714
+ }
715
+
716
+ try {
717
+ locale = getLocaleOfDocument();
718
+ return date.toLocaleDateString(locale.toString(), {
719
+ year: "numeric",
720
+ month: "2-digit",
721
+ day: "2-digit",
722
+ });
723
+ } catch (e) {
724
+ throw new Error(`unsupported locale or missing format (${e.message})`);
725
+ }
726
+
727
+ case "time-ago":
728
+ date = new Date(value);
729
+ if (isNaN(date.getTime())) {
730
+ throw new Error("invalid date");
731
+ }
732
+
733
+ try {
734
+ locale = getLocaleOfDocument();
735
+ return formatTimeAgo(date, locale.toString());
736
+ } catch (e) {
737
+ throw new Error(`unsupported locale or missing format (${e.message})`);
738
+ }
739
+
740
+ case "year":
741
+ date = new Date(value);
742
+ if (isNaN(date.getTime())) {
743
+ throw new Error("invalid date");
744
+ }
745
+
746
+ return date.getFullYear();
747
+
748
+ case "month":
749
+ date = new Date(value);
750
+ if (isNaN(date.getTime())) {
751
+ throw new Error("invalid date");
752
+ }
753
+
754
+ return date.getMonth() + 1;
755
+
756
+ case "day":
757
+ date = new Date(value);
758
+ if (isNaN(date.getTime())) {
759
+ throw new Error("invalid date");
760
+ }
761
+
762
+ return date.getDate();
763
+
764
+ case "weekday":
765
+ date = new Date(value);
766
+ if (isNaN(date.getTime())) {
767
+ throw new Error("invalid date");
768
+ }
769
+
770
+ return date.getDay();
771
+
772
+ case "hour":
773
+ case "hours":
774
+ date = new Date(value);
775
+ if (isNaN(date.getTime())) {
776
+ throw new Error("invalid date");
777
+ }
778
+
779
+ return date.getHours();
780
+
781
+ case "minute":
782
+ case "minutes":
783
+ date = new Date(value);
784
+ if (isNaN(date.getTime())) {
785
+ throw new Error("invalid date");
786
+ }
787
+
788
+ return date.getMinutes();
789
+
790
+ case "second":
791
+ case "seconds":
792
+ date = new Date(value);
793
+ if (isNaN(date.getTime())) {
794
+ throw new Error("invalid date");
795
+ }
796
+
797
+ return date.getSeconds();
798
+
799
+ case "i18n":
800
+ case "translation":
801
+ translations = getDocumentTranslations();
802
+ if (!(translations instanceof Translations)) {
803
+ throw new Error("missing translations");
804
+ }
805
+
806
+ key = args.shift() || undefined;
807
+ if (key === undefined) {
808
+ key = value;
809
+ }
810
+
811
+ defaultValue = args.shift() || undefined;
812
+
813
+ defaultValue = convertSpecialStrings(defaultValue, value);
814
+
815
+ return translations.getText(key, defaultValue);
816
+
817
+ case "set-toggle":
818
+ case "set-set":
819
+ case "set-remove":
820
+ const modifier = args.shift();
821
+ let delimiter = args.shift();
822
+ if (delimiter === undefined) {
823
+ delimiter = " ";
824
+ }
825
+
826
+ const set = new Set(value.split(delimiter));
827
+ const toggle = new Set(modifier.split(delimiter));
828
+ if (this.command === "set-toggle") {
829
+ for (const t of toggle) {
830
+ if (set.has(t)) {
831
+ set.delete(t);
832
+ } else {
833
+ set.add(t);
834
+ }
835
+ }
836
+ } else if (this.command === "set-set") {
837
+ for (const t of toggle) {
838
+ set.add(t);
839
+ }
840
+ } else if (this.command === "set-remove") {
841
+ for (const t of toggle) {
842
+ set.delete(t);
843
+ }
844
+ }
845
+ return Array.from(set).join(delimiter);
846
+
847
+ default:
848
+ throw new Error(`unknown command ${this.command}`);
849
+ }
836
850
  }
837
851
 
838
852
  /**
@@ -843,18 +857,18 @@ function transform(value) {
843
857
  * @return {undefined|*|null|string}
844
858
  */
845
859
  function convertSpecialStrings(input, value) {
846
- switch (input) {
847
- case "value":
848
- return value;
849
- case "\\value":
850
- return "value";
851
- case "\\undefined":
852
- return undefined;
853
- case "\\null":
854
- return null;
855
- default:
856
- return input;
857
- }
860
+ switch (input) {
861
+ case "value":
862
+ return value;
863
+ case "\\value":
864
+ return "value";
865
+ case "\\undefined":
866
+ return undefined;
867
+ case "\\null":
868
+ return null;
869
+ default:
870
+ return input;
871
+ }
858
872
  }
859
873
 
860
874
  /**
@@ -863,17 +877,20 @@ function convertSpecialStrings(input, value) {
863
877
  * @return {boolean}
864
878
  */
865
879
  function evaluateCondition(value) {
866
- const lowerValue = typeof value === "string" ? value.toLowerCase() : value;
867
-
868
- return (
869
- (value !== undefined &&
870
- value !== null &&
871
- value !== "" &&
872
- lowerValue !== "off" &&
873
- lowerValue !== "false" &&
874
- value !== false) ||
875
- lowerValue === "on" ||
876
- lowerValue === "true" ||
877
- value === true
878
- );
880
+ const lowerValue = typeof value === "string" ? value.toLowerCase() : value;
881
+
882
+ return (
883
+ (value !== undefined &&
884
+ value !== null &&
885
+ value !== "" &&
886
+ lowerValue !== "off" &&
887
+ lowerValue !== "false" &&
888
+ value !== false) ||
889
+ lowerValue === "on" ||
890
+ lowerValue === "true" ||
891
+ value === true
892
+ );
879
893
  }
894
+
895
+
896
+