@schukai/monster 3.57.0 → 3.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,62 +5,60 @@
5
5
  * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
6
  */
7
7
 
8
- import {findElementWithIdUpwards} from "./util.mjs";
9
- import {internalSymbol} from "../constants.mjs";
10
- import {extend} from "../data/extend.mjs";
11
- import {Pathfinder} from "../data/pathfinder.mjs";
12
- import {Formatter} from "../text/formatter.mjs";
13
-
14
- import {parseDataURL} from "../types/dataurl.mjs";
15
- import {getGlobalObject} from "../types/global.mjs";
8
+ import { findElementWithIdUpwards } from "./util.mjs";
9
+ import { internalSymbol } from "../constants.mjs";
10
+ import { extend } from "../data/extend.mjs";
11
+ import { Pathfinder } from "../data/pathfinder.mjs";
12
+ import { Formatter } from "../text/formatter.mjs";
13
+
14
+ import { parseDataURL } from "../types/dataurl.mjs";
15
+ import { getGlobalObject } from "../types/global.mjs";
16
16
  import {
17
- isArray,
18
- isFunction,
19
- isIterable,
20
- isObject,
21
- isString,
17
+ isArray,
18
+ isFunction,
19
+ isIterable,
20
+ isObject,
21
+ isString,
22
22
  } from "../types/is.mjs";
23
- import {Observer} from "../types/observer.mjs";
24
- import {ProxyObserver} from "../types/proxyobserver.mjs";
23
+ import { Observer } from "../types/observer.mjs";
24
+ import { ProxyObserver } from "../types/proxyobserver.mjs";
25
25
  import {
26
- validateFunction,
27
- validateInstance,
28
- validateObject,
26
+ validateFunction,
27
+ validateInstance,
28
+ validateObject,
29
29
  } from "../types/validate.mjs";
30
- import {clone} from "../util/clone.mjs";
30
+ import { clone } from "../util/clone.mjs";
31
31
  import {
32
- addAttributeToken,
33
- getLinkedObjects,
34
- hasObjectLink,
32
+ addAttributeToken,
33
+ getLinkedObjects,
34
+ hasObjectLink,
35
35
  } from "./attributes.mjs";
36
36
  import {
37
- ATTRIBUTE_DISABLED,
38
- ATTRIBUTE_ERRORMESSAGE,
39
- ATTRIBUTE_OPTIONS,
40
- ATTRIBUTE_INIT_CALLBACK,
41
- ATTRIBUTE_OPTIONS_SELECTOR,
42
- ATTRIBUTE_SCRIPT_HOST,
43
- customElementUpdaterLinkSymbol,
44
- initControlCallbackName,
37
+ ATTRIBUTE_DISABLED,
38
+ ATTRIBUTE_ERRORMESSAGE,
39
+ ATTRIBUTE_OPTIONS,
40
+ ATTRIBUTE_INIT_CALLBACK,
41
+ ATTRIBUTE_OPTIONS_SELECTOR,
42
+ ATTRIBUTE_SCRIPT_HOST,
43
+ customElementUpdaterLinkSymbol,
44
+ initControlCallbackName,
45
45
  } from "./constants.mjs";
46
- import {findDocumentTemplate, Template} from "./template.mjs";
47
- import {addObjectWithUpdaterToElement} from "./updater.mjs";
48
- import {instanceSymbol} from "../constants.mjs";
49
- import {
50
- getDocumentTranslations,
51
- } from "../i18n/translations.mjs";
52
- import {getSlottedElements} from "./slotted.mjs";
53
- import {initOptionsFromAttributes} from "./util/init-options-from-attributes.mjs";
54
- import {setOptionFromAttribute} from "./util/set-option-from-attribute.mjs";
46
+ import { findDocumentTemplate, Template } from "./template.mjs";
47
+ import { addObjectWithUpdaterToElement } from "./updater.mjs";
48
+ import { instanceSymbol } from "../constants.mjs";
49
+ import { getDocumentTranslations } from "../i18n/translations.mjs";
50
+ import { getSlottedElements } from "./slotted.mjs";
51
+ import { initOptionsFromAttributes } from "./util/init-options-from-attributes.mjs";
52
+ import { setOptionFromAttribute } from "./util/set-option-from-attribute.mjs";
55
53
 
56
54
  export {
57
- CustomElement,
58
- initMethodSymbol,
59
- assembleMethodSymbol,
60
- attributeObserverSymbol,
61
- registerCustomElement,
62
- getSlottedElements,
63
- updaterTransformerMethodsSymbol
55
+ CustomElement,
56
+ initMethodSymbol,
57
+ assembleMethodSymbol,
58
+ attributeObserverSymbol,
59
+ registerCustomElement,
60
+ getSlottedElements,
61
+ updaterTransformerMethodsSymbol,
64
62
  };
65
63
 
66
64
  /**
@@ -74,7 +72,7 @@ const initMethodSymbol = Symbol.for("@schukai/monster/dom/@@initMethodSymbol");
74
72
  * @type {symbol}
75
73
  */
76
74
  const assembleMethodSymbol = Symbol.for(
77
- "@schukai/monster/dom/@@assembleMethodSymbol",
75
+ "@schukai/monster/dom/@@assembleMethodSymbol",
78
76
  );
79
77
 
80
78
  /**
@@ -82,7 +80,7 @@ const assembleMethodSymbol = Symbol.for(
82
80
  * @type {symbol}
83
81
  */
84
82
  const updaterTransformerMethodsSymbol = Symbol.for(
85
- "@schukai/monster/dom/@@updaterTransformerMethodsSymbol",
83
+ "@schukai/monster/dom/@@updaterTransformerMethodsSymbol",
86
84
  );
87
85
 
88
86
  /**
@@ -91,7 +89,7 @@ const updaterTransformerMethodsSymbol = Symbol.for(
91
89
  * @type {symbol}
92
90
  */
93
91
  const attributeObserverSymbol = Symbol.for(
94
- "@schukai/monster/dom/@@attributeObserver",
92
+ "@schukai/monster/dom/@@attributeObserver",
95
93
  );
96
94
 
97
95
  /**
@@ -99,7 +97,7 @@ const attributeObserverSymbol = Symbol.for(
99
97
  * @type {symbol}
100
98
  */
101
99
  const attributeMutationObserverSymbol = Symbol(
102
- "@schukai/monster/dom/@@mutationObserver",
100
+ "@schukai/monster/dom/@@mutationObserver",
103
101
  );
104
102
 
105
103
  /**
@@ -238,517 +236,513 @@ const scriptHostElementSymbol = Symbol("scriptHostElement");
238
236
  * @summary A base class for HTML5 custom controls.
239
237
  */
