@schukai/monster 3.73.3 → 3.73.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,28 +12,18 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import {internalSymbol} from "../../constants.mjs";
16
- import {Pathfinder} from "../../data/pathfinder.mjs";
15
+ import { Datasource } from "../../data/datasource.mjs";
16
+ import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
17
+ import { DataSet } from "../datatable/dataset.mjs";
17
18
  import {
18
- ATTRIBUTE_FORM_BIND,
19
- ATTRIBUTE_FORM_BIND_TYPE,
20
- ATTRIBUTE_UPDATER_BIND,
21
- } from "../../dom/constants.mjs";
22
- import {findTargetElementFromEvent} from "../../dom/events.mjs";
23
- import {ID} from "../../types/id.mjs";
24
- import {clone} from "../../util/clone.mjs";
25
- import {DeadMansSwitch} from "../../util/deadmansswitch.mjs";
26
- import {DataSet} from "../datatable/dataset.mjs";
27
- import {
28
- assembleMethodSymbol,
29
- registerCustomElement,
30
- getSlottedElements,
19
+ assembleMethodSymbol,
20
+ registerCustomElement,
21
+ getSlottedElements,
31
22
  } from "../../dom/customelement.mjs";
32
- import {FormStyleSheet} from "./stylesheet/form.mjs";
33
- import {diff} from "../../data/diff.mjs";
34
- import {isString} from "../../types/is.mjs";
23
+ import { datasourceLinkedElementSymbol } from "../datatable/util.mjs";
24
+ import { FormStyleSheet } from "./stylesheet/form.mjs";
35
25
 
36
- export {Form};
26
+ export { Form };
37
27
 
