@schukai/monster 2.2.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,7 @@ import {
17
17
  ATTRIBUTE_UPDATER_INSERT_REFERENCE,
18
18
  ATTRIBUTE_UPDATER_REMOVE,
19
19
  ATTRIBUTE_UPDATER_REPLACE,
20
- ATTRIBUTE_UPDATER_SELECT_THIS
20
+ ATTRIBUTE_UPDATER_SELECT_THIS, customElementUpdaterLinkSymbol
21
21
  } from "../dom/constants.mjs";
22
22
 
23
23
  import {Base} from "../types/base.mjs";
@@ -27,11 +27,12 @@ import {ProxyObserver} from "../types/proxyobserver.mjs";
27
27
  import {validateArray, validateInstance} from "../types/validate.mjs";
28
28
  import {clone} from "../util/clone.mjs";
29
29
  import {trimSpaces} from "../util/trimspaces.mjs";
30
+ import {addToObjectLink} from "./attributes.mjs";
30
31
  import {findTargetElementFromEvent} from "./events.mjs";
31
32
  import {findDocumentTemplate} from "./template.mjs";
32
33
  import {getDocument} from "./util.mjs";
33
34
 
34
- export {Updater}
35
+ export {Updater, addObjectWithUpdaterToElement}
35
36
 
36
37
  /**
37
38
  * The updater class connects an object with the dom. In this way, structures and contents in the DOM can be programmatically adapted via attributes.
@@ -252,7 +253,7 @@ function getCheckStateCallback() {
252
253
  /**
253
254
  * @private
254
255
  */
255
- const symbol = Symbol('EventHandler');
256
+ const symbol = Symbol('@schukai/monster/updater@@EventHandler');
256
257
 
257
258
  /**
258
259
  * @private
@@ -308,7 +309,7 @@ function retrieveAndSetValue(element) {
308
309
  throw new Error('the bind argument must start as a value with a path');
309
310
  }
310
311
 
311
- path = path.substr(5);
312
+ path = path.substring(5);
312
313
 
313
314
  let value;
314
315
 
@@ -525,8 +526,6 @@ function insertElement(change) {
525
526
  }
526
527
 
527
528
  }
528
-
529
-
530
529
  }
531
530
 
532
531
  /**
@@ -608,8 +607,6 @@ function updateContent(change) {
608
607
  }
609
608
  }
610
609
  }
611
-
612
-
613
610
  }
614
611
 
615
612
  /**
@@ -869,3 +866,64 @@ function handleInputControlAttributeUpdate(element, name, value) {
869
866
  }
870
867
 
871
868
  }
869
+
870
+
871
+
872
+ /**
873
+ * @param {NodeList|HTMLElement|Set<HTMLElement>} elements
874
+ * @param {Symbol} symbol
875
+ * @param {object} object
876
+ * @return {Promise[]}
877
+ * @license AGPLv3
878
+ * @since 1.23.0
879
+ * @memberOf Monster.DOM
880
+ * @throws {TypeError} elements is not an instance of NodeList, HTMLElement or Set
881
+ * @throws {TypeError} the context of the function is not an instance of HTMLElement
882
+ * @throws {TypeError} symbol must be an instance of Symbol
883
+ */
884
+ function addObjectWithUpdaterToElement (elements,symbol, object) {
885
+ const self = this;
886
+ if (!(self instanceof HTMLElement)) {
887
+ throw new TypeError('the context of this function must be an instance of HTMLElement');
888
+ }
889
+
890
+ if (!(typeof symbol === 'symbol')) {
891
+ throw new TypeError('symbol must be an instance of Symbol');
892
+ }
893
+
894
+ const updaters = new Set;
895
+
896
+ if (elements instanceof NodeList) {
897
+ elements = new Set([
898
+ ...elements
899
+ ])
900
+ } else if (elements instanceof HTMLElement) {
901
+ elements = new Set([
902
+ elements
903
+ ])
904
+ } else if (elements instanceof Set) {
905
+ } else {
906
+ throw new TypeError('elements is not a valid type. (actual: ' + typeof elements + ')');
907
+ }
908
+
909
+ let result = [];
910
+
911
+ elements.forEach((element) => {
912
+ if (!(element instanceof HTMLElement)) return;
913
+ if ((element instanceof HTMLTemplateElement)) return;
914
+
915
+ const u = new Updater(element, object)
916
+ updaters.add(u);
917
+
918
+ result.push(u.run().then(() => {
919
+ return u.enableEventProcessing();
920
+ }));
921
+
922
+ });
923
+
924
+ if (updaters.size > 0) {
925
+ addToObjectLink(self, symbol, updaters);
926
+ }
927
+
928
+ return result;
929
+ }
@@ -99,7 +99,6 @@ export {
99
99
  assembleMethodSymbol,
100
100
  attributeObserverSymbol,
101
101
  registerCustomElement,
102
- assignUpdaterToElement,
103
102
  getSlottedElements
104
103
  } from "./dom/customelement.mjs"