240
238
  class CustomElement extends HTMLElement {
241
- /**
242
- * A new object is created. First the `initOptions` method is called. Here the
243
- * options can be defined in derived classes. Subsequently, the shadowRoot is initialized.
244
- *
245
- * IMPORTANT: CustomControls instances are not created via the constructor, but either via a tag in the HTML or via <code>document.createElement()</code>.
246
- *
247
- * @throws {Error} the options attribute does not contain a valid json definition.
248
- * @since 1.7.0
249
- */
250
- constructor() {
251
- super();
252
-
253
- this[attributeObserverSymbol] = {};
254
- this[internalSymbol] = new ProxyObserver({
255
- options: initOptionsFromAttributes(this, extend({}, this.defaults)),
256
- });
257
- this[initMethodSymbol]();
258
- initOptionObserver.call(this);
259
- this[scriptHostElementSymbol] = [];
260
- }
261
-
262
- /**
263
- * This method is called by the `instanceof` operator.
264
- * @returns {symbol}
265
- * @since 2.1.0
266
- */
267
- static get [instanceSymbol]() {
268
- return Symbol.for("@schukai/monster/dom/custom-element@@instance");
269
- }
270
-
271
- /**
272
- * This method determines which attributes are to be
273
- * monitored by `attributeChangedCallback()`. Unfortunately, this method is static.
274
- * Therefore, the `observedAttributes` property cannot be changed during runtime.
275
- *
276
- * @return {string[]}
277
- * @since 1.15.0
278
- */
279
- static get observedAttributes() {
280
- return [];
281
- }
282
-
283
- /**
284
- *
285
- * @param attribute
286
- * @param callback
287
- * @returns {Monster.DOM.CustomElement}
288
- */
289
- addAttributeObserver(attribute, callback) {
290
- validateFunction(callback);
291
- this[attributeObserverSymbol][attribute] = callback;
292
- return this;
293
- }
294
-
295
- /**
296
- *
297
- * @param attribute
298
- * @returns {Monster.DOM.CustomElement}
299
- */
300
- removeAttributeObserver(attribute) {
301
- delete this[attributeObserverSymbol][attribute];
302
- return this;
303
- }
304
-
305
- /**
306
- * The `defaults` property defines the default values for a control. If you want to override these,
307
- * you can use various methods, which are described in the documentation available at
308
- * {@link https://monsterjs.orgendocconfigurate-a-monster-control}.
309
- *
310
- * The individual configuration values are listed below:
311
- *
312
- * More information about the shadowRoot can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow),
313
- * in the [HTML Standard](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) or in the [WHATWG Wiki](https://wiki.whatwg.org/wiki/Custom_Elements).
314
- *
315
- * More information about the template element can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).
316
- *
317
- * More information about the slot element can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot).
318
- *
319
- * @property {boolean} disabled=false Specifies whether the control is disabled. When present, it makes the element non-mutable, non-focusable, and non-submittable with the form.
320
- * @property {string} shadowMode=open Specifies the mode of the shadow root. When set to `open`, elements in the shadow root are accessible from JavaScript outside the root, while setting it to `closed` denies access to the root's nodes from JavaScript outside it.
321
- * @property {Boolean} delegatesFocus=true Specifies the behavior of the control with respect to focusability. When set to `true`, it mitigates custom element issues around focusability. When a non-focusable part of the shadow DOM is clicked, the first focusable part is given focus, and the shadow host is given any available :focus styling.
322
- * @property {Object} templates Specifies the templates used by the control.
323
- * @property {string} templates.main=undefined Specifies the main template used by the control.
324
- * @property {Object} templateMapping Specifies the mapping of templates.
325
- * @since 1.8.0
326
- */
327
- get defaults() {
328
- return {
329
- disabled: false,
330
- shadowMode: "open",
331
- delegatesFocus: true,
332
- templates: {
333
- main: undefined,
334
- },
335
- templateMapping: {},
336
- };
337
- }
338
-
339
- /**
340
- * This method updates the labels of the element.
341
- * The labels are defined in the options object.
342
- * The key of the label is used to retrieve the translation from the document.
343
- * If the translation is different from the label, the label is updated.
344
- *
345
- * Before you can use this method, you must have loaded the translations.
346
- *
347
- * @returns {Monster.DOM.CustomElement}
348
- * @throws {Error} Cannot find element with translations. Add a translations object to the document.
349
- */
350
- updateI18n() {
351
- const translations = getDocumentTranslations();
352
- if (!translations) {
353
- return this;
354
- }
355
-
356
- const labels = this.getOption("labels");
357
- if (!(isObject(labels) || isIterable(labels))) {
358
- return this;
359
- }
360
-
361
- for (const key in labels) {
362
- const def = labels[key];
363
-
364
- if (isString(def)) {
365
- const text = translations.getText(key, def);
366
- if (text !== def) {
367
- this.setOption(`labels.${key}`, text);
368
- }
369
- continue;
370
- } else if (isObject(def)) {
371
- for (const k in def) {
372
- const d = def[k];
373
-
374
- const text = translations.getPluralRuleText(key, k, d);
375
- if (!isString(text)) {
376
- throw new Error("Invalid labels definition");
377
- }
378
- if (text !== d) {
379
- this.setOption(`labels.${key}.${k}`, text);
380
- }
381
- }
382
- continue;
383
- }
384
-
385
- throw new Error("Invalid labels definition");
386
- }
387
- return this;
388
- }
389
-
390
- /**
391
- * The `getTag()` method returns the tag name associated with the custom element. This method should be overwritten
392
- * by the derived class.
393
- *
394
- * Note that there is no check on the name of the tag in this class. It is the responsibility of
395
- * the developer to assign an appropriate tag name. If the name is not valid, the
396
- * `registerCustomElement()` method will issue an error.
397
- *
398
- * @see https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
399
- * @throws {Error} This method must be overridden by the derived class.
400
- * @return {string} The tag name associated with the custom element.
401
- * @since 1.7.0
402
- */
403
- static getTag() {
404
- throw new Error(
405
- "The method `getTag()` must be overridden by the derived class.",
406
- );
407
- }
408
-
409
- /**
410
- * The `getCSSStyleSheet()` method returns a `CSSStyleSheet` object that defines the styles for the custom element.
411
- * If the environment does not support the `CSSStyleSheet` constructor, then an object can be built using the provided detour.
412
- *
413
- * If `undefined` is returned, then the shadow root does not receive a stylesheet.
414
- *
415
- * Example usage:
416
- *
417
- * ```js
418
- * static getCSSStyleSheet() {
419
- * const sheet = new CSSStyleSheet();
420
- * sheet.replaceSync("p { color: red; }");
421
- * return sheet;
422
- * }
423
- * ```
424
- *
425
- * If the environment does not support the `CSSStyleSheet` constructor,
426
- * you can use the following workaround to create the stylesheet:
427
- *
428
- * ```js
429
- * const doc = document.implementation.createHTMLDocument('title');
430
- * let style = doc.createElement("style");
431
- * style.innerHTML = "p { color: red; }";
432
- * style.appendChild(document.createTextNode(""));
433
- * doc.head.appendChild(style);
434
- * return doc.styleSheets[0];
435
- * ```
436
- *
437
- * @return {CSSStyleSheet|CSSStyleSheet[]|string|undefined} A `CSSStyleSheet` object or an array of such objects that define the styles for the custom element, or `undefined` if no stylesheet should be applied.
438
- */
439
- static getCSSStyleSheet() {
440
- return undefined;
441
- }
442
-
443
- /**
444
- * attach a new observer
445
- *
446
- * @param {Observer} observer
447
- * @returns {CustomElement}
448
- */
449
- attachObserver(observer) {
450
- this[internalSymbol].attachObserver(observer);
451
- return this;
452
- }
453
-
454
- /**
455
- * detach a observer
456
- *
457
- * @param {Observer} observer
458
- * @returns {CustomElement}
459
- */
460
- detachObserver(observer) {
461
- this[internalSymbol].detachObserver(observer);
462
- return this;
463
- }
464
-
465
- /**
466
- * @param {Observer} observer
467
- * @returns {ProxyObserver}
468
- */
469
- containsObserver(observer) {
470
- return this[internalSymbol].containsObserver(observer);
471
- }
472
-
473
- /**
474
- * nested options can be specified by path `a.b.c`
475
- *
476
- * @param {string} path
477
- * @param {*} defaultValue
478
- * @return {*}
479
- * @since 1.10.0
480
- */
481
- getOption(path, defaultValue = undefined) {
482
- let value;
483
-
484
- try {
485
- value = new Pathfinder(
486
- this[internalSymbol].getRealSubject()["options"],
487
- ).getVia(path);
488
- } catch (e) {
489
- }
490
-
491
- if (value === undefined) return defaultValue;
492
- return value;
493
- }
494
-
495
- /**
496
- * Set option and inform elements
497
- *
498
- * @param {string} path
499
- * @param {*} value
500
- * @return {CustomElement}
501
- * @since 1.14.0
502
- */
503
- setOption(path, value) {
504
- new Pathfinder(this[internalSymbol].getSubject()["options"]).setVia(
505
- path,
506
- value,
507
- );
508
- return this;
509
- }
510
-
511
- /**
512
- * @since 1.15.0
513
- * @param {string|object} options
514
- * @return {CustomElement}
515
- */
516
- setOptions(options) {
517
- if (isString(options)) {
518
- options = parseOptionsJSON.call(this, options);
519
- }
520
- // 2024-01-21: remove this.defaults, otherwise it will overwrite
521
- // the current settings that have already been made.
522
- // https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/136
523
- extend(this[internalSymbol].getSubject()["options"], options);
524
-
525
- return this;
526
- }
527
-
528
- /**
529
- * Is called once via the constructor
530
- *
531
- * @return {CustomElement}
532
- * @since 1.8.0
533
- */
534
- [initMethodSymbol]() {
535
- return this;
536
- }
537
-
538
- /**
539
- * This method is called once when the object is equipped with update for the dynamic change of the dom.
540
- * The functions returned here can be used as pipe functions in the template.
541
- *
542
- * In the example, the function `my-transformer` is defined. In the template you can use it as follows:
543
- *
544
- * ```html
545
- * <my-element data-monster-option-transformer="path:my-value | call:my-transformer"></my-element>
546
- * ```
547
- *
548
- * @example
549
- * [updaterTransformerMethodsSymbol]() {
550
- * return {
551
- * "my-transformer": (value) => {
552
- * switch (typeof Wert) {
553
- * case "string":
554
- * return value + "!";
555
- * case "Zahl":
556
- * return value + 1;
557
- * default:
558
- * return value;
559
- * }
560
- * }
561
- * };
562
- * };
563
- *
564
- * @return {object}
565
- * @since 2.43.0
566
- */
567
- [updaterTransformerMethodsSymbol]() {
568
- return {};
569
- }
570
-
571
- /**
572
- * This method is called once when the object is included in the DOM for the first time. It performs the following actions:
573
- * 1. Extracts the options from the attributes and the script tag of the element and sets them.
574
- * 2. Initializes the shadow root and its CSS stylesheet (if specified).
575
- * 3. Initializes the HTML content of the element.
576
- * 4. Initializes the custom elements inside the shadow root and the slotted elements.
577
- * 5. Attaches a mutation observer to observe changes to the attributes of the element.
578
- *
579
- * @return {CustomElement} - The updated custom element.
580
- * @since 1.8.0
581
- */
582
- [assembleMethodSymbol]() {
583
- let elements;
584
- let nodeList;
585
-
586
- // Extract options from attributes and set them
587
- const AttributeOptions = getOptionsFromAttributes.call(this);
588
- if (
589
- isObject(AttributeOptions) &&
590
- Object.keys(AttributeOptions).length > 0
591
- ) {
592
- this.setOptions(AttributeOptions);
593
- }
594
-
595
- // Extract options from script tag and set them
596
- const ScriptOptions = getOptionsFromScriptTag.call(this);
597
- if (isObject(ScriptOptions) && Object.keys(ScriptOptions).length > 0) {
598
- this.setOptions(ScriptOptions);
599
- }
600
-
601
- // Initialize the shadow root and its CSS stylesheet
602
- if (this.getOption("shadowMode", false) !== false) {
603
- try {
604
- initShadowRoot.call(this);
605
- elements = this.shadowRoot.childNodes;
606
- } catch (e) {
607
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
608
- }
609
-
610
- try {
611
- initCSSStylesheet.call(this);
612
- } catch (e) {
613
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
614
- }
615
- }
616
-
617
- // If the elements are not found inside the shadow root, initialize the HTML content of the element
618
- if (!(elements instanceof NodeList)) {
619
- initHtmlContent.call(this);
620
- elements = this.childNodes;
621
- }
622
-
623
- // Initialize the custom elements inside the shadow root and the slotted elements
624
- initFromCallbackHost.call(this);
625
- try {
626
- nodeList = new Set([...elements, ...getSlottedElements.call(this)]);
627
- } catch (e) {
628
- nodeList = elements;
629
- }
630
-
631
- this[updateCloneDataSymbol] = clone(
632
- this[internalSymbol].getRealSubject()["options"],
633
- );
634
-
635
- addObjectWithUpdaterToElement.call(
636
- this,
637
- nodeList,
638
- customElementUpdaterLinkSymbol,
639
- this[updateCloneDataSymbol],
640
- );
641
-
642
- // Attach a mutation observer to observe changes to the attributes of the element
643
- attachAttributeChangeMutationObserver.call(this);
644
-
645
- return this;
646
- }
647
-
648
-
649
- /**
650
- * You know what you are doing? This function is only for advanced users.
651
- * The result is a clone of the internal data.
652
- *
653
- * @returns {*}
654
- */
655
- getInternalUpdateCloneData() {
656
- return clone(this[updateCloneDataSymbol]);
657
- }
658
-
659
- /**
660
- * This method is called every time the element is inserted into the DOM. It checks if the custom element
661
- * has already been initialized and if not, calls the assembleMethod to initialize it.
662
- *
663
- * @return {void}
664
- * @since 1.7.0
665
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/connectedCallback
666
- */
667
- connectedCallback() {
668
- // Check if the object has already been initialized
669
- if (!hasObjectLink(this, customElementUpdaterLinkSymbol)) {
670
- // If not, call the assembleMethod to initialize the object
671
- this[assembleMethodSymbol]();
672
- }
673
- }
674
-
675
- /**
676
- * Called every time the element is removed from the DOM. Useful for running clean up code.
677
- *
678
- * @return {void}
679
- * @since 1.7.0
680
- */
681
- disconnectedCallback() {
682
- }
683
-
684
- /**
685
- * The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)).
686
- *
687
- * @return {void}
688
- * @since 1.7.0
689
- */
690
- adoptedCallback() {
691
- }
692
-
693
- /**
694
- * Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial
695
- * values when an element is created by the parser, or upgraded. Note: only attributes listed in the observedAttributes
696
- * property will receive this callback.
697
- *
698
- * @param {string} attrName
699
- * @param {string} oldVal
700
- * @param {string} newVal
701
- * @return {void}
702
- * @since 1.15.0
703
- */
704
- attributeChangedCallback(attrName, oldVal, newVal) {
705
- if (attrName.startsWith("data-monster-option-")) {
706
- setOptionFromAttribute(
707
- this,
708
- attrName,
709
- this[internalSymbol].getSubject()["options"],
710
- );
711
- }
712
-
713
- const callback = this[attributeObserverSymbol]?.[attrName];
714
- if (isFunction(callback)) {
715
- try {
716
- callback.call(this, newVal, oldVal);
717
- } catch (e) {
718
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
719
- }
720
- }
721
- }
722
-
723
- /**
724
- *
725
- * @param {Node} node
726
- * @return {boolean}
727
- * @throws {TypeError} value is not an instance of
728
- * @since 1.19.0
729
- */
730
- hasNode(node) {
731
- if (containChildNode.call(this, validateInstance(node, Node))) {
732
- return true;
733
- }
734
-
735
- if (!(this.shadowRoot instanceof ShadowRoot)) {
736
- return false;
737
- }
738
-
739
- return containChildNode.call(this.shadowRoot, node);
740
- }
741
-
742
- /**
743
- * Calls a callback function if it exists.
744
- *
745
- * @param {string} name
746
- * @param {*} args
747
- * @returns {*}
748
- */
749
- callCallback(name, args) {
750
- return callControlCallback.call(this, name, ...args);
751
- }
239
+ /**
240
+ * A new object is created. First the `initOptions` method is called. Here the
241
+ * options can be defined in derived classes. Subsequently, the shadowRoot is initialized.
242
+ *
243
+ * IMPORTANT: CustomControls instances are not created via the constructor, but either via a tag in the HTML or via <code>document.createElement()</code>.
244
+ *
245
+ * @throws {Error} the options attribute does not contain a valid json definition.
246
+ * @since 1.7.0
247
+ */
248
+ constructor() {
249
+ super();
250
+
251
+ this[attributeObserverSymbol] = {};
252
+ this[internalSymbol] = new ProxyObserver({
253
+ options: initOptionsFromAttributes(this, extend({}, this.defaults)),
254
+ });
255
+ this[initMethodSymbol]();
256
+ initOptionObserver.call(this);
257
+ this[scriptHostElementSymbol] = [];
258
+ }
259
+
260
+ /**
261
+ * This method is called by the `instanceof` operator.
262
+ * @returns {symbol}
263
+ * @since 2.1.0
264
+ */
265
+ static get [instanceSymbol]() {
266
+ return Symbol.for("@schukai/monster/dom/custom-element@@instance");
267
+ }
268
+
269
+ /**
270
+ * This method determines which attributes are to be
271
+ * monitored by `attributeChangedCallback()`. Unfortunately, this method is static.
272
+ * Therefore, the `observedAttributes` property cannot be changed during runtime.
273
+ *
274
+ * @return {string[]}
275
+ * @since 1.15.0
276
+ */
277
+ static get observedAttributes() {
278
+ return [];
279
+ }
280
+
281
+ /**
282
+ *
283
+ * @param attribute
284
+ * @param callback
285
+ * @returns {Monster.DOM.CustomElement}
286
+ */
287
+ addAttributeObserver(attribute, callback) {
288
+ validateFunction(callback);
289
+ this[attributeObserverSymbol][attribute] = callback;
290
+ return this;
291
+ }
292
+
293
+ /**
294
+ *
295
+ * @param attribute
296
+ * @returns {Monster.DOM.CustomElement}
297
+ */
298
+ removeAttributeObserver(attribute) {
299
+ delete this[attributeObserverSymbol][attribute];
300
+ return this;
301
+ }
302
+
303
+ /**
304
+ * The `defaults` property defines the default values for a control. If you want to override these,
305
+ * you can use various methods, which are described in the documentation available at
306
+ * {@link https://monsterjs.orgendocconfigurate-a-monster-control}.
307
+ *
308
+ * The individual configuration values are listed below:
309
+ *
310
+ * More information about the shadowRoot can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow),
311
+ * in the [HTML Standard](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) or in the [WHATWG Wiki](https://wiki.whatwg.org/wiki/Custom_Elements).
312
+ *
313
+ * More information about the template element can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).
314
+ *
315
+ * More information about the slot element can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot).
316
+ *
317
+ * @property {boolean} disabled=false Specifies whether the control is disabled. When present, it makes the element non-mutable, non-focusable, and non-submittable with the form.
318
+ * @property {string} shadowMode=open Specifies the mode of the shadow root. When set to `open`, elements in the shadow root are accessible from JavaScript outside the root, while setting it to `closed` denies access to the root's nodes from JavaScript outside it.
319
+ * @property {Boolean} delegatesFocus=true Specifies the behavior of the control with respect to focusability. When set to `true`, it mitigates custom element issues around focusability. When a non-focusable part of the shadow DOM is clicked, the first focusable part is given focus, and the shadow host is given any available :focus styling.
320
+ * @property {Object} templates Specifies the templates used by the control.
321
+ * @property {string} templates.main=undefined Specifies the main template used by the control.
322
+ * @property {Object} templateMapping Specifies the mapping of templates.
323
+ * @since 1.8.0
324
+ */
325
+ get defaults() {
326
+ return {
327
+ disabled: false,
328
+ shadowMode: "open",
329
+ delegatesFocus: true,
330
+ templates: {
331
+ main: undefined,
332
+ },
333
+ templateMapping: {},
334
+ };
335
+ }
336
+
337
+ /**
338
+ * This method updates the labels of the element.
339
+ * The labels are defined in the options object.
340
+ * The key of the label is used to retrieve the translation from the document.
341
+ * If the translation is different from the label, the label is updated.
342
+ *
343
+ * Before you can use this method, you must have loaded the translations.
344
+ *
345
+ * @returns {Monster.DOM.CustomElement}
346
+ * @throws {Error} Cannot find element with translations. Add a translations object to the document.
347
+ */
348
+ updateI18n() {
349
+ const translations = getDocumentTranslations();
350
+ if (!translations) {
351
+ return this;
352
+ }
353
+
354
+ const labels = this.getOption("labels");
355
+ if (!(isObject(labels) || isIterable(labels))) {
356
+ return this;
357
+ }
358
+
359
+ for (const key in labels) {
360
+ const def = labels[key];
361
+
362
+ if (isString(def)) {
363
+ const text = translations.getText(key, def);
364
+ if (text !== def) {
365
+ this.setOption(`labels.${key}`, text);
366
+ }
367
+ continue;
368
+ } else if (isObject(def)) {
369
+ for (const k in def) {
370
+ const d = def[k];
371
+
372
+ const text = translations.getPluralRuleText(key, k, d);
373
+ if (!isString(text)) {
374
+ throw new Error("Invalid labels definition");
375
+ }
376
+ if (text !== d) {
377
+ this.setOption(`labels.${key}.${k}`, text);
378
+ }
379
+ }
380
+ continue;
381
+ }
382
+
383
+ throw new Error("Invalid labels definition");
384
+ }
385
+ return this;
386
+ }
387
+
388
+ /**
389
+ * The `getTag()` method returns the tag name associated with the custom element. This method should be overwritten
390
+ * by the derived class.
391
+ *
392
+ * Note that there is no check on the name of the tag in this class. It is the responsibility of
393
+ * the developer to assign an appropriate tag name. If the name is not valid, the
394
+ * `registerCustomElement()` method will issue an error.
395
+ *
396
+ * @see https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
397
+ * @throws {Error} This method must be overridden by the derived class.
398
+ * @return {string} The tag name associated with the custom element.
399
+ * @since 1.7.0
400
+ */
401
+ static getTag() {
402
+ throw new Error(
403
+ "The method `getTag()` must be overridden by the derived class.",
404
+ );
405
+ }
406
+
407
+ /**
408
+ * The `getCSSStyleSheet()` method returns a `CSSStyleSheet` object that defines the styles for the custom element.
409
+ * If the environment does not support the `CSSStyleSheet` constructor, then an object can be built using the provided detour.
410
+ *
411
+ * If `undefined` is returned, then the shadow root does not receive a stylesheet.
412
+ *
413
+ * Example usage:
414
+ *
415
+ * ```js
416
+ * static getCSSStyleSheet() {
417
+ * const sheet = new CSSStyleSheet();
418
+ * sheet.replaceSync("p { color: red; }");
419
+ * return sheet;
420
+ * }
421
+ * ```
422
+ *
423
+ * If the environment does not support the `CSSStyleSheet` constructor,
424
+ * you can use the following workaround to create the stylesheet:
425
+ *
426
+ * ```js
427
+ * const doc = document.implementation.createHTMLDocument('title');
428
+ * let style = doc.createElement("style");
429
+ * style.innerHTML = "p { color: red; }";
430
+ * style.appendChild(document.createTextNode(""));
431
+ * doc.head.appendChild(style);
432
+ * return doc.styleSheets[0];
433
+ * ```
434
+ *
435
+ * @return {CSSStyleSheet|CSSStyleSheet[]|string|undefined} A `CSSStyleSheet` object or an array of such objects that define the styles for the custom element, or `undefined` if no stylesheet should be applied.
436
+ */
437
+ static getCSSStyleSheet() {
438
+ return undefined;
439
+ }
440
+
441
+ /**
442
+ * attach a new observer
443
+ *
444
+ * @param {Observer} observer
445
+ * @returns {CustomElement}
446
+ */
447
+ attachObserver(observer) {
448
+ this[internalSymbol].attachObserver(observer);
449
+ return this;
450
+ }
451
+
452
+ /**
453
+ * detach a observer
454
+ *
455
+ * @param {Observer} observer
456
+ * @returns {CustomElement}
457
+ */
458
+ detachObserver(observer) {
459
+ this[internalSymbol].detachObserver(observer);
460
+ return this;
461
+ }
462
+
463
+ /**
464
+ * @param {Observer} observer
465
+ * @returns {ProxyObserver}
466
+ */
467
+ containsObserver(observer) {
468
+ return this[internalSymbol].containsObserver(observer);
469
+ }
470
+
471
+ /**
472
+ * nested options can be specified by path `a.b.c`
473
+ *
474
+ * @param {string} path
475
+ * @param {*} defaultValue
476
+ * @return {*}
477
+ * @since 1.10.0
478
+ */
479
+ getOption(path, defaultValue = undefined) {
480
+ let value;
481
+
482
+ try {
483
+ value = new Pathfinder(
484
+ this[internalSymbol].getRealSubject()["options"],
485
+ ).getVia(path);
486
+ } catch (e) {}
487
+
488
+ if (value === undefined) return defaultValue;
489
+ return value;
490
+ }
491
+
492
+ /**
493
+ * Set option and inform elements
494
+ *
495
+ * @param {string} path
496
+ * @param {*} value
497
+ * @return {CustomElement}
498
+ * @since 1.14.0
499
+ */
500
+ setOption(path, value) {
501
+ new Pathfinder(this[internalSymbol].getSubject()["options"]).setVia(
502
+ path,
503
+ value,
504
+ );
505
+ return this;
506
+ }
507
+
508
+ /**
509
+ * @since 1.15.0
510
+ * @param {string|object} options
511
+ * @return {CustomElement}
512
+ */
513
+ setOptions(options) {
514
+ if (isString(options)) {
515
+ options = parseOptionsJSON.call(this, options);
516
+ }
517
+ // 2024-01-21: remove this.defaults, otherwise it will overwrite
518
+ // the current settings that have already been made.
519
+ // https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/136
520
+ extend(this[internalSymbol].getSubject()["options"], options);
521
+
522
+ return this;
523
+ }
524
+
525
+ /**
526
+ * Is called once via the constructor
527
+ *
528
+ * @return {CustomElement}
529
+ * @since 1.8.0
530
+ */
531
+ [initMethodSymbol]() {
532
+ return this;
533
+ }
534
+
535
+ /**
536
+ * This method is called once when the object is equipped with update for the dynamic change of the dom.
537
+ * The functions returned here can be used as pipe functions in the template.
538
+ *
539
+ * In the example, the function `my-transformer` is defined. In the template you can use it as follows:
540
+ *
541
+ * ```html
542
+ * <my-element data-monster-option-transformer="path:my-value | call:my-transformer"></my-element>
543
+ * ```
544
+ *
545
+ * @example
546
+ * [updaterTransformerMethodsSymbol]() {
547
+ * return {
548
+ * "my-transformer": (value) => {
549
+ * switch (typeof Wert) {
550
+ * case "string":
551
+ * return value + "!";
552
+ * case "Zahl":
553
+ * return value + 1;
554
+ * default:
555
+ * return value;
556
+ * }
557
+ * }
558
+ * };
559
+ * };
560
+ *
561
+ * @return {object}
562
+ * @since 2.43.0
563
+ */
564
+ [updaterTransformerMethodsSymbol]() {
565
+ return {};
566
+ }
567
+
568
+ /**
569
+ * This method is called once when the object is included in the DOM for the first time. It performs the following actions:
570
+ * 1. Extracts the options from the attributes and the script tag of the element and sets them.
571
+ * 2. Initializes the shadow root and its CSS stylesheet (if specified).
572
+ * 3. Initializes the HTML content of the element.
573
+ * 4. Initializes the custom elements inside the shadow root and the slotted elements.
574
+ * 5. Attaches a mutation observer to observe changes to the attributes of the element.
575
+ *
576
+ * @return {CustomElement} - The updated custom element.
577
+ * @since 1.8.0
578
+ */
579
+ [assembleMethodSymbol]() {
580
+ let elements;
581
+ let nodeList;
582
+
583
+ // Extract options from attributes and set them
584
+ const AttributeOptions = getOptionsFromAttributes.call(this);
585
+ if (
586
+ isObject(AttributeOptions) &&
587
+ Object.keys(AttributeOptions).length > 0
588
+ ) {
589
+ this.setOptions(AttributeOptions);
590
+ }
591
+
592
+ // Extract options from script tag and set them
593
+ const ScriptOptions = getOptionsFromScriptTag.call(this);
594
+ if (isObject(ScriptOptions) && Object.keys(ScriptOptions).length > 0) {
595
+ this.setOptions(ScriptOptions);
596
+ }
597
+
598
+ // Initialize the shadow root and its CSS stylesheet
599
+ if (this.getOption("shadowMode", false) !== false) {
600
+ try {
601
+ initShadowRoot.call(this);
602
+ elements = this.shadowRoot.childNodes;
603
+ } catch (e) {
604
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
605
+ }
606
+
607
+ try {
608
+ initCSSStylesheet.call(this);
609
+ } catch (e) {
610
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
611
+ }
612
+ }
613
+
614
+ // If the elements are not found inside the shadow root, initialize the HTML content of the element
615
+ if (!(elements instanceof NodeList)) {
616
+ initHtmlContent.call(this);
617
+ elements = this.childNodes;
618
+ }
619
+
620
+ // Initialize the custom elements inside the shadow root and the slotted elements
621
+ initFromCallbackHost.call(this);
622
+ try {
623
+ nodeList = new Set([...elements, ...getSlottedElements.call(this)]);
624
+ } catch (e) {
625
+ nodeList = elements;
626
+ }
627
+
628
+ this[updateCloneDataSymbol] = clone(
629
+ this[internalSymbol].getRealSubject()["options"],
630
+ );
631
+
632
+ addObjectWithUpdaterToElement.call(
633
+ this,
634
+ nodeList,
635
+ customElementUpdaterLinkSymbol,
636
+ this[updateCloneDataSymbol],
637
+ );
638
+
639
+ // Attach a mutation observer to observe changes to the attributes of the element
640
+ attachAttributeChangeMutationObserver.call(this);
641
+
642
+ return this;
643
+ }
644
+
645
+ /**
646
+ * You know what you are doing? This function is only for advanced users.
647
+ * The result is a clone of the internal data.
648
+ *
649
+ * @returns {*}
650
+ */
651
+ getInternalUpdateCloneData() {
652
+ return clone(this[updateCloneDataSymbol]);
653
+ }
654
+
655
+ /**
656
+ * This method is called every time the element is inserted into the DOM. It checks if the custom element
657
+ * has already been initialized and if not, calls the assembleMethod to initialize it.
658
+ *
659
+ * @return {void}
660
+ * @since 1.7.0
661
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/connectedCallback
662
+ */
663
+ connectedCallback() {
664
+ // Check if the object has already been initialized
665
+ if (!hasObjectLink(this, customElementUpdaterLinkSymbol)) {
666
+ // If not, call the assembleMethod to initialize the object
667
+ this[assembleMethodSymbol]();
668
+ }
669
+ }
670
+
671
+ /**
672
+ * Called every time the element is removed from the DOM. Useful for running clean up code.
673
+ *
674
+ * @return {void}
675
+ * @since 1.7.0
676
+ */
677
+ disconnectedCallback() {}
678
+
679
+ /**
680
+ * The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)).
681
+ *
682
+ * @return {void}
683
+ * @since 1.7.0
684
+ */
685
+ adoptedCallback() {}
686
+
687
+ /**
688
+ * Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial
689
+ * values when an element is created by the parser, or upgraded. Note: only attributes listed in the observedAttributes
690
+ * property will receive this callback.
691
+ *
692
+ * @param {string} attrName
693
+ * @param {string} oldVal
694
+ * @param {string} newVal
695
+ * @return {void}
696
+ * @since 1.15.0
697
+ */
698
+ attributeChangedCallback(attrName, oldVal, newVal) {
699
+ if (attrName.startsWith("data-monster-option-")) {
700
+ setOptionFromAttribute(
701
+ this,
702
+ attrName,
703
+ this[internalSymbol].getSubject()["options"],
704
+ );
705
+ }
706
+
707
+ const callback = this[attributeObserverSymbol]?.[attrName];
708
+ if (isFunction(callback)) {
709
+ try {
710
+ callback.call(this, newVal, oldVal);
711
+ } catch (e) {
712
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
713
+ }
714
+ }
715
+ }
716
+
717
+ /**
718
+ *
719
+ * @param {Node} node
720
+ * @return {boolean}
721
+ * @throws {TypeError} value is not an instance of
722
+ * @since 1.19.0
723
+ */
724
+ hasNode(node) {
725
+ if (containChildNode.call(this, validateInstance(node, Node))) {
726
+ return true;
727
+ }
728
+
729
+ if (!(this.shadowRoot instanceof ShadowRoot)) {
730
+ return false;
731
+ }
732
+
733
+ return containChildNode.call(this.shadowRoot, node);
734
+ }
735
+
736
+ /**
737
+ * Calls a callback function if it exists.
738
+ *
739
+ * @param {string} name
740
+ * @param {*} args
741
+ * @returns {*}
742
+ */
743
+ callCallback(name, args) {
744
+ return callControlCallback.call(this, name, ...args);
745
+ }
752
746
  }
