@cocreate/utils 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.
@@ -0,0 +1,174 @@
1
+ const queryTypes = [
2
+ "$closest",
3
+ "$parent",
4
+ "$next",
5
+ "$previous",
6
+ "$document",
7
+ "$frame",
8
+ "$top"
9
+ ];
10
+
11
+ const regexPatternString = `(?:${queryTypes
12
+ .map((type) => type.replace("$", "\\$"))
13
+ .join("|")})`;
14
+
15
+ const queryTypesRegex = new RegExp(regexPatternString);
16
+
17
+ const mediaRanges = {
18
+ xs: [0, 575],
19
+ sm: [576, 768],
20
+ md: [769, 992],
21
+ lg: [993, 1200],
22
+ xl: [1201, 0]
23
+ };
24
+
25
+ export function checkMediaQueries(selector) {
26
+ if (selector && selector.includes("@")) {
27
+ const viewportWidth = window.innerWidth;
28
+ let mediaViewport = false;
29
+
30
+ let screenSizes = selector.split("@");
31
+ selector = screenSizes.shift();
32
+
33
+ for (let screenSize of screenSizes) {
34
+ if (mediaRanges.hasOwnProperty(screenSize)) {
35
+ const [minWidth, maxWidth] = mediaRanges[screenSize];
36
+ if (viewportWidth >= minWidth && viewportWidth <= maxWidth) {
37
+ mediaViewport = true;
38
+ break;
39
+ }
40
+ }
41
+ }
42
+ if (!mediaViewport) return false;
43
+ }
44
+
45
+ return selector;
46
+ }
47
+
48
+ function queryType(element, type) {
49
+ if (!element) return null;
50
+
51
+ switch (type) {
52
+ case "$top":
53
+ return window.top.document;
54
+ case "$frame":
55
+ if (element.nodeType === 9) return window.frameElement;
56
+ return element;
57
+ case "$document":
58
+ return element.nodeType === 9 ? element : element.ownerDocument;
59
+ case "$closest":
60
+ return element.nodeType === 9 ? element : element.ownerDocument;
61
+ case "$parent":
62
+ if (element.nodeType === 9) {
63
+ return element.defaultView !== window.top
64
+ ? element.defaultView.parent.document
65
+ : null;
66
+ }
67
+ return element.parentElement;
68
+ case "$next":
69
+ return element.nextElementSibling;
70
+ case "$previous":
71
+ return element.previousElementSibling;
72
+ default:
73
+ return null;
74
+ }
75
+ }
76
+
77
+ function querySelector(element, selector) {
78
+ if (!element) return null;
79
+ return selector.endsWith("[]")
80
+ ? element.querySelectorAll(selector.slice(0, -2))
81
+ : element.querySelector(selector);
82
+ }
83
+
84
+ export function queryElements({ element = document, prefix, selector }) {
85
+ try {
86
+ let elements = new Set();
87
+
88
+ if (!selector && element.nodeType === 1) {
89
+ if (!prefix) {
90
+ for (let attr of element.attributes) {
91
+ if (attr.name.endsWith("-query") || attr.name.endsWith(".query")) {
92
+ prefix = attr.name.slice(0, -6);
93
+ }
94
+ }
95
+ if (!prefix) return [];
96
+ }
97
+ selector =
98
+ element.getAttribute(prefix + "-" + "query") ||
99
+ element.getAttribute(prefix + "." + "query");
100
+ if (!selector) return [];
101
+ }
102
+
103
+ let selectors = selector.split(/,(?![^()\[\]]*[)\]])/g);
104
+ for (let i = 0; i < selectors.length; i++) {
105
+ if (!selectors[i]) continue;
106
+
107
+ let queriedElement = element;
108
+
109
+ if (selectors[i].includes("@")) {
110
+ selectors[i] = checkMediaQueries(selectors[i]);
111
+ if (selectors[i] === false) continue;
112
+ }
113
+
114
+ let remainingSelector = selectors[i].trim();
115
+ let match;
116
+
117
+ while ((match = queryTypesRegex.exec(remainingSelector)) !== null) {
118
+ const matchIndex = match.index;
119
+ const operator = match[0];
120
+
121
+ const part = remainingSelector
122
+ .substring(0, matchIndex)
123
+ .trim()
124
+ .replace(/,$/, "");
125
+ if (part) {
126
+ queriedElement = querySelector(queriedElement, part);
127
+ if (!queriedElement) break;
128
+ }
129
+
130
+ remainingSelector = remainingSelector
131
+ .substring(matchIndex + operator.length)
132
+ .trim();
133
+
134
+ if (operator === "$closest") {
135
+ let [closest, remaining = ""] = remainingSelector.split(/\s+/, 2);
136
+ queriedElement = queriedElement.closest(closest);
137
+ remainingSelector = remaining.trim();
138
+ } else {
139
+ queriedElement = queryType(queriedElement, operator);
140
+ }
141
+
142
+ if (!queriedElement) break;
143
+ }
144
+
145
+ if (!queriedElement) continue;
146
+
147
+ if (remainingSelector) {
148
+ queriedElement = querySelector(queriedElement, remainingSelector);
149
+ }
150
+
151
+ if (
152
+ Array.isArray(queriedElement) ||
153
+ queriedElement instanceof HTMLCollection ||
154
+ queriedElement instanceof NodeList
155
+ ) {
156
+ for (let el of queriedElement) {
157
+ if (el instanceof Element) {
158
+ elements.add(el);
159
+ }
160
+ }
161
+ } else if (queriedElement instanceof Element) {
162
+ elements.add(queriedElement);
163
+ }
164
+ }
165
+
166
+ return Array.from(elements);
167
+ } catch (e) {
168
+ console.error(
169
+ `CoCreate: Error in queryElements with selector: "${selector}".`,
170
+ e
171
+ );
172
+ return [];
173
+ }
174
+ }
@@ -0,0 +1,171 @@
1
+ const constants = { PI: Math.PI, E: Math.E };
2
+
3
+ const functions = {
4
+ abs: Math.abs,
5
+ ceil: Math.ceil,
6
+ floor: Math.floor,
7
+ round: Math.round,
8
+ max: Math.max,
9
+ min: Math.min,
10
+ pow: Math.pow,
11
+ sqrt: Math.sqrt,
12
+ log: Math.log,
13
+ sin: Math.sin,
14
+ cos: Math.cos,
15
+ tan: Math.tan
16
+ };
17
+
18
+ export function safeParse(expression, context = {}) {
19
+ if (typeof expression !== "string") {
20
+ expression = String(expression);
21
+ }
22
+
23
+ let currentExpr = expression.trim();
24
+ if (!currentExpr) return null;
25
+
26
+ const tokenizerRegex =
27
+ /('[^']*'|"[^"]*"|\d+(?:\.\d+)?|>=|<=|===|!==|==|!=|&&|\|\||[a-zA-Z_][a-zA-Z0-9_\.]*|[\+\-\*\/\%\(\)\?\:\>\<\!\,\=])/g;
28
+
29
+ const tokens = currentExpr.match(tokenizerRegex) || [];
30
+ let pos = 0;
31
+
32
+ function peek() {
33
+ return tokens[pos];
34
+ }
35
+ function consume() {
36
+ return tokens[pos++];
37
+ }
38
+
39
+ function parse() {
40
+ return parseTernary();
41
+ }
42
+
43
+ function parseTernary() {
44
+ let left = parseLogical();
45
+ if (peek() === "?") {
46
+ consume();
47
+ let trueExpr = parseTernary();
48
+ if (peek() === ":") {
49
+ consume();
50
+ let falseExpr = parseTernary();
51
+ return left ? trueExpr : falseExpr;
52
+ }
53
+ }
54
+ return left;
55
+ }
56
+
57
+ function parseLogical() {
58
+ let left = parseComparison();
59
+ while (peek() === "&&" || peek() === "||") {
60
+ let op = consume();
61
+ let right = parseComparison();
62
+ if (op === "&&") left = left && right;
63
+ if (op === "||") left = left || right;
64
+ }
65
+ return left;
66
+ }
67
+
68
+ function parseComparison() {
69
+ let left = parseAdditive();
70
+ while ([">", "<", ">=", "<=", "===", "!==", "==", "!="].includes(peek())) {
71
+ let op = consume();
72
+ let right = parseAdditive();
73
+ if (op === ">") left = left > right;
74
+ if (op === "<") left = left < right;
75
+ if (op === ">=") left = left >= right;
76
+ if (op === "<=") left = left <= right;
77
+ if (op === "===") left = left === right;
78
+ if (op === "!==") left = left !== right;
79
+ if (op === "==") left = left == right;
80
+ if (op === "!=") left = left != right;
81
+ }
82
+ return left;
83
+ }
84
+
85
+ function parseAdditive() {
86
+ let left = parseMultiplicative();
87
+ while (["+", "-"].includes(peek())) {
88
+ let op = consume();
89
+ let right = parseMultiplicative();
90
+ if (op === "+") left = left + right;
91
+ if (op === "-") left = left - right;
92
+ }
93
+ return left;
94
+ }
95
+
96
+ function parseMultiplicative() {
97
+ let left = parsePrimary();
98
+ while (["*", "/", "%"].includes(peek())) {
99
+ let op = consume();
100
+ let right = parsePrimary();
101
+ if (op === "*") left = left * right;
102
+ if (op === "/") left = left / right;
103
+ if (op === "%") left = left % right;
104
+ }
105
+ return left;
106
+ }
107
+
108
+ function parsePrimary() {
109
+ let token = consume();
110
+ if (!token) return undefined;
111
+
112
+ if (/^\d/.test(token)) return parseFloat(token);
113
+
114
+ if (token.startsWith("'") || token.startsWith('"')) {
115
+ return token.slice(1, -1);
116
+ }
117
+
118
+ if (token === "true") return true;
119
+ if (token === "false") return false;
120
+
121
+ if (token === "(") {
122
+ let expr = parse();
123
+ if (peek() === ")") consume();
124
+ return expr;
125
+ }
126
+
127
+ if (token === "-") return -parsePrimary();
128
+ if (token === "!") return !parsePrimary();
129
+
130
+ if (constants.hasOwnProperty(token)) {
131
+ return constants[token];
132
+ }
133
+
134
+ if (peek() === "(" && functions.hasOwnProperty(token)) {
135
+ consume();
136
+ let args = [];
137
+ if (peek() !== ")") {
138
+ args.push(parse());
139
+ while (peek() === ",") {
140
+ consume();
141
+ args.push(parse());
142
+ }
143
+ }
144
+ if (peek() === ")") consume();
145
+
146
+ return functions[token](...args);
147
+ }
148
+
149
+ let path = token.split(".");
150
+ let val = context;
151
+ for (let part of path) {
152
+ if (val !== null && typeof val === "object" && part in val) {
153
+ val = val[part];
154
+ } else {
155
+ return undefined;
156
+ }
157
+ }
158
+ return val;
159
+ }
160
+
161
+ try {
162
+ const result = parse();
163
+ return result !== undefined ? result : null;
164
+ } catch (error) {
165
+ console.warn(
166
+ `Unexpected parsing error: ${error.message} (Expression context: "${expression}")`,
167
+ error
168
+ );
169
+ return null;
170
+ }
171
+ }
package/src/uid.js ADDED
@@ -0,0 +1,16 @@
1
+ export function uid(length = 36) {
2
+ let pattern = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
3
+ if (length > 36) {
4
+ length = 36;
5
+ }
6
+
7
+ let uuid = pattern
8
+ .replace(/[xy]/g, function (c) {
9
+ var r = (Math.random() * 16) | 0;
10
+ var v = c === "x" ? r : (r & 0x3) | 0x8;
11
+ return v.toString(16);
12
+ })
13
+ .substring(0, length);
14
+
15
+ return uuid;
16
+ }