PageSourceSearch

https://sanjeevsharmasan.github.io/assets/js/golden-retriever-guide.js

js sanjeevsharmasan.github.io collected 2026-10-03 09:53:58 UTC 17,689 bytes, 483 lines download raw bytes

1/**
2 * Golden Retriever Finance Guide - Above Yoda Icon
3 * Positioned: above Yoda, right 20px with gap
4 * Z-index: 1001 (above Yoda 997)
5 */
6
7(function() {
8    // Inject styles first
9    const style = document.createElement('style');
10    style.textContent = `
11        .gr-container { 
12            position: fixed; 
13            bottom: 516px; 
14            right: 50px; 
15            z-index: 1001; 
16            pointer-events: auto;
17            user-select: none;
18        }
19        
20        .gr-dog { 
21            width: 100px; 
22            height: 100px; 
23            cursor: grab; 
24            animation: breathe 3s infinite, sniff 0.8s infinite; 
25            pointer-events: all;
26        }
27        
28        .gr-dog:active {
29            cursor: grabbing;
30        }
31        
32        .gr-bubble { 
33            position: absolute; 
34            bottom: 110px; 
35            left: 50%;
36            transform: translateX(-50%);
37            background: linear-gradient(135deg, #FFB366, #FF9999); 
38            color: white; 
39            padding: 12px 16px; 
40            border-radius: 12px; 
41            max-width: 280px; 
42            font-size: 14px; 
43            z-index: 1001; 
44            animation: slideIn 0.4s ease-out; 
45            pointer-events: none;
46            box-shadow: 0 6px 20px rgba(0,0,0,0.2);
47            white-space: normal;
48        }
49        
50        .gr-menu { 
51            position: absolute; 
52            bottom: 110px; 
53            left: 50%;
54            transform: translateX(-50%);
55            background: white; 
56            padding: 16px; 
57            border-radius: 12px; 
58            box-shadow: 0 8px 30px rgba(0,0,0,0.25); 
59            z-index: 1001; 
60            min-width: 220px;
61            pointer-events: all;
62        }
63        
64        .gr-btn { 
65            display: block; 
66            background: linear-gradient(135deg, #1e3c72, #2a5298); 
67            color: white; 
68            border: none; 
69            padding: 12px 14px; 
70            border-radius: 8px; 
71            margin: 6px 0; 
72            font-size: 13px; 
73            font-weight: 600; 
74            cursor: pointer; 
75            width: 100%; 
76            text-align: left; 
77            transition: all 0.3s; 
78            pointer-events: auto;
79        }
80        
81        .gr-btn:hover { 
82            transform: translateX(4px); 
83            box-shadow: 0 4px 12px rgba(30, 60, 114, 0.3); 
84        }
85        
86        .gr-close { 
87            position: absolute; 
88            top: 8px; 
89            right: 8px; 
90            width: 28px; 
91            height: 28px; 
92            border-radius: 50%; 
93            background: #FF6B6B; 
94            color: white; 
95            border: none; 
96            cursor: pointer; 
97            font-weight: bold;
98            pointer-events: auto;
99            font-size: 16px;
100        }
101        
102        .gr-close:hover {
103            background: #FF5252;
104        }
105        
106        @keyframes breathe { 
107            0%, 100% { transform: scale(1); } 
108            50% { transform: scale(1.03); } 
109        }
110        
111        @keyframes slideIn { 
112            0% { opacity: 0; transform: translateY(10px); } 
113            100% { opacity: 1; transform: translateY(0); } 
114        }
115        
116        @keyframes wag { 
117            0%, 100% { transform: rotate(0deg); } 
118            50% { transform: rotate(-30deg); } 
119        }
120        
121        @keyframes sniff {
122            0%, 100% { transform: translateY(0); }
123            25% { transform: translateY(-4px); }
124            50% { transform: translateY(4px); }
125            75% { transform: translateY(-2px); }
126        }
127        
128        @keyframes pant { 
129            0%, 100% { opacity: 1; } 
130            50% { opacity: 0.7; } 
131        }
132        
133        @keyframes coinFall {
134            0% {
135                transform: translateY(0) translateX(0) rotate(0deg) scale(1);
136                opacity: 1;
137            }
138            100% {
139                transform: translateY(300px) translateX(var(--spread-x, 0)) rotate(720deg) scale(0.7);
140                opacity: 0;
141            }
142        }
143
144        @keyframes mobileWalk {
145            0%, 100% { transform: translateX(0) translateY(0); }
146            25% { transform: translateX(20px) translateY(-4px); }
147            50% { transform: translateX(0) translateY(0); }
148            75% { transform: translateX(-20px) translateY(-4px); }
149        }
150        
151        .gr-tongue { 
152            animation: pant 1.2s infinite; 
153        }
154        
155        .gr-tail { 
156            animation: wag 0.8s infinite; 
157            transform-origin: 50% 120px; 
158        }
159        
160        .gr-dog.sniffing {
161            animation: sniff 0.6s infinite !important;
162        }
163        
164        .gr-coin {
165            position: fixed;
166            width: 16px;
167            height: 16px;
168            background: radial-gradient(circle at 30% 30%, #FFD700, #FFA500);
169            border-radius: 50%;
170            border: 1px solid #FF8C00;
171            pointer-events: none;
172            z-index: 1000;
173            box-shadow: inset -2px -2px 3px rgba(0, 0, 0, 0.3), 2px 2px 3px rgba(0, 0, 0, 0.2);
174        }
175        
176        /* Mobile Optimization */
177        @media (max-width: 768px) { 
178            .gr-container { 
179                bottom: 410px; 
180                right: 10px;
181            } 
182            
183            .gr-dog { 
184                width: 75px; 
185                height: 75px; 
186            } 
187            
188            .gr-bubble { 
189                bottom: 90px;
190                left: 50%;
191                transform: translateX(-50%);
192                max-width: 240px;
193                font-size: 12px;
194                padding: 10px 12px;
195            } 
196            
197            .gr-menu { 
198                bottom: 90px;
199                left: 50%;
200                transform: translateX(-50%);
201                min-width: 200px;
202                padding: 12px;
203            }
204            
205            .gr-btn {
206                padding: 10px 12px;
207                margin: 4px 0;
208                font-size: 12px;
209            }
210        }
211        
212        @media (max-width: 480px) {
213            .gr-container {
214                bottom: 336px;
215                right: 111px;
216            }
217        }
218    `;
219    document.head.appendChild(style);
220    
221    class GoldenRetriever {
222        constructor() {
223            this.messages = ["🐕 Woof! How I can Help you?", "1️⃣ YodaBot for Queries\n2️⃣ Booking for Packages\n3️⃣ Topmate for Coaching"];
224            this.isDragging = false;
225            this.dragOffsetX = 0;
226            this.dragOffsetY = 0;
227            this.savedPosition = this.getSavedPosition();
228            this.init();
229        }
230        
231        getSavedPosition() {
232            try {
233                const saved = localStorage.getItem('grDogPosition');
234                return saved ? JSON.parse(saved) : null;
235            } catch (e) {
236                return null;
237            }
238        }
239        
240        savePosition(bottom, right) {
241            try {
242                localStorage.setItem('grDogPosition', JSON.stringify({ bottom, right }));
243            } catch (e) {
244                // Silently fail if localStorage is not available
245            }
246        }
247        
248        init() {
249            const html = `
250                <div class="gr-container">
251                    <svg class="gr-dog" viewBox="0 0 200 200"><ellipse cx="100" cy="130" rx="45" ry="50" fill="#D4A574"/><ellipse cx="100" cy="110" rx="40" ry="45" fill="#E8C89F"/><circle cx="100" cy="70" r="35" fill="#E8C89F"/><ellipse cx="100" cy="85" rx="20" ry="16" fill="#F0D9B5"/><ellipse cx="100" cy="82" rx="8" ry="7" fill="#4A4A4A"/><circle cx="92" cy="62" r="5" fill="#4A4A4A"/><circle cx="108" cy="62" r="5" fill="#4A4A4A"/><circle cx="93" cy="60" r="2" fill="#fff"/><circle cx="109" cy="60" r="2" fill="#fff"/><ellipse cx="75" cy="50" rx="15" ry="25" fill="#D4A574"/><ellipse cx="125" cy="50" rx="15" ry="25" fill="#D4A574"/><path d="M 95 95 Q 100 100 105 95" stroke="#8B6B47" stroke-width="2" fill="none" stroke-linecap="round"/><ellipse cx="100" cy="105" rx="6" ry="8" fill="#FF6B9D" class="gr-tongue"/><path class="gr-tail" d="M 50 120 Q 35 110 40 90" stroke="#D4A574" stroke-width="12" fill="none" stroke-linecap="round"/></svg>
252                    <div id="gr-bubble" class="gr-bubble" style="display:none;"></div>
253                    <div id="gr-menu" class="gr-menu" style="display:none;">
254                        <button class="gr-btn" id="gr-chat-btn">💬 Click YodaBot for any Query</button>
255                        <button class="gr-btn" id="gr-coaching-btn">📦 Click Booking for Packages</button>
256                        <button class="gr-btn" id="gr-services-btn">🎯 Click Topmate for Coaching</button>
257                        <button class="gr-close" id="gr-close-btn">✕</button>
258                    </div>
259                </div>
260            `;
261            document.body.insertAdjacentHTML('beforeend', html);
262            
263            // Restore saved position
264            const container = document.querySelector('.gr-container');
265            if (this.savedPosition && container) {
266                container.style.bottom = this.savedPosition.bottom + 'px';
267                container.style.right = this.savedPosition.right + 'px';
268            }
269            
270            // Set up event listeners with proper scope
271            const dog = document.querySelector('.gr-dog');
272            if (dog) {
273                dog.addEventListener('mousedown', (e) => this.startDrag(e));
274                dog.addEventListener('touchstart', (e) => this.startDrag(e));
275                dog.addEventListener('click', (e) => {
276                    // Only open menu if not dragging
277                    if (!this.isDragging) {
278                        this.showMenu();
279                    }
280                });
281            }
282            
283            const chatBtn = document.getElementById('gr-chat-btn');
284            const coachingBtn = document.getElementById('gr-coaching-btn');
285            const servicesBtn = document.getElementById('gr-services-btn');
286            const closeBtn = document.getElementById('gr-close-btn');
287            
288            if (chatBtn) chatBtn.addEventListener('click', () => this.chat());
289            if (coachingBtn) coachingBtn.addEventListener('click', () => this.coaching());
290            if (servicesBtn) servicesBtn.addEventListener('click', () => this.services());
291            if (closeBtn) closeBtn.addEventListener('click', () => this.hideMenu());
292            
293            // Add drag listeners globally
294            document.addEventListener('mousemove', (e) => this.drag(e));
295            document.addEventListener('touchmove', (e) => this.drag(e), { passive: false });
296            document.addEventListener('mouseup', () =>
296 this.stopDrag());
297            document.addEventListener('touchend', () => this.stopDrag());
298            
299            setTimeout(() => this.welcome(), 1500);
300        }
301        
302        startDrag(e) {
303            this.isDragging = true;
304            const container = document.querySelector('.gr-container');
305            if (!container) return;
306            
307            const rect = container.getBoundingClientRect();
308            const clientX = e.touches ? e.touches[0].clientX : e.clientX;
309            const clientY = e.touches ? e.touches[0].clientY : e.clientY;
310            
311            this.dragOffsetX = clientX - rect.left;
312            this.dragOffsetY = clientY - rect.top;
313            
314            document.querySelector('.gr-dog').style.cursor = 'grabbing';
315        }
316        
317        drag(e) {
318            if (!this.isDragging) return;
319            
320            const container = document.querySelector('.gr-container');
321            if (!container) return;
322            
323            const clientX = e.touches ? e.touches[0].clientX : e.clientX;
324            const clientY = e.touches ? e.touches[0].clientY : e.clientY;
325            
326            const x = clientX - this.dragOffsetX;
327            const y = clientY - this.dragOffsetY;
328            
329            // Keep within viewport bounds
330            const viewportWidth = window.innerWidth;
331            const viewportHeight = window.innerHeight;
332            const containerWidth = 100;
333            const containerHeight = 100;
334            
335            const boundedX = Math.max(0, Math.min(x, viewportWidth - containerWidth));
336            const boundedY = Math.max(0, Math.min(y, viewportHeight - containerHeight));
337            
338            container.style.left = boundedX + 'px';
339            container.style.right = 'auto';
340            container.style.top = boundedY + 'px';
341            container.style.bottom = 'auto';
342        }
343        
344        stopDrag() {
345            if (this.isDragging) {
346                this.isDragging = false;
347                const container = document.querySelector('.gr-container');
348                if (container) {
349                    const rect = container.getBoundingClientRect();
350                    // Save position as left/top instead of bottom/right when dragged
351                    this.savePosition(null, null);
352                    // But store actual pixel position
353                    try {
354                        localStorage.setItem('grDogPosition', JSON.stringify({ 
355                            left: rect.left, 
356                            top: rect.top 
357                        }));
358                    } catch (e) {
359                        // Silently fail
360                    }
361                }
362                document.querySelector('.gr-dog').style.cursor = 'grab';
363            }
364        }
365        
366        welcome() {
367            this.showBubble(this.messages[0]);
368            setTimeout(() => {
369                this.showBubble(this.messages[1]);
370                setTimeout(() => this.showMenu(), 3500);
371            }, 3500);
372        }
373        
374        showBubble(text) {
375            const bubble = document.getElementById('gr-bubble');
376            if (bubble) {
377                bubble.textContent = text;
378                bubble.style.display = 'block';
379                setTimeout(() => {
380                    if (bubble.style.display !== 'none') {
381                        bubble.style.display = 'none';
382                    }
383                }, 3000);
384            }
385        }
386        
387        showMenu() {
388            const bubble = document.getElementById('gr-bubble');
389            const menu = document.getElementById('gr-menu');
390            const dog = document.querySelector('.gr-dog');
391            
392            if (bubble) bubble.style.display = 'none';
393            if (menu) menu.style.display = 'block';
394            
395            // Start sniffing and throw coins
396            this.sniff();
397            this.throwCoins();
398        }
399        
400        hideMenu() {
401            const menu = document.getElementById('gr-menu');
402            if (menu) menu.style.display = 'none';
403        }
404        
405        chat() {
406            this.hideMenu();
407            const yoda = document.querySelector('.yoda-chatbot-btn');
408            if (yoda) {
409                setTimeout(() =>
409 yoda.click(), 100);
410            } else {
411                this.showBubble('💬 YodaBot not found!');
412            }
413        }
414        
415        coaching() { 
416            this.hideMenu();
417            window.open('https://topmate.io/sanjeev_kumar_sharma', '_blank');
418        }
419        
420        services() { 
421            this.hideMenu();
422            window.open('https://sanjeevsharmasan.github.io/postmate-bookings-package.html', '_blank');
423        }
424        
425        sniff() {
426            const dog = document.querySelector('.gr-dog');
427            if (!dog) return;
428            
429            dog.classList.add('sniffing');
430            setTimeout(() => {
431                dog.classList.remove('sniffing');
432            }, 2000);
433        }
434        
435        throwCoins() {
436            const container = document.querySelector('.gr-container');
437            if (!container) return;
438            
439            const rect = container.getBoundingClientRect();
440            const dogX = rect.left + rect.width / 2;
441            const dogY = rect.top;
442            
443            // Throw 3-5 coins
444            const coinCount = 3 + Math.floor(Math.random() * 3);
445            for (let i = 0; i < coinCount; i++) {
446                setTimeout(() => {
447                    const coin = document.createElement('div');
448                    coin.className = 'gr-coin';
449                    
450                    // Random starting position near dog
451                    const offsetX = (Math.random() - 0.5) * 40;
452                    coin.style.left = (dogX + offsetX) + 'px';
453                    coin.style.top = dogY + 'px';
454                    
455                    // Random horizontal spread
456                    const spreadX = (Math.random() - 0.5) * 200;
457                    const duration = 1.8 + Math.random() * 0.4;
458                    
459                    coin.style.animation = `coinFall ${duration}s ease-in forwards`;
460                    coin.style.setProperty('--spread-x', spreadX + 'px');
461                    
462                    document.body.appendChild(coin);
463                    
464                    // Clean up after animation
465                    setTimeout(() => coin.remove(), duration * 1000);
466                }, i * 70);
467            }
468        }
469        
470        show() {
471            const container = document.querySelector('.gr-container');
472            if (container) container.style.display = 'block';
473        }
474        
475        hide() {
476            const container = document.querySelector('.gr-container');
477            if (container) container.style.display = 'none';
478        }
479    }
480    
481    window.gr = new GoldenRetriever();
482    window.goldenRetriever = window.gr;
483})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.