753
747
 
754
748
  /**
@@ -757,50 +751,50 @@ class CustomElement extends HTMLElement {
757
751
  * @return {any}
758
752
  */
759
753
  function callControlCallback(callBackFunctionName, ...args) {
760
- if (!isString(callBackFunctionName) || callBackFunctionName === "") {
761
- return;
762
- }
763
-
764
- if (callBackFunctionName in this) {
765
- return this[callBackFunctionName](this, ...args);
766
- }
767
-
768
- if (!this.hasAttribute(ATTRIBUTE_SCRIPT_HOST)) {
769
- return;
770
- }
771
-
772
- if (this[scriptHostElementSymbol].length === 0) {
773
- const targetId = this.getAttribute(ATTRIBUTE_SCRIPT_HOST);
774
- if (!targetId) {
775
- return;
776
- }
777
-
778
- const list = targetId.split(",");
779
- for (const id of list) {
780
- const host = findElementWithIdUpwards(this, targetId);
781
- if (!(host instanceof HTMLElement)) {
782
- continue;
783
- }
784
-
785
- this[scriptHostElementSymbol].push(host);
786
- }
787
- }
788
-
789
- for (const host of this[scriptHostElementSymbol]) {
790
- if (callBackFunctionName in host) {
791
- try {
792
- return host[callBackFunctionName](this, ...args);
793
- } catch (e) {
794
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
795
- }
796
- }
797
- }
798
-
799
- addAttributeToken(
800
- this,
801
- ATTRIBUTE_ERRORMESSAGE,
802
- `callback ${callBackFunctionName} not found`,
803
- );
754
+ if (!isString(callBackFunctionName) || callBackFunctionName === "") {
755
+ return;
756
+ }
757
+
758
+ if (callBackFunctionName in this) {
759
+ return this[callBackFunctionName](this, ...args);
760
+ }
761
+
762
+ if (!this.hasAttribute(ATTRIBUTE_SCRIPT_HOST)) {
763
+ return;
764
+ }
765
+
766
+ if (this[scriptHostElementSymbol].length === 0) {
767
+ const targetId = this.getAttribute(ATTRIBUTE_SCRIPT_HOST);
768
+ if (!targetId) {
769
+ return;
770
+ }
771
+
772
+ const list = targetId.split(",");
773
+ for (const id of list) {
774
+ const host = findElementWithIdUpwards(this, targetId);
775
+ if (!(host instanceof HTMLElement)) {
776
+ continue;
777
+ }
778
+
779
+ this[scriptHostElementSymbol].push(host);
780
+ }
781
+ }
782
+
783
+ for (const host of this[scriptHostElementSymbol]) {
784
+ if (callBackFunctionName in host) {
785
+ try {
786
+ return host[callBackFunctionName](this, ...args);
787
+ } catch (e) {
788
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.toString());
789
+ }
790
+ }
791
+ }
792
+
793
+ addAttributeToken(
794
+ this,
795
+ ATTRIBUTE_ERRORMESSAGE,
796
+ `callback ${callBackFunctionName} not found`,
797
+ );
804
798
  }
