@dinoreic/fez 0.3.2 → 0.4.1
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 +35 -8
- package/dist/fez.js +44 -21
- package/dist/fez.js.map +4 -4
- package/package.json +2 -2
- package/src/fez/compile.js +4 -7
- package/src/fez/connect.js +54 -77
- package/src/fez/defaults.js +11 -0
- package/src/fez/instance.js +68 -19
- package/src/fez/root.js +57 -170
- package/src/fez/utility.js +198 -2
- package/src/fez/utils/css_mixin.js +25 -0
- package/src/fez/utils/dump.js +44 -15
- package/src/fez/utils/highlight_all.js +98 -0
- package/src/rollup.js +1 -1
- package/src/svelte-cde-adapter.coffee +123 -0
package/package.json
CHANGED
package/src/fez/compile.js
CHANGED
|
@@ -70,10 +70,7 @@ const compileToClass = (html) => {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (String(result.style).includes(':')) {
|
|
73
|
-
|
|
74
|
-
result.style = result.style.replaceAll(`:${key} `, `${val} `)
|
|
75
|
-
})
|
|
76
|
-
|
|
73
|
+
result.style = Fez.cssMixin(result.style)
|
|
77
74
|
result.style = result.style.includes(':fez') || /(?:^|\s)body\s*\{/.test(result.style) ? result.style : `:fez {\n${result.style}\n}`
|
|
78
75
|
klass = klass.replace(/\}\s*$/, `\n CSS = \`${result.style}\`\n}`)
|
|
79
76
|
}
|
|
@@ -105,8 +102,8 @@ function compile_bulk(data) {
|
|
|
105
102
|
if (fezName && !fezName.includes('-')) {
|
|
106
103
|
console.error(`Fez: Invalid custom element name "${fezName}". Custom element names must contain a dash (e.g., 'my-element', 'ui-button').`)
|
|
107
104
|
}
|
|
108
|
-
|
|
109
|
-
return
|
|
105
|
+
compile(fezName, node.innerHTML)
|
|
106
|
+
return
|
|
110
107
|
}
|
|
111
108
|
}
|
|
112
109
|
else {
|
|
@@ -195,7 +192,7 @@ function compile(tagName, html) {
|
|
|
195
192
|
if (klass.includes('import ')) {
|
|
196
193
|
Fez.head({script: klass})
|
|
197
194
|
|
|
198
|
-
// best we can do it inform that node did not compile, so we assume there is
|
|
195
|
+
// best we can do it inform that node did not compile, so we assume there is an error
|
|
199
196
|
setTimeout(()=>{
|
|
200
197
|
if (!Fez.classes[tagName]) {
|
|
201
198
|
Fez.error(`Template "${tagName}" possible compile error. (can be a false positive, it imports are not loaded)`)
|
package/src/fez/connect.js
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import createTemplate from './lib/template.js'
|
|
2
2
|
import FezBase from './instance.js'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Global mutation observer for reactive attribute changes
|
|
6
|
+
* Watches for attribute changes and triggers component updates
|
|
7
|
+
*/
|
|
8
|
+
const observer = new MutationObserver((mutationsList, _) => {
|
|
9
|
+
for (const mutation of mutationsList) {
|
|
10
|
+
if (mutation.type === 'attributes') {
|
|
11
|
+
const fez = mutation.target.fez
|
|
12
|
+
const name = mutation.attributeName
|
|
13
|
+
const value = mutation.target.getAttribute(name)
|
|
14
|
+
|
|
15
|
+
if (fez) {
|
|
16
|
+
fez.props[name] = value
|
|
17
|
+
fez.onPropsChange(name, value)
|
|
18
|
+
// console.log(`The [${name}] attribute was modified to [${value}].`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
4
24
|
/**
|
|
5
25
|
* Registers a new custom element with Fez framework
|
|
6
26
|
* @param {string} name - Custom element name (must contain a dash)
|
|
@@ -31,22 +51,17 @@ export default function connect(name, klass) {
|
|
|
31
51
|
props.forEach(prop => newKlass.prototype[prop] = klassObj[prop])
|
|
32
52
|
|
|
33
53
|
// Map component configuration properties
|
|
34
|
-
if (klassObj.
|
|
35
|
-
if (klassObj.
|
|
54
|
+
if (klassObj.FAST) { newKlass.FAST = klassObj.FAST } // Global instance reference
|
|
55
|
+
if (klassObj.GLOBAL) { newKlass.GLOBAL = klassObj.GLOBAL } // Global instance reference
|
|
56
|
+
if (klassObj.CSS) { newKlass.css = klassObj.CSS } // Component styles
|
|
36
57
|
if (klassObj.HTML) {
|
|
37
|
-
newKlass.html = closeCustomTags(klassObj.HTML)
|
|
58
|
+
newKlass.html = closeCustomTags(klassObj.HTML) // Component template
|
|
38
59
|
}
|
|
39
|
-
if (klassObj.NAME) { newKlass.nodeName = klassObj.NAME }
|
|
60
|
+
if (klassObj.NAME) { newKlass.nodeName = klassObj.NAME } // Custom DOM node name
|
|
40
61
|
|
|
41
62
|
// Auto-mount global components to body
|
|
42
63
|
if (klassObj.GLOBAL) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (document.readyState === 'loading') {
|
|
46
|
-
document.addEventListener('DOMContentLoaded', mountGlobalComponent);
|
|
47
|
-
} else {
|
|
48
|
-
mountGlobalComponent()
|
|
49
|
-
}
|
|
64
|
+
document.body.appendChild(document.createElement(name))
|
|
50
65
|
}
|
|
51
66
|
|
|
52
67
|
klass = newKlass
|
|
@@ -59,11 +74,11 @@ export default function connect(name, klass) {
|
|
|
59
74
|
|
|
60
75
|
// Process component template
|
|
61
76
|
if (klass.html) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
let slotTag = klass.SLOT || 'div'
|
|
78
|
+
|
|
79
|
+
klass.html = klass.html
|
|
80
|
+
.replace('<slot', `<${slotTag} class="fez-slot" fez-keep="default-slot"`)
|
|
81
|
+
.replace('</slot>', `</${slotTag}>`)
|
|
67
82
|
|
|
68
83
|
// Compile template function
|
|
69
84
|
klass.fezHtmlFunc = createTemplate(klass.html)
|
|
@@ -76,47 +91,16 @@ export default function connect(name, klass) {
|
|
|
76
91
|
|
|
77
92
|
Fez.classes[name] = klass
|
|
78
93
|
|
|
79
|
-
connectCustomElement(name, klass)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Registers the custom element with the browser
|
|
84
|
-
* Sets up batched rendering for optimal performance
|
|
85
|
-
*/
|
|
86
|
-
function connectCustomElement(name, klass) {
|
|
87
|
-
const Fez = globalThis.window?.Fez || globalThis.Fez;
|
|
88
|
-
|
|
89
94
|
if (!customElements.get(name)) {
|
|
90
95
|
customElements.define(name, class extends HTMLElement {
|
|
91
96
|
connectedCallback() {
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!Fez._batchScheduled) {
|
|
101
|
-
Fez._batchScheduled = true
|
|
102
|
-
Promise.resolve().then(() => {
|
|
103
|
-
const connections = Fez._pendingConnections.slice()
|
|
104
|
-
// console.error(`Batch processing ${connections.length} components:`, connections.map(c => c.name))
|
|
105
|
-
Fez._pendingConnections = []
|
|
106
|
-
Fez._batchScheduled = false
|
|
107
|
-
|
|
108
|
-
// Sort by DOM order to ensure parent nodes are processed before children
|
|
109
|
-
connections.sort((a, b) => {
|
|
110
|
-
if (a.node.contains(b.node)) return -1
|
|
111
|
-
if (b.node.contains(a.node)) return 1
|
|
112
|
-
return 0
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
connections.forEach(({ name, node }) => {
|
|
116
|
-
if (node.isConnected && node.parentNode) {
|
|
117
|
-
connectNode(name, node)
|
|
118
|
-
}
|
|
119
|
-
})
|
|
97
|
+
// Fez.onReady(()=>{connectNode(name, this)})
|
|
98
|
+
// connectNode(name, this)
|
|
99
|
+
if (useFastRender(this, klass)) {
|
|
100
|
+
connectNode(name, this)
|
|
101
|
+
} else {
|
|
102
|
+
requestAnimationFrame(()=>{
|
|
103
|
+
connectNode(name, this)
|
|
120
104
|
})
|
|
121
105
|
}
|
|
122
106
|
}
|
|
@@ -124,6 +108,20 @@ function connectCustomElement(name, klass) {
|
|
|
124
108
|
}
|
|
125
109
|
}
|
|
126
110
|
|
|
111
|
+
function useFastRender(node, klass) {
|
|
112
|
+
const fezFast = node.getAttribute('fez-fast')
|
|
113
|
+
var isFast = typeof klass.FAST === 'function' ? klass.FAST(node) : klass.FAST
|
|
114
|
+
if (fezFast || isFast || node.childNodes[0] || node.nextSibling) {
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
else if (fezFast == 'false') {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
127
125
|
/**
|
|
128
126
|
* Converts self-closing custom tags to full open/close format
|
|
129
127
|
* Required for proper HTML parsing of custom elements
|
|
@@ -138,7 +136,6 @@ function closeCustomTags(html) {
|
|
|
138
136
|
})
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
|
|
142
139
|
/**
|
|
143
140
|
* Initializes a Fez component instance from a DOM node
|
|
144
141
|
* Replaces the custom element with the component's rendered content
|
|
@@ -172,8 +169,8 @@ function connectNode(name, node) {
|
|
|
172
169
|
|
|
173
170
|
newNode.fez = fez
|
|
174
171
|
|
|
175
|
-
if (klass.
|
|
176
|
-
window[klass.
|
|
172
|
+
if (klass.GLOBAL && klass.GLOBAL != true) {
|
|
173
|
+
window[klass.GLOBAL] = fez
|
|
177
174
|
}
|
|
178
175
|
|
|
179
176
|
if (window.$) {
|
|
@@ -216,23 +213,3 @@ function connectNode(name, node) {
|
|
|
216
213
|
}
|
|
217
214
|
}
|
|
218
215
|
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Global mutation observer for reactive attribute changes
|
|
222
|
-
* Watches for attribute changes and triggers component updates
|
|
223
|
-
*/
|
|
224
|
-
const observer = new MutationObserver((mutationsList, _) => {
|
|
225
|
-
for (const mutation of mutationsList) {
|
|
226
|
-
if (mutation.type === 'attributes') {
|
|
227
|
-
const fez = mutation.target.fez
|
|
228
|
-
const name = mutation.attributeName
|
|
229
|
-
const value = mutation.target.getAttribute(name)
|
|
230
|
-
|
|
231
|
-
if (fez) {
|
|
232
|
-
fez.props[name] = value
|
|
233
|
-
fez.onPropsChange(name, value)
|
|
234
|
-
// console.log(`The [${name}] attribute was modified to [${value}].`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
});
|
package/src/fez/defaults.js
CHANGED
|
@@ -54,6 +54,17 @@ const loadDefaults = () => {
|
|
|
54
54
|
}
|
|
55
55
|
})
|
|
56
56
|
|
|
57
|
+
// Show node only if test validates
|
|
58
|
+
// <fez-if if="window.foo">...
|
|
59
|
+
Fez('fez-if', class {
|
|
60
|
+
init(props) {
|
|
61
|
+
const test = new Function(`return (${props.if || props.test})`)
|
|
62
|
+
if (!test()) {
|
|
63
|
+
this.root.remove()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
57
68
|
// Memory store for memoization
|
|
58
69
|
const memoStore = new Map()
|
|
59
70
|
|
package/src/fez/instance.js
CHANGED
|
@@ -130,13 +130,11 @@ export default class FezBase {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this.root[name] = value
|
|
139
|
-
}
|
|
133
|
+
if (typeof value == 'string') {
|
|
134
|
+
this.root.setAttribute(name, value)
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.root[name] = value
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
140
|
}
|
|
@@ -390,11 +388,24 @@ export default class FezBase {
|
|
|
390
388
|
|
|
391
389
|
// <button fez-use="animate" -> this.animate(node]
|
|
392
390
|
fetchAttr('fez-use', (value, n) => {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
391
|
+
if (value.includes('=>')) {
|
|
392
|
+
// fez-use="el => el.focus()"
|
|
393
|
+
Fez.getFunction(value)(n)
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
if (value.includes('.')) {
|
|
397
|
+
// fez-use="this.focus()"
|
|
398
|
+
Fez.getFunction(value).bind(n)()
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
// fez-use="animate"
|
|
402
|
+
const target = this[value]
|
|
403
|
+
if (typeof target == 'function') {
|
|
404
|
+
target(n)
|
|
405
|
+
} else {
|
|
406
|
+
console.error(`Fez error: "${value}" is not a function in ${this.fezName}`)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
398
409
|
}
|
|
399
410
|
})
|
|
400
411
|
|
|
@@ -406,7 +417,7 @@ export default class FezBase {
|
|
|
406
417
|
if (lastClass) {
|
|
407
418
|
setTimeout(()=>{
|
|
408
419
|
n.classList.add(lastClass)
|
|
409
|
-
},
|
|
420
|
+
}, 1)
|
|
410
421
|
}
|
|
411
422
|
})
|
|
412
423
|
|
|
@@ -442,8 +453,28 @@ export default class FezBase {
|
|
|
442
453
|
// Keep the old element in place of the new one
|
|
443
454
|
newEl.parentNode.replaceChild(oldEl, newEl)
|
|
444
455
|
} else if (key === 'default-slot') {
|
|
445
|
-
|
|
446
|
-
|
|
456
|
+
if (newEl.getAttribute('hide')) {
|
|
457
|
+
// You cant use state any more
|
|
458
|
+
this.state = null
|
|
459
|
+
|
|
460
|
+
const parent = newEl.parentNode
|
|
461
|
+
|
|
462
|
+
// Insert all root children before the slot's next sibling
|
|
463
|
+
Array.from(this.root.childNodes).forEach(child => {
|
|
464
|
+
parent.insertBefore(child, newEl)
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
// Remove the slot element
|
|
468
|
+
newEl.remove()
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
// First render - populate the slot with current root children
|
|
472
|
+
Array.from(this.root.childNodes).forEach(
|
|
473
|
+
child => {
|
|
474
|
+
newEl.appendChild(child)
|
|
475
|
+
}
|
|
476
|
+
)
|
|
477
|
+
}
|
|
447
478
|
}
|
|
448
479
|
})
|
|
449
480
|
}
|
|
@@ -606,7 +637,18 @@ export default class FezBase {
|
|
|
606
637
|
methods.forEach(method => this[method] = this[method].bind(this))
|
|
607
638
|
}
|
|
608
639
|
|
|
609
|
-
|
|
640
|
+
// dissolve into parent, if you want to promote first child or given node with this.root
|
|
641
|
+
dissolve(inNode) {
|
|
642
|
+
if (inNode) {
|
|
643
|
+
inNode.classList.add('fez')
|
|
644
|
+
inNode.classList.add(`fez-${this.fezName}`)
|
|
645
|
+
inNode.fez = this
|
|
646
|
+
if (this.attr('id')) inNode.setAttribute('id', this.attr('id'))
|
|
647
|
+
|
|
648
|
+
this.root.innerHTML = ''
|
|
649
|
+
this.root.appendChild(inNode)
|
|
650
|
+
}
|
|
651
|
+
|
|
610
652
|
const node = this.root
|
|
611
653
|
const nodes = this.childNodes()
|
|
612
654
|
const parent = this.root.parentNode
|
|
@@ -614,7 +656,12 @@ export default class FezBase {
|
|
|
614
656
|
nodes.reverse().forEach(el => parent.insertBefore(el, node.nextSibling))
|
|
615
657
|
|
|
616
658
|
this.root.remove()
|
|
617
|
-
this.root =
|
|
659
|
+
this.root = undefined
|
|
660
|
+
|
|
661
|
+
if (inNode) {
|
|
662
|
+
this.root = inNode
|
|
663
|
+
}
|
|
664
|
+
|
|
618
665
|
return nodes
|
|
619
666
|
}
|
|
620
667
|
|
|
@@ -622,8 +669,10 @@ export default class FezBase {
|
|
|
622
669
|
obj ||= {}
|
|
623
670
|
|
|
624
671
|
handler ||= (o, k, v, oldValue) => {
|
|
625
|
-
|
|
626
|
-
|
|
672
|
+
if (v != oldValue) {
|
|
673
|
+
this.onStateChange(k, v, oldValue)
|
|
674
|
+
this.nextTick(this.render, 'render')
|
|
675
|
+
}
|
|
627
676
|
}
|
|
628
677
|
|
|
629
678
|
handler.bind(this)
|
package/src/fez/root.js
CHANGED
|
@@ -5,6 +5,7 @@ import Gobber from './vendor/gobber.js'
|
|
|
5
5
|
import { Idiomorph } from './vendor/idiomorph.js'
|
|
6
6
|
|
|
7
7
|
import objectDump from './utils/dump.js'
|
|
8
|
+
import highlightAll from './utils/highlight_all.js'
|
|
8
9
|
import connect from './connect.js'
|
|
9
10
|
import compile from './compile.js'
|
|
10
11
|
import state from './lib/global-state.js'
|
|
@@ -106,13 +107,9 @@ Fez.globalCss = (cssClass, opts = {}) => {
|
|
|
106
107
|
cssClass = Fez.cssClass(text)
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
Fez.onReady(() => {
|
|
110
111
|
document.body.parentElement.classList.add(cssClass)
|
|
111
|
-
}
|
|
112
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
113
|
-
document.body.parentElement.classList.add(cssClass)
|
|
114
|
-
})
|
|
115
|
-
}
|
|
112
|
+
})
|
|
116
113
|
|
|
117
114
|
return cssClass
|
|
118
115
|
}
|
|
@@ -137,186 +134,77 @@ Fez.morphdom = (target, newNode, opts = {}) => {
|
|
|
137
134
|
}
|
|
138
135
|
}
|
|
139
136
|
|
|
137
|
+
Fez._globalSubs ||= new Map()
|
|
138
|
+
|
|
140
139
|
Fez.publish = (channel, ...args) => {
|
|
141
140
|
Fez._subs ||= {}
|
|
142
141
|
Fez._subs[channel] ||= []
|
|
143
142
|
Fez._subs[channel].forEach((el) => {
|
|
144
143
|
el[1].bind(el[0])(...args)
|
|
145
144
|
})
|
|
146
|
-
}
|
|
147
145
|
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
// Trigger global subscriptions
|
|
147
|
+
const subs = Fez._globalSubs.get(channel)
|
|
148
|
+
if (subs) {
|
|
149
|
+
subs.forEach((sub) => {
|
|
150
|
+
if (sub.node.isConnected) {
|
|
151
|
+
sub.callback.call(sub.node, ...args)
|
|
152
|
+
} else {
|
|
153
|
+
// Remove disconnected nodes from subscriptions
|
|
154
|
+
subs.delete(sub)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
157
|
}
|
|
158
|
-
return hash.toString(36).replaceAll('-', '');
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
Fez.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
Fez.error = (text, show) => {
|
|
170
|
-
text = `Fez: ${text}`
|
|
171
|
-
console.error(text)
|
|
172
|
-
if (show) {
|
|
173
|
-
return `<span style="border: 1px solid red; font-size: 14px; padding: 3px 7px; background: #fee; border-radius: 4px;">${text}</span>`
|
|
160
|
+
Fez.subscribe = (node, eventName, callback) => {
|
|
161
|
+
// If second arg is function, shift arguments
|
|
162
|
+
if (typeof eventName === 'function') {
|
|
163
|
+
callback = eventName
|
|
164
|
+
eventName = node
|
|
165
|
+
node = document.body
|
|
174
166
|
}
|
|
175
|
-
}
|
|
176
|
-
Fez.log = (text) => {
|
|
177
|
-
if (Fez.LOG === true) {
|
|
178
|
-
text = String(text).substring(0, 180)
|
|
179
|
-
console.log(`Fez: ${text}`)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
183
|
-
Fez.log('Fez.LOG === true, logging enabled.')
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
// execute function until it returns true
|
|
187
|
-
Fez.untilTrue = (func, pingRate) => {
|
|
188
|
-
pingRate ||= 200
|
|
189
167
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
} ,pingRate)
|
|
168
|
+
// Handle string selectors
|
|
169
|
+
if (typeof node === 'string') {
|
|
170
|
+
node = document.querySelector(node)
|
|
194
171
|
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// throttle function calls
|
|
198
|
-
Fez.throttle = (func, delay = 200) => {
|
|
199
|
-
let lastRun = 0;
|
|
200
|
-
let timeout;
|
|
201
172
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (now - lastRun >= delay) {
|
|
206
|
-
func.apply(this, args);
|
|
207
|
-
lastRun = now;
|
|
208
|
-
} else {
|
|
209
|
-
clearTimeout(timeout);
|
|
210
|
-
timeout = setTimeout(() => {
|
|
211
|
-
func.apply(this, args);
|
|
212
|
-
lastRun = Date.now();
|
|
213
|
-
}, delay - (now - lastRun));
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Fetch wrapper with automatic caching and data handling
|
|
219
|
-
// Usage:
|
|
220
|
-
// Fez.fetch(url) - GET request (default)
|
|
221
|
-
// Fez.fetch(url, callback) - GET with callback
|
|
222
|
-
// Fez.fetch(url, data) - GET with query params (?foo=bar&baz=qux)
|
|
223
|
-
// Fez.fetch(url, data, callback) - GET with query params and callback
|
|
224
|
-
// Fez.fetch('POST', url, data) - POST with FormData body (multipart/form-data)
|
|
225
|
-
// Fez.fetch('POST', url, data, callback) - POST with FormData and callback
|
|
226
|
-
// Data object is automatically converted:
|
|
227
|
-
// - GET: appended as URL query parameters
|
|
228
|
-
// - POST: sent as FormData (multipart/form-data) without custom headers
|
|
229
|
-
Fez.fetch = function(...args) {
|
|
230
|
-
// Initialize cache if not exists
|
|
231
|
-
Fez._fetchCache ||= {};
|
|
232
|
-
|
|
233
|
-
let method = 'GET';
|
|
234
|
-
let url;
|
|
235
|
-
let callback;
|
|
236
|
-
|
|
237
|
-
// Check if first arg is HTTP method (uppercase letters)
|
|
238
|
-
if (typeof args[0] === 'string' && /^[A-Z]+$/.test(args[0])) {
|
|
239
|
-
method = args.shift();
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// URL is required
|
|
243
|
-
url = args.shift();
|
|
244
|
-
|
|
245
|
-
// Check for data/options object
|
|
246
|
-
let opts = {};
|
|
247
|
-
let data = null;
|
|
248
|
-
if (typeof args[0] === 'object') {
|
|
249
|
-
data = args.shift();
|
|
173
|
+
if (!Fez._globalSubs.has(eventName)) {
|
|
174
|
+
Fez._globalSubs.set(eventName, new Set())
|
|
250
175
|
}
|
|
251
176
|
|
|
252
|
-
|
|
253
|
-
if (typeof args[0] === 'function') {
|
|
254
|
-
callback = args.shift();
|
|
255
|
-
}
|
|
177
|
+
const subs = Fez._globalSubs.get(eventName)
|
|
256
178
|
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
const params = new URLSearchParams(data);
|
|
262
|
-
url += (url.includes('?') ? '&' : '?') + params.toString();
|
|
263
|
-
} else if (method === 'POST') {
|
|
264
|
-
// For POST, convert to FormData
|
|
265
|
-
const formData = new FormData();
|
|
266
|
-
for (const [key, value] of Object.entries(data)) {
|
|
267
|
-
formData.append(key, value);
|
|
268
|
-
}
|
|
269
|
-
opts.body = formData;
|
|
179
|
+
// Remove existing subscription for same node and callback
|
|
180
|
+
subs.forEach(sub => {
|
|
181
|
+
if (sub.node === node && sub.callback === callback) {
|
|
182
|
+
subs.delete(sub)
|
|
270
183
|
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Set method
|
|
274
|
-
opts.method = method;
|
|
184
|
+
})
|
|
275
185
|
|
|
276
|
-
|
|
277
|
-
|
|
186
|
+
const subscription = { node, callback }
|
|
187
|
+
subs.add(subscription)
|
|
278
188
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
Fez.log(`fetch cache hit: ${method} ${url}`);
|
|
283
|
-
if (callback) {
|
|
284
|
-
callback(cachedData);
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
return Promise.resolve(cachedData);
|
|
189
|
+
// Return unsubscribe function
|
|
190
|
+
return () => {
|
|
191
|
+
subs.delete(subscription)
|
|
288
192
|
}
|
|
193
|
+
}
|
|
289
194
|
|
|
290
|
-
|
|
291
|
-
Fez
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (response.headers.get('content-type')?.includes('application/json')) {
|
|
296
|
-
return response.json();
|
|
297
|
-
}
|
|
298
|
-
return response.text();
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// If callback provided, execute and handle
|
|
302
|
-
if (callback) {
|
|
303
|
-
fetch(url, opts)
|
|
304
|
-
.then(processResponse)
|
|
305
|
-
.then(data => {
|
|
306
|
-
Fez._fetchCache[cacheKey] = data;
|
|
307
|
-
callback(data);
|
|
308
|
-
})
|
|
309
|
-
.catch(error => Fez.onError('fetch', error));
|
|
310
|
-
return;
|
|
195
|
+
Fez.error = (text, show) => {
|
|
196
|
+
text = `Fez: ${text}`
|
|
197
|
+
console.error(text)
|
|
198
|
+
if (show) {
|
|
199
|
+
return `<span style="border: 1px solid red; font-size: 14px; padding: 3px 7px; background: #fee; border-radius: 4px;">${text}</span>`
|
|
311
200
|
}
|
|
201
|
+
}
|
|
312
202
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
.
|
|
316
|
-
.
|
|
317
|
-
|
|
318
|
-
return data;
|
|
319
|
-
});
|
|
203
|
+
Fez.log = (text) => {
|
|
204
|
+
if (Fez.LOG === true) {
|
|
205
|
+
text = String(text).substring(0, 180)
|
|
206
|
+
console.log(`Fez: ${text}`)
|
|
207
|
+
}
|
|
320
208
|
}
|
|
321
209
|
|
|
322
210
|
Fez.onError = (kind, message) => {
|
|
@@ -328,14 +216,6 @@ Fez.onError = (kind, message) => {
|
|
|
328
216
|
console.error(`${kind}: ${message.toString()}`);
|
|
329
217
|
}
|
|
330
218
|
|
|
331
|
-
// define custom style macro
|
|
332
|
-
// Fez.styleMacro('mobile', '@media (max-width: 768px)')
|
|
333
|
-
// :mobile { ... } -> @media (max-width: 768px) { ... }
|
|
334
|
-
Fez._styleMacros = {}
|
|
335
|
-
Fez.styleMacro = (name, content) => {
|
|
336
|
-
Fez._styleMacros[name] = content
|
|
337
|
-
}
|
|
338
|
-
|
|
339
219
|
// work with tmp store
|
|
340
220
|
Fez.store = {
|
|
341
221
|
store: new Map(),
|
|
@@ -360,10 +240,17 @@ Fez.store = {
|
|
|
360
240
|
|
|
361
241
|
// Load utility functions
|
|
362
242
|
import addUtilities from './utility.js'
|
|
243
|
+
import cssMixin from './utils/css_mixin.js'
|
|
363
244
|
addUtilities(Fez)
|
|
245
|
+
cssMixin(Fez)
|
|
364
246
|
|
|
365
247
|
Fez.compile = compile
|
|
366
248
|
Fez.state = state
|
|
367
249
|
Fez.dump = objectDump
|
|
250
|
+
Fez.highlightAll = highlightAll
|
|
251
|
+
|
|
252
|
+
Fez.onReady(() => {
|
|
253
|
+
Fez.log('Fez.LOG === true, logging enabled.')
|
|
254
|
+
})
|
|
368
255
|
|
|
369
256
|
export default Fez
|