@schukai/monster 3.44.1 → 3.47.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.44.1",
3
+ "version": "3.47.0",
4
4
  "description": "Monster is a simple library for creating fast, robust and lightweight websites.",
5
5
  "keywords": [
6
6
  "framework",
@@ -59,6 +59,9 @@ export {
59
59
  ATTRIBUTE_HIDDEN,
60
60
  objectUpdaterLinkSymbol,
61
61
  customElementUpdaterLinkSymbol,
62
+ optionCallbackName,
63
+ ATTRIBUTE_SCRIPT_HOST,
64
+ ATTRIBUTE_OPTION_CALLBACK
62
65
  };
63
66
 
64
67
  /**
@@ -86,6 +89,16 @@ const ATTRIBUTE_PREFIX = "data-monster-";
86
89
  */
87
90
  const ATTRIBUTE_OPTIONS = `${ATTRIBUTE_PREFIX}options`;
88
91
 
92
+ /**
93
+ * This is name of the attribute to pass the script host to a control
94
+ *
95
+ * @memberOf Monster.DOM
96
+ * @license AGPLv3
97
+ * @since 3.48.0
98
+ * @type {string}
99
+ */
100
+ const ATTRIBUTE_SCRIPT_HOST = `${ATTRIBUTE_PREFIX}script-host`;
101
+
89
102
  /**
90
103
  * This is the name of the attribute to pass options to a control
91
104
  *
@@ -96,6 +109,26 @@ const ATTRIBUTE_OPTIONS = `${ATTRIBUTE_PREFIX}options`;
96
109
  */
97
110
  const ATTRIBUTE_OPTIONS_SELECTOR = `${ATTRIBUTE_PREFIX}options-selector`;
98
111
 
112
+ /**
113
+ * This is the name of the attribute to pass the callback to a control
114
+ *
115
+ * @memberOf Monster.DOM
116
+ * @license AGPLv3
117
+ * @since 3.48.0
118
+ * @type {string}
119
+ */
120
+ const ATTRIBUTE_OPTION_CALLBACK = `${ATTRIBUTE_PREFIX}option-callback`;
121
+
122
+ /**
123
+ * This is the name of the callback to pass the callback to a control
124
+ *
125
+ * @memberOf Monster.DOM
126
+ * @license AGPLv3
127
+ * @since 3.48.0
128
+ * @type {string}
129
+ */
130
+ const optionCallbackName = `initCustomControlOptionsCallback`;
131
+
99
132
  /**
100
133
  * @memberOf Monster.DOM
101
134
  * @type {string}
@@ -5,11 +5,12 @@
5
5
  * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
6
  */
7
7
 
8
- import { extend } from "../data/extend.mjs";
9
- import { ATTRIBUTE_VALUE } from "./constants.mjs";
10
- import { CustomElement, attributeObserverSymbol } from "./customelement.mjs";
11
- import { instanceSymbol } from "../constants.mjs";
12
- export { CustomControl };
8
+ import {extend} from "../data/extend.mjs";
9
+ import {ATTRIBUTE_VALUE} from "./constants.mjs";
10
+ import {CustomElement, attributeObserverSymbol} from "./customelement.mjs";
11
+ import {instanceSymbol} from "../constants.mjs";
12
+
13
+ export {CustomControl};
13
14
 
14
15
  /**
15
16
  * @private
@@ -41,6 +42,7 @@ const attachedInternalSymbol = Symbol("attachedInternal");
41
42
  * @see {@link https://www.npmjs.com/package/element-internals-polyfill}
42
43
  * @see {@link https://github.com/WICG/webcomponents}
43
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}
44
46
  * @license AGPLv3
45
47
  * @since 1.14.0
46
48
  * @copyright schukai GmbH
@@ -74,7 +76,7 @@ class CustomControl extends CustomElement {
74
76
  * @since 2.1.0
75
77
  */
76
78
  static get [instanceSymbol]() {
77
- return Symbol.for("@schukai/monster/dom/custom-control");
79
+ return Symbol.for("@schukai/monster/dom/custom-control@@instance");
78
80
  }
79
81
 
80
82
  /**
@@ -84,20 +86,18 @@ class CustomControl extends CustomElement {
84
86
  * @since 1.15.0
85
87
  */
86
88
  static get observedAttributes() {
87
- const list = super.observedAttributes;
88
- list.push(ATTRIBUTE_VALUE);
89
- return list;
89
+ return super.observedAttributes;
90
90
  }
91
91
 
92
92
  /**
93
+ * Adding a static formAssociated property, with a true value, makes an autonomous custom element a form-associated custom element.
93
94
  *
94
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}
95
97
  * @since 1.14.0
96
98
  * @return {boolean}
97
99
  */
98
- static get formAssociated() {
99
- return true;
100
- }
100
+ static formAssociated = true
101
101
 
102
102
  /**
103
103
  * Derived classes can override and extend this method as follows.
@@ -116,7 +116,8 @@ class CustomControl extends CustomElement {
116
116
  * @since 1.14.0
117
117
  */
118
118
  get defaults() {
119
- return extend({}, super.defaults);
119
+ return extend({
120
+ }, super.defaults);
120
121
  }
121
122
 
122
123
  /**
@@ -298,6 +299,46 @@ class CustomControl extends CustomElement {
298
299
  reportValidity() {
299
300
  return getInternal.call(this)?.reportValidity();
300
301
  }
302
+
303
+ /**
304
+ * @param {string} form
305
+ */
306
+ formAssociatedCallback(form) {
307
+ if (form) {
308
+ if(form.id) {
309
+ this.setAttribute("form", form.id);
310
+ }
311
+ } else {
312
+ this.removeAttribute("form");
313
+ }
314
+ }
315
+
316
+ /**
317
+ * @param {string} disabled
318
+ */
319
+ formDisabledCallback(disabled) {
320
+ if (disabled) {
321
+ this.setAttribute("disabled", "");
322
+ } else {
323
+ this.removeAttribute("disabled");
324
+ }
325
+ }
326
+
327
+ /**
328
+ * @param {string} state
329
+ * @param {string} mode
330
+ */
331
+ formStateRestoreCallback(state, mode) {
332
+
333
+ }
334
+
335
+ /**
336
+ *
337
+ */
338
+ formResetCallback() {
339
+ this.value = "";
340
+ }
341
+
301
342
  }