805
799
 
806
800
  /**
@@ -817,16 +811,16 @@ function callControlCallback(callBackFunctionName, ...args) {
817
811
  * @since 1.8.0
818
812
  */
819
813
  function initFromCallbackHost() {
820
- // Set the default callback function name
821
- let callBackFunctionName = initControlCallbackName;
814
+ // Set the default callback function name
815
+ let callBackFunctionName = initControlCallbackName;
822
816
 
823
- // If the `data-monster-option-callback` attribute is set, use its value as the callback function name
824
- if (this.hasAttribute(ATTRIBUTE_INIT_CALLBACK)) {
825
- callBackFunctionName = this.getAttribute(ATTRIBUTE_INIT_CALLBACK);
826
- }
817
+ // If the `data-monster-option-callback` attribute is set, use its value as the callback function name
818
+ if (this.hasAttribute(ATTRIBUTE_INIT_CALLBACK)) {
819
+ callBackFunctionName = this.getAttribute(ATTRIBUTE_INIT_CALLBACK);
820
+ }
827
821
 
828
- // Call the callback function with the element as a parameter if it exists
829
- callControlCallback.call(this, callBackFunctionName);
822
+ // Call the callback function with the element as a parameter if it exists
823
+ callControlCallback.call(this, callBackFunctionName);
830
824
  }
