@schukai/monster 4.38.3 → 4.38.4

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.
@@ -14,18 +14,18 @@
14
14
 
15
15
  import { instanceSymbol } from "../../constants.mjs";
16
16
  import {
17
- findTargetElementFromEvent,
18
- fireCustomEvent,
17
+ findTargetElementFromEvent,
18
+ fireCustomEvent,
19
19
  } from "../../dom/events.mjs";
20
20
  import {
21
- findElementWithIdUpwards,
22
- findElementWithSelectorUpwards,
21
+ findElementWithIdUpwards,
22
+ findElementWithSelectorUpwards,
23
23
  } from "../../dom/util.mjs";
24
24
  import {
25
- assembleMethodSymbol,
26
- CustomElement,
27
- getSlottedElements,
28
- registerCustomElement,
25
+ assembleMethodSymbol,
26
+ CustomElement,
27
+ getSlottedElements,
28
+ registerCustomElement,
29
29
  } from "../../dom/customelement.mjs";
30
30
  import { ID } from "../../types/id.mjs";
31
31
  import { Settings } from "./filter/settings.mjs";
@@ -33,11 +33,11 @@ import { FilterStyleSheet } from "./stylesheet/filter.mjs";
33
33
  import { getDocument, getWindow } from "../../dom/util.mjs";
34
34
  import { getGlobal } from "../../types/global.mjs";
35
35
  import {
36
- isInstance,
37
- isFunction,
38
- isObject,
39
- isArray,
40
- isString,
36
+ isInstance,
37
+ isFunction,
38
+ isObject,
39
+ isArray,
40
+ isString,
41
41
  } from "../../types/is.mjs";
42
42
  import { Host } from "../host/host.mjs";
43
43
  import { addAttributeToken } from "../../dom/attributes.mjs";
@@ -47,17 +47,17 @@ import { Formatter } from "../../text/formatter.mjs";
47
47
  import { generateRangeComparisonExpression } from "../../text/util.mjs";
48
48
 
49
49
  import {
50
- parseBracketedKeyValueHash,
51
- createBracketedKeyValueHash,
50
+ parseBracketedKeyValueHash,
51
+ createBracketedKeyValueHash,
52
52
  } from "../../text/bracketed-key-value-hash.mjs";
53
53
  import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
54
54
  import { SpaceStyleSheet } from "../stylesheet/space.mjs";
55
55
  import { FormStyleSheet } from "../stylesheet/form.mjs";
56
56
 
57
57
  import {
58
- getStoredFilterConfigKey,
59
- getFilterConfigKey,
60
- parseDateInput,
58
+ getStoredFilterConfigKey,
59
+ getFilterConfigKey,
60
+ parseDateInput,
61
61
  } from "./filter/util.mjs";
62
62
 
63
63
  import "./filter/select.mjs";
@@ -110,7 +110,7 @@ const filterControlElementSymbol = Symbol("filterControlElement");
110
110
  * @type {symbol}
111
111
  */
112
112
  const filterSaveActionButtonElementSymbol = Symbol(
113
- "filterSaveActionButtonElement",
113
+ "filterSaveActionButtonElement",
114
114
  );
115
115
 
116
116
  /**
@@ -170,241 +170,241 @@ const hashChangeSymbol = Symbol("hashChange");
170
170
  * @summary The Filter component is used to show and handle the filter values.
171
171
  */
172
172
  class Filter extends CustomElement {
173
- /**
174
- *
175
- */
176
- constructor() {
177
- super();
178
- this[settingsSymbol] = new Settings();
179
-
180
- // debounce the hash change event if doSearch is called by click the search button
181
- this[hashChangeSymbol] = 0;
182
- }
183
-
184
- /**
185
- * This method is called by the `instanceof` operator.
186
- * @return {symbol}
187
- */
188
- static get [instanceSymbol]() {
189
- return Symbol.for("@schukai/monster/components/filter@@instance");
190
- }
191
-
192
- /**
193
- *
194
- * @param {string} message
195
- * @return {Filter}
196
- */
197
- showFailureMessage(message) {
198
- this[searchButtonElementSymbol].setState(
199
- "failed",
200
- this.getOption("timeouts.message", 4000),
201
- );
202
- this[searchButtonElementSymbol]
203
- .setMessage(message.toString())
204
- .showMessage(this.getOption("timeouts.message", 4000));
205
- return this;
206
- }
207
-
208
- /**
209
- *
210
- * @return {Filter}
211
- */
212
- resetFailureMessage() {
213
- this[searchButtonElementSymbol].hideMessage();
214
- this[searchButtonElementSymbol].removeState();
215
- return this;
216
- }
217
-
218
- /**
219
- *
220
- * @return {Filter}
221
- */
222
- showSuccess() {
223
- this[searchButtonElementSymbol].setState(
224
- "successful",
225
- this.getOption("timeouts.message", 4000),
226
- );
227
- return this;
228
- }
229
-
230
- /**
231
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
232
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
233
- *
234
- * The individual configuration values can be found in the table.
235
- *
236
- * @property {Object} templates Template definitions
237
- * @property {string} templates.main Main template
238
- * @property {Object} labels Label definitions
239
- * @property {string} labels.search Search button label
240
- * @property {string} labels.reset Reset button label
241
- * @property {string} labels.save Save button label
242
- * @property {string} labels.filter-name Filter name label
243
- * @property {string} labels.empty-query-and-no-default Empty query and no default query label
244
- * @property {string} labels.query-not-changed Query not changed label
245
- * @property {Object} formatter Formatter definitions
246
- * @property {Object} formatter.marker Marker definitions
247
- * @property {Object} formatter.marker.open Marker open
248
- * @property {Object} formatter.marker.close Marker close
249
- * @property {Object} features Feature definitions
250
- * @property {boolean} features.storedConfig Stored configuration, this replaces the setting `storedConfig.enabled` @since 3.97.0
251
- * @property {boolean} features.autoFilter Auto filter @since 3.100.0
252
- * @property {boolean} features.preventSameQuery Prevent same query @since 3.103.0
253
- * @property {Object} storedConfig Stored configuration
254
- * @property {boolean} storedConfig.enabled The store has been enabled, this option will no longer have any effect. @deprecated 20250101
255
- * @property {string} storedConfig.selector Selector
256
- * @property {Object} timeouts Timeout definitions
257
- * @property {number} timeouts.message Message timeout
258
- * @property {Object} queries Query definitions
259
- * @property {Function} queries.wrap Wrap callback
260
- * @property {Function} queries.join Join callback
261
- * @property {string} query Query
262
- * @property {string} defaultQuery Default query
263
- * @property {boolean} eventProcessing Event processing
264
- */
265
- get defaults() {
266
- return Object.assign({}, super.defaults, {
267
- templates: {
268
- main: getTemplate(),
269
- },
270
-
271
- formatter: {
272
- marker: {
273
- open: null,
274
- close: null,
275
- },
276
- },
277
-
278
- labels: getTranslations(),
279
-
280
- templateMapping: {
281
- "filter-save-label": null,
282
- "filter-name-label": name,
283
- },
284
-
285
- features: {
286
- storedConfig: false,
287
- autoFilter: true,
288
- preventSameQuery: false,
289
- },
290
-
291
- storedConfig: {
292
- enabled: true,
293
- selector: "",
294
- },
295
-
296
- timeouts: {
297
- message: 4000,
298
- },
299
-
300
- queries: {
301
- wrap: (value, definition) => {
302
- return value;
303
- },
304
- join: (queries) => {
305
- if (queries.length === 0) {
306
- return "";
307
- }
308
- return queries.join(" AND ");
309
- },
310
- },
311
-
312
- query: null,
313
- defaultQuery: null,
314
- eventProcessing: true,
315
-
316
- templateFormatter: {
317
- marker: {
318
- open: null,
319
- close: null,
320
- },
321
- i18n: true,
322
- },
323
- });
324
- }
325
-
326
- /**
327
- *
328
- * @return {string}
329
- */
330
- static getTag() {
331
- return "monster-datatable-filter";
332
- }
333
-
334
- /**
335
- * @return {FilterButton}
336
- * @fires monster-filter-initialized
337
- */
338
- [assembleMethodSymbol]() {
339
- const self = this;
340
-
341
- this.setOption(
342
- "templateMapping.filter-save-label",
343
- this.getOption("labels.save"),
344
- );
345
-
346
- this.setOption(
347
- "templateMapping.filter-name-label",
348
- this.getOption("labels.filter-name"),
349
- );
350
-
351
- super[assembleMethodSymbol]();
352
-
353
- initControlReferences.call(self);
354
- getWindow().requestAnimationFrame(() => {
355
- initEventHandler.call(self);
356
- });
357
-
358
- initFromConfig
359
- .call(self)
360
- .then(() => {})
361
- .catch((error) => {
362
- addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, error?.message);
363
- })
364
- .finally(() => {
365
- initFilter.call(self);
366
- updateFilterTabs.call(self);
367
-
368
- if (self.getOption("features.autoFilter") === true) {
369
- doSearch
370
- .call(self, { showEffect: false })
371
- .then(() => {
372
- fireCustomEvent(self, "monster-filter-initialized");
373
- })
374
- .catch(() => {});
375
- }
376
- });
377
- }
378
-
379
- /**
380
- *
381
- */
382
- connectedCallback() {
383
- super.connectedCallback();
384
- getWindow().addEventListener(
385
- "hashchange",
386
- this[locationChangeHandlerSymbol],
387
- );
388
- }
389
-
390
- /**
391
- *
392
- */
393
- disconnectedCallback() {
394
- super.disconnectedCallback();
395
-
396
- getWindow().removeEventListener(
397
- "hashchange",
398
- this[locationChangeHandlerSymbol],
399
- );
400
- }
401
-
402
- /**
403
- * @return {CSSStyleSheet[]}
404
- */
405
- static getCSSStyleSheet() {
406
- return [FilterStyleSheet, FormStyleSheet, ThemeStyleSheet, SpaceStyleSheet];
407
- }
173
+ /**
174
+ *
175
+ */
176
+ constructor() {
177
+ super();
178
+ this[settingsSymbol] = new Settings();
179
+
180
+ // debounce the hash change event if doSearch is called by click the search button
181
+ this[hashChangeSymbol] = 0;
182
+ }
183
+
184
+ /**
185
+ * This method is called by the `instanceof` operator.
186
+ * @return {symbol}
187
+ */
188
+ static get [instanceSymbol]() {
189
+ return Symbol.for("@schukai/monster/components/filter@@instance");
190
+ }
191
+
192
+ /**
193
+ *
194
+ * @param {string} message
195
+ * @return {Filter}
196
+ */
197
+ showFailureMessage(message) {
198
+ this[searchButtonElementSymbol].setState(
199
+ "failed",
200
+ this.getOption("timeouts.message", 4000),
201
+ );
202
+ this[searchButtonElementSymbol]
203
+ .setMessage(message.toString())
204
+ .showMessage(this.getOption("timeouts.message", 4000));
205
+ return this;
206
+ }
207
+
208
+ /**
209
+ *
210
+ * @return {Filter}
211
+ */
212
+ resetFailureMessage() {
213
+ this[searchButtonElementSymbol].hideMessage();
214
+ this[searchButtonElementSymbol].removeState();
215
+ return this;
216
+ }
217
+
218
+ /**
219
+ *
220
+ * @return {Filter}
221
+ */
222
+ showSuccess() {
223
+ this[searchButtonElementSymbol].setState(
224
+ "successful",
225
+ this.getOption("timeouts.message", 4000),
226
+ );
227
+ return this;
228
+ }
229
+
230
+ /**
231
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
232
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
233
+ *
234
+ * The individual configuration values can be found in the table.
235
+ *
236
+ * @property {Object} templates Template definitions
237
+ * @property {string} templates.main Main template
238
+ * @property {Object} labels Label definitions
239
+ * @property {string} labels.search Search button label
240
+ * @property {string} labels.reset Reset button label
241
+ * @property {string} labels.save Save button label
242
+ * @property {string} labels.filter-name Filter name label
243
+ * @property {string} labels.empty-query-and-no-default Empty query and no default query label
244
+ * @property {string} labels.query-not-changed Query not changed label
245
+ * @property {Object} formatter Formatter definitions
246
+ * @property {Object} formatter.marker Marker definitions
247
+ * @property {Object} formatter.marker.open Marker open
248
+ * @property {Object} formatter.marker.close Marker close
249
+ * @property {Object} features Feature definitions
250
+ * @property {boolean} features.storedConfig Stored configuration, this replaces the setting `storedConfig.enabled` @since 3.97.0
251
+ * @property {boolean} features.autoFilter Auto filter @since 3.100.0
252
+ * @property {boolean} features.preventSameQuery Prevent same query @since 3.103.0
253
+ * @property {Object} storedConfig Stored configuration
254
+ * @property {boolean} storedConfig.enabled The store has been enabled, this option will no longer have any effect. @deprecated 20250101
255
+ * @property {string} storedConfig.selector Selector
256
+ * @property {Object} timeouts Timeout definitions
257
+ * @property {number} timeouts.message Message timeout
258
+ * @property {Object} queries Query definitions
259
+ * @property {Function} queries.wrap Wrap callback
260
+ * @property {Function} queries.join Join callback
261
+ * @property {string} query Query
262
+ * @property {string} defaultQuery Default query
263
+ * @property {boolean} eventProcessing Event processing
264
+ */
265
+ get defaults() {
266
+ return Object.assign({}, super.defaults, {
267
+ templates: {
268
+ main: getTemplate(),
269
+ },
270
+
271
+ formatter: {
272
+ marker: {
273
+ open: null,
274
+ close: null,
275
+ },
276
+ },
277
+
278
+ labels: getTranslations(),
279
+
280
+ templateMapping: {
281
+ "filter-save-label": null,
282
+ "filter-name-label": name,
283
+ },
284
+
285
+ features: {
286
+ storedConfig: false,
287
+ autoFilter: true,
288
+ preventSameQuery: false,
289
+ },
290
+
291
+ storedConfig: {
292
+ enabled: true,
293
+ selector: "",
294
+ },
295
+
296
+ timeouts: {
297
+ message: 4000,
298
+ },
299
+
300
+ queries: {
301
+ wrap: (value, definition) => {
302
+ return value;
303
+ },
304
+ join: (queries) => {
305
+ if (queries.length === 0) {
306
+ return "";
307
+ }
308
+ return queries.join(" AND ");
309
+ },
310
+ },
311
+
312
+ query: null,
313
+ defaultQuery: null,
314
+ eventProcessing: true,
315
+
316
+ templateFormatter: {
317
+ marker: {
318
+ open: null,
319
+ close: null,
320
+ },
321
+ i18n: true,
322
+ },
323
+ });
324
+ }
325
+
326
+ /**
327
+ *
328
+ * @return {string}
329
+ */
330
+ static getTag() {
331
+ return "monster-datatable-filter";
332
+ }
333
+
334
+ /**
335
+ * @return {FilterButton}
336
+ * @fires monster-filter-initialized
337
+ */
338
+ [assembleMethodSymbol]() {
339
+ const self = this;
340
+
341
+ this.setOption(
342
+ "templateMapping.filter-save-label",
343
+ this.getOption("labels.save"),
344
+ );
345
+
346
+ this.setOption(
347
+ "templateMapping.filter-name-label",
348
+ this.getOption("labels.filter-name"),
349
+ );
350
+
351
+ super[assembleMethodSymbol]();
352
+
353
+ initControlReferences.call(self);
354
+ getWindow().requestAnimationFrame(() => {
355
+ initEventHandler.call(self);
356
+ });
357
+
358
+ initFromConfig
359
+ .call(self)
360
+ .then(() => {})
361
+ .catch((error) => {
362
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, error?.message);
363
+ })
364
+ .finally(() => {
365
+ initFilter.call(self);
366
+ updateFilterTabs.call(self);
367
+
368
+ if (self.getOption("features.autoFilter") === true) {
369
+ doSearch
370
+ .call(self, { showEffect: false })
371
+ .then(() => {
372
+ fireCustomEvent(self, "monster-filter-initialized");
373
+ })
374
+ .catch(() => {});
375
+ }
376
+ });
377
+ }
378
+
379
+ /**
380
+ *
381
+ */
382
+ connectedCallback() {
383
+ super.connectedCallback();
384
+ getWindow().addEventListener(
385
+ "hashchange",
386
+ this[locationChangeHandlerSymbol],
387
+ );
388
+ }
389
+
390
+ /**
391
+ *
392
+ */
393
+ disconnectedCallback() {
394
+ super.disconnectedCallback();
395
+
396
+ getWindow().removeEventListener(
397
+ "hashchange",
398
+ this[locationChangeHandlerSymbol],
399
+ );
400
+ }
401
+
402
+ /**
403
+ * @return {CSSStyleSheet[]}
404
+ */
405
+ static getCSSStyleSheet() {
406
+ return [FilterStyleSheet, FormStyleSheet, ThemeStyleSheet, SpaceStyleSheet];
407
+ }
408
408
  }
