@cocreate/element-prototype 1.28.1 → 1.29.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,24 @@
1
+ # [1.29.0](https://github.com/CoCreate-app/CoCreate-element-prototype/compare/v1.28.1...v1.29.0) (2025-04-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * default to string if value is falsy and not 0 ([b8eadb1](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/b8eadb19405393eb1121e257571d3cff45db6263))
7
+ * format dat and time to correct format to set on inputs ([35dd407](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/35dd4071c89741b468ac8e9a3927abf06cef5838))
8
+ * if value-type condition before switch case ([066da57](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/066da57a48fb8c0758254525a9d273977d8dba0a))
9
+ * setAttribute value ([afcc695](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/afcc695ba678cc2c8a525ea5093a5421407eebf5))
10
+ * update exports ([19eb2ae](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/19eb2aeab014f367d67009e6e9ae38f50f2b0104))
11
+ * utility import ([1f3337a](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/1f3337a6422e0615a3e3d38532f71d489b0d9690))
12
+
13
+
14
+ ### Features
15
+
16
+ * $scrollWidth , $relativPath and $path operators ([08844ba](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/08844ba13c82f9542cf70b68a42a77d3e259f999))
17
+ * add [@cocreate](https://github.com/cocreate) utils ([d82ee9b](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/d82ee9b685e98eec292e96dfc247b87847fde56e))
18
+ * added queryElements() to element prototype. wiil automatically use attibutes if found on element as well as options could be passed ([70114a1](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/70114a1dde8dc8263b1fd4f86907a118aa596167))
19
+ * improved operators to target element poperties and methods ([2734187](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/2734187b6640119f8e00dc374e056acd502f8d71))
20
+ * utility for operator handling ([d76a0f6](https://github.com/CoCreate-app/CoCreate-element-prototype/commit/d76a0f6640ce5069823f7b8a71272d3738a44e17))
21
+
1
22
  ## [1.28.1](https://github.com/CoCreate-app/CoCreate-element-prototype/compare/v1.28.0...v1.28.1) (2025-01-18)
2
23
 
3
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/element-prototype",
3
- "version": "1.28.1",
3
+ "version": "1.29.0",
4
4
  "description": "A simple element-prototype component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "element-prototype",
@@ -57,5 +57,7 @@
57
57
  "webpack-cli": "^4.5.0",
58
58
  "webpack-log": "^3.0.1"
59
59
  },
60
- "dependencies": {}
60
+ "dependencies": {
61
+ "@cocreate/utils": "1.37.3"
62
+ }
61
63
  }
@@ -1,4 +1,4 @@
1
- import utility from "./utility";
1
+ import { processOperators } from "./utility";
2
2
 
3
3
  // Store a reference to the original getAttribute function
4
4
  const originalGetAttribute = Element.prototype.getAttribute;
@@ -22,34 +22,5 @@ function updateAttributes(e) {
22
22
  // Override the getAttribute function
23
23
  Element.prototype.getAttribute = function (name) {
24
24
  let value = originalGetAttribute.call(this, name);
25
-
26
- const localKeys = {
27
- $organization_id: "organization_id",
28
- $user_id: "user_id",
29
- $clientId: "clientId",
30
- $session_id: "session_id"
31
- };
32
-
33
- if (localKeys.hasOwnProperty(value)) {
34
- let newValue = localStorage.getItem(localKeys[value]);
35
-
36
- if (!attributes.has(localKeys[value])) {
37
- attributes.set(localKeys[value], []);
38
- }
39
-
40
- attributes.get(localKeys[value]).push({
41
- element: this,
42
- name,
43
- value: newValue
44
- });
45
- value = newValue;
46
- } else if (value === "$innerWidth") {
47
- value = window.innerWidth;
48
- } else if (value === "$innerHeight") {
49
- value = window.innerHeight;
50
- } else if (typeof value === "string" && value.includes("$")) {
51
- value = utility.urlOperators(value);
52
- }
53
-
54
- return value;
25
+ return processOperators(this, value);
55
26
  };
package/src/getValue.js CHANGED
@@ -1,4 +1,4 @@
1
- import utility from "./utility";
1
+ import { processOperators } from "./utility";
2
2
 
3
3
  const storage = new Map();
4
4
 
@@ -95,51 +95,53 @@ const getValue = (element) => {
95
95
  // value = value.substring(11, 8) + 'Z';
96
96
  value = value.substring(11, 19) + "Z";
97
97
 
98
- switch (valueType) {
99
- case "getDayName":
100
- const days = [
101
- "Sunday",
102
- "Monday",
103
- "Tuesday",
104
- "Wednesday",
105
- "Thursday",
106
- "Friday",
107
- "Saturday"
108
- ];
109
- value = days[value.getDay()];
110
- break;
111
- case "getMonthName":
112
- const months = [
113
- "January",
114
- "February",
115
- "March",
116
- "April",
117
- "May",
118
- "June",
119
- "July",
120
- "August",
121
- "September",
122
- "October",
123
- "November",
124
- "December"
125
- ];
126
- value = months[value.getMonth()];
127
- break;
128
- case "toUnixTimestamp":
129
- value = Math.floor(value.getTime() / 1000);
130
- break;
131
- case "toLocaleString":
132
- let locale = element.getAttribute("locale") || "en-US";
133
- value = value[valueType](locale);
134
- break;
135
- default:
136
- if (typeof value[valueType] === "function") {
137
- value = value[valueType]();
138
- } else {
139
- console.warn(
140
- `The method ${valueType} is not a function of Date object.`
141
- );
142
- }
98
+ if (valueType) {
99
+ switch (valueType) {
100
+ case "getDayName":
101
+ const days = [
102
+ "Sunday",
103
+ "Monday",
104
+ "Tuesday",
105
+ "Wednesday",
106
+ "Thursday",
107
+ "Friday",
108
+ "Saturday"
109
+ ];
110
+ value = days[value.getDay()];
111
+ break;
112
+ case "getMonthName":
113
+ const months = [
114
+ "January",
115
+ "February",
116
+ "March",
117
+ "April",
118
+ "May",
119
+ "June",
120
+ "July",
121
+ "August",
122
+ "September",
123
+ "October",
124
+ "November",
125
+ "December"
126
+ ];
127
+ value = months[value.getMonth()];
128
+ break;
129
+ case "toUnixTimestamp":
130
+ value = Math.floor(value.getTime() / 1000);
131
+ break;
132
+ case "toLocaleString":
133
+ let locale = element.getAttribute("locale") || "en-US";
134
+ value = value[valueType](locale);
135
+ break;
136
+ default:
137
+ if (typeof value[valueType] === "function") {
138
+ value = value[valueType]();
139
+ } else {
140
+ console.warn(
141
+ `The method ${valueType} is not a function of Date object.`
142
+ );
143
+ }
144
+ }
143
145
  }
144
146
  }
145
147
  } else if (element.tagName == "INPUT" || element.tagName == "SELECT") {
@@ -171,6 +173,8 @@ const getValue = (element) => {
171
173
  value = targetElement.innerText;
172
174
  } else if (valueType === "outerHTML") {
173
175
  value = targetElement.outerHTML;
176
+ } else if (valueType === "element" || valueType === "node") {
177
+ value = targetElement.outerHTML;
174
178
  } else {
175
179
  value = targetElement.innerHTML;
176
180
  }
@@ -181,15 +185,7 @@ const getValue = (element) => {
181
185
  else return true;
182
186
  }
183
187
 
184
- if (value === "$organization_id")
185
- value = localStorage.getItem("organization_id");
186
- else if (value === "$user_id") value = localStorage.getItem("user_id");
187
- else if (value === "$clientId") value = localStorage.getItem("clientId");
188
- else if (value === "$session_id")
189
- value = localStorage.getItem("session_id");
190
- else if (typeof value === "string" && value.includes("$")) {
191
- value = utility.urlOperators(value);
192
- }
188
+ value = processOperators(element, value, ["$value"]);
193
189
 
194
190
  try {
195
191
  const attributes = element.attributes; // Get all attributes of the element
package/src/index.js CHANGED
@@ -20,8 +20,16 @@
20
20
  // you must obtain a commercial license from CoCreate LLC.
21
21
  // For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
22
22
 
23
- import { getAttribute } from './getAttribute';
24
- import { setValue } from './setValue';
25
- import { getValue } from './getValue';
23
+ import { getAttribute } from "./getAttribute";
24
+ import { setValue } from "./setValue";
25
+ import { getValue } from "./getValue";
26
+ import { queryElements } from "./queryElements";
27
+ import { processOperators } from "./utility";
26
28
 
27
- export default { getAttribute, getValue, setValue }
29
+ export default {
30
+ getAttribute,
31
+ getValue,
32
+ setValue,
33
+ processOperators,
34
+ queryElements
35
+ };
@@ -0,0 +1,5 @@
1
+ import utils from "@cocreate/utils";
2
+
3
+ Element.prototype.queryElements = function (options = {}) {
4
+ return utils.queryElements({ element: this, ...options });
5
+ };
package/src/setValue.js CHANGED
@@ -19,11 +19,26 @@ const setValue = (el, value, dispatch) => {
19
19
  else if (typeof value === "object") value = JSON.stringify(value, null, 2);
20
20
 
21
21
  if (["time", "datetime", "datetime-local"].includes(el.type)) {
22
- if (el.value) {
23
- value = new Date(el.value).toLocalString();
22
+ if (value) {
23
+ const date = new Date(value);
24
+ if (el.type === "time") {
25
+ // Format time as "HH:MM"
26
+ const hours = String(date.getHours()).padStart(2, "0");
27
+ const minutes = String(date.getMinutes()).padStart(2, "0");
28
+ el.value = `${hours}:${minutes}`;
29
+ } else if (el.type === "datetime-local") {
30
+ // Format datetime-local as "YYYY-MM-DDTHH:MM"
31
+ const year = date.getFullYear();
32
+ const month = String(date.getMonth() + 1).padStart(2, "0");
33
+ const day = String(date.getDate()).padStart(2, "0");
34
+ const hours = String(date.getHours()).padStart(2, "0");
35
+ const minutes = String(date.getMinutes()).padStart(2, "0");
36
+ el.value = `${year}-${month}-${day}T${hours}:${minutes}`;
37
+ }
24
38
  } else {
25
- value = "";
39
+ el.value = "";
26
40
  }
41
+ return dispatchEvents(el, bubbles, dispatch);
27
42
  }
28
43
 
29
44
  let valueType = el.getAttribute("value-type");
@@ -100,6 +115,8 @@ const setValue = (el, value, dispatch) => {
100
115
  el.srcdoc = value;
101
116
  } else if (el.tagName === "SCRIPT") {
102
117
  setScript(el, value);
118
+ } else if (el.hasAttribute("value")) {
119
+ value = el.setAttribute("value", value);
103
120
  } else {
104
121
  if (el.hasAttribute("contenteditable") && el == document.activeElement)
105
122
  return;
package/src/utility.js CHANGED
@@ -1,80 +1,200 @@
1
- import { ObjectId } from "@cocreate/utils";
1
+ import { ObjectId, queryElements } from "@cocreate/utils";
2
2
 
3
- function getSubdomain() {
4
- const hostname = window.location.hostname; // e.g., "api.dev.example.com"
5
- const parts = hostname.split(".");
3
+ // Operators handled directly for simple, synchronous value retrieval
4
+ const customOperators = new Map(
5
+ Object.entries({
6
+ $organization_id: () => localStorage.getItem("organization_id"),
7
+ $user_id: () => localStorage.getItem("user_id"),
8
+ $clientId: () => localStorage.getItem("clientId"),
9
+ $session_id: () => localStorage.getItem("session_id"),
10
+ $value: (element) => element.getValue() || "",
11
+ $innerWidth: () => window.innerWidth,
12
+ $innerHeight: () => window.innerHeight,
13
+ $href: () => window.location.href.replace(/\/$/, ""),
14
+ $origin: () => window.location.origin,
15
+ $protocol: () => window.location.protocol,
16
+ $hostname: () => window.location.hostname,
17
+ $host: () => window.location.host,
18
+ $port: () => window.location.port,
19
+ $pathname: () => window.location.pathname.replace(/\/$/, ""),
20
+ $hash: () => window.location.hash,
21
+ $subdomain: () => getSubdomain() || "",
22
+ $object_id: () => ObjectId().toString(),
23
+ "ObjectId()": () => ObjectId().toString(),
24
+ $relativePath: () => {
25
+ let depth = window.location.pathname.split("/").length - 1;
26
+ return depth > 0 ? "../".repeat(depth) : "./";
27
+ },
28
+ $path: () => {
29
+ let path = window.location.pathname;
30
+ if (path.split("/").pop().includes(".")) {
31
+ path = path.replace(/\/[^\/]+$/, "/");
32
+ }
33
+ return path === "/" ? "" : path;
34
+ },
35
+ $param: (element, args) => args,
36
+ $setValue: (element, args) => element.setValue(...args) || ""
37
+ })
38
+ );
39
+
40
+ // Operators that access a specific property of a target element
41
+ const propertyOperators = new Set([
42
+ "$scrollWidth",
43
+ "$scrollHeight",
44
+ "$offsetWidth",
45
+ "$offsetHeight",
46
+ "$id",
47
+ "$tagName",
48
+ "$className",
49
+ "$textContent",
50
+ "$innerHTML",
51
+ "$getValue"
52
+ ]);
53
+
54
+ // Combine all known operator keys for the main parsing regex
55
+ const knownOperatorKeys = [
56
+ ...customOperators.keys(),
57
+ ...propertyOperators
58
+ ].sort((a, b) => b.length - a.length);
59
+
60
+ function escapeRegexKey(key) {
61
+ if (key.startsWith("$")) {
62
+ return "\\" + key; // Escape the leading $
63
+ } else if (key === "ObjectId()") {
64
+ return "ObjectId\\(\\)"; // Escape the parentheses
65
+ }
66
+ return key; // Should not happen with current keys, but fallback
67
+ }
68
+
69
+ const operatorPattern = knownOperatorKeys.map(escapeRegexKey).join("|");
70
+
71
+ // Regex to find potential operators and their arguments
72
+ // $1: Potential operator identifier (e.g., $user_id, $closestDiv)
73
+ // $2: Arguments within parentheses (optional)
74
+ const regex = new RegExp(`(${operatorPattern})(?:\\s*\\((.*?)\\))?`, "g");
75
+
76
+ /**
77
+ * Synchronously processes a string, finding and replacing operators recursively.
78
+ * Assumes ALL underlying operations (getValue, queryElements) are synchronous.
79
+ * @param {Element | null} element - Context element.
80
+ * @param {string} value - String containing operators.
81
+ * @param {string[]} [exclude=[]] - Operator prefixes to ignore.
82
+ * @returns {string} - Processed string.
83
+ */
84
+ function processOperators(element, value, exclude = [], parent) {
85
+ // Early exit if no operators are possible or value is not a string
86
+ if (typeof value !== "string" || !value.includes("$")) {
87
+ return value;
88
+ }
89
+ let params = [];
90
+ const processedValue = value.replace(
91
+ regex,
92
+ (match, operator, args = "") => {
93
+ // 'match' is the full matched string (e.g., "$closest(.myClass)")
94
+ // 'operator' is the identifier part (e.g., "$closest")
95
+ // 'args' is the content within parentheses (e.g., ".myClass") or "" if no parentheses
96
+
97
+ if (operator === "$param" && !args) {
98
+ return match;
99
+ }
100
+
101
+ // If a valid operator was identified AND it's not in the exclude list
102
+ if (operator && !exclude.includes(operator)) {
103
+ // Resolve the value for the identified operator and its arguments
104
+ // Pass the *trimmed* arguments to the resolver
105
+ let resolvedValue = resolveOperator(
106
+ element,
107
+ operator,
108
+ args.replace(/^['"]|['"]$/g, "").trim(),
109
+ parent
110
+ );
111
+
112
+ if (operator === "$param") {
113
+ params.push(resolvedValue);
114
+ return "";
115
+ }
6
116
 
7
- // Handle edge cases for single-word hostnames or IPs
8
- if (parts.length > 2 && isNaN(parts[parts.length - 1])) {
9
- return parts.slice(0, parts.length - 2).join("."); // Subdomain
117
+ return resolvedValue ?? "";
118
+ } else {
119
+ // If no known operator matched, or if it was excluded,
120
+ // return the original matched string (no replacement).
121
+ return match;
122
+ }
123
+ }
124
+ );
125
+
126
+ if (params.length) {
127
+ return params;
10
128
  }
11
129
 
12
- return null; // No subdomain
130
+ return processedValue;
13
131
  }
14
132
 
15
- const operatorsMap = {
16
- $href: function () {
17
- return window.location.href;
18
- },
19
- $origin: function () {
20
- return window.location.origin;
21
- },
22
- $protocol: function () {
23
- return window.location.protocol;
24
- },
25
- $host: function () {
26
- return window.location.host;
27
- },
28
- $hostname: function () {
29
- return window.location.hostname;
30
- },
31
- $port: function () {
32
- return window.location.port;
33
- },
34
- $pathname: function () {
35
- return window.location.pathname;
36
- },
37
- $hash: function () {
38
- return window.location.hash;
39
- },
40
- $subdomain: function () {
41
- return getSubdomain() || "";
42
- },
43
- $object_id: function () {
44
- return ObjectId().toString();
133
+ /**
134
+ * Synchronously determines and executes the action for a single operator token.
135
+ * @returns {string} The final string value for the token.
136
+ */
137
+ function resolveOperator(element, operator, args, parent) {
138
+ if (args && args.includes("$")) {
139
+ args = processOperators(element, args, "", operator);
140
+ }
141
+
142
+ let targetElements = element ? [element] : [];
143
+ if (args && typeof args === "string") {
144
+ targetElements = queryElements({
145
+ element,
146
+ args
147
+ });
45
148
  }
46
- };
47
149
 
48
- function urlOperators(value) {
49
- if (typeof value !== "string") {
50
- console.error("Value is not a string:", value);
51
- return value; // Return as-is for non-string input
150
+ let value = processValues(targetElements, operator, args, parent);
151
+ if (value && typeof value === "string" && value.includes("$")) {
152
+ value = processOperators(element, value, parent);
52
153
  }
53
154
 
54
- // Dynamically construct a regex from the keys in operatorsMap
55
- const operatorKeys = Object.keys(operatorsMap)
56
- .map((key) => `\\${key}`)
57
- .join("|");
58
- const regex = new RegExp(operatorKeys, "g");
59
-
60
- // Debugging regex match
61
- // if (!regex.test(value)) {
62
- // console.warn("Regex did not match any part of the input value.");
63
- // }
64
-
65
- // Replace matched operators with their resolved values
66
- const updatedValue = value.replace(regex, function (match) {
67
- if (operatorsMap[match]) {
68
- const replacement = operatorsMap[match]();
69
- console.log(`Replacing "${match}" with "${replacement}"`);
70
- return replacement || "";
155
+ return value;
156
+ }
157
+
158
+ /**
159
+ * Synchronously aggregates values.
160
+ * @returns {string} The aggregated string value.
161
+ */
162
+ function processValues(elements, operator, args, parent) {
163
+ let customOp = customOperators.get(operator);
164
+ let aggregatedString = "";
165
+ for (const el of elements) {
166
+ if (!el) continue;
167
+ let rawValue = customOp || el?.[operator.substring(1)];
168
+ if (typeof rawValue === "function") {
169
+ if (Array.isArray(args)) {
170
+ if (args.length) {
171
+ return "";
172
+ }
173
+ rawValue = rawValue(el, ...args);
174
+ } else {
175
+ rawValue = rawValue(el, args);
176
+ }
177
+ }
178
+
179
+ if (parent === "$param") {
180
+ if (rawValue) {
181
+ return rawValue;
182
+ }
71
183
  } else {
72
- console.warn(`No match found for "${match}" in operatorsMap.`);
73
- return ""; // Default replacement if operator not found
184
+ aggregatedString += String(rawValue ?? "");
74
185
  }
75
- });
186
+ }
187
+
188
+ return aggregatedString;
189
+ }
76
190
 
77
- return updatedValue;
191
+ function getSubdomain() {
192
+ const hostname = window.location.hostname;
193
+ const parts = hostname.split(".");
194
+ if (parts.length > 2 && isNaN(parseInt(parts[parts.length - 1]))) {
195
+ return parts.slice(0, parts.length - 2).join(".");
196
+ }
197
+ return null;
78
198
  }
79
199
 
80
- export default { urlOperators };
200
+ export { processOperators };