38
28
  /**
39
29
  * @private
@@ -48,198 +38,159 @@ const debounceWriteBackSymbol = Symbol("debounceWriteBack");
48
38
  const debounceBindSymbol = Symbol("debounceBind");
49
39
 
50
40
  class Form extends DataSet {
51
- /**
52
- *
53
- * @property {Object} templates Template definitions
54
- * @property {string} templates.main Main template
55
- * @property {Object} classes Class definitions
56
- * @property {string} classes.form Form class
57
- * @property {Object} writeBack Write back definitions
58
- * @property {string[]} writeBack.events Write back events
59
- * @property {Object} bind Bind definitions
60
- * @property {string[]} bind.events Bind events
61
- * @property {Object} reportValidity Report validity definitions
62
- * @property {string} reportValidity.selector Report validity selector
63
- * @property {boolean} features.mutationObserver Mutation observer feature
64
- * @property {boolean} features.writeBack Write back feature
65
- * @property {boolean} features.bind Bind feature
66
- */
67
- get defaults() {
68
- const obj = Object.assign({}, super.defaults, {
69
- templates: {
70
- main: getTemplate(),
71
- },
72
-
73
- classes: {
74
- form: "",
75
- },
76
-
77
- writeBack: {
78
- events: ["keyup", "click", "change", "drop", "touchend", "input"]
79
- },
80
-
81
- bind: {
82
- events: ["keyup", "click", "change", "drop", "touchend", "input"]
83
- },
84
-
85
- reportValidity: {
86
- selector: "input,select,textarea",
87
- },
88
- });
89
-
90
- obj["features"]["mutationObserver"] = false;
91
- obj["features"]["writeBack"] = true;
92
- obj["features"]["bind"] = true;
93
-
94
- return obj;
95
- }
96
-
97
- /**
98
- *
99
- * @return {string}
100
- */
101
- static getTag() {
102
- return "monster-form";
103
- }
104
-
105
- /**
106
- * @return {CSSStyleSheet[]}
107
- */
108
- static getCSSStyleSheet() {
109
- return [FormStyleSheet];
110
- }
111
-
112
- /**
113
- *
114
- */
115
- [assembleMethodSymbol]() {
116
- super[assembleMethodSymbol]();
117
-
118
- initControlReferences.call(this);
119
- initEventHandler.call(this);
120
- initDataSourceHandler.call(this);
121
- }
122
-
123
- /**
124
- * This method is called when the component is created.
125
- * @since 3.70.0
126
- * @returns {DataSet}
127
- */
128
- refresh() {
129
- this.write();
130
- super.refresh();
131
- return this;
132
- }
133
-
134
- /**
135
- * Run reportValidation on all child html form controls.
136
- *
137
- * @since 2.10.0
138
- * @returns {boolean}
139
- */
140
- reportValidity() {
141
- let valid = true;
142
-
143
- const selector = this.getOption("reportValidity.selector");
144
- const nodes = getSlottedElements.call(this, selector);
145
-
146
- nodes.forEach((node) => {
147
- if (typeof node.reportValidity === "function") {
148
- if (node.reportValidity() === false) {
149
- valid = false;
150
- }
151
- }
152
- });
153
-
154
- return valid;
155
- }
41
+ /**
42
+ *
43
+ * @property {Object} templates Template definitions
44
+ * @property {string} templates.main Main template
45
+ * @property {Object} classes Class definitions
46
+ * @property {string} classes.form Form class
47
+ * @property {Object} writeBack Write back definitions
48
+ * @property {string[]} writeBack.events Write back events
49
+ * @property {Object} bind Bind definitions
50
+ * @property {Object} reportValidity Report validity definitions
51
+ * @property {string} reportValidity.selector Report validity selector
52
+ * @property {boolean} features.mutationObserver Mutation observer feature
53
+ * @property {boolean} features.writeBack Write back feature
54
+ * @property {boolean} features.bind Bind feature
55
+ */
56
+ get defaults() {
57
+ const obj = Object.assign({}, super.defaults, {
58
+ templates: {
59
+ main: getTemplate(),
60
+ },
61
+
62
+ classes: {
63
+ form: "",
64
+ },
65
+
66
+ writeBack: {
67
+ events: ["keyup", "click", "change", "drop", "touchend", "input"],
68
+ },
69
+
70
+ reportValidity: {
71
+ selector: "input,select,textarea",
72
+ },
73
+
74
+ eventProcessing: true,
75
+ });
76
+
77
+ obj["features"]["mutationObserver"] = false;
78
+ obj["features"]["writeBack"] = true;
79
+
80
+ return obj;
81
+ }
82
+
83
+ /**
84
+ *
85
+ * @return {string}
86
+ */
87
+ static getTag() {
88
+ return "monster-form";
89
+ }
90
+
91
+ /**
92
+ * @return {CSSStyleSheet[]}
93
+ */
94
+ static getCSSStyleSheet() {
95
+ return [FormStyleSheet];
96
+ }
97
+
98
+ /**
99
+ *
100
+ */
101
+ [assembleMethodSymbol]() {
102
+ const selector = this.getOption("datasource.selector");
103
+
104
+ if (!selector) {
105
+ this[datasourceLinkedElementSymbol] = new Datasource(this);
106
+ }
107
+
108
+ super[assembleMethodSymbol]();
109
+
110
+ initControlReferences.call(this);
111
+ initEventHandler.call(this);
112
+ initDataSourceHandler.call(this);
113
+ }
114
+
115
+ /**
116
+ * This method is called when the component is created.
117
+ * @since 3.70.0
118
+ * @returns {DataSet}
119
+ */
120
+ refresh() {
121
+ this.write();
122
+ super.refresh();
123
+ return this;
124
+ }
125
+
126
+ /**
127
+ * Run reportValidation on all child html form controls.
128
+ *
129
+ * @since 2.10.0
130
+ * @returns {boolean}
131
+ */
132
+ reportValidity() {
133
+ let valid = true;
134
+
135
+ const selector = this.getOption("reportValidity.selector");
136
+ const nodes = getSlottedElements.call(this, selector);
137
+
138
+ nodes.forEach((node) => {
139
+ if (typeof node.reportValidity === "function") {
140
+ if (node.reportValidity() === false) {
141
+ valid = false;
142
+ }
143
+ }
144
+ });
145
+
146
+ return valid;
147
+ }
156
148
  }
157
149
 
158
- function initDataSourceHandler() {
159
- }
150
+ function initDataSourceHandler() {}
160
151
 
161
152
  /**
162
153
  * @private
163
154
  * @returns {initEventHandler}
164
155
  */
