@schukai/monster 3.96.2 → 3.97.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +23 -94
  2. package/package.json +1 -1
  3. package/source/components/accessibility/locale-picker.mjs +598 -0
  4. package/source/components/accessibility/style/locale-picker.css +1 -0
  5. package/source/components/accessibility/style/locale-picker.pcss +26 -0
  6. package/source/components/accessibility/stylesheet/locale-picker.mjs +31 -0
  7. package/source/components/content/stylesheet/copy.mjs +2 -2
  8. package/source/components/datatable/datasource/rest.mjs +33 -10
  9. package/source/components/datatable/filter.mjs +164 -63
  10. package/source/components/datatable/stylesheet/change-button.mjs +2 -2
  11. package/source/components/datatable/stylesheet/column-bar.mjs +2 -2
  12. package/source/components/datatable/stylesheet/dataset.mjs +2 -2
  13. package/source/components/datatable/stylesheet/datasource.mjs +1 -1
  14. package/source/components/datatable/stylesheet/datatable.mjs +1 -1
  15. package/source/components/datatable/stylesheet/embedded-pagination.mjs +2 -2
  16. package/source/components/datatable/stylesheet/filter-button.mjs +1 -1
  17. package/source/components/datatable/stylesheet/filter-controls-defaults.mjs +1 -1
  18. package/source/components/datatable/stylesheet/filter-date-range.mjs +1 -1
  19. package/source/components/datatable/stylesheet/filter-range.mjs +1 -1
  20. package/source/components/datatable/stylesheet/filter-select.mjs +2 -2
  21. package/source/components/datatable/stylesheet/filter.mjs +2 -2
  22. package/source/components/datatable/stylesheet/pagination.mjs +1 -1
  23. package/source/components/datatable/stylesheet/save-button.mjs +2 -2
  24. package/source/components/datatable/stylesheet/status.mjs +1 -1
  25. package/source/components/form/form.mjs +4 -2
  26. package/source/components/form/stylesheet/action-button.mjs +1 -1
  27. package/source/components/form/stylesheet/api-bar.mjs +1 -1
  28. package/source/components/form/stylesheet/api-button.mjs +1 -1
  29. package/source/components/form/stylesheet/button-bar.mjs +1 -1
  30. package/source/components/form/stylesheet/button.mjs +1 -1
  31. package/source/components/form/stylesheet/confirm-button.mjs +1 -1
  32. package/source/components/form/stylesheet/context-error.mjs +1 -1
  33. package/source/components/form/stylesheet/context-help.mjs +1 -1
  34. package/source/components/form/stylesheet/field-set.mjs +1 -1
  35. package/source/components/form/stylesheet/form.mjs +1 -1
  36. package/source/components/form/stylesheet/input-group.mjs +1 -1
  37. package/source/components/form/stylesheet/message-state-button.mjs +1 -1
  38. package/source/components/form/stylesheet/password.mjs +1 -1
  39. package/source/components/form/stylesheet/popper-button.mjs +1 -1
  40. package/source/components/form/stylesheet/select.mjs +1 -1
  41. package/source/components/form/stylesheet/state-button.mjs +1 -1
  42. package/source/components/form/stylesheet/toggle-switch.mjs +1 -1
  43. package/source/components/form/stylesheet/tree-select.mjs +1 -1
  44. package/source/components/host/stylesheet/call-button.mjs +2 -2
  45. package/source/components/host/stylesheet/config-manager.mjs +1 -1
  46. package/source/components/host/stylesheet/host.mjs +2 -2
  47. package/source/components/host/stylesheet/overlay.mjs +2 -2
  48. package/source/components/host/stylesheet/toggle-button.mjs +2 -2
  49. package/source/components/host/stylesheet/viewer.mjs +2 -2
  50. package/source/components/layout/stylesheet/collapse.mjs +2 -2
  51. package/source/components/layout/stylesheet/details.mjs +2 -2
  52. package/source/components/layout/stylesheet/iframe.mjs +1 -1
  53. package/source/components/layout/stylesheet/panel.mjs +2 -2
  54. package/source/components/layout/stylesheet/popper.mjs +1 -1
  55. package/source/components/layout/stylesheet/slider.mjs +2 -2
  56. package/source/components/layout/stylesheet/split-panel.mjs +1 -1
  57. package/source/components/layout/stylesheet/tabs.mjs +2 -2
  58. package/source/components/layout/stylesheet/width-toggle.mjs +1 -1
  59. package/source/components/navigation/stylesheet/table-of-content.mjs +2 -2
  60. package/source/components/notify/stylesheet/message.mjs +2 -2
  61. package/source/components/notify/stylesheet/notify.mjs +1 -1
  62. package/source/components/state/stylesheet/log.mjs +1 -1
  63. package/source/components/state/stylesheet/state.mjs +1 -1
  64. package/source/components/style/property.css +1 -0
  65. package/source/components/style/theme.css +4 -4
  66. package/source/components/stylesheet/badge.mjs +1 -1
  67. package/source/components/stylesheet/border.mjs +1 -1
  68. package/source/components/stylesheet/button.mjs +1 -1
  69. package/source/components/stylesheet/card.mjs +1 -1
  70. package/source/components/stylesheet/color.mjs +1 -1
  71. package/source/components/stylesheet/common.mjs +1 -1
  72. package/source/components/stylesheet/control.mjs +1 -1
  73. package/source/components/stylesheet/data-grid.mjs +1 -1
  74. package/source/components/stylesheet/display.mjs +1 -1
  75. package/source/components/stylesheet/floating-ui.mjs +1 -1
  76. package/source/components/stylesheet/form.mjs +1 -1
  77. package/source/components/stylesheet/host.mjs +1 -1
  78. package/source/components/stylesheet/icons.mjs +1 -1
  79. package/source/components/stylesheet/link.mjs +1 -1
  80. package/source/components/stylesheet/mixin/badge.mjs +1 -1
  81. package/source/components/stylesheet/mixin/button.mjs +1 -1
  82. package/source/components/stylesheet/mixin/hover.mjs +1 -1
  83. package/source/components/stylesheet/mixin/icon.mjs +1 -1
  84. package/source/components/stylesheet/mixin/media.mjs +1 -1
  85. package/source/components/stylesheet/mixin/property.mjs +1 -1
  86. package/source/components/stylesheet/mixin/skeleton.mjs +1 -1
  87. package/source/components/stylesheet/mixin/spinner.mjs +1 -1
  88. package/source/components/stylesheet/mixin/typography.mjs +1 -1
  89. package/source/components/stylesheet/normalize.mjs +1 -1
  90. package/source/components/stylesheet/popper.mjs +1 -1
  91. package/source/components/stylesheet/property.mjs +2 -2
  92. package/source/components/stylesheet/ripple.mjs +1 -1
  93. package/source/components/stylesheet/skeleton.mjs +1 -1
  94. package/source/components/stylesheet/space.mjs +1 -1
  95. package/source/components/stylesheet/spinner.mjs +1 -1
  96. package/source/components/stylesheet/table.mjs +1 -1
  97. package/source/components/stylesheet/theme.mjs +1 -1
  98. package/source/components/stylesheet/typography.mjs +1 -1
  99. package/source/components/tree-menu/stylesheet/tree-menu.mjs +1 -1
  100. package/source/constants.mjs +14 -1
  101. package/source/data/extend.mjs +2 -1
  102. package/source/data/transformer.mjs +2 -0
  103. package/source/dom/customelement.mjs +7 -3
  104. package/source/dom/updater.mjs +5 -1
  105. package/source/i18n/locale.mjs +151 -151
  106. package/source/i18n/map/languages.mjs +104 -0
  107. package/source/i18n/util.mjs +139 -0
  108. package/source/monster.mjs +1 -1
  109. package/source/text/formatter.mjs +5 -3
  110. package/source/types/is.mjs +13 -0
  111. package/source/types/proxyobserver.mjs +7 -2
  112. package/source/types/version.mjs +1 -1
  113. package/source/util/clone.mjs +9 -14
  114. package/test/cases/data/pathfinder.mjs +18 -0
  115. package/test/cases/i18n/util.mjs +295 -0
  116. package/test/cases/monster.mjs +1 -1
  117. package/test/cases/text/formatter.mjs +21 -1
  118. package/test/web/test.html +2 -2
  119. package/test/web/tests.js +115 -75
