@schukai/monster 3.46.0 → 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.46.0",
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,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";
@@ -67,6 +71,12 @@ const attributeObserverSymbol = Symbol.for("@schukai/monster/dom/@@attributeObse
67
71
  */
68
72
  const attributeMutationObserverSymbol = Symbol("@schukai/monster/dom/@@mutationObserver");
69
73
 
74
+ /**
75
+ * @private
76
+ * @type {symbol}
77
+ */
78
+ const scriptHostElementSymbol = Symbol("scriptHostElement");
79
+
70
80
  /**
71
81
  * HTMLElement
72
82
  * @external HTMLElement
@@ -212,6 +222,7 @@ class CustomElement extends HTMLElement {
212
222
  });
213
223
  this[initMethodSymbol]();
214
224
  initOptionObserver.call(this);
225
+ this[scriptHostElementSymbol] = [];
215
226
 
216
227
  }
217
228
 
@@ -520,6 +531,7 @@ class CustomElement extends HTMLElement {
520
531
  self.setOptions(ScriptOptions);
521
532
  }
522
533
 
534
+
523
535
  if (self.getOption("shadowMode", false) !== false) {
524
536
  try {
525
537
  initShadowRoot.call(self);
@@ -541,6 +553,8 @@ class CustomElement extends HTMLElement {
541
553
  }
542
554
  }
543
555
 
556
+ initFromCallbackHost.call(this);
557
+
544
558
  try {
545
559
  nodeList = new Set([...elements, ...getSlottedElements.call(self)]);
546
560
  } catch (e) {
@@ -607,7 +621,7 @@ class CustomElement extends HTMLElement {
607
621
  const self = this;
608
622
 
609
623
  if (attrName.startsWith("data-monster-option-")) {
610
- setOptionFromAttribute(self, attrName, this[internalSymbol].getSubject()["options"])
624
+ setOptionFromAttribute(self, attrName, this[internalSymbol].getSubject()["options"])
611
625
  }
612
626
 
613
627
  const callback = self[attributeObserverSymbol]?.[attrName];
@@ -641,6 +655,99 @@ class CustomElement extends HTMLElement {
641
655
 
642
656
  return containChildNode.call(self.shadowRoot, node);
643
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
+
644
751
  }
645
752
 
646
753
  /**
@@ -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.46.0");
145
+ monsterVersion = new Version("3.47.0");
146
146
 
147
147
  return monsterVersion;
148
148
  }
@@ -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
+ })
@@ -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.46.0")
10
+ monsterVersion = new Version("3.47.0")
11
11
 
12
12
  let m = getMonsterVersion();
13
13