165
156
  function initEventHandler() {
166
- this[debounceBindSymbol] = {};
167
-
168
- if (this.getOption("features.bind") === true) {
169
- const events = this.getOption("bind.events");
170
-
171
- for (const event of events) {
172
- this.addEventListener(event, (e) => {
173
- const element = findTargetElementFromEvent(e, ATTRIBUTE_FORM_BIND);
174
-
175
- if (!(element instanceof HTMLElement)) {
176
- return;
177
- }
178
-
179
- let elementID
180
- if (!element.hasAttribute("data-monster-debounce-id")) {
181
- elementID = new ID('debounce').toString();
182
- element.setAttribute("data-monster-debounce-id", elementID);
183
- } else {
184
- elementID = element.getAttribute("data-monster-debounce-id");
185
- }
186
-
187
- if (this[debounceBindSymbol][elementID] instanceof DeadMansSwitch) {
188
- try {
189
- this[debounceBindSymbol][elementID].touch();
190
- return;
191
- } catch (e) {
192
- if (e.message !== "has already run") {
193
- throw e;
194
- }
195
-
196
- delete this[debounceBindSymbol][elementID];
197
- }
198
- }
199
-
200
- this[debounceBindSymbol][elementID] = new DeadMansSwitch(200, () => {
201
- delete this[debounceBindSymbol][elementID];
202
- retrieveAndSetValue.call(this, element);
203
- });
204
- });
205
- }
206
- }
207
-
208
- if (this.getOption("features.writeBack") === true) {
209
- const events = this.getOption("writeBack.events");
210
- for (const event of events) {
211
- this.addEventListener(event, (e) => {
212
- if (!this.reportValidity()) {
213
- this.classList.add("invalid");
214
- setTimeout(() => {
215
- this.classList.remove("invalid");
216
- }, 1000);
217
-
218
- return;
219
- }
220
-
221
- if (this[debounceWriteBackSymbol] instanceof DeadMansSwitch) {
222
- try {
223
- this[debounceWriteBackSymbol].touch();
224
- return;
225
- } catch (e) {
226
- if (e.message !== "has already run") {
227
- throw e;
228
- }
229
- delete this[debounceWriteBackSymbol];
230
- }
231
- }
232
-
233
- this[debounceWriteBackSymbol] = new DeadMansSwitch(200, () => {
234
- setTimeout(() => {
235
- this.write();
236
- }, 0);
237
- });
238
- });
239
- }
240
- }
241
-
242
- return this;
157
+ this[debounceBindSymbol] = {};
158
+
159
+ if (this.getOption("features.writeBack") === true) {
160
+ const events = this.getOption("writeBack.events");
161
+ for (const event of events) {
162
+ this.addEventListener(event, (e) => {
163
+ if (!this.reportValidity()) {
164
+ this.classList.add("invalid");
165
+ setTimeout(() => {
166
+ this.classList.remove("invalid");
167
+ }, 1000);
168
+
169
+ return;
170
+ }
171
+
172
+ if (this[debounceWriteBackSymbol] instanceof DeadMansSwitch) {
173
+ try {
174
+ this[debounceWriteBackSymbol].touch();
175
+ return;
176
+ } catch (e) {
177
+ if (e.message !== "has already run") {
178
+ throw e;
179
+ }
180
+ delete this[debounceWriteBackSymbol];
181
+ }
182
+ }
183
+
184
+ this[debounceWriteBackSymbol] = new DeadMansSwitch(200, () => {
185
+ setTimeout(() => {
186
+ this.write();
187
+ }, 0);
188
+ });
189
+ });
190
+ }
191
+ }
192
+
193
+ return this;
243
194
  }
244
195
 
245
196
  /**
@@ -247,115 +198,10 @@ function initEventHandler() {
247
198
  * @return {FilterButton}
248
199
  */
