solarite 0.5.2 → 0.7.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/LICENSE +21 -0
- package/dist/Solarite-debug.js +2823 -1856
- package/dist/Solarite.js +2779 -1824
- package/dist/Solarite.min.js +2 -4
- package/package.json +16 -3
- package/readme.md +58 -11
- package/src/Globals.js +54 -72
- package/src/HtmlParser.js +90 -90
- package/src/MultiValueMap.js +57 -105
- package/src/NodeGroup.js +624 -470
- package/src/Path.js +224 -211
- package/src/PathToAttribValue.js +401 -261
- package/src/PathToAttribs.js +113 -80
- package/src/PathToComment.js +7 -7
- package/src/PathToComponent.js +183 -188
- package/src/PathToEvent.js +76 -64
- package/src/PathToKey.js +19 -0
- package/src/PathToNodes.js +1053 -566
- package/src/RootNodeGroup.js +120 -8
- package/src/Shell.js +570 -348
- package/src/Solarite.d.ts +134 -113
- package/src/Solarite.js +243 -286
- package/src/Template.js +195 -274
- package/src/Util.js +353 -351
- package/src/assert.js +10 -10
- package/src/assignAttributes.js +63 -0
- package/src/delve.js +55 -43
- package/src/h.js +220 -138
- package/src/jsx-dev-runtime.d.ts +1 -0
- package/src/jsx-dev-runtime.js +5 -0
- package/src/jsx-runtime.d.ts +21 -0
- package/src/jsx-runtime.js +84 -0
- package/src/jsx.js +194 -0
- package/src/toEl.js +77 -82
- package/dist/udomdiff-license.txt +0 -18
- package/src/getArg.js +0 -137
- package/src/hash.js +0 -89
- package/src/udomdiff.js +0 -176
- package/src/unused/FastLookupArray.js +0 -54
- package/src/unused/Hashes.js +0 -339
- package/src/unused/InUse.test.js +0 -92
- package/src/unused/InUseMap.js +0 -98
- package/src/unused/LinkedList.js +0 -117
- package/src/unused/LinkedList.test.js +0 -115
- package/src/unused/Misc.js +0 -13
- package/src/unused/Perf.js +0 -47
- package/src/unused/TrackedArray.js +0 -54
- package/src/unused/WeakArray.js +0 -33
- package/src/watch.js +0 -543
package/src/Util.js
CHANGED
|
@@ -1,352 +1,354 @@
|
|
|
1
|
-
import Globals from "./Globals.js";
|
|
2
|
-
import delve from "./delve.js";
|
|
3
|
-
|
|
4
|
-
let Util = {
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Returns true if they're the same.
|
|
8
|
-
* @param a
|
|
9
|
-
* @param b
|
|
10
|
-
* @returns {boolean} */
|
|
11
|
-
arraySame(a, b) {
|
|
12
|
-
let aLength = a.length;
|
|
13
|
-
if (aLength !== b.length)
|
|
14
|
-
return false;
|
|
15
|
-
for (let i=0; i<aLength; i++)
|
|
16
|
-
if (a[i] !== b[i])
|
|
17
|
-
return false;
|
|
18
|
-
return true; // the same.
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Convert HTMLElement attributes to an object.
|
|
23
|
-
* Converts dash (kebob-case) attribute names to camelCase.
|
|
24
|
-
* See also Solarite.getAttribs()
|
|
25
|
-
* @param el {HTMLElement}
|
|
26
|
-
* @param ignore {?string} Optionally ignore this attribute.
|
|
27
|
-
* @return {Object} */
|
|
28
|
-
attribsToObject(el, ignore=null) {
|
|
29
|
-
let result = {};
|
|
30
|
-
for (let attrib of el.attributes)
|
|
31
|
-
if (attrib.name !== ignore)
|
|
32
|
-
result[Util.dashesToCamel(attrib.name)] = attrib.value;
|
|
33
|
-
return result;
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
bindId(root, el) {
|
|
37
|
-
let id = el.getAttribute('data-id') || el.getAttribute('id');
|
|
38
|
-
if (id) { // If something hasn't removed the id.
|
|
39
|
-
|
|
40
|
-
// Don't
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
let
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
result.push(
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
result.
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
1
|
+
import Globals from "./Globals.js";
|
|
2
|
+
import delve from "./delve.js";
|
|
3
|
+
|
|
4
|
+
let Util = {
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns true if they're the same.
|
|
8
|
+
* @param a
|
|
9
|
+
* @param b
|
|
10
|
+
* @returns {boolean} */
|
|
11
|
+
arraySame(a, b) {
|
|
12
|
+
let aLength = a.length;
|
|
13
|
+
if (aLength !== b.length)
|
|
14
|
+
return false;
|
|
15
|
+
for (let i=0; i<aLength; i++)
|
|
16
|
+
if (a[i] !== b[i])
|
|
17
|
+
return false;
|
|
18
|
+
return true; // the same.
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Convert HTMLElement attributes to an object.
|
|
23
|
+
* Converts dash (kebob-case) attribute names to camelCase.
|
|
24
|
+
* See also Solarite.getAttribs()
|
|
25
|
+
* @param el {HTMLElement}
|
|
26
|
+
* @param ignore {?string} Optionally ignore this attribute.
|
|
27
|
+
* @return {Object} */
|
|
28
|
+
attribsToObject(el, ignore=null) {
|
|
29
|
+
let result = {};
|
|
30
|
+
for (let attrib of el.attributes)
|
|
31
|
+
if (attrib.name !== ignore)
|
|
32
|
+
result[Util.dashesToCamel(attrib.name)] = attrib.value;
|
|
33
|
+
return result;
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
bindId(root, el) {
|
|
37
|
+
let id = el.getAttribute('data-id') || el.getAttribute('id');
|
|
38
|
+
if (id) { // If something hasn't removed the id.
|
|
39
|
+
|
|
40
|
+
// Don't clobber a non-element value. For a simple (non-nested) id this covers two cases:
|
|
41
|
+
// an inherited/built-in property like `title` or `style`, or an own property that already
|
|
42
|
+
// holds a non-Node value. A previously-bound element (a Node) is fine to re-assign.
|
|
43
|
+
if (!id.includes('.')) {
|
|
44
|
+
let existing = root[id];
|
|
45
|
+
let isInherited = (id in root) && !Object.hasOwn(root, id);
|
|
46
|
+
if (!existing?.nodeType && (existing != null || isInherited))
|
|
47
|
+
throw new Error(`${root.constructor.name}.${id} can't be a reference to ` +
|
|
48
|
+
`<${el.tagName.toLowerCase()} id="${id}"> because it would clobber an existing ` +
|
|
49
|
+
`${isInherited ? 'built-in ' : ''}property. Rename the id or the property.`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
delve(root, id.split(/\./g), el);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* If the style tab has a global attribute:
|
|
58
|
+
* 1. Put it in the document head as <style data-style="tag-name">...</style>
|
|
59
|
+
* 2. Replace the :host {...} CSS selector as tag-name {...}.
|
|
60
|
+
* Otherwise keep it where it is and:
|
|
61
|
+
* 1. Add data-style="1" attribute to the root element.
|
|
62
|
+
* 2. Replace the :host {...} selector in the style as tag-name[data-style='1'] {...}
|
|
63
|
+
* @param style {HTMLStyleElement}
|
|
64
|
+
* @param root {HTMLElement} */
|
|
65
|
+
bindStyles(style, root) {
|
|
66
|
+
|
|
67
|
+
let tagName = root.tagName.toLowerCase();
|
|
68
|
+
let styleId, attribSelector;
|
|
69
|
+
|
|
70
|
+
if (style.hasAttribute('global') || style.hasAttribute('data-global')) {
|
|
71
|
+
styleId = tagName;
|
|
72
|
+
attribSelector = '';
|
|
73
|
+
let doc = Globals.doc || root.ownerDocument || document;
|
|
74
|
+
if (!doc.head.querySelector(`style[data-style="${styleId}"]`)) {
|
|
75
|
+
doc.head.append(style)
|
|
76
|
+
style.setAttribute('data-style', styleId);
|
|
77
|
+
}
|
|
78
|
+
else // TODO: Make sure the style has no expressions.
|
|
79
|
+
style.remove(); // already in the head.
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
let styleId = root.getAttribute('data-style');
|
|
83
|
+
if (!styleId) {
|
|
84
|
+
// Keep track of one style id for each class.
|
|
85
|
+
// TODO: Put this outside the class in a map, so it doesn't conflict with static properties.
|
|
86
|
+
if (!root.constructor.styleId)
|
|
87
|
+
root.constructor.styleId = 1;
|
|
88
|
+
styleId = root.constructor.styleId++;
|
|
89
|
+
|
|
90
|
+
root.setAttribute('data-style', styleId);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
attribSelector = `[data-style="${styleId}"]`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Replace ":host" with "tagName[data-style=...]" in the css.
|
|
97
|
+
for (let child of style.childNodes) {
|
|
98
|
+
if (child.nodeType === 3) {
|
|
99
|
+
let oldText = child.textContent;
|
|
100
|
+
let newText = oldText.replace(/:host(?=[^a-z0-9_])/gi, `${tagName}${attribSelector}`)
|
|
101
|
+
if (oldText !== newText)
|
|
102
|
+
child.textContent = newText;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Convert a Proper Case name to a name with dashes.
|
|
109
|
+
* Dashes will be placed between letters and numbers.
|
|
110
|
+
* If there are multiple consecutive capital letters followed by another chracater, a dash will be placed before the last capital letter.
|
|
111
|
+
* @param str {string}
|
|
112
|
+
* @return {string}
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* 'ProperName' => 'proper-name'
|
|
116
|
+
* 'HTMLElement' => 'html-element'
|
|
117
|
+
* 'BigUI' => 'big-ui'
|
|
118
|
+
* 'UIForm' => 'ui-form'
|
|
119
|
+
* 'A100' => 'a-100' */
|
|
120
|
+
camelToDashes(str) {
|
|
121
|
+
// Convert any capital letter that is preceded by a lowercase letter or number to lowercase and precede with a dash.
|
|
122
|
+
str = str.replace(/([a-z0-9])([A-Z])/g, '$1-$2');
|
|
123
|
+
|
|
124
|
+
// Convert any capital letter that is followed by a lowercase letter or number to lowercase and precede with a dash.
|
|
125
|
+
str = str.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2');
|
|
126
|
+
|
|
127
|
+
// Convert any number that is preceded by a lowercase or uppercase letter to be preceded by a dash.
|
|
128
|
+
str = str.replace(/([a-zA-Z])([0-9])/g, '$1-$2');
|
|
129
|
+
|
|
130
|
+
// Convert all the remaining capital letters to lowercase.
|
|
131
|
+
return str.toLowerCase();
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Converts a string written in kebab-case to camelCase.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} str - The input string written in kebab-case.
|
|
138
|
+
* @return {string} - The resulting camelCase string.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* dashesToCamel('example-string') // Returns 'exampleString'
|
|
142
|
+
* dashesToCamel('another-example-test') // Returns 'anotherExampleTest' */
|
|
143
|
+
dashesToCamel(str) {
|
|
144
|
+
return str.replace(/-([a-z])/g, g => g[1].toUpperCase());
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
defineClass(Class, tagName) {
|
|
148
|
+
if (!customElements[getName](Class)) { // If not previously defined.
|
|
149
|
+
tagName = tagName || Util.camelToDashes(Class.name)
|
|
150
|
+
if (!tagName.includes('-')) // Browsers require that web components always have a dash in the name.
|
|
151
|
+
tagName += '-element';
|
|
152
|
+
customElements[define](tagName, Class)
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get the value of an input as the most appropriate JavaScript type.
|
|
158
|
+
* @param node {HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement|HTMLElement}
|
|
159
|
+
* @return {string|string[]|number|[]|File[]|Date|boolean} */
|
|
160
|
+
getInputValue(node) {
|
|
161
|
+
// .type is a built-in DOM property
|
|
162
|
+
if (node.type === 'checkbox')
|
|
163
|
+
return node.checked; // Boolean
|
|
164
|
+
if (node.type === 'radio')
|
|
165
|
+
return node.value; // String value of the selected radio in the group
|
|
166
|
+
if (node.type === 'file')
|
|
167
|
+
return [...node.files]; // FileList
|
|
168
|
+
if (node.type === 'number' || node.type === 'range')
|
|
169
|
+
return node.valueAsNumber; // Number
|
|
170
|
+
if (node.type === 'date' || node.type === 'time' || node.type === 'datetime-local')
|
|
171
|
+
return node.valueAsDate; // Date Object
|
|
172
|
+
if (node.type === 'select-multiple') // <select multiple>
|
|
173
|
+
return [...node.selectedOptions].map(option => option.value); // Array of Strings
|
|
174
|
+
if (node.hasAttribute('contenteditable'))
|
|
175
|
+
return node.innerHTML;
|
|
176
|
+
|
|
177
|
+
return node.value; // String
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
isEvent(attrName) {
|
|
181
|
+
return attrName.startsWith('on') && attrName in Globals.div;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @param el {HTMLElement}
|
|
186
|
+
* @param prop {string}
|
|
187
|
+
* @returns {boolean} */
|
|
188
|
+
isHtmlProp(el, prop) {
|
|
189
|
+
let key = el.tagName + '.' + prop;
|
|
190
|
+
let result = Globals.htmlProps[key];
|
|
191
|
+
if (result === undefined) { // Caching just barely makes this slightly faster.
|
|
192
|
+
let proto = Object.getPrototypeOf(el);
|
|
193
|
+
|
|
194
|
+
// Find the first HTMLElement that we inherit from (not our own classes)
|
|
195
|
+
while (proto) {
|
|
196
|
+
const ctorName = proto.constructor.name;
|
|
197
|
+
if (ctorName.startsWith('HTML') && ctorName.endsWith('Element'))
|
|
198
|
+
break
|
|
199
|
+
proto = Object.getPrototypeOf(proto);
|
|
200
|
+
}
|
|
201
|
+
Globals.htmlProps[key] = result = (proto
|
|
202
|
+
? !!Object.getOwnPropertyDescriptor(proto, prop)?.set
|
|
203
|
+
: false);
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
isFalsy(val) {
|
|
209
|
+
return val === undefined || val === false || val === null;
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Split a string like 'foo="bar" baz=123' into an object like {foo: 'bar', baz: '123'}.
|
|
214
|
+
* @param str {string}
|
|
215
|
+
* @returns {Object} */
|
|
216
|
+
splitAttribs(str) {
|
|
217
|
+
let result = {};
|
|
218
|
+
let attrs = (str + '') // Split string into multiple attributes.
|
|
219
|
+
.split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|\S+))/g)
|
|
220
|
+
.map(text => text.trim())
|
|
221
|
+
.filter(text => text.length);
|
|
222
|
+
|
|
223
|
+
for (let attr of attrs) {
|
|
224
|
+
let [name, value] = attr.split(/\s*=\s*/); // split on first equals.
|
|
225
|
+
value = (value || '').replace(/^(['"])(.*)\1$/, '$2'); // trim value quotes if they match.
|
|
226
|
+
result[name] = value;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return result;
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
isPrimitive(val) {
|
|
234
|
+
return typeof val === 'string' || typeof val === 'number'
|
|
235
|
+
},*/
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* If val is a function, evaluate it recursively until the result is not a function.
|
|
239
|
+
* If it's an array or an object, convert it to Json.
|
|
240
|
+
* If it's a Date, format it as Y-m-d H:i:s
|
|
241
|
+
* @param val
|
|
242
|
+
* @returns {string|number|boolean} */
|
|
243
|
+
makePrimitive(val) {
|
|
244
|
+
if (typeof val === 'function')
|
|
245
|
+
return Util.makePrimitive(val());
|
|
246
|
+
else if (val instanceof Date)
|
|
247
|
+
return val.toISOString().replace(/T/, ' ');
|
|
248
|
+
else if (Array.isArray(val) || typeof val === 'object')
|
|
249
|
+
return ''; // JSON.stringify(val);
|
|
250
|
+
return val;
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
saveOrphans(nodes) {
|
|
254
|
+
let fragment = Globals.doc.createDocumentFragment();
|
|
255
|
+
fragment.append(...nodes);
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Remove nodes from the beginning and end that are not:
|
|
260
|
+
* 1. Elements.
|
|
261
|
+
* 2. Non-whitespace text nodes.
|
|
262
|
+
* @param nodes {Node[]|NodeList}
|
|
263
|
+
* @returns {Node[]} */
|
|
264
|
+
trimEmptyNodes(nodes) {
|
|
265
|
+
const shouldTrimNode = node =>
|
|
266
|
+
node.nodeType !== Node.ELEMENT_NODE &&
|
|
267
|
+
(node.nodeType !== Node.TEXT_NODE || node.textContent.trim() === '');
|
|
268
|
+
|
|
269
|
+
// Convert nodeList to an array for easier manipulation
|
|
270
|
+
const result = [...nodes]
|
|
271
|
+
|
|
272
|
+
// Trim from the start
|
|
273
|
+
while (result.length > 0 && shouldTrimNode(result[0]))
|
|
274
|
+
result.shift();
|
|
275
|
+
|
|
276
|
+
// Trim from the end
|
|
277
|
+
while (result.length > 0 && shouldTrimNode(result[result.length - 1]))
|
|
278
|
+
result.pop();
|
|
279
|
+
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
// Trick to prevent minifier from renaming these methods.
|
|
287
|
+
let define = 'define';
|
|
288
|
+
let getName = 'getName';
|
|
289
|
+
|
|
290
|
+
export default Util;
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
// For debugging only
|
|
295
|
+
//#IFDEV
|
|
296
|
+
export function setIndent(items, level=1) {
|
|
297
|
+
if (typeof items === 'string')
|
|
298
|
+
items = items.split(/\r?\n/g)
|
|
299
|
+
|
|
300
|
+
return items.map(str => {
|
|
301
|
+
if (level > 0)
|
|
302
|
+
return ' '.repeat(level) + str;
|
|
303
|
+
else if (level < 0)
|
|
304
|
+
return str.replace(new RegExp(`^ {0,${Math.abs(level)}}`), '');
|
|
305
|
+
return str;
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function nodeToArrayTree(node, callback=null) {
|
|
310
|
+
if (!node) return [];
|
|
311
|
+
|
|
312
|
+
let result = [];
|
|
313
|
+
|
|
314
|
+
if (callback)
|
|
315
|
+
result.push(...callback(node))
|
|
316
|
+
|
|
317
|
+
if (node.nodeType === 1) {
|
|
318
|
+
let attrs = Array.from(node.attributes).map(attr => `${attr.name}="${attr.value}"`).join(' ');
|
|
319
|
+
let openingTag = `<${node.nodeName.toLowerCase()}${attrs ? ' ' + attrs : ''}>`;
|
|
320
|
+
|
|
321
|
+
let childrenArray = [];
|
|
322
|
+
for (let child of node.childNodes) {
|
|
323
|
+
let childResult = nodeToArrayTree(child, callback);
|
|
324
|
+
if (childResult.length > 0) {
|
|
325
|
+
childrenArray.push(childResult);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
//let closingTag = `</${node.nodeName.toLowerCase()}>`;
|
|
330
|
+
|
|
331
|
+
result.push(openingTag, ...childrenArray);
|
|
332
|
+
} else if (node.nodeType === 3) {
|
|
333
|
+
result.push("'"+node.nodeValue+"'");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
export function flattenAndIndent(inputArray, indent = "") {
|
|
341
|
+
let result = [];
|
|
342
|
+
|
|
343
|
+
for (let item of inputArray) {
|
|
344
|
+
if (Array.isArray(item)) {
|
|
345
|
+
// Recursively handle nested arrays with increased indentation
|
|
346
|
+
result = result.concat(flattenAndIndent(item, indent + " "));
|
|
347
|
+
} else {
|
|
348
|
+
result.push(indent + item);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
352
354
|
//#ENDIF
|