@@ -18,7 +18,7 @@ import { Observer } from "./observer.mjs";
18
18
  import { ObserverList } from "./observerlist.mjs";
19
19
  import { validateObject } from "./validate.mjs";
20
20
  import { extend } from "../data/extend.mjs";
21
- import { instanceSymbol } from "../constants.mjs";
21
+ import { instanceSymbol, proxyInstanceMarker } from "../constants.mjs";
22
22
  import { clone } from "../util/clone.mjs";
23
23
  export { ProxyObserver };
24
24
 
@@ -74,7 +74,7 @@ class ProxyObserver extends Base {
74
74
  /**
75
75
  * @since 1.24.0
76
76
  * @param {Object} obj
77
- * @return {Monster.Types.ProxyObserver}
77
+ * @return {ProxyObserver}
78
78
  */
79
79
  setSubject(obj) {
80
80
  let i;
@@ -152,6 +152,11 @@ function getHandler() {
152
152
  const handler = {
153
153
  // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
154
154
  get: function (target, key, receiver) {
155
+ // this is an internal hack to identify proxy
156
+ if (key === proxyInstanceMarker) {
157
+ return proxyInstanceMarker;
158
+ }
159
+
155
160
  const value = Reflect.get(target, key, receiver);
156
161
 
157
162
  if (typeof key === "symbol") {
@@ -156,7 +156,7 @@ function getMonsterVersion() {
156
156
  }
157
157
 
158
158
  /** don't touch, replaced by make with package.json version */
159
- monsterVersion = new Version("3.96.1");
159
+ monsterVersion = new Version("3.96.2");
160
160
 
161
161
  return monsterVersion;
162
162
  }
@@ -13,7 +13,13 @@
13
13
  */
14
14
 
15
15
  import { getGlobal } from "../types/global.mjs";
16
- import { isArray, isFunction, isObject, isPrimitive } from "../types/is.mjs";
16
+ import {
17
+ isArray,
18
+ isFunction,
19
+ isObject,
20
+ isPrimitive,
21
+ isProxy,
22
+ } from "../types/is.mjs";
17
23
  import { typeOf } from "../types/typeof.mjs";
18
24
  import { validateObject } from "../types/validate.mjs";
19
25
 
@@ -71,8 +77,7 @@ function clone(obj) {
71
77
 
72
78
  /** Do not clone DOM nodes */
73
79
  if (typeof Element !== "undefined" && obj instanceof Element) return obj;
74
- if (typeof HTMLDocument !== "undefined" && obj instanceof HTMLDocument)
75
- return obj;
80
+ if (typeof Document !== "undefined" && obj instanceof Document) return obj;
76
81
  if (
77
82
  typeof DocumentFragment !== "undefined" &&
78
83
  obj instanceof DocumentFragment
@@ -81,21 +86,11 @@ function clone(obj) {
81
86
 
82
87
  /** Do not clone global objects */
83
88
  if (obj === getGlobal()) return obj;
84
- if (typeof globalContext !== "undefined" && obj === globalContext)
85
- return obj;
86
89
  if (typeof window !== "undefined" && obj === window) return obj;
87
90
  if (typeof document !== "undefined" && obj === document) return obj;
88
91
  if (typeof navigator !== "undefined" && obj === navigator) return obj;
89
92
  if (typeof JSON !== "undefined" && obj === JSON) return obj;
90
-
91
- // Handle Proxy-Object
92
- try {
93
- // try/catch because possible: TypeError: Function has non-object prototype 'undefined' in instanceof check
94
- if (obj instanceof Proxy) {
95
- return obj;
96
- }
97
- } catch (e) {}
98
-
93
+ if (isProxy(obj)) return obj; // Handle Proxy-Object
99
94
  return cloneObject(obj);
100
95
  }
101
96
 
@@ -22,6 +22,24 @@ describe('Pathfinder', function () {
22
22
  return r;
23
23
  }
24
24
 
25
+ describe("value is not an integer issue #274", function () {
26
+ it("should not be fail with", function () {
27
+
28
+ const pf = new Pathfinder({
29
+ data: {}
30
+ });
31
+
32
+ try {
33
+ pf.setVia("data.age", 10);
34
+ } catch (e) {
35
+ expect(e).to.be.null;
36
+ }
37
+
38
+ expect(pf.getVia("data.age")).to.be.equal(10);
39
+
40
+ });
41
+ });
42
+
25
43
 
26
44
  describe('with Wildcard and Iterations', function () {
27
45
  let pf, obj;
@@ -0,0 +1,295 @@
1
+ import {getGlobal} from "../../../source/types/global.mjs";
2
+
3
+ import * as chai from 'chai';
4
+ import {chaiDom} from "../../util/chai-dom.mjs";
5
+ import {initJSDOM} from "../../util/jsdom.mjs";
6
+
7
+ let expect = chai.expect;
8
+ chai.use(chaiDom);
9
+
10
+ describe('LocalPicker', function () {
11
+
12
+ let LocalPicker, documentLanguage, linkTags, originalLanguages, getPreferredLanguage;
13
+
14
+ before(function (done) {
15
+ initJSDOM().then(() => {
16
+
17
+ documentLanguage = document.documentElement.lang;
18
+ linkTags = Array.from(document.querySelectorAll('link[rel="alternate"]'));
19
+ linkTags.forEach(item => item.remove());
20
+
21
+ import("element-internals-polyfill").catch(e => done(e));
22
+
23
+ import("../../../source/i18n/util.mjs").then((m) => {
24
+
25
+ LocalPicker = m['LocalPicker'];
26
+ getPreferredLanguage = m['detectUserLanguagePreference'];
27
+
28
+ originalLanguages = navigator.languages;
29
+
30
+ done()
31
+ }).catch(e => done(e))
32
+
33
+
34
+ });
35
+ })
36
+
37
+ after(function () {
38
+ document.documentElement.lang = documentLanguage;
39
+
40
+ linkTags.forEach(item => {
41
+ const link = document.createElement('link');
42
+ link.setAttribute('rel', 'alternate');
43
+ link.setAttribute('hreflang', item.hreflang);
44
+ link.setAttribute('href', item.href);
45
+
46
+ // check if already exits
47
+ if (!document.querySelector(`link[hreflang="${item.hreflang}"]`)) {
48
+ document.querySelector('head').appendChild(link);
49
+ }
50
+ });
51
+
52
+
53
+ Object.defineProperty(navigator, 'languages', {
54
+ value: originalLanguages,
55
+ writable: true
56
+ });
57
+
58
+
59
+ })
60
+
61
+ // Hilfsfunktion zum Setup eines JSDOM-Dokuments mit <html> und ggf. link-Tags
62
+ function setupDOM({htmlLang = '', linkHreflangs = [], navLang = ""}) {
63
+
64
+ linkTags = Array.from(document.querySelectorAll('link[rel="alternate"]'));
65
+ linkTags.forEach(item => item.remove());
66
+
67
+ // Links hinzufügen
68
+ const head = window.document.querySelector('head');
69
+ linkHreflangs.forEach(item => {
70
+ const link = window.document.createElement('link');
71
+ link.setAttribute('rel', 'alternate');
72
+ link.setAttribute('hreflang', item.hreflang);
73
+ link.setAttribute('href', item.href);
74
+ head.appendChild(link);
75
+ });
76
+
77
+ window.document.documentElement.lang = htmlLang;
78
+
79
+
80
+ Object.defineProperty(navigator, 'language', {
81
+ value: navLang[0] || "",
82
+ writable: true
83
+ });
84
+
85
+ Object.defineProperty(navigator, 'languages', {
86
+ value: navLang,
87
+ writable: true
88
+ });
89
+ }
90
+
91
+ describe('getPreferredLanguage()', () => {
92
+
93
+ // 1) No document language, no navigator.languages => "No language information available."
94
+ it('should return "No language information available." when no current and no user preferences', () => {
95
+ setupDOM({htmlLang: '', linkHreflangs: [], navLang: []});
96
+
97
+ const result = getPreferredLanguage();
98
+ expect(result).to.have.property('message').that.not.empty;
99
+ });
100
+
101
+ // 2) Document lang is set, but no <link> tags => "No <link> tags with hreflang available."
102
+ it('should return "No <link> tags with hreflang available." when there are no link tags', () => {
103
+ setupDOM({htmlLang: 'en', linkHreflangs: []});
104
+ window.navigator.languages = [];
105
+
106
+ const result = getPreferredLanguage();
107
+ expect(result).to.have.property('current', 'en');
108
+ expect(result).to.have.property('message').that.equals('No <link> tags with hreflang available.');
109
+ });
110
+
111
+ // 3) Document lang 'en', no user preferences, but link tags exist => "None of the preferred languages are available."
112
+ it('should return "None of the preferred languages are available." when there are link tags but no matching user preferences', () => {
113
+ setupDOM({
114
+ htmlLang: 'en',
115
+ linkHreflangs: [
116
+ {hreflang: 'de', href: 'http://example.com/de'},
117
+ {hreflang: 'fr', href: 'http://example.com/fr'}
118
+ ],
119
+ navLang: []
120
+ });
121
+
122
+ const result = getPreferredLanguage();
123
+ expect(result).to.have.property('current', 'en');
124
+ expect(result).to.have.property('message').that.equals('No available languages match the user\'s preferences.');
125
+ expect(result.available).to.be.an('array').that.has.lengthOf(2);
126
+ });
127
+
128
+ // 4) Document lang 'en', user prefers ['en'], link tags with 'en' and 'de' => best match is 'en'
129
+ it('should return best match = "en" when user prefers en', () => {
130
+ setupDOM({
131
+ htmlLang: 'en',
132
+ linkHreflangs: [
133
+ {hreflang: 'en', href: 'http://example.com/en'},
134
+ {hreflang: 'de', href: 'http://example.com/de'}
135
+ ]
136
+ });
137
+ window.navigator.languages = ['en'];
138
+
139
+ const result = getPreferredLanguage();
140
+ expect(result).to.have.property('current', 'en');
141
+ console.log(JSON.stringify(result.preferred));
142
+ expect(result.preferred).to.have.property('full', "en");
143
+ expect(result.preferred).to.have.property('base', "en");
144
+ expect(result.preferred).to.have.property('label', "English");
145
+ expect(result.preferred).to.have.property('href', "http://example.com/en");
146
+ });
147
+
148
+ // 5) Document lang 'de-DE', user prefers ['de-DE'], link tags with 'de-DE' & 'en-US' => best match is 'de-DE'
149
+ it('should return best match = "de-DE" when user prefers de-DE', () => {
150
+ setupDOM({
151
+ htmlLang: 'de-DE',
152
+ linkHreflangs: [
153
+ {hreflang: 'de-DE', href: 'http://example.com/de-DE'},
154
+ {hreflang: 'en-US', href: 'http://example.com/en-US'}
155
+ ]
156
+ });
157
+ window.navigator.languages = ['de-DE'];
158
+
159
+ const result = getPreferredLanguage();
160
+ expect(result).to.have.property('current', 'de-DE');
161
+
162
+ expect(result.preferred).to.have.property('full', "de-DE");
163
+ expect(result.preferred).to.have.property('base', "de");
164
+ expect(result.preferred).to.have.property('label', "Deutsch (Deutschland)");
165
+ expect(result.preferred).to.have.property('href', "http://example.com/de-DE");
166
+
167
+ });
168
+
169
+
170
+ // 5x) Document lang 'de-DE', user prefers ['de-DE'], link tags with 'de-DE' & 'en-US' => best match is 'de-DE'
171
+ it('should return best match = "de-DE" when user prefers de-DE', () => {
172
+ setupDOM({
173
+ htmlLang: 'en',
174
+ linkHreflangs: [
175
+ {hreflang: 'de', href: 'http://example.com/de-DE'},
176
+ {hreflang: 'en', href: 'http://example.com/en-US'}
177
+ ]
178
+ });
179
+ window.navigator.languages = ['de-DE'];
180
+
181
+ const result = getPreferredLanguage();
182
+ expect(result).to.have.property('current', 'en');
183
+
184
+ expect(result.preferred).to.have.property('full', "de");
185
+ expect(result.preferred).to.have.property('base', "de");
186
+ expect(result.preferred).to.have.property('label', "Deutsch");
187
+ expect(result.preferred).to.have.property('href', "http://example.com/de-DE");
188
+
189
+ });
190
+
191
+ // 6) Document lang 'de-DE', user prefers ['en-US', 'en'], link tags with 'en-US' & 'en-GB' => best match = 'en-US'
192
+ it('should return best match = "en-US" for user preferences [en-US, en]', () => {
193
+ setupDOM({
194
+ htmlLang: 'de-DE',
195
+ linkHreflangs: [
196
+ {hreflang: 'en-US', href: 'http://example.com/en-US'},
197
+ {hreflang: 'en-GB', href: 'http://example.com/en-GB'}
198
+ ]
199
+ });
200
+ window.navigator.languages = ['en-US', 'en'];
201
+
202
+ const result = getPreferredLanguage();
203
+ expect(result).to.have.property('current', 'de-DE');
204
+ expect(result.preferred).to.have.property('full', "en-US");
205
+ expect(result.preferred).to.have.property('base', "en");
206
+ expect(result.preferred).to.have.property('label', "English (United States)");
207
+ expect(result.preferred).to.have.property('href', "http://example.com/en-US");
208
+
209
+ });
210
+
211
+ // 7) Document lang 'de-DE', user prefers ['de','en'], link tags with 'de-DE' & 'en-US' => best match = 'de-DE' (baseLang = 'de')
212
+ it('should return best match = "de-DE" when user preferences include its base language "de"', () => {
213
+ setupDOM({
214
+ htmlLang: 'de-DE',
215
+ linkHreflangs: [
216
+ {hreflang: 'de-DE', href: 'http://example.com/de-DE'},
217
+ {hreflang: 'en-US', href: 'http://example.com/en-US'}
218
+ ]
219
+ });
220
+ window.navigator.languages = ['de', 'en'];
221
+
222
+ const result = getPreferredLanguage();
223
+ expect(result).to.have.property('current', 'de-DE');
224
+
225
+ expect(result.preferred).to.have.property('full', "de-DE");
226
+ expect(result.preferred).to.have.property('base', "de");
227
+ expect(result.preferred).to.have.property('label', "Deutsch (Deutschland)");
228
+ expect(result.preferred).to.have.property('href', "http://example.com/de-DE");
229
+
230
+ });
231
+
232
+ // 8) Multiple link tags share the same weight => the first in the sorted array should be returned
233
+ it('should return the first item when multiple link tags have the same weight', () => {
234
+ setupDOM({
235
+ htmlLang: 'en',
236
+ linkHreflangs: [
237
+ {hreflang: 'fr', href: 'http://example.com/fr'},
238
+ {hreflang: 'es', href: 'http://example.com/es'}
239
+ ]
240
+ });
241
+ // Neither "fr" nor "es" is in the user preferences => both have weight = 1
242
+ window.navigator.languages = ['de'];
243
+
244
+ const result = getPreferredLanguage();
245
+ // Both 'fr' and 'es' have weight 1. After sorting, 'fr' appears first (as it was added first).
246
+ expect(result.available[0].fullLang).to.equal('fr');
247
+ expect(result.available[1].fullLang).to.equal('es');
248
+ expect(result.offerable).to.be.undefined;
249
+ expect(result).to.have.property('message', 'No available languages match the user\'s preferences.');
250
+ });
251
+
252
+ // 9) Check that bestURL is returned if a best match is found
253
+ it('should include bestURL when a best match is found', () => {
254
+ setupDOM({
255
+ htmlLang: 'fr',
256
+ linkHreflangs: [
257
+ {hreflang: 'fr-FR', href: 'http://example.com/fr-FR'},
258
+ {hreflang: 'de-DE', href: 'http://example.com/de-DE'}
259
+ ]
260
+ });
261
+ window.navigator.languages = ['fr-FR', 'de-DE'];
262
+
263
+ const result = getPreferredLanguage();
264
+
265
+ expect(result.preferred).to.have.property('full', "fr-FR");
266
+ expect(result.preferred).to.have.property('base', "fr");
267
+ expect(result.preferred).to.have.property('label', "Français (France)");
268
+ expect(result.preferred).to.have.property('href', "http://example.com/fr-FR");
269
+
270
+ });
271
+
272
+ // 10) Check presence of availableLanguages array, ensuring it has weight info
273
+ it('should return availableLanguages with weight for each link', () => {
274
+ setupDOM({
275
+ htmlLang: 'en-GB',
276
+ linkHreflangs: [
277
+ {hreflang: 'en-GB', href: 'http://example.com/en-GB'},
278
+ {hreflang: 'en-US', href: 'http://example.com/en-US'}
279
+ ]
280
+ });
281
+ window.navigator.languages = ['en-GB', 'en', 'en-US'];
282
+
283
+ const result = getPreferredLanguage();
284
+ expect(result.available).to.be.an('array').with.lengthOf(2);
285
+ result.available.forEach(item => {
286
+ expect(item).to.have.property('weight');
287
+ expect(item).to.have.property('fullLang');
288
+ expect(item).to.have.property('baseLang');
289
+ expect(item).to.have.property('href');
290
+ });
291
+ });
292
+
293
+ });
294
+
295
+ })
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("3.96.1")
10
+ monsterVersion = new Version("3.96.2")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -4,7 +4,27 @@ import {Formatter} from "../../../source/text/formatter.mjs";
4
4
 
5
5
  describe('Formatter', function () {
6
6
 
7
- // https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/47
7
+ // https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/275
8
+ describe('change empty handling', function () {
9
+
10
+ it('modification 1', function () {
11
+
12
+ const formatter = new Formatter({
13
+
14
+ a: null,
15
+ b: undefined,
16
+ c : 0
17
+
18
+
19
+ })
20
+
21
+ expect(formatter.format("${a | tostring}")).to.be.equal('null');
22
+ expect(formatter.format("${b | tostring}")).to.be.equal('undefined');
23
+ expect(formatter.format("${c | tostring}")).to.be.equal('0');
24
+ })
25
+ })
26
+
27
+ // https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/47
8
28
  describe('examples', function () {
9
29
 
10
30
  it('rfc example should run', function () {
@@ -9,8 +9,8 @@
9
9
  </head>
10
10
  <body>
11
11
  <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
12
- <h1 style='margin-bottom: 0.1em;'>Monster 3.96.1</h1>
13
- <div id="lastupdate" style='font-size:0.7em'>last update Di 31. Dez 23:30:20 CET 2024</div>
12
+ <h1 style='margin-bottom: 0.1em;'>Monster 3.96.2</h1>
13
+ <div id="lastupdate" style='font-size:0.7em'>last update Fr 3. Jan 15:17:06 CET 2025</div>
14
14
  </div>
15
15
  <div id="mocha-errors"
16
16
  style="color: red;font-weight: bold;display: flex;align-items: center;justify-content: center;flex-direction: column;margin:20px;"></div>