@schukai/monster 3.47.0 → 3.49.0

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schukai/monster",
3
- "version": "3.47.0",
3
+ "version": "3.49.0",
4
4
  "description": "Monster is a simple library for creating fast, robust and lightweight websites.",
5
5
  "keywords": [
6
6
  "framework",
@@ -59,9 +59,9 @@ export {
59
59
  ATTRIBUTE_HIDDEN,
60
60
  objectUpdaterLinkSymbol,
61
61
  customElementUpdaterLinkSymbol,
62
- optionCallbackName,
62
+ initControlCallbackName,
63
63
  ATTRIBUTE_SCRIPT_HOST,
64
- ATTRIBUTE_OPTION_CALLBACK
64
+ ATTRIBUTE_INIT_CALLBACK
65
65
  };
66
66
 
67
67
  /**
@@ -117,7 +117,7 @@ const ATTRIBUTE_OPTIONS_SELECTOR = `${ATTRIBUTE_PREFIX}options-selector`;
117
117
  * @since 3.48.0
118
118
  * @type {string}
119
119
  */
120
- const ATTRIBUTE_OPTION_CALLBACK = `${ATTRIBUTE_PREFIX}option-callback`;
120
+ const ATTRIBUTE_INIT_CALLBACK = `${ATTRIBUTE_PREFIX}init-callback`;
121
121
 
122
122
  /**
123
123
  * This is the name of the callback to pass the callback to a control
@@ -127,7 +127,7 @@ const ATTRIBUTE_OPTION_CALLBACK = `${ATTRIBUTE_PREFIX}option-callback`;
127
127
  * @since 3.48.0
128
128
  * @type {string}
129
129
  */
130
- const optionCallbackName = `initCustomControlOptionsCallback`;
130
+ const initControlCallbackName = `initCustomControlCallback`;
131
131
 
132
132
  /**
133
133
  * @memberOf Monster.DOM
@@ -6,7 +6,8 @@
6
6
  */
7
7
 
8
8
  import {extend} from "../data/extend.mjs";
9
- import {ATTRIBUTE_VALUE} from "./constants.mjs";
9
+ import {addAttributeToken} from "./attributes.mjs";
10
+ import {ATTRIBUTE_ERRORMESSAGE} from "./constants.mjs";
10
11
  import {CustomElement, attributeObserverSymbol} from "./customelement.mjs";
11
12
  import {instanceSymbol} from "../constants.mjs";
12
13
 
@@ -19,57 +20,66 @@ export {CustomControl};
19
20
  const attachedInternalSymbol = Symbol("attachedInternal");
20
21
 
21
22
  /**
22
- * To define a new HTML control we need the power of CustomElement
23
+ * This is a base class for creating custom controls using the power of CustomElement.
23
24
  *
24
- * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called
25
- * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM.
25
+ * After defining a `CustomElement`, the `registerCustomElement` method must be called with the new class name. Only then
26
+ * will the tag defined via the `getTag` method be made known to the DOM.
26
27
  *
27
28
  * <img src="./images/customcontrol-class.png">
28
29
  *
29
- * This control uses `attachInternals()` to integrate the control into a form.
30
- * If the target environment does not support this method, the [polyfill](https://www.npmjs.com/package/element-internals-polyfill ) can be used.
30
+ * This control uses `attachInternals()` to integrate the control into a form. If the target environment does not support
31
+ * this method, the [polyfill](https://www.npmjs.com/package/element-internals-polyfill) can be used.
31
32
  *
32
- * You can create the object via the function `document.createElement()`.
33
+ * You can create the object using the function `document.createElement()`.
33
34
  *
34
- * @startuml customcontrol-class.png
35
- * skinparam monochrome true
36
- * skinparam shadowing false
37
- * HTMLElement <|-- CustomElement
38
- * CustomElement <|-- CustomControl
39
- * @enduml
35
+ * This control uses `attachInternals()` to integrate the control into a form. If the target environment does not support
36
+ * this method, the Polyfill for attachInternals() can be used: {@link https://www.npmjs.com/package/element-internals-polyfill|element-internals-polyfill}.
40
37
  *
41
- * @summary A base class for customcontrols based on CustomElement
42
- * @see {@link https://www.npmjs.com/package/element-internals-polyfill}
43
- * @see {@link https://github.com/WICG/webcomponents}
44
- * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements}
45
- * @see {@link https://html.spec.whatwg.org/dev/custom-elements.html#custom-element-reactions}
38
+ * Learn more about WICG Web Components: {@link https://github.com/WICG/webcomponents|WICG Web Components}.
39
+ *
40
+ * Read the HTML specification for Custom Elements: {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements|Custom Elements}.
41
+ *
42
+ * Read the HTML specification for Custom Element Reactions: {@link https://html.spec.whatwg.org/dev/custom-elements.html#custom-element-reactions|Custom Element Reactions}.
43
+ *
44
+ * @summary A base class for custom controls based on CustomElement.
45
+ * @copyright schukai GmbH
46
46
  * @license AGPLv3
47
47
  * @since 1.14.0
48
- * @copyright schukai GmbH
49
48
  * @memberOf Monster.DOM
49
+ * @extends Monster.DOM.CustomElement
50
50
  */
51
51
  class CustomControl extends CustomElement {
52
+
52
53
  /**
53
- * IMPORTANT: CustomControls instances are not created via the constructor, but either via a tag in the HTML or via <code>document.createElement()</code>.
54
+ * The constructor method of CustomControl, which is called when creating a new instance.
55
+ * It checks whether the element supports `attachInternals()` and initializes an internal form-associated element
56
+ * if supported. Additionally, it initializes a MutationObserver to watch for attribute changes.
57
+ *
58
+ * See the links below for more information:
59
+ * {@link https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-define|CustomElementRegistry.define()}
60
+ * {@link https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-get|CustomElementRegistry.get()}
61
+ * and {@link https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals|ElementInternals}
54
62
  *
63
+ * @inheritdoc
55
64
  * @throws {Error} the ElementInternals is not supported and a polyfill is necessary
56
- * @summary create new Instance
65
+ * @since 1.7.0
57
66
  */
58
67
  constructor() {
59
68
  super();
60
69
 
70
+ // check if element supports `attachInternals()`
61
71
  if (typeof this["attachInternals"] === "function") {
62
- /**
63
- * currently only supported by chrome
64
- * @property {Object}
65
- * @private
66
- */
67
72
  this[attachedInternalSymbol] = this.attachInternals();
73
+ } else {
74
+ // `attachInternals()` is not supported, so a polyfill is necessary
75
+ throw Error("the ElementInternals is not supported and a polyfill is necessary");
68
76
  }
69
77
 
78
+ // initialize a MutationObserver to watch for attribute changes
70
79
  initObserver.call(this);
71
80
  }
72
81
 
82
+
73
83
  /**
74
84
  * This method is called by the `instanceof` operator.
75
85
  * @returns {symbol}
@@ -90,40 +100,27 @@ class CustomControl extends CustomElement {
90
100
  }
91
101
 
92
102
  /**
93
- * Adding a static formAssociated property, with a true value, makes an autonomous custom element a form-associated custom element.
103
+ * Adding a static `formAssociated` property, with a true value, makes an autonomous custom element a form-associated custom element.
94
104
  *
95
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
96
- * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example}
105
+ * @see [attachInternals()]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
106
+ * @see [Custom Elements Face Example]{@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example}
97
107
  * @since 1.14.0
98
108
  * @return {boolean}
99
109
  */
100
- static formAssociated = true
110
+ static formAssociated = true;
101
111
 
102
112
  /**
103
- * Derived classes can override and extend this method as follows.
104
- *
105
- * ```
106
- * get defaults() {
107
- * return extends{}, super.defaults, {
108
- * myValue:true
109
- * });
110
- * }
111
- * ```
112
- *
113
- * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example}
114
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
115
- * @return {object}
113
+ * @inheritdoc
116
114
  * @since 1.14.0
117
- */
115
+ **/
118
116
  get defaults() {
119
- return extend({
120
- }, super.defaults);
117
+ return extend({}, super.defaults);
121
118
  }
122
119
 
123
120
  /**
124
121
  * Must be overridden by a derived class and return the value of the control.
125
122
  *
126
- * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals)
123
+ * This is a method of [internal API](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals), which is a part of the web standard for custom elements.
127
124
  *
128
125
  * @since 1.14.0
129
126
  * @throws {Error} the value getter must be overwritten by the derived class
@@ -133,11 +130,11 @@ class CustomControl extends CustomElement {
133
130
  }
134
131
 
135
132
  /**
136
- * Must be overridden by a derived class and return the value of the control.
133
+ * Must be overridden by a derived class and set the value of the control.
137
134
  *
138
- * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals)
135
+ * This is a method of [internal API](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals), which is a part of the web standard for custom elements.
139
136
  *
140
- * @param {*} value
137
+ * @param {*} value The value to set.
141
138
  * @since 1.14.0
142
139
  * @throws {Error} the value setter must be overwritten by the derived class
143
140
  */
@@ -145,6 +142,7 @@ class CustomControl extends CustomElement {
145
142
  throw Error("the value setter must be overwritten by the derived class");
146
143
  }
147
144
 
145
+
148
146
  /**
149
147
  * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals)
150
148
  *
@@ -180,8 +178,8 @@ class CustomControl extends CustomElement {
180
178
  *
181
179
  * @return {ValidityState}
182
180
  * @throws {Error} the ElementInternals is not supported and a polyfill is necessary
183
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState}
184
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/validity}
181
+ * @see [ValidityState]{@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState}
182
+ * @see [validity]{@link https://developer.mozilla.org/en-US/docs/Web/API/validity}
185
183
  */
186
184
  get validity() {
187
185
  return getInternal.call(this)?.validity;
@@ -214,7 +212,7 @@ class CustomControl extends CustomElement {
214
212
  /**
215
213
  * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals)
216
214
  *
217
- * @return {CustomStateSet}
215
+ * @return {boolean}
218
216
  * @since 1.14.0
219
217
  * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states
220
218
  * @throws {Error} the ElementInternals is not supported and a polyfill is necessary
@@ -301,12 +299,15 @@ class CustomControl extends CustomElement {
301
299
  }
302
300
 
303
301
  /**
304
- * @param {string} form
302
+ * Sets the `form` attribute of the custom control to the `id` of the passed form element.
303
+ * If no form element is passed, removes the `form` attribute.
304
+ *
305
+ * @param {HTMLFormElement} form - The form element to associate with the control
305
306
  */
306
307
  formAssociatedCallback(form) {
307
308
  if (form) {
308
- if(form.id) {
309
- this.setAttribute("form", form.id);
309
+ if (form.id) {
310
+ this.setAttribute("form", form.id);
310
311
  }
311
312
  } else {
312
313
  this.removeAttribute("form");
@@ -314,7 +315,9 @@ class CustomControl extends CustomElement {
314
315
  }
315
316
 
316
317
  /**
317
- * @param {string} disabled
318
+ * Sets or removes the `disabled` attribute of the custom control based on the passed value.
319
+ *
320
+ * @param {boolean} disabled - Whether or not the control should be disabled
318
321
  */
319
322
  formDisabledCallback(disabled) {
320
323
  if (disabled) {
@@ -324,19 +327,20 @@ class CustomControl extends CustomElement {
324
327
  }
325
328
  }
326
329
 
330
+
327
331
  /**
328
332
  * @param {string} state
329
333
  * @param {string} mode
330
334
  */
331
335
  formStateRestoreCallback(state, mode) {
332
-
336
+
333
337
  }
334
338
 
335
339
  /**
336
340
  *
337
341
  */
338
342
  formResetCallback() {
339
- this.value = "";
343
+ this.value = "";
340
344
  }
341
345
 
342
346
  }
@@ -23,11 +23,11 @@ import {
23
23
  ATTRIBUTE_DISABLED,
24
24
  ATTRIBUTE_ERRORMESSAGE,
25
25
  ATTRIBUTE_OPTIONS,
26
- ATTRIBUTE_OPTION_CALLBACK,
26
+ ATTRIBUTE_INIT_CALLBACK,
27
27
  ATTRIBUTE_OPTIONS_SELECTOR,
28
28
  ATTRIBUTE_SCRIPT_HOST,
29
29
  customElementUpdaterLinkSymbol,
30
- optionCallbackName
30
+ initControlCallbackName
31
31
  } from "./constants.mjs";
32
32
  import {findDocumentTemplate, Template} from "./template.mjs";
33
33
  import {addObjectWithUpdaterToElement} from "./updater.mjs";
@@ -121,15 +121,12 @@ const scriptHostElementSymbol = Symbol("scriptHostElement");
121
121
  */
122
122
 
123
123
  /**
124
- * To define a new HTML element we need the power of CustomElement
124
+ * The `CustomElement` class provides a way to define a new HTML element using the power of Custom Elements.
125
125
  *
126
- * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called
127
- * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM.
128
- *
129
- * <img src="./images/customelement-class.png">
130
- *
131
- * You can create the object via the function `document.createElement()`.
126
+ * **IMPORTANT:** After defining a `CustomElement`, the `registerCustomElement` method must be called with the new class name
127
+ * to make the tag defined via the `getTag` method known to the DOM.
132
128
  *
129
+ * You can create an instance of the object via the `document.createElement()` function.
133
130
  *
134
131
  * ## Interaction
135
132
  *
@@ -137,15 +134,13 @@ const scriptHostElementSymbol = Symbol("scriptHostElement");
137
134
  *
138
135
  * ## Styling
139
136
  *
140
- * For optimal display of custom-elements the pseudo-class :defined can be used.
137
+ * To display custom elements optimally, the `:defined` pseudo-class can be used. To prevent custom elements from being displayed and flickering until the control is registered,
138
+ * it is recommended to create a CSS directive.
141
139
  *
142
- * To prevent the custom elements from being displayed and flickering until the control is registered, it is recommended to create a css directive.
140
+ * In the simplest case, you can simply hide the control:
143
141
  *
144
- * In the simplest case, you can simply hide the control.
145
- *
146
- * ```
142
+ * ```html
147
143
  * <style>
148
- *
149
144
  * my-custom-element:not(:defined) {
150
145
  * display: none;
151
146
  * }
@@ -153,62 +148,64 @@ const scriptHostElementSymbol = Symbol("scriptHostElement");
153
148
  * my-custom-element:defined {
154
149
  * display: flex;
155
150
  * }
156
- *
157
151
  * </style>
158
152
  * ```
159
153
  *
160
- * Alternatively you can also display a loader
154
+ * Alternatively, you can display a loader:
161
155
  *
162
- * ```
156
+ * ```css
163
157
  * my-custom-element:not(:defined) {
164
- * display: flex;
165
- * box-shadow: 0 4px 10px 0 rgba(33, 33, 33, 0.15);
166
- * border-radius: 4px;
167
- * height: 200px;
168
- * position: relative;
169
- * overflow: hidden;
170
- * }
158
+ * display: flex;
159
+ * box-shadow: 0 4px 10px 0 rgba(33, 33, 33, 0.15);
160
+ * border-radius: 4px;
161
+ * height: 200px;
162
+ * position: relative;
163
+ * overflow: hidden;
164
+ * }
171
165
  *
172
166
  * my-custom-element:not(:defined)::before {
173
- * content: '';
174
- * display: block;
175
- * position: absolute;
176
- * left: -150px;
177
- * top: 0;
178
- * height: 100%;
179
- * width: 150px;
180
- * background: linear-gradient(to right, transparent 0%, #E8E8E8 50%, transparent 100%);
181
- * animation: load 1s cubic-bezier(0.4, 0.0, 0.2, 1) infinite;
182
- * }
167
+ * content: '';
168
+ * display: block;
169
+ * position: absolute;
170
+ * left: -150px;
171
+ * top: 0;
172
+ * height: 100%;
173
+ * width: 150px;
174
+ * background: linear-gradient(to right, transparent 0%, #E8E8E8 50%, transparent 100%);
175
+ * animation: load 1s cubic-bezier(0.4, 0.0, 0.2, 1) infinite;
176
+ * }
183
177
  *
184
178
  * @keyframes load {
185
- * from {
186
- * left: -150px;
187
- * }
188
- * to {
189
- * left: 100%;
190
- * }
191
- * }
179
+ * from {
180
+ * left: -150px;
181
+ * }
182
+ * to {
183
+ * left: 100%;
184
+ * }
185
+ * }
192
186
  *
193
187
  * my-custom-element:defined {
194
- * display: flex;
195
- * }
188
+ * display: flex;
189
+ * }
196
190
  * ```
191
+ *
192
+ * More information about Custom Elements can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements).
193
+ * And 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).
197
194
  *
198
195
  * @externalExample ../../example/dom/theme.mjs
199
- * @see https://github.com/WICG/webcomponents
200
- * @see https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements
201
196
  * @license AGPLv3
202
197
  * @since 1.7.0
203
198
  * @copyright schukai GmbH
204
199
  * @memberOf Monster.DOM
205
200
  * @extends external:HTMLElement
206
- * @summary A base class for HTML5 customcontrols
201
+ * @summary A base class for HTML5 custom controls.
207
202
  */
208
203
  class CustomElement extends HTMLElement {
209
204
  /**
210
205
  * A new object is created. First the `initOptions` method is called. Here the
211
206
  * options can be defined in derived classes. Subsequently, the shadowRoot is initialized.
207
+ *
208
+ * IMPORTANT: CustomControls instances are not created via the constructor, but either via a tag in the HTML or via <code>document.createElement()</code>.
212
209
  *
213
210
  * @throws {Error} the options attribute does not contain a valid json definition.
214
211
  * @since 1.7.0
@@ -270,53 +267,25 @@ class CustomElement extends HTMLElement {
270
267
  }
271
268
 
272
269
  /**
273
- * Derived classes can override and extend this method as follows.
270
+ * The `defaults` property defines the default values for a control. If you want to override these,
271
+ * you can use various methods, which are described in the documentation available at
272
+ * {@link https://monsterjs.orgendocconfigurate-a-monster-control}.
274
273
  *
275
- * ```
276
- * get defaults() {
277
- * return Object.assign({}, super.defaults, {
278
- * myValue:true
279
- * });
280
- * }
281
- * ```
282
- *
283
- * To set the options via the html tag the attribute data-monster-options must be set.
284
- * As value a JSON object with the desired values must be defined.
285
- *
286
- * Since 1.18.0 the JSON can be specified as a DataURI.
287
- *
288
- * ```
289
- * new Monster.Types.DataUrl(btoa(JSON.stringify({
290
- * shadowMode: 'open',
291
- * delegatesFocus: true,
292
- * templates: {
293
- * main: undefined
294
- * }
295
- * })),'application/json',true).toString()
296
- * ```
297
- *
298
- * The attribute data-monster-options-selector can be used to access a script tag that contains additional configuration.
299
- *
300
- * As value a selector must be specified, which belongs to a script tag and contains the configuration as json.
301
- *
302
- * ```
303
- * <script id="id-for-this-config" type="application/json">
304
- * {
305
- * "config-key": "config-value"
306
- * }
307
- * </script>
308
- * ```
274
+ * The individual configuration values are listed below:
309
275
  *
310
- * The individual configuration values can be found in the table.
276
+ * More information about the shadowRoot can be found in the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow),
277
+ * 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).
311
278
  *
312
- * @property {boolean} disabled=false Object The Boolean disabled attribute, when present, makes the element not mutable, focusable, or even submitted with the form.
313
- * @property {string} shadowMode=open `open` Elements of the shadow root are accessible from JavaScript outside the root, for example using. `close` Denies access to the node(s) of a closed shadow root from JavaScript outside it
314
- * @property {Boolean} delegatesFocus=true A boolean that, when set to true, specifies behavior that 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.
315
- * @property {Object} templates Templates
316
- * @property {string} templates.main=undefined Main template
317
- * @property {Object} templateMapping Template mapping
279
+ * 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).
318
280
  *
319
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow
281
+ * 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).
282
+ *
283
+ * @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.
284
+ * @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.
285
+ * @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.
286
+ * @property {Object} templates Specifies the templates used by the control.
287
+ * @property {string} templates.main=undefined Specifies the main template used by the control.
288
+ * @property {Object} templateMapping Specifies the mapping of templates.
320
289
  * @since 1.8.0
321
290
  */
322
291
  get defaults() {
@@ -383,39 +352,51 @@ class CustomElement extends HTMLElement {
383
352
  }
384
353
 
385
354
  /**
386
- * There is no check on the name by this class. the developer is responsible for assigning an appropriate tag.
387
- * if the name is not valid, registerCustomElement() will issue an error
355
+ * The `getTag()` method returns the tag name associated with the custom element. This method should be overwritten
356
+ * by the derived class.
388
357
  *
389
- * @link https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
390
- * @return {string}
391
- * @throws {Error} the method getTag must be overwritten by the derived class.
358
+ * Note that there is no check on the name of the tag in this class. It is the responsibility of
359
+ * the developer to assign an appropriate tag name. If the name is not valid, the
360
+ * `registerCustomElement()` method will issue an error.
361
+ *
362
+ * @see https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
363
+ * @throws {Error} This method must be overridden by the derived class.
364
+ * @return {string} The tag name associated with the custom element.
392
365
  * @since 1.7.0
393
366
  */
394
367
  static getTag() {
395
- throw new Error("the method getTag must be overwritten by the derived class.");
368
+ throw new Error("The method `getTag()` must be overridden by the derived class.");
396
369
  }
397
370
 
398
371
  /**
399
- * At this point a `CSSStyleSheet` object can be returned. If the environment does not
400
- * support a constructor, then an object can also be built using the following detour.
372
+ * The `getCSSStyleSheet()` method returns a `CSSStyleSheet` object that defines the styles for the custom element.
373
+ * If the environment does not support the `CSSStyleSheet` constructor, then an object can be built using the provided detour.
374
+ *
375
+ * If `undefined` is returned, then the shadow root does not receive a stylesheet.
401
376
  *
402
- * If `undefined` is returned then the shadowRoot does not get a stylesheet.
377
+ * Example usage:
403
378
  *
379
+ * ```js
380
+ * static getCSSStyleSheet() {
381
+ * const sheet = new CSSStyleSheet();
382
+ * sheet.replaceSync("p { color: red; }");
383
+ * return sheet;
384
+ * }
404
385
  * ```
405
- * const doc = document.implementation.createHTMLDocument('title');
406
386
  *
407
- * let style = doc.createElement("style");
408
- * style.innerHTML="p{color:red;}";
387
+ * If the environment does not support the `CSSStyleSheet` constructor,
388
+ * you can use the following workaround to create the stylesheet:
409
389
  *
410
- * // WebKit Hack
390
+ * ```js
391
+ * const doc = document.implementation.createHTMLDocument('title');
392
+ * let style = doc.createElement("style");
393
+ * style.innerHTML = "p { color: red; }";
411
394
  * style.appendChild(document.createTextNode(""));
412
- * // Add the <style> element to the page
413
395
  * doc.head.appendChild(style);
414
396
  * return doc.styleSheets[0];
415
- * ;
416
397
  * ```
417
398
  *
418
- * @return {CSSStyleSheet|CSSStyleSheet[]|string|undefined}
399
+ * @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.
419
400
  */
420
401
  static getCSSStyleSheet() {
421
402
  return undefined;
@@ -511,9 +492,14 @@ class CustomElement extends HTMLElement {
511
492
  }
512
493
 
513
494
  /**
514
- * Is called once when the object is included in the DOM for the first time.
495
+ * This method is called once when the object is included in the DOM for the first time. It performs the following actions:
496
+ * 1. Extracts the options from the attributes and the script tag of the element and sets them.
497
+ * 2. Initializes the shadow root and its CSS stylesheet (if specified).
498
+ * 3. Initializes the HTML content of the element.
499
+ * 4. Initializes the custom elements inside the shadow root and the slotted elements.
500
+ * 5. Attaches a mutation observer to observe changes to the attributes of the element.
515
501
  *
516
- * @return {CustomElement}
502
+ * @return {CustomElement} - The updated custom element.
517
503
  * @since 1.8.0
518
504
  */
519
505
  [assembleMethodSymbol]() {
@@ -521,22 +507,25 @@ class CustomElement extends HTMLElement {
521
507
  let elements;
522
508
  let nodeList;
523
509
 
510
+ // Extract options from attributes and set them
524
511
  const AttributeOptions = getOptionsFromAttributes.call(self);
525
512
  if (isObject(AttributeOptions) && Object.keys(AttributeOptions).length > 0) {
526
513
  self.setOptions(AttributeOptions);
527
514
  }
528
515
 
516
+ // Extract options from script tag and set them
529
517
  const ScriptOptions = getOptionsFromScriptTag.call(self);
530
518
  if (isObject(ScriptOptions) && Object.keys(ScriptOptions).length > 0) {
531
519
  self.setOptions(ScriptOptions);
532
520
  }
533
521
 
534
-
522
+ // Initialize the shadow root and its CSS stylesheet
535
523
  if (self.getOption("shadowMode", false) !== false) {
536
524
  try {
537
525
  initShadowRoot.call(self);
538
526
  elements = self.shadowRoot.childNodes;
539
527
  } catch (e) {
528
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
540
529
  }
541
530
 
542
531
  try {
@@ -546,21 +535,19 @@ class CustomElement extends HTMLElement {
546
535
  }
547
536
  }
548
537
 
538
+ // If the elements are not found inside the shadow root, initialize the HTML content of the element
549
539
  if (!(elements instanceof NodeList)) {
550
- if (!(elements instanceof NodeList)) {
551
- initHtmlContent.call(this);
552
- elements = this.childNodes;
553
- }
540
+ initHtmlContent.call(this);
541
+ elements = this.childNodes;
554
542
  }
555
543
 
544
+ // Initialize the custom elements inside the shadow root and the slotted elements
556
545
  initFromCallbackHost.call(this);
557
-
558
546
  try {
559
547
  nodeList = new Set([...elements, ...getSlottedElements.call(self)]);
560
548
  } catch (e) {
561
549
  nodeList = elements;
562
550
  }
563
-
564
551
  addObjectWithUpdaterToElement.call(
565
552
  self,
566
553
  nodeList,
@@ -568,26 +555,33 @@ class CustomElement extends HTMLElement {
568
555
  clone(self[internalSymbol].getRealSubject()["options"]),
569
556
  );
570
557
 
558
+ // Attach a mutation observer to observe changes to the attributes of the element
571
559
  attachAttributeChangeMutationObserver.call(this);
572
560
 
573
561
  return self;
574
562
  }
575
563
 
576
564
  /**
577
- * Called every time the element is inserted into the DOM. Useful for running setup code, such as
578
- * fetching resources or rendering. Generally, you should try to delay work until this time.
565
+ * This method is called every time the element is inserted into the DOM. It checks if the custom element
566
+ * has already been initialized and if not, calls the assembleMethod to initialize it.
579
567
  *
580
568
  * @return {void}
581
569
  * @since 1.7.0
570
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/connectedCallback
582
571
  */
583
572
  connectedCallback() {
584
- let self = this;
573
+ const self = this;
574
+
575
+ // Check if the object has already been initialized
585
576
  if (!hasObjectLink(self, customElementUpdaterLinkSymbol)) {
577
+
578
+ // If not, call the assembleMethod to initialize the object
586
579
  self[assembleMethodSymbol]();
587
580
  }
588
-
589
581
  }
590
582
 
583
+
584
+
591
585
  /**
592
586
  * Called every time the element is removed from the DOM. Useful for running clean up code.
593
587
  *
@@ -725,31 +719,34 @@ function callControlCallback(callBackFunctionName, ...args) {
725
719
  }
726
720
 
727
721
  /**
728
- * This Function is called when the element is attached to the DOM.
729
- *
730
- * It looks for the attribute `data-monster-option-callback`. Is this attribute is not set, the default callback
731
- * `initCustomControlOptionsCallback` is called.
722
+ * Initializes the custom element based on the provided callback function.
732
723
  *
733
- * The callback is searched in this element and in the host element. If the callback is found, it is called with the
734
- * element as parameter.
735
- *
736
- * The `monster
724
+ * This function is called when the element is attached to the DOM. It checks if the
725
+ * `data-monster-option-callback` attribute is set, and if not, the default callback
726
+ * `initCustomControlCallback` is called. The callback function is searched for in this
727
+ * element and in the host element. If the callback is found, it is called with the element
728
+ * as a parameter.
737
729
  *
738
730
  * @this CustomElement
731
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define#providing_a_construction_callback
732
+ * @since 1.8.0
739
733
  */
740
734
  function initFromCallbackHost() {
741
735
  const self = this;
742
736
 
743
- let callBackFunctionName = optionCallbackName // default callback
744
- if (self.hasAttribute(ATTRIBUTE_OPTION_CALLBACK)) {
745
- callBackFunctionName = self.getAttribute(ATTRIBUTE_OPTION_CALLBACK);
737
+ // Set the default callback function name
738
+ let callBackFunctionName = initControlCallbackName;
739
+
740
+ // If the `data-monster-option-callback` attribute is set, use its value as the callback function name
741
+ if (self.hasAttribute(ATTRIBUTE_INIT_CALLBACK)) {
742
+ callBackFunctionName = self.getAttribute(ATTRIBUTE_INIT_CALLBACK);
746
743
  }
747
744
 
745
+ // Call the callback function with the element as a parameter if it exists
748
746
  callControlCallback.call(self, callBackFunctionName);
749
-
750
-
751
747
  }
752
748
 
749
+
753
750
  /**
754
751
  * This method is called when the element is first created.
755
752
  *
@@ -142,7 +142,7 @@ function getMonsterVersion() {
142
142
  }
143
143
 
144
144
  /** don't touch, replaced by make with package.json version */
145
- monsterVersion = new Version("3.47.0");
145
+ monsterVersion = new Version("3.49.0");
146
146
 
147
147
  return monsterVersion;
148
148
  }
@@ -22,6 +22,10 @@ describe('DOM', function () {
22
22
  before(function (done) {
23
23
  initJSDOM().then(() => {
24
24
 
25
+ import("element-internals-polyfill").then((m) => {
26
+ m.polyfill();
27
+ });
28
+
25
29
  // jsdom does not support ElementInternals
26
30
  jsdomFlag = navigator.userAgent.includes("jsdom");
27
31
 
@@ -72,8 +76,13 @@ describe('DOM', function () {
72
76
 
73
77
  describe('create', function () {
74
78
  it('should return custom-element object', function () {
75
- let d = new TestComponent();
76
- expect(typeof d).is.equal('object');
79
+ try {
80
+ let d = new TestComponent();
81
+ } catch (e) {
82
+ expect(e).to.be.not.null;
83
+ }
84
+
85
+ expect(typeof d).is.equal('undefined');
77
86
  });
78
87
  });
79
88
 
@@ -84,7 +93,7 @@ describe('DOM', function () {
84
93
  document.getElementById('test1').appendChild(d);
85
94
  expect(document.getElementsByTagName('monster-customcontrol').length).is.equal(1);
86
95
  // no data-monster-objectlink="Symbol(monsterUpdater)" because it has nothing to update
87
- expect(document.getElementById('test1')).contain.html('<monster-customcontrol></monster-customcontrol>');
96
+ expect(document.getElementById('test1')).contain.html('<monster-customcontrol data-monster-error="Error: html is not set."></monster-customcontrol>')
88
97
  });
89
98
  });
90
99
 
@@ -129,11 +138,13 @@ describe('DOM', function () {
129
138
  let d = document.createElement('monster-customcontrol');
130
139
  form.appendChild(d);
131
140
 
132
- if (jsdomFlag) {
133
- expect(() => d.form).to.throw(Error);
134
- } else {
135
- expect(d.form).to.be.instanceof(HTMLFormElement)
136
- }
141
+ expect(d.form).to.be.instanceof(HTMLFormElement)
142
+
143
+ // if (jsdomFlag) {
144
+ // expect(() => d.form).to.throw(Error);
145
+ // } else {
146
+ // expect(d.form).to.be.instanceof(HTMLFormElement)
147
+ // }
137
148
 
138
149
 
139
150
  });
@@ -160,13 +171,7 @@ describe('DOM', function () {
160
171
 
161
172
  let d = document.createElement('monster-customcontrol');
162
173
  form.appendChild(d);
163
-
164
- if (jsdomFlag) {
165
- expect(() => d.setFormValue()).to.throw(Error);
166
- } else {
167
-
168
- }
169
-
174
+
170
175
  });
171
176
 
172
177
  it('name getter', function () {
@@ -191,11 +196,6 @@ describe('DOM', function () {
191
196
 
192
197
  let d = document.createElement('monster-customcontrol');
193
198
  form.appendChild(d);
194
- if (jsdomFlag) {
195
- expect(() => d.validity).to.throw(Error);
196
- } else {
197
-
198
- }
199
199
 
200
200
  });
201
201
 
@@ -204,11 +204,6 @@ describe('DOM', function () {
204
204
  let d = document.createElement('monster-customcontrol');
205
205
  form.appendChild(d);
206
206
 
207
- if (jsdomFlag) {
208
- expect(() => d.validity).to.throw(Error);
209
- } else {
210
-
211
- }
212
207
 
213
208
  });
214
209
 
@@ -217,11 +212,6 @@ describe('DOM', function () {
217
212
  let d = document.createElement('monster-customcontrol');
218
213
  form.appendChild(d);
219
214
 
220
- if (jsdomFlag) {
221
- expect(() => d.willValidate).to.throw(Error);
222
- } else {
223
-
224
- }
225
215
 
226
216
  });
227
217
  it('checkValidity()', function () {
@@ -229,11 +219,6 @@ describe('DOM', function () {
229
219
  let d = document.createElement('monster-customcontrol');
230
220
  form.appendChild(d);
231
221
 
232
- if (jsdomFlag) {
233
- expect(() => d.checkValidity()).to.throw(Error);
234
- } else {
235
-
236
- }
237
222
 
238
223
  });
239
224
 
@@ -242,11 +227,6 @@ describe('DOM', function () {
242
227
  let d = document.createElement('monster-customcontrol');
243
228
  form.appendChild(d);
244
229
 
245
- if (jsdomFlag) {
246
- expect(() => d.reportValidity()).to.throw(Error);
247
- } else {
248
-
249
- }
250
230
 
251
231
  });
252
232
 
@@ -255,11 +235,7 @@ describe('DOM', function () {
255
235
 
256
236
  let d = document.createElement('monster-customcontrol');
257
237
  form.appendChild(d);
258
- if (jsdomFlag) {
259
- expect(() => d.setValidity()).to.throw(Error);
260
- } else {
261
- expect(d.setValidity({'valueMissing': true}, "my message")).to.be.undefined;
262
- }
238
+ expect(d.setValidity({'valueMissing': true}, "my message")).to.be.undefined;
263
239
 
264
240
  });
265
241
 
@@ -102,13 +102,13 @@ describe('DOM', function () {
102
102
 
103
103
  });
104
104
 
105
- it('should found callback initCustomControlOptionsCallback', function () {
105
+ it('should found callback initCustomControlCallback', function () {
106
106
 
107
107
  let mocks = document.getElementById('mocks');
108
108
  mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
109
109
 
110
110
  const container = document.getElementById('call-back-host');
111
- container.initCustomControlOptionsCallback = function (control) {
111
+ container.initCustomControlCallback = function (control) {
112
112
  control.setOption('test', 1);
113
113
  }
114
114
 
@@ -120,14 +120,14 @@ describe('DOM', function () {
120
120
 
121
121
  });
122
122
 
123
- it('should found callback initCustomControlOptionsCallback from self', function () {
123
+ it('should found callback initCustomControlCallback from self', function () {
124
124
 
125
125
  let mocks = document.getElementById('mocks');
126
126
  mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
127
127
 
128
128
  let control = document.createElement(randomTagNumber);
129
129
  expect(control.getOption('test')).is.eql(0);
130
- control.initCustomControlOptionsCallback = function (control) {
130
+ control.initCustomControlCallback = function (control) {
131
131
  control.setOption('test', 2);
132
132
  }
133
133
 
@@ -239,7 +239,8 @@ describe('DOM', function () {
239
239
  document.getElementById('test1').appendChild(d);
240
240
  expect(document.getElementsByTagName('monster-testclass').length).is.equal(1);
241
241
  // no data-monster-objectlink="Symbol(monsterUpdater)" because it has nothing to update
242
- expect(document.getElementById('test1')).contain.html('<monster-testclass></monster-testclass>');
242
+ // but data-monster-error="Error: html is not set."
243
+ expect(document.getElementById('test1')).contain.html('<monster-testclass data-monster-error="Error: html is not set."></monster-testclass>');
243
244
  });
244
245
  });
245
246
 
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("3.47.0")
10
+ monsterVersion = new Version("3.49.0")
11
11
 
12
12
  let m = getMonsterVersion();
13
13