409
409
 
410
410
  /**
@@ -412,212 +412,215 @@ class Filter extends CustomElement {
412
412
  * @returns {object}
413
413
  */
414
414
  function getTranslations() {
415
- const locale = getLocaleOfDocument();
416
- switch (locale.language) {
417
- case "de":
418
- return {
419
- search: "Suchen",
420
- reset: "Zurücksetzen",
421
- save: "Speichern",
422
- "filter-name": "Filtername",
423
- "empty-query-and-no-default":
424
- "Die Abfrage ist leer und es gibt keine Standardabfrage.",
425
- "query-not-changed":
426
- "Die Suchanfrage hat sich nicht verändert, daher ist keine Suche erforderlich.",
427
- };
428
- case "fr":
429
- return {
430
- search: "Chercher",
431
- reset: "Réinitialiser",
432
- save: "Sauvegarder",
433
- "filter-name": "Nom du filtre",
434
- "empty-query-and-no-default":
435
- "La requête est vide et il n'y a pas de requête par défaut.",
436
- "query-not-changed":
437
- "La requête de recherche n'a pas changé, donc aucune recherche n'est nécessaire.",
438
- };
439
- case "es":
440
- return {
441
- search: "Buscar",
442
- reset: "Restablecer",
443
- save: "Guardar",
444
- "filter-name": "Nombre del filtro",
445
- "empty-query-and-no-default":
446
- "La consulta está vacía y no hay una consulta predeterminada.",
447
- "query-not-changed":
448
- "La solicitud de búsqueda no ha cambiado, por lo que no se requiere búsqueda.",
449
- };
450
- case "it":
451
- return {
452
- search: "Cerca",
453
- reset: "Reimposta",
454
- save: "Salva",
455
- "filter-name": "Nome del filtro",
456
- "empty-query-and-no-default":
457
- "La query è vuota e non c'è una query predefinita.",
458
- "query-not-changed":
459
- "La richiesta di ricerca non è cambiata, quindi non è necessaria una ricerca.",
460
- };
461
- case "pl":
462
- return {
463
- search: "Szukaj",
464
- reset: "Resetuj",
465
- save: "Zapisz",
466
- "filter-name": "Nazwa filtra",
467
- "empty-query-and-no-default":
468
- "Zapytanie jest puste i nie ma domyślnego zapytania.",
469
- "query-not-changed":
470
- "Żądanie wyszukiwania nie zmieniło się, więc wyszukiwanie nie jest wymagane.",
471
- };
472
- case "no":
473
- return {
474
- search: "Søk",
475
- reset: "Tilbakestill",
476
- save: "Lagre",
477
- "filter-name": "Filternavn",
478
- "empty-query-and-no-default":
479
- "Spørringen er tom og det er ingen standardspørring.",
480
- "query-not-changed":
481
- "Søkeforespørselen har ikke endret seg, så ingen søk er nødvendig.",
482
- };
483
- case "da":
484
- return {
485
- search: "Søg",
486
- reset: "Nulstil",
487
- save: "Gem",
488
- "filter-name": "Filternavn",
489
- "empty-query-and-no-default":
490
- "Forespørgslen er tom og der er ingen standardforespørgsel.",
491
- "query-not-changed":
492
- "Søgeanmodningen er ikke ændret, så ingen søgning er nødvendig.",
493
- };
494
- case "sv":
495
- return {
496
- search: "Sök",
497
- reset: "Återställ",
498
- save: "Spara",
499
- "filter-name": "Filternamn",
500
- "empty-query-and-no-default":
501
- "Förfrågan är tom och det finns ingen standardförfrågan.",
502
- "query-not-changed":
503
- "Sökförfrågan har inte ändrats, så ingen sökning krävs.",
504
- };
505
- case "nl":
506
- return {
507
- search: "Zoeken",
508
- reset: "Resetten",
509
- save: "Opslaan",
510
- "filter-name": "Filternaam",
511
- "empty-query-and-no-default":
512
- "De zoekopdracht is leeg en er is geen standaardzoekopdracht.",
513
- "query-not-changed":
514
- "De zoekopdracht is niet gewijzigd, dus zoeken is niet nodig.",
515
- };
516
- case "fi":
517
- return {
518
- search: "Haku",
519
- reset: "Palauta",
520
- save: "Tallenna",
521
- "filter-name": "Suodattimen nimi",
522
- "empty-query-and-no-default":
523
- "Hakukysely on tyhjä eikä oletushakua ole määritetty.",
524
- "query-not-changed":
525
- "Hakupyyntö ei ole muuttunut, joten hakua ei tarvita.",
526
- };
527
- case "cs":
528
- return {
529
- search: "Hledat",
530
- reset: "Resetovat",
531
- save: "Uložit",
532
- "filter-name": "Název filtru",
533
- "empty-query-and-no-default":
534
- "Dotaz je prázdný a není nastavena žádná výchozí hodnota.",
535
- "query-not-changed":
536
- "Dotaz na hledání se nezměnil, takže hledání není nutné.",
537
- };
538
- case "pt":
539
- return {
540
- search: "Buscar",
541
- reset: "Redefinir",
542
- save: "Salvar",
543
- "filter-name": "Nome do filtro",
544
- "empty-query-and-no-default":
545
- "A consulta está vazia e não há uma consulta padrão.",
546
- "query-not-changed":
547
- "A solicitação de pesquisa não foi alterada, portanto, nenhuma pesquisa é necessária.",
548
- };
549
- case "ru":
550
- return {
551
- search: "Поиск",
552
- reset: "Сброс",
553
- save: "Сохранить",
554
- "filter-name": "Имя фильтра",
555
- "empty-query-and-no-default": "Запрос пуст и нет запроса по умолчанию.",
556
- "query-not-changed":
557
- "Поисковый запрос не изменился, поэтому поиск не требуется.",
558
- };
559
- case "zh":
560
- return {
561
- search: "搜索",
562
- reset: "重置",
563
- save: "保存",
564
- "filter-name": "过滤器名称",
565
- "empty-query-and-no-default": "查询为空,且没有默认查询。",
566
- "query-not-changed": "搜索请求没有更改,因此不需要进行搜索。",
567
- };
568
- case "hi":
569
- return {
570
- search: "खोजें",
571
- reset: "रीसेट करें",
572
- save: "सहेजें",
573
- "filter-name": "फ़िल्टर नाम",
574
- "empty-query-and-no-default": "क्वेरी खाली है और कोई डिफ़ॉल्ट क्वेरी नहीं है।",
575
- "query-not-changed":
576
- "खोज अनुरोध में कोई बदलाव नहीं हुआ है, इसलिए खोज आवश्यक नहीं है।",
577
- };
578
- case "bn":
579
- return {
580
- search: "অনুসন্ধান",
581
- reset: "রিসেট",
582
- save: "সংরক্ষণ করুন",
583
- "filter-name": "ফিল্টারের নাম",
584
- "empty-query-and-no-default": "কোয়েরি খালি এবং কোনো ডিফল্ট কোয়েরি নেই।",
585
- "query-not-changed":
586
- "অনুসন্ধানের অনুরোধ পরিবর্তন হয়নি, তাই অনুসন্ধান প্রয়োজন নয়।",
587
- };
588
- case "ja":
589
- return {
590
- search: "検索",
591
- reset: "リセット",
592
- save: "保存",
593
- "filter-name": "フィルター名",
594
- "empty-query-and-no-default":
595
- "クエリが空で、デフォルトクエリがありません。",
596
- "query-not-changed": "検索リクエストに変更がないため、検索は不要です。",
597
- };
598
- case "pa":
599
- return {
600
- search: "ਖੋਜੋ",
601
- reset: "ਰੀਸੈੱਟ ਕਰੋ",
602
- save: "ਸੇਵ ਕਰੋ",
603
- "filter-name": "ਫਿਲਟਰ ਦਾ ਨਾਂ",
604
- "empty-query-and-no-default": "ਕੁਐਰੀ ਖਾਲੀ ਹੈ ਅਤੇ ਕੋਈ ਡਿਫੌਲਟ ਕੁਐਰੀ ਨਹੀਂ ਹੈ।",
605
- "query-not-changed":
606
- "ਖੋਜ ਦੀ ਬੇਨਤੀ ਵਿੱਚ ਕੋਈ ਤਬਦੀਲੀ ਨਹੀਂ ਆਈ ਹੈ, ਇਸ ਲਈ ਖੋਜ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।",
607
- };
608
- default:
609
- case "en":
610
- return {
611
- search: "Search",
612
- reset: "Reset",
613
- save: "Save",
614
- "filter-name": "Filter name",
615
- "empty-query-and-no-default":
616
- "The query is empty and there is no default query.",
617
- "query-not-changed":
618
- "The search request has not changed, so no search is required.",
619
- };
620
- }
415
+ const locale = getLocaleOfDocument();
416
+ switch (locale.language) {
417
+ case "de":
418
+ return {
419
+ search: "Suchen",
420
+ reset: "Zurücksetzen",
421
+ save: "Speichern",
422
+ "filter-name": "Filtername",
423
+ "empty-query-and-no-default":
424
+ "Die Abfrage ist leer und es gibt keine Standardabfrage.",
425
+ "query-not-changed":
426
+ "Die Suchanfrage hat sich nicht verändert, daher ist keine Suche erforderlich.",
427
+ };
428
+ case "fr":
429
+ return {
430
+ search: "Chercher",
431
+ reset: "Réinitialiser",
432
+ save: "Sauvegarder",
433
+ "filter-name": "Nom du filtre",
434
+ "empty-query-and-no-default":
435
+ "La requête est vide et il n'y a pas de requête par défaut.",
436
+ "query-not-changed":
437
+ "La requête de recherche n'a pas changé, donc aucune recherche n'est nécessaire.",
438
+ };
439
+ case "es":
440
+ return {
441
+ search: "Buscar",
442
+ reset: "Restablecer",
443
+ save: "Guardar",
444
+ "filter-name": "Nombre del filtro",
445
+ "empty-query-and-no-default":
446
+ "La consulta está vacía y no hay una consulta predeterminada.",
447
+ "query-not-changed":
448
+ "La solicitud de búsqueda no ha cambiado, por lo que no se requiere búsqueda.",
449
+ };
450
+ case "it":
451
+ return {
452
+ search: "Cerca",
453
+ reset: "Reimposta",
454
+ save: "Salva",
455
+ "filter-name": "Nome del filtro",
456
+ "empty-query-and-no-default":
457
+ "La query è vuota e non c'è una query predefinita.",
458
+ "query-not-changed":
459
+ "La richiesta di ricerca non è cambiata, quindi non è necessaria una ricerca.",
460
+ };
461
+ case "pl":
462
+ return {
463
+ search: "Szukaj",
464
+ reset: "Resetuj",
465
+ save: "Zapisz",
466
+ "filter-name": "Nazwa filtra",
467
+ "empty-query-and-no-default":
468
+ "Zapytanie jest puste i nie ma domyślnego zapytania.",
469
+ "query-not-changed":
470
+ "Żądanie wyszukiwania nie zmieniło się, więc wyszukiwanie nie jest wymagane.",
471
+ };
472
+ case "no":
473
+ return {
474
+ search: "Søk",
475
+ reset: "Tilbakestill",
476
+ save: "Lagre",
477
+ "filter-name": "Filternavn",
478
+ "empty-query-and-no-default":
479
+ "Spørringen er tom og det er ingen standardspørring.",
480
+ "query-not-changed":
481
+ "Søkeforespørselen har ikke endret seg, så ingen søk er nødvendig.",
482
+ };
483
+ case "da":
484
+ return {
485
+ search: "Søg",
486
+ reset: "Nulstil",
487
+ save: "Gem",
488
+ "filter-name": "Filternavn",
489
+ "empty-query-and-no-default":
490
+ "Forespørgslen er tom og der er ingen standardforespørgsel.",
491
+ "query-not-changed":
492
+ "Søgeanmodningen er ikke ændret, så ingen søgning er nødvendig.",
493
+ };
494
+ case "sv":
495
+ return {
496
+ search: "Sök",
497
+ reset: "Återställ",
498
+ save: "Spara",
499
+ "filter-name": "Filternamn",
500
+ "empty-query-and-no-default":
501
+ "Förfrågan är tom och det finns ingen standardförfrågan.",
502
+ "query-not-changed":
503
+ "Sökförfrågan har inte ändrats, så ingen sökning krävs.",
504
+ };
505
+ case "nl":
506
+ return {
507
+ search: "Zoeken",
508
+ reset: "Resetten",
509
+ save: "Opslaan",
510
+ "filter-name": "Filternaam",
511
+ "empty-query-and-no-default":
512
+ "De zoekopdracht is leeg en er is geen standaardzoekopdracht.",
513
+ "query-not-changed":
514
+ "De zoekopdracht is niet gewijzigd, dus zoeken is niet nodig.",
515
+ };
516
+ case "fi":
517
+ return {
518
+ search: "Haku",
519
+ reset: "Palauta",
520
+ save: "Tallenna",
521
+ "filter-name": "Suodattimen nimi",
522
+ "empty-query-and-no-default":
523
+ "Hakukysely on tyhjä eikä oletushakua ole määritetty.",
524
+ "query-not-changed":
525
+ "Hakupyyntö ei ole muuttunut, joten hakua ei tarvita.",
526
+ };
527
+ case "cs":
528
+ return {
529
+ search: "Hledat",
530
+ reset: "Resetovat",
531
+ save: "Uložit",
532
+ "filter-name": "Název filtru",
533
+ "empty-query-and-no-default":
534
+ "Dotaz je prázdný a není nastavena žádná výchozí hodnota.",
535
+ "query-not-changed":
536
+ "Dotaz na hledání se nezměnil, takže hledání není nutné.",
537
+ };
538
+ case "pt":
539
+ return {
540
+ search: "Buscar",
541
+ reset: "Redefinir",
542
+ save: "Salvar",
543
+ "filter-name": "Nome do filtro",
544
+ "empty-query-and-no-default":
545
+ "A consulta está vazia e não há uma consulta padrão.",
546
+ "query-not-changed":
547
+ "A solicitação de pesquisa não foi alterada, portanto, nenhuma pesquisa é necessária.",
548
+ };
549
+ case "ru":
550
+ return {
551
+ search: "Поиск",
552
+ reset: "Сброс",
553
+ save: "Сохранить",
554
+ "filter-name": "Имя фильтра",
555
+ "empty-query-and-no-default": "Запрос пуст и нет запроса по умолчанию.",
556
+ "query-not-changed":
557
+ "Поисковый запрос не изменился, поэтому поиск не требуется.",
558
+ };
559
+ case "zh":
560
+ return {
561
+ search: "搜索",
562
+ reset: "重置",
563
+ save: "保存",
564
+ "filter-name": "过滤器名称",
565
+ "empty-query-and-no-default": "查询为空,且没有默认查询。",
566
+ "query-not-changed": "搜索请求没有更改,因此不需要进行搜索。",
567
+ };
568
+ case "hi":
569
+ return {
570
+ search: "खोजें",
571
+ reset: "रीसेट करें",
572
+ save: "सहेजें",
573
+ "filter-name": "फ़िल्टर नाम",
574
+ "empty-query-and-no-default":
575
+ "क्वेरी खाली है और कोई डिफ़ॉल्ट क्वेरी नहीं है।",
576
+ "query-not-changed":
577
+ "खोज अनुरोध में कोई बदलाव नहीं हुआ है, इसलिए खोज आवश्यक नहीं है।",
578
+ };
579
+ case "bn":
580
+ return {
581
+ search: "অনুসন্ধান",
582
+ reset: "রিসেট",
583
+ save: "সংরক্ষণ করুন",
584
+ "filter-name": "ফিল্টারের নাম",
585
+ "empty-query-and-no-default":
586
+ "কোয়েরি খালি এবং কোনো ডিফল্ট কোয়েরি নেই।",
587
+ "query-not-changed":
588
+ "অনুসন্ধানের অনুরোধ পরিবর্তন হয়নি, তাই অনুসন্ধান প্রয়োজন নয়।",
589
+ };
590
+ case "ja":
591
+ return {
592
+ search: "検索",
593
+ reset: "リセット",
594
+ save: "保存",
595
+ "filter-name": "フィルター名",
596
+ "empty-query-and-no-default":
597
+ "クエリが空で、デフォルトクエリがありません。",
598
+ "query-not-changed": "検索リクエストに変更がないため、検索は不要です。",
599
+ };
600
+ case "pa":
601
+ return {
602
+ search: "ਖੋਜੋ",
603
+ reset: "ਰੀਸੈੱਟ ਕਰੋ",
604
+ save: "ਸੇਵ ਕਰੋ",
605
+ "filter-name": "ਫਿਲਟਰ ਦਾ ਨਾਂ",
606
+ "empty-query-and-no-default":
607
+ "ਕੁਐਰੀ ਖਾਲੀ ਹੈ ਅਤੇ ਕੋਈ ਡਿਫੌਲਟ ਕੁਐਰੀ ਨਹੀਂ ਹੈ।",
608
+ "query-not-changed":
609
+ "ਖੋਜ ਦੀ ਬੇਨਤੀ ਵਿੱਚ ਕੋਈ ਤਬਦੀਲੀ ਨਹੀਂ ਆਈ ਹੈ, ਇਸ ਲਈ ਖੋਜ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।",
610
+ };
611
+ default:
612
+ case "en":
613
+ return {
614
+ search: "Search",
615
+ reset: "Reset",
616
+ save: "Save",
617
+ "filter-name": "Filter name",
618
+ "empty-query-and-no-default":
619
+ "The query is empty and there is no default query.",
620
+ "query-not-changed":
621
+ "The search request has not changed, so no search is required.",
622
+ };
623
+ }
621
624
  }
