@schukai/monster 4.46.5 → 4.46.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
 
4
4
 
5
+ ## [4.46.6] - 2025-11-22
6
+
7
+ ### Bug Fixes
8
+
9
+ - some NaN Issues
10
+
11
+
12
+
5
13
  ## [4.46.5] - 2025-11-22
6
14
 
7
15
  ### Bug Fixes
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.46.5"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.46.6"}
@@ -72,6 +72,12 @@ const pendingDiffsSymbol = Symbol("pendingDiffs");
72
72
  */
73
73
  const processingSymbol = Symbol("processing");
74
74
 
75
+ /**
76
+ * @private
77
+ * Performance optimization: static Set for boolean checks
78
+ */
79
+ const TRUE_VALUES = new Set(["true", "1", "on"]);
80
+
75
81
  /**
76
82
  * The updater class connects an object with the DOM. In this way, structures and contents in the DOM can be
77
83
  * programmatically adapted via attributes.
@@ -194,7 +200,7 @@ class Updater extends Base {
194
200
  *
195
201
  * ```js
196
202
  * updater.run().then(() => {
197
- * updater.enableEventProcessing();
203
+ * updater.enableEventProcessing();
198
204
  * });
199
205
  * ```
200
206
  *
@@ -243,7 +249,7 @@ class Updater extends Base {
243
249
  *
244
250
  * ```js
245
251
  * updater.run().then(() => {
246
- * updater.enableEventProcessing();
252
+ * updater.enableEventProcessing();
247
253
  * });
248
254
  * ```
249
255
  *
@@ -308,8 +314,9 @@ function getCheckStateCallback() {
308
314
  return function (current) {
309
315
  // this is a reference to the current object (therefore no array function here)
310
316
  if (this instanceof HTMLInputElement) {
311
- if (["radio", "checkbox"].indexOf(this.type) !== -1) {
312
- return `${this.value}` === `${current}` ? "true" : undefined;
317
+ if (["radio", "checkbox"].includes(this.type)) {
318
+ if (current == null) return undefined;
319
+ return String(this.value) === String(current) ? "true" : undefined;
313
320
  }
314
321
  } else if (this instanceof HTMLOptionElement) {
315
322
  if (isArray(current) && current.indexOf(this.value) !== -1) {
@@ -447,18 +454,21 @@ function retrieveAndSetValue(element) {
447
454
  case "int":
448
455
  case "float":
449
456
  case "integer":
450
- value = Number(value);
451
- if (isNaN(value)) {
452
- value = 0;
457
+ const num = Number(value);
458
+ if (value === "" || value === null || value === undefined) {
459
+ value = undefined;
460
+ } else if (Number.isNaN(num)) {
461
+ value = undefined;
462
+ } else {
463
+ value = num;
453
464
  }
454
465
  break;
455
466
  case "boolean":
456
467
  case "bool":
457
468
  case "checkbox":
458
- value =
459
- value === "true" || value === "1" || value === "on" || value === true;
469
+ // Use static set (Performance fix)
470
+ value = TRUE_VALUES.has(String(value).toLowerCase()) || value === true;
460
471
  break;
461
-
462
472
  case "string[]":
463
473
  case "[]string":
464
474
  if (isString(value)) {
@@ -509,11 +519,14 @@ function retrieveAndSetValue(element) {
509
519
  case "object":
510
520
  case "json":
511
521
  if (isString(value)) {
512
- value = JSON.parse(value);
522
+ try {
523
+ value = JSON.parse(value);
524
+ } catch (e) {
525
+ throw new Error("unsupported value for object");
526
+ }
513
527
  } else {
514
528
  throw new Error("unsupported value for object");
515
529
  }
516
-
517
530
  break;
518
531
  default:
519
532
  break;
@@ -535,21 +548,11 @@ function retrieveAndSetValue(element) {
535
548
  * @private
536
549
  */
537
550
  function parseIntArray(val) {
538
- if (isString(val)) {
539
- return val.trim() === ""
540
- ? []
541
- : val
542
- .split(",")
543
- .map((v) => parseInt(v, 10))
544
- .filter((v) => !isNaN(v));
545
- } else if (isInteger(val)) {
546
- return [val];
547
- } else if (val === undefined || val === null) {
548
- return [];
549
- } else if (isArray(val)) {
550
- return val.map((v) => parseInt(v, 10)).filter((v) => !isNaN(v));
551
- }
552
- throw new Error("unsupported value for int array");
551
+ if (val === undefined || val === null) return [];
552
+
553
+ const list = isArray(val) ? val : String(val).split(",");
554
+
555
+ return list.map((v) => parseInt(v, 10)).filter((v) => !Number.isNaN(v));
553
556
  }
554
557
 