831
825
 
832
826
  /**
@@ -836,35 +830,35 @@ function initFromCallbackHost() {
836
830
  * @this CustomElement
837
831
  */
838
832
  function attachAttributeChangeMutationObserver() {
839
- const self = this;
840
-
841
- if (typeof self[attributeMutationObserverSymbol] !== "undefined") {
842
- return;
843
- }
844
-
845
- self[attributeMutationObserverSymbol] = new MutationObserver(function (
846
- mutations,
847
- observer,
848
- ) {
849
- for (const mutation of mutations) {
850
- if (mutation.type === "attributes") {
851
- self.attributeChangedCallback(
852
- mutation.attributeName,
853
- mutation.oldValue,
854
- mutation.target.getAttribute(mutation.attributeName),
855
- );
856
- }
857
- }
858
- });
859
-
860
- try {
861
- self[attributeMutationObserverSymbol].observe(self, {
862
- attributes: true,
863
- attributeOldValue: true,
864
- });
865
- } catch (e) {
866
- addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
867
- }
833
+ const self = this;
834
+
835
+ if (typeof self[attributeMutationObserverSymbol] !== "undefined") {
836
+ return;
837
+ }
838
+
839
+ self[attributeMutationObserverSymbol] = new MutationObserver(function (
840
+ mutations,
841
+ observer,
842
+ ) {
843
+ for (const mutation of mutations) {
844
+ if (mutation.type === "attributes") {
845
+ self.attributeChangedCallback(
846
+ mutation.attributeName,
847
+ mutation.oldValue,
848
+ mutation.target.getAttribute(mutation.attributeName),
849
+ );
850
+ }
851
+ }
852
+ });
853
+
854
+ try {
855
+ self[attributeMutationObserverSymbol].observe(self, {
856
+ attributes: true,
857
+ attributeOldValue: true,
858
+ });
859
+ } catch (e) {
860
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
861
+ }
868
862
  }