249
200
  function initControlReferences() {
250
- if (!this.shadowRoot) {
251
- throw new Error("no shadow-root is defined");
252
- }
253
- return this;
254
- }
255
-
256
- /**
257
- * @throws {Error} the bind argument must start as a value with a path
258
- * @param {HTMLElement} element
259
- * @return void
260
- * @memberOf Monster.DOM
261
- * @private
262
- */
263
- function retrieveAndSetValue(element) {
264
- let path = element.getAttribute(ATTRIBUTE_FORM_BIND);
265
- if (path === null)
266
- throw new Error("the bind argument must start as a value with a path");
267
-
268
- if (path.indexOf("path:") !== 0) {
269
- throw new Error("the bind argument must start as a value with a path");
270
- }
271
-
272
- path = path.substring(5); // remove path: from the string
273
-
274
- let value;
275
-
276
- if (element instanceof HTMLInputElement) {
277
- switch (element.type) {
278
- case "checkbox":
279
- value = element.checked ? element.value : undefined;
280
- break;
281
- default:
282
- value = element.value;
283
- break;
284
- }
285
- } else if (element instanceof HTMLTextAreaElement) {
286
- value = element.value;
287
- } else if (element instanceof HTMLSelectElement) {
288
- switch (element.type) {
289
- case "select-one":
290
- value = element.value;
291
- break;
292
- case "select-multiple":
293
- value = element.value;
294
-
295
- let options = element?.selectedOptions;
296
- if (options === undefined)
297
- options = element.querySelectorAll(":scope option:checked");
298
- value = Array.from(options).map(({value}) => value);
299
-
300
- break;
301
- }
302
-
303
- // values from custom elements
304
- } else if (
305
- (element?.constructor?.prototype &&
306
- !!Object.getOwnPropertyDescriptor(
307
- element.constructor.prototype,
308
- "value",
309
- )?.["get"]) ||
310
- element.hasOwnProperty("value")
311
- ) {
312
- value = element?.["value"];
313
- } else {
314
- throw new Error("unsupported object");
315
- }
316
-
317
- if (isString(value)) {
318
- const type = element.getAttribute(ATTRIBUTE_FORM_BIND_TYPE);
319
- switch (type) {
320
- case "number":
321
- case "int":
322
- case "float":
323
- case "integer":
324
- value = Number(value);
325
- if (isNaN(value)) {
326
- value = 0;
327
- }
328
- break;
329
- case "boolean":
330
- case "bool":
331
- case "checkbox":
332
- value = value === "true" || value === "1" || value === "on";
333
- break;
334
- case "array":
335
- case "list":
336
- value = value.split(",");
337
- break;
338
- case "object":
339
- case "json":
340
- value = JSON.parse(value);
341
- break;
342
- default:
343
- break;
344
- }
345
- }
346
-
347
- const copy = clone(this[internalSymbol].getRealSubject()?.options);
348
-
349
- const pf = new Pathfinder(copy);
350
- pf.setVia(path, value);
351
-
352
- const diffResult = diff(copy, this[internalSymbol].getRealSubject()?.options);
353
-
354
- if (diffResult.length > 0) {
355
- setTimeout(() => {
356
- this.setOption(path, value);
357
- }, 50);
358
- }
201
+ if (!this.shadowRoot) {
202
+ throw new Error("no shadow-root is defined");
203
+ }
204
+ return this;
359
205
  }
360
206
 
361
207
  /**
@@ -363,8 +209,8 @@ function retrieveAndSetValue(element) {
363
209
  * @return {string}
364
210
  */
