AnesKAM commited on
Commit
2fc37e6
·
verified ·
1 Parent(s): 6c1e821

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +2 -533
index.html CHANGED
@@ -6,296 +6,7 @@
6
  <title>SurfGO — premium search engine by AnesNT</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;400;500;600;700;800&display=swap" rel="stylesheet">
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
- <style>
10
- * {
11
- margin: 0;
12
- padding: 0;
13
- box-sizing: border-box;
14
- }
15
-
16
- body {
17
- font-family: 'Inter', system-ui, -apple-system, sans-serif;
18
- background: linear-gradient(145deg, #f5f7fe 0%, #eef2fa 100%);
19
- min-height: 100vh;
20
- color: #121826;
21
- }
22
-
23
- /* premium glass header */
24
- .header {
25
- background: rgba(255, 255, 255, 0.94);
26
- backdrop-filter: blur(20px);
27
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
28
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.03);
29
- position: sticky;
30
- top: 0;
31
- z-index: 100;
32
- }
33
-
34
- .container {
35
- max-width: 1280px;
36
- margin: 0 auto;
37
- padding: 1.25rem 2rem;
38
- }
39
-
40
- .logo-area {
41
- display: flex;
42
- align-items: baseline;
43
- justify-content: space-between;
44
- flex-wrap: wrap;
45
- gap: 1rem;
46
- margin-bottom: 1rem;
47
- }
48
-
49
- .brand {
50
- display: flex;
51
- align-items: center;
52
- gap: 12px;
53
- }
54
-
55
- .brand h1 {
56
- font-size: 2.1rem;
57
- font-weight: 800;
58
- letter-spacing: -0.5px;
59
- background: linear-gradient(135deg, #1A2A6C, #b21f1f, #fdbb4d);
60
- background-clip: text;
61
- -webkit-background-clip: text;
62
- color: transparent;
63
- }
64
-
65
- .brand .badge {
66
- background: rgba(0, 0, 0, 0.05);
67
- padding: 4px 10px;
68
- border-radius: 40px;
69
- font-size: 0.7rem;
70
- font-weight: 600;
71
- color: #2c3e66;
72
- }
73
-
74
- .creator {
75
- font-size: 0.85rem;
76
- font-weight: 500;
77
- color: #4a5b7a;
78
- background: #ffffffcc;
79
- padding: 5px 12px;
80
- border-radius: 30px;
81
- }
82
-
83
- .creator i {
84
- margin-right: 6px;
85
- color: #fdbb4d;
86
- }
87
-
88
- /* search bar */
89
- .search-section {
90
- margin: 1.5rem 0 0.5rem;
91
- }
92
-
93
- .search-wrapper {
94
- background: white;
95
- border-radius: 60px;
96
- box-shadow: 0 12px 35px rgba(0, 0, 0, 0.07), 0 0 0 1px rgba(0, 0, 0, 0.02);
97
- display: flex;
98
- align-items: center;
99
- padding: 0.35rem 0.35rem 0.35rem 1.8rem;
100
- transition: all 0.25s ease;
101
- }
102
-
103
- .search-wrapper:focus-within {
104
- box-shadow: 0 18px 40px rgba(0, 0, 0, 0.12), 0 0 0 2px #fdbb4d80;
105
- transform: scale(1.01);
106
- }
107
-
108
- .search-wrapper input {
109
- flex: 1;
110
- border: none;
111
- padding: 1rem 0;
112
- font-size: 1.1rem;
113
- font-weight: 500;
114
- background: transparent;
115
- outline: none;
116
- font-family: 'Inter', monospace;
117
- color: #111827;
118
- }
119
-
120
- .search-wrapper input::placeholder {
121
- color: #9ca3af;
122
- font-weight: 400;
123
- }
124
-
125
- .search-actions {
126
- display: flex;
127
- gap: 8px;
128
- align-items: center;
129
- }
130
-
131
- .voice-btn, .search-submit {
132
- background: transparent;
133
- border: none;
134
- width: 48px;
135
- height: 48px;
136
- border-radius: 50%;
137
- font-size: 1.3rem;
138
- cursor: pointer;
139
- transition: 0.2s;
140
- color: #4b5563;
141
- display: inline-flex;
142
- align-items: center;
143
- justify-content: center;
144
- }
145
-
146
- .voice-btn:hover, .search-submit:hover {
147
- background: #f3f4f6;
148
- color: #fdbb4d;
149
- }
150
-
151
- .search-submit {
152
- background: #1e293b;
153
- color: white;
154
- width: auto;
155
- padding: 0 1.6rem;
156
- border-radius: 50px;
157
- font-weight: 600;
158
- gap: 8px;
159
- font-size: 0.95rem;
160
- }
161
-
162
- .search-submit:hover {
163
- background: #fdbb4d;
164
- color: #1e293b;
165
- }
166
-
167
- /* TABS */
168
- .tabs {
169
- display: flex;
170
- gap: 0.25rem;
171
- background: #ffffffcc;
172
- backdrop-filter: blur(8px);
173
- padding: 0.5rem;
174
- border-radius: 60px;
175
- margin: 1.8rem 0 1.2rem;
176
- border: 1px solid rgba(0,0,0,0.03);
177
- width: fit-content;
178
- }
179
-
180
- .tab-btn {
181
- padding: 0.6rem 1.8rem;
182
- border-radius: 40px;
183
- font-weight: 600;
184
- font-size: 0.9rem;
185
- background: transparent;
186
- border: none;
187
- cursor: pointer;
188
- transition: all 0.2s;
189
- color: #334155;
190
- display: flex;
191
- align-items: center;
192
- gap: 8px;
193
- font-family: 'Inter', sans-serif;
194
- }
195
-
196
- .tab-btn i {
197
- font-size: 1rem;
198
- }
199
-
200
- .tab-btn.active {
201
- background: #1e293b;
202
- color: white;
203
- box-shadow: 0 6px 12px -8px rgba(0,0,0,0.2);
204
- }
205
-
206
- .tab-btn:not(.active):hover {
207
- background: #eef2ff;
208
- color: #1e293b;
209
- }
210
-
211
- /* results container */
212
- .results-container {
213
- margin-top: 1.5rem;
214
- background: rgba(255,255,255,0.6);
215
- backdrop-filter: blur(2px);
216
- border-radius: 32px;
217
- padding: 0.2rem;
218
- min-height: 55vh;
219
- }
220
-
221
- .cse-wrapper {
222
- display: none;
223
- }
224
-
225
- .cse-wrapper.active {
226
- display: block;
227
- }
228
-
229
- /* style Google results */
230
- .gsc-control-cse, .gsc-control-wrapper-cse {
231
- background: transparent !important;
232
- border: none !important;
233
- padding: 0 !important;
234
- }
235
- .gsc-result-info, .gsc-orderby {
236
- font-family: 'Inter', sans-serif !important;
237
- color: #334155 !important;
238
- }
239
- .gsc-webResult .gsc-result {
240
- background: white !important;
241
- border-radius: 20px !important;
242
- padding: 0.8rem 1.2rem !important;
243
- margin-bottom: 1rem !important;
244
- box-shadow: 0 2px 8px rgba(0,0,0,0.03) !important;
245
- border: 1px solid rgba(0,0,0,0.03) !important;
246
- }
247
-
248
- .footer {
249
- text-align: center;
250
- margin-top: 3rem;
251
- padding: 1.5rem;
252
- color: #6c757d;
253
- font-size: 0.75rem;
254
- border-top: 1px solid rgba(0,0,0,0.05);
255
- }
256
-
257
- .toast-msg {
258
- position: fixed;
259
- bottom: 25px;
260
- left: 50%;
261
- transform: translateX(-50%);
262
- background: #1e293be6;
263
- backdrop-filter: blur(12px);
264
- color: #fef9e6;
265
- padding: 12px 24px;
266
- border-radius: 50px;
267
- font-size: 0.85rem;
268
- font-weight: 500;
269
- z-index: 999;
270
- border-left: 4px solid #fdbb4d;
271
- pointer-events: none;
272
- opacity: 0;
273
- transition: opacity 0.2s;
274
- white-space: nowrap;
275
- }
276
-
277
- @media (max-width: 700px) {
278
- .container { padding: 1rem; }
279
- .brand h1 { font-size: 1.7rem; }
280
- .search-wrapper { padding-left: 1rem; }
281
- .search-wrapper input { font-size: 0.95rem; }
282
- .tab-btn { padding: 0.4rem 1rem; font-size: 0.8rem; }
283
- .search-submit span { display: none; }
284
- .search-submit { width: 48px; padding: 0; }
285
- .toast-msg { white-space: nowrap; font-size: 0.75rem; }
286
- }
287
-
288
- @keyframes pulseMic {
289
- 0% { box-shadow: 0 0 0 0 rgba(253, 187, 77, 0.6); }
290
- 70% { box-shadow: 0 0 0 12px rgba(253, 187, 77, 0); }
291
- 100% { box-shadow: 0 0 0 0 rgba(253, 187, 77, 0); }
292
- }
293
- .listening-active {
294
- animation: pulseMic 1.2s infinite;
295
- background: #fdbb4d !important;
296
- color: #121826 !important;
297
- }
298
- </style>
299
  </head>
300
  <body>
301
 
@@ -344,248 +55,6 @@
344
  </div>
345
  <div id="toastMsg" class="toast-msg"></div>
346
 
347
- <script>
348
- (function() {
349
- // Configuration: mapping tab -> container ID & CSE ID
350
- const config = {
351
- all: { container: 'cse-all', cx: '2342fc17cd34f4cba' },
352
- images: { container: 'cse-images', cx: '23c7fd94975714dd8' },
353
- news: { container: 'cse-news', cx: '35b0f5b6012e94f78' },
354
- videos: { container: 'cse-videos', cx: 'b11f5dceb04784b9f' }
355
- };
356
-
357
- let activeTab = 'all';
358
- let cseReady = { all: false, images: false, news: false, videos: false };
359
- let cseElements = {}; // store element references
360
-
361
- // DOM elements
362
- const searchInput = document.getElementById('searchInput');
363
- const searchBtn = document.getElementById('searchBtn');
364
- const voiceBtn = document.getElementById('voiceBtn');
365
- const tabs = document.querySelectorAll('.tab-btn');
366
-
367
- function showMessage(msg, isError = false) {
368
- const toast = document.getElementById('toastMsg');
369
- toast.textContent = msg;
370
- toast.style.opacity = '1';
371
- toast.style.background = isError ? '#b91c1ce6' : '#1e293be6';
372
- toast.style.borderLeftColor = isError ? '#ff8a5c' : '#fdbb4d';
373
- setTimeout(() => { toast.style.opacity = '0'; }, 2800);
374
- }
375
-
376
- // Load Google CSE API dynamically once (without default cx, we'll render manually)
377
- function loadGoogleCSE() {
378
- return new Promise((resolve, reject) => {
379
- if (window.google && window.google.search && window.google.search.cse) {
380
- resolve();
381
- return;
382
- }
383
- const script = document.createElement('script');
384
- // We must include a cx, but we will override with render. Use a dummy temporary cx.
385
- script.src = 'https://cse.google.com/cse.js?cx=2342fc17cd34f4cba'; // any valid one to load library
386
- script.async = true;
387
- script.onload = () => {
388
- // Wait for google.search.cse to be fully defined
389
- const checkInterval = setInterval(() => {
390
- if (window.google && window.google.search && window.google.search.cse && window.google.search.cse.element) {
391
- clearInterval(checkInterval);
392
- resolve();
393
- }
394
- }, 100);
395
- setTimeout(() => { clearInterval(checkInterval); reject(new Error('Timeout loading CSE')); }, 8000);
396
- };
397
- script.onerror = () => reject(new Error('Failed to load CSE script'));
398
- document.head.appendChild(script);
399
- });
400
- }
401
-
402
- // Render a specific CSE engine inside its container
403
- async function renderCSE(tabId) {
404
- if (cseReady[tabId]) return true;
405
- const cfg = config[tabId];
406
- if (!cfg) return false;
407
- const containerDiv = document.getElementById(cfg.container);
408
- if (!containerDiv) return false;
409
-
410
- // Clear any previous content
411
- containerDiv.innerHTML = '';
412
-
413
- return new Promise((resolve) => {
414
- try {
415
- google.search.cse.element.render({
416
- div: cfg.container,
417
- tag: 'searchresults-only',
418
- cx: cfg.cx,
419
- query: '' // start empty
420
- });
421
- // Wait a bit for the element to be registered
422
- setTimeout(() => {
423
- const element = google.search.cse.element.getElement(cfg.container);
424
- if (element) {
425
- cseElements[tabId] = element;
426
- cseReady[tabId] = true;
427
- resolve(true);
428
- } else {
429
- resolve(false);
430
- }
431
- }, 400);
432
- } catch(e) {
433
- console.error(e);
434
- resolve(false);
435
- }
436
- });
437
- }
438
-
439
- // Perform search on active tab
440
- async function performSearch(query) {
441
- if (!query.trim()) {
442
- showMessage('✨ Please enter a search term.', true);
443
- return;
444
- }
445
- const tab = activeTab;
446
- // Ensure CSE for this tab is rendered
447
- if (!cseReady[tab]) {
448
- showMessage(`Initializing ${tab} search engine...`, false);
449
- const ok = await renderCSE(tab);
450
- if (!ok) {
451
- showMessage(`Failed to load ${tab} search. Check your internet or CSE ID.`, true);
452
- return;
453
- }
454
- }
455
- const element = cseElements[tab];
456
- if (element && typeof element.execute === 'function') {
457
- element.execute(query);
458
- showMessage(`🔍 SurfGO: "${query}" in ${tab.toUpperCase()}`, false);
459
- } else {
460
- // Fallback: try to re-render with query
461
- const cfg = config[tab];
462
- const containerDiv = document.getElementById(cfg.container);
463
- if (containerDiv) {
464
- containerDiv.innerHTML = '';
465
- google.search.cse.element.render({
466
- div: cfg.container,
467
- tag: 'searchresults-only',
468
- cx: cfg.cx,
469
- query: query
470
- });
471
- showMessage(`🔄 Loading results for "${query}"...`, false);
472
- // update reference after render
473
- setTimeout(() => {
474
- const newEl = google.search.cse.element.getElement(cfg.container);
475
- if (newEl) cseElements[tab] = newEl;
476
- cseReady[tab] = true;
477
- }, 300);
478
- } else {
479
- showMessage('Cannot display results container', true);
480
- }
481
- }
482
- }
483
-
484
- // Switch active tab visually and prepare CSE if needed
485
- function switchTab(tabId) {
486
- activeTab = tabId;
487
- // Update UI
488
- tabs.forEach(btn => {
489
- if (btn.getAttribute('data-tab') === tabId) {
490
- btn.classList.add('active');
491
- } else {
492
- btn.classList.remove('active');
493
- }
494
- });
495
- // Show correct wrapper
496
- for (const [key, cfg] of Object.entries(config)) {
497
- const wrapper = document.getElementById(cfg.container);
498
- if (wrapper) {
499
- if (key === tabId) wrapper.classList.add('active');
500
- else wrapper.classList.remove('active');
501
- }
502
- }
503
- // If there's already a query, re-run search on new tab (great UX)
504
- const currentQuery = searchInput.value.trim();
505
- if (currentQuery !== "") {
506
- performSearch(currentQuery);
507
- } else {
508
- // ensure engine is ready silently
509
- if (!cseReady[tabId]) renderCSE(tabId);
510
- }
511
- }
512
-
513
- // Event handlers
514
- searchBtn.addEventListener('click', () => performSearch(searchInput.value.trim()));
515
- searchInput.addEventListener('keypress', (e) => {
516
- if (e.key === 'Enter') performSearch(searchInput.value.trim());
517
- });
518
-
519
- tabs.forEach(btn => {
520
- btn.addEventListener('click', () => {
521
- const tabId = btn.getAttribute('data-tab');
522
- if (tabId === activeTab) return;
523
- switchTab(tabId);
524
- });
525
- });
526
-
527
- // Voice search
528
- let recognition = null;
529
- let isListening = false;
530
- if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
531
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
532
- recognition = new SpeechRecognition();
533
- recognition.lang = 'en-US';
534
- recognition.continuous = false;
535
- recognition.interimResults = false;
536
- recognition.onstart = () => {
537
- isListening = true;
538
- voiceBtn.classList.add('listening-active');
539
- showMessage("🎙️ Listening... speak your query", false);
540
- };
541
- recognition.onend = () => {
542
- isListening = false;
543
- voiceBtn.classList.remove('listening-active');
544
- };
545
- recognition.onerror = (event) => {
546
- voiceBtn.classList.remove('listening-active');
547
- let errMsg = "Voice recognition failed";
548
- if (event.error === 'no-speech') errMsg = "No speech detected. Try again.";
549
- else if (event.error === 'not-allowed') errMsg = "Microphone permission denied.";
550
- showMessage(`🎤 ${errMsg}`, true);
551
- };
552
- recognition.onresult = (event) => {
553
- const transcript = event.results[0][0].transcript;
554
- if (transcript.trim()) {
555
- searchInput.value = transcript.trim();
556
- performSearch(transcript.trim());
557
- } else {
558
- showMessage("Could not understand, please type.", true);
559
- }
560
- };
561
- voiceBtn.addEventListener('click', () => {
562
- if (isListening) recognition.stop();
563
- else recognition.start();
564
- });
565
- } else {
566
- voiceBtn.addEventListener('click', () => showMessage("Voice search not supported in this browser.", true));
567
- }
568
-
569
- // Initialize: load CSE library, then pre-render active tab (All)
570
- async function init() {
571
- await loadGoogleCSE();
572
- // Pre-render the 'all' tab
573
- const ok = await renderCSE('all');
574
- if (ok) {
575
- showMessage("SurfGO is ready — search anything.", false);
576
- } else {
577
- showMessage("CSE initialization issue, but you can still try searching.", true);
578
- }
579
- // Preload other tabs in background (optional)
580
- setTimeout(() => {
581
- renderCSE('images');
582
- renderCSE('news');
583
- renderCSE('videos');
584
- }, 1000);
585
- searchInput.focus();
586
- }
587
- init();
588
- })();
589
- </script>
590
  </body>
591
  </html>
 
6
  <title>SurfGO — premium search engine by AnesNT</title>
7
  <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;400;500;600;700;800&display=swap" rel="stylesheet">
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
+ <link rel="stylesheet" href="styles.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  </head>
11
  <body>
12
 
 
55
  </div>
56
  <div id="toastMsg" class="toast-msg"></div>
57
 
58
+ <script src="script.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  </body>
60
  </html>