869
863
 
870
864
  /**
@@ -874,19 +868,19 @@ function attachAttributeChangeMutationObserver() {
874
868
  * @return {boolean}
875
869
  */
876
870
  function containChildNode(node) {
877
- if (this.contains(node)) {
878
- return true;
879
- }
871
+ if (this.contains(node)) {
872
+ return true;
873
+ }
880
874
 
881
- for (const [, e] of Object.entries(this.childNodes)) {
882
- if (e.contains(node)) {
883
- return true;
884
- }
875
+ for (const [, e] of Object.entries(this.childNodes)) {
876
+ if (e.contains(node)) {
877
+ return true;
878
+ }
885
879
 
886
- containChildNode.call(e, node);
887
- }
880
+ containChildNode.call(e, node);
881
+ }
888
882
 
889
- return false;
883
+ return false;
890
884
  }
891
885
 
892
886
  /**
@@ -896,89 +890,89 @@ function containChildNode(node) {
896
890
  * @this CustomElement
897
891
  */
898
892
  function initOptionObserver() {
899
- const self = this;
900
-
901
- let lastDisabledValue = undefined;
902
- self.attachObserver(
903
- new Observer(function () {
904
- const flag = self.getOption("disabled");
905
-
906
- if (flag === lastDisabledValue) {
907
- return;
908
- }
909
-
910
- lastDisabledValue = flag;
911
-
912
- if (!(self.shadowRoot instanceof ShadowRoot)) {
913
- return;
914
- }
915
-
916
- const query =
917
- "button, command, fieldset, keygen, optgroup, option, select, textarea, input, [data-monster-objectlink]";
918
- const elements = self.shadowRoot.querySelectorAll(query);
919
-
920
- let nodeList;
921
- try {
922
- nodeList = new Set([
923
- ...elements,
924
- ...getSlottedElements.call(self, query),
925
- ]);
926
- } catch (e) {
927
- nodeList = elements;
928
- }
929
-
930
- for (const element of [...nodeList]) {
931
- if (flag === true) {
932
- element.setAttribute(ATTRIBUTE_DISABLED, "");
933
- } else {
934
- element.removeAttribute(ATTRIBUTE_DISABLED);
935
- }
936
- }
937
- }),
938
- );
939
-
940
- self.attachObserver(
941
- new Observer(function () {
942
- // not initialised
943
- if (!hasObjectLink(self, customElementUpdaterLinkSymbol)) {
944
- return;
945
- }
946
- // inform every element
947
- const updaters = getLinkedObjects(self, customElementUpdaterLinkSymbol);
948
-
949
- for (const list of updaters) {
950
- for (const updater of list) {
951
- const d = clone(self[internalSymbol].getRealSubject()["options"]);
952
- Object.assign(updater.getSubject(), d);
953
- }
954
- }
955
- }),
956
- );
957
-
958
- // disabled
959
- self[attributeObserverSymbol][ATTRIBUTE_DISABLED] = () => {
960
- if (self.hasAttribute(ATTRIBUTE_DISABLED)) {
961
- self.setOption(ATTRIBUTE_DISABLED, true);
962
- } else {
963
- self.setOption(ATTRIBUTE_DISABLED, undefined);
964
- }
965
- };
966
-
967
- // data-monster-options
968
- self[attributeObserverSymbol][ATTRIBUTE_OPTIONS] = () => {
969
- const options = getOptionsFromAttributes.call(self);
970
- if (isObject(options) && Object.keys(options).length > 0) {
971
- self.setOptions(options);
972
- }
973
- };
974
-
975
- // data-monster-options-selector
976
- self[attributeObserverSymbol][ATTRIBUTE_OPTIONS_SELECTOR] = () => {
977
- const options = getOptionsFromScriptTag.call(self);
978
- if (isObject(options) && Object.keys(options).length > 0) {
979
- self.setOptions(options);
980
- }
981
- };
893
+ const self = this;
894
+
895
+ let lastDisabledValue = undefined;
896
+ self.attachObserver(
897
+ new Observer(function () {
898
+ const flag = self.getOption("disabled");
899
+
900
+ if (flag === lastDisabledValue) {
901
+ return;
902
+ }
903
+
904
+ lastDisabledValue = flag;
905
+
906
+ if (!(self.shadowRoot instanceof ShadowRoot)) {
907
+ return;
908
+ }
909
+
910
+ const query =
911
+ "button, command, fieldset, keygen, optgroup, option, select, textarea, input, [data-monster-objectlink]";
912
+ const elements = self.shadowRoot.querySelectorAll(query);
913
+
914
+ let nodeList;
915
+ try {
916
+ nodeList = new Set([
917
+ ...elements,
918
+ ...getSlottedElements.call(self, query),
919
+ ]);
920
+ } catch (e) {
921
+ nodeList = elements;
922
+ }
923
+
924
+ for (const element of [...nodeList]) {
925
+ if (flag === true) {
926
+ element.setAttribute(ATTRIBUTE_DISABLED, "");
927
+ } else {
928
+ element.removeAttribute(ATTRIBUTE_DISABLED);
929
+ }
930
+ }
931
+ }),
932
+ );
933
+
934
+ self.attachObserver(
935
+ new Observer(function () {
936
+ // not initialised
937
+ if (!hasObjectLink(self, customElementUpdaterLinkSymbol)) {
938
+ return;
939
+ }
940
+ // inform every element
941
+ const updaters = getLinkedObjects(self, customElementUpdaterLinkSymbol);
942
+
943
+ for (const list of updaters) {
944
+ for (const updater of list) {
945
+ const d = clone(self[internalSymbol].getRealSubject()["options"]);
946
+ Object.assign(updater.getSubject(), d);
947
+ }
948
+ }
949
+ }),
950
+ );
951
+
952
+ // disabled
953
+ self[attributeObserverSymbol][ATTRIBUTE_DISABLED] = () => {
954
+ if (self.hasAttribute(ATTRIBUTE_DISABLED)) {
955
+ self.setOption(ATTRIBUTE_DISABLED, true);
956
+ } else {
957
+ self.setOption(ATTRIBUTE_DISABLED, undefined);
958
+ }
959
+ };
960
+
961
+ // data-monster-options
962
+ self[attributeObserverSymbol][ATTRIBUTE_OPTIONS] = () => {
963
+ const options = getOptionsFromAttributes.call(self);
964
+ if (isObject(options) && Object.keys(options).length > 0) {
965
+ self.setOptions(options);
966
+ }
967
+ };
968
+
969
+ // data-monster-options-selector
970
+ self[attributeObserverSymbol][ATTRIBUTE_OPTIONS_SELECTOR] = () => {
971
+ const options = getOptionsFromScriptTag.call(self);
972
+ if (isObject(options) && Object.keys(options).length > 0) {
973
+ self.setOptions(options);
974
+ }
975
+ };
982
976
  }
