@schukai/monster 3.65.20 → 3.65.21

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/CHANGELOG.md CHANGED
@@ -2,55 +2,21 @@
2
2
 
3
3
 
4
4
 
5
- ## [3.65.20] - 2024-06-20
6
-
7
- ### Bug Fixes
8
-
9
- - wip new pipeline
10
- - wip new pipeline
11
-
12
-
13
-
14
- ## [3.65.19] - 2024-06-20
15
-
16
- ### Bug Fixes
17
-
18
- - wip new pipeline
19
-
20
-
21
-
22
- ## [3.65.18] - 2024-06-20
23
-
24
- ### Bug Fixes
25
-
26
- - wip new pipeline
27
-
28
-
29
-
30
- ## [3.65.17] - 2024-06-20
31
-
32
- ### Bug Fixes
33
-
34
- - wip new pipeline
35
-
36
-
37
-
38
- ## [3.65.16] - 2024-06-20
5
+ ## [3.65.21] - 2024-06-20
39
6
 
40
7
  ### Bug Fixes
41
8
 
42
- - wip new pipeline
9
+ - exchange of document.selector with function findElementWithSelectorUpwards [#199](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/199)
43
10
 
11
+ ## [3.65.20] - 2024-06-20
44
12
 
45
-
46
- ## [3.65.15] - 2024-06-20
47
-
13
+ - Changeover to new release process
48
14
 
49
15
  ## [3.65.3] - 2024-06-19
50
16
 
51
17
  ### Bug Fixes
52
18
 
53
- - switch nodejs_22 to nodejs_20
19
+ - switch nodejs_22 to nodejs_20 (segmentation fault)
54
20
 
55
21
  ## [3.65.1] - 2024-06-17
56
22
 
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.5","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"3.65.20"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.5","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"3.65.21"}
@@ -20,6 +20,7 @@ import {
20
20
  CustomElement,
21
21
  registerCustomElement,
22
22
  } from "../../dom/customelement.mjs";
23
+ import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
23
24
  import { isString } from "../../types/is.mjs";
24
25
  import { State } from "../form/types/state.mjs";
25
26
  import "../form/state-button.mjs";