622
625
 
623
626
  /**
@@ -625,52 +628,52 @@ function getTranslations() {
625
628
  * @return {FilterButton}
626
629
  */
627
630
  function initControlReferences() {
628
- if (!this.shadowRoot) {
629
- throw new Error("no shadow-root is defined");
630
- }
631
-
632
- this[filterControlElementSymbol] = this.shadowRoot.querySelector(
633
- "[data-monster-role=control]",
634
- );
635
- this[filterSelectElementSymbol] = this.shadowRoot.querySelector(
636
- "[data-monster-role=filter-select]",
637
- );
638
- this[searchButtonElementSymbol] = this.shadowRoot.querySelector(
639
- "[data-monster-role=search-button]",
640
- );
641
- this[resetButtonElementSymbol] = this.shadowRoot.querySelector(
642
- "[data-monster-role=reset-button]",
643
- );
644
-
645
- this[saveButtonElementSymbol] = this.shadowRoot.querySelector(
646
- "[data-monster-role=save-button]",
647
- );
648
-
649
- this[filterSaveActionButtonElementSymbol] = this.shadowRoot.querySelector(
650
- "[data-monster-role=save-action-button]",
651
- );
652
-
653
- this[filterTabElementSymbol] = findElementWithSelectorUpwards(
654
- this,
655
- this.getOption("storedConfig.selector", ""),
656
- );
657
-
658
- return this;
631
+ if (!this.shadowRoot) {
632
+ throw new Error("no shadow-root is defined");
633
+ }
634
+
635
+ this[filterControlElementSymbol] = this.shadowRoot.querySelector(
636
+ "[data-monster-role=control]",
637
+ );
638
+ this[filterSelectElementSymbol] = this.shadowRoot.querySelector(
639
+ "[data-monster-role=filter-select]",
640
+ );
641
+ this[searchButtonElementSymbol] = this.shadowRoot.querySelector(
642
+ "[data-monster-role=search-button]",
643
+ );
644
+ this[resetButtonElementSymbol] = this.shadowRoot.querySelector(
645
+ "[data-monster-role=reset-button]",
646
+ );
647
+
648
+ this[saveButtonElementSymbol] = this.shadowRoot.querySelector(
649
+ "[data-monster-role=save-button]",
650
+ );
651
+
652
+ this[filterSaveActionButtonElementSymbol] = this.shadowRoot.querySelector(
653
+ "[data-monster-role=save-action-button]",
654
+ );
655
+
656
+ this[filterTabElementSymbol] = findElementWithSelectorUpwards(
657
+ this,
658
+ this.getOption("storedConfig.selector", ""),
659
+ );
660
+
661
+ return this;
659
662
  }