365
211
  function getTemplate() {
366
- // language=HTML
367
- return `
212
+ // language=HTML
213
+ return `
368
214
  <div data-monster-role="control" part="control">
369
215
  <form data-monster-attributes="disabled path:disabled | if:true, class path:classes.form"
370
216
  data-monster-role="form"
@@ -327,6 +327,7 @@ class CustomElement extends HTMLElement {
327
327
  * @property {Object} templates Specifies the templates used by the control.
328
328
  * @property {string} templates.main=undefined Specifies the main template used by the control.
329
329
  * @property {Object} templateMapping Specifies the mapping of templates.
330
+ * @property {Boolean} eventProcessing=false Specifies whether the control processes events.
330
331
  * @since 1.8.0
331
332
  */
332
333
  get defaults() {
@@ -338,6 +339,8 @@ class CustomElement extends HTMLElement {
338
339
  main: undefined,
339
340
  },
340
341
  templateMapping: {},
342
+
343
+ eventProcessing: false,
341
344
  };
342
345
  }
343
346
 
@@ -644,11 +647,17 @@ class CustomElement extends HTMLElement {
644
647
  this[internalSymbol].getRealSubject()["options"],
645
648
  );
646
649
 
650
+ const cfg = {};
651
+ if (this.getOption("eventProcessing") === true) {
652
+ cfg.eventProcessing = true;
653
+ }
654
+
647
655
  addObjectWithUpdaterToElement.call(
648
656
  this,
649
657
  nodeList,
650
658
  customElementUpdaterLinkSymbol,
651
659
  this[updateCloneDataSymbol],
660
+ cfg,
652
661
  );
653
662
 
654
663
  // Attach a mutation observer to observe changes to the attributes of the element
@@ -156,7 +156,6 @@ class Updater extends Base {
156
156
 
157
157
  for (const type of this[internalSymbol].eventTypes) {
158
158
  // @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
159
-
160
159
  this[internalSymbol].element.addEventListener(
161
160
  type,
162
161
  getControlEventHandler.call(this),
@@ -745,9 +744,7 @@ function runUpdateContent(container, parts, subject) {
745
744
 
746
745
  /**
747
746
  * @private
748
- * @license AGPLv3
749
747
  * @since 1.8.0
750
- * @param {string} path
751
748
  * @param {object} change
752
749
  * @return {void}
753
750
  */
@@ -900,6 +897,10 @@ function handleInputControlAttributeUpdate(element, name, value) {
900
897
  * @param {NodeList|HTMLElement|Set<HTMLElement>} elements
901
898
  * @param {Symbol} symbol
902
899
  * @param {object} object
900
+ * @param {object} config
901
+ *
902
+ * Config: enableEventProcessing {boolean} - default: false - enables the event processing
903
+ *
903
904
  * @return {Promise[]}
904
905
  * @license AGPLv3
905
906
  * @since 1.23.0
@@ -908,7 +909,7 @@ function handleInputControlAttributeUpdate(element, name, value) {
908
909
  * @throws {TypeError} the context of the function is not an instance of HTMLElement
909
910
  * @throws {TypeError} symbol must be an instance of Symbol
910
911
  */
911
- function addObjectWithUpdaterToElement(elements, symbol, object) {
912
+ function addObjectWithUpdaterToElement(elements, symbol, object, config = {}) {
912
913
  if (!(this instanceof HTMLElement)) {
913
914
  throw new TypeError(
914
915
  "the context of this function must be an instance of HTMLElement",
@@ -974,7 +975,11 @@ function addObjectWithUpdaterToElement(elements, symbol, object) {
974
975
 
975
976
  result.push(
976
977
  u.run().then(() => {
977
- return u.enableEventProcessing();
978
+ if (config.eventProcessing === true) {
979
+ u.enableEventProcessing();
980
+ }
981
+
982
+ return u;
978
983
  }),
979
984
  );
980
985
  });
@@ -159,7 +159,7 @@ function getMonsterVersion() {
159
159
  }
160
160
 
161
161
  /** don't touch, replaced by make with package.json version */
162
- monsterVersion = new Version("3.65.0");
162
+ monsterVersion = new Version("3.73.2");
163
163
 
164
164
  return monsterVersion;
165
165
  }
@@ -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.65.0")
10
+ monsterVersion = new Version("3.73.2")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -9,8 +9,8 @@
9
9
  </head>
10
10
  <body>
11
11
  <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
12
- <h1 style='margin-bottom: 0.1em;'>Monster 3.65.0</h1>
13
- <div id="lastupdate" style='font-size:0.7em'>last update Di 18. Jun 22:58:11 CEST 2024</div>
12
+ <h1 style='margin-bottom: 0.1em;'>Monster 3.73.2</h1>
13
+ <div id="lastupdate" style='font-size:0.7em'>last update Di 2. Jul 20:16:21 CEST 2024</div>
14
14
  </div>
15
15
  <div id="mocha-errors"
16
16
  style="color: red;font-weight: bold;display: flex;align-items: center;justify-content: center;flex-direction: column;margin:20px;"></div>