983
977
 
984
978
  /**
@@ -987,37 +981,37 @@ function initOptionObserver() {
987
981
  * @throws {TypeError} value is not a object
988
982
  */
989
983
  function getOptionsFromScriptTag() {
990
- if (!this.hasAttribute(ATTRIBUTE_OPTIONS_SELECTOR)) {
991
- return {};
992
- }
993
-
994
- const node = document.querySelector(
995
- this.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR),
996
- );
997
- if (!(node instanceof HTMLScriptElement)) {
998
- addAttributeToken(
999
- this,
1000
- ATTRIBUTE_ERRORMESSAGE,
1001
- `the selector ${ATTRIBUTE_OPTIONS_SELECTOR} for options was specified (${this.getAttribute(
1002
- ATTRIBUTE_OPTIONS_SELECTOR,
1003
- )}) but not found.`,
1004
- );
1005
- return {};
1006
- }
1007
-
1008
- let obj = {};
1009
-
1010
- try {
1011
- obj = parseOptionsJSON.call(this, node.textContent.trim());
1012
- } catch (e) {
1013
- addAttributeToken(
1014
- this,
1015
- ATTRIBUTE_ERRORMESSAGE,
1016
- `when analyzing the configuration from the script tag there was an error. ${e}`,
1017
- );
1018
- }
1019
-
1020
- return obj;
984
+ if (!this.hasAttribute(ATTRIBUTE_OPTIONS_SELECTOR)) {
985
+ return {};
986
+ }
987
+
988
+ const node = document.querySelector(
989
+ this.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR),
990
+ );
991
+ if (!(node instanceof HTMLScriptElement)) {
992
+ addAttributeToken(
993
+ this,
994
+ ATTRIBUTE_ERRORMESSAGE,
995
+ `the selector ${ATTRIBUTE_OPTIONS_SELECTOR} for options was specified (${this.getAttribute(
996
+ ATTRIBUTE_OPTIONS_SELECTOR,
997
+ )}) but not found.`,
998
+ );
999
+ return {};
1000
+ }
1001
+
1002
+ let obj = {};
1003
+
1004
+ try {
1005
+ obj = parseOptionsJSON.call(this, node.textContent.trim());
1006
+ } catch (e) {
1007
+ addAttributeToken(
1008
+ this,
1009
+ ATTRIBUTE_ERRORMESSAGE,
1010
+ `when analyzing the configuration from the script tag there was an error. ${e}`,
1011
+ );
1012
+ }
1013
+
1014
+ return obj;
1021
1015
  }
1022
1016
 
1023
1017
  /**
@@ -1025,21 +1019,21 @@ function getOptionsFromScriptTag() {
1025
1019
  * @return {object}
1026
1020
  */
1027
1021
  function getOptionsFromAttributes() {
1028
- if (this.hasAttribute(ATTRIBUTE_OPTIONS)) {
1029
- try {
1030
- return parseOptionsJSON.call(this, this.getAttribute(ATTRIBUTE_OPTIONS));
1031
- } catch (e) {
1032
- addAttributeToken(
1033
- this,
1034
- ATTRIBUTE_ERRORMESSAGE,
1035
- `the options attribute ${ATTRIBUTE_OPTIONS} does not contain a valid json definition (actual: ${this.getAttribute(
1036
- ATTRIBUTE_OPTIONS,
1037
- )}).${e}`,
1038
- );
1039
- }
1040
- }
1041
-
1042
- return {};
1022
+ if (this.hasAttribute(ATTRIBUTE_OPTIONS)) {
1023
+ try {
1024
+ return parseOptionsJSON.call(this, this.getAttribute(ATTRIBUTE_OPTIONS));
1025
+ } catch (e) {
1026
+ addAttributeToken(
1027
+ this,
1028
+ ATTRIBUTE_ERRORMESSAGE,
1029
+ `the options attribute ${ATTRIBUTE_OPTIONS} does not contain a valid json definition (actual: ${this.getAttribute(
1030
+ ATTRIBUTE_OPTIONS,
1031
+ )}).${e}`,
1032
+ );
1033
+ }
1034
+ }
1035
+
1036
+ return {};
1043
1037
  }
1044
1038
 
1045
1039
  /**
@@ -1051,26 +1045,25 @@ function getOptionsFromAttributes() {
1051
1045
  * @throws {error} Throws an error if the JSON data is not valid.
1052
1046
  */
1053
1047
  function parseOptionsJSON(data) {
1054
- let obj = {};
1055
-
1056
- if (!isString(data)) {
1057
- return obj;
1058
- }
1059
-
1060
- // the configuration can be specified as a data url.
1061
- try {
1062
- const dataUrl = parseDataURL(data);
1063
- data = dataUrl.content;
1064
- } catch (e) {
1065
- }
1066
-
1067
- try {
1068
- obj = JSON.parse(data);
1069
- } catch (e) {
1070
- throw e;
1071
- }
1072
-
1073
- return validateObject(obj);
1048
+ let obj = {};
1049
+
1050
+ if (!isString(data)) {
1051
+ return obj;
1052
+ }
1053
+
1054
+ // the configuration can be specified as a data url.
1055
+ try {
1056
+ const dataUrl = parseDataURL(data);
1057
+ data = dataUrl.content;
1058
+ } catch (e) {}
1059
+
1060
+ try {
1061
+ obj = JSON.parse(data);
1062
+ } catch (e) {
1063
+ throw e;
1064
+ }
1065
+
1066
+ return validateObject(obj);
1074
1067
  }
1075
1068
 
