seawolf2357 commited on
Commit
f0f6fd0
ยท
verified ยท
1 Parent(s): 7a25048

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +257 -677
index.html CHANGED
@@ -1,696 +1,276 @@
1
- <!DOCTYPE html>
2
- <html lang="ko">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>EV Cook Site - ๋ฐฉ๋ฌธ์ž ์ถ”์  ํ™œ์„ฑํ™”</title>
7
- <style>
8
- * {
9
- margin: 0;
10
- padding: 0;
11
- box-sizing: border-box;
12
- }
13
-
14
- body {
15
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
16
- background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
17
- min-height: 100vh;
18
- color: #333;
19
- }
20
-
21
- .header {
22
- background: rgba(255, 255, 255, 0.95);
23
- padding: 20px 0;
24
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
25
- }
26
-
27
- .header-content {
28
- max-width: 1200px;
29
- margin: 0 auto;
30
- padding: 0 20px;
31
- display: flex;
32
- justify-content: space-between;
33
- align-items: center;
34
- }
35
-
36
- .logo {
37
- font-size: 28px;
38
- font-weight: bold;
39
- color: #1e3c72;
40
- }
41
-
42
- .nav {
43
- display: flex;
44
- gap: 30px;
45
- }
46
-
47
- .nav a {
48
- color: #333;
49
- text-decoration: none;
50
- font-weight: 500;
51
- transition: color 0.3s;
52
- }
53
-
54
- .nav a:hover {
55
- color: #1e3c72;
56
- }
57
-
58
- .main-container {
59
- max-width: 1200px;
60
- margin: 50px auto;
61
- padding: 0 20px;
62
- }
63
-
64
- .hero-section {
65
- background: white;
66
- border-radius: 20px;
67
- padding: 60px;
68
- text-align: center;
69
- box-shadow: 0 20px 60px rgba(0,0,0,0.1);
70
- margin-bottom: 40px;
71
- }
72
-
73
- .hero-section h1 {
74
- font-size: 48px;
75
- color: #1e3c72;
76
- margin-bottom: 20px;
77
- }
78
-
79
- .hero-section p {
80
- font-size: 20px;
81
- color: #666;
82
- margin-bottom: 30px;
83
- }
84
-
85
- .tracking-status {
86
- background: #e8f5e9;
87
- border: 2px solid #4caf50;
88
- border-radius: 10px;
89
- padding: 20px;
90
- margin: 20px 0;
91
- }
92
-
93
- .tracking-status h3 {
94
- color: #2e7d32;
95
- margin-bottom: 10px;
96
- }
97
-
98
- .info-grid {
99
- display: grid;
100
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
101
- gap: 20px;
102
- margin-top: 40px;
103
- }
104
-
105
- .info-card {
106
- background: white;
107
- padding: 30px;
108
- border-radius: 15px;
109
- box-shadow: 0 10px 30px rgba(0,0,0,0.1);
110
- transition: transform 0.3s;
111
- }
112
-
113
- .info-card:hover {
114
- transform: translateY(-5px);
115
- }
116
-
117
- .info-card h3 {
118
- color: #1e3c72;
119
- margin-bottom: 15px;
120
- font-size: 24px;
121
- }
122
-
123
- .info-card p {
124
- color: #666;
125
- line-height: 1.6;
126
- }
127
-
128
- .status-indicator {
129
- display: inline-flex;
130
- align-items: center;
131
- gap: 8px;
132
- background: #f5f5f5;
133
- padding: 8px 16px;
134
- border-radius: 20px;
135
- font-size: 14px;
136
- }
137
-
138
- .status-indicator.active {
139
- background: #e8f5e9;
140
- color: #2e7d32;
141
- }
142
-
143
- .status-dot {
144
- width: 8px;
145
- height: 8px;
146
- border-radius: 50%;
147
- background: #4caf50;
148
- animation: pulse 2s infinite;
149
- }
150
-
151
- @keyframes pulse {
152
- 0% {
153
- box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7);
154
- }
155
- 70% {
156
- box-shadow: 0 0 0 10px rgba(76, 175, 80, 0);
157
- }
158
- 100% {
159
- box-shadow: 0 0 0 0 rgba(76, 175, 80, 0);
160
  }
