@schukai/monster 3.57.0 → 3.58.1

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