@dinoreic/fez 0.2.0 → 0.3.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/README.md +154 -157
- package/dist/fez.js +18 -18
- package/dist/fez.js.map +4 -4
- package/dist/log.js +5 -0
- package/dist/log.js.map +7 -0
- package/package.json +17 -13
- package/src/fez/compile.js +70 -47
- package/src/fez/connect.js +98 -63
- package/src/fez/defaults.js +64 -0
- package/src/fez/instance.js +131 -123
- package/src/fez/lib/template.js +4 -0
- package/src/fez/root.js +33 -134
- package/src/fez/utility.js +184 -0
- package/src/fez.js +5 -37
- package/src/log.js +154 -0
- package/src/rollup.js +73 -22
- package/dist/rollup.js +0 -3
- package/dist/rollup.js.map +0 -7
package/src/fez/instance.js
CHANGED
|
@@ -81,11 +81,6 @@ export default class FezBase {
|
|
|
81
81
|
return formObject
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
static fastBind() {
|
|
85
|
-
// return true to bind without requestAnimationFrame
|
|
86
|
-
// you can do this if you are sure you are not expecting innerHTML data
|
|
87
|
-
return false
|
|
88
|
-
}
|
|
89
84
|
|
|
90
85
|
static nodeName = 'div'
|
|
91
86
|
|
|
@@ -106,42 +101,11 @@ export default class FezBase {
|
|
|
106
101
|
if (this.root?.isConnected) {
|
|
107
102
|
return true
|
|
108
103
|
} else {
|
|
109
|
-
this.
|
|
104
|
+
this.fezOnDestroy()
|
|
110
105
|
return false
|
|
111
106
|
}
|
|
112
107
|
}
|
|
113
108
|
|
|
114
|
-
// clear all node references
|
|
115
|
-
fezRemoveSelf() {
|
|
116
|
-
this._setIntervalCache ||= {}
|
|
117
|
-
Object.keys(this._setIntervalCache).forEach((key)=> {
|
|
118
|
-
clearInterval(this._setIntervalCache[key])
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
if (this._eventHandlers) {
|
|
122
|
-
Object.entries(this._eventHandlers).forEach(([eventName, handler]) => {
|
|
123
|
-
window.removeEventListener(eventName, handler);
|
|
124
|
-
});
|
|
125
|
-
this._eventHandlers = {};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (this._timeouts) {
|
|
129
|
-
Object.values(this._timeouts).forEach(timeoutId => {
|
|
130
|
-
clearTimeout(timeoutId);
|
|
131
|
-
});
|
|
132
|
-
this._timeouts = {};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this.onDestroy()
|
|
136
|
-
this.onDestroy = () => {}
|
|
137
|
-
|
|
138
|
-
if (this.root) {
|
|
139
|
-
this.root.fez = undefined
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this.root = undefined
|
|
143
|
-
}
|
|
144
|
-
|
|
145
109
|
// get single node property
|
|
146
110
|
prop(name) {
|
|
147
111
|
let v = this.oldRoot[name] || this.props[name]
|
|
@@ -178,80 +142,92 @@ export default class FezBase {
|
|
|
178
142
|
}
|
|
179
143
|
}
|
|
180
144
|
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
145
|
+
// clear all node references
|
|
146
|
+
// Centralized destroy logic
|
|
147
|
+
fezOnDestroy() {
|
|
148
|
+
// Execute all registered cleanup callbacks
|
|
149
|
+
if (this._onDestroyCallbacks) {
|
|
150
|
+
this._onDestroyCallbacks.forEach(callback => {
|
|
151
|
+
try {
|
|
152
|
+
callback();
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.error('Fez: Error in cleanup callback:', e);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
this._onDestroyCallbacks = [];
|
|
191
158
|
}
|
|
192
159
|
|
|
193
|
-
|
|
194
|
-
|
|
160
|
+
// Call user's onDestroy lifecycle hook
|
|
161
|
+
this.onDestroy()
|
|
162
|
+
this.onDestroy = () => {}
|
|
163
|
+
|
|
164
|
+
// Clean up root references
|
|
165
|
+
if (this.root) {
|
|
166
|
+
this.root.fez = undefined
|
|
195
167
|
}
|
|
196
168
|
|
|
197
|
-
|
|
169
|
+
this.root = undefined
|
|
170
|
+
}
|
|
198
171
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
if (this._timeouts[eventName]) {
|
|
206
|
-
clearTimeout(this._timeouts[eventName]);
|
|
207
|
-
delete this._timeouts[eventName];
|
|
208
|
-
}
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
func.call(this);
|
|
212
|
-
return true;
|
|
213
|
-
};
|
|
172
|
+
// Add a cleanup callback to be executed on destroy
|
|
173
|
+
addOnDestroy(callback) {
|
|
174
|
+
this._onDestroyCallbacks = this._onDestroyCallbacks || [];
|
|
175
|
+
this._onDestroyCallbacks.push(callback);
|
|
176
|
+
}
|
|
214
177
|
|
|
215
|
-
|
|
216
|
-
|
|
178
|
+
// Generic function to handle window events with automatic cleanup
|
|
179
|
+
on(eventName, func, delay = 200) {
|
|
180
|
+
this._eventHandlers = this._eventHandlers || {};
|
|
217
181
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
} else {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
182
|
+
if (this._eventHandlers[eventName]) {
|
|
183
|
+
window.removeEventListener(eventName, this._eventHandlers[eventName]);
|
|
184
|
+
}
|
|
225
185
|
|
|
226
|
-
|
|
227
|
-
if (this.
|
|
228
|
-
|
|
186
|
+
const throttledFunc = Fez.throttle(() => {
|
|
187
|
+
if (this.isConnected) {
|
|
188
|
+
func.call(this);
|
|
229
189
|
}
|
|
190
|
+
}, delay);
|
|
230
191
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
lastRun = Date.now();
|
|
234
|
-
}
|
|
235
|
-
delete this._timeouts[eventName];
|
|
236
|
-
}, delay);
|
|
237
|
-
};
|
|
192
|
+
this._eventHandlers[eventName] = throttledFunc;
|
|
193
|
+
window.addEventListener(eventName, throttledFunc);
|
|
238
194
|
|
|
239
|
-
this.
|
|
240
|
-
|
|
195
|
+
this.addOnDestroy(() => {
|
|
196
|
+
window.removeEventListener(eventName, throttledFunc);
|
|
197
|
+
delete this._eventHandlers[eventName];
|
|
198
|
+
});
|
|
241
199
|
}
|
|
242
200
|
|
|
243
201
|
// Helper function for resize events
|
|
244
|
-
|
|
202
|
+
onWindowResize(func, delay) {
|
|
245
203
|
this.on('resize', func, delay);
|
|
246
204
|
func();
|
|
247
205
|
}
|
|
248
206
|
|
|
249
207
|
// Helper function for scroll events
|
|
250
|
-
|
|
208
|
+
onWindowScroll(func, delay) {
|
|
251
209
|
this.on('scroll', func, delay);
|
|
252
210
|
func();
|
|
253
211
|
}
|
|
254
212
|
|
|
213
|
+
// Helper function for element resize events using ResizeObserver
|
|
214
|
+
onElementResize(el, func, delay = 200) {
|
|
215
|
+
const throttledFunc = Fez.throttle(() => {
|
|
216
|
+
if (this.isConnected) {
|
|
217
|
+
func.call(this, el.getBoundingClientRect(), el);
|
|
218
|
+
}
|
|
219
|
+
}, delay);
|
|
220
|
+
|
|
221
|
+
const observer = new ResizeObserver(throttledFunc);
|
|
222
|
+
observer.observe(el);
|
|
223
|
+
|
|
224
|
+
func.call(this, el.getBoundingClientRect(), el);
|
|
225
|
+
|
|
226
|
+
this.addOnDestroy(() => {
|
|
227
|
+
observer.disconnect();
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
255
231
|
// copy child nodes, natively to preserve bound events
|
|
256
232
|
// if node name is SLOT insert adjacent and remove SLOT, else as a child nodes
|
|
257
233
|
slot(source, target) {
|
|
@@ -286,14 +262,48 @@ export default class FezBase {
|
|
|
286
262
|
onDestroy() {}
|
|
287
263
|
onStateChange() {}
|
|
288
264
|
onGlobalStateChange() {}
|
|
289
|
-
|
|
265
|
+
|
|
266
|
+
// component publish will search for parent component that subscribes by name
|
|
267
|
+
publish(channel, ...args) {
|
|
268
|
+
const handle_publish = (component) => {
|
|
269
|
+
if (Fez._subs && Fez._subs[channel]) {
|
|
270
|
+
const sub = Fez._subs[channel].find(([comp]) => comp === component)
|
|
271
|
+
if (sub) {
|
|
272
|
+
sub[1].bind(component)(...args)
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return false
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Check if current component has subscription
|
|
280
|
+
if (handle_publish(this)) {
|
|
281
|
+
return true
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Bubble up to parent components
|
|
285
|
+
let parent = this.root.parentElement
|
|
286
|
+
while (parent) {
|
|
287
|
+
if (parent.fez) {
|
|
288
|
+
if (handle_publish(parent.fez)) {
|
|
289
|
+
return true
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
parent = parent.parentElement
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// If no parent handled it, fall back to global publish
|
|
296
|
+
// Fez.publish(channel, ...args)
|
|
297
|
+
return false
|
|
298
|
+
}
|
|
299
|
+
|
|
290
300
|
fezBlocks = {}
|
|
291
301
|
|
|
292
302
|
parseHtml(text) {
|
|
293
303
|
const base = this.fezHtmlRoot.replaceAll('"', '"')
|
|
294
304
|
|
|
295
305
|
text = text
|
|
296
|
-
.replace(/(['"\s;])fez
|
|
306
|
+
.replace(/([!'"\s;])fez\.(\w)/g, `$1${base}$2`)
|
|
297
307
|
.replace(/>\s+</g, '><')
|
|
298
308
|
|
|
299
309
|
return text.trim()
|
|
@@ -325,7 +335,8 @@ export default class FezBase {
|
|
|
325
335
|
|
|
326
336
|
this.beforeRender()
|
|
327
337
|
|
|
328
|
-
const
|
|
338
|
+
const nodeName = typeof this.class.nodeName == 'function' ? this.class.nodeName(this.root) : this.class.nodeName
|
|
339
|
+
const newNode = document.createElement(nodeName || 'div')
|
|
329
340
|
|
|
330
341
|
let renderedTpl
|
|
331
342
|
if (Array.isArray(template)) {
|
|
@@ -348,22 +359,18 @@ export default class FezBase {
|
|
|
348
359
|
newNode.innerHTML = this.parseHtml(renderedTpl)
|
|
349
360
|
}
|
|
350
361
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
this.slot(this.root, slot.parentNode)
|
|
355
|
-
slot.parentNode.removeChild(slot)
|
|
356
|
-
}
|
|
362
|
+
newNode.querySelectorAll('[fez-keep]').forEach(newEl => {
|
|
363
|
+
const key = newEl.getAttribute('fez-keep')
|
|
364
|
+
const oldEl = this.root.querySelector(`[fez-keep="${key}"]`)
|
|
357
365
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
this.slot(this.root, newSlot)
|
|
366
|
+
if (oldEl) {
|
|
367
|
+
// Keep the old element in place of the new one
|
|
368
|
+
newEl.parentNode.replaceChild(oldEl, newEl)
|
|
369
|
+
} else if (key === 'default-slot') {
|
|
370
|
+
// First render - populate the slot with current root children
|
|
371
|
+
Array.from(this.root.childNodes).forEach(child => newEl.appendChild(child))
|
|
365
372
|
}
|
|
366
|
-
}
|
|
373
|
+
})
|
|
367
374
|
|
|
368
375
|
Fez.morphdom(this.root, newNode)
|
|
369
376
|
|
|
@@ -437,8 +444,7 @@ export default class FezBase {
|
|
|
437
444
|
refresh(selector) {
|
|
438
445
|
alert('NEEDS FIX and remove htmlTemplate')
|
|
439
446
|
if (selector) {
|
|
440
|
-
const n =
|
|
441
|
-
n.innerHTML = this.class.htmlTemplate
|
|
447
|
+
const n = Fez.domRoot(this.class.htmlTemplate)
|
|
442
448
|
const tpl = n.querySelector(selector).innerHTML
|
|
443
449
|
this.render(selector, tpl)
|
|
444
450
|
} else {
|
|
@@ -457,13 +463,21 @@ export default class FezBase {
|
|
|
457
463
|
this._setIntervalCache ||= {}
|
|
458
464
|
clearInterval(this._setIntervalCache[name])
|
|
459
465
|
|
|
460
|
-
|
|
466
|
+
const intervalID = setInterval(() => {
|
|
461
467
|
if (this.isConnected) {
|
|
462
468
|
func()
|
|
463
469
|
}
|
|
464
470
|
}, tick)
|
|
465
471
|
|
|
466
|
-
|
|
472
|
+
this._setIntervalCache[name] = intervalID
|
|
473
|
+
|
|
474
|
+
// Register cleanup callback
|
|
475
|
+
this.addOnDestroy(() => {
|
|
476
|
+
clearInterval(intervalID);
|
|
477
|
+
delete this._setIntervalCache[name];
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
return intervalID
|
|
467
481
|
}
|
|
468
482
|
|
|
469
483
|
find(selector) {
|
|
@@ -511,7 +525,7 @@ export default class FezBase {
|
|
|
511
525
|
|
|
512
526
|
// get root node child nodes as array
|
|
513
527
|
childNodes(func) {
|
|
514
|
-
|
|
528
|
+
let children = Array.from(this.root.children)
|
|
515
529
|
|
|
516
530
|
if (func) {
|
|
517
531
|
// Create temporary container to avoid ancestor-parent errors
|
|
@@ -520,12 +534,11 @@ export default class FezBase {
|
|
|
520
534
|
document.body.appendChild(tmpContainer)
|
|
521
535
|
children.forEach(child => tmpContainer.appendChild(child))
|
|
522
536
|
|
|
523
|
-
|
|
537
|
+
children = Array.from(tmpContainer.children).map(func)
|
|
524
538
|
document.body.removeChild(tmpContainer)
|
|
525
|
-
return list
|
|
526
|
-
} else {
|
|
527
|
-
return children
|
|
528
539
|
}
|
|
540
|
+
|
|
541
|
+
return children
|
|
529
542
|
}
|
|
530
543
|
|
|
531
544
|
subscribe(channel, func) {
|
|
@@ -565,19 +578,14 @@ export default class FezBase {
|
|
|
565
578
|
|
|
566
579
|
fezHide() {
|
|
567
580
|
const node = this.root
|
|
581
|
+
const nodes = this.childNodes()
|
|
568
582
|
const parent = this.root.parentNode
|
|
569
|
-
const fragment = document.createDocumentFragment();
|
|
570
583
|
|
|
571
|
-
|
|
572
|
-
fragment.appendChild(node.firstChild)
|
|
573
|
-
}
|
|
584
|
+
nodes.reverse().forEach(el => parent.insertBefore(el, node.nextSibling))
|
|
574
585
|
|
|
575
|
-
|
|
576
|
-
node.parentNode.replaceChild(fragment, node);
|
|
577
|
-
// parent.classList.add('fez')
|
|
578
|
-
// parent.classList.add(`fez-${this.fezName}`)
|
|
586
|
+
this.root.remove()
|
|
579
587
|
this.root = parent
|
|
580
|
-
return
|
|
588
|
+
return nodes
|
|
581
589
|
}
|
|
582
590
|
|
|
583
591
|
reactiveStore(obj, handler) {
|
package/src/fez/lib/template.js
CHANGED
package/src/fez/root.js
CHANGED
|
@@ -117,7 +117,7 @@ Fez.globalCss = (cssClass, opts = {}) => {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
Fez.info = () => {
|
|
120
|
-
console.log(
|
|
120
|
+
console.log('Fez components:', Object.keys(Fez.classes || {}))
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
Fez.morphdom = (target, newNode, opts = {}) => {
|
|
@@ -136,24 +136,6 @@ Fez.morphdom = (target, newNode, opts = {}) => {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
Fez.htmlEscape = (text) => {
|
|
140
|
-
if (typeof text == 'string') {
|
|
141
|
-
text = text
|
|
142
|
-
// .replaceAll('&', "&")
|
|
143
|
-
.replace(/font-family\s*:\s*(?:&[^;]+;|[^;])*?;/gi, '')
|
|
144
|
-
.replaceAll("&", '&')
|
|
145
|
-
.replaceAll("'", ''')
|
|
146
|
-
.replaceAll('"', '"')
|
|
147
|
-
.replaceAll('<', '<')
|
|
148
|
-
.replaceAll('>', '>')
|
|
149
|
-
// .replaceAll('@', '@') // needed for template escaping
|
|
150
|
-
|
|
151
|
-
return text
|
|
152
|
-
} else {
|
|
153
|
-
return text === undefined ? '' : text
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
139
|
Fez.publish = (channel, ...args) => {
|
|
158
140
|
Fez._subs ||= {}
|
|
159
141
|
Fez._subs[channel] ||= []
|
|
@@ -183,6 +165,19 @@ Fez.tag = (tag, opts = {}, html = '') => {
|
|
|
183
165
|
// return data
|
|
184
166
|
};
|
|
185
167
|
|
|
168
|
+
// Resolve a function from a string or function reference
|
|
169
|
+
Fez.getFunction = (pointer) => {
|
|
170
|
+
if (!pointer) {
|
|
171
|
+
return ()=>{}
|
|
172
|
+
}
|
|
173
|
+
else if (typeof pointer === 'function') {
|
|
174
|
+
return pointer;
|
|
175
|
+
}
|
|
176
|
+
else if (typeof pointer === 'string') {
|
|
177
|
+
return new Function(pointer);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
186
181
|
Fez.error = (text, show) => {
|
|
187
182
|
text = `Fez: ${text}`
|
|
188
183
|
console.error(text)
|
|
@@ -211,125 +206,25 @@ Fez.untilTrue = (func, pingRate) => {
|
|
|
211
206
|
}
|
|
212
207
|
}
|
|
213
208
|
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// Script with callback
|
|
219
|
-
// Fez.head({ js: 'https://example.com/script.js' }, () => { console.log('loaded') });
|
|
220
|
-
// Module loading with auto-import to window
|
|
221
|
-
// Fez.head({ js: 'https://example.com/module.js', module: 'MyModule' }); // imports and sets window.MyModule
|
|
222
|
-
// CSS inclusion
|
|
223
|
-
// Fez.head({ css: 'https://example.com/styles.css' });
|
|
224
|
-
// CSS with additional attributes and callback
|
|
225
|
-
// Fez.head({ css: 'https://example.com/styles.css', media: 'print' }, () => { console.log('CSS loaded') })
|
|
226
|
-
// Inline script evaluation
|
|
227
|
-
// Fez.head({ script: 'console.log("Hello world")' })
|
|
228
|
-
// Extract from nodes
|
|
229
|
-
// Fez.head(domNode)
|
|
230
|
-
Fez.head = (config, callback) => {
|
|
231
|
-
if (config.nodeName) {
|
|
232
|
-
if (config.nodeName == 'SCRIPT') {
|
|
233
|
-
Fez.head({script: config.innerText})
|
|
234
|
-
config.remove()
|
|
235
|
-
} else {
|
|
236
|
-
config.querySelectorAll('script').forEach((n) => Fez.head(n) )
|
|
237
|
-
config.querySelectorAll('template[fez], xmp[fez], script[fez]').forEach((n) => Fez.compile(n) )
|
|
238
|
-
}
|
|
209
|
+
// throttle function calls
|
|
210
|
+
Fez.throttle = (func, delay = 200) => {
|
|
211
|
+
let lastRun = 0;
|
|
212
|
+
let timeout;
|
|
239
213
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (typeof config !== 'object' || config === null) {
|
|
244
|
-
throw new Error('head requires an object parameter');
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
let src, attributes = {}, elementType;
|
|
248
|
-
|
|
249
|
-
if (config.script) {
|
|
250
|
-
if (config.script.includes('import ')) {
|
|
251
|
-
if (callback) {
|
|
252
|
-
Fez.error('Fez.head callback is not supported when script with import is passed (module context).')
|
|
253
|
-
}
|
|
214
|
+
return function(...args) {
|
|
215
|
+
const now = Date.now();
|
|
254
216
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
script.textContent = config.script;
|
|
259
|
-
document.head.appendChild(script);
|
|
260
|
-
setTimeout(()=>script.remove(), 100)
|
|
217
|
+
if (now - lastRun >= delay) {
|
|
218
|
+
func.apply(this, args);
|
|
219
|
+
lastRun = now;
|
|
261
220
|
} else {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
console.log(config.script);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return;
|
|
271
|
-
} else if (config.js) {
|
|
272
|
-
src = config.js;
|
|
273
|
-
elementType = 'script';
|
|
274
|
-
// Copy all properties except 'js' as attributes
|
|
275
|
-
for (const [key, value] of Object.entries(config)) {
|
|
276
|
-
if (key !== 'js' && key !== 'module') {
|
|
277
|
-
attributes[key] = value;
|
|
278
|
-
}
|
|
221
|
+
clearTimeout(timeout);
|
|
222
|
+
timeout = setTimeout(() => {
|
|
223
|
+
func.apply(this, args);
|
|
224
|
+
lastRun = Date.now();
|
|
225
|
+
}, delay - (now - lastRun));
|
|
279
226
|
}
|
|
280
|
-
|
|
281
|
-
if (config.module) {
|
|
282
|
-
attributes.type = 'module';
|
|
283
|
-
}
|
|
284
|
-
} else if (config.css) {
|
|
285
|
-
src = config.css;
|
|
286
|
-
elementType = 'link';
|
|
287
|
-
attributes.rel = 'stylesheet';
|
|
288
|
-
// Copy all properties except 'css' as attributes
|
|
289
|
-
for (const [key, value] of Object.entries(config)) {
|
|
290
|
-
if (key !== 'css') {
|
|
291
|
-
attributes[key] = value;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
throw new Error('head requires either "script", "js" or "css" property');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const existingNode = document.querySelector(`${elementType}[src="${src}"], ${elementType}[href="${src}"]`);
|
|
299
|
-
if (existingNode) {
|
|
300
|
-
if (callback) callback();
|
|
301
|
-
return existingNode;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const element = document.createElement(elementType);
|
|
305
|
-
|
|
306
|
-
if (elementType === 'link') {
|
|
307
|
-
element.href = src;
|
|
308
|
-
} else {
|
|
309
|
-
element.src = src;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
313
|
-
element.setAttribute(key, value);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (callback || config.module) {
|
|
317
|
-
element.onload = () => {
|
|
318
|
-
// If module name is provided, import it and assign to window
|
|
319
|
-
if (config.module && elementType === 'script') {
|
|
320
|
-
import(src).then(module => {
|
|
321
|
-
window[config.module] = module.default || module[config.module] || module;
|
|
322
|
-
}).catch(error => {
|
|
323
|
-
console.error(`Error importing module ${config.module}:`, error);
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
if (callback) callback();
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
document.head.appendChild(element);
|
|
331
|
-
|
|
332
|
-
return element;
|
|
227
|
+
};
|
|
333
228
|
}
|
|
334
229
|
|
|
335
230
|
// Fetch wrapper with automatic caching and data handling
|
|
@@ -475,6 +370,10 @@ Fez.store = {
|
|
|
475
370
|
}
|
|
476
371
|
};
|
|
477
372
|
|
|
373
|
+
// Load utility functions
|
|
374
|
+
import addUtilities from './utility.js'
|
|
375
|
+
addUtilities(Fez)
|
|
376
|
+
|
|
478
377
|
Fez.compile = compile
|
|
479
378
|
Fez.state = state
|
|
480
379
|
|