161
- }
162
-
163
- .tracking-details {
164
- background: #f8f9fa;
165
- padding: 20px;
166
- border-radius: 10px;
167
- margin-top: 20px;
168
- font-family: monospace;
169
- font-size: 14px;
170
- }
171
-
172
- .tracking-details .row {
173
- display: flex;
174
- justify-content: space-between;
175
- padding: 8px 0;
176
- border-bottom: 1px solid #e0e0e0;
177
- }
178
-
179
- .tracking-details .row:last-child {
180
- border-bottom: none;
181
- }
182
-
183
- .tracking-details .label {
184
- font-weight: bold;
185
- color: #555;
186
- }
187
-
188
- .tracking-details .value {
189
- color: #1e3c72;
190
- }
191
-
192
- .buttons {
193
- display: flex;
194
- gap: 15px;
195
- margin-top: 30px;
196
- justify-content: center;
197
- flex-wrap: wrap;
198
- }
199
-
200
- button {
201
- padding: 12px 30px;
202
- background: #1e3c72;
203
- color: white;
204
- border: none;
205
- border-radius: 8px;
206
- font-size: 16px;
207
- font-weight: 500;
208
- cursor: pointer;
209
- transition: all 0.3s;
210
- }
211
-
212
- button:hover {
213
- background: #2a5298;
214
- transform: translateY(-2px);
215
- box-shadow: 0 5px 15px rgba(0,0,0,0.2);
216
- }
217
-
218
- button.secondary {
219
- background: #757575;
220
- }
221
-
222
- button.secondary:hover {
223
- background: #616161;
224
- }
225
-
226
- .footer {
227
- text-align: center;
228
- padding: 40px 20px;
229
- color: white;
230
- margin-top: 60px;
231
- }
232
-
233
- .console-hint {
234
- background: #fff3cd;
235
- border: 1px solid #ffeaa7;
236
- padding: 15px;
237
- border-radius: 8px;
238
- margin: 20px 0;
239
- text-align: center;
240
- }
241
-
242
- .console-hint code {
243
- background: #f8f9fa;
244
- padding: 2px 6px;
245
- border-radius: 3px;
246
- font-family: monospace;
247
- }
248
- </style>
249
- </head>
250
- <body>
251
- <header class="header">
252
- <div class="header-content">
253
- <div class="logo">โšก EV Cook</div>
254
- <nav class="nav">
255
- <a href="#home">ํ™ˆ</a>
256
- <a href="#about">์†Œ๊ฐœ</a>
257
- <a href="#tracking">์ถ”์ ์ •๋ณด</a>
258
- <a href="#contact">๋ฌธ์˜</a>
259
- </nav>
260
- </div>
261
- </header>
262
-
263
- <div class="main-container">
264
- <section class="hero-section">
265
- <h1>EV Cook ์ถ”์  ๋ฐ๋ชจ ์‚ฌ์ดํŠธ</h1>
266
- <p>์ด ์‚ฌ์ดํŠธ๋Š” ๋ฐฉ๋ฌธ์ž ์ถ”์  ์‹œ์Šคํ…œ์ด ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค</p>
267
 
268
- <div class="tracking-status">
269
- <h3>๐Ÿ” ์ถ”์  ์ƒํƒœ</h3>
270
- <div class="status-indicator active">
271
- <span class="status-dot"></span>
272
- <span>์ถ”์  ํ™œ์„ฑํ™”๋จ</span>
273
- </div>
274
-
275
- <div class="tracking-details">
276
- <div class="row">
277
- <span class="label">Device ID:</span>
278
- <span class="value" id="deviceId">๋กœ๋”ฉ ์ค‘...</span>
279
- </div>
280
- <div class="row">
281
- <span class="label">์ถ”์  ์„œ๋ฒ„:</span>
282
- <span class="value">seawolf2357-evcook.hf.space</span>
283
- </div>
284
- <div class="row">
285
- <span class="label">์‚ฌ์ดํŠธ ID:</span>
286
- <span class="value">38e0a883</span>
287
- </div>
288
- <div class="row">
289
- <span class="label">์ถ”์  ์‹œ์ž‘:</span>
290
- <span class="value" id="trackingStart">-</span>
291
- </div>
292
- </div>
293
- </div>
294
 