302
343
 
303
344
  /**
@@ -313,7 +354,7 @@ function getInternal() {
313
354
  throw new Error("ElementInternals is not supported and a polyfill is necessary");
314
355
  }
315
356
 
316
- return this[attachedInternalSymbol];
357
+ return self[attachedInternalSymbol];
317
358
  }
318
359
 
319
360
  /**
@@ -5,6 +5,7 @@
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";
8
9
  import {internalSymbol} from "../constants.mjs";
9
10
  import {extend} from "../data/extend.mjs";
10
11
  import {Pathfinder} from "../data/pathfinder.mjs";
@@ -22,8 +23,11 @@ import {
22
23
  ATTRIBUTE_DISABLED,
23
24
  ATTRIBUTE_ERRORMESSAGE,
24
25
  ATTRIBUTE_OPTIONS,
26
+ ATTRIBUTE_OPTION_CALLBACK,
25
27
  ATTRIBUTE_OPTIONS_SELECTOR,
28
+ ATTRIBUTE_SCRIPT_HOST,
26
29
  customElementUpdaterLinkSymbol,
30
+ optionCallbackName
27
31
  } from "./constants.mjs";
28
32
  import {findDocumentTemplate, Template} from "./template.mjs";
29
33
  import {addObjectWithUpdaterToElement} from "./updater.mjs";
@@ -31,6 +35,7 @@ import {instanceSymbol} from "../constants.mjs";
31
35
  import {getDocumentTranslations, Translations} from "../i18n/translations.mjs";
32
36
  import {getSlottedElements} from "./slotted.mjs";
33
37
  import {initOptionsFromAttributes} from "./util/init-options-from-attributes.mjs";
38
+ import {setOptionFromAttribute} from "./util/set-option-from-attribute.mjs";
34
39
 
35
40
  export {
36
41
  CustomElement,
@@ -66,6 +71,12 @@ const attributeObserverSymbol = Symbol.for("@schukai/monster/dom/@@attributeObse
66
71
  */
67
72
  const attributeMutationObserverSymbol = Symbol("@schukai/monster/dom/@@mutationObserver");
68
73
 