660
663
 
661
664
  /**
662
665
  * @private
663
666
  */
664
667
  function updateFilterSelections() {
665
- queueMicrotask(() => {
666
- const options = this[settingsSymbol].getOptions();
667
- this[filterSelectElementSymbol].setOption("options", options);
668
-
669
- setTimeout(() => {
670
- this[filterSelectElementSymbol].value =
671
- this[settingsSymbol].getSelected();
672
- }, 10);
673
- });
668
+ queueMicrotask(() => {
669
+ const options = this[settingsSymbol].getOptions();
670
+ this[filterSelectElementSymbol].setOption("options", options);
671
+
672
+ setTimeout(() => {
673
+ this[filterSelectElementSymbol].value =
674
+ this[settingsSymbol].getSelected();
675
+ }, 10);
676
+ });
674
677
  }
675
678
 
676
679
  /**
@@ -678,64 +681,64 @@ function updateFilterSelections() {
678
681
  * @throws {Error} no filter label is defined
679
682
  */
680
683
  function initFilter() {
681
- const storedSetting = this[settingsSymbol];
682
- this[settingsSymbol] = new Settings();
683
-
684
- const result = parseBracketedKeyValueHash(getGlobal().location.hash);
685
- let valuesFromHash = {};
686
- if (isObject(result) && result?.[this.id]) {
687
- valuesFromHash = result[this.id];
688
- }
689
-
690
- getSlottedElements
691
- .call(this, "label[data-monster-label]")
692
- .forEach((element) => {
693
- const label = element.getAttribute("data-monster-label");
694
- if (!label) {
695
- addAttributeToken(
696
- this,
697
- ATTRIBUTE_ERRORMESSAGE,
698
- "no filter label is defined",
699
- );
700
- return;
701
- }
702
-
703
- let value = element.id;
704
- if (!value) {
705
- const prefix = label.replace(/\W/g, "-");
706
- prefix.charAt(0).match(/[\d_]/g)?.length ? `f${prefix}` : prefix;
707
-
708
- value = new ID(prefix + "-").toString();
709
- element.id = value;
710
- }
711
-
712
- let setting = storedSetting.get(value);
713
-
714
- if (setting) {
715
- this[settingsSymbol].set(setting);
716
- }
717
-
718
- if (valuesFromHash?.[element.id]) {
719
- const v = escapeAttributeValue(valuesFromHash[element.id]);
720
- const searchInput = element.firstElementChild;
721
- try {
722
- searchInput.value = v;
723
- } catch (error) {}
724
- }
725
-
726
- setting = this[settingsSymbol].get(value);
727
- let visible = false;
728
- if (setting) {
729
- setSlotAttribute(element, setting.visible);
730
- visible = setting.visible;
731
- } else {
732
- visible = getVisibilityFromSlotAttribute(element);
733
- }
734
-
735
- this[settingsSymbol].set({ value, label, visible });
736
- });
737
-
738
- updateFilterSelections.call(this);
684
+ const storedSetting = this[settingsSymbol];
685
+ this[settingsSymbol] = new Settings();
686
+
687
+ const result = parseBracketedKeyValueHash(getGlobal().location.hash);
688
+ let valuesFromHash = {};
689
+ if (isObject(result) && result?.[this.id]) {
690
+ valuesFromHash = result[this.id];
691
+ }
692
+
693
+ getSlottedElements
694
+ .call(this, "label[data-monster-label]")
695
+ .forEach((element) => {
696
+ const label = element.getAttribute("data-monster-label");
697
+ if (!label) {
698
+ addAttributeToken(
699
+ this,
700
+ ATTRIBUTE_ERRORMESSAGE,
701
+ "no filter label is defined",
702
+ );
703
+ return;
704
+ }
705
+
706
+ let value = element.id;
707
+ if (!value) {
708
+ const prefix = label.replace(/\W/g, "-");
709
+ prefix.charAt(0).match(/[\d_]/g)?.length ? `f${prefix}` : prefix;
710
+
711
+ value = new ID(prefix + "-").toString();
712
+ element.id = value;
713
+ }
714
+
715
+ let setting = storedSetting.get(value);
716
+
717
+ if (setting) {
718
+ this[settingsSymbol].set(setting);
719
+ }
720
+
721
+ if (valuesFromHash?.[element.id]) {
722
+ const v = escapeAttributeValue(valuesFromHash[element.id]);
723
+ const searchInput = element.firstElementChild;
724
+ try {
725
+ searchInput.value = v;
726
+ } catch (error) {}
727
+ }
728
+
729
+ setting = this[settingsSymbol].get(value);
730
+ let visible = false;
731
+ if (setting) {
732
+ setSlotAttribute(element, setting.visible);
733
+ visible = setting.visible;
734
+ } else {
735
+ visible = getVisibilityFromSlotAttribute(element);
736
+ }
737
+
738
+ this[settingsSymbol].set({ value, label, visible });
739
+ });
740
+
741
+ updateFilterSelections.call(this);
739
742
  }
740
743
 
741
744
  /**
@@ -744,16 +747,16 @@ function initFilter() {
744
747
  * @return {*}
745
748
  */
