@schukai/monster 3.55.1 → 3.55.2

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
@@ -1,4 +1,10 @@
1
1
 
2
+ ## [3.55.2] - 2024-01-22
3
+
4
+ ### Bug Fixes
5
+
6
+ - double fetch [#134](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/134)
7
+
2
8
  ## [3.55.1] - 2024-01-22
3
9
 
4
10
  ### Bug Fixes
@@ -8,6 +14,7 @@
8
14
  - check parameter [#132](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/132)
9
15
  ### Changes
10
16
 
17
+ - release and publish to npm new version 3.55.1
11
18
  - doc
12
19
  - lint + formatt
13
20
  - update nixos to 23-11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schukai/monster",
3
- "version": "3.55.1",
3
+ "version": "3.55.2",
4
4
  "description": "Monster is a simple library for creating fast, robust and lightweight websites.",
5
5
  "keywords": [
6
6
  "framework",
@@ -3,25 +3,25 @@
3
3
  * SPDX-License-Identifier: AGPL-3.0
4
4
  */
5
5
 
6
- import { addAttributeToken } from "../../../dom/attributes.mjs";
7
- import { ATTRIBUTE_ERRORMESSAGE } from "../../../dom/constants.mjs";
8
- import { Datasource, dataSourceSymbol } from "../datasource.mjs";
9
- import { DatasourceStyleSheet } from "../stylesheet/datasource.mjs";
10
- import { instanceSymbol } from "../../../constants.mjs";
6
+ import {addAttributeToken} from "../../../dom/attributes.mjs";
7
+ import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
8
+ import {Datasource, dataSourceSymbol} from "../datasource.mjs";
9
+ import {DatasourceStyleSheet} from "../stylesheet/datasource.mjs";
10
+ import {instanceSymbol} from "../../../constants.mjs";
11
11
  import {
12
- assembleMethodSymbol,
13
- registerCustomElement,
12
+ assembleMethodSymbol,
13
+ registerCustomElement,
14
14
  } from "../../../dom/customelement.mjs";
15
- import { RestAPI } from "../../../data/datasource/server/restapi.mjs";
16
- import { Formatter } from "../../../text/formatter.mjs";
17
- import { clone } from "../../../util/clone.mjs";
18
- import { validateBoolean } from "../../../types/validate.mjs";
19
- import { findElementWithIdUpwards } from "../../../dom/util.mjs";
20
- import { Observer } from "../../../types/observer.mjs";
21
- import { Pathfinder } from "../../../data/pathfinder.mjs";
22
- import { fireCustomEvent } from "../../../dom/events.mjs";
15
+ import {RestAPI} from "../../../data/datasource/server/restapi.mjs";
16
+ import {Formatter} from "../../../text/formatter.mjs";
17
+ import {clone} from "../../../util/clone.mjs";
18
+ import {validateBoolean} from "../../../types/validate.mjs";
19
+ import {findElementWithIdUpwards} from "../../../dom/util.mjs";
20
+ import {Observer} from "../../../types/observer.mjs";
21
+ import {Pathfinder} from "../../../data/pathfinder.mjs";
22
+ import {fireCustomEvent} from "../../../dom/events.mjs";
23
23
 
24
- export { Rest };
24
+ export {Rest};
25
25
 
26
26
  /**
27
27
  * @private
@@ -29,12 +29,21 @@ export { Rest };
29
29
  */
30
30
  const intersectionObserverHandlerSymbol = Symbol("intersectionObserverHandler");
31
31
 
32
+ /**
33
+ * @private
34
+ * Original at source/components/datatable/datasource/rest.mjs
35
+ * @type {symbol}
36
+ */
37
+ const rawDataSymbol = Symbol.for(
38
+ "@schukai/monster/data/datasource/server/restapi/rawdata",
39
+ );
40
+
32
41
  /**
33
42
  * @private
34
43
  * @type {symbol}
35
44
  */
36
45
  const intersectionObserverObserverSymbol = Symbol(
37
- "intersectionObserverObserver",
46
+ "intersectionObserverObserver",
38
47
  );
39
48
 
40
49
  /**
@@ -63,23 +72,23 @@ const filterObserverSymbol = Symbol("filterObserver");
63
72
  * @summary A rest api datasource
64
73
  */
65
74
  class Rest extends Datasource {
66
- /**
67
- * the constructor of the class
68
- */
69
- constructor() {
70
- super();
71
- this[dataSourceSymbol] = new RestAPI();
72
- }
73
-
74
- /**
75
- * This method is called by the `instanceof` operator.
76
- * @returns {symbol}
77
- */
78
- static get [instanceSymbol]() {
79
- return Symbol.for("@schukai/monster/components/datasource/rest@@instance");
80
- }
81
-
82
- /**
75
+ /**
76
+ * the constructor of the class
77
+ */
78
+ constructor() {
79
+ super();
80
+ this[dataSourceSymbol] = new RestAPI();
81
+ }
82
+
83
+ /**
84
+ * This method is called by the `instanceof` operator.
85
+ * @returns {symbol}
86
+ */
87
+ static get [instanceSymbol]() {
88
+ return Symbol.for("@schukai/monster/components/datasource/rest@@instance");
89
+ }
90
+
91
+ /**
83
92
  * To set the options via the html tag the attribute `data-monster-options` must be used.
84
93
  * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
85
94
  *
@@ -98,7 +107,8 @@ class Rest extends Datasource {
98
107
  * @property {Object} datatable Datatable definitions
99
108
  * @property {string} datatable.id The id of the datatable control
100
109
  * @property {Object} response Response definitions
101
- * @property {string} response.errorMessagePath The path to the error message in the response
110
+ * @property {Object} response.path Path definitions (changed in 3.56.0)
111
+ * @property {string} response.path.message Path to the message (changed in 3.56.0)
102
112
  * @property {Object} read Read configuration
103
113
  * @property {string} read.url The url of the rest api
104
114
  * @property {string} read.method The method of the rest api
@@ -109,312 +119,346 @@ class Rest extends Datasource {
109
119
  * @property {Object} write Write configuration
110
120
 
111
121
  */
112
- get defaults() {
113
- const restOptions = new RestAPI().defaults;
114
-
115
- restOptions.read.parameters = {
116
- filter: undefined,
117
- oderBy: undefined,
118
- page: "1",
119
- };
120
-
121
- return Object.assign({}, super.defaults, restOptions, {
122
- templates: {
123
- main: getTemplate(),
124
- },
125
-
126
- features: {
127
- autoInit: true,
128
- filter: false,
129
- },
130
-
131
- autoInit: {
132
- intersectionObserver: false,
133
- oneTime: true,
134
- },
135
-
136
- filter: {
137
- id: undefined,
138
- },
139
-
140
- datatable: {
141
- id: undefined,
142
- },
143
-
144
- response: {
145
- errorMessagePath: "sys.message",
146
- },
147
- });
148
- }
149
-
150
- /**
151
- *
152
- * @param {string} page
153
- * @param {string} query
154
- * @param {string} orderBy
155
- * @returns {Monster.Components.Datatable.Datasource.Rest}
156
- */
157
- setParameters({ page, query, orderBy }) {
158
- const parameters = this.getOption("read.parameters");
159
- if (query !== undefined) {
160
- parameters.query = `${query}`;
161
- parameters.page = "1";
162
- }
163
-
164
- // after a query the page is set to 1, so if the page is not set, it is set to 1
165
- if (page !== undefined) parameters.page = `${page}`;
166
- if (orderBy !== undefined) parameters.order = `${orderBy}`;
167
- this.setOption("read.parameters", parameters);
168
- return this;
169
- }
170
-
171
- /**
172
- *
173
- * @return {Monster.Components.Form.Form}
174
- */
175
- [assembleMethodSymbol]() {
176
- super[assembleMethodSymbol]();
177
-
178
- initEventHandler.call(this);
179
- initAutoInit.call(this);
180
- }
181
-
182
- /**
183
- * @deprecated 2023-06-25
184
- * @returns {Promise<never>|*}
185
- */
186
- reload() {
187
- return this.fetch();
188
- }
189
-
190
- /**
191
- * Fetches the data from the rest api
192
- * @returns {Promise<never>|*}
193
- */
194
- fetch() {;
195
- const opt = clone(this.getOption("read"));
196
- this[dataSourceSymbol].setOption("read", opt);
197
-
198
- let url = this.getOption("read.url");
199
- const formatter = new Formatter(this.getOption("read.parameters"));
200
-
201
- if (!url) {
202
- return Promise.reject(new Error("No url defined"));
203
- }
204
-
205
- url = formatter.format(url);
206
-
207
- this[dataSourceSymbol].setOption("read.url", url);
208
-
209
- return new Promise((resolve, reject) => {
210
- fireCustomEvent(this, "monster-datasource-fetch", {
211
- datasource: this,
212
- });
213
-
214
- setTimeout(() => {
215
- this[dataSourceSymbol]
216
- .read()
217
- .then((response) => {
218
- fireCustomEvent(this, "monster-datasource-fetched", {
219
- datasource: this,
220
- });
221
-
222
- resolve(response);
223
- })
224
- .catch((error) => {
225
- fireCustomEvent(this, "monster-datasource-error", {
226
- error: error,
227
- });
228
-
229
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
230
- reject(error);
231
- });
232
- }, 0);
233
- });
234
- }
235
-
236
- /**
237
- *
238
- * @return {CSSStyleSheet[]}
239
- */
240
- static getCSSStyleSheet() {
241
- return [DatasourceStyleSheet];
242
- }
243
-
244
- /**
245
- * @private
246
- * @return {string}
247
- */
248
- static getTag() {
249
- return "monster-datasource-rest";
250
- }
251
-
252
- /**
253
- * This method activates the intersection observer manually.
254
- * For this purpose, the option `autoInit.intersectionObserver` must be set to `false`.
255
- *
256
- * @returns {Monster.Components.Datatable.Datasource.Rest}
257
- */
258
- initIntersectionObserver() {
259
- initIntersectionObserver.call(this);
260
- return this;
261
- }
262
-
263
- /**
264
- * @private
265
- */
266
- connectedCallback() {
267
- super.connectedCallback();
268
-
269
- setTimeout(() => {
270
- if (this.getOption("features.filter", false) === true) {
271
- initFilter.call(this);
272
- }
273
- }, 0);
274
- }
275
-
276
- /**
277
- * @private
278
- */
279
- disconnectedCallback() {
280
- super.disconnectedCallback();
281
- removeFilter.call(this);
282
- }
122
+ get defaults() {
123
+ const restOptions = new RestAPI().defaults;
124
+
125
+ restOptions.read.parameters = {
126
+ filter: undefined,
127
+ oderBy: undefined,
128
+ page: "1",
129
+ };
130
+
131
+ return Object.assign({}, super.defaults, restOptions, {
132
+ templates: {
133
+ main: getTemplate(),
134
+ },
135
+
136
+ features: {
137
+ autoInit: false,
138
+ filter: false,
139
+ },
140
+
141
+ autoInit: {
142
+ intersectionObserver: false,
143
+ oneTime: true,
144
+ },
145
+
146
+ filter: {
147
+ id: undefined,
148
+ },
149
+
150
+ datatable: {
151
+ id: undefined,
152
+ },
153
+
154
+ response: {
155
+ path: {
156
+ message: "sys.message",
157
+ code: "sys.code",
158
+ }
159
+ },
160
+ });
161
+ }
162
+
163
+ /**
164
+ *
165
+ * @param {string} page
166
+ * @param {string} query
167
+ * @param {string} orderBy
168
+ * @returns {Monster.Components.Datatable.Datasource.Rest}
169
+ */
170
+ setParameters({page, query, orderBy}) {
171
+ const parameters = this.getOption("read.parameters");
172
+ if (query !== undefined) {
173
+ parameters.query = `${query}`;
174
+ parameters.page = "1";
175
+ }
176
+
177
+ // after a query the page is set to 1, so if the page is not set, it is set to 1
178
+ if (page !== undefined) parameters.page = `${page}`;
179
+ if (orderBy !== undefined) parameters.order = `${orderBy}`;
180
+ this.setOption("read.parameters", parameters);
181
+ return this;
182
+ }
183
+
184
+ /**
185
+ *
186
+ * @return {Monster.Components.Form.Form}
187
+ */
188
+ [assembleMethodSymbol]() {
189
+ super[assembleMethodSymbol]();
190
+
191
+ initEventHandler.call(this);
192
+ initAutoInit.call(this);
193
+ }
194
+
195
+ /**
196
+ * @deprecated 2023-06-25
197
+ * @returns {Promise<never>|*}
198
+ */
199
+ reload() {
200
+ return this.fetch();
201
+ }
202
+
203
+ /**
204
+ * Fetches the data from the rest api
205
+ * @returns {Promise<never>|*}
206
+ */
207
+ fetch() {
208
+ const opt = clone(this.getOption("read"));
209
+ this[dataSourceSymbol].setOption("read", opt);
210
+
211
+ let url = this.getOption("read.url");
212
+ const formatter = new Formatter(this.getOption("read.parameters"));
213
+
214
+ if (!url) {
215
+ return Promise.reject(new Error("No url defined"));
216
+ }
217
+
218
+ url = formatter.format(url);
219
+
220
+ this[dataSourceSymbol].setOption("read.url", url);
221
+
222
+ return new Promise((resolve, reject) => {
223
+ fireCustomEvent(this, "monster-datasource-fetch", {
224
+ datasource: this,
225
+ });
226
+
227
+ setTimeout(() => {
228
+ this[dataSourceSymbol]
229
+ .read()
230
+ .then((response) => {
231
+ fireCustomEvent(this, "monster-datasource-fetched", {
232
+ datasource: this,
233
+ });
234
+
235
+ resolve(response);
236
+ })
237
+ .catch((error) => {
238
+ fireCustomEvent(this, "monster-datasource-error", {
239
+ error: error,
240
+ });
241
+
242
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
243
+ reject(error);
244
+ });
245
+ }, 0);
246
+ });
247
+ }
248
+
249
+ /**
250
+ *
251
+ * @return {CSSStyleSheet[]}
252
+ */
253
+ static getCSSStyleSheet() {
254
+ return [DatasourceStyleSheet];
255
+ }
256
+
257
+ /**
258
+ * @private
259
+ * @return {string}
260
+ */
261
+ static getTag() {
262
+ return "monster-datasource-rest";
263
+ }
264
+
265
+ /**
266
+ * This method activates the intersection observer manually.
267
+ * For this purpose, the option `autoInit.intersectionObserver` must be set to `false`.
268
+ *
269
+ * @returns {Monster.Components.Datatable.Datasource.Rest}
270
+ */
271
+ initIntersectionObserver() {
272
+ initIntersectionObserver.call(this);
273
+ return this;
274
+ }
275
+
276
+ /**
277
+ * @private
278
+ */
279
+ connectedCallback() {
280
+ super.connectedCallback();
281
+
282
+ setTimeout(() => {
283
+ if (this.getOption("features.filter", false) === true) {
284
+ initFilter.call(this);
285
+ }
286
+ }, 0);
287
+ }
288
+
289
+ /**
290
+ * @private
291
+ */
292
+ disconnectedCallback() {
293
+ super.disconnectedCallback();
294
+ removeFilter.call(this);
295
+ }
283
296
  }
284
297
 
285
298
  /**
286
299
  * @private
287
300
  */
288
301
  function removeFilter() {
289
- const filterID = this.getOption("filter.id", undefined);
290
- if (!filterID) return;
302
+ const filterID = this.getOption("filter.id", undefined);
303
+ if (!filterID) return;
291
304
 
292
- const filterControl = findElementWithIdUpwards(this, filterID);
305
+ const filterControl = findElementWithIdUpwards(this, filterID);
293
306
 
294
- if (filterControl && this[filterObserverSymbol]) {
295
- filterControl?.detachObserver(this[filterObserverSymbol]);
296
- }
307
+ if (filterControl && this[filterObserverSymbol]) {
308
+ filterControl?.detachObserver(this[filterObserverSymbol]);
309
+ }
297
310
  }
298
311
 
299
312
  /**
300
313
  * @private
301
314
  */
302
315
  function initFilter() {
303
- const filterID = this.getOption("filter.id", undefined);
304
-
305
- if (!filterID)
306
- throw new Error("filter feature is enabled but no filter id is defined");
307
-
308
- const filterControl = findElementWithIdUpwards(this, filterID);
309
- if (!filterControl)
310
- throw new Error(
311
- "filter feature is enabled but no filter control with id " +
312
- filterID +
313
- " is found",
314
- );
315
-
316
- this[filterObserverSymbol] = new Observer(() => {
317
- const query = filterControl.getOption("query", undefined);
318
- this.setParameters({ query: query });
319
- this.fetch()
320
- .then((response) => {
321
- if (!(response instanceof Response)) {
322
- throw new Error("Response is not an instance of Response");
323
- }
324
-
325
- if (response?.ok === true) {
326
- this.dispatchEvent(new CustomEvent("reload", { bubbles: true }));
327
- filterControl?.showSuccess();
328
- }
329
-
330
- response
331
- .json()
332
- .then((json) => {
333
- const path = new Pathfinder(json);
334
- const error = path.getVia(
335
- this.getOption("response.errorMessagePath"),
336
- );
337
- if (error) {
338
- filterControl?.showFailureMessage(error);
339
- return;
340
- }
341
-
342
- filterControl?.showFailureMessage(e.message);
343
- })
344
- .catch((e) => {
345
- filterControl?.showFailureMessage(e.message);
346
- });
347
- })
348
- .catch((e) => {
349
- this.dispatchEvent(
350
- new CustomEvent("error", { bubbles: true, detail: e }),
351
- );
352
-
353
- if (!(e instanceof Error)) {
354
- e = new Error(e);
355
- }
356
-
357
- filterControl?.showFailureMessage(e.message);
358
- return Promise.reject(e);
359
- });
360
- });
361
-
362
- filterControl.attachObserver(this[filterObserverSymbol]);
316
+ const filterID = this.getOption("filter.id", undefined);
317
+
318
+ if (!filterID)
319
+ throw new Error("filter feature is enabled but no filter id is defined");
320
+
321
+ const filterControl = findElementWithIdUpwards(this, filterID);
322
+ if (!filterControl)
323
+ throw new Error(
324
+ "filter feature is enabled but no filter control with id " +
325
+ filterID +
326
+ " is found",
327
+ );
328
+
329
+ this[filterObserverSymbol] = new Observer(() => {
330
+ const query = filterControl.getOption("query", undefined);
331
+ this.setParameters({query: query});
332
+ this.fetch()
333
+ .then((response) => {
334
+ if (!(response instanceof Response)) {
335
+ throw new Error("Response is not an instance of Response");
336
+ }
337
+
338
+ if (response?.ok === true) {
339
+ this.dispatchEvent(new CustomEvent("reload", {bubbles: true}));
340
+ filterControl?.showSuccess();
341
+ }
342
+
343
+ if (response.bodyUsed === true) {
344
+ return handleIntersectionObserver.call(this, response[rawDataSymbol], response, filterControl);
345
+ }
346
+
347
+ response
348
+ .text()
349
+ .then((jsonAsText) => {
350
+ let json;
351
+ try {
352
+ json = JSON.parse(jsonAsText);
353
+ } catch (e) {
354
+ let message = e instanceof Error ? e.message : `${e}`;
355
+ filterControl?.showFailureMessage(message);
356
+ return Promise.reject(e);
357
+ }
358
+
359
+ return handleIntersectionObserver.call(this,json, response, filterControl);
360
+
361
+
362
+ })
363
+ .catch((e) => {
364
+ filterControl?.showFailureMessage(e.message);
365
+ });
366
+ })
367
+ .catch((e) => {
368
+ this.dispatchEvent(
369
+ new CustomEvent("error", {bubbles: true, detail: e}),
370
+ );
371
+
372
+ if (!(e instanceof Error)) {
373
+ e = new Error(e);
374
+ }
375
+
376
+ filterControl?.showFailureMessage(e.message);
377
+ return Promise.reject(e);
378
+ });
379
+ });
380
+
381
+ filterControl.attachObserver(this[filterObserverSymbol]);
382
+ }
383
+
384
+ function handleIntersectionObserver(json, response, filterControl) {
385
+
386
+ const path = new Pathfinder(json);
387
+
388
+ const codePath = this.getOption("response.path.code");
389
+
390
+ if (path.exists(codePath)) {
391
+ const code = `${path.getVia(codePath)}`;
392
+ if (code && code === "200") {
393
+ filterControl?.showSuccess();
394
+ return Promise.resolve(response);
395
+ }
396
+
397
+ const messagePath = this.getOption("response.path.message");
398
+ if (path.exists(messagePath)) {
399
+ const message = path.getVia(messagePath);
400
+ filterControl?.showFailureMessage(message);
401
+ return Promise.reject(new Error(message));
402
+ }
403
+
404
+ return Promise.reject(new Error("Response code is not 200"));
405
+ }
363
406
  }
364
407
 
365
408
  /**
366
409
  * @private
367
410
  */
368
411
  function initAutoInit() {
369
- const autoInit = this.getOption("features.autoInit");
370
- validateBoolean(autoInit);
412
+ const autoInit = this.getOption("features.autoInit");
413
+ validateBoolean(autoInit);
371
414
 
372
- if (autoInit !== true) return;
415
+ if (autoInit !== true) return;
373
416
 
374
- if (this.getOption("autoInit.intersectionObserver") === true) {
375
- initIntersectionObserver.call(this);
376
- return;
377
- }
417
+ if (this.getOption("autoInit.intersectionObserver") === true) {
418
+ initIntersectionObserver.call(this);
419
+ return;
420
+ }
378
421
 
379
- setTimeout(() => {
380
- this.fetch().catch(() => {});
381
- }, 0);
422
+ setTimeout(() => {
423
+ this.fetch().catch(() => {
424
+ });
425
+ }, 0);
382
426
  }
383
427
 
384
428
  function initEventHandler() {
385
- this[intersectionObserverHandlerSymbol] = (entries) => {
386
- entries.forEach((entry) => {
387
- if (entry.isIntersecting) {
388
- if (entry.intersectionRatio > 0) {
389
- this.fetch();
390
- }
391
-
392
- // only load once
393
- if (
394
- this.getOption("autoInit.oneTime") === true &&
395
- this[intersectionObserverObserverSymbol] !== undefined
396
- ) {
397
- this[intersectionObserverObserverSymbol].unobserve(this);
398
- }
399
- }
400
- });
401
- };
429
+ this[intersectionObserverHandlerSymbol] = (entries) => {
430
+ entries.forEach((entry) => {
431
+ if (entry.isIntersecting) {
432
+ if (entry.intersectionRatio > 0) {
433
+ this.fetch();
434
+ }
435
+
436
+ // only load once
437
+ if (
438
+ this.getOption("autoInit.oneTime") === true &&
439
+ this[intersectionObserverObserverSymbol] !== undefined
440
+ ) {
441
+ this[intersectionObserverObserverSymbol].unobserve(this);
442
+ }
443
+ }
444
+ });
445
+ };
402
446
  }
403
447
 
404
448
  function initIntersectionObserver() {
405
- this.classList.add("intersection-observer");
406
-
407
- const options = {
408
- root: null,
409
- rootMargin: "0px",
410
- threshold: 0.1,
411
- };
412
-
413
- this[intersectionObserverObserverSymbol] = new IntersectionObserver(
414
- this[intersectionObserverHandlerSymbol],
415
- options,
416
- );
417
- this[intersectionObserverObserverSymbol].observe(this);
449
+ this.classList.add("intersection-observer");
450
+
451
+ const options = {
452
+ root: null,
453
+ rootMargin: "0px",
454
+ threshold: 0.1,
455
+ };
456
+
457
+ this[intersectionObserverObserverSymbol] = new IntersectionObserver(
458
+ this[intersectionObserverHandlerSymbol],
459
+ options,
460
+ );
461
+ this[intersectionObserverObserverSymbol].observe(this);
418
462
  }
419
463
 
420
464
  /**
@@ -422,8 +466,8 @@ function initIntersectionObserver() {
422
466
  * @return {string}
423
467
  */
424
468
  function getTemplate() {
425
- // language=HTML
426
- return `
469
+ // language=HTML
470
+ return `
427
471
  <slot></slot>`;
428
472
  }
429
473
 
@@ -254,7 +254,7 @@ class Filter extends CustomElement {
254
254
  },
255
255
  },
256
256
 
257
- query: "",
257
+ query: undefined,
258
258
  defaultQuery: "",
259
259
  });
260
260
  }
@@ -441,7 +441,6 @@ function initFilter() {
441
441
  */
442
442
  function escapeAttributeValue(input) {
443
443
  if (input === undefined || input === null) {
444
- debugger;
445
444
  return input;
446
445
  }
447
446
 
@@ -686,11 +685,16 @@ function initEventHandler() {
686
685
  }
687
686
 
688
687
  function initTabEvents() {
688
+
689
689
  this[filterTabElementSymbol].addEventListener(
690
690
  "monster-tab-changed",
691
691
  (event) => {
692
+
692
693
  const query = event?.detail?.data?.["data-monster-query"];
693
- this.setOption("query", query);
694
+ const q = this.getOption("query");
695
+ if (query !== q) {
696
+ this.setOption("query", query);
697
+ }
694
698
  },
695
699
  );
696
700
 
@@ -123,6 +123,7 @@ class RestAPI extends Server {
123
123
  * @throws {Error} the data cannot be read
124
124
  */
125
125
  read() {
126
+
126
127
  let init = this.getOption("read.init");
127
128
  if (!isObject(init)) init = {};
128
129
  if (!init["method"]) init["method"] = "GET";
@@ -465,7 +465,7 @@ class CustomElement extends HTMLElement {
465
465
  * @return {*}
466
466
  * @since 1.10.0
467
467
  */
468
- getOption(path, defaultValue) {
468
+ getOption(path, defaultValue=undefined) {
469
469
  let value;
470
470
 
471
471
  try {
@@ -152,7 +152,7 @@ function getMonsterVersion() {
152
152
  }
153
153
 
154
154
  /** don't touch, replaced by make with package.json version */
155
- monsterVersion = new Version("3.55.1");
155
+ monsterVersion = new Version("3.55.2");
156
156
 
157
157
  return monsterVersion;
158
158
  }
@@ -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.55.1")
10
+ monsterVersion = new Version("3.55.2")
11
11
 
12
12
  let m = getMonsterVersion();
13
13