74
+ /**
75
+ * @private
76
+ * @type {symbol}
77
+ */
78
+ const scriptHostElementSymbol = Symbol("scriptHostElement");
79
+
69
80
  /**
70
81
  * HTMLElement
71
82
  * @external HTMLElement
@@ -211,6 +222,7 @@ class CustomElement extends HTMLElement {
211
222
  });
212
223
  this[initMethodSymbol]();
213
224
  initOptionObserver.call(this);
225
+ this[scriptHostElementSymbol] = [];
214
226
 
215
227
  }
216
228
 
@@ -220,7 +232,7 @@ class CustomElement extends HTMLElement {
220
232
  * @since 2.1.0
221
233
  */
222
234
  static get [instanceSymbol]() {
223
- return Symbol.for("@schukai/monster/dom/custom-element");
235
+ return Symbol.for("@schukai/monster/dom/custom-element@@instance");
224
236
  }
225
237
 
226
238
  /**
@@ -232,7 +244,7 @@ class CustomElement extends HTMLElement {
232
244
  * @since 1.15.0
233
245
  */
234
246
  static get observedAttributes() {
235
- return [ATTRIBUTE_OPTIONS, ATTRIBUTE_DISABLED];
247
+ return [];
236
248
  }
237
249
 
238
250
  /**
@@ -309,7 +321,7 @@ class CustomElement extends HTMLElement {
309
321
  */
