@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
package/source/dom/constants.mjs
CHANGED
@@ -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
|
/**
|
package/source/types/version.mjs
CHANGED
@@ -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
|
+
})
|
package/test/cases/monster.mjs
CHANGED