746
749
  function escapeAttributeValue(input) {
747
- if (input === undefined || input === null) {
748
- return input;
749
- }
750
-
751
- return input
752
- .replace(/&/g, "&")
753
- .replace(/"/g, """)
754
- .replace(/'/g, "'")
755
- .replace(/</g, "&lt;")
756
- .replace(/>/g, "&gt;");
750
+ if (input === undefined || input === null) {
751
+ return input;
752
+ }
753
+
754
+ return input
755
+ .replace(/&/g, "&amp;")
756
+ .replace(/"/g, "&quot;")
757
+ .replace(/'/g, "&#x27;")
758
+ .replace(/</g, "&lt;")
759
+ .replace(/>/g, "&gt;");
757
760
  }
758
761
 
759
762
  /**
@@ -762,9 +765,9 @@ function escapeAttributeValue(input) {
762
765
  * @return {boolean}
763
766
  */
764
767
  function getVisibilityFromSlotAttribute(element) {
765
- return !(
766
- element.hasAttribute("slot") && element.getAttribute("slot") === "hidden"
767
- );
768
+ return !(
769
+ element.hasAttribute("slot") && element.getAttribute("slot") === "hidden"
770
+ );
768
771
  }
769
772
 
770
773
  /**
@@ -773,369 +776,369 @@ function getVisibilityFromSlotAttribute(element) {
773
776
  * @param {boolean} visible
774
777
  */
775
778
  function setSlotAttribute(element, visible) {
776
- if (visible) {
777
- element.removeAttribute("slot");
778
- return;
779
- }
779
+ if (visible) {
780
+ element.removeAttribute("slot");
781
+ return;
782
+ }
780
783
 
781
- element.setAttribute("slot", "hidden");
784
+ element.setAttribute("slot", "hidden");
782
785
  }
783
786
 
784
787
  /**
785
788
  * @private
786
789
  */
787
790
  function initEventHandler() {
788
- const self = this;
789
-
790
- let lastHash = getGlobal().location.hash;
791
- self[locationChangeHandlerSymbol] = () => {
792
- if (lastHash === getGlobal().location.hash) {
793
- return;
794
- }
795
-
796
- /**
797
- * debounce the hash change event if doSearch
798
- * is called by click the search button
799
- */
800
- if (self[hashChangeSymbol] > 0) {
801
- self[hashChangeSymbol]--;
802
- return;
803
- }
804
-
805
- initFilter.call(this);
806
-
807
- doSearch
808
- .call(self)
809
- .then(() => {})
810
- .catch((error) => {})
811
- .finally(() => {
812
- lastHash = getGlobal().location.hash;
813
- });
814
- };
815
-
816
- /**
817
- * Monster.Components.Form.event:monster-selection-cleared
818
- */
819
- if (self[filterSelectElementSymbol]) {
820
- self[filterSelectElementSymbol].addEventListener(
821
- "monster-selection-cleared",
822
- function () {
823
- const settings = self[settingsSymbol].getOptions();
824
-
825
- for (const setting of settings) {
826
- const filterElement = findElementWithIdUpwards(self, setting.value);
827
- if (filterElement) {
828
- setSlotAttribute(filterElement, false);
829
-
830
- self[settingsSymbol].set({ value: setting.value, visible: false });
831
- }
832
- }
833
-
834
- updateConfig.call(self);
835
- },
836
- );
837
-
838
- self[filterSelectElementSymbol].addEventListener(
839
- "monster-changed",
840
- function (event) {
841
- const filterElement = findElementWithIdUpwards(
842
- self,
843
- event.detail.value,
844
- );
845
- if (filterElement) {
846
- setSlotAttribute(filterElement, event.detail.checked);
847
- }
848
-
849
- self[settingsSymbol].set({
850
- value: event.detail.value,
851
- visible: event.detail.checked,
852
- });
853
-
854
- updateConfig.call(self);
855
- },
856
- );
857
- }
858
-
859
- /** save the current filter */
860
- if (self[filterSaveActionButtonElementSymbol]) {
861
- self[filterSaveActionButtonElementSymbol].setOption(
862
- "actions.click",
863
- function (event) {
864
- const button = findTargetElementFromEvent(
865
- event,
866
- "data-monster-role",
867
- "save-action-button",
868
- );
869
- const form = button.closest("[data-monster-role=form]");
870
-
871
- if (!form) {
872
- button.setState("failed", self.getOption("timeouts.message", 4000));
873
- return;
874
- }
875
-
876
- const input = form.querySelector("input[name=filter-name]");
877
- if (!input) {
878
- button.setState("failed", self.getOption("timeouts.message", 4000));
879
- return;
880
- }
881
-
882
- const name = input.value;
883
- if (!name) {
884
- button.setState("failed", self.getOption("timeouts.message", 4000));
885
- button.setMessage("Please enter a name").showMessage();
886
- return;
887
- }
888
-
889
- doSearch
890
- .call(self, { showEffect: false })
891
- .then(() => {
892
- const configKey = getStoredFilterConfigKey.call(self);
893
- const host = getDocument().querySelector("monster-host");
894
- if (!host) {
895
- return;
896
- }
897
-
898
- const query = self.getOption("query");
899
- if (!query) {
900
- button.setState(
901
- "failed",
902
- self.getOption(
903
- "timeouts.message",
904
- self.getOption("timeouts.message", 4000),
905
- ),
906
- );
907
- button
908
- .setMessage("No query found")
909
- .showMessage(self.getOption("timeouts.message", 4000));
910
- return;
911
- }
912
-
913
- host
914
- .hasConfig(configKey)
915
- .then((hasConfig) => {
916
- return new Promise((resolve, reject) => {
917
- if (hasConfig) {
918
- host.getConfig(configKey).then(resolve).catch(reject);
919
- return;
920
- }
921
- return resolve({});
922
- });
923
- })
924
- .then((config) => {
925
- config[name] = query;
926
- return host.setConfig(configKey, {
927
- ...config,
928
- });
929
- })
930
- .then(() => {
931
- button.setState(
932
- "successful",
933
- self.getOption("timeouts.message", 4000),
934
- );
935
- updateFilterTabs.call(self);
936
- })
937
- .catch((error) => {
938
- button.setState(
939
- "failed",
940
- self.getOption("timeouts.message", 4000),
941
- );
942
- button
943
- .setMessage(error.message)
944
- .showMessage(self.getOption("timeouts.message", 4000));
945
- });
946
- })
947
- .catch((error) => {
948
- button.setState("failed", self.getOption("timeouts.message", 4000));
949
- const msg = error.message || error;
950
- button
951
- .setMessage(msg)
952
- .showMessage(self.getOption("timeouts.message", 4000));
953
- });
954
- },
955
- );
956
- }
957
-
958
- self[searchButtonElementSymbol].setOption("actions.click", () => {
959
- self[hashChangeSymbol] = 1;
960
-
961
- doSearch
962
- .call(self)
963
- .then(() => {})
964
- .catch((error) => {});
965
- });
966
-
967
- // the reset button should reset the filter and the search query
968
- // all input elements should be reset to their default values
969
- // which is the empty string. we search for all input elements
970
- // in the filter and reset them to their default value
971
- self[resetButtonElementSymbol].setOption("actions.click", () => {
972
- getSlottedElements
973
- .call(self, "label[data-monster-label]")
974
- .forEach((element) => {
975
- const label = element.getAttribute("data-monster-label");
976
- if (!label) {
977
- return;
978
- }
979
-
980
- const input = element.firstElementChild;
981
-
982
- if (input) {
983
- input.value = "";
984
- }
985
- });
986
-
987
- doSearch
988
- .call(self, { showEffect: false })
989
- .then(() => {})
990
- .catch((e) => addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.message));
991
- });
992
-
993
- self.addEventListener("keyup", (event) => {
994
- const path = event.composedPath();
995
- if (path.length === 0) {
996
- return;
997
- }
998
-
999
- if (!(path[0] instanceof HTMLInputElement)) {
1000
- return;
1001
- }
1002
-
1003
- if (event.keyCode === 13) {
1004
- doSearch
1005
- .call(self, { showEffect: false })
1006
- .then(() => {})
1007
- .catch((error) => {});
1008
- }
1009
- });
1010
-
1011
- // tabs
1012
- const element = this[filterTabElementSymbol];
1013
- if (element) {
1014
- initTabEvents.call(this);
1015
- }
791
+ const self = this;
792
+
793
+ let lastHash = getGlobal().location.hash;
794
+ self[locationChangeHandlerSymbol] = () => {
795
+ if (lastHash === getGlobal().location.hash) {
796
+ return;
797
+ }
798
+
799
+ /**
800
+ * debounce the hash change event if doSearch
801
+ * is called by click the search button
802
+ */
803
+ if (self[hashChangeSymbol] > 0) {
804
+ self[hashChangeSymbol]--;
805
+ return;
806
+ }
807
+
808
+ initFilter.call(this);
809
+
810
+ doSearch
811
+ .call(self)
812
+ .then(() => {})
813
+ .catch((error) => {})
814
+ .finally(() => {
815
+ lastHash = getGlobal().location.hash;
816
+ });
817
+ };
818
+
819
+ /**
820
+ * Monster.Components.Form.event:monster-selection-cleared
821
+ */
822
+ if (self[filterSelectElementSymbol]) {
823
+ self[filterSelectElementSymbol].addEventListener(
824
+ "monster-selection-cleared",
825
+ function () {
826
+ const settings = self[settingsSymbol].getOptions();
827
+
828
+ for (const setting of settings) {
829
+ const filterElement = findElementWithIdUpwards(self, setting.value);
830
+ if (filterElement) {
831
+ setSlotAttribute(filterElement, false);
832
+
833
+ self[settingsSymbol].set({ value: setting.value, visible: false });
834
+ }
835
+ }
836
+
837
+ updateConfig.call(self);
838
+ },
839
+ );
840
+
841
+ self[filterSelectElementSymbol].addEventListener(
842
+ "monster-changed",
843
+ function (event) {
844
+ const filterElement = findElementWithIdUpwards(
845
+ self,
846
+ event.detail.value,
847
+ );
848
+ if (filterElement) {
849
+ setSlotAttribute(filterElement, event.detail.checked);
850
+ }
851
+
852
+ self[settingsSymbol].set({
853
+ value: event.detail.value,
854
+ visible: event.detail.checked,
855
+ });
856
+
857
+ updateConfig.call(self);
858
+ },
859
+ );
860
+ }
861
+
862
+ /** save the current filter */
863
+ if (self[filterSaveActionButtonElementSymbol]) {
864
+ self[filterSaveActionButtonElementSymbol].setOption(
865
+ "actions.click",
866
+ function (event) {
867
+ const button = findTargetElementFromEvent(
868
+ event,
869
+ "data-monster-role",
870
+ "save-action-button",
871
+ );
872
+ const form = button.closest("[data-monster-role=form]");
873
+
874
+ if (!form) {
875
+ button.setState("failed", self.getOption("timeouts.message", 4000));
876
+ return;
877
+ }
878
+
879
+ const input = form.querySelector("input[name=filter-name]");
880
+ if (!input) {
881
+ button.setState("failed", self.getOption("timeouts.message", 4000));
882
+ return;
883
+ }
884
+
885
+ const name = input.value;
886
+ if (!name) {
887
+ button.setState("failed", self.getOption("timeouts.message", 4000));
888
+ button.setMessage("Please enter a name").showMessage();
889
+ return;
890
+ }
891
+
892
+ doSearch
893
+ .call(self, { showEffect: false })
894
+ .then(() => {
895
+ const configKey = getStoredFilterConfigKey.call(self);
896
+ const host = getDocument().querySelector("monster-host");
897
+ if (!host) {
898
+ return;
899
+ }
900
+
901
+ const query = self.getOption("query");
902
+ if (!query) {
903
+ button.setState(
904
+ "failed",
905
+ self.getOption(
906
+ "timeouts.message",
907
+ self.getOption("timeouts.message", 4000),
908
+ ),
909
+ );
910
+ button
911
+ .setMessage("No query found")
912
+ .showMessage(self.getOption("timeouts.message", 4000));
913
+ return;
914
+ }
915
+
916
+ host
917
+ .hasConfig(configKey)
918
+ .then((hasConfig) => {
919
+ return new Promise((resolve, reject) => {
920
+ if (hasConfig) {
921
+ host.getConfig(configKey).then(resolve).catch(reject);
922
+ return;
923
+ }
924
+ return resolve({});
925
+ });
926
+ })
927
+ .then((config) => {
928
+ config[name] = query;
929
+ return host.setConfig(configKey, {
930
+ ...config,
931
+ });
932
+ })
933
+ .then(() => {
934
+ button.setState(
935
+ "successful",
936
+ self.getOption("timeouts.message", 4000),
937
+ );
938
+ updateFilterTabs.call(self);
939
+ })
940
+ .catch((error) => {
941
+ button.setState(
942
+ "failed",
943
+ self.getOption("timeouts.message", 4000),
944
+ );
945
+ button
946
+ .setMessage(error.message)
947
+ .showMessage(self.getOption("timeouts.message", 4000));
948
+ });
949
+ })
950
+ .catch((error) => {
951
+ button.setState("failed", self.getOption("timeouts.message", 4000));
952
+ const msg = error.message || error;
953
+ button
954
+ .setMessage(msg)
955
+ .showMessage(self.getOption("timeouts.message", 4000));
956
+ });
957
+ },
958
+ );
959
+ }
960
+
961
+ self[searchButtonElementSymbol].setOption("actions.click", () => {
962
+ self[hashChangeSymbol] = 1;
963
+
964
+ doSearch
965
+ .call(self)
966
+ .then(() => {})
967
+ .catch((error) => {});
968
+ });
969
+
970
+ // the reset button should reset the filter and the search query
971
+ // all input elements should be reset to their default values
972
+ // which is the empty string. we search for all input elements
973
+ // in the filter and reset them to their default value
974
+ self[resetButtonElementSymbol].setOption("actions.click", () => {
975
+ getSlottedElements
976
+ .call(self, "label[data-monster-label]")
977
+ .forEach((element) => {
978
+ const label = element.getAttribute("data-monster-label");
979
+ if (!label) {
980
+ return;
981
+ }
982
+
983
+ const input = element.firstElementChild;
984
+
985
+ if (input) {
986
+ input.value = "";
987
+ }
988
+ });
989
+
990
+ doSearch
991
+ .call(self, { showEffect: false })
992
+ .then(() => {})
993
+ .catch((e) => addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.message));
994
+ });
995
+
996
+ self.addEventListener("keyup", (event) => {
997
+ const path = event.composedPath();
998
+ if (path.length === 0) {
999
+ return;
1000
+ }
1001
+
1002
+ if (!(path[0] instanceof HTMLInputElement)) {
1003
+ return;
1004
+ }
1005
+
1006
+ if (event.keyCode === 13) {
1007
+ doSearch
1008
+ .call(self, { showEffect: false })
1009
+ .then(() => {})
1010
+ .catch((error) => {});
1011
+ }
1012
+ });
1013
+
1014
+ // tabs
1015
+ const element = this[filterTabElementSymbol];
1016
+ if (element) {
1017
+ initTabEvents.call(this);
1018
+ }
1016
1019
  }
1017
1020
 
1018
1021
  function initTabEvents() {
1019
- const self = this;
1020
- this[filterTabElementSymbol].addEventListener(
1021
- "monster-tab-changed",
1022
- (event) => {
1023
- const query = event?.detail?.data?.["data-monster-query"];
1024
- const q = this.getOption("query");
1025
- if (query !== q) {
1026
- this.setOption("query", query);
1027
- }
1028
- },
1029
- );
1030
-
1031
- this[filterTabElementSymbol].addEventListener(
1032
- "monster-tab-remove",
1033
- (event) => {
1034
- const labels = [];
1035
- const buttons = this[filterTabElementSymbol].getOption("buttons");
1036
-
1037
- const keys = ["popper", "standard"];
1038
- for (let i = 0; i < keys.length; i++) {
1039
- const key = keys[i];
1040
-
1041
- for (const button of buttons[key]) {
1042
- if (button.label !== event.detail.label) {
1043
- labels.push(button.label);
1044
- }
1045
- }
1046
- }
1047
-
1048
- const host = findElementWithSelectorUpwards(this, "monster-host");
1049
- if (!(host && this.id)) {
1050
- return;
1051
- }
1052
-
1053
- const configKey = getStoredFilterConfigKey.call(this);
1054
- host
1055
- .hasConfig(configKey)
1056
- .then((hasConfig) => {
1057
- if (!hasConfig) {
1058
- return;
1059
- }
1060
-
1061
- return host.getConfig(configKey);
1062
- })
1063
- .then((config) => {
1064
- for (const [name, query] of Object.entries(config)) {
1065
- if (labels.includes(name)) {
1066
- continue;
1067
- }
1068
-
1069
- delete config[name];
1070
- }
1071
-
1072
- return host.setConfig(configKey, {
1073
- ...config,
1074
- });
1075
- });
1076
- },
1077
- );
1022
+ const self = this;
1023
+ this[filterTabElementSymbol].addEventListener(
1024
+ "monster-tab-changed",
1025
+ (event) => {
1026
+ const query = event?.detail?.data?.["data-monster-query"];
1027
+ const q = this.getOption("query");
1028
+ if (query !== q) {
1029
+ this.setOption("query", query);
1030
+ }
1031
+ },
1032
+ );
1033
+
1034
+ this[filterTabElementSymbol].addEventListener(
1035
+ "monster-tab-remove",
1036
+ (event) => {
1037
+ const labels = [];
1038
+ const buttons = this[filterTabElementSymbol].getOption("buttons");
1039
+
1040
+ const keys = ["popper", "standard"];
1041
+ for (let i = 0; i < keys.length; i++) {
1042
+ const key = keys[i];
1043
+
1044
+ for (const button of buttons[key]) {
1045
+ if (button.label !== event.detail.label) {
1046
+ labels.push(button.label);
1047
+ }
1048
+ }
1049
+ }
1050
+
1051
+ const host = findElementWithSelectorUpwards(this, "monster-host");
1052
+ if (!(host && this.id)) {
1053
+ return;
1054
+ }
1055
+
1056
+ const configKey = getStoredFilterConfigKey.call(this);
1057
+ host
1058
+ .hasConfig(configKey)
1059
+ .then((hasConfig) => {
1060
+ if (!hasConfig) {
1061
+ return;
1062
+ }
1063
+
1064
+ return host.getConfig(configKey);
1065
+ })
1066
+ .then((config) => {
1067
+ for (const [name, query] of Object.entries(config)) {
1068
+ if (labels.includes(name)) {
1069
+ continue;
1070
+ }
1071
+
1072
+ delete config[name];
1073
+ }
1074
+
1075
+ return host.setConfig(configKey, {
1076
+ ...config,
1077
+ });
1078
+ });
1079
+ },
1080
+ );
1078
1081
  }
1079
1082
 
1080
1083
  /**
1081
1084
  * @private
1082
1085
  */
1083
1086
  function updateFilterTabs() {
1084
- const element = this[filterTabElementSymbol];
1085
- if (!element) {
1086
- return;
1087
- }
1088
-
1089
- const host = findElementWithSelectorUpwards(this, "monster-host");
1090
- if (!(host && this.id)) {
1091
- return;
1092
- }
1093
-
1094
- const configKey = getStoredFilterConfigKey.call(this);
1095
- host
1096
- .hasConfig(configKey)
1097
- .then((hasConfig) => {
1098
- if (!hasConfig) {
1099
- return;
1100
- }
1101
-
1102
- return host.getConfig(configKey);
1103
- })
1104
- .then((config) => {
1105
- for (const [name, query] of Object.entries(config)) {
1106
- const found = element.querySelector(
1107
- `[data-monster-button-label="${name}"]`,
1108
- );
1109
- if (found) {
1110
- continue;
1111
- }
1112
-
1113
- if (query === undefined || query === null) {
1114
- continue;
1115
- }
1116
-
1117
- const escapedQuery = escapeAttributeValue(query);
1118
-
1119
- element.insertAdjacentHTML(
1120
- "beforeend",
1121
- `<div data-monster-button-label="${name}"
1087
+ const element = this[filterTabElementSymbol];
1088
+ if (!element) {
1089
+ return;
1090
+ }
1091
+
1092
+ const host = findElementWithSelectorUpwards(this, "monster-host");
1093
+ if (!(host && this.id)) {
1094
+ return;
1095
+ }
1096
+
1097
+ const configKey = getStoredFilterConfigKey.call(this);
1098
+ host
1099
+ .hasConfig(configKey)
1100
+ .then((hasConfig) => {
1101
+ if (!hasConfig) {
1102
+ return;
1103
+ }
1104
+
1105
+ return host.getConfig(configKey);
1106
+ })
1107
+ .then((config) => {
1108
+ for (const [name, query] of Object.entries(config)) {
1109
+ const found = element.querySelector(
1110
+ `[data-monster-button-label="${name}"]`,
1111
+ );
1112
+ if (found) {
1113
+ continue;
1114
+ }
1115
+
1116
+ if (query === undefined || query === null) {
1117
+ continue;
1118
+ }
1119
+
1120
+ const escapedQuery = escapeAttributeValue(query);
1121
+
1122
+ element.insertAdjacentHTML(
1123
+ "beforeend",
1124
+ `<div data-monster-button-label="${name}"
1122
1125
  data-monster-removable="true"
1123
1126
  data-monster-query="${escapedQuery}" data-monster-role="filter-tab" >
1124
1127
  </div>`,
1125
- );
1126
- }
1127
- })
1128
- .catch((error) => {
1129
- if (error instanceof Error) {
1130
- addAttributeToken(
1131
- this,
1132
- ATTRIBUTE_ERRORMESSAGE,
1133
- error.message + " " + error.stack,
1134
- );
1135
- } else {
1136
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error + "");
1137
- }
1138
- });
1128
+ );
1129
+ }
1130
+ })
1131
+ .catch((error) => {
1132
+ if (error instanceof Error) {
1133
+ addAttributeToken(
1134
+ this,
1135
+ ATTRIBUTE_ERRORMESSAGE,
1136
+ error.message + " " + error.stack,
1137
+ );
1138
+ } else {
1139
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error + "");
1140
+ }
1141
+ });
1139
1142
  }
1140
1143
 
1141
1144
  /**
@@ -1144,92 +1147,92 @@ function updateFilterTabs() {
1144
1147
  * @return {Promise}
1145
1148
  */
1146
1149
  function doSearch({ showEffect } = { showEffect: true }) {
1147
- this.resetFailureMessage();
1148
-
1149
- if (showEffect) {
1150
- this[searchButtonElementSymbol].setState(
1151
- "activity",
1152
- this.getOption("timeouts.message", 4000),
1153
- );
1154
- }
1155
-
1156
- return collectSearchQueries
1157
- .call(this)
1158
- .then((query) => {
1159
- const buildQuery = buildSearchQuery.call(this, query);
1160
- if (buildQuery === null) {
1161
- const msg = this.getOption("labels.empty-query-and-no-default");
1162
- if (showEffect) {
1163
- this[searchButtonElementSymbol].removeState();
1164
- this[searchButtonElementSymbol]
1165
- .setMessage(msg)
1166
- .showMessage(this.getOption("timeouts.message", 4000));
1167
- }
1168
- return Promise.reject(new Error(msg));
1169
- }
1170
-
1171
- if (buildQuery === "" && this.getOption("defaultQuery") === null) {
1172
- const msg = this.getOption("labels.empty-query-and-no-default");
1173
-
1174
- if (showEffect) {
1175
- this[searchButtonElementSymbol].removeState();
1176
- this[searchButtonElementSymbol]
1177
- .setMessage(msg)
1178
- .showMessage(this.getOption("timeouts.message", 4000));
1179
- }
1180
-
1181
- return Promise.reject(new Error(msg));
1182
- }
1183
-
1184
- if (
1185
- this.getOption("features.preventSameQuery") &&
1186
- buildQuery === this.getOption("query")
1187
- ) {
1188
- const msg = this.getOption("labels.query-not-changed");
1189
-
1190
- if (showEffect) {
1191
- this[searchButtonElementSymbol].removeState();
1192
- this[searchButtonElementSymbol]
1193
- .setMessage(msg)
1194
- .showMessage(this.getOption("timeouts.message", 4000));
1195
- }
1196
-
1197
- return Promise.reject(new Error(msg));
1198
- }
1199
-
1200
- if (showEffect) {
1201
- this[searchButtonElementSymbol].removeState();
1202
- this[searchButtonElementSymbol].setState(
1203
- "activity",
1204
- this.getOption("timeouts.message", 4000),
1205
- );
1206
- }
1207
-
1208
- this.setOption("query", buildSearchQuery.call(this, query));
1209
-
1210
- return Promise.resolve();
1211
- })
1212
- .catch((error) => {
1213
- if (error instanceof Error) {
1214
- addAttributeToken(
1215
- this,
1216
- ATTRIBUTE_ERRORMESSAGE,
1217
- error.message + " " + error.stack,
1218
- );
1219
- } else {
1220
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
1221
- }
1222
-
1223
- if (showEffect) {
1224
- this[searchButtonElementSymbol].setState(
1225
- "failed",
1226
- this.getOption("timeouts.message", 4000),
1227
- );
1228
- this[searchButtonElementSymbol].setMessage(error.message).showMessage();
1229
- }
1230
-
1231
- return Promise.reject(error);
1232
- });
1150
+ this.resetFailureMessage();
1151
+
1152
+ if (showEffect) {
1153
+ this[searchButtonElementSymbol].setState(
1154
+ "activity",
1155
+ this.getOption("timeouts.message", 4000),
1156
+ );
1157
+ }
1158
+
1159
+ return collectSearchQueries
1160
+ .call(this)
1161
+ .then((query) => {
1162
+ const buildQuery = buildSearchQuery.call(this, query);
1163
+ if (buildQuery === null) {
1164
+ const msg = this.getOption("labels.empty-query-and-no-default");
1165
+ if (showEffect) {
1166
+ this[searchButtonElementSymbol].removeState();
1167
+ this[searchButtonElementSymbol]
1168
+ .setMessage(msg)
1169
+ .showMessage(this.getOption("timeouts.message", 4000));
1170
+ }
1171
+ return Promise.reject(new Error(msg));
1172
+ }
1173
+
1174
+ if (buildQuery === "" && this.getOption("defaultQuery") === null) {
1175
+ const msg = this.getOption("labels.empty-query-and-no-default");
1176
+
1177
+ if (showEffect) {
1178
+ this[searchButtonElementSymbol].removeState();
1179
+ this[searchButtonElementSymbol]
1180
+ .setMessage(msg)
1181
+ .showMessage(this.getOption("timeouts.message", 4000));
1182
+ }
1183
+
1184
+ return Promise.reject(new Error(msg));
1185
+ }
1186
+
1187
+ if (
1188
+ this.getOption("features.preventSameQuery") &&
1189
+ buildQuery === this.getOption("query")
1190
+ ) {
1191
+ const msg = this.getOption("labels.query-not-changed");
1192
+
1193
+ if (showEffect) {
1194
+ this[searchButtonElementSymbol].removeState();
1195
+ this[searchButtonElementSymbol]
1196
+ .setMessage(msg)
1197
+ .showMessage(this.getOption("timeouts.message", 4000));
1198
+ }
1199
+
1200
+ return Promise.reject(new Error(msg));
1201
+ }
1202
+
1203
+ if (showEffect) {
1204
+ this[searchButtonElementSymbol].removeState();
1205
+ this[searchButtonElementSymbol].setState(
1206
+ "activity",
1207
+ this.getOption("timeouts.message", 4000),
1208
+ );
1209
+ }
1210
+
1211
+ this.setOption("query", buildSearchQuery.call(this, query));
1212
+
1213
+ return Promise.resolve();
1214
+ })
1215
+ .catch((error) => {
1216
+ if (error instanceof Error) {
1217
+ addAttributeToken(
1218
+ this,
1219
+ ATTRIBUTE_ERRORMESSAGE,
1220
+ error.message + " " + error.stack,
1221
+ );
1222
+ } else {
1223
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
1224
+ }
1225
+
1226
+ if (showEffect) {
1227
+ this[searchButtonElementSymbol].setState(
1228
+ "failed",
1229
+ this.getOption("timeouts.message", 4000),
1230
+ );
1231
+ this[searchButtonElementSymbol].setMessage(error.message).showMessage();
1232
+ }
1233
+
1234
+ return Promise.reject(error);
1235
+ });
1233
1236
  }
1234
1237
 
1235
1238
  /**
@@ -1238,21 +1241,21 @@ function doSearch({ showEffect } = { showEffect: true }) {
1238
1241
  * @return {*|string}
1239
1242
  */
1240
1243
  function buildSearchQuery(queries) {
1241
- if (!isArray(queries) || queries.length === 0) {
1242
- return this.getOption("defaultQuery");
1243
- }
1244
+ if (!isArray(queries) || queries.length === 0) {
1245
+ return this.getOption("defaultQuery");
1246
+ }
1244
1247
 
1245
- const joinCallback = this.getOption("queries.join");
1246
- if (isFunction(joinCallback)) {
1247
- return joinCallback(queries);
1248
- }
1248
+ const joinCallback = this.getOption("queries.join");
1249
+ if (isFunction(joinCallback)) {
1250
+ return joinCallback(queries);
1251
+ }
1249
1252
 
1250
- const q = queries.join(" ").trim();
1251
- if (q.length === 0) {
1252
- return this.getOption("defaultQuery");
1253
- }
1253
+ const q = queries.join(" ").trim();
1254
+ if (q.length === 0) {
1255
+ return this.getOption("defaultQuery");
1256
+ }
1254
1257
 
1255
- return q;
1258
+ return q;
1256
1259
  }
1257
1260
 
1258
1261
  /**
@@ -1260,229 +1263,247 @@ function buildSearchQuery(queries) {
1260
1263
  * @return {Promise<unknown>}
1261
1264
  */
1262
1265
  function collectSearchQueries() {
1263
- const currentHash = parseBracketedKeyValueHash(getGlobal().location.hash);
1264
- const self = this;
1265
-
1266
- return new Promise((resolve, reject) => {
1267
- const query = [];
1268
- const wrapCallback = this.getOption("queries.wrap");
1269
-
1270
- let hasNoIdError = false;
1271
-
1272
- getSlottedElements
1273
- .call(this, "label[data-monster-label]")
1274
- .forEach((element) => {
1275
- const label = element.getAttribute("data-monster-label");
1276
- if (!label) {
1277
- throw new Error("no filter label is defined");
1278
- }
1279
-
1280
- const id = element.id;
1281
- if (!id) {
1282
- hasNoIdError = true;
1283
- return;
1284
- }
1285
-
1286
- const visible = getVisibilityFromSlotAttribute(element);
1287
- if (!visible) {
1288
- return;
1289
- }
1290
-
1291
- let template = element.getAttribute("data-monster-template");
1292
- if (!template) {
1293
- template = "${id}=${value}";
1294
- }
1295
-
1296
- const controlValue = getControlValuesFromLabel(element);
1297
- if (!controlValue) {
1298
- if (controlValue === "" && currentHash?.[this.id]?.[id]) {
1299
- delete currentHash[this.id][id];
1300
- }
1301
-
1302
- return;
1303
- }
1304
-
1305
- if (!isObject(currentHash[this.id])) {
1306
- currentHash[this.id] = {};
1307
- }
1308
- currentHash[this.id][id] = controlValue;
1309
-
1310
- const mapping = {
1311
- id,
1312
- value: controlValue,
1313
- label,
1314
- };
1315
-
1316
- const formatter = new Formatter(mapping, {
1317
- callbacks: {
1318
- range: (value, key) => {
1319
- return generateRangeComparisonExpression(value, key, {
1320
- urlEncode: true,
1321
- andOp: "AND",
1322
- orOp: "OR",
1323
- eqOp: "=",
1324
- gtOp: ">",
1325
- ltOp: "<",
1326
- });
1327
- },
1328
- "tag-list": (value, key) => {
1329
- if (isString(value)) {
1330
- value = value.split(",");
1331
- }
1332
-
1333
- if (!isArray(value)) {
1334
- return "";
1335
- }
1336
-
1337
- return (
1338
- key +
1339
- " IN " +
1340
- value
1341
- .map((v) => {
1342
- return `"${encodeURIComponent(v)}"`;
1343
- })
1344
- .join(",")
1345
- );
1346
- },
1347
- "list-tag": (value, key) => {
1348
- if (isString(value)) {
1349
- value = value.split(",");
1350
- }
1351
-
1352
- if (!isArray(value)) {
1353
- return "";
1354
- }
1355
-
1356
- return (
1357
- value
1358
- .map((v) => {
1359
- return `"${encodeURIComponent(v)}"`;
1360
- })
1361
- .join(",") +
1362
- " IN " +
1363
- encodeURIComponent(key)
1364
- );
1365
- },
1366
- "tags-in-list": (value, key, op) => {
1367
- if (isString(value)) {
1368
- value = value.split(",");
1369
- }
1370
-
1371
- if (!isArray(value)) {
1372
- return "";
1373
- }
1374
-
1375
- if (!op || !isString(op)) op = "OR";
1376
- op = " " + op.toUpperCase().trim() + " ";
1377
-
1378
- let query = "";
1379
- value.forEach((v) => {
1380
- if (query.length > 0) {
1381
- query += op;
1382
- }
1383
- query += `${encodeURIComponent(key)} IN "${encodeURIComponent(v)}"`;
1384
- });
1385
-
1386
- return query;
1387
- },
1388
- "list-in-tags": (value, key, op) => {
1389
- if (isString(value)) {
1390
- value = value.split(",");
1391
- }
1392
-
1393
- if (!isArray(value)) {
1394
- return "";
1395
- }
1396
-
1397
- if (!op || !isString(op)) op = "OR";
1398
- op = " " + op.toUpperCase().trim() + " ";
1399
-
1400
- let query = "";
1401
- value.forEach((v) => {
1402
- if (query.length > 0) {
1403
- query += op;
1404
- }
1405
- query += `"${encodeURIComponent(v)}" IN ${encodeURIComponent(key)}`;
1406
- });
1407
-
1408
- return query;
1409
- },
1410
- "array-list": (value, key) => {
1411
- if (isString(value)) {
1412
- value = value.split(",");
1413
- }
1414
-
1415
- if (!isArray(value)) {
1416
- return "";
1417
- }
1418
-
1419
- return (
1420
- key +
1421
- "=" +
1422
- value
1423
- .map((v) => {
1424
- return `"${encodeURIComponent(v)}"`;
1425
- })
1426
- .join(",")
1427
- );
1428
- },
1429
- "date-range": (value, key) => {
1430
- const query = parseDateInput(value, key);
1431
- if (!query || query === "false") {
1432
- return "";
1433
- }
1434
-
1435
- // return query as url encoded
1436
- return encodeURIComponent(query);
1437
- },
1438
- "to-int-2": (value, key) => {
1439
- const query = normalizeNumber(value);
1440
- if (Number.isNaN(query)) {
1441
- return key + " IS NULL";
1442
- }
1443
- return key + "=" + encodeURIComponent(Math.round(query * 100));
1444
- },
1445
- "to-int-3": (value, key) => {
1446
- const query = normalizeNumber(value);
1447
- if (Number.isNaN(query)) {
1448
- return "";
1449
- }
1450
- return key + "=" + encodeURIComponent(Math.round(query * 1000));
1451
- },
1452
- "to-int-4": (value, key) => {
1453
- const query = normalizeNumber(value);
1454
- if (Number.isNaN(query)) {
1455
- return "";
1456
- }
1457
- return key + "=" + encodeURIComponent(Math.round(query * 10000));
1458
- },
1459
- },
1460
- });
1461
-
1462
- if (self.getOption("formatter.marker.open")) {
1463
- formatter.setMarker(
1464
- self.getOption("formatter.marker.open"),
1465
- self.getOption("formatter.marker.close"),
1466
- );
1467
- }
1468
-
1469
- let queryPart = formatter.format(template);
1470
- if (queryPart) {
1471
- if (isFunction(wrapCallback)) {
1472
- queryPart = wrapCallback(queryPart, mapping);
1473
- }
1474
- query.push(queryPart);
1475
- }
1476
- });
1477
-
1478
- if (hasNoIdError) {
1479
- reject(new Error("some or all filter elements have no id"));
1480
- return;
1481
- }
1482
-
1483
- getGlobal().location.hash = createBracketedKeyValueHash(currentHash);
1484
- resolve(query);
1485
- });
1266
+ const currentHash = parseBracketedKeyValueHash(getGlobal().location.hash);
1267
+ const self = this;
1268
+
1269
+ return new Promise((resolve, reject) => {
1270
+ const query = [];
1271
+ const wrapCallback = this.getOption("queries.wrap");
1272
+
1273
+ let hasNoIdError = false;
1274
+
1275
+ getSlottedElements
1276
+ .call(this, "label[data-monster-label]")
1277
+ .forEach((element) => {
1278
+ const label = element.getAttribute("data-monster-label");
1279
+ if (!label) {
1280
+ throw new Error("no filter label is defined");
1281
+ }
1282
+
1283
+ const id = element.id;
1284
+ if (!id) {
1285
+ hasNoIdError = true;
1286
+ return;
1287
+ }
1288
+
1289
+ const visible = getVisibilityFromSlotAttribute(element);
1290
+ if (!visible) {
1291
+ return;
1292
+ }
1293
+
1294
+ let template = element.getAttribute("data-monster-template");
1295
+ if (!template) {
1296
+ template = "${id}=${value}";
1297
+ }
1298
+
1299
+ const controlValue = getControlValuesFromLabel(element);
1300
+ if (!controlValue) {
1301
+ if (controlValue === "" && currentHash?.[this.id]?.[id]) {
1302
+ delete currentHash[this.id][id];
1303
+ }
1304
+
1305
+ return;
1306
+ }
1307
+
1308
+ if (!isObject(currentHash[this.id])) {
1309
+ currentHash[this.id] = {};
1310
+ }
1311
+ currentHash[this.id][id] = controlValue;
1312
+
1313
+ const mapping = {
1314
+ id,
1315
+ value: controlValue,
1316
+ label,
1317
+ };
1318
+
1319
+ const formatter = new Formatter(mapping, {
1320
+ callbacks: {
1321
+ range: (value, key) => {
1322
+ return generateRangeComparisonExpression(value, key, {
1323
+ urlEncode: true,
1324
+ andOp: "AND",
1325
+ orOp: "OR",
1326
+ eqOp: "=",
1327
+ gtOp: ">",
1328
+ ltOp: "<",
1329
+ });
1330
+ },
1331
+ "tag-list": (value, key) => {
1332
+ if (isString(value)) {
1333
+ value = value.split(",");
1334
+ }
1335
+
1336
+ if (!isArray(value)) {
1337
+ return "";
1338
+ }
1339
+
1340
+ return (
1341
+ key +
1342
+ " IN " +
1343
+ value
1344
+ .map((v) => {
1345
+ return `"${encodeURIComponent(v)}"`;
1346
+ })
1347
+ .join(",")
1348
+ );
1349
+ },
1350
+ "list-tag": (value, key) => {
1351
+ if (isString(value)) {
1352
+ value = value.split(",");
1353
+ }
1354
+
1355
+ if (!isArray(value)) {
1356
+ return "";
1357
+ }
1358
+
1359
+ return (
1360
+ value
1361
+ .map((v) => {
1362
+ return `"${encodeURIComponent(v)}"`;
1363
+ })
1364
+ .join(",") +
1365
+ " IN " +
1366
+ encodeURIComponent(key)
1367
+ );
1368
+ },
1369
+ "tags-in-list": (value, key, op) => {
1370
+ if (isString(value)) {
1371
+ value = value.split(",");
1372
+ }
1373
+
1374
+ if (!isArray(value)) {
1375
+ return "";
1376
+ }
1377
+
1378
+ if (!op || !isString(op)) op = "OR";
1379
+ op = " " + op.toUpperCase().trim() + " ";
1380
+
1381
+ let query = "";
1382
+ value.forEach((v) => {
1383
+ if (query.length > 0) {
1384
+ query += op;
1385
+ }
1386
+ query += `${encodeURIComponent(key)} IN "${encodeURIComponent(v)}"`;
1387
+ });
1388
+
1389
+ return query;
1390
+ },
1391
+ "list-not-in-tags": (value, key, op) => {
1392
+ if (isString(value)) {
1393
+ value = value.split(",");
1394
+ }
1395
+ if (!isArray(value)) {
1396
+ return "";
1397
+ }
1398
+ if (!op || !isString(op)) op = "OR";
1399
+ op = " " + op.toUpperCase().trim() + " ";
1400
+ let query = "";
1401
+ value.forEach((v) => {
1402
+ if (query.length > 0) {
1403
+ query += op;
1404
+ }
1405
+ query += `"${encodeURIComponent(v)}" NOT IN ${encodeURIComponent(key)}`;
1406
+ });
1407
+ return query;
1408
+ },
1409
+ "list-in-tags": (value, key, op) => {
1410
+ if (isString(value)) {
1411
+ value = value.split(",");
1412
+ }
1413
+
1414
+ if (!isArray(value)) {
1415
+ return "";
1416
+ }
1417
+
1418
+ if (!op || !isString(op)) op = "OR";
1419
+ op = " " + op.toUpperCase().trim() + " ";
1420
+
1421
+ let query = "";
1422
+ value.forEach((v) => {
1423
+ if (query.length > 0) {
1424
+ query += op;
1425
+ }
1426
+ query += `"${encodeURIComponent(v)}" IN ${encodeURIComponent(key)}`;
1427
+ });
1428
+
1429
+ return query;
1430
+ },
1431
+ "array-list": (value, key) => {
1432
+ if (isString(value)) {
1433
+ value = value.split(",");
1434
+ }
1435
+
1436
+ if (!isArray(value)) {
1437
+ return "";
1438
+ }
1439
+
1440
+ return (
1441
+ key +
1442
+ "=" +
1443
+ value
1444
+ .map((v) => {
1445
+ return `"${encodeURIComponent(v)}"`;
1446
+ })
1447
+ .join(",")
1448
+ );
1449
+ },
1450
+ "date-range": (value, key) => {
1451
+ const query = parseDateInput(value, key);
1452
+ if (!query || query === "false") {
1453
+ return "";
1454
+ }
1455
+
1456
+ // return query as url encoded
1457
+ return encodeURIComponent(query);
1458
+ },
1459
+ "to-int-2": (value, key) => {
1460
+ const query = normalizeNumber(value);
1461
+ if (Number.isNaN(query)) {
1462
+ return key + " IS NULL";
1463
+ }
1464
+ return key + "=" + encodeURIComponent(Math.round(query * 100));
1465
+ },
1466
+ "to-int-3": (value, key) => {
1467
+ const query = normalizeNumber(value);
1468
+ if (Number.isNaN(query)) {
1469
+ return "";
1470
+ }
1471
+ return key + "=" + encodeURIComponent(Math.round(query * 1000));
1472
+ },
1473
+ "to-int-4": (value, key) => {
1474
+ const query = normalizeNumber(value);
1475
+ if (Number.isNaN(query)) {
1476
+ return "";
1477
+ }
1478
+ return key + "=" + encodeURIComponent(Math.round(query * 10000));
1479
+ },
1480
+ },
1481
+ });
1482
+
1483
+ if (self.getOption("formatter.marker.open")) {
1484
+ formatter.setMarker(
1485
+ self.getOption("formatter.marker.open"),
1486
+ self.getOption("formatter.marker.close"),
1487
+ );
1488
+ }
1489
+
1490
+ let queryPart = formatter.format(template);
1491
+ if (queryPart) {
1492
+ if (isFunction(wrapCallback)) {
1493
+ queryPart = wrapCallback(queryPart, mapping);
1494
+ }
1495
+ query.push(queryPart);
1496
+ }
1497
+ });
1498
+
1499
+ if (hasNoIdError) {
1500
+ reject(new Error("some or all filter elements have no id"));
1501
+ return;
1502
+ }
1503
+
1504
+ getGlobal().location.hash = createBracketedKeyValueHash(currentHash);
1505
+ resolve(query);
1506
+ });
1486
1507
  }
1487
1508
 
1488
1509
  /**
@@ -1491,41 +1512,41 @@ function collectSearchQueries() {
1491
1512
  * @return {null|Array|undefined|string}
1492
1513
  */
1493
1514
  function getControlValuesFromLabel(label) {
1494
- // finde das erste Kind-Element vom type input
1495
- // wenn es ein input-Element ist, dann @todo
1496
-
1497
- const foundControl = label.firstElementChild;
1498
-
1499
- if (foundControl) {
1500
- if (foundControl.tagName === "INPUT") {
1501
- if (foundControl.type === "checkbox") {
1502
- const checkedControls = label.querySelectorAll(
1503
- `${foundControl}:checked`,
1504
- );
1505
- const values = [];
1506
-
1507
- checkedControls.forEach((checkedControl) => {
1508
- values.push(checkedControl.value);
1509
- });
1510
-
1511
- return values;
1512
- } else if (foundControl.type === "radio") {
1513
- const checkedControl = label.querySelector(`${foundControl}:checked`);
1514
-
1515
- if (checkedControl) {
1516
- return checkedControl.value;
1517
- } else {
1518
- return null;
1519
- }
1520
- } else {
1521
- return foundControl.value;
1522
- }
1523
- } else {
1524
- return foundControl.value;
1525
- }
1526
- }
1527
-
1528
- return null;
1515
+ // finde das erste Kind-Element vom type input
1516
+ // wenn es ein input-Element ist, dann @todo
1517
+
1518
+ const foundControl = label.firstElementChild;
1519
+
1520
+ if (foundControl) {
1521
+ if (foundControl.tagName === "INPUT") {
1522
+ if (foundControl.type === "checkbox") {
1523
+ const checkedControls = label.querySelectorAll(
1524
+ `${foundControl}:checked`,
1525
+ );
1526
+ const values = [];
1527
+
1528
+ checkedControls.forEach((checkedControl) => {
1529
+ values.push(checkedControl.value);
1530
+ });
1531
+
1532
+ return values;
1533
+ } else if (foundControl.type === "radio") {
1534
+ const checkedControl = label.querySelector(`${foundControl}:checked`);
1535
+
1536
+ if (checkedControl) {
1537
+ return checkedControl.value;
1538
+ } else {
1539
+ return null;
1540
+ }
1541
+ } else {
1542
+ return foundControl.value;
1543
+ }
1544
+ } else {
1545
+ return foundControl.value;
1546
+ }
1547
+ }
1548
+
1549
+ return null;
1529
1550
  }
1530
1551
 
1531
1552
  /**
@@ -1533,60 +1554,60 @@ function getControlValuesFromLabel(label) {
1533
1554
  * @return {Promise<unknown>}
1534
1555
  */
1535
1556
  function initFromConfig() {
1536
- const host = findElementWithSelectorUpwards(this, "monster-host");
1537
-
1538
- if (!(isInstance(host, Host) && this.id)) {
1539
- return Promise.resolve();
1540
- }
1541
-
1542
- const configKey = getFilterConfigKey.call(this);
1543
-
1544
- return new Promise((resolve, reject) => {
1545
- host
1546
- .getConfig(configKey)
1547
- .then((config) => {
1548
- if ((config && isObject(config)) || isArray(config)) {
1549
- this[settingsSymbol].setOptions(config);
1550
- }
1551
- resolve();
1552
- })
1553
- .catch((error) => {
1554
- if (error === undefined) {
1555
- resolve();
1556
- return;
1557
- }
1558
-
1559
- // config not written
1560
- if (error?.message?.match(/is not defined/)) {
1561
- resolve();
1562
- return;
1563
- }
1564
-
1565
- addAttributeToken(
1566
- this,
1567
- ATTRIBUTE_ERRORMESSAGE,
1568
- error?.message || error,
1569
- );
1570
- reject(error);
1571
- });
1572
- });
1557
+ const host = findElementWithSelectorUpwards(this, "monster-host");
1558
+
1559
+ if (!(isInstance(host, Host) && this.id)) {
1560
+ return Promise.resolve();
1561
+ }
1562
+
1563
+ const configKey = getFilterConfigKey.call(this);
1564
+
1565
+ return new Promise((resolve, reject) => {
1566
+ host
1567
+ .getConfig(configKey)
1568
+ .then((config) => {
1569
+ if ((config && isObject(config)) || isArray(config)) {
1570
+ this[settingsSymbol].setOptions(config);
1571
+ }
1572
+ resolve();
1573
+ })
1574
+ .catch((error) => {
1575
+ if (error === undefined) {
1576
+ resolve();
1577
+ return;
1578
+ }
1579
+
1580
+ // config not written
1581
+ if (error?.message?.match(/is not defined/)) {
1582
+ resolve();
1583
+ return;
1584
+ }
1585
+
1586
+ addAttributeToken(
1587
+ this,
1588
+ ATTRIBUTE_ERRORMESSAGE,
1589
+ error?.message || error,
1590
+ );
1591
+ reject(error);
1592
+ });
1593
+ });
1573
1594
  }
1574
1595
 
1575
1596
  /**
1576
1597
  * @private
1577
1598
  */
1578
1599
  function updateConfig() {
1579
- const host = findElementWithSelectorUpwards(this, "monster-host");
1580
- if (!(host && this.id)) {
1581
- return;
1582
- }
1583
- const configKey = getFilterConfigKey.call(this);
1584
-
1585
- try {
1586
- host.setConfig(configKey, this[settingsSymbol].getOptions());
1587
- } catch (error) {
1588
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message || error);
1589
- }
1600
+ const host = findElementWithSelectorUpwards(this, "monster-host");
1601
+ if (!(host && this.id)) {
1602
+ return;
1603
+ }
1604
+ const configKey = getFilterConfigKey.call(this);
1605
+
1606
+ try {
1607
+ host.setConfig(configKey, this[settingsSymbol].getOptions());
1608
+ } catch (error) {
1609
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message || error);
1610
+ }
1590
1611
  }
1591
1612
 
1592
1613
  /**
@@ -1594,8 +1615,8 @@ function updateConfig() {
1594
1615
  * @return {string}
1595
1616
  */
1596
1617
  function getTemplate() {
1597
- // language=HTML
1598
- return `
1618
+ // language=HTML
1619
+ return `
1599
1620
  <div data-monster-role="control" part="control">
1600
1621
  <div data-monster-role="container">
1601
1622
  <div data-monster-role="layout">