@dinoreic/fez 0.3.0 → 0.4.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 +49 -5
- package/dist/fez.js +43 -17
- package/dist/fez.js.map +4 -4
- package/package.json +2 -2
- package/src/fez/compile.js +4 -7
- package/src/fez/connect.js +50 -77
- package/src/fez/defaults.js +36 -1
- package/src/fez/instance.js +100 -21
- package/src/fez/root.js +13 -187
- package/src/fez/utility.js +210 -1
- package/src/fez/utils/css_mixin.js +25 -0
- package/src/fez/utils/dump.js +240 -0
- package/src/fez/utils/highlight_all.js +98 -0
- package/src/svelte-cde-adapter.coffee +122 -0
- package/dist/log.js +0 -5
- package/dist/log.js.map +0 -7
- package/src/log.js +0 -154
package/src/fez/utility.js
CHANGED
|
@@ -46,7 +46,7 @@ export default (Fez) => {
|
|
|
46
46
|
script.type = 'module';
|
|
47
47
|
script.textContent = config.script;
|
|
48
48
|
document.head.appendChild(script);
|
|
49
|
-
|
|
49
|
+
requestAnimationFrame(()=>script.remove())
|
|
50
50
|
} else {
|
|
51
51
|
try {
|
|
52
52
|
new Function(config.script)();
|
|
@@ -121,6 +121,110 @@ export default (Fez) => {
|
|
|
121
121
|
return element;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
// Fetch wrapper with automatic caching and data handling
|
|
125
|
+
// Usage:
|
|
126
|
+
// Fez.fetch(url) - GET request (default)
|
|
127
|
+
// Fez.fetch(url, callback) - GET with callback
|
|
128
|
+
// Fez.fetch(url, data) - GET with query params (?foo=bar&baz=qux)
|
|
129
|
+
// Fez.fetch(url, data, callback) - GET with query params and callback
|
|
130
|
+
// Fez.fetch('POST', url, data) - POST with FormData body (multipart/form-data)
|
|
131
|
+
// Fez.fetch('POST', url, data, callback) - POST with FormData and callback
|
|
132
|
+
// Data object is automatically converted:
|
|
133
|
+
// - GET: appended as URL query parameters
|
|
134
|
+
// - POST: sent as FormData (multipart/form-data) without custom headers
|
|
135
|
+
Fez.fetch = function(...args) {
|
|
136
|
+
// Initialize cache if not exists
|
|
137
|
+
Fez._fetchCache ||= {};
|
|
138
|
+
|
|
139
|
+
let method = 'GET';
|
|
140
|
+
let url;
|
|
141
|
+
let callback;
|
|
142
|
+
|
|
143
|
+
// Check if first arg is HTTP method (uppercase letters)
|
|
144
|
+
if (typeof args[0] === 'string' && /^[A-Z]+$/.test(args[0])) {
|
|
145
|
+
method = args.shift();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// URL is required
|
|
149
|
+
url = args.shift();
|
|
150
|
+
|
|
151
|
+
// Check for data/options object
|
|
152
|
+
let opts = {};
|
|
153
|
+
let data = null;
|
|
154
|
+
if (typeof args[0] === 'object') {
|
|
155
|
+
data = args.shift();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check for callback function
|
|
159
|
+
if (typeof args[0] === 'function') {
|
|
160
|
+
callback = args.shift();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Handle data based on method
|
|
164
|
+
if (data) {
|
|
165
|
+
if (method === 'GET') {
|
|
166
|
+
// For GET, append data as query parameters
|
|
167
|
+
const params = new URLSearchParams(data);
|
|
168
|
+
url += (url.includes('?') ? '&' : '?') + params.toString();
|
|
169
|
+
} else if (method === 'POST') {
|
|
170
|
+
// For POST, convert to FormData
|
|
171
|
+
const formData = new FormData();
|
|
172
|
+
for (const [key, value] of Object.entries(data)) {
|
|
173
|
+
formData.append(key, value);
|
|
174
|
+
}
|
|
175
|
+
opts.body = formData;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Set method
|
|
180
|
+
opts.method = method;
|
|
181
|
+
|
|
182
|
+
// Create cache key from method, url, and stringified opts
|
|
183
|
+
const cacheKey = `${method}:${url}:${JSON.stringify(opts)}`;
|
|
184
|
+
|
|
185
|
+
// Check cache first
|
|
186
|
+
if (Fez._fetchCache[cacheKey]) {
|
|
187
|
+
const cachedData = Fez._fetchCache[cacheKey];
|
|
188
|
+
Fez.log(`fetch cache hit: ${method} ${url}`);
|
|
189
|
+
if (callback) {
|
|
190
|
+
callback(cachedData);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
return Promise.resolve(cachedData);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Log live fetch
|
|
197
|
+
Fez.log(`fetch live: ${method} ${url}`);
|
|
198
|
+
|
|
199
|
+
// Helper to process and cache response
|
|
200
|
+
const processResponse = (response) => {
|
|
201
|
+
if (response.headers.get('content-type')?.includes('application/json')) {
|
|
202
|
+
return response.json();
|
|
203
|
+
}
|
|
204
|
+
return response.text();
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// If callback provided, execute and handle
|
|
208
|
+
if (callback) {
|
|
209
|
+
fetch(url, opts)
|
|
210
|
+
.then(processResponse)
|
|
211
|
+
.then(data => {
|
|
212
|
+
Fez._fetchCache[cacheKey] = data;
|
|
213
|
+
callback(data);
|
|
214
|
+
})
|
|
215
|
+
.catch(error => Fez.onError('fetch', error));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Return promise with automatic JSON parsing
|
|
220
|
+
return fetch(url, opts)
|
|
221
|
+
.then(processResponse)
|
|
222
|
+
.then(data => {
|
|
223
|
+
Fez._fetchCache[cacheKey] = data;
|
|
224
|
+
return data;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
124
228
|
Fez.darkenColor = (color, percent = 20) => {
|
|
125
229
|
// Convert hex to RGB
|
|
126
230
|
const num = parseInt(color.replace("#", ""), 16)
|
|
@@ -181,4 +285,109 @@ export default (Fez) => {
|
|
|
181
285
|
Fez.isTrue = (val) => {
|
|
182
286
|
return ['1', 'true', 'on'].includes(String(val).toLowerCase())
|
|
183
287
|
}
|
|
288
|
+
|
|
289
|
+
// Resolve a function from a string or function reference
|
|
290
|
+
Fez.getFunction = (pointer) => {
|
|
291
|
+
if (!pointer) {
|
|
292
|
+
return ()=>{}
|
|
293
|
+
}
|
|
294
|
+
else if (typeof pointer === 'function') {
|
|
295
|
+
return pointer;
|
|
296
|
+
}
|
|
297
|
+
else if (typeof pointer === 'string') {
|
|
298
|
+
// Check if it's a function expression (arrow function or function keyword)
|
|
299
|
+
// Arrow function: (args) => or args =>
|
|
300
|
+
const arrowFuncPattern = /^\s*\(?\s*\w+(\s*,\s*\w+)*\s*\)?\s*=>/;
|
|
301
|
+
const functionPattern = /^\s*function\s*\(/;
|
|
302
|
+
|
|
303
|
+
if (arrowFuncPattern.test(pointer) || functionPattern.test(pointer)) {
|
|
304
|
+
return new Function('return ' + pointer)();
|
|
305
|
+
} else if (pointer.includes('.') && !pointer.includes('(')) {
|
|
306
|
+
// It's a property access like "this.focus" - return a function that calls it
|
|
307
|
+
return new Function(`return function() { return ${pointer}(); }`);
|
|
308
|
+
} else {
|
|
309
|
+
// It's a function body
|
|
310
|
+
return new Function(pointer);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Execute a function when DOM is ready or immediately if already loaded
|
|
316
|
+
Fez.onReady = (callback) => {
|
|
317
|
+
if (document.readyState === 'loading') {
|
|
318
|
+
document.addEventListener('DOMContentLoaded', ()=>{
|
|
319
|
+
callback()
|
|
320
|
+
}, { once: true })
|
|
321
|
+
} else {
|
|
322
|
+
callback()
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// get unique id from string
|
|
327
|
+
Fez.fnv1 = (str) => {
|
|
328
|
+
var FNV_OFFSET_BASIS, FNV_PRIME, hash, i, j, ref;
|
|
329
|
+
FNV_OFFSET_BASIS = 2166136261;
|
|
330
|
+
FNV_PRIME = 16777619;
|
|
331
|
+
hash = FNV_OFFSET_BASIS;
|
|
332
|
+
for (i = j = 0, ref = str.length - 1; (0 <= ref ? j <= ref : j >= ref); i = 0 <= ref ? ++j : --j) {
|
|
333
|
+
hash ^= str.charCodeAt(i);
|
|
334
|
+
hash *= FNV_PRIME;
|
|
335
|
+
}
|
|
336
|
+
return hash.toString(36).replaceAll('-', '');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
Fez.tag = (tag, opts = {}, html = '') => {
|
|
340
|
+
const json = encodeURIComponent(JSON.stringify(opts))
|
|
341
|
+
return `<${tag} data-props="${json}">${html}</${tag}>`
|
|
342
|
+
// const json = JSON.stringify(opts, null, 2)
|
|
343
|
+
// const data = `<script type="text/template">${json}</script><${tag} data-json-template="true">${html}</${tag}>`
|
|
344
|
+
// return data
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// execute function until it returns true
|
|
348
|
+
Fez.untilTrue = (func, pingRate) => {
|
|
349
|
+
pingRate ||= 200
|
|
350
|
+
|
|
351
|
+
if (!func()) {
|
|
352
|
+
setTimeout(()=>{
|
|
353
|
+
Fez.untilTrue(func, pingRate)
|
|
354
|
+
} ,pingRate)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// throttle function calls
|
|
359
|
+
Fez.throttle = (func, delay = 200) => {
|
|
360
|
+
let lastRun = 0;
|
|
361
|
+
let timeout;
|
|
362
|
+
|
|
363
|
+
return function(...args) {
|
|
364
|
+
const now = Date.now();
|
|
365
|
+
|
|
366
|
+
if (now - lastRun >= delay) {
|
|
367
|
+
func.apply(this, args);
|
|
368
|
+
lastRun = now;
|
|
369
|
+
} else {
|
|
370
|
+
clearTimeout(timeout);
|
|
371
|
+
timeout = setTimeout(() => {
|
|
372
|
+
func.apply(this, args);
|
|
373
|
+
lastRun = Date.now();
|
|
374
|
+
}, delay - (now - lastRun));
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// const firstTimeHash = new Map()
|
|
380
|
+
// Fez.firstTime = (key, func) => {
|
|
381
|
+
// if ( !firstTimeHash.get(key) ) {
|
|
382
|
+
// firstTimeHash.set(key, true)
|
|
383
|
+
|
|
384
|
+
// if (func) {
|
|
385
|
+
// func()
|
|
386
|
+
// }
|
|
387
|
+
|
|
388
|
+
// return true
|
|
389
|
+
// }
|
|
390
|
+
|
|
391
|
+
// return false
|
|
392
|
+
// }
|
|
184
393
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// define custom style macro - simple scss mixin
|
|
2
|
+
// :mobile { ... } -> @media (max-width: 768px) { ... }
|
|
3
|
+
// @include mobile { ... } -> @media (max-width: 768px) { ... }
|
|
4
|
+
// demo/fez/ui-style.fez
|
|
5
|
+
|
|
6
|
+
const CssMixins = {}
|
|
7
|
+
|
|
8
|
+
export default (Fez) => {
|
|
9
|
+
Fez.cssMixin = (name, content) => {
|
|
10
|
+
if (content) {
|
|
11
|
+
CssMixins[name] = content
|
|
12
|
+
} else {
|
|
13
|
+
Object.entries(CssMixins).forEach(([key, val])=>{
|
|
14
|
+
name = name.replaceAll(`:${key} `, `${val} `)
|
|
15
|
+
name = name.replaceAll(`@include ${key} `, `${val} `)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return name
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
Fez.cssMixin('mobile', '@media (max-width: 767px)')
|
|
23
|
+
Fez.cssMixin('tablet', '@media (min-width: 768px) and (max-width: 1023px)')
|
|
24
|
+
Fez.cssMixin('desktop', '@media (min-width: 1200px)')
|
|
25
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// pretty print HTML
|
|
2
|
+
const log_pretty_print = (html) => {
|
|
3
|
+
const parts = html
|
|
4
|
+
.split(/(<\/?[^>]+>)/g)
|
|
5
|
+
.map(p => p.trim())
|
|
6
|
+
.filter(p => p);
|
|
7
|
+
|
|
8
|
+
let indent = 0;
|
|
9
|
+
const lines = [];
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < parts.length; i++) {
|
|
12
|
+
const part = parts[i];
|
|
13
|
+
const nextPart = parts[i + 1];
|
|
14
|
+
const nextNextPart = parts[i + 2];
|
|
15
|
+
|
|
16
|
+
// Check if it's a tag
|
|
17
|
+
if (part.startsWith('<')) {
|
|
18
|
+
// Check if this is an opening tag followed by text and then its closing tag
|
|
19
|
+
if (!part.startsWith('</') && !part.endsWith('/>') && nextPart && !nextPart.startsWith('<') && nextNextPart && nextNextPart.startsWith('</')) {
|
|
20
|
+
// Combine them on one line
|
|
21
|
+
const actualIndent = Math.max(0, indent);
|
|
22
|
+
lines.push(' '.repeat(actualIndent) + part + nextPart + nextNextPart);
|
|
23
|
+
i += 2; // Skip the next two parts
|
|
24
|
+
}
|
|
25
|
+
// Closing tag
|
|
26
|
+
else if (part.startsWith('</')) {
|
|
27
|
+
indent--;
|
|
28
|
+
const actualIndent = Math.max(0, indent);
|
|
29
|
+
lines.push(' '.repeat(actualIndent) + part);
|
|
30
|
+
}
|
|
31
|
+
// Self-closing tag
|
|
32
|
+
else if (part.endsWith('/>') || part.includes(' />')) {
|
|
33
|
+
const actualIndent = Math.max(0, indent);
|
|
34
|
+
lines.push(' '.repeat(actualIndent) + part);
|
|
35
|
+
}
|
|
36
|
+
// Opening tag
|
|
37
|
+
else {
|
|
38
|
+
const actualIndent = Math.max(0, indent);
|
|
39
|
+
lines.push(' '.repeat(actualIndent) + part);
|
|
40
|
+
indent++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Text node
|
|
44
|
+
else if (part) {
|
|
45
|
+
const actualIndent = Math.max(0, indent);
|
|
46
|
+
lines.push(' '.repeat(actualIndent) + part);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return lines.join('\n');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const LOG = (() => {
|
|
54
|
+
const logs = [];
|
|
55
|
+
const logTypes = []; // Track the original type of each log
|
|
56
|
+
let currentIndex = 0;
|
|
57
|
+
let renderContent = null; // Will hold the render function
|
|
58
|
+
|
|
59
|
+
// Add ESC key handler and arrow key navigation
|
|
60
|
+
document.addEventListener('keydown', (e) => {
|
|
61
|
+
if (e.key === 'Escape') {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
const dialog = document.getElementById('dump-dialog');
|
|
64
|
+
const button = document.getElementById('log-reopen-button');
|
|
65
|
+
|
|
66
|
+
if (dialog) {
|
|
67
|
+
// Close dialog
|
|
68
|
+
dialog.remove();
|
|
69
|
+
createLogButton();
|
|
70
|
+
} else if (button) {
|
|
71
|
+
// Open dialog
|
|
72
|
+
button.remove();
|
|
73
|
+
showLogDialog();
|
|
74
|
+
}
|
|
75
|
+
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === 'ArrowUp' || e.key === 'ArrowDown') {
|
|
76
|
+
const dialog = document.getElementById('dump-dialog');
|
|
77
|
+
if (dialog && logs.length > 0) {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
if (e.key === 'ArrowLeft' && currentIndex > 0) {
|
|
80
|
+
currentIndex--;
|
|
81
|
+
localStorage.setItem('_LOG_INDEX', currentIndex);
|
|
82
|
+
renderContent();
|
|
83
|
+
} else if (e.key === 'ArrowRight' && currentIndex < logs.length - 1) {
|
|
84
|
+
currentIndex++;
|
|
85
|
+
localStorage.setItem('_LOG_INDEX', currentIndex);
|
|
86
|
+
renderContent();
|
|
87
|
+
} else if (e.key === 'ArrowUp' && currentIndex > 0) {
|
|
88
|
+
currentIndex = Math.max(0, currentIndex - 5);
|
|
89
|
+
localStorage.setItem('_LOG_INDEX', currentIndex);
|
|
90
|
+
renderContent();
|
|
91
|
+
} else if (e.key === 'ArrowDown' && currentIndex < logs.length - 1) {
|
|
92
|
+
currentIndex = Math.min(logs.length - 1, currentIndex + 5);
|
|
93
|
+
localStorage.setItem('_LOG_INDEX', currentIndex);
|
|
94
|
+
renderContent();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const createLogButton = () => {
|
|
101
|
+
let btn = document.getElementById('log-reopen-button');
|
|
102
|
+
if (!btn) {
|
|
103
|
+
btn = document.body.appendChild(document.createElement('button'));
|
|
104
|
+
btn.id = 'log-reopen-button';
|
|
105
|
+
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-right:4px"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>LOG';
|
|
106
|
+
btn.style.cssText =
|
|
107
|
+
'position:fixed; top: 10px; right: 10px;' +
|
|
108
|
+
'padding:10px 20px;background:#ff3333;color:#fff;border:none;' +
|
|
109
|
+
'cursor:pointer;font:14px/1.4 monospace;z-index:2147483647;' +
|
|
110
|
+
'border-radius:8px;display:flex;align-items:center;' +
|
|
111
|
+
'opacity:1;visibility:visible;box-shadow:0 4px 12px rgba(255,51,51,0.3)';
|
|
112
|
+
btn.onclick = () => {
|
|
113
|
+
btn.remove();
|
|
114
|
+
showLogDialog();
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const showLogDialog = () => {
|
|
120
|
+
let d = document.getElementById('dump-dialog');
|
|
121
|
+
if (!d) {
|
|
122
|
+
d = document.body.appendChild(document.createElement('div'));
|
|
123
|
+
d.id = 'dump-dialog';
|
|
124
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
125
|
+
d.style.cssText =
|
|
126
|
+
'position:absolute; top:' + (scrollTop + 20) + 'px; left: 20px; right:20px;' +
|
|
127
|
+
'background:#fff; border:1px solid #333; box-shadow:0 0 10px rgba(0,0,0,0.5);' +
|
|
128
|
+
'padding:20px; overflow:auto; z-index:2147483646; font:13px/1.4 monospace;' +
|
|
129
|
+
'white-space:pre; display:block; opacity:1; visibility:visible';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check if we have a saved index and it's still valid
|
|
133
|
+
const savedIndex = parseInt(localStorage.getItem('_LOG_INDEX'));
|
|
134
|
+
if (!isNaN(savedIndex) && savedIndex >= 0 && savedIndex < logs.length) {
|
|
135
|
+
currentIndex = savedIndex;
|
|
136
|
+
} else {
|
|
137
|
+
currentIndex = logs.length - 1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
renderContent = () => {
|
|
141
|
+
const buttons = logs.map((_, i) => {
|
|
142
|
+
let bgColor = '#f0f0f0'; // default
|
|
143
|
+
if (i !== currentIndex) {
|
|
144
|
+
if (logTypes[i] === 'object') {
|
|
145
|
+
bgColor = '#d6e3ef'; // super light blue
|
|
146
|
+
} else if (logTypes[i] === 'array') {
|
|
147
|
+
bgColor = '#d8d5ef'; // super light indigo
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return `<button style="font-size: 14px; font-weight: 400; padding:2px 6px; margin: 0 2px 2px 0;cursor:pointer;background:${i === currentIndex ? '#333' : bgColor};color:${i === currentIndex ? '#fff' : '#000'}" data-index="${i}">${i + 1}</button>`
|
|
151
|
+
}).join('');
|
|
152
|
+
|
|
153
|
+
d.innerHTML =
|
|
154
|
+
'<div style="display:flex;flex-direction:column;height:100%">' +
|
|
155
|
+
'<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:10px">' +
|
|
156
|
+
'<div style="display:flex;flex-wrap:wrap;gap:4px;flex:1;margin-right:10px">' + buttons + '</div>' +
|
|
157
|
+
'<button style="padding:4px 8px;cursor:pointer;flex-shrink:0">×</button>' +
|
|
158
|
+
'</div>' +
|
|
159
|
+
'<xmp style="font-family:monospace;flex:1;overflow:auto;margin:0;padding:0;color:#000;background:#fff;font-size:14px;line-height:22px">' + logs[currentIndex] + '</xmp>' +
|
|
160
|
+
'</div>';
|
|
161
|
+
|
|
162
|
+
d.querySelector('button[style*="flex-shrink:0"]').onclick = () => {
|
|
163
|
+
d.remove();
|
|
164
|
+
createLogButton();
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
d.querySelectorAll('button[data-index]').forEach(btn => {
|
|
168
|
+
btn.onclick = () => {
|
|
169
|
+
currentIndex = parseInt(btn.dataset.index);
|
|
170
|
+
localStorage.setItem('_LOG_INDEX', currentIndex);
|
|
171
|
+
renderContent();
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
renderContent();
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return o => {
|
|
180
|
+
if (!document.body) {
|
|
181
|
+
window.requestAnimationFrame( () => LOG(o) )
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Store the original type
|
|
186
|
+
let originalType = typeof o;
|
|
187
|
+
|
|
188
|
+
if (o instanceof Node) {
|
|
189
|
+
if (o.nodeType === Node.TEXT_NODE) {
|
|
190
|
+
o = o.textContent || String(o)
|
|
191
|
+
} else {
|
|
192
|
+
o = log_pretty_print(o.outerHTML)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (o === undefined) { o = 'undefined' }
|
|
197
|
+
if (o === null) { o = 'null' }
|
|
198
|
+
|
|
199
|
+
if (Array.isArray(o)) {
|
|
200
|
+
originalType = 'array';
|
|
201
|
+
} else if (typeof o === 'object' && o !== null) {
|
|
202
|
+
originalType = 'object';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (typeof o != 'string') {
|
|
206
|
+
o = JSON.stringify(o, (key, value) => {
|
|
207
|
+
if (typeof value === 'function') {
|
|
208
|
+
return String(value);
|
|
209
|
+
}
|
|
210
|
+
return value;
|
|
211
|
+
}, 2).replaceAll('<', '<')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
o = o.trim()
|
|
215
|
+
|
|
216
|
+
logs.push(o + `\n\ntype: ${originalType}`);
|
|
217
|
+
logTypes.push(originalType);
|
|
218
|
+
|
|
219
|
+
// Check if log dialog is open by checking for element
|
|
220
|
+
const isOpen = !!document.getElementById('dump-dialog');
|
|
221
|
+
|
|
222
|
+
if (!isOpen) {
|
|
223
|
+
// Show log dialog by default
|
|
224
|
+
showLogDialog();
|
|
225
|
+
} else {
|
|
226
|
+
// Update current index to the new log and refresh
|
|
227
|
+
currentIndex = logs.length - 1;
|
|
228
|
+
localStorage.setItem('_LOG_INDEX', currentIndex);
|
|
229
|
+
if (renderContent) {
|
|
230
|
+
renderContent();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
})();
|
|
235
|
+
|
|
236
|
+
if (typeof window !== 'undefined' && !window.LOG) {
|
|
237
|
+
window.LOG = LOG
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export default LOG
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// Highlight all Fez elements with their names
|
|
2
|
+
const highlightAll = () => {
|
|
3
|
+
// Only work if Fez.DEV is true OR (port is above 2999 and Fez.DEV is not false)
|
|
4
|
+
const port = parseInt(window.location.port) || 80;
|
|
5
|
+
if (!(Fez.DEV === true || (port > 2999 && Fez.DEV !== false))) return;
|
|
6
|
+
|
|
7
|
+
// Check if highlights already exist
|
|
8
|
+
const existingHighlights = document.querySelectorAll('.fez-highlight-overlay');
|
|
9
|
+
|
|
10
|
+
if (existingHighlights.length > 0) {
|
|
11
|
+
// Remove existing highlights
|
|
12
|
+
existingHighlights.forEach(el => el.remove());
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Find all Fez and Svelte elements
|
|
17
|
+
const allElements = document.querySelectorAll('.fez, .svelte');
|
|
18
|
+
|
|
19
|
+
allElements.forEach(el => {
|
|
20
|
+
let componentName = null;
|
|
21
|
+
let componentType = null;
|
|
22
|
+
|
|
23
|
+
// Check for Fez component
|
|
24
|
+
if (el.classList.contains('fez') && el.fez && el.fez.fezName) {
|
|
25
|
+
componentName = el.fez.fezName;
|
|
26
|
+
componentType = 'fez';
|
|
27
|
+
}
|
|
28
|
+
// Check for Svelte component
|
|
29
|
+
else if (el.classList.contains('svelte') && el.svelte && el.svelte.svelteName) {
|
|
30
|
+
componentName = el.svelte.svelteName;
|
|
31
|
+
componentType = 'svelte';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (componentName) {
|
|
35
|
+
// Create overlay div
|
|
36
|
+
const overlay = document.createElement('div');
|
|
37
|
+
overlay.className = 'fez-highlight-overlay';
|
|
38
|
+
|
|
39
|
+
// Get element position
|
|
40
|
+
const rect = el.getBoundingClientRect();
|
|
41
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
42
|
+
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
43
|
+
|
|
44
|
+
// Style the overlay
|
|
45
|
+
overlay.style.cssText = `
|
|
46
|
+
position: absolute;
|
|
47
|
+
top: ${rect.top + scrollTop}px;
|
|
48
|
+
left: ${rect.left + scrollLeft}px;
|
|
49
|
+
width: ${rect.width}px;
|
|
50
|
+
height: ${rect.height}px;
|
|
51
|
+
border: 1px solid ${componentType === 'svelte' ? 'blue' : 'red'};
|
|
52
|
+
pointer-events: none;
|
|
53
|
+
z-index: 9999;
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
// Create label for component name
|
|
57
|
+
const label = document.createElement('div');
|
|
58
|
+
label.textContent = componentName;
|
|
59
|
+
label.style.cssText = `
|
|
60
|
+
position: absolute;
|
|
61
|
+
top: -20px;
|
|
62
|
+
left: 0;
|
|
63
|
+
background: ${componentType === 'svelte' ? 'blue' : 'red'};
|
|
64
|
+
color: white;
|
|
65
|
+
padding: 4px 6px 2px 6px;
|
|
66
|
+
font-size: 14px;
|
|
67
|
+
font-family: monospace;
|
|
68
|
+
line-height: 1;
|
|
69
|
+
white-space: nowrap;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
pointer-events: auto;
|
|
72
|
+
text-transform: uppercase;
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
// Add click handler to dump the node
|
|
76
|
+
label.addEventListener('click', (e) => {
|
|
77
|
+
e.stopPropagation();
|
|
78
|
+
Fez.dump(el);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
overlay.appendChild(label);
|
|
82
|
+
document.body.appendChild(overlay);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Bind Ctrl+E to highlightAll
|
|
88
|
+
document.addEventListener('keydown', (event) => {
|
|
89
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'e') {
|
|
90
|
+
// Check if target is not inside a form
|
|
91
|
+
if (!event.target.closest('form')) {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
highlightAll();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export default highlightAll
|