1076
1069
  /**
@@ -1078,21 +1071,21 @@ function parseOptionsJSON(data) {
1078
1071
  * @return {initHtmlContent}
1079
1072
  */
1080
1073
  function initHtmlContent() {
1081
- try {
1082
- const template = findDocumentTemplate(this.constructor.getTag());
1083
- this.appendChild(template.createDocumentFragment());
1084
- } catch (e) {
1085
- let html = this.getOption("templates.main", "");
1086
- if (isString(html) && html.length > 0) {
1087
- const mapping = this.getOption("templateMapping", {});
1088
- if (isObject(mapping)) {
1089
- html = new Formatter(mapping, {}).format(html);
1090
- }
1091
- this.innerHTML = html;
1092
- }
1093
- }
1094
-
1095
- return this;
1074
+ try {
1075
+ const template = findDocumentTemplate(this.constructor.getTag());
1076
+ this.appendChild(template.createDocumentFragment());
1077
+ } catch (e) {
1078
+ let html = this.getOption("templates.main", "");
1079
+ if (isString(html) && html.length > 0) {
1080
+ const mapping = this.getOption("templateMapping", {});
1081
+ if (isObject(mapping)) {
1082
+ html = new Formatter(mapping, {}).format(html);
1083
+ }
1084
+ this.innerHTML = html;
1085
+ }
1086
+ }
1087
+
1088
+ return this;
1096
1089
  }
1097
1090
 
1098
1091
  /**
@@ -1105,49 +1098,49 @@ function initHtmlContent() {
1105
1098
  * @throws {TypeError} value is not an instance of
1106
1099
  */
1107
1100
  function initCSSStylesheet() {
1108
- if (!(this.shadowRoot instanceof ShadowRoot)) {
1109
- return this;
1110
- }
1111
-
1112
- const styleSheet = this.constructor.getCSSStyleSheet();
1113
-
1114
- if (styleSheet instanceof CSSStyleSheet) {
1115
- if (styleSheet.cssRules.length > 0) {
1116
- this.shadowRoot.adoptedStyleSheets = [styleSheet];
1117
- }
1118
- } else if (isArray(styleSheet)) {
1119
- const assign = [];
1120
- for (const s of styleSheet) {
1121
- if (isString(s)) {
1122
- const trimedStyleSheet = s.trim();
1123
- if (trimedStyleSheet !== "") {
1124
- const style = document.createElement("style");
1125
- style.innerHTML = trimedStyleSheet;
1126
- this.shadowRoot.prepend(style);
1127
- }
1128
- continue;
1129
- }
1130
-
1131
- validateInstance(s, CSSStyleSheet);
1132
-
1133
- if (s.cssRules.length > 0) {
1134
- assign.push(s);
1135
- }
1136
- }
1137
-
1138
- if (assign.length > 0) {
1139
- this.shadowRoot.adoptedStyleSheets = assign;
1140
- }
1141
- } else if (isString(styleSheet)) {
1142
- const trimedStyleSheet = styleSheet.trim();
1143
- if (trimedStyleSheet !== "") {
1144
- const style = document.createElement("style");
1145
- style.innerHTML = styleSheet;
1146
- this.shadowRoot.prepend(style);
1147
- }
1148
- }
1149
-
1150
- return this;
1101
+ if (!(this.shadowRoot instanceof ShadowRoot)) {
1102
+ return this;
1103
+ }
1104
+
1105
+ const styleSheet = this.constructor.getCSSStyleSheet();
1106
+
1107
+ if (styleSheet instanceof CSSStyleSheet) {
1108
+ if (styleSheet.cssRules.length > 0) {
1109
+ this.shadowRoot.adoptedStyleSheets = [styleSheet];
1110
+ }
1111
+ } else if (isArray(styleSheet)) {
1112
+ const assign = [];
1113
+ for (const s of styleSheet) {
1114
+ if (isString(s)) {
1115
+ const trimedStyleSheet = s.trim();
1116
+ if (trimedStyleSheet !== "") {
1117
+ const style = document.createElement("style");
1118
+ style.innerHTML = trimedStyleSheet;
1119
+ this.shadowRoot.prepend(style);
1120
+ }
1121
+ continue;
1122
+ }
1123
+
1124
+ validateInstance(s, CSSStyleSheet);
1125
+
1126
+ if (s.cssRules.length > 0) {
1127
+ assign.push(s);
1128
+ }
1129
+ }
1130
+
1131
+ if (assign.length > 0) {
1132
+ this.shadowRoot.adoptedStyleSheets = assign;
1133
+ }
1134
+ } else if (isString(styleSheet)) {
1135
+ const trimedStyleSheet = styleSheet.trim();
1136
+ if (trimedStyleSheet !== "") {
1137
+ const style = document.createElement("style");
1138
+ style.innerHTML = styleSheet;
1139
+ this.shadowRoot.prepend(style);
1140
+ }
1141
+ }
1142
+
1143
+ return this;
1151
1144
  }
1152
1145
 
1153
1146
  /**
@@ -1160,35 +1153,35 @@ function initCSSStylesheet() {
1160
1153
  * @since 1.8.0
1161
1154
  */
1162
1155
  function initShadowRoot() {
1163
- let template;
1164
- let html;
1165
-
1166
- try {
1167
- template = findDocumentTemplate(this.constructor.getTag());
1168
- } catch (e) {
1169
- html = this.getOption("templates.main", "");
1170
- if (!isString(html) || html === undefined || html === "") {
1171
- throw new Error("html is not set.");
1172
- }
1173
- }
1174
-
1175
- this.attachShadow({
1176
- mode: this.getOption("shadowMode", "open"),
1177
- delegatesFocus: this.getOption("delegatesFocus", true),
1178
- });
1179
-
1180
- if (template instanceof Template) {
1181
- this.shadowRoot.appendChild(template.createDocumentFragment());
1182
- return this;
1183
- }
1184
-
1185
- const mapping = this.getOption("templateMapping", {});
1186
- if (isObject(mapping)) {
1187
- html = new Formatter(mapping).format(html);
1188
- }
1189
-
1190
- this.shadowRoot.innerHTML = html;
1191
- return this;
1156
+ let template;
1157
+ let html;
1158
+
1159
+ try {
1160
+ template = findDocumentTemplate(this.constructor.getTag());
1161
+ } catch (e) {
1162
+ html = this.getOption("templates.main", "");
1163
+ if (!isString(html) || html === undefined || html === "") {
1164
+ throw new Error("html is not set.");
1165
+ }
1166
+ }
1167
+
1168
+ this.attachShadow({
1169
+ mode: this.getOption("shadowMode", "open"),
1170
+ delegatesFocus: this.getOption("delegatesFocus", true),
1171
+ });
1172
+
1173
+ if (template instanceof Template) {
1174
+ this.shadowRoot.appendChild(template.createDocumentFragment());
1175
+ return this;
1176
+ }
1177
+
1178
+ const mapping = this.getOption("templateMapping", {});
1179
+ if (isObject(mapping)) {
1180
+ html = new Formatter(mapping).format(html);
1181
+ }
1182
+
1183
+ this.shadowRoot.innerHTML = html;
1184
+ return this;
1192
1185
  }
1193
1186
 
1194
1187
  /**
@@ -1203,20 +1196,20 @@ function initShadowRoot() {
1203
1196
  * @throws {DOMException} Failed to execute 'define' on 'CustomElementRegistry': is not a valid custom element name
1204
1197
  */
1205
1198
  function registerCustomElement(element) {
1206
- validateFunction(element);
1207
- const customElements = getGlobalObject("customElements");
1208
- if (customElements === undefined) {
1209
- throw new Error("customElements is not supported.");
1210
- }
1211
-
1212
- const tag = element?.getTag();
1213
- if (!isString(tag) || tag === "") {
1214
- throw new Error("tag is not set.");
1215
- }
1216
-
1217
- if (customElements.get(tag) !== undefined) {
1218
- return;
1219
- }
1220
-
1221
- customElements.define(tag, element);
1199
+ validateFunction(element);
1200
+ const customElements = getGlobalObject("customElements");
1201
+ if (customElements === undefined) {
1202
+ throw new Error("customElements is not supported.");
1203
+ }
1204
+
1205
+ const tag = element?.getTag();
1206
+ if (!isString(tag) || tag === "") {
1207
+ throw new Error("tag is not set.");
1208
+ }
1209
+
1210
+ if (customElements.get(tag) !== undefined) {
1211
+ return;
1212
+ }
1213
+
1214
+ customElements.define(tag, element);
1222
1215
  }