555
558
  /**
@@ -830,18 +833,11 @@ function runUpdateContent(container, parts, subject) {
830
833
 
831
834
  // Unfortunately, static data is always changed as well, since it is not possible to react to changes here.
832
835
  const query = `[${ATTRIBUTE_UPDATER_REPLACE}^="path:${current}"], [${ATTRIBUTE_UPDATER_REPLACE}^="static:"], [${ATTRIBUTE_UPDATER_REPLACE}^="i18n:"]`;
833
- const e = container.querySelectorAll(`${query}`);
834
-
835
- const iterator = new Set([...e]);
836
836
 
837
- if (container.matches(query)) {
838
- iterator.add(container);
839
- }
837
+ // Performance optimization: avoid new Set([...NodeList])
838
+ const elements = container.querySelectorAll(`${query}`);
840
839
 
841
- /**
842
- * @type {HTMLElement}
843
- */
844
- for (const [element] of iterator.entries()) {
840
+ const process = (element) => {
845
841
  if (mem.has(element)) return;
846
842
  mem.add(element);
847
843
 
@@ -874,6 +870,16 @@ function runUpdateContent(container, parts, subject) {
874
870
  } else {
875
871
  element.innerHTML = value;
876
872
  }
873
+ };
874
+
875
+ // Iterate NodeList directly
876
+ for (const element of elements) {
877
+ process(element);
878
+ }
879
+
880
+ // Check container
881
+ if (container.matches(query)) {
882
+ process(container);
877
883
  }
878
884
  }
879
885
  }
@@ -908,27 +914,18 @@ function runUpdateAttributes(container, parts, subject) {
908
914
  const current = parts.join(".");
909
915
  parts.pop();
910
916
 
911
- let iterator = new Set();
912
-
913
917
  const query = `[${ATTRIBUTE_UPDATER_SELECT_THIS}][${ATTRIBUTE_UPDATER_ATTRIBUTES}], [${ATTRIBUTE_UPDATER_ATTRIBUTES}*="path:${current}"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="static:"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="i18n:"]`;
914
918
 
915
- const e = container.querySelectorAll(query);
916
-
917
- if (e.length > 0) {
918
- iterator = new Set([...e]);
919
- }
920
-
921
- if (container.matches(query)) {
922
- iterator.add(container);
923
- }
919
+ // Performance optimization: avoid new Set([...NodeList])
920
+ const elements = container.querySelectorAll(query);
924
921
 
925
- for (const [element] of iterator.entries()) {
922
+ const process = (element) => {
926
923
  if (mem.has(element)) return;
927
924
  mem.add(element);
928
925
 
929
926
  // this case occurs when the ATTRIBUTE_UPDATER_SELECT_THIS attribute is set
930
927
  if (!element.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) {
931
- continue;
928
+ return;
932
929
  }
933
930
 
934
931
  const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES);
@@ -967,6 +964,16 @@ function runUpdateAttributes(container, parts, subject) {
967
964
  }
968
965
  handleInputControlAttributeUpdate.call(this, element, name, value);
969
966
  }
967
+ };
968
+
969
+ // Iterate NodeList directly
970
+ for (const element of elements) {
971
+ process(element);
972
+ }
973
+
974
+ // Check container
975
+ if (container.matches(query)) {
976
+ process(container);
970
977
  }
971
978
  }
972
979
  }
@@ -979,52 +986,59 @@ function runUpdateAttributes(container, parts, subject) {
979
986
  * @return {void}
980
987
  * @this Updater
981
988
  */
982
-
983
989
  function handleInputControlAttributeUpdate(element, name, value) {
990
+ // Prevent NaN warnings by normalizing invalid numbers to undefined
991
+ if (typeof value === "number" && isNaN(value)) {
992
+ value = undefined;
993
+ }
994
+
984
995
  if (element instanceof HTMLSelectElement) {
985
996
  switch (element.type) {
986
997
  case "select-multiple":
987
- for (const [index, opt] of Object.entries(element.options)) {
988
- opt.selected = value.indexOf(opt.value) !== -1;
998
+ // Ensure value is an array before calling indexOf to avoid errors
999
+ if (Array.isArray(value) || typeof value === "string") {
1000
+ for (const [index, opt] of Object.entries(element.options)) {
1001
+ opt.selected = value.indexOf(opt.value) !== -1;
1002
+ }
989
1003
  }
990
-
991
1004
  break;
992
1005
  case "select-one":
993
1006
  // Only one value may be selected
994
-
995
1007
  for (const [index, opt] of Object.entries(element.options)) {
996
- if (opt.value === value) {
1008
+ if (opt.value == value) {
1009
+ // Loose equality to match string numbers
997
1010
  element.selectedIndex = index;
998
1011
  break;
999
1012
  }
1000
1013
  }
1001
-
1002
1014
  break;
1003
1015
  }
1004
1016
  } else if (element instanceof HTMLInputElement) {
1005
1017
  switch (element.type) {
1006
1018
  case "radio":
1007
1019
  if (name === "checked") {
1008
- element.checked = value !== undefined;
1020
+ element.checked = value !== undefined && value !== null;
1009
1021
  }
1010
1022
  break;
1011
1023
 
1012
1024
  case "checkbox":
1013
1025
  if (name === "checked") {
1014
- element.checked = value !== undefined;
1026
+ element.checked = value !== undefined && value !== null;
1015
1027
  }
1016
1028
  break;
1017
1029
 
1018
1030
  case "text":
1019
1031
  default:
1020
1032
  if (name === "value") {
1021
- element.value = value === undefined ? "" : value;
1033
+ // Check for undefined, null, or NaN
1034
+ element.value = value === undefined || value === null ? "" : value;
1022
1035
  }
1023
1036
  break;
1024
1037
  }
1025
1038
  } else if (element instanceof HTMLTextAreaElement) {
1026
1039
  if (name === "value") {
1027
- element.value = value === undefined ? "" : value;
1040
+ // Check for undefined, null, or NaN
1041
+ element.value = value === undefined || value === null ? "" : value;
1028
1042
  }
1029
1043
  }
1030
1044
  }