@cocreate/elements 1.41.1 → 1.42.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/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ # [1.42.0](https://github.com/CoCreate-app/CoCreate-elements/compare/v1.41.1...v1.42.0) (2025-04-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * $relativePath handeling ([e9d41df](https://github.com/CoCreate-app/CoCreate-elements/commit/e9d41df24291083acc2bd5fa928c6b8b621e8f7c))
7
+ * handeling onRest ([ba102ce](https://github.com/CoCreate-app/CoCreate-elements/commit/ba102cedec35c41f1a974d0f62de3670ed40105e))
8
+ * update observer obseve param to type and and attributeName to attributeFilter ([3ce1a06](https://github.com/CoCreate-app/CoCreate-elements/commit/3ce1a0615fd16eeacc24b89c722593d7a470a566))
9
+ * update to how action is handled ([25f2654](https://github.com/CoCreate-app/CoCreate-elements/commit/25f26548618397c3cbae0469ff284ca1be5a8a7b))
10
+
11
+
12
+ ### Features
13
+
14
+ * value-selectors to get value from target element ([e3ae15b](https://github.com/CoCreate-app/CoCreate-elements/commit/e3ae15b5e0d2d953399bd45dc82ea44aa7aa6cde))
15
+
1
16
  ## [1.41.1](https://github.com/CoCreate-app/CoCreate-elements/compare/v1.41.0...v1.41.1) (2025-01-18)
2
17
 
3
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/elements",
3
- "version": "1.41.1",
3
+ "version": "1.42.0",
4
4
  "description": "Fetch content into any html element by using html5 attributes to query data stored in CoCreate headless CMS. If data is changed it will instantly reflect in html tags as changes are broadcasted in realtime",
5
5
  "keywords": [
6
6
  "elements",
package/src/fetchSrc.js CHANGED
@@ -22,163 +22,137 @@ async function initElements(elements) {
22
22
  if (!src || /{{\s*([\w\W]+)\s*}}/g.test(src)) continue;
23
23
 
24
24
  let initialize = initializing.get(element);
25
- if (!initialize || initialize.src != src) {
26
- initializing.set(element, { src });
27
- if (src) {
28
- try {
29
- let response = await fetch(src);
30
- if (
31
- element.tagName === "AUDIO" ||
32
- element.tagName === "VIDEO"
33
- ) {
34
- try {
35
- let mediaConfig = await response.json();
36
- let sourceBuffer;
37
-
38
- const mediaSource = new MediaSource();
39
- element.src = URL.createObjectURL(mediaSource);
40
- mediaSource.addEventListener(
41
- "sourceopen",
42
- async () => {
43
- sourceBuffer = mediaSource.addSourceBuffer(
44
- `${mediaConfig["content-type"]}; codecs="${mediaConfig.codecs}"`
45
- );
46
-
47
- sourceBuffer.addEventListener(
48
- "updateend",
49
- () => {
50
- console.log(
51
- "Update ended, buffered ranges:",
52
- sourceBuffer.buffered
53
- );
54
- if (
55
- !sourceBuffer.updating &&
56
- mediaSource.readyState ===
57
- "open"
58
- ) {
59
- mediaSource.endOfStream();
60
-
61
- element.addEventListener(
62
- "ended",
63
- () => {
64
- URL.revokeObjectURL(
65
- element.src
66
- );
67
- }
68
- );
69
- }
70
- }
71
- );
72
- sourceBuffer.addEventListener(
73
- "error",
74
- (e) => {
75
- console.error(
76
- "SourceBuffer error event:",
77
- e
78
- );
79
- console.log(
80
- "MediaSource readyState:",
81
- mediaSource.readyState
82
- );
83
- console.log(
84
- "SourceBuffer updating:",
85
- sourceBuffer.updating
86
- );
87
- console.log(
88
- "SourceBuffer buffered ranges:",
89
- sourceBuffer.buffered
90
- );
91
- // Any other state information you can log
92
- }
93
- );
94
-
95
- getSegment(
96
- sourceBuffer,
97
- mediaConfig.segments[0]
98
- );
99
- }
100
- );
101
-
102
- mediaSource.addEventListener("sourceended", () => {
103
- console.log("MediaSource ended event fired");
104
- });
105
-
106
- mediaSource.addEventListener("sourceclose", () => {
107
- console.log("MediaSource close event fired");
108
- });
109
-
110
- // Event listener for seeking
111
- element.addEventListener("seeking", () => {
112
- let currentTime = element.currentTime;
113
- let chunkIndex = Math.floor(
114
- currentTime / chunkDuration
115
- );
116
- });
117
-
118
- // Event listener for seeked
119
- element.addEventListener("seeked", () => {
120
- let currentTime = element.currentTime;
121
- if (isBuffered(element, currentTime)) return;
122
-
123
- let query = {
124
- start: { $lte: currentTime },
125
- end: { $gte: currentTime }
126
- };
127
-
128
- let segments = queryData(
129
- mediaConfig.segments,
130
- query
131
- );
132
- for (let segment of segments) {
133
- getSegment(sourceBuffer, segment);
134
- }
135
- });
136
-
137
- element.addEventListener("timeupdate", function () {
138
- const bufferEnd = element.buffered.end(0);
139
- const currentTime = element.currentTime;
140
- const threshold = 10; // seconds before buffer end to fetch the next segment
141
-
142
- if (bufferEnd - currentTime < threshold) {
143
- // Time to fetch the next segment
144
- getSegment(sourceBuffer, mediaConfig);
145
- }
146
- });
147
- } catch (error) {
148
- element.src = src;
149
- // let blob = await response.json();
150
- // URL.createObjectURL(blob);
151
- }
152
- }
153
- // else if (element.hasAttribute("rendered")) {
154
- // element.removeAttribute("rendered");
155
- // // let path = element.getAttribute("path");
156
- // // if (path) {
157
- // // let elements = element.querySelectorAll("[src]");
158
- // // for (let i = 0; i < elements.length; i++) {
159
- // // text = text.replaceAll("{{path}}", path);
160
- // // }
161
- // // }
162
- // }
163
- else {
164
- element.removeAttribute("rendered");
165
-
166
- let text = await response.text();
167
- if (text) {
168
- let path = element.getAttribute("path");
169
- if (path) text = text.replaceAll("{{path}}", path);
170
- element.setValue(text);
171
- initializing.delete(element);
172
- }
25
+ if (initialize && initialize.src === src) continue;
26
+
27
+ initializing.set(element, { src });
28
+ try {
29
+ let state = localStorage.getItem("state"); // Assume this is your state object
30
+ let response = await fetch(src, {
31
+ headers: {
32
+ "X-State": state // Custom header to send state
33
+ }
34
+ });
35
+
36
+ if (element.tagName === "AUDIO" || element.tagName === "VIDEO") {
37
+ initMediaSource(element, src);
38
+ } else {
39
+ element.removeAttribute("rendered");
40
+
41
+ let text = await response.text();
42
+ if (text) {
43
+ let pathElement = element.closest("[path]");
44
+ if (pathElement) {
45
+ let path =
46
+ pathElement.getAttribute("path") || getPath();
47
+ if (path) text = text.replaceAll("$relativePath", path);
173
48
  }
174
- } catch (err) {
175
- console.log("FetchSrc error:" + err);
49
+ element.setValue(text);
50
+ initializing.delete(element);
176
51
  }
177
52
  }
53
+ } catch (err) {
54
+ console.log("FetchSrc error:" + err);
178
55
  }
179
56
  }
180
57
  }
181
58
 
59
+ function getPath() {
60
+ let currentPath = window.location.pathname.replace(/\/[^\/]*$/, ""); // Remove file from path
61
+ let depth = currentPath.split("/").filter(Boolean).length; // Count depth levels
62
+
63
+ return depth > 0 ? "../".repeat(depth) : "./"; // Return the correct relative path
64
+ }
65
+
66
+ async function initMediaSource(element, src) {
67
+ try {
68
+ let mediaConfig = await response.json();
69
+ let sourceBuffer;
70
+
71
+ const mediaSource = new MediaSource();
72
+ element.src = URL.createObjectURL(mediaSource);
73
+ mediaSource.addEventListener("sourceopen", async () => {
74
+ sourceBuffer = mediaSource.addSourceBuffer(
75
+ `${mediaConfig["content-type"]}; codecs="${mediaConfig.codecs}"`
76
+ );
77
+
78
+ sourceBuffer.addEventListener("updateend", () => {
79
+ console.log(
80
+ "Update ended, buffered ranges:",
81
+ sourceBuffer.buffered
82
+ );
83
+ if (
84
+ !sourceBuffer.updating &&
85
+ mediaSource.readyState === "open"
86
+ ) {
87
+ mediaSource.endOfStream();
88
+
89
+ element.addEventListener("ended", () => {
90
+ URL.revokeObjectURL(element.src);
91
+ });
92
+ }
93
+ });
94
+
95
+ sourceBuffer.addEventListener("error", (e) => {
96
+ console.error("SourceBuffer error event:", e);
97
+ console.log("MediaSource readyState:", mediaSource.readyState);
98
+ console.log("SourceBuffer updating:", sourceBuffer.updating);
99
+ console.log(
100
+ "SourceBuffer buffered ranges:",
101
+ sourceBuffer.buffered
102
+ );
103
+ // Any other state information you can log
104
+ });
105
+
106
+ getSegment(sourceBuffer, mediaConfig.segments[0]);
107
+ });
108
+
109
+ mediaSource.addEventListener("sourceended", () => {
110
+ console.log("MediaSource ended event fired");
111
+ });
112
+
113
+ mediaSource.addEventListener("sourceclose", () => {
114
+ console.log("MediaSource close event fired");
115
+ });
116
+
117
+ // Event listener for seeking
118
+ element.addEventListener("seeking", () => {
119
+ let currentTime = element.currentTime;
120
+ let chunkIndex = Math.floor(currentTime / chunkDuration);
121
+ });
122
+
123
+ // Event listener for seeked
124
+ element.addEventListener("seeked", () => {
125
+ let currentTime = element.currentTime;
126
+ if (isBuffered(element, currentTime)) return;
127
+
128
+ let query = {
129
+ start: { $lte: currentTime },
130
+ end: { $gte: currentTime }
131
+ };
132
+
133
+ let segments = queryData(mediaConfig.segments, query);
134
+ for (let segment of segments) {
135
+ getSegment(sourceBuffer, segment);
136
+ }
137
+ });
138
+
139
+ element.addEventListener("timeupdate", function () {
140
+ const bufferEnd = element.buffered.end(0);
141
+ const currentTime = element.currentTime;
142
+ const threshold = 10; // seconds before buffer end to fetch the next segment
143
+
144
+ if (bufferEnd - currentTime < threshold) {
145
+ // Time to fetch the next segment
146
+ getSegment(sourceBuffer, mediaConfig);
147
+ }
148
+ });
149
+ } catch (error) {
150
+ element.src = src;
151
+ // let blob = await response.json();
152
+ // URL.createObjectURL(blob);
153
+ }
154
+ }
155
+
182
156
  async function getSegment(sourceBuffer, segment) {
183
157
  // TODO: use socket/crud/file with room using the url to get one or more segments and append
184
158
  let segments = [];
@@ -266,17 +240,30 @@ function base64ToArrayBuffer(base64) {
266
240
 
267
241
  observer.init({
268
242
  name: "CoCreateSrc",
269
- observe: ["addedNodes"],
243
+ types: ["addedNodes"],
270
244
  selector: selector,
271
245
  callback: function (mutation) {
272
246
  init(mutation.target);
273
247
  }
274
248
  });
275
249
 
250
+ observer.init({
251
+ name: "CoCreateSrcOperator",
252
+ types: ["addedNodes"],
253
+ selector: "img",
254
+ // selector: "img, video, audio, script, input, iframe, frame, link, source",
255
+ callback: function (mutation) {
256
+ let src = mutation.target.getAttribute("src");
257
+ if (src) {
258
+ mutation.target.setAttribute("src", src);
259
+ }
260
+ }
261
+ });
262
+
276
263
  observer.init({
277
264
  name: "CoCreateSrcAttributes",
278
- observe: ["attributes"],
279
- attributeName: ["src"],
265
+ types: ["attributes"],
266
+ attributeFilter: ["src"],
280
267
  selector: selector,
281
268
  callback: function (mutation) {
282
269
  if (
package/src/form.js CHANGED
@@ -21,11 +21,18 @@ function init(elements) {
21
21
  setAttribute(element);
22
22
  disableAutoFill(element);
23
23
  setValue(element);
24
+
24
25
  element.addEventListener("submit", function (event) {
25
26
  if (!element.hasAttribute("action")) {
26
27
  event.preventDefault();
27
28
  }
28
29
  });
30
+
31
+ // Handle form reset event
32
+ element.addEventListener("reset", function (event) {
33
+ event.preventDefault(); // Prevent default reset
34
+ reset({ form: event.target }); // Call custom reset logic
35
+ });
29
36
  }
30
37
  }
31
38
 
@@ -93,7 +100,9 @@ function disableAutoFill(element) {
93
100
  /**
94
101
  * @param form
95
102
  */
96
- function reset(form) {
103
+ function reset(action) {
104
+ let form = action.form;
105
+
97
106
  // Convert the form elements collection to an array
98
107
  const formElementsArray = Array.from(form.elements);
99
108
 
@@ -127,7 +136,13 @@ function reset(form) {
127
136
  element.value ||
128
137
  element.getAttribute("value")
129
138
  );
130
- if (!resetType || resetType === "value") element.setValue("");
139
+ if (!resetType || resetType === "value") {
140
+ if (element.contentEditable === "true") {
141
+ element.innerHTML = "";
142
+ } else {
143
+ element.setValue("");
144
+ }
145
+ }
131
146
  }
132
147
 
133
148
  if (form.hasAttribute("object")) form.setAttribute("object", "");
@@ -141,11 +156,13 @@ function reset(form) {
141
156
  });
142
157
 
143
158
  // Dispatch a custom reset event
144
- document.dispatchEvent(
145
- new CustomEvent("reset", {
146
- detail: {}
147
- })
148
- );
159
+ if (action.element) {
160
+ action.element.dispatchEvent(
161
+ new CustomEvent("reset", {
162
+ detail: {}
163
+ })
164
+ );
165
+ }
149
166
  }
150
167
 
151
168
  function setValue(form) {
@@ -166,15 +183,15 @@ function setValue(form) {
166
183
 
167
184
  Observer.init({
168
185
  name: "CoCreateForm",
169
- observe: ["addedNodes"],
186
+ types: ["addedNodes"],
170
187
  selector: "form",
171
188
  callback: (mutation) => init(mutation.target)
172
189
  });
173
190
 
174
191
  Observer.init({
175
192
  name: "CoCreateForm",
176
- observe: ["attributes"],
177
- attributeName: getAttributeNames([
193
+ types: ["attributes"],
194
+ attributeFilter: getAttributeNames([
178
195
  "organization_id",
179
196
  "host",
180
197
  "storage",
@@ -190,7 +207,7 @@ Observer.init({
190
207
 
191
208
  Observer.init({
192
209
  name: "CoCreateFormElements",
193
- observe: ["addedNodes"],
210
+ types: ["addedNodes"],
194
211
  selector:
195
212
  "[organization_id], [host], [storage], [database], [array], [index], [object], [key]",
196
213
  callback: function (mutation) {
@@ -203,7 +220,7 @@ Action.init({
203
220
  name: "reset",
204
221
  endEvent: "reset",
205
222
  callback: (action) => {
206
- if (action.form) reset(action.form);
223
+ if (action.form) reset(action);
207
224
  }
208
225
  });
209
226
 
package/src/index.js CHANGED
@@ -40,6 +40,7 @@ import { render, renderValue, renderedNodes } from "@cocreate/render";
40
40
  import "@cocreate/element-prototype";
41
41
  import "./fetchSrc";
42
42
  import { reset } from "./form";
43
+ import "./value.js";
43
44
 
44
45
  const selector = "[storage], [database], [array], [render-json]";
45
46
  const elements = new Map();
@@ -462,7 +463,12 @@ async function filterData(element, data, type, key) {
462
463
  }
463
464
 
464
465
  let property = key;
465
- if (operator) property = property.replace(operator + ".", "");
466
+ if (operator) {
467
+ property = property.replace(operator + ".", "");
468
+ if (operator === "$sum") {
469
+ value = 0;
470
+ }
471
+ }
466
472
  if (Array.isArray(data[type])) {
467
473
  let Data = [],
468
474
  isObject;
@@ -482,7 +488,7 @@ async function filterData(element, data, type, key) {
482
488
  if (docValue) {
483
489
  if (operator === "$sum" && typeof docValue === "number") {
484
490
  if (typeof value !== "number") value = 0;
485
- value += docValue;
491
+ value += docValue || 0;
486
492
  } else if (Array.isArray(docValue)) {
487
493
  Data.push(...docValue);
488
494
  } else {
@@ -1010,7 +1016,7 @@ async function getData(form) {
1010
1016
  return dataArray;
1011
1017
  }
1012
1018
 
1013
- async function save(element) {
1019
+ async function save(element, action) {
1014
1020
  if (!element) return;
1015
1021
  let data, value;
1016
1022
  let upsert = element.getAttribute("upsert");
@@ -1116,19 +1122,28 @@ async function save(element) {
1116
1122
  (data[i].type !== "object" &&
1117
1123
  data[i].method.endsWith(".update")))
1118
1124
  ) {
1119
- setTypeValue(element, data[i]);
1120
- } else if (data[i])
1121
- document.dispatchEvent(
1122
- new CustomEvent("saved", {
1123
- detail: data[i]
1124
- })
1125
- );
1125
+ setTypeValue(element, data[i], action);
1126
+ } else if (data[i]) {
1127
+ if (action) {
1128
+ action.element.dispatchEvent(
1129
+ new CustomEvent("saved", {
1130
+ detail: data
1131
+ })
1132
+ );
1133
+ } else {
1134
+ document.dispatchEvent(
1135
+ new CustomEvent("saved", {
1136
+ detail: data
1137
+ })
1138
+ );
1139
+ }
1140
+ }
1126
1141
  }
1127
1142
 
1128
1143
  return Data;
1129
1144
  }
1130
1145
 
1131
- function setTypeValue(element, data) {
1146
+ function setTypeValue(element, data, action) {
1132
1147
  // TODO: if an array name is updated, the attibute array="" needs to be updated.
1133
1148
 
1134
1149
  if (!data) return;
@@ -1164,7 +1179,7 @@ function setTypeValue(element, data) {
1164
1179
  }
1165
1180
  setData(Array.from(elements.keys()), data);
1166
1181
 
1167
- // const state_ids = new Map();
1182
+ // const state_ids = new Map()
1168
1183
 
1169
1184
  // let state_id = form.getAttribute('state_id');
1170
1185
  // if (state_id) {
@@ -1217,11 +1232,19 @@ function setTypeValue(element, data) {
1217
1232
  // }
1218
1233
  }
1219
1234
 
1220
- document.dispatchEvent(
1221
- new CustomEvent("saved", {
1222
- detail: data
1223
- })
1224
- );
1235
+ if (action) {
1236
+ action.element.dispatchEvent(
1237
+ new CustomEvent("saved", {
1238
+ detail: data
1239
+ })
1240
+ );
1241
+ } else {
1242
+ document.dispatchEvent(
1243
+ new CustomEvent("saved", {
1244
+ detail: data
1245
+ })
1246
+ );
1247
+ }
1225
1248
  }
1226
1249
 
1227
1250
  async function remove(element) {
@@ -1300,7 +1323,7 @@ function initSocket() {
1300
1323
 
1301
1324
  Observer.init({
1302
1325
  name: "render",
1303
- observe: ["addedNodes"],
1326
+ types: ["addedNodes"],
1304
1327
  selector: "[render-clone]",
1305
1328
  callback: function (mutation) {
1306
1329
  if (!mutation.parentElement.hasAttribute("dnd")) return;
@@ -1449,16 +1472,16 @@ function dndCrudSend(data, crudType) {
1449
1472
 
1450
1473
  Observer.init({
1451
1474
  name: "CoCreateElementsChildList",
1452
- observe: ["childList"],
1475
+ types: ["addedNodes"],
1453
1476
  selector: selector,
1454
1477
  callback: function (mutation) {
1455
- init(mutation.addedNodes);
1478
+ init([mutation.target]);
1456
1479
  }
1457
1480
  });
1458
1481
 
1459
1482
  Observer.init({
1460
1483
  name: "CoCreateElementsRemovedNodes",
1461
- observe: ["removedNodes"],
1484
+ types: ["removedNodes"],
1462
1485
  selector: selector,
1463
1486
  callback: function (mutation) {
1464
1487
  // if (mutation.target.parentElement) return
@@ -1476,8 +1499,8 @@ Observer.init({
1476
1499
 
1477
1500
  Observer.init({
1478
1501
  name: "CoCreateElementsAttributes",
1479
- observe: ["attributes"],
1480
- attributeName: [
1502
+ types: ["attributes"],
1503
+ attributeFilter: [
1481
1504
  "organization_id",
1482
1505
  "host",
1483
1506
  "storage",
@@ -1503,7 +1526,7 @@ Actions.init([
1503
1526
  name: "save",
1504
1527
  endEvent: "saved",
1505
1528
  callback: (action) => {
1506
- if (action.form) save(action.form);
1529
+ if ((action.form, action)) save(action.form, action);
1507
1530
  }
1508
1531
  },
1509
1532
  {
@@ -1546,7 +1569,7 @@ Actions.init([
1546
1569
  data[data.type] = { _id: data[data.type] };
1547
1570
 
1548
1571
  let response = await CRUD.send(data);
1549
- document.dispatchEvent(
1572
+ action.element.dispatchEvent(
1550
1573
  new CustomEvent("deleted", {
1551
1574
  detail: response
1552
1575
  })
package/src/value.js ADDED
@@ -0,0 +1,115 @@
1
+ import Observer from "@cocreate/observer";
2
+ import "@cocreate/element-prototype";
3
+
4
+ const selector =
5
+ "[value-selector], [value-closest], [value-parent], [value-next], [value-previous], [value-document], [value-frame], [value-top]";
6
+
7
+ /**
8
+ * Initializes elements with value-* attributes to enable dynamic value retrieval and setting.
9
+ *
10
+ * @param {HTMLElement|HTMLCollection|NodeList|Array<HTMLElement>} [element] - Optional element(s) to initialize. If not provided, initializes all matching elements in the document.
11
+ */
12
+ async function init(element) {
13
+ if (
14
+ element &&
15
+ !(element instanceof HTMLCollection) &&
16
+ !(element instanceof NodeList) &&
17
+ !Array.isArray(element)
18
+ ) {
19
+ element = [element];
20
+ } else if (!element) {
21
+ element = document.querySelectorAll(selector);
22
+ }
23
+
24
+ for (let i = 0; i < element.length; i++) {
25
+ initElement(element[i]);
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Initializes a single element with value-* attributes.
31
+ *
32
+ * @param {HTMLElement} element - The element to initialize.
33
+ */
34
+ function initElement(element) {
35
+ let targets = element.queryElements();
36
+
37
+ valueHandler(element, targets, true);
38
+ }
39
+
40
+ /**
41
+ * Handles the retrieval and setting of values based on value-* attributes.
42
+ *
43
+ * @param {HTMLElement} element - The element to set the value on.
44
+ * @param {object} targets - An object containing elements retrieved by queryElements().
45
+ */
46
+ function valueHandler(element, targets = [], initialize) {
47
+ let values = [];
48
+ // TODO: consdier the potential of targets being an array of elements, should value be an array of the values?
49
+ for (let i = 0; i < targets.length; i++) {
50
+ if (!targets[i] || !targets[i].isConnected) {
51
+ targets.splice(i, 1); // Remove the element
52
+ i -= 1; // Adjust the index
53
+ continue; // Skip to the next iteration
54
+ }
55
+
56
+ if (initialize) {
57
+ // Remove existing listener (if any)
58
+ targets[i].removeEventListener("input", elementValueHandler);
59
+
60
+ // Add new listener
61
+ targets[i].addEventListener("input", elementValueHandler);
62
+ }
63
+
64
+ values.push(targets[i].getValue());
65
+ }
66
+
67
+ if (!values.length) {
68
+ return;
69
+ } else if (values.length === 1) {
70
+ values = values[0];
71
+ }
72
+
73
+ let attribute = element.getAttribute("value-attribute");
74
+ if (attribute) {
75
+ element.setAttribute(attribute, values);
76
+ } else {
77
+ element.setValue(values);
78
+ }
79
+
80
+ function elementValueHandler(e) {
81
+ valueHandler(element, targets);
82
+ }
83
+ }
84
+
85
+ init();
86
+
87
+ Observer.init({
88
+ name: "CoCreateElementsValueAdded",
89
+ types: ["addedNodes"],
90
+ selector: selector,
91
+ callback: function (mutation) {
92
+ init(mutation.target);
93
+ }
94
+ });
95
+
96
+ Observer.init({
97
+ name: "CoCreateElementsAttributes",
98
+ types: ["attributes"],
99
+ attributeFilter: [
100
+ "value-selector",
101
+ "value-closest",
102
+ "value-parent",
103
+ "value-next",
104
+ "value-previous",
105
+ "value-document",
106
+ "value-frame",
107
+ "value-top"
108
+ ],
109
+ callback: function (mutation) {
110
+ let currentValue = mutation.target.getAttribute(mutation.attributeName);
111
+ if (currentValue !== mutation.oldValue) {
112
+ initElement(mutation.target);
113
+ }
114
+ }
115
+ });