@@ -189,12 +190,12 @@ function initControlReferences() {
189
190
  const selector = this.getOption("dataset.selector");
190
191
 
191
192
  if (isString(selector)) {
192
- const elements = document.querySelectorAll(selector);
193
- if (elements.length !== 1) {
193
+
194
+ const element = findElementWithSelectorUpwards( this,selector);
195
+ if (element===null) {
194
196
  throw new Error("the selector must match exactly one element");
195
197
  }
196
198
 
197
- const element = elements[0];
198
199
  if (!(element instanceof HTMLElement)) {
199
200
  throw new TypeError("the element must be a dataset");
200
201
  }
@@ -205,12 +206,11 @@ function initControlReferences() {
205
206
  const selector2 = this.getOption("overlay.selector");
206
207
 
207
208
  if (isString(selector2)) {
208
- const elements = document.querySelectorAll(selector2);
209
- if (elements.length !== 1) {
209
+ const element = findElementWithSelectorUpwards( this,selector);
210
+ if (element===null) {
210
211
  throw new Error("the selector must match exactly one element");
211
212
  }
212
213
 
213
- const element = elements[0];
214
214
  if (!(element instanceof HTMLElement)) {
215
215
  throw new TypeError("the element must be a overlay");
216
216
  }
@@ -12,32 +12,33 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import { instanceSymbol, internalSymbol } from "../../constants.mjs";
16
- import { Pathfinder } from "../../data/pathfinder.mjs";
17
- import { getLinkedObjects, hasObjectLink } from "../../dom/attributes.mjs";
18
- import { customElementUpdaterLinkSymbol } from "../../dom/constants.mjs";
15
+ import {instanceSymbol, internalSymbol} from "../../constants.mjs";
16
+ import {Pathfinder} from "../../data/pathfinder.mjs";
17
+ import {getLinkedObjects, hasObjectLink} from "../../dom/attributes.mjs";
18
+ import {customElementUpdaterLinkSymbol} from "../../dom/constants.mjs";
19
19
  import {
20
- assembleMethodSymbol,
21
- CustomElement,
22
- attributeObserverSymbol,
23
- registerCustomElement,
20
+ assembleMethodSymbol,
21
+ CustomElement,
22
+ attributeObserverSymbol,
23
+ registerCustomElement,
24
24
  } from "../../dom/customelement.mjs";
25
- import { isString } from "../../types/is.mjs";
26
- import { Observer } from "../../types/observer.mjs";
27
- import { clone } from "../../util/clone.mjs";
25
+ import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
26
+ import {isString} from "../../types/is.mjs";
27
+ import {Observer} from "../../types/observer.mjs";
28
+ import {clone} from "../../util/clone.mjs";
28
29
  import {
29
- ATTRIBUTE_DATASOURCE_SELECTOR,
30
- ATTRIBUTE_DATATABLE_INDEX,
30
+ ATTRIBUTE_DATASOURCE_SELECTOR,
31
+ ATTRIBUTE_DATATABLE_INDEX,
31
32
  } from "./constants.mjs";
32
- import { Datasource } from "./datasource.mjs";
33
- import { DatasetStyleSheet } from "./stylesheet/dataset.mjs";
33
+ import {Datasource} from "./datasource.mjs";
34
+ import {DatasetStyleSheet} from "./stylesheet/dataset.mjs";
34
35
  import {
35
- handleDataSourceChanges,
36
- datasourceLinkedElementSymbol,
36
+ handleDataSourceChanges,
37
+ datasourceLinkedElementSymbol,
37
38
  } from "./util.mjs";
38
- import { FormStyleSheet } from "../stylesheet/form.mjs";
39
+ import {FormStyleSheet} from "../stylesheet/form.mjs";
39
40
 
40
- export { DataSet };
41
+ export {DataSet};
41
42
 
42
43
  /**
43
44
  * The data set component is used to show the data of a data source.
@@ -78,185 +79,184 @@ export { DataSet };
78
79
  * @summary A data set
79
80
  */
80
81
  class DataSet extends CustomElement {
81
- /**
82
- * This method is called by the `instanceof` operator.
83
- * @returns {symbol}
84
- */
85
- static get [instanceSymbol]() {
86
- return Symbol.for("@schukai/monster/components/dataset@@instance");
87
- }
88
-
89
- /**
90
- * This method determines which attributes are to be monitored by `attributeChangedCallback()`.
91
- *
92
- * @return {string[]}
93
- * @since 1.15.0
94
- */
95
- static get observedAttributes() {
96
- const attributes = super.observedAttributes;
97
- attributes.push(ATTRIBUTE_DATATABLE_INDEX);
98
- return attributes;
99
- }
100
-
101
- /**
102
- * To set the options via the html tag the attribute `data-monster-options` must be used.
103
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
104
- *
105
- * The individual configuration values can be found in the table.
106
- *
107
- * @property {Object} templates Template definitions
108
- * @property {string} templates.main Main template
109
- * @property {object} datasource The datasource
110
- * @property {string} datasource.selector The selector of the datasource
111
- * @property {object} mapping The mapping
112
- * @property {string} mapping.data The data
113
- * @property {number} mapping.index The index
114
- * @property {Array} data The data
115
- */
116
- get defaults() {
117
- const obj = Object.assign({}, super.defaults, {
118
- templates: {
119
- main: getTemplate(),
120
- },
121
-
122
- datasource: {
123
- selector: null,
124
- },
125
-
126
- mapping: {
127
- data: "dataset",
128
- index: 0,
129
- },
130
-
131
- data: {},
132
- });
133
-
134
- updateOptionsFromArguments.call(this, obj);
135
- return obj;
136
- }
137
-
138
- /**
139
- *
140
- * @return {string}
141
- */
142
- static getTag() {
143
- return "monster-dataset";
144
- }
145
-
146
- write() {
147
- return new Promise((resolve, reject) => {
148
- if (!this[datasourceLinkedElementSymbol]) {
149
- reject(new Error("No datasource"));
150
- return;
151
- }
152
-
153
- const internalUpdateCloneData = this.getInternalUpdateCloneData();
154
- if (!internalUpdateCloneData) {
155
- reject(new Error("No update data"));
156
- return;
157
- }
158
-
159
- const internalData = internalUpdateCloneData?.["data"];
160
- if (
161
- internalData === undefined ||
162
- internalData === null ||
163
- internalData === ""
164
- ) {
165
- reject(new Error("No data"));
166
- return;
167
- }
168
-
169
- setTimeout(() => {
170
- const path = this.getOption("mapping.data");
171
- const index = this.getOption("mapping.index");
172
-
173
- let pathWithIndex;
174
-
175
- if (isString(path) && path !== "") {
176
- pathWithIndex = path + "." + index;
177
- } else {
178
- pathWithIndex = index;
179
- }
180
-
181
- const data = this[datasourceLinkedElementSymbol].data;
182
- const unref = JSON.stringify(data);
183
- const ref = JSON.parse(unref);
184
-
185
- new Pathfinder(ref).setVia(pathWithIndex, internalData);
186
-
187
- this[datasourceLinkedElementSymbol].data = ref;
188
-
189
- resolve();
190
- }, 0);
191
- });
192
- }
193
-
194
- /**
195
- * This method is responsible for assembling the component.
196
- *
197
- * It calls the parent's assemble method first, then initializes control references and event handlers.
198
- * If the `datasource.selector` option is provided and is a string, it searches for the corresponding
199
- * element in the DOM using that selector.
200
- *
201
- * If the selector matches exactly one element, it checks if the element is an instance of the `Datasource` class.
202
- *
203
- * If it is, the component's `datasourceLinkedElementSymbol` property is set to the element, and the component
204
- * attaches an observer to the datasource's changes.
205
- *
206
- * The observer is a function that calls the `handleDataSourceChanges` method in the context of the component.
207
- * Additionally, the component attaches an observer to itself, which also calls the `handleDataSourceChanges`
208
- * method in the component's context.
209
- */
210
- [assembleMethodSymbol]() {
211
- super[assembleMethodSymbol]();
212
-
213
- // initControlReferences.call(self);
214
- initEventHandler.call(this);
215
-
216
- const selector = this.getOption("datasource.selector");
217
-
218
- if (isString(selector)) {
219
- const elements = document.querySelectorAll(selector);
220
- if (elements.length !== 1) {
221
- throw new Error("the selector must match exactly one element");
222
- }
223
-
224
- const element = elements[0];
225
- if (!(element instanceof Datasource)) {
226
- throw new TypeError("the element must be a datasource");
227
- }
228
-
229
- this[datasourceLinkedElementSymbol] = element;
230
- element.datasource.attachObserver(
231
- new Observer(handleDataSourceChanges.bind(this)),
232
- );
233
- }
234
-
235
- this.attachObserver(
236
- new Observer(() => {
237
- handleDataSourceChanges.call(this);
238
- }),
239
- );
240
- }
241
-
242
- /**
243
- * @return [CSSStyleSheet]
244
- */
245
- static getCSSStyleSheet() {
246
- return [FormStyleSheet, DatasetStyleSheet];
247
- }
82
+ /**
83
+ * This method is called by the `instanceof` operator.
84
+ * @returns {symbol}
85
+ */
86
+ static get [instanceSymbol]() {
87
+ return Symbol.for("@schukai/monster/components/dataset@@instance");
88
+ }
89
+
90
+ /**
91
+ * This method determines which attributes are to be monitored by `attributeChangedCallback()`.
92
+ *
93
+ * @return {string[]}
94
+ * @since 1.15.0
95
+ */
96
+ static get observedAttributes() {
97
+ const attributes = super.observedAttributes;
98
+ attributes.push(ATTRIBUTE_DATATABLE_INDEX);
99
+ return attributes;
100
+ }
101
+
102
+ /**
103
+ * To set the options via the html tag the attribute `data-monster-options` must be used.
104
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
105
+ *
106
+ * The individual configuration values can be found in the table.
107
+ *
108
+ * @property {Object} templates Template definitions
109
+ * @property {string} templates.main Main template
110
+ * @property {object} datasource The datasource
111
+ * @property {string} datasource.selector The selector of the datasource
112
+ * @property {object} mapping The mapping
113
+ * @property {string} mapping.data The data
114
+ * @property {number} mapping.index The index
115
+ * @property {Array} data The data
116
+ */
117
+ get defaults() {
118
+ const obj = Object.assign({}, super.defaults, {
119
+ templates: {
120
+ main: getTemplate(),
121
+ },
122
+
123
+ datasource: {
124
+ selector: null,
125
+ },
126
+
127
+ mapping: {
128
+ data: "dataset",
129
+ index: 0,
130
+ },
131
+
132
+ data: {},
133
+ });
134
+
135
+ updateOptionsFromArguments.call(this, obj);
136
+ return obj;
137
+ }
138
+
139
+ /**
140
+ *
141
+ * @return {string}
142
+ */
143
+ static getTag() {
144
+ return "monster-dataset";
145
+ }
146
+
147
+ write() {
148
+ return new Promise((resolve, reject) => {
149
+ if (!this[datasourceLinkedElementSymbol]) {
150
+ reject(new Error("No datasource"));
151
+ return;
152
+ }
153
+
154
+ const internalUpdateCloneData = this.getInternalUpdateCloneData();
155
+ if (!internalUpdateCloneData) {
156
+ reject(new Error("No update data"));
157
+ return;
158
+ }
159
+
160
+ const internalData = internalUpdateCloneData?.["data"];
161
+ if (
162
+ internalData === undefined ||
163
+ internalData === null ||
164
+ internalData === ""
165
+ ) {
166
+ reject(new Error("No data"));
167
+ return;
168
+ }
169
+
170
+ setTimeout(() => {
171
+ const path = this.getOption("mapping.data");
172
+ const index = this.getOption("mapping.index");
173
+
174
+ let pathWithIndex;
175
+
176
+ if (isString(path) && path !== "") {
177
+ pathWithIndex = path + "." + index;
178
+ } else {
179
+ pathWithIndex = index;
180
+ }
181
+
182
+ const data = this[datasourceLinkedElementSymbol].data;
183
+ const unref = JSON.stringify(data);
184
+ const ref = JSON.parse(unref);
185
+
186
+ new Pathfinder(ref).setVia(pathWithIndex, internalData);
187
+
188
+ this[datasourceLinkedElementSymbol].data = ref;
189
+
190
+ resolve();
191
+ }, 0);
192
+ });
193
+ }
194
+
195
+ /**
196
+ * This method is responsible for assembling the component.
197
+ *
198
+ * It calls the parent's assemble method first, then initializes control references and event handlers.
199
+ * If the `datasource.selector` option is provided and is a string, it searches for the corresponding
200
+ * element in the DOM using that selector.
201
+ *
202
+ * If the selector matches exactly one element, it checks if the element is an instance of the `Datasource` class.
203
+ *
204
+ * If it is, the component's `datasourceLinkedElementSymbol` property is set to the element, and the component
205
+ * attaches an observer to the datasource's changes.
206
+ *
207
+ * The observer is a function that calls the `handleDataSourceChanges` method in the context of the component.
208
+ * Additionally, the component attaches an observer to itself, which also calls the `handleDataSourceChanges`
209
+ * method in the component's context.
210
+ */
211
+ [assembleMethodSymbol]() {
212
+ super[assembleMethodSymbol]();
213
+
214
+ // initControlReferences.call(self);
215
+ initEventHandler.call(this);
216
+
217
+ const selector = this.getOption("datasource.selector");
218
+
219
+ if (isString(selector)) {
220
+ const element = findElementWithSelectorUpwards(this, selector);
221
+ if (element === null) {
222
+ throw new Error("the selector must match exactly one element");
223
+ }
224
+
225
+ if (!(element instanceof Datasource)) {
226
+ throw new TypeError("the element must be a datasource");
227
+ }
228
+
229
+ this[datasourceLinkedElementSymbol] = element;
230
+ element.datasource.attachObserver(
231
+ new Observer(handleDataSourceChanges.bind(this)),
232
+ );
233
+ }
234
+
235
+ this.attachObserver(
236
+ new Observer(() => {
237
+ handleDataSourceChanges.call(this);
238
+ }),
239
+ );
240
+ }
241
+
242
+ /**
243
+ * @return [CSSStyleSheet]
244
+ */
245
+ static getCSSStyleSheet() {
246
+ return [FormStyleSheet, DatasetStyleSheet];
247
+ }
248
248
  }
249
249
 
250
250
  /**
251
251
  * @private
252
252
  */
253
253
  function initEventHandler() {
254
- this[attributeObserverSymbol][ATTRIBUTE_DATATABLE_INDEX] = () => {
255
- const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
256
- if (index) {
257
- this.setOption("mapping.index", parseInt(index, 10));
258
- }
259
- };
254
+ this[attributeObserverSymbol][ATTRIBUTE_DATATABLE_INDEX] = () => {
255
+ const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
256
+ if (index) {
257
+ this.setOption("mapping.index", parseInt(index, 10));
258
+ }
259
+ };
260
260
  }
261
261
 
262
262
  /**
@@ -264,17 +264,17 @@ function initEventHandler() {
264
264
  * @param {Object} options
265
265
  */
266
266
  function updateOptionsFromArguments(options) {
267
- const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
267
+ const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
268
268
 
269
- if (index !== null && index !== undefined) {
270
- options.mapping.index = parseInt(index, 10);
271
- }
269
+ if (index !== null && index !== undefined) {
270
+ options.mapping.index = parseInt(index, 10);
271
+ }
272
272
 
273
- const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
273
+ const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
274
274
 
275
- if (selector) {
276
- options.datasource.selector = selector;
277
- }
275
+ if (selector) {
276
+ options.datasource.selector = selector;
277
+ }
278
278
  }
279
279
 
280
280
  /**
@@ -282,8 +282,8 @@ function updateOptionsFromArguments(options) {
282
282
  * @return {string}
283
283
  */
284
284
  function getTemplate() {
285
- // language=HTML
286
- return `
285
+ // language=HTML
286
+ return `
287
287
  <div data-monster-role="control" part="control">
288
288
  <slot></slot>
289
289
  </div>
@@ -511,8 +511,7 @@ function getFilterConfigKey() {
511
511
  * @returns {Promise}
512
512
  */
513
513
  function getHostConfig(callback) {
514
- const document = getDocument();
515
- const host = document.querySelector("monster-host");
514
+ const host = findElementWithSelectorUpwards( this,"monster-host");
516
515
 
517
516
  if (!(host && this.id)) {
518
517
  return Promise.resolve({});
@@ -608,8 +607,7 @@ function updateConfigColumnBar() {
608
607
  map[option.name] = option.visible;
609
608
  }
610
609
 
611
- const document = getDocument();
612
- const host = document.querySelector("monster-host");
610
+ const host = findElementWithSelectorUpwards( this,"monster-host");
613
611
  if (!(host && this.id)) {
614
612
  return;
615
613
  }
@@ -816,8 +814,7 @@ function storeOrderStatement(doFetch) {
816
814
  const statement = createOrderStatement(headers);
817
815
  setDataSource.call(this, { orderBy: statement }, doFetch);
818
816
 
819
- const document = getDocument();
820
- const host = document.querySelector("monster-host");
817
+ const host = findElementWithSelectorUpwards( this,"monster-host");
821
818
  if (!(host && this.id)) {
822
819
  return;
823
820
  }
@@ -726,8 +726,7 @@ function initTabEvents() {
726
726
  }
727
727
  }
728
728
 
729
- const document = getDocument();
730
- const host = document.querySelector("monster-host");
729
+ const host = findElementWithSelectorUpwards( this,"monster-host");
731
730
  if (!(host && this.id)) {
732
731
  return;
733
732
  }
@@ -768,8 +767,7 @@ function updateFilterTabs() {
768
767
  return;
769
768
  }
770
769
 
771
- const document = getDocument();
772
- const host = document.querySelector("monster-host");
770
+ const host = findElementWithSelectorUpwards( this,"monster-host");
773
771
  if (!(host && this.id)) {
774
772
  return;
775
773
  }
@@ -1067,8 +1065,7 @@ function getControlValuesFromLabel(label) {
1067
1065
  * @returns {Promise<unknown>}
1068
1066
  */
1069
1067
  function initFromConfig() {
1070
- const document = getDocument();
1071
- const host = document.querySelector("monster-host");
1068
+ const host = findElementWithSelectorUpwards( this,"monster-host");
1072
1069
 
1073
1070
  if (!(isInstance(host, Host) && this.id)) {
1074
1071
  return Promise.resolve();
@@ -1111,8 +1108,7 @@ function initFromConfig() {
1111
1108
  * @private
1112
1109
  */
1113
1110
  function updateConfig() {
1114
- const document = getDocument();
1115
- const host = document.querySelector("monster-host");
1111
+ const host = findElementWithSelectorUpwards( this,"monster-host");
1116
1112
  if (!(host && this.id)) {
1117
1113
  return;
1118
1114
  }
@@ -17,6 +17,7 @@ import {
17
17
  CustomElement,
18
18
  registerCustomElement,
19
19
  } from "../../dom/customelement.mjs";
20
+ import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
20
21
  import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
21
22
  import { ATTRIBUTE_DATASOURCE_SELECTOR } from "./constants.mjs";
22
23
  import { Datasource } from "./datasource.mjs";
@@ -188,12 +189,11 @@ class Pagination extends CustomElement {
188
189
  const selector = this.getOption("datasource.selector", "");
189
190
 
190
191
  if (isString(selector)) {
191
- const elements = document.querySelectorAll(selector);
192
- if (elements.length !== 1) {
192
+ const element = findElementWithSelectorUpwards( this,selector);
193
+ if (element===null) {
193
194
  throw new Error("the selector must match exactly one element");
194
195
  }
195
-
196
- const element = elements[0];
196
+
197
197
  if (!(element instanceof Datasource)) {
198
198
  throw new TypeError("the element must be a datasource");
199
199
  }
@@ -22,6 +22,7 @@ import {
22
22
  attributeObserverSymbol,
23
23
  registerCustomElement,
24
24
  } from "../../dom/customelement.mjs";
25
+ import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
25
26
  import { isString, isArray } from "../../types/is.mjs";
26
27
  import { Observer } from "../../types/observer.mjs";
27
28
  import { TokenList } from "../../types/tokenlist.mjs";
@@ -154,12 +155,12 @@ class SaveButton extends CustomElement {
154
155
  const selector = this.getOption("datasource.selector");
155
156
 
156
157
  if (isString(selector)) {
157
- const elements = document.querySelectorAll(selector);
158
- if (elements.length !== 1) {
158
+
159
+ const element = findElementWithSelectorUpwards( this,selector);
160
+ if (element===null) {
159
161
  throw new Error("the selector must match exactly one element");
160
162
  }
161
-
162
- const element = elements[0];
163
+
163
164
  if (!(element instanceof Datasource)) {
164
165
  throw new TypeError("the element must be a datasource");
165
166
  }
@@ -17,6 +17,7 @@ import {
17
17
  CustomElement,
18
18
  registerCustomElement,
19
19
  } from "../../dom/customelement.mjs";
20
+ import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
20
21
  import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
21
22
  import { Datasource } from "./datasource.mjs";
22
23
  import { SpinnerStyleSheet } from "../stylesheet/spinner.mjs";
@@ -174,12 +175,12 @@ function initEventHandler() {
174
175
  const self = this;
175
176
 
176
177
  if (isString(selector)) {
177
- const elements = document.querySelectorAll(selector);
178
- if (elements.length !== 1) {
178
+
179
+ const element = findElementWithSelectorUpwards( this,selector);
180
+ if (element===null) {
179
181
  throw new Error("the selector must match exactly one element");
180
182
  }
181
-
182
- const element = elements[0];
183
+
183
184
  if (!(element instanceof Datasource)) {
184
185
  throw new TypeError("the element must be a datasource");
185
186
  }
@@ -12,11 +12,12 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import { instanceSymbol } from "../../constants.mjs";
16
- import { isObject } from "../../types/is.mjs";
17
- import { Datasource } from "../datasource.mjs";
15
+ import {instanceSymbol} from "../../constants.mjs";
16
+ import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
17
+ import {isObject} from "../../types/is.mjs";
18
+ import {Datasource} from "../datasource.mjs";
18
19
 
19
- export { DomStorage };
20
+ export {DomStorage};
20
21
 
21
22
  /**
22
23
  * The DomStorage is a class that stores data in memory.
@@ -26,93 +27,93 @@ export { DomStorage };
26
27
  * @memberOf Monster.Data.Datasource
27
28
  */
28
29
  class DomStorage extends Datasource {
29
- /**
30
- * @param {Object} [options] options contains definitions for the datasource.
31
- */
32
- constructor(options) {
33
- super();
30
+ /**
31
+ * @param {Object} [options] options contains definitions for the datasource.
32
+ */
33
+ constructor(options) {
34
+ super();
34
35
 
35
- if (isObject(options)) {
36
- this.setOptions(options);
37
- }
38
- }
36
+ if (isObject(options)) {
37
+ this.setOptions(options);
38
+ }
39
+ }
39
40
 
40
- /**
41
- * This method is called by the `instanceof` operator.
42
- * @returns {symbol}
43
- */
44
- static get [instanceSymbol]() {
45
- return Symbol.for("@schukai/monster/data/datasource/storage/dom-storage");
46
- }
41
+ /**
42
+ * This method is called by the `instanceof` operator.
43
+ * @returns {symbol}
44
+ */
45
+ static get [instanceSymbol]() {
46
+ return Symbol.for("@schukai/monster/data/datasource/storage/dom-storage");
47
+ }
47
48
 
48
- /**
49
- * @property {Object} defaults
50
- * @property {Object} defaults.read
51
- * @property {string} defaults.read.selector
52
- * @property {Object} defaults.write
53
- * @property {string} defaults.write.selector
54
- */
55
- get defaults() {
56
- return Object.assign({}, super.defaults, {
57
- read: {
58
- selector: undefined,
59
- },
60
- write: {
61
- selector: undefined,
62
- },
63
- });
64
- }
49
+ /**
50
+ * @property {Object} defaults
51
+ * @property {Object} defaults.read
52
+ * @property {string} defaults.read.selector
53
+ * @property {Object} defaults.write
54
+ * @property {string} defaults.write.selector
55
+ */
56
+ get defaults() {
57
+ return Object.assign({}, super.defaults, {
58
+ read: {
59
+ selector: undefined,
60
+ },
61
+ write: {
62
+ selector: undefined,
63
+ },
64
+ });
65
+ }
65
66
 
66
- /**
67
- * @return {Promise}
68
- * @throws {Error} The read selector is not defined
69
- * @throws {Error} There are no storage element
70
- */
71
- read() {
72
- const selector = this.getOption("read.selector", undefined);
73
- if (!selector) {
74
- throw new Error("The read selector is not defined");
75
- }
67
+ /**
68
+ * @return {Promise}
69
+ * @throws {Error} The read selector is not defined
70
+ * @throws {Error} There are no storage element
71
+ */
72
+ read() {
73
+ const selector = this.getOption("read.selector", undefined);
74
+ if (!selector) {
75
+ throw new Error("The read selector is not defined");
76
+ }
76
77
 
77
- const storage = document.querySelector(selector);
78
- if (!storage) {
79
- throw new Error("There are no storage element");
80
- }
78
+ const storage = findElementWithSelectorUpwards(this, selector);
79
+ if (!storage) {
80
+ throw new Error("There is no storage element");
81
+ }
81
82
 
82
- return new Promise((resolve, reject) => {
83
- try {
84
- const data = JSON.parse(storage.innerHTML);
85
- this.set(data);
86
- resolve(data);
87
- } catch (e) {
88
- reject(e);
89
- }
90
- });
91
- }
83
+ return new Promise((resolve, reject) => {
84
+ try {
85
+ const data = JSON.parse(storage.innerHTML);
86
+ this.set(data);
87
+ resolve(data);
88
+ } catch (e) {
89
+ reject(e);
90
+ }
91
+ });
92
+ }
92
93
 
93
- /**
94
- * @return {Promise}
95
- * @throws {Error} The write selector is not defined
96
- * @throws {Error} There are no storage element
97
- */
98
- write() {
99
- const selector = this.getOption("write.selector");
100
- if (!selector) {
101
- throw new Error("The write selector is not defined");
102
- }
94
+ /**
95
+ * @return {Promise}
96
+ * @throws {Error} The write selector is not defined
97
+ * @throws {Error} There are no storage element
98
+ */
99
+ write() {
100
+ const selector = this.getOption("write.selector");
101
+ if (!selector) {
102
+ throw new Error("The option write.selector is not defined");
103
+ }
103
104
 
104
- const storage = document.querySelector(selector);
105
- if (!storage) {
106
- throw new Error("There are no storage element");
107
- }
105
+ const storage = findElementWithSelectorUpwards(this, selector);
106
+ if (!storage) {
107
+ throw new Error("There is no storage element");
108
+ }
108
109
 
109
- return new Promise((resolve, reject) => {
110
- try {
111
- storage.innerHTML = JSON.stringify(this.get());
112
- resolve(storage);
113
- } catch (e) {
114
- reject(e);
115
- }
116
- });
117
- }
110
+ return new Promise((resolve, reject) => {
111
+ try {
112
+ storage.innerHTML = JSON.stringify(this.get());
113
+ resolve(storage);
114
+ } catch (e) {
115
+ reject(e);
116
+ }
117
+ });
118
+ }
118
119
  }
@@ -12,26 +12,28 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import { internalStateSymbol } from "../../constants.mjs";
16
- import { extend } from "../../data/extend.mjs";
17
- import { getGlobalFunction } from "../../types/global.mjs";
15
+ import {internalStateSymbol} from "../../constants.mjs";
16
+ import {extend} from "../../data/extend.mjs";
17
+ import {getGlobalFunction} from "../../types/global.mjs";
18
+ import {addAttributeToken} from "../attributes.mjs";
18
19
  import {
19
- ATTRIBUTE_CLASS,
20
- ATTRIBUTE_ERRORMESSAGE,
21
- ATTRIBUTE_ID,
22
- ATTRIBUTE_SRC,
23
- ATTRIBUTE_TITLE,
24
- ATTRIBUTE_TYPE,
25
- TAG_SCRIPT,
20
+ ATTRIBUTE_CLASS,
21
+ ATTRIBUTE_ERRORMESSAGE,
22
+ ATTRIBUTE_ID,
23
+ ATTRIBUTE_SRC,
24
+ ATTRIBUTE_TITLE,
25
+ ATTRIBUTE_TYPE,
26
+ TAG_SCRIPT,
26
27
  } from "../constants.mjs";
27
28
  import {
28
- KEY_DOCUMENT,
29
- KEY_QUERY,
30
- referenceSymbol,
31
- Resource,
29
+ KEY_DOCUMENT,
30
+ KEY_QUERY,
31
+ referenceSymbol,
32
+ Resource,
32
33
  } from "../resource.mjs";
33
- import { instanceSymbol } from "../../constants.mjs";
34
- export { Data };
34
+ import {instanceSymbol} from "../../constants.mjs";
35
+
36
+ export {Data};
35
37
 
36
38
  /**
37
39
  * This class is used by the resource manager to embed data.
@@ -43,58 +45,59 @@ export { Data };
43
45
  * @summary A Data Resource class
44
46
  */
45
47
  class Data extends Resource {
46
- /**
47
- * @property {string} mode=cors https://developer.mozilla.org/en-US/docs/Web/API/fetch
48
- * @property {string} credentials=same-origin https://developer.mozilla.org/en-US/docs/Web/API/fetch
49
- * @property {string} type=application/json {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type}
50
- */
51
- get defaults() {
52
- return extend({}, super.defaults, {
53
- mode: "cors",
54
- credentials: "same-origin",
55
- type: "application/json",
56
- });
57
- }
58
-
59
- /**
60
- *
61
- * @return {Monster.DOM.Resource.Data}
62
- */
63
- create() {
64
- createElement.call(this);
65
- return this;
66
- }
67
-
68
- /**
69
- * This method appends the HTMLElement to the specified document
70
- *
71
- * throws {Error} target not found
72
- * @return {Monster.DOM.Resource}
73
- */
74
- connect() {
75
- if (!(this[referenceSymbol] instanceof HTMLElement)) {
76
- this.create();
77
- }
78
-
79
- appendToDocument.call(this);
80
- return this;
81
- }
82
-
83
- /**
84
- * This method is called by the `instanceof` operator.
85
- * @returns {symbol}
86
- * @since 2.1.0
87
- */
88
- static get [instanceSymbol]() {
89
- return Symbol.for("@schukai/monster/dom/resource/data");
90
- }
91
-
92
- /**
93
- * @return {string}
94
- */
95
- static getURLAttribute() {
96
- return ATTRIBUTE_SRC;
97
- }
48
+ /**
49
+ * @property {string} mode=cors https://developer.mozilla.org/en-US/docs/Web/API/fetch
50
+ * @property {string} credentials=same-origin https://developer.mozilla.org/en-US/docs/Web/API/fetch
51
+ * @property {string} type=application/json {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type}
52
+ */
53
+ get defaults() {
54
+ return extend({}, super.defaults, {
55
+ mode: "cors",
56
+ credentials: "same-origin",
57
+ type: "application/json",
58
+ });
59
+ }
60
+
61
+ /**
62
+ *
63
+ * @return {Monster.DOM.Resource.Data}
64
+ */
65
+ create() {
66
+ createElement.call(this);
67
+ return this;
68
+ }
69
+
70
+ /**
71
+ * This method appends the HTMLElement to the specified document
72
+ *
73
+ * throws {Error} target not found
74
+ * @return {Monster.DOM.Resource}
75
+ */
76
+ connect() {
77
+ const self = this;
78
+ if (!(this[referenceSymbol] instanceof HTMLElement)) {
79
+ this.create();
80
+ }
81
+
82
+ appendToDocument.call(this);
83
+ return this;
84
+ }
85
+
86
+ /**
87
+ * This method is called by the `instanceof` operator.
88
+ * @returns {symbol}
89
+ * @since 2.1.0
90
+ */
91
+ static get [instanceSymbol]() {
92
+ return Symbol.for("@schukai/monster/dom/resource/data");
93
+ }
94
+
95
+ /**
96
+ * @return {string}
97
+ */
98
+ static getURLAttribute() {
99
+ return ATTRIBUTE_SRC;
100
+ }
98
101
  }
99
102
 
100
103
  /**
@@ -102,21 +105,21 @@ class Data extends Resource {
102
105
  * @return {Monster.DOM.Resource.Data}
103
106
  */
104
107
  function createElement() {
105
- const document = this.getOption(KEY_DOCUMENT);
106
- this[referenceSymbol] = document.createElement(TAG_SCRIPT);
107
-
108
- for (const key of [
109
- ATTRIBUTE_TYPE,
110
- ATTRIBUTE_ID,
111
- ATTRIBUTE_CLASS,
112
- ATTRIBUTE_TITLE,
113
- ]) {
114
- if (this.getOption(key) !== undefined) {
115
- this[referenceSymbol][key] = this.getOption(key);
116
- }
117
- }
118
-
119
- return this;
108
+ const document = this.getOption(KEY_DOCUMENT);
109
+ this[referenceSymbol] = document.createElement(TAG_SCRIPT);
110
+
111
+ for (const key of [
112
+ ATTRIBUTE_TYPE,
113
+ ATTRIBUTE_ID,
114
+ ATTRIBUTE_CLASS,
115
+ ATTRIBUTE_TITLE,
116
+ ]) {
117
+ if (this.getOption(key) !== undefined) {
118
+ this[referenceSymbol][key] = this.getOption(key);
119
+ }
120
+ }
121
+
122
+ return this;
120
123
  }
121
124
 
122
125
  /**
@@ -125,41 +128,41 @@ function createElement() {
125
128
  * throws {Error} target not found
126
129
  */
127
130
  function appendToDocument() {
128
- const targetNode = document.querySelector(this.getOption(KEY_QUERY, "head"));
129
- if (!(targetNode instanceof HTMLElement)) {
130
- throw new Error("target not found");
131
- }
132
-
133
- targetNode.appendChild(this[referenceSymbol]);
134
-
135
- getGlobalFunction("fetch")(this.getOption(ATTRIBUTE_SRC), {
136
- method: "GET", // *GET, POST, PUT, DELETE, etc.
137
- mode: this.getOption("mode", "cors"), // no-cors, *cors, same-origin
138
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
139
- credentials: this.getOption("credentials", "same-origin"), // include, *same-origin, omit
140
- headers: {
141
- Accept: this.getOption("type", "application/json"),
142
- },
143
- redirect: "follow", // manual, *follow, error
144
- referrerPolicy: "no-referrer", // no-referrer,
145
- })
146
- .then((response) => {
147
- return response.text();
148
- })
149
- .then((text) => {
150
- const textNode = document.createTextNode(text);
151
- this[referenceSymbol].appendChild(textNode);
152
-
153
- this[internalStateSymbol].getSubject()["loaded"] = true;
154
- })
155
- .catch((e) => {
156
- this[internalStateSymbol].setSubject({
157
- loaded: true,
158
- error: e.toString(),
159
- });
160
-
161
- targetNode.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString());
162
- });
163
-
164
- return this;
131
+ const targetNode = document.querySelector(this.getOption(KEY_QUERY, "head"));
132
+ if (!(targetNode instanceof HTMLElement)) {
133
+ throw new Error("target not found");
134
+ }
135
+
136
+ targetNode.appendChild(this[referenceSymbol]);
137
+
138
+ getGlobalFunction("fetch")(this.getOption(ATTRIBUTE_SRC), {
139
+ method: "GET", // *GET, POST, PUT, DELETE, etc.
140
+ mode: this.getOption("mode", "cors"), // no-cors, *cors, same-origin
141
+ cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
142
+ credentials: this.getOption("credentials", "same-origin"), // include, *same-origin, omit
143
+ headers: {
144
+ Accept: this.getOption("type", "application/json"),
145
+ },
146
+ redirect: "follow", // manual, *follow, error
147
+ referrerPolicy: "no-referrer", // no-referrer,
148
+ })
149
+ .then((response) => {
150
+ return response.text();
151
+ })
152
+ .then((text) => {
153
+ const textNode = document.createTextNode(text);
154
+ this[referenceSymbol].appendChild(textNode);
155
+
156
+ this[internalStateSymbol].getSubject()["loaded"] = true;
157
+ })
158
+ .catch((e) => {
159
+ this[internalStateSymbol].setSubject({
160
+ loaded: true,
161
+ error: e.toString(),
162
+ });
163
+
164
+ targetNode.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString());
165
+ });
166
+
167
+ return this;
165
168
  }
@@ -242,7 +242,6 @@ function addEvents() {
242
242
  this[referenceSymbol].removeEventListener("error", onError);
243
243
  this[referenceSymbol].removeEventListener("load", onLoad);
244
244
  this[internalStateSymbol].getSubject()["loaded"] = true;
245
- return;
246
245
  };
247
246
 
248
247
  this[referenceSymbol].addEventListener("load", onLoad, false);