310
322
  get defaults() {
311
323
  return {
312
- ATTRIBUTE_DISABLED: this.getAttribute(ATTRIBUTE_DISABLED),
324
+ disabled: false,
313
325
  shadowMode: "open",
314
326
  delegatesFocus: true,
315
327
  templates: {
@@ -519,6 +531,7 @@ class CustomElement extends HTMLElement {
519
531
  self.setOptions(ScriptOptions);
520
532
  }
521
533
 
534
+
522
535
  if (self.getOption("shadowMode", false) !== false) {
523
536
  try {
524
537
  initShadowRoot.call(self);
@@ -540,6 +553,8 @@ class CustomElement extends HTMLElement {
540
553
  }
541
554
  }
542
555
 
556
+ initFromCallbackHost.call(this);
557
+
543
558
  try {
544
559
  nodeList = new Set([...elements, ...getSlottedElements.call(self)]);
545
560
  } catch (e) {
@@ -605,8 +620,11 @@ class CustomElement extends HTMLElement {
605
620
  attributeChangedCallback(attrName, oldVal, newVal) {
606
621
  const self = this;
607
622
 
608
- const callback = self[attributeObserverSymbol]?.[attrName];
623
+ if (attrName.startsWith("data-monster-option-")) {
624
+ setOptionFromAttribute(self, attrName, this[internalSymbol].getSubject()["options"])
625
+ }
609
626
 
627
+ const callback = self[attributeObserverSymbol]?.[attrName];
610
628
  if (isFunction(callback)) {
611
629
  try {
612
630
  callback.call(self, newVal, oldVal);
@@ -637,6 +655,99 @@ class CustomElement extends HTMLElement {
637
655
 
638
656
  return containChildNode.call(self.shadowRoot, node);
639
657
  }
658
+
659
+ /**
660
+ * Calls a callback function if it exists.
661
+ *
662
+ * @param {string} name
663
+ * @param {*} args
664
+ * @returns {*}
665
+ */
666
+ callCallback(name, args) {
667
+ const self = this;
668
+ return callControlCallback.call(self, name, ...args);
669
+ }
670
+
671
+
672
+ }
673
+
674
+ /**
675
+ * @param {string} callBackFunctionName
676
+ * @param {*} args
677
+ * @return {any}
678
+ */
679
+ function callControlCallback(callBackFunctionName, ...args) {
680
+ const self = this;
681
+
682
+ if (!isString(callBackFunctionName) || callBackFunctionName === "") {
683
+ return;
684
+ }
685
+
686
+ if (callBackFunctionName in self) {
687
+ return self[callBackFunctionName](self, ...args);
688
+
689
+ }
690
+
691
+ if (!self.hasAttribute(ATTRIBUTE_SCRIPT_HOST)) {
692
+ return;
693
+ }
694
+
695
+ if (self[scriptHostElementSymbol].length === 0) {
696
+
697
+ const targetId = self.getAttribute(ATTRIBUTE_SCRIPT_HOST);
698
+ if (!targetId) {
699
+ return;
700
+ }
701
+
702
+ const list = targetId.split(",")
703
+ for (const id of list) {
704
+ const host = findElementWithIdUpwards(self, targetId);
705
+ if (!(host instanceof HTMLElement)) {
706
+ continue;
707
+ }
708
+
709
+ self[scriptHostElementSymbol].push(host);
710
+ }
711
+ }
712
+
713
+ for (const host of self[scriptHostElementSymbol]) {
714
+ if (callBackFunctionName in host) {
715
+ try {
716
+ return host[callBackFunctionName](self, ...args);
717
+ } catch (e) {
718
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString());
719
+ }
720
+ }
721
+ }
722
+
723
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, `callback ${callBackFunctionName} not found`);
724
+
725
+ }
726
+
727
+ /**
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.
732
+ *
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
737
+ *
738
+ * @this CustomElement
739
+ */
740
+ function initFromCallbackHost() {
741
+ const self = this;
742
+
743
+ let callBackFunctionName = optionCallbackName // default callback
744
+ if (self.hasAttribute(ATTRIBUTE_OPTION_CALLBACK)) {
745
+ callBackFunctionName = self.getAttribute(ATTRIBUTE_OPTION_CALLBACK);
746
+ }
747
+
748
+ callControlCallback.call(self, callBackFunctionName);
749
+
750
+
640
751
  }
641
752
 
642
753
  /**
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Copyright schukai GmbH and contributors 2023. All Rights Reserved.
3
+ * Node module: @schukai/monster
4
+ * This file is licensed under the AGPLv3 License.
5
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
+ */
7
+
8
+ export {extractKeys}
9
+
10
+ /**
11
+ * Extracts the keys from the given object and returns a map with the keys and values.
12
+ *
13
+ * @private
14
+ * @param {object} obj
15
+ * @param {string} keyPrefix
16
+ * @param {string} keySeparator
17
+ * @param {string} valueSeparator
18
+ * @returns {Map<any, any>}
19
+ */
20
+ function extractKeys(obj, keyPrefix = '', keySeparator = '-', valueSeparator = '.') {
21
+ const resultMap = new Map();
22
+
23
+ function helper(currentObj, currentKeyPrefix, currentValuePrefix) {
24
+ for (const key in currentObj) {
25
+ if (typeof currentObj[key] === 'object' && !Array.isArray(currentObj[key])) {
26
+ const newKeyPrefix = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
27
+ const newValuePrefix = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
28
+ helper(currentObj[key], newKeyPrefix, newValuePrefix);
29
+ } else {
30
+ const finalKey = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
31
+ const finalValue = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
32
+ resultMap.set(finalKey, finalValue);
33
+ }
34
+ }
35
+ }
36
+
37
+ helper(obj, keyPrefix, keyPrefix);
38
+ return resultMap;
39
+ }
@@ -8,6 +8,7 @@
8
8
  import {Pathfinder} from '../../data/pathfinder.mjs';
9
9
  import {isFunction} from '../../types/is.mjs';
10
10
  import {attributeObserverSymbol} from "../customelement.mjs";
11
+ import {extractKeys} from "./extract-keys.mjs";
11
12
 
12
13
  export {initOptionsFromAttributes};
13
14
 
@@ -75,63 +76,10 @@ function initOptionsFromAttributes(element, options, mapping = {}, prefix = 'dat
75
76
  }
76
77
 
77
78
  finder.setVia(optionName, value);
78
-
79
- // if element has an attribute observer, then register the attribute observer
80
- if (element?.[attributeObserverSymbol]) {
81
- element[attributeObserverSymbol][name] = (newValue, oldValue) => {
82
-
83
- if (newValue === oldValue) return;
84
-
85
- let changedValue = newValue;
86
-
87
- if (typeOfOptionValue === 'boolean') {
88
- changedValue = changedValue === 'true';
89
- } else if (typeOfOptionValue === 'number') {
90
- changedValue = Number(changedValue);
91
- } else if (typeOfOptionValue === 'string') {
92
- changedValue = String(changedValue);
93
- } else if (typeOfOptionValue === 'object') {
94
- changedValue = JSON.parse(changedValue);
95
- }
96
-
97
- finder.setVia(optionName, changedValue);
98
- }
99
- }
100
-
101
-
102
79
  }
103
80
  })
104
81
 
105
82
  return options;
106
83
  }
107
84
 
108
- /**
109
- * Extracts the keys from the given object and returns a map with the keys and values.
110
- *
111
- * @private
112
- * @param {object} obj
113
- * @param {string} keyPrefix
114
- * @param {string} keySeparator
115
- * @param {string} valueSeparator
116
- * @returns {Map<any, any>}
117
- */
118
- function extractKeys(obj, keyPrefix = '', keySeparator = '-', valueSeparator = '.') {
119
- const resultMap = new Map();
120
85
 
121
- function helper(currentObj, currentKeyPrefix, currentValuePrefix) {
122
- for (const key in currentObj) {
123
- if (typeof currentObj[key] === 'object' && !Array.isArray(currentObj[key])) {
124
- const newKeyPrefix = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
125
- const newValuePrefix = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
126
- helper(currentObj[key], newKeyPrefix, newValuePrefix);
127
- } else {
128
- const finalKey = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
129
- const finalValue = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
130
- resultMap.set(finalKey, finalValue);
131
- }
132
- }
133
- }
134
-
135
- helper(obj, keyPrefix, keyPrefix);
136
- return resultMap;
137
- }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Copyright schukai GmbH and contributors 2023. All Rights Reserved.
3
+ * Node module: @schukai/monster
4
+ * This file is licensed under the AGPLv3 License.
5
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
+ */
7
+
8
+ import {Pathfinder} from '../../data/pathfinder.mjs';
9
+ import {isFunction} from '../../types/is.mjs';
10
+ import {attributeObserverSymbol} from "../customelement.mjs";
11
+ import {extractKeys} from "./extract-keys.mjs";
12
+
13
+ export {setOptionFromAttribute};
14
+
15
+ /**
16
+ * Set the given options object based on the attributes of the current DOM element.
17
+ * The function looks for attributes with the prefix 'data-monster-option-', and maps them to
18
+ * properties in the options object. It replaces the dashes with dots to form the property path.
19
+ * For example, the attribute 'data-monster-option-url' maps to the 'url' property in the options object.
20
+ *
21
+ * With the mapping parameter, the attribute value can be mapped to a different value.
22
+ * For example, the attribute 'data-monster-option-foo' maps to the 'bar' property in the options object.
23
+ *
24
+ * The mapping object would look like this:
25
+ * {
26
+ * 'foo': (value) => value + 'bar'
27
+ * // the value of the attribute 'data-monster-option-foo' is appended with 'bar'
28
+ * // and assigned to the 'bar' property in the options object.
29
+ * // e.g. <div data-monster-option-foo="foo"></div>
30
+ * 'bar.baz': (value) => value + 'bar'
31
+ * // the value of the attribute 'data-monster-option-bar-baz' is appended with 'bar'
32
+ * // and assigned to the 'bar.baz' property in the options object.
33
+ * // e.g. <div data-monster-option-bar-baz="foo"></div>
34
+ * }
35
+ *
36
+ * @since 3.45.0
37
+ * @param {HTMLElement} element - The DOM element to be used as the source of the attributes.
38
+ * @param {Object} name - The attribute object to be used as the source of the attributes.
39
+ * @param {Object} options - The options object to be initialized.
40
+ * @param {Object} mapping - A mapping between the attribute value and the property value.
41
+ * @param {string} prefix - The prefix of the attributes to be considered.
42
+ * @returns {Object} - The initialized options object.
43
+ * @this HTMLElement - The context of the DOM element.
44
+ */
45
+ function setOptionFromAttribute(element, name, options, mapping = {}, prefix = 'data-monster-option-') {
46
+ if (!(element instanceof HTMLElement)) return options;
47
+ if (!element.hasAttributes()) return options;
48
+
49
+ const keyMap = extractKeys(options);
50
+ const finder = new Pathfinder(options);
51
+
52
+ // check if the attribute name is a valid option.
53
+ // the mapping between the attribute is simple. The dash is replaced by a dot.
54
+ // e.g. data-monster-url => url
55
+ const optionName = keyMap.get(name.substring(prefix.length).toLowerCase());
56
+ if (!finder.exists(optionName)) return;
57
+
58
+ if (!element.hasAttribute(name)) {
59
+ return options;
60
+ }
61
+
62
+ let value = element.getAttribute(name);
63
+ if (mapping.hasOwnProperty(optionName) && isFunction(mapping[optionName])) {
64
+ value = mapping[optionName](value);
65
+ }
66
+
67
+ const typeOfOptionValue = typeof finder.getVia(optionName);
68
+ if (typeOfOptionValue === 'boolean') {
69
+ value = value === 'true';
70
+ } else if (typeOfOptionValue === 'number') {
71
+ value = Number(value);
72
+ } else if (typeOfOptionValue === 'string') {
73
+ value = String(value);
74
+ } else if (typeOfOptionValue === 'object') {
75
+ value = JSON.parse(value);
76
+ }
77
+
78
+ finder.setVia(optionName, value);
79
+
80
+ return options;
81
+ }
82
+
83
+
@@ -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.44.1");
145
+ monsterVersion = new Version("3.47.0");
146
146
 
147
147
  return monsterVersion;
148
148
  }
@@ -4,9 +4,9 @@ import chai from "chai"
4
4
  import {ATTRIBUTE_OPTIONS} from "../../../../application/source/dom/constants.mjs";
5
5
  import {getDocument} from "../../../../application/source/dom/util.mjs";
6
6
  import {chaiDom} from "../../util/chai-dom.mjs";
7
+ import {cleanupDOMFromTesting, initMutationObserverForTesting} from "../../util/cleanupdom.mjs";
7
8
  import {initJSDOM} from "../../util/jsdom.mjs";
8
9
 
9
-
10
10
  let expect = chai.expect;
11
11
  chai.use(chaiDom);
12
12
 
@@ -19,45 +19,46 @@ describe('DOM', function () {
19
19
 
20
20
  let CustomControl, registerCustomElement, TestComponent, document, jsdomFlag;
21
21
 
22
- describe('CustomControl()', function () {
23
-
24
- before(function (done) {
25
- initJSDOM().then(() => {
22
+ before(function (done) {
23
+ initJSDOM().then(() => {
26
24
 
27
- // jsdom does not support ElementInternals
28
- jsdomFlag = navigator.userAgent.includes("jsdom");
25
+ // jsdom does not support ElementInternals
26
+ jsdomFlag = navigator.userAgent.includes("jsdom");
29
27
 
30
- import("../../../../application/source/dom/customelement.mjs").then((m) => {
31
- registerCustomElement = m['registerCustomElement'];
28
+ import("../../../../application/source/dom/customelement.mjs").then((m) => {
29
+ registerCustomElement = m['registerCustomElement'];
32
30
 
33
31
 
34
- import("../../../../application/source/dom/customcontrol.mjs").then((m) => {
32
+ import("../../../../application/source/dom/customcontrol.mjs").then((m) => {
35
33
 
36
- document = getDocument();
34
+ document = getDocument();
37
35
 
38
- try {
39
- CustomControl = m['CustomControl'];
36
+ try {
37
+ CustomControl = m['CustomControl'];
40
38
 
41
- TestComponent = class extends CustomControl {
42
- static getTag() {
43
- return "monster-customcontrol"
44
- }
39
+ TestComponent = class extends CustomControl {
40
+ static getTag() {
41
+ return "monster-customcontrol"
45
42
  }
46
- registerCustomElement(TestComponent)
43
+ }
44
+ registerCustomElement(TestComponent)
47
45
 
48
46
 
49
- done()
50
- } catch (e) {
51
- done(e);
52
- }
47
+ done()
48
+ } catch (e) {
49
+ done(e);
50
+ }
53
51
 
54
52
 
55
- });
56
53
  });
57
54
  });
58
- })
55
+ });
56
+ })
57
+
58
+ describe('CustomControl()', function () {
59
59
 
60
60
  beforeEach(() => {
61
+ initMutationObserverForTesting()
61
62
  let mocks = document.getElementById('mocks');
62
63
  mocks.innerHTML = html1;
63
64
  })
@@ -65,6 +66,8 @@ describe('DOM', function () {
65
66
  afterEach(() => {
66
67
  let mocks = document.getElementById('mocks');
67
68
  mocks.innerHTML = "";
69
+ cleanupDOMFromTesting();
70
+
68
71
  })
69
72
 
70
73
  describe('create', function () {
@@ -76,28 +79,33 @@ describe('DOM', function () {
76
79
 
77
80
  describe('connect empty element', function () {
78
81
  it('document should contain monster-customcontrol', function () {
79
-
82
+
80
83
  let d = document.createElement('monster-customcontrol');
81
84
  document.getElementById('test1').appendChild(d);
82
85
  expect(document.getElementsByTagName('monster-customcontrol').length).is.equal(1);
83
- // no data-monster-objectlink="Symbol(monsterUpdater)" because it has nothing to update
86
+ // no data-monster-objectlink="Symbol(monsterUpdater)" because it has nothing to update
84
87
  expect(document.getElementById('test1')).contain.html('<monster-customcontrol></monster-customcontrol>');
85
88
  });
86
89
  });
87
90
 
88
- describe('Options change', function () {
89
-
90
- it('delegatesFocus should change from true to false', function () {
91
- let element = document.createElement('monster-customcontrol')
92
-
93
- expect(element.getOption('delegatesFocus')).to.be.true;
94
- element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
95
- expect(element.getOption('delegatesFocus')).to.be.false;
96
-
97
- })
98
-
99
-
100
- })
91
+ // describe('Options change', function () {
92
+ //
93
+ // it('delegatesFocus should change from true to false', function (done) {
94
+ // let element = document.createElement('monster-customcontrol')
95
+ //
96
+ // expect(element.getOption('delegatesFocus')).to.be.true;
97
+ // setTimeout(() => {
98
+ // element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
99
+ // setTimeout(() => {
100
+ // expect(element.getOption('delegatesFocus')).to.be.false;
101
+ // done();
102
+ // }, 10);
103
+ // }, 10);
104
+ //
105
+ //
106
+ // }).timeout(100);
107
+ //
108
+ // })
101
109
 
102
110
  describe('Test ElementInternals', function () {
103
111
 
@@ -115,6 +123,7 @@ describe('DOM', function () {
115
123
  expect(d.constructor.formAssociated).to.be.true;
116
124
 
117
125
  });
126
+
118
127
  it('form', function () {
119
128
 
120
129
  let d = document.createElement('monster-customcontrol');
@@ -147,7 +156,6 @@ describe('DOM', function () {
147
156
 
148
157
  });
149
158
 
150
-
151
159
  it('setFormValue', function () {
152
160
 
153
161
  let d = document.createElement('monster-customcontrol');
@@ -161,7 +169,6 @@ describe('DOM', function () {
161
169
 
162
170
  });
163
171
 
164
-
165
172
  it('name getter', function () {
166
173
 
167
174
  let d = document.createElement('monster-customcontrol');
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+
3
+ import chai from "chai"
4
+ // import {internalSymbol} from "../../../../application/source/constants.mjs";
5
+ // import {ATTRIBUTE_OPTIONS} from "../../../../application/source/dom/constants.mjs";
6
+ import {getDocument} from "../../../../application/source/dom/util.mjs";
7
+ // import {ProxyObserver} from "../../../../application/source/types/proxyobserver.mjs";
8
+ // import {addObjectWithUpdaterToElement} from "../../../../application/source/dom/updater.mjs";
9
+ import {chaiDom} from "../../util/chai-dom.mjs";
10
+ import {initJSDOM} from "../../util/jsdom.mjs";
11
+
12
+
13
+ let expect = chai.expect;
14
+ chai.use(chaiDom);
15
+
16
+ // let html1 = `
17
+ // <div id="scripthost">
18
+ // </div>
19
+ //
20
+ // <div>
21
+ // <
22
+ // </div>
23
+ // `;
24
+
25
+
26
+ // defined in constants.mjs
27
+ // const updaterSymbolKey = "@schukai/monster/dom/custom-element@@options-updater-link"
28
+ // const updaterSymbolSymbol = Symbol.for(updaterSymbolKey);
29
+
30
+
31
+
32
+ describe('DOM', function () {
33
+
34
+ let CustomElement, registerCustomElement, TestComponent, document, TestComponent2,assignUpdaterToElement;
35
+
36
+ describe('initFromScriptHost()', function () {
37
+
38
+ const randomTagNumber = "monster-test"+Math.floor(Math.random() * 1000000);
39
+
40
+ before(function (done) {
41
+ initJSDOM().then(() => {
42
+
43
+ import("../../../../application/source/dom/customelement.mjs").then((m) => {
44
+
45
+ try {
46
+ CustomElement = m['CustomElement'];
47
+ registerCustomElement = m['registerCustomElement'];
48
+
49
+ TestComponent2 = class extends CustomElement {
50
+ static getTag() {
51
+ return randomTagNumber;
52
+ }
53
+
54
+ /**
55
+ *
56
+ * @return {Object}
57
+ */
58
+ get defaults() {
59
+
60
+ return Object.assign({}, super.defaults, {
61
+ test: 0,
62
+ templates: {
63
+ main: '<h1></h1><article><p>test</p><div id="container"></div></article>'
64
+ },
65
+ })
66
+ }
67
+
68
+ }
69
+
70
+ registerCustomElement(TestComponent2)
71
+
72
+ document = getDocument();
73
+ done()
74
+ } catch (e) {
75
+ done(e);
76
+ }
77
+
78
+
79
+ }).catch((e) => {
80
+ done(e);
81
+ });
82
+
83
+ });
84
+ })
85
+
86
+ afterEach(() => {
87
+ let mocks = document.getElementById('mocks');
88
+ mocks.innerHTML = "";
89
+ })
90
+
91
+ describe('call callback', function () {
92
+ it('should not found callback and add error attribute', function () {
93
+
94
+ let mocks = document.getElementById('mocks');
95
+ mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
96
+
97
+ let control = document.createElement(randomTagNumber);
98
+ control.setAttribute('data-monster-script-host', "call-back-host");
99
+ document.getElementById('container').appendChild(control);
100
+ expect(control.getOption('test')).is.eql(0);
101
+ expect(control.hasAttribute('data-monster-error')).is.true;
102
+
103
+ });
104
+
105
+ it('should found callback initCustomControlOptionsCallback', function () {
106
+
107
+ let mocks = document.getElementById('mocks');
108
+ mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
109
+
110
+ const container = document.getElementById('call-back-host');
111
+ container.initCustomControlOptionsCallback = function (control) {
112
+ control.setOption('test', 1);
113
+ }
114
+
115
+ let control = document.createElement(randomTagNumber);
116
+ control.setAttribute('data-monster-script-host', "call-back-host");
117
+ document.getElementById('container').appendChild(control);
118
+ expect(control.getOption('test')).is.eql(1);
119
+ expect(control.hasAttribute('data-monster-error')).is.false;
120
+
121
+ });
122
+
123
+ it('should found callback initCustomControlOptionsCallback from self', function () {
124
+
125
+ let mocks = document.getElementById('mocks');
126
+ mocks.innerHTML = `<div id="call-back-host"></div><div id="container"></div>`;
127
+
128
+ let control = document.createElement(randomTagNumber);
129
+ expect(control.getOption('test')).is.eql(0);
130
+ control.initCustomControlOptionsCallback = function (control) {
131
+ control.setOption('test', 2);
132
+ }
133
+
134
+ control.setAttribute('data-monster-script-host', "call-back-host");
135
+ document.getElementById('container').appendChild(control);
136
+ expect(control.getOption('test')).is.eql(2);
137
+ expect(control.hasAttribute('data-monster-error')).is.false;
138
+
139
+ });
140
+ })
141
+
142
+ });
143
+ })
@@ -277,12 +277,12 @@ describe('DOM', function () {
277
277
  expect(element.getOption('delegatesFocus')).to.be.true;
278
278
  expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
279
279
 
280
- element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
281
- expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
282
-
283
- expect(element.getOption('delegatesFocus')).to.be.false;
284
- expect(element[internalSymbol].realSubject.options.delegatesFocus).to.be.false;
285
- expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
280
+ // element.setAttribute(ATTRIBUTE_OPTIONS, JSON.stringify({delegatesFocus: false}));
281
+ // expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
282
+ //
283
+ // expect(element.getOption('delegatesFocus')).to.be.false;
284
+ // expect(element[internalSymbol].realSubject.options.delegatesFocus).to.be.false;
285
+ // expect(Object.is(element[internalSymbol].realSubject, o)).to.be.true;
286
286
 
287
287
  })
288
288
 
@@ -55,7 +55,6 @@ describe('FocusManager', function () {
55
55
  });
56
56
  }
57
57
 
58
-
59
58
  })
60
59
 
61
60
  it('run ist', function () {
@@ -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.44.1")
10
+ monsterVersion = new Version("3.47.0")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -16,14 +16,10 @@ function init() {
16
16
  }
17
17
  })
18
18
  }
19
-
20
19
  });
21
20
  });
22
-
23
-
24
21
  }
25
22
 
26
-
27
23
  /**
28
24
  *
29
25
  */