PageSourceSearch

https://pupwife.com/scripts/dependencies/accountPage.js

js pupwife.com collected 2026-09-30 15:05:43 UTC 12,984 bytes, 317 lines download raw bytes

1// ====================================
2// ACCOUNT PAGE FUNCTIONALITY
3// Handles account settings, password changes, and statistics
4// ====================================
5
6(function (global) {
7    'use strict';
8
9    var activeController = null;
10
11    function getEl(id, root) {
12        if (root) {
13            return root.querySelector('[data-account-id="' + id + '"]');
14        }
15        return document.getElementById(id);
16    }
17
18    function init(options) {
19        options = options || {};
20        var root = options.root || null;
21
22        if (activeController && typeof activeController.abort === 'function') {
23            activeController.abort();
24        }
25        activeController = typeof AbortController !== 'undefined' ? new AbortController() : null;
26        var signal = activeController ? activeController.signal : null;
27
28        if (typeof global.__sharedUsername === 'undefined') {
29            setTimeout(function () { init(options); }, 100);
30            return;
31        }
32
33        var username = global.__sharedUsername.get();
34        var hasPassword = localStorage.getItem('puppy_username_password') !== null;
35        var notLoggedIn = getEl('not-logged-in', root);
36        var accountInfo = getEl('account-info', root);
37
38        if (!username || !hasPassword) {
39            if (notLoggedIn) notLoggedIn.style.display = 'block';
40            if (accountInfo) accountInfo.style.display = 'none';
41            return;
42        }
43
44        if (notLoggedIn) notLoggedIn.style.display = 'none';
45        if (accountInfo) accountInfo.style.display = 'block';
46
47        var usernameEl = getEl('account-username', root);
48        if (usernameEl) usernameEl.textContent = username;
49
50        loadStatistics(username, root);
51        setupStatusBio(username, root, signal);
52        setupPasswordChange(username, root, signal);
53        setupLogout(root, options, signal);
54    }
55
56    function setupStatusBio(username, root, signal) {
57        var input = getEl('account-status-bio', root);
58        var countEl = getEl('account-status-count', root);
59        var saveBtn = getEl('account-status-save', root);
60        var errorEl = getEl('account-status-error', root);
61        if (!input || !saveBtn) return;
62
63        function updateCount() {
64            if (countEl) countEl.textContent = (input.value || '').length;
65        }
66
67        input.addEventListener('input', updateCount, signal ? { signal: signal } : false);
68
69        if (typeof firebase !== 'undefined' && firebase.database) {
70            firebase.database().ref('chatroom/userProfiles').child(username).once('value').then(function (snap) {
71                var data = snap.val();
72                var status = (data && typeof data.status === 'string') ? data.status : '';
73                input.value = status;
74                updateCount();
75            }).catch(function (err) {
76                console.warn('Could not load status:', err);
77            });
78        }
79
80        saveBtn.addEventListener('click', async function () {
81            var text = (input.value || '').trim().substring(0, 200);
82            if (errorEl) errorEl.style.display = 'none';
83            saveBtn.disabled = true;
84            saveBtn.textContent = 'Saving...';
85            try {
86                if (typeof firebase === 'undefined' || !firebase.database) {
87                    if (errorEl) {
88                        errorEl.textContent = 'Firebase not available.';
89                        errorEl.style.display = 'block';
90                    }
91                    return;
92                }
93                await firebase.database().ref('chatroom/userProfiles').child(username).update({
94                    status: text,
95                    statusUpdatedAt: Date.now()
96                });
97                if (errorEl) {
98                    errorEl.className = 'account-error account-status-success';
99                    errorEl.textContent = 'Status saved!';
100                    errorEl.style.display = 'block';
101                    setTimeout(function () {
102                        errorEl.style.display = 'none';
103                        errorEl.className = 'account-error';
104                    }, 3000);
105                }
106            } catch (err) {
107                console.error('Error saving status:', err);
108                if (errorEl) {
109                    errorEl.textContent = 'Failed to save. Try again.';
110                    errorEl.style.display = 'block';
111                }
112            } finally {
113                saveBtn.disabled = false;
114                saveBtn.textContent = 'Save Status';
115            }
116        }, signal ? { signal: signal } : false);
117    }
118
119    async function loadStatistics(username, root) {
120        var statsDiv = getEl('account-stats', root);
121        if (!statsDiv) return;
122
123        try {
124            var leaderboards = [
125                { name: 'Fish Food', ref: firebase.database().ref('fishFoodScores') },
126                { name: 'Plong', ref: firebase.database().ref('plongScores') },
127                { name: 'Breakout', ref: firebase.database().ref('breakoutScores') },
128                { name: 'Puggle', ref: firebase.database().ref('puggleScores') }
129            ];
130
131            var totalScores = 0;
132            var gamesPlayed = 0;
133            var highestScores = {};
134            var i;
135
136            for (i = 0; i < leaderboards.length; i++) {
137                var lb = leaderboards[i];
138                try {
139                    var snapshot = await lb.ref.orderByChild('name').equalTo(username).once('value');
140                    var scores = [];
141                    snapshot.forEach(function (child) {
142                        var entry = child.val();
143                        var score = entry.score || entry.coins || 0;
144                        scores.push(score);
145                        totalScores++;
146                    });
147
148                    if (scores.length > 0) {
149                        gamesPlayed++;
150                        highestScores[lb.name] = Math.max.apply(null, scores);
151                    }
152                } catch (error) {
153                    console.error('Error loading ' + lb.name + ' stats:', error);
154                }
155            }
156
157            var statsHTML = '<div class="account-stats-grid">';
158            statsHTML += '<div class="account-stat-item"><span class="account-stat-num">' + totalScores + '</span><span class="account-stat-label">Total Scores</span></div>';
159            statsHTML += '<div class="account-stat-item"><span class="account-stat-num">' + gamesPlayed + '</span><span class="account-stat-label">Games Played</span></div>';
160            statsHTML += '</div>';
161
162            var gameName;
163            if (Object.keys(highestScores).length > 0) {
164                statsHTML += '<div class="account-stats-highest">';
165                statsHTML += '<div class="account-stat-label" style="margin-bottom: 8px;">Highest Scores:</div>';
166                for (gameName in highestScores) {
167                    if (Object.prototype.hasOwnProperty.call(highestScores, gameName)) {
168                        statsHTML += '<div class="account-stat-row"><span>' + gameName + ':</span><strong class="account-stat-score">' + highestScores[gameName].toLocaleString() + '</strong></div>';
169                    }
170                }
171                statsHTML += '</div>';
172            }
173
174            statsDiv.innerHTML = statsHTML;
175        } catch (error) {
176            console.error('Error loading statistics:', error);
177            statsDiv.innerHTML = '<div class="account-text-muted account-error">Error loading statistics</div>';
178        }
179    }
180
181    function setupPasswordChange(username, root, signal) {
182        var changeBtn = getEl('change-password-btn', root);
183        var currentInput = getEl('current-password', root);
184        var newInput = getEl('new-password', root);
185        var confirmInput = getEl('confirm-password', root);
186        var errorMsg = getEl('password-error', root);
187
188        if (!changeBtn) return;
189
190        changeBtn.addEventListener('click', async function () {
191            var current = currentInput.value.trim();
192            var newPass = newInput.value.trim();
193            var confirm = confirmInput.value.trim();
194
195            errorMsg.style.display = 'none';
196            errorMsg.textContent = '';
197
198            if (!current || !newPass || !confirm) {
199                errorMsg.textContent = 'Please fill in all fields!';
200                errorMsg.style.display = 'block';
201                return;
202            }
203
204            if (newPass !== confirm) {
205                errorMsg.textContent = 'New passwords do not match!';
206                errorMsg.style.display = 'block';
207                return;
208            }
209
210            if (newPass.length < 3) {
211                errorMsg.textContent = 'Password must be at least 3 characters!';
212                errorMsg.style.display = 'block';
213                return;
214            }
215
216            try {
217                var usernamesRef = firebase.database().ref('usernames');
218                var usernameData = await usernamesRef.child(username).once('value');
219
220                if (!usernameData.exists()) {
221                    errorMsg.textContent = 'Account not found!';
222                    errorMsg.style.display = 'block';
223                    return;
224                }
225
226                var data = usernameData.val();
227                var storedPassword = typeof data === 'object' ? data.password : null;
228
229                if (!storedPassword || storedPassword !== current) {
230                    errorMsg.textContent = 'Current password is incorrect!';
231                    errorMsg.style.display = 'block';
232                    return;
233                }
234
235                var fingerprint = typeof data === 'object' ? data.fingerprint : data;
236                await usernamesRef.child(username).set({
237                    fingerprint: fingerprint,
238                    password: newPass
239                });
240
241                localStorage.setItem('puppy_username_password', newPass);
242
243                currentInput.value = '';
244                newInput.value = '';
245                confirmInput.value = '';
246
247                errorMsg.className = 'account-error account-status-success';
248                errorMsg.textContent = 'Password changed successfully!';
249                errorMsg.style.display = 'block';
250
251                setTimeout(function () {
252                    errorMsg.style.display = 'none';
253                    errorMsg.className = 'account-error';
254                }, 3000);
255            } catch (error) {
256                console.error('Error changing password:', error);
257                errorMsg.textContent = 'Error changing password. Please try again.';
258                errorMsg.style.display = 'block';
259            }
260        }, signal ? { signal: signal }
260 : false);
261    }
262
263    function setupLogout(root, options, signal) {
264        var logoutBtn = getEl('logout-btn', root);
265        if (!logoutBtn) return;
266
267        logoutBtn.addEventListener('click', function () {
268            if (!confirm('Are you sure you want to log out? You will need to enter your password to log back in.')) {
269                return;
270            }
271            if (options && typeof options.onLogout === 'function') {
272                if (global.__sharedUsername && typeof global.__sharedUsername.clear === 'function') {
273                    global.__sharedUsername.clear();
274                } else {
275                    localStorage.removeItem('puppy_shared_username');
276                    localStorage.removeItem('puppy_username_password');
277                    localStorage.removeItem('puppy_guest_mode');
278                }
279                if (global.AccountIndicator && typeof global.AccountIndicator.update === 'function') {
280                    global.AccountIndicator.update();
281                }
282                try {
283                    global.dispatchEvent(new CustomEvent('usernameSet', { detail: { username: null } }));
284                } catch (e) { /* ignore */ }
285                options.onLogout();
286                return;
287            }
288            if (global.__sharedUsername && typeof global.__sharedUsername.logout === 'function') {
289                global.__sharedUsername.logout();
290            } else {
291                localStorage.removeItem('puppy_shared_username');
292                localStorage.removeItem('puppy_username_password');
293                global.location.reload();
294            }
295        }, signal ? { signal: signal } : false);
296    }
297
298    function destroy() {
299        if (activeController && typeof activeController.abort === 'function') {
300            activeController.abort();
301        }
302        activeController = null;
303    }
304
305    global.AccountPage = {
306        init: init,
307        destroy: destroy
308    };
309
310    if (document.body && document.body.classList.contains('account-page')) {
311        if (document.readyState === 'loading') {
312            document.addEventListener('DOMContentLoaded', function () { init(); });
313        } else {
314            init();
315        }
316    }
317})(typeof window !== 'undefined' ? window : this);

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.