PageSourceSearch

https://score-international.com/js/push-notifications-v2.js

js score-international.com collected 2026-10-02 10:25:29 UTC 9,834 bytes, 303 lines download raw bytes

1/**
2 * Enhanced SCORE Push Notifications with Anonymous User Support
3 * Version 2.0
4 */
5
6class SCOREPushNotifications {
7    constructor() {
8        this.config = this.getConfig();
9        this.vapidPublicKey = 'BIHx4pRAwO2v8HT6tX1G1CcPwWPTU_gtMHiJd1HmQfSShLEdrSDH3jAzSTDluMuYIGdShMTw0ndsSpUb99g4Fic';
10        this.registration = null;
11        this.subscription = null;
12        this.anonymousUserId = this.getAnonymousUserId();
13        this.userId = null;
14    }
15
16    /**
17     * Get or generate anonymous user ID
18     */
19    getAnonymousUserId() {
20        let anonymousId = localStorage.getItem('score_anonymous_id');
21        if (!anonymousId) {
22            anonymousId = 'anon_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
23            localStorage.setItem('score_anonymous_id', anonymousId);
24        }
25        return anonymousId;
26    }
27
28    /**
29     * Get configuration based on hostname
30     */
31    getConfig() {
32        const hostname = window.location.hostname;
33        const configs = {
34            'score-international.com': {
35                hostname: 'score-international.com',
36                API_BASE_URL: '/notify-api/api.php',
37                useProxy: true
38            },
39            'score-development.com': {
40                hostname: 'score-development.com',
41                API_BASE_URL: '/notify-api/api.php',
42                useProxy: true
43            },
44            'localhost': {
45                hostname: 'localhost',
46                API_BASE_URL: 'http://54.188.219.133:8000',
47                useProxy: false
48            }
49        };
50
51        const config = configs[hostname] || configs['score-international.com'];
52        console.log('Push Notifications Config:', config);
53        console.log('Anonymous User ID:', this.anonymousUserId);
54        return config;
55    }
56
57    /**
58     * Make API request with anonymous user header
59     */
60    async apiRequest(endpoint, options = {}) {
61        const url = this.config.useProxy 
62            ? `${this.config.API_BASE_URL}?path=${encodeURIComponent(endpoint)}`
63            : `${this.config.API_BASE_URL}${endpoint}`;
64
65        const headers = {
66            'Content-Type': 'application/json',
67            'X-Anonymous-User-ID': this.anonymousUserId,
68            ...options.headers
69        };
70
71        const response = await fetch(url, {
72            ...options,
73            headers
74        });
75
76        if (!response.ok) {
77            const error = await response.text();
78            throw new Error(error);
79        }
80
81        return response.json();
82    }
83
84    /**
85     * Initialize push notifications
86     */
87    async init() {
88        console.log('Initializing SCORE Push Notifications...');
89        console.log('Using anonymous user ID:', this.anonymousUserId);
90
91        if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
92            console.error('Push notifications not supported');
93            return false;
94        }
95
96        try {
97            // Register service worker
98            this.registration = await navigator.serviceWorker.register('/sw.js');
99            console.log('Service Worker registered:', this.registration);
100
101            // Check current subscription
102            await this.checkSubscription();
103            
104            return true;
105        } catch (error) {
106            console.error('Failed to initialize push notifications:', error);
107            return false;
108        }
109    }
110
111    /**
112     * Check current subscription status
113     */
114    async checkSubscription() {
115        try {
116            this.subscription = await this.registration.pushManager.getSubscription();
117            
118            if (this.subscription) {
119                console.log('Existing subscription found');
120                // Verify with server
121                try {
122                    const response = await this.apiRequest('/api/push/verify', {
123                        method: 'POST',
124                        body: JSON.stringify({
125                            endpoint: this.subscription.endpoint,
126                            anonymous_id: this.anonymousUserId
127                        })
128                    });
129                    
130                    if (response.userId) {
131                        this.userId = response.userId;
132                        console.log('Subscription verified for user:', this.userId);
133                    }
134                } catch (error) {
135                    console.log('Subscription not found on server');
136                }
137            } else {
138                console.log('No existing subscription');
139            }
140            
141            this.updateUI();
142        } catch (error) {
143            console.error('Error checking subscription:', error);
144        }
145    }
146
147    /**
148     * Toggle push subscription
149     */
150    async toggleSubscription() {
151        if (this.subscription) {
152            await this.unsubscribeUser();
153        } else {
154            await this.subscribeUser();
155        }
156    }
157
158    /**
159     * Subscribe user to push notifications
160     */
161    async subscribeUser() {
162        try {
163            // Request notification permission
164            const permission = await Notification.requestPermission();
165            if (permission !== 'granted') {
166                throw new Error('Notification permission denied');
167            }
168
169            // Create subscription
170            const subscription = await this.registration.pushManager.subscribe({
171                userVisibleOnly: true,
172                applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicKey)
173            });
174
175            console.log('Push subscription created:', subscription);
176
177            // Send to server
178            console.log('Saving subscription for anonymous user:', this.anonymousUserId);
179            const response = await this.apiRequest('/api/push/subscribe', {
180                method: 'POST',
181                body: JSON.stringify({
182                    subscription: subscription.toJSON(),
183                    anonymous_id: this.anonymousUserId
184                })
185            });
186
187            console.log('Server response:', response);
188            
189            if (response.userId) {
190                this.userId = response.userId;
191                this.subscription = subscription;
192                console.log('Successfully subscribed! User ID:', this.userId);
193                this.updateUI();
194            } else {
195                throw new Error('Failed to save subscription on server');
196            }
197        } catch (error) {
198            console.error('Failed to subscribe:', error);
199            this.updateUI();
200            throw error;
201        }
202    }
203
204    /**
205     * Unsubscribe user from push notifications
206     */
207    async unsubscribeUser() {
208        try {
209            if (!this.subscription) return;
210
211            // Unsubscribe from push manager
212            await this.subscription.unsubscribe();
213
214            // Notify server
215            await this.apiRequest('/api/push/unsubscribe', {
216                method: 'POST',
217                body: JSON.stringify({
218                    endpoint: this.subscription.endpoint,
219                    anonymous_id: this.anonymousUserId,
220                    user_id: this.userId
221                })
222            });
223
224            this.subscription = null;
225            this.userId = null;
226            console.log('Successfully unsubscribed');
227            this.updateUI();
228        } catch (error) {
229            console.error('Failed to unsubscribe:', error);
230            this.updateUI();
231        }
232    }
233
234    /**
235     * Update UI based on subscription status
236     */
237    updateUI() {
238        // Support both old and new element IDs for compatibility
239        const button = document.getElementById('push-toggle-btn') || 
240                       document.getElementById('push-notification-btn');
241        const status = document.getElementById('push-status') || 
242                       document.getElementById('push-notification-status');
243        
244        if (button) {
245            if (this.subscription) {
246                button.textContent = 'Disable Push Notifications';
247                button.classList.remove('btn-primary');
248                button.classList.add('btn-secondary');
249            } else {
250                button.textContent = 'Enable Push Notifications';
251                button.classList.remove('btn-secondary');
252                button.classList.add('btn-primary');
253            }
254        }
255        
256        if (status) {
257            if (this.subscription) {
258                status.textContent = `Push notifications enabled (User: ${this.userId || 'Anonymous'})`;
259                status.className = 'alert alert-success';
260            } else {
261                status.textContent = 'Push notifications disabled';
262                status.className = 'alert alert-info';
263            }
264        }
265    }
266
267    /**
268     * Convert VAPID key
269     */
270    urlBase64ToUint8Array(base64String) {
271        const padding = '='.repeat((4 - base64String.length % 4) % 4);
272        const base64 = (base64String + padding)
273            .replace(/\-/g, '+')
274            .replace(/_/g, '/');
275
276        const rawData = window.atob(base64);
277        const outputArray = new Uint8Array(rawData.length);
278
279        for (let i = 0; i < rawData.length; ++i) {
280            outputArray[i] = rawData.charCodeAt(i);
281        }
282        return outputArray;
283    }
284}
285
286// Initialize on page load
287let scorePushNotifications;
288
289document.addEventListener('DOMContentLoaded', async () => {
290    scorePushNotifications = new SCOREPushNotifications();
291    await scorePushNotifications.init();
292});
293
294// Global function for button onclick
295async function togglePushNotifications() {
296    if (!scorePushNotifications) return;
297    
298    try {
299        await scorePushNotifications.toggleSubscription();
300    } catch (error) {
301        alert('Failed to toggle push notifications: ' + error.message);
302    }
303}

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.