105
104
  export {
@@ -7,11 +7,18 @@
7
7
 
8
8
  import {Base} from './base.mjs';
9
9
  import {instanceSymbol} from '../constants.mjs';
10
+
10
11
  export {Queue}
11
12
 
12
13
  /**
13
- * You can create the instance via the monster namespace `new Monster.Types.Queue()`.
14
- *
14
+ * A queue is a list of items that are processed one after another (first in, first out).
15
+ *
16
+ * With a queue you can add items to the end of the list `Queue.add()` and remove items from the beginning of the list `Queue.pop()`.
17
+ *
18
+ * With `Queue.peek()` you can get the first item without removing it from the list.
19
+ *
20
+ * You can create the instance via `new Queue()`.
21
+ *
15
22
  * @externalExample ../../example/types/queue.mjs
16
23
  * @license AGPLv3
17
24
  * @since 1.4.0
@@ -149,7 +149,7 @@ function getMonsterVersion() {
149
149
  }
150
150
 
151
151
  /** don't touch, replaced by make with package.json version */
152
- monsterVersion = new Version('2.2.1')
152
+ monsterVersion = new Version('3.1.0')
153
153
 
154
154
  return monsterVersion;
155
155
 
@@ -0,0 +1,96 @@
1
+ import {expect} from "chai"
2
+ import {WebSocketDatasource} from "../../../../../application/source/data/datasource/websocket.mjs";
3
+ import {initWebSocket} from "../../../util/websocket.mjs";
4
+
5
+ const testUrl = "wss://ws.postman-echo.com/raw"
6
+
7
+ // const g = getGlobal();
8
+ // g['WebSocket'] = WS;
9
+
10
+
11
+ describe('Websocket', function () {
12
+
13
+ let ds = undefined
14
+
15
+ before(function (done) {
16
+ initWebSocket().then(() => {
17
+ done()
18
+ }).catch((e) => {
19
+ done(e)
20
+ })
21
+ });
22
+
23
+ afterEach(function (done) {
24
+ if (ds) {
25
+ ds.close()
26
+ }
27
+
28
+ // without this, the node test will hang
29
+ for (const sym of Object.getOwnPropertySymbols(ds)) {
30
+ if (sym.toString() ==='Symbol(connection)') {
31
+ if(ds[sym]?.socket?.['terminate']) {
32
+ ds[sym]?.socket?.['terminate']()
33
+ }
34
+ }
35
+ }
36
+
37
+ done()
38
+ });
39
+
40
+ it('should connect', function (done) {
41
+ ds = new WebSocketDatasource({
42
+ url: testUrl,
43
+ reconnect: {
44
+ enabled: false
45
+ }
46
+ });
47
+ ds.connect()
48
+ setTimeout(() => {
49
+ expect(ds.isConnected()).to.be.true;
50
+ done();
51
+ }, 500);
52
+
53
+
54
+ })
55
+
56
+ it('should send message', function (done) {
57
+ ds = new WebSocketDatasource({
58
+ url: testUrl,
59
+ reconnect: {
60
+ enabled: false
61
+ }
62
+ });
63
+ ds.connect()
64
+
65
+ ds.set({
66
+ data: {
67
+ message: "Hello World"
68
+ }
69
+ })
70
+
71
+ setTimeout(() => {
72
+
73
+ ds.write().then(() => {
74
+
75
+ ds.set({})
76
+ expect(ds.get()).to.be.deep.equal({});
77
+
78
+
79
+ setTimeout(() => {
80
+
81
+ expect(ds.get()).to.be.deep.equal({
82
+ data: {
83
+ message: "Hello World"
84
+ }
85
+ });
86
+ done();
87
+ }, 1000);
88
+ }).catch((err) => {
89
+ done(new Error(err));
90
+ })
91
+ }, 1000)
92
+
93
+
94
+ }).timeout(10000);
95
+
96
+ });
@@ -2,8 +2,10 @@
2
2
 
3
3
  import chai from "chai"
4
4
  import {internalSymbol} from "../../../../application/source/constants.mjs";
5
- import {ATTRIBUTE_OPTIONS} from "../../../../application/source/dom/constants.mjs";
5
+ import {ATTRIBUTE_OPTIONS, customElementUpdaterLinkSymbol} from "../../../../application/source/dom/constants.mjs";
6
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";
7
9
  import {chaiDom} from "../../util/chai-dom.mjs";
8
10
  import {initJSDOM} from "../../util/jsdom.mjs";
9
11
 
@@ -16,10 +18,164 @@ let html1 = `
16
18
  </div>
17
19
  `;
18
20
 
21
+ let html2 = `
22
+ <input data-monster-bind="path:a" id="test2" data-monster-attributes="value path:a">
23
+ `;
24
+
25
+ // defined in constants.mjs
26
+ const updaterSymbolKey = "@schukai/monster/dom/custom-element@@options-updater-link"
27
+ const updaterSymbolSymbol = Symbol.for(updaterSymbolKey);
28
+
29
+
19
30
 
20
31
  describe('DOM', function () {
21
32
 
22
- let CustomElement, registerCustomElement, TestComponent, document, TestComponent2;
33
+ let CustomElement, registerCustomElement, TestComponent, document, TestComponent2,assignUpdaterToElement;
34
+
35
+ // This allows us to inspect the addEventListener calls
36
+
37
+ // let addEventListener = EventTarget.prototype.addEventListener;
38
+ // let removeEventListener = EventTarget.prototype.removeEventListener;
39
+ //
40
+ // before(function (done) {
41
+ // initJSDOM().then(() => {
42
+ //
43
+ // EventTarget.prototype.addEventListener = function (type, callback, options) {
44
+ // /* store args and then… */
45
+ // // callback = (e) => {
46
+ // // console.log("event fired" + e);
47
+ // // callback(e);
48
+ // // };
49
+ //
50
+ // addEventListener.call(this, type, callback, options);
51
+ // };
52
+ //
53
+ // EventTarget.prototype.removeEventListener = function (type, callback, options) {
54
+ // /* remove from stored args and then… */
55
+ // removeEventListener.call(this, type, callback, options);
56
+ // };
57
+ //
58
+ // done(e);
59
+ //
60
+ //
61
+ // });
62
+ //
63
+ //
64
+ // })
65
+ //
66
+ // after(function () {
67
+ // EventTarget.prototype.addEventListener = addEventListener;
68
+ // EventTarget.prototype.removeEventListener = removeEventListener;
69
+ // });
70
+
71
+
72
+ describe("assignUpdaterToElement", function () {
73
+
74
+ before(function (done) {
75
+ initJSDOM().then(() => {
76
+ import("../../../../application/source/dom/customelement.mjs").then((m) => {
77
+ try {
78
+ CustomElement = m['CustomElement'];
79
+ assignUpdaterToElement= function (elements, object) {
80
+ return addObjectWithUpdaterToElement.call(this, elements, updaterSymbolSymbol, object);
81
+ }
82
+
83
+
84
+ document = getDocument();
85
+
86
+ done()
87
+ } catch (e) {
88
+ done(e);
89
+ }
90
+
91
+
92
+ });
93
+
94
+ });
95
+ })
96
+
97
+ beforeEach(() => {
98
+ let mocks = document.getElementById('mocks');
99
+ mocks.innerHTML = html2;
100
+ })
101
+
102
+ afterEach(() => {
103
+ let mocks = document.getElementById('mocks');
104
+ mocks.innerHTML = "";
105
+ })
106
+
107
+ /**
108
+ * this test try to simulate the bug that was found in the assignUpdaterToElement function.
109
+ * The bug was that the updater was not assigned to the element when the element was created.
110
+ *
111
+ * unfortunately, this test does not reproduce the bug.
112
+ */
113
+ it("should assign an updater to an element", function (done) {
114
+ let element = document.getElementById('test2');
115
+
116
+ expect(document.getElementById("mocks").innerHTML).to.equal(html2);
117
+
118
+ const a = {a: 1};
119
+ const b = {b: 2};
120
+
121
+ const ap = new ProxyObserver(a);
122
+ const bp = new ProxyObserver(b);
123
+
124
+ const x = ap.getSubject()
125
+ const y = bp.getSubject()
126
+
127
+ const set = new Set();
128
+ set.add(element);
129
+
130
+ assignUpdaterToElement.call(element, set, ap);
131
+ assignUpdaterToElement.call(element, set, bp);
132
+
133
+ expect(JSON.stringify(x)).to.equal('{"a":1}');
134
+ expect(JSON.stringify(y)).to.equal('{"b":2}');
135
+
136
+ const sy = updaterSymbolSymbol;
137
+
138
+ let v = element.getAttribute("data-monster-objectlink");
139
+ expect(v).to.equal('Symbol('+updaterSymbolKey+')');
140
+
141
+ const updater = element[sy];
142
+
143
+ for (const v of updater) {
144
+ for (const u of v) {
145
+ u.run().then(() => {
146
+ u.enableEventProcessing();
147
+ });
148
+ }
149
+ }
150
+
151
+ expect(updater).to.be.an.instanceof(Set);
152
+ expect(updater).to.be.a("Set");
153
+
154
+ x.a = 3;
155
+ bp.getSubject().b = 4;
156
+
157
+ setTimeout(() => {
158
+
159
+ let mockHTML = document.getElementById("mocks");
160
+
161
+ // html expexted:
162
+ // <input data-monster-bind="path:a" id="test2" data-monster-attributes="value path:a" data-monster-objectlink="Symbol(@schukai/monster/dom/@@object-updater-link)" value="3">
163
+
164
+ expect(mockHTML.querySelector("#test2")).to.have.value('3')
165
+ expect(mockHTML.querySelector("#test2")).to.have.attribute('data-monster-objectlink', 'Symbol('+updaterSymbolKey+')')
166
+ //expect(mockHTML).to.have.html(resultHTML);
167
+
168
+ expect(element.value).to.equal("3");
169
+
170
+ expect(JSON.stringify(ap.getRealSubject())).to.equal('{"a":3}');
171
+ expect(JSON.stringify(bp.getRealSubject())).to.equal('{"b":4}');
172
+ done()
173
+ }, 10)
174
+
175
+ })
176
+
177
+ })
178
+
23
179
 
24
180
  describe('CustomElement()', function () {
25
181
 
@@ -102,7 +258,7 @@ describe('DOM', function () {
102
258
 
103
259
  let monster = document.getElementById('thisisatest');
104
260
  expect(monster.getOption('demotest')).is.eql(1425);
105
-
261
+
106
262
  });
107
263
  });
108
264
 
@@ -133,7 +289,7 @@ describe('DOM', function () {
133
289
  try {
134
290
  expect(document.getElementsByTagName('monster-testclass2').length).is.equal(1);
135
291
  expect(document.getElementsByTagName('monster-testclass2').item(0).shadowRoot.innerHTML).is.equal('<h1></h1><article><p>test</p><div id="container"></div></article>');
136
- expect(document.getElementById('test1')).contain.html('<monster-testclass2 data-monster-objectlink="Symbol(@schukai/monster/dom/@@object-updater-link)"></monster-testclass2>');
292
+ expect(document.getElementById('test1')).contain.html('<monster-testclass2 data-monster-objectlink="Symbol('+updaterSymbolKey+')"></monster-testclass2>');
137
293
  return done();
138
294
  } catch (e) {
139
295
  done(e);
@@ -514,6 +670,4 @@ describe('DOM', function () {
514
670
  })
515
671
 
516
672
  });
517
- });
518
-
519
-
673
+ })
@@ -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('2.2.1')
10
+ monsterVersion = new Version('3.1.0')
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -9,7 +9,7 @@ describe('DeadMansSwitch', function () {
9
9
 
10
10
  const ms1 = Date.now();
11
11
 
12
- const deadmansswitch = new DeadMansSwitch(100, () => {
12
+ new DeadMansSwitch(100, () => {
13
13
 
14
14
  const ms2 = Date.now();
15
15
 
@@ -38,12 +38,12 @@ describe('DeadMansSwitch', function () {
38
38
  const diff = ms2 - ms1;
39
39
 
40
40
  if (ms1 > ms2) {
41
- done('timing error');
41
+ done(new Error('timing error'));
42
42
  return;
43
43
  }
44
44
 
45
45
  if (diff < 600) {
46
- done('to short ' + diff);
46
+ done(new Error('to short ' + diff));
47
47
  return;
48
48
  }
49
49
  done();
@@ -54,6 +54,7 @@ function initJSDOM(options) {
54
54
  'Document',
55
55
  'Node',
56
56
  'ShadowRoot',
57
+ 'EventTarget',
57
58
  'Event',
58
59
  'CustomEvent',
59
60
  'Element',
@@ -62,6 +63,7 @@ function initJSDOM(options) {
62
63
  'customElements',
63
64
  'DocumentFragment',
64
65
  'DOMParser',
66
+ 'KeyboardEvent',
65
67
  'CSSStyleSheet',
66
68
  'HTMLScriptElement',
67
69
  'MutationObserver',
@@ -0,0 +1,22 @@
1
+ import {getGlobal} from "../../../application/source/types/global.mjs";
2
+
3
+ function initWebSocket() {
4
+ if (typeof window === "object" && window['WebSocket']) return Promise.resolve();
5
+
6
+ return import("ws").then((ws) => {
7
+ getGlobal().WebSocket = class extends ws['WebSocket'] {
8
+ constructor(url, protocols) {
9
+ super(url, protocols, {
10
+ handshakeTimeout: 1000,
11
+ maxPayload: 1024 * 1024 * 1024,
12
+ });
13
+
14
+ }
15
+ };
16
+
17
+ });
18
+
19
+
20
+ }
21
+
22
+ export {initWebSocket}
@@ -14,6 +14,7 @@ import "../cases/dom/updater.mjs";
14
14
  import "../cases/dom/customcontrol.mjs";
15
15
  import "../cases/dom/locale.mjs";
16
16
  import "../cases/dom/theme.mjs";
17
+ import "../cases/dom/resource.mjs";
17
18
  import "../cases/dom/resourcemanager.mjs";
18
19
  import "../cases/dom/util.mjs";
19
20
  import "../cases/dom/customelement.mjs";
@@ -58,6 +59,7 @@ import "../cases/constraint/invalid.mjs";
58
59
  import "../cases/constraint/andoperator.mjs";
59
60
  import "../cases/constraint/oroperator.mjs";
60
61
  import "../cases/constraint/isarray.mjs";
62
+ import "../cases/constraint/abstractoperator.mjs";
61
63
  import "../cases/constraint/valid.mjs";
62
64
  import "../cases/util/trimspaces.mjs";
63
65
  import "../cases/util/deadmansswitch.mjs";
@@ -76,4 +78,5 @@ import "../cases/data/diff.mjs";
76
78
  import "../cases/data/datasource/restapi.mjs";
77
79
  import "../cases/data/datasource/storage/sessionstorage.mjs";
78
80
  import "../cases/data/datasource/storage/localstorage.mjs";
81
+ import "../cases/data/datasource/webservice.mjs";
79
82
  import "../cases/math/random.mjs";
@@ -5,7 +5,7 @@
5
5
  <title>Mocha Monster</title>
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
7
7
  <link rel="stylesheet" href="mocha.css"/>
8
- <script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.filter,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,CustomEvent,DataView,document,Document,DocumentFragment,Element,Event,fetch,globalThis,HTMLDocument,HTMLTemplateElement,Intl,JSON,Map,Math.log2,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.freeze,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getOwnPropertySymbols,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.for,Symbol.hasInstance,Symbol.iterator,Uint16Array,Uint8Array,URL,WeakMap,WeakSet"
8
+ <script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.filter,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,CustomEvent,DataView,document,Document,DocumentFragment,Element,Event,fetch,globalThis,HTMLDocument,HTMLTemplateElement,Intl,JSON,Map,Math.log2,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.freeze,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.for,Symbol.hasInstance,Symbol.iterator,Uint16Array,Uint8Array,URL,WeakMap,WeakSet"
9
9
  src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,DataView,document,DocumentFragment,Element,Event,globalThis,HTMLDocument,HTMLTemplateElement,JSON,Map,Math.log2,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.getOwnPropertyDescriptor,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.iterator,WeakMap,WeakSet"
10
10
  crossorigin="anonymous"
11
11
  referrerpolicy="no-referrer"></script>
@@ -14,8 +14,8 @@
14
14
  </head>
15
15
  <body>
16
16
  <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
17
- <h1 style='margin-bottom: 0.1em;'>Monster 2.0.16</h1>
18
- <div id="lastupdate" style='font-size:0.7em'>last update Do 29. Dez 14:29:19 CET 2022</div>
17
+ <h1 style='margin-bottom: 0.1em;'>Monster 3.0.0</h1>
18
+ <div id="lastupdate" style='font-size:0.7em'>last update Fr 6. Jan 12:54:47 CET 2023</div>
19
19
  </div>
20
20
  <div id="mocks"></div>
21
21
  <div id="mocha"></div>