@schukai/monster 3.55.1 → 3.55.2

Sign up to get free protection for your applications and to get access to all the features.
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