295
- <div class="buttons">
296
- <button onclick="testTracking()">๐Ÿ“Š ์ถ”์  ํ…Œ์ŠคํŠธ</button>
297
- <button onclick="sendCustomEvent()">๐ŸŽฏ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ</button>
298
- <button onclick="viewDetails()" class="secondary">๐Ÿ“‹ ์ƒ์„ธ ์ •๋ณด</button>
299
- <button onclick="checkServer()" class="secondary">๐Ÿ” ์„œ๋ฒ„ ํ™•์ธ</button>
300
- </div>
301
- </section>
302
-
303
- <div class="info-grid">
304
- <div class="info-card">
305
- <h3>๐Ÿš€ ์‹ค์‹œ๊ฐ„ ์ถ”์ </h3>
306
- <p>ํŽ˜์ด์ง€ ๋ฐฉ๋ฌธ, ํด๋ฆญ, ์Šคํฌ๋กค ๋“ฑ ๋ชจ๋“  ์‚ฌ์šฉ์ž ํ™œ๋™์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถ”์ ๋ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ฝ˜์†”์—์„œ ์ถ”์  ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
307
- </div>
308
- <div class="info-card">
309
- <h3>๐Ÿ”’ ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ</h3>
310
- <p>๋””๋ฐ”์ด์Šค ID๋Š” ๋ธŒ๋ผ์šฐ์ € ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑ๋˜๋ฉฐ, ๊ฐœ์ธ์„ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋Š” ์ˆ˜์ง‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.</p>
311
- </div>
312
- <div class="info-card">
313
- <h3>๐Ÿ“ˆ ๋ถ„์„ ๋Œ€์‹œ๋ณด๋“œ</h3>
314
- <p>์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ๋Š” ์ถ”์  ์„œ๋ฒ„์˜ ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
315
- </div>
316
- </div>
317
-
318
- <div class="console-hint">
319
- ๐Ÿ’ก <strong>๊ฐœ๋ฐœ์ž ๋„๊ตฌ ํ™œ์šฉ:</strong> <code>F12</code>๋ฅผ ๋ˆŒ๋Ÿฌ Console ํƒญ์—์„œ ์ถ”์  ์ด๋ฒคํŠธ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•˜์„ธ์š”!
320
- </div>
321
-
322
- <div id="detailsSection" style="display: none;">
323
- <section class="hero-section" style="margin-top: 40px;">
324
- <h2>๐Ÿ“Š ๋ธŒ๋ผ์šฐ์ € ์ƒ์„ธ ์ •๋ณด</h2>
325
- <pre id="browserDetails" style="text-align: left; background: #f5f5f5; padding: 20px; border-radius: 8px; overflow-x: auto;"></pre>
326
- </section>
327
- </div>
328
- </div>
329
-
330
- <footer class="footer">
331
- <p>&copy; 2025 EV Cook. ๋ชจ๋“  ๊ถŒ๋ฆฌ ๋ณด์œ .</p>
332
- <p>์ถ”์  ์„œ๋ฒ„: <a href="https://seawolf2357-evcook.hf.space" target="_blank" style="color: white;">seawolf2357-evcook.hf.space</a></p>
333
- </footer>
334
-
335
- <!-- Fixed Visitor Tracking Script -->
336
- <script>
337
- (function() {
338
- // ์ถ”์  ์„œ๋ฒ„ ์„ค์ •
339
- const TRACKING_SERVER = 'https://seawolf2357-evcook.hf.space';
340
- const SITE_ID = '38e0a883'; // ์„œ๋ฒ„์—์„œ ์ƒ์„ฑํ•œ ์‚ฌ์ดํŠธ ID ์‚ฌ์šฉ
341
-
342
- // ๋””๋ฐ”์ด์Šค ID ์ƒ์„ฑ/์กฐํšŒ
343
- function getDeviceId() {
344
- let deviceId = localStorage.getItem('_tracker_device_id');
345
- if (!deviceId) {
346
- // ํ•˜๋“œ์›จ์–ด ๊ธฐ๋ฐ˜ ID ์ƒ์„ฑ
347
- const components = [
348
- screen.width + 'x' + screen.height,
349
- navigator.hardwareConcurrency || 0,
350
- navigator.deviceMemory || 0,
351
- navigator.platform,
352
- navigator.language,
353
- new Date().getTimezoneOffset()
354
- ];
355
-
356
- // ๊ฐ„๋‹จํ•œ ํ•ด์‹œ ํ•จ์ˆ˜
357
- let hash = 0;
358
- const str = components.join('|');
359
- for (let i = 0; i < str.length; i++) {
360
- const char = str.charCodeAt(i);
361
- hash = ((hash << 5) - hash) + char;
362
- hash = hash & hash;
363
- }
364
-
365
- deviceId = 'DEV_' + Math.abs(hash).toString(36);
366
- localStorage.setItem('_tracker_device_id', deviceId);
367
  }
368
- return deviceId;
369
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
 
371
- // WebGL ์ •๋ณด ์ˆ˜์ง‘
372
- function getWebGLInfo() {
373
- try {
374
- const canvas = document.createElement('canvas');
375
- const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
376
- if (!gl) return 'Not supported';
377
-
378
- const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
379
- if (debugInfo) {
380
- return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
381
- }
382
- return 'Unknown';
383
- } catch(e) {
384
- return 'Error';
385
- }
386
- }
387
 
388
- // ์ถ”์  ๋ฐ์ดํ„ฐ ์ „์†ก (์ˆ˜์ •๋œ ๋ฒ„์ „)
389
- async function collectAndSend(eventType = 'pageview', eventData = {}) {
390
- const data = {
391
- siteId: SITE_ID,
392
- deviceId: getDeviceId(),
393
- eventType: eventType,
394
- eventData: eventData,
395
- pageUrl: window.location.href,
396
- pageTitle: document.title,
397
- referrer: document.referrer,
398
- userAgent: navigator.userAgent,
399
- screenResolution: screen.width + 'x' + screen.height,
400
- platform: navigator.platform,
401
- language: navigator.language,
402
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
403
- cpuCores: navigator.hardwareConcurrency || 0,
404
- deviceMemory: navigator.deviceMemory || 0,
405
- gpuInfo: getWebGLInfo(),
406
- timestamp: new Date().toISOString()
407
- };
408
-
409
- console.log('[Tracker] Sending:', eventType);
410
-
411
- // ์—ฌ๋Ÿฌ fn_index ์‹œ๋„ (TabbedInterface ๋•Œ๋ฌธ์— ์ธ๋ฑ์Šค๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ)
412
- const fnIndexes = [0, 1, 2]; // ๊ฐ€๋Šฅํ•œ ์ธ๋ฑ์Šค๋“ค
413
- let success = false;
414
-
415
- for (const fnIndex of fnIndexes) {
416
- if (success) break;
417
-
418
- try {
419
- // ๋ฐฉ๋ฒ• 1: /run/predict ์—”๋“œํฌ์ธํŠธ
420
- const response = await fetch(TRACKING_SERVER + '/run/predict', {
421
- method: 'POST',
422
- headers: {
423
- 'Content-Type': 'application/json',
424
- },
425
- body: JSON.stringify({
426
- data: [JSON.stringify(data)],
427
- fn_index: fnIndex
428
- })
429
- });
430
-
431
- if (response.ok) {
432
- const result = await response.json();
433
- console.log(`[Tracker] โœ… Success with fn_index=${fnIndex}:`, result);
434
- success = true;
435
-
436
- // ์„ฑ๊ณตํ•œ fn_index ์ €์žฅ
437
- window._trackerFnIndex = fnIndex;
438
- break;
439
- } else if (response.status === 422) {
440
- console.log(`[Tracker] fn_index=${fnIndex} not valid, trying next...`);
441
- } else {
442
- console.log(`[Tracker] fn_index=${fnIndex} error: ${response.status}`);
443
- }
444
- } catch (e) {
445
- console.log(`[Tracker] fn_index=${fnIndex} failed:`, e.message);
446
- }
447
- }
448
-
449
- // ๋ฐฉ๋ฒ• 2: API ์—”๋“œํฌ์ธํŠธ ์ง์ ‘ ํ˜ธ์ถœ (ํด๋ฐฑ)
450
- if (!success) {
451
- try {
452
- const apiResponse = await fetch(TRACKING_SERVER + '/api/predict/', {
453
- method: 'POST',
454
- headers: {
455
- 'Content-Type': 'application/json',
456
- },
457
- body: JSON.stringify({
458
- data: [JSON.stringify(data)],
459
- fn_index: window._trackerFnIndex || 1 // ์ด์ „์— ์„ฑ๊ณตํ•œ ์ธ๋ฑ์Šค ์‚ฌ์šฉ
460
- })
461
- });
462
-
463
- if (apiResponse.ok) {
464
- console.log('[Tracker] โœ… API method success');
465
- success = true;
466
- }
467
- } catch (e) {
468
- console.error('[Tracker] API method failed:', e);
469
- }
470
- }
471
 
472
- // ๋ฐฉ๋ฒ• 3: Queue/Join ๋ฐฉ์‹ (Gradio 4.x)
473
- if (!success) {
474
- try {
475
- const session_hash = Math.random().toString(36).substring(2, 15);
476
- const queueResponse = await fetch(TRACKING_SERVER + '/queue/join', {
477
- method: 'POST',
478
- headers: {
479
- 'Content-Type': 'application/json',
480
- },
481
- body: JSON.stringify({
482
- data: [JSON.stringify(data)],
483
- fn_index: window._trackerFnIndex || 1,
484
- session_hash: session_hash
485
- })
486
- });
487
-
488
- if (queueResponse.ok) {
489
- console.log('[Tracker] โœ… Queue method success');
490
- success = true;
491
- }
492
- } catch (e) {
493
- console.error('[Tracker] Queue method failed:', e);
494
  }
495
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
 
497
- if (!success) {
498
- console.error('[Tracker] โŒ All methods failed for event:', eventType);
 
499
  }
500
- }
501
-
502
- // ์ „์—ญ ์ถ”์  ๊ฐ์ฒด
503
- window._tracker = {
504
- trackEvent: function(eventName, eventData) {
505
- collectAndSend('custom', { name: eventName, data: eventData });
506
- },
 
 
 
 
 
 
 
 
 
507
 
508
- // ๋””๋ฒ„๊น… ๋„๊ตฌ
509
- debug: {
510
- getDeviceId: getDeviceId,
511
-
512
- // ์„œ๋ฒ„ ๊ตฌ์กฐ ํ™•์ธ
513
- checkConfig: async function() {
514
- try {
515
- const response = await fetch(TRACKING_SERVER + '/config');
516
- const config = await response.json();
517
- console.log('Server config:', config);
518
-
519
- if (config.dependencies) {
520
- console.log('Available functions:');
521
- config.dependencies.forEach((dep, i) => {
522
- console.log(`[${i}] ${dep.api_name || 'unnamed'}`);
523
- });
524
- }
525
- } catch (e) {
526
- console.error('Config check failed:', e);
527
- }
528
- },
529
-
530
- // ํ…Œ์ŠคํŠธ ์ „์†ก
531
- test: function() {
532
- collectAndSend('debug_test', {
533
- test: true,
534
- timestamp: new Date().toISOString(),
535
- random: Math.random()
536
- });
537
- }
538
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  };
540
-
541
- // ํŽ˜์ด์ง€ ๋กœ๋“œ์‹œ ์ถ”์ 
542
- if (document.readyState === 'loading') {
543
- document.addEventListener('DOMContentLoaded', () => {
544
- console.log('[Tracker] DOM loaded, sending pageview...');
545
- collectAndSend();
 
 
 
 
 
 
 
 
 
 
 
 
546
  });
547
- } else {
548
- console.log('[Tracker] Page already loaded, sending pageview...');
549
- collectAndSend();
550
  }
551
-
552
- // SPA ์ง€์› - URL ๋ณ€๊ฒฝ ๊ฐ์ง€
553
- let lastUrl = location.href;
554
- new MutationObserver(() => {
555
- const url = location.href;
556
- if (url !== lastUrl) {
557
- lastUrl = url;
558
- collectAndSend('navigation', { from: lastUrl, to: url });
559
- }
560
- }).observe(document, {subtree: true, childList: true});
561
-
562
- // ์Šคํฌ๋กค ์ถ”์ 
563
- let scrollTracked = false;
564
- window.addEventListener('scroll', () => {
565
- if (!scrollTracked && window.scrollY > 100) {
566
- scrollTracked = true;
567
- window._tracker.trackEvent('scroll', { depth: window.scrollY });
568
- }
569
- });
570
-
571
- // ์ดˆ๊ธฐํ™” ๋ฉ”์‹œ์ง€
572
- console.log('%c๐Ÿ” EV Cook Tracker v4.0 (Fixed)', 'color: #4CAF50; font-size: 16px; font-weight: bold;');
573
- console.log('Server:', TRACKING_SERVER);
574
- console.log('Site ID:', SITE_ID);
575
- console.log('Device ID:', getDeviceId());
576
- console.log('\n์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ช…๋ น์–ด:');
577
- console.log('- window._tracker.trackEvent(name, data)');
578
- console.log('- window._tracker.debug.checkConfig()');
579
- console.log('- window._tracker.debug.test()');
580
- })();
581
- </script>
582
- <!-- End Visitor Tracking Script -->
583
-
584
- <!-- ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ ์Šคํฌ๋ฆฝํŠธ -->
585
- <script>
586
- // ํŽ˜์ด์ง€ ๋กœ๋“œ์‹œ ์‹คํ–‰
587
- window.addEventListener('DOMContentLoaded', () => {
588
- // Device ID ํ‘œ์‹œ
589
- setTimeout(() => {
590
- const deviceId = localStorage.getItem('_tracker_device_id');
591
- if (deviceId) {
592
- document.getElementById('deviceId').textContent = deviceId;
593
- }
594
- document.getElementById('trackingStart').textContent = new Date().toLocaleString('ko-KR');
595
- }, 500);
596
- });
597
-
598
- // ์ถ”์  ํ…Œ์ŠคํŠธ
599
- function testTracking() {
600
- if (window._tracker) {
601
- window._tracker.trackEvent('test_button_click', {
602
- button: 'tracking_test',
603
- timestamp: Date.now(),
604
- location: 'hero_section'
605
- });
606
-
607
- alert('โœ… ์ถ”์  ์ด๋ฒคํŠธ๊ฐ€ ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค!\n\n๊ฐœ๋ฐœ์ž ๋„๊ตฌ(F12) Console ํƒญ์—์„œ ํ™•์ธํ•˜์„ธ์š”.');
608
- } else {
609
- alert('โŒ ์ถ”์  ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.');
610
- }
611
- }
612
-
613
- // ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์ „์†ก
614
- function sendCustomEvent() {
615
- const eventName = prompt('์ด๋ฒคํŠธ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”:', 'custom_action');
616
- if (eventName && window._tracker) {
617
- const eventData = {
618
- custom_name: eventName,
619
- user_input: true,
620
- timestamp: Date.now()
621
- };
622
-
623
- window._tracker.trackEvent(eventName, eventData);
624
-
625
- alert(`โœ… '${eventName}' ์ด๋ฒคํŠธ๊ฐ€ ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค!`);
626
- console.log('[Custom Event]', eventName, eventData);
627
- }
628
  }
629
-
630
- // ์ƒ์„ธ ์ •๋ณด ๋ณด๊ธฐ
631
- function viewDetails() {
632
- const details = {
633
- tracking: {
634
- deviceId: localStorage.getItem('_tracker_device_id'),
635
- siteId: '38e0a883',
636
- serverUrl: 'https://seawolf2357-evcook.hf.space'
637
- },
638
- browser: {
639
- userAgent: navigator.userAgent,
640
- platform: navigator.platform,
641
- vendor: navigator.vendor,
642
- language: navigator.language,
643
- languages: navigator.languages,
644
- cookieEnabled: navigator.cookieEnabled,
645
- doNotTrack: navigator.doNotTrack,
646
- onLine: navigator.onLine
647
- },
648
- screen: {
649
- resolution: screen.width + 'x' + screen.height,
650
- colorDepth: screen.colorDepth,
651
- pixelRatio: window.devicePixelRatio
652
- },
653
- viewport: {
654
- width: window.innerWidth,
655
- height: window.innerHeight
656
- },
657
- hardware: {
658
- cpuCores: navigator.hardwareConcurrency || 'Unknown',
659
- deviceMemory: navigator.deviceMemory ? navigator.deviceMemory + 'GB' : 'Unknown'
660
- },
661
- location: {
662
- href: window.location.href,
663
- hostname: window.location.hostname,
664
- protocol: window.location.protocol,
665
- pathname: window.location.pathname
666
- },
667
- time: {
668
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
669
- offset: new Date().getTimezoneOffset(),
670
- locale: new Date().toLocaleString()
671
- }
672
  };
673
-
674
- document.getElementById('browserDetails').textContent = JSON.stringify(details, null, 2);
675
- document.getElementById('detailsSection').style.display = 'block';
676
-
677
- // ์ƒ์„ธ ์ •๋ณด ๋ณด๊ธฐ๋„ ์ถ”์ 
678
- window._tracker.trackEvent('view_details', { action: 'show_browser_info' });
679
- }
680
-
681
- // ์„œ๋ฒ„ ํ™•์ธ
682
- function checkServer() {
683
- console.log('๐Ÿ” ์„œ๋ฒ„ ๊ตฌ์กฐ ํ™•์ธ ์ค‘...');
684
- window._tracker.debug.checkConfig();
685
- alert('์„œ๋ฒ„ ๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.\n๊ฐœ๋ฐœ์ž ๋„๊ตฌ(F12) Console ํƒญ์„ ํ™•์ธํ•˜์„ธ์š”.');
 
 
 
 
 
 
 
686
  }
687
-
688
- // ์ฝ˜์†”์— ์ถ”์  ์ •๋ณด ์ถœ๋ ฅ
689
- console.log('%c๐Ÿ” EV Cook ์ถ”์  ์‹œ์Šคํ…œ ํ™œ์„ฑํ™”๋จ', 'color: #4CAF50; font-size: 16px; font-weight: bold;');
690
- console.log('%c์ถ”์  ์„œ๋ฒ„:', 'font-weight: bold;', 'https://seawolf2357-evcook.hf.space');
691
- console.log('%c์‚ฌ์ดํŠธ ID:', 'font-weight: bold;', '38e0a883');
692
- console.log('%cDevice ID:', 'font-weight: bold;', localStorage.getItem('_tracker_device_id') || '์ƒ์„ฑ ์ค‘...');
693
- console.log('\n%c๐Ÿ’ก Tip: window._tracker.trackEvent(name, data)๋กœ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.', 'color: #666;');
694
- </script>
695
- </body>
696
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- EV Cook Visitor Tracking Script v6.0 -->
2
+ <!-- ์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”์ ํ•˜๋ ค๋Š” ์›น์‚ฌ์ดํŠธ์˜ </body> ํƒœ๊ทธ ๋ฐ”๋กœ ์•ž์— ์‚ฝ์ž…ํ•˜์„ธ์š” -->
3
+ <script>
4
+ (function() {
5
+ // ========== ์„ค์ • ==========
6
+ const TRACKING_SERVER = 'https://seawolf2357-evcook.hf.space';
7
+ const SITE_ID = '38e0a883'; // ์„œ๋ฒ„์—์„œ ์ƒ์„ฑํ•œ ์‚ฌ์ดํŠธ ID๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”
8
+ const PROCESS_TRACKING_FN_INDEX = 10;
9
+ const DEBUG_MODE = false; // true๋กœ ์„ค์ •ํ•˜๋ฉด ์ฝ˜์†”์— ์ƒ์„ธ ๋กœ๊ทธ ์ถœ๋ ฅ
10
+
11
+ // ========== ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ ==========
12
+ function log(level, message, data) {
13
+ if (DEBUG_MODE || level === 'error') {
14
+ const prefix = `[EV Cook Tracker ${new Date().toLocaleTimeString()}]`;
15
+ console[level === 'error' ? 'error' : 'log'](`${prefix} ${message}`, data || '');
16
+ }
17
+ }
18
+
19
+ // ๋””๋ฐ”์ด์Šค ID ์ƒ์„ฑ/์กฐํšŒ
20
+ function getDeviceId() {
21
+ let deviceId = localStorage.getItem('_evcook_device_id');
22
+ if (!deviceId) {
23
+ const components = [
24
+ screen.width + 'x' + screen.height,
25
+ navigator.hardwareConcurrency || 0,
26
+ navigator.deviceMemory || 0,
27
+ navigator.platform,
28
+ navigator.language,
29
+ new Date().getTimezoneOffset(),
30
+ navigator.vendor || '',
31
+ screen.colorDepth,
32
+ window.devicePixelRatio || 1
33
+ ];
34
+
35
+ let hash = 0;
36
+ const str = components.join('|');
37
+ for (let i = 0; i < str.length; i++) {
38
+ const char = str.charCodeAt(i);
39
+ hash = ((hash << 5) - hash) + char;
40
+ hash = hash & hash;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ deviceId = 'DEV_' + Math.abs(hash).toString(36).toUpperCase();
44
+ localStorage.setItem('_evcook_device_id', deviceId);
45
+ log('info', 'New device ID generated:', deviceId);
46
+ }
47
+ return deviceId;
48
+ }
49
+
50
+ // WebGL ์ •๋ณด ์ˆ˜์ง‘
51
+ function getWebGLInfo() {
52
+ try {
53
+ const canvas = document.createElement('canvas');
54
+ const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
55
+ if (!gl) return 'Not supported';
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
+ const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
58
+ if (debugInfo) {
59
+ return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
+ return 'Unknown';
62
+ } catch(e) {
63
+ return 'Error';
64
+ }
65
+ }
66
+
67
+ // ========== ์ถ”์  ๋ฐ์ดํ„ฐ ์ „์†ก ==========
68
+ async function sendTrackingData(eventType, eventData = {}) {
69
+ const data = {
70
+ siteId: SITE_ID,
71
+ deviceId: getDeviceId(),
72
+ eventType: eventType,
73
+ eventData: eventData,
74
+ pageUrl: window.location.href,
75
+ pageTitle: document.title,
76
+ referrer: document.referrer,
77
+ userAgent: navigator.userAgent,
78
+ screenResolution: screen.width + 'x' + screen.height,
79
+ platform: navigator.platform,
80
+ language: navigator.language,
81
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
82
+ cpuCores: navigator.hardwareConcurrency || 0,
83
+ deviceMemory: navigator.deviceMemory || 0,
84
+ gpuInfo: getWebGLInfo(),
85
+ timestamp: new Date().toISOString()
86
+ };
87
 
88
+ log('info', `Sending ${eventType} event`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ // ๋ฐฉ๋ฒ• 1: Gradio Client API
91
+ try {
92
+ const response = await fetch(TRACKING_SERVER + '/call/process_tracking', {
93
+ method: 'POST',
94
+ headers: { 'Content-Type': 'application/json' },
95
+ body: JSON.stringify({ data: [JSON.stringify(data)] })
96
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ if (response.ok) {
99
+ const result = await response.json();
100
+ if (result.event_id) {
101
+ log('info', 'Event sent successfully via Client API');
102
+ return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  }
104
  }
105
+ } catch (e) {
106
+ log('debug', 'Client API failed:', e.message);
107
+ }
108
+
109
+ // ๋ฐฉ๋ฒ• 2: Direct API with fn_index
110
+ try {
111
+ const response = await fetch(TRACKING_SERVER + '/api/predict', {
112
+ method: 'POST',
113
+ headers: { 'Content-Type': 'application/json' },
114
+ body: JSON.stringify({
115
+ data: [JSON.stringify(data)],
116
+ fn_index: PROCESS_TRACKING_FN_INDEX
117
+ })
118
+ });
119
 
120
+ if (response.ok) {
121
+ log('info', 'Event sent successfully via Direct API');
122
+ return true;
123
  }
124
+ } catch (e) {
125
+ log('debug', 'Direct API failed:', e.message);
126
+ }
127
+
128
+ // ๋ฐฉ๋ฒ• 3: Queue API (ํด๋ฐฑ)
129
+ try {
130
+ const session_hash = Math.random().toString(36).substring(2, 15);
131
+ const response = await fetch(TRACKING_SERVER + '/queue/join', {
132
+ method: 'POST',
133
+ headers: { 'Content-Type': 'application/json' },
134
+ body: JSON.stringify({
135
+ data: [JSON.stringify(data)],
136
+ fn_index: PROCESS_TRACKING_FN_INDEX,
137
+ session_hash: session_hash
138
+ })
139
+ });
140
 
141
+ if (response.ok) {
142
+ log('info', 'Event sent successfully via Queue API');
143
+ return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  }
145
+ } catch (e) {
146
+ log('debug', 'Queue API failed:', e.message);
147
+ }
148
+
149
+ log('error', 'All tracking methods failed');
150
+ return false;
151
+ }
152
+
153
+ // ========== ์ „์—ญ ์ถ”์  ๊ฐ์ฒด ==========
154
+ window._evCookTracker = {
155
+ trackEvent: function(eventName, eventData) {
156
+ return sendTrackingData('custom', { name: eventName, data: eventData });
157
+ },
158
+
159
+ trackPageView: function(pageData) {
160
+ return sendTrackingData('pageview', pageData || {});
161
+ },
162
+
163
+ getDeviceId: getDeviceId,
164
+
165
+ // ์„ค์ • ๋ณ€๊ฒฝ
166
+ setDebugMode: function(enabled) {
167
+ DEBUG_MODE = enabled;
168
+ log('info', 'Debug mode ' + (enabled ? 'enabled' : 'disabled'));
169
+ }
170
+ };
171
+
172
+ // ========== ์ž๋™ ์ถ”์  ==========
173
+
174
+ // ํŽ˜๏ฟฝ๏ฟฝ์ง€ ๋กœ๋“œ ์ถ”์ 
175
+ function trackPageLoad() {
176
+ const loadData = {
177
+ loadTime: window.performance && window.performance.timing ?
178
+ window.performance.timing.loadEventEnd - window.performance.timing.navigationStart : 0,
179
+ source: document.referrer || 'direct'
180
  };
181
+ sendTrackingData('pageview', loadData);
182
+ }
183
+
184
+ if (document.readyState === 'loading') {
185
+ document.addEventListener('DOMContentLoaded', trackPageLoad);
186
+ } else {
187
+ trackPageLoad();
188
+ }
189
+
190
+ // URL ๋ณ€๊ฒฝ ๊ฐ์ง€ (SPA ์ง€์›)
191
+ let lastUrl = location.href;
192
+ new MutationObserver(() => {
193
+ const url = location.href;
194
+ if (url !== lastUrl) {
195
+ lastUrl = url;
196
+ sendTrackingData('navigation', {
197
+ from: lastUrl.substring(0, 200),
198
+ to: url.substring(0, 200)
199
  });
 
 
 
200
  }
201
+ }).observe(document, { subtree: true, childList: true });
202
+
203
+ // ์Šคํฌ๋กค ์ถ”์  (์ตœ์ดˆ 100px ์Šคํฌ๋กค ์‹œ)
204
+ let scrollTracked = false;
205
+ window.addEventListener('scroll', () => {
206
+ if (!scrollTracked && window.scrollY > 100) {
207
+ scrollTracked = true;
208
+ sendTrackingData('scroll', {
209
+ depth: window.scrollY,
210
+ pageHeight: document.documentElement.scrollHeight
211
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
+ });
214
+
215
+ // ํŽ˜์ด์ง€ ์ดํƒˆ ์‹œ๊ฐ„ ์ถ”์ 
216
+ const sessionStartTime = Date.now();
217
+ window.addEventListener('beforeunload', () => {
218
+ const timeOnPage = Math.floor((Date.now() - sessionStartTime) / 1000);
219
+ // ๋™๊ธฐ ์š”์ฒญ (๋น„์ฝ˜ API ์‚ฌ์šฉ)
220
+ if (navigator.sendBeacon) {
221
+ const data = {
222
+ siteId: SITE_ID,
223
+ deviceId: getDeviceId(),
224
+ eventType: 'page_exit',
225
+ eventData: { timeOnPage: timeOnPage, scrollDepth: window.scrollY },
226
+ timestamp: new Date().toISOString()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  };
228
+ navigator.sendBeacon(
229
+ TRACKING_SERVER + '/api/predict',
230
+ JSON.stringify({
231
+ data: [JSON.stringify(data)],
232
+ fn_index: PROCESS_TRACKING_FN_INDEX
233
+ })
234
+ );
235
+ }
236
+ });
237
+
238
+ // ์˜ค๋ฅ˜ ์ถ”์  (์„ ํƒ์‚ฌํ•ญ)
239
+ window.addEventListener('error', function(e) {
240
+ if (Math.random() < 0.1) { // 10% ์ƒ˜ํ”Œ๋ง
241
+ sendTrackingData('error', {
242
+ message: e.message,
243
+ source: e.filename,
244
+ line: e.lineno,
245
+ column: e.colno,
246
+ stack: e.error ? e.error.stack : ''
247
+ });
248
  }
249
+ });
250
+
251
+ // ์ดˆ๊ธฐํ™” ์™„๋ฃŒ
252
+ log('info', 'EV Cook Tracker initialized', {
253
+ server: TRACKING_SERVER,
254
+ siteId: SITE_ID,
255
+ deviceId: getDeviceId()
256
+ });
257
+
258
+ })();
259
+ </script>
260
+ <!-- End EV Cook Tracking Script -->
261
+
262
+ <!--
263
+ ์‚ฌ์šฉ๋ฒ•:
264
+ 1. ์œ„ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ์ถ”์ ํ•˜๋ ค๋Š” ์›น์‚ฌ์ดํŠธ์˜ </body> ํƒœ๊ทธ ๋ฐ”๋กœ ์•ž์— ๋ถ™์—ฌ๋„ฃ์œผ์„ธ์š”.
265
+ 2. SITE_ID๋ฅผ ์„œ๋ฒ„์—์„œ ์ƒ์„ฑํ•œ ์‹ค์ œ ์‚ฌ์ดํŠธ ID๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.
266
+ 3. ํ•„์š”์‹œ DEBUG_MODE๋ฅผ true๋กœ ์„ค์ •ํ•˜์—ฌ ์ฝ˜์†”์—์„œ ์ƒ์„ธ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
267
+
268
+ ์ปค์Šคํ…€ ์ด๋ฒคํŠธ ์ „์†ก:
269
+ window._evCookTracker.trackEvent('button_click', { buttonId: 'submit-btn' });
270
+
271
+ ์ˆ˜๋™ ํŽ˜์ด์ง€๋ทฐ ์ „์†ก:
272
+ window._evCookTracker.trackPageView({ customData: 'value' });
273
+
274
+ ๋””๋ฒ„๊ทธ ๋ชจ๋“œ ์ผœ๊ธฐ:
275
+ window._evCookTracker.setDebugMode(true);
276
+ -->