@schukai/monster 3.46.0 → 3.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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