PageSourceSearch

https://pupwife.com/scripts/desktop/citrusAccountWindow.js

js pupwife.com collected 2026-09-30 15:05:56 UTC 8,554 bytes, 183 lines download raw bytes

1/**
2 * CitrusOS Account window — retro-styled account panel (replaces account.html navigation on index).
3 */
4(function (global) {
5    'use strict';
6
7    var ACCOUNT_AUX_ID = 'account';
8    var CSS_HREF = '/css/citrusAccount.css';
9    var activeRoot = null;
10
11    function ensureCss() {
12        if (document.querySelector('link[data-citrus-account-css]')) return;
13        var link = document.createElement('link');
14        link.rel = 'stylesheet';
15        link.href = CSS_HREF;
16        link.setAttribute('data-citrus-account-css', '1');
17        document.head.appendChild(link);
18    }
19
20    function isLoggedIn() {
21        if (global.AccountIndicator && typeof global.AccountIndicator.isLoggedIn === 'function') {
22            return global.AccountIndicator.isLoggedIn();
23        }
24        var username = localStorage.getItem('puppy_shared_username');
25        var hasPassword = localStorage.getItem('puppy_username_password') !== null;
26        var isGuestMode = localStorage.getItem('puppy_guest_mode') === 'true';
27        return !!(username && hasPassword && !isGuestMode);
28    }
29
30    function loggedInTemplate() {
31        return (
32            '<div class="citrus-account-window__scroll">' +
33            '<header class="citrus-account-window__header">' +
34            '<i class="fa-solid fa-user-gear citrus-account-window__header-icon" aria-hidden="true"></i>' +
35            '<h2 class="citrus-account-window__title">Account Settings</h2>' +
36            '</header>' +
37            '<div data-account-id="account-info" class="citrus-account-window__body">' +
38            '<section class="citrus-account-section">' +
39            '<h3 class="citrus-account-section__title">Account Information</h3>' +
40            '<div class="citrus-account-row"><span class="citrus-account-label">Username</span><span data-account-id="account-username" class="citrus-account-value"></span></div>' +
41            '<div class="citrus-account-row"><span class="citrus-account-label">Status</span><span class="citrus-account-value citrus-account-value--ok"><i class="fa-solid fa-lock" aria-hidden="true"></i> Password protected</span></div>' +
42            '</section>' +
43            '<section class="citrus-account-section">' +
44            '<h3 class="citrus-account-section__title">Status / Bio</h3>' +
45            '<p class="citrus-account-hint">A short line other users can see on Who\'s Here and in chat.</p>' +
46            '<label class="citrus-account-field"><input type="text" data-account-id="account-status-bio" class="citrus-account-input" maxlength="200" placeholder="e.g. vibing with the fish"></label>' +
47            '<p class="citrus-account-char"><span data-account-id="account-status-count">0</span>/200</p>' +
48            '<p data-account-id="account-status-error" class="citrus-account-msg citrus-account-msg--error" style="display: none;"></p>' +
49            '<button type="button" data-account-id="account-status-save" class="citrus-account-btn citrus-account-btn--primary">Save Status</button>' +
50            '</section>' +
51            '<section class="citrus-account-section">' +
52            '<h3 class="citrus-account-section__title">Change Password</h3>' +
53            '<label class="citrus-account-field"><span class="citrus-account-label">Current password</span><input type="password" data-account-id="current-password" class="citrus-account-input" autocomplete="current-password"></label>' +
54            '<label class="citrus-account-field"><span class="citrus-account-label">New password</span><input type="password" data-account-id="new-password" class="citrus-account-input" autocomplete="new-password"></label>' +
55            '<label class="citrus-account-field"><span class="citrus-account-label">Confirm new password</span><input type="password" data-account-id="confirm-password" class="citrus-account-input" autocomplete="new-password"></label>' +
56            '<p data-account-id="password-error" class="citrus-account-msg citrus-account-msg--error" style="display: none;"></p>' +
57            '<button type="button" data-account-id="change-password-btn" class="citrus-account-btn citrus-account-btn--primary">Change Password</button>' +
58            '</section>' +
59            '<section class="citrus-account-section">' +
60            '<h3 class="citrus-account-section__title">Your Statistics</h3>' +
61            '<div data-account-id="account-stats" class="citrus-account-stats"><div class="citrus-account-hint">Loading statistics...</div></div>' +
62            '</section>' +
63            '<section class="citrus-account-section citrus-account-section--logout">' +
64            '<button type="button" data-account-id="logout-btn" class="citrus-account-btn citrus-account-btn--danger">Log Out</button>' +
65            '</section>' +
66            '</div>' +
67            '<div data-account-id="not-logged-in" hidden></div>' +
68            '</div>'
69        );
70    }
71
72    function authTemplate() {
73        return (
74            '<div class="citrus-account-window__scroll">' +
75            '<header class="citrus-account-window__header">' +
76            '<i class="fa-solid fa-user citrus-account-window__header-icon" aria-hidden="true"></i>' +
77            '<h2 class="citrus-account-window__title">User Account</h2>' +
78            '</header>' +
79            '<div data-citrus-account-auth-root></div>' +
80            '</div>'
81        );
82    }
83
84    function mountAuth(root) {
85        var authRoot = root.querySelector('[data-citrus-account-auth-root]');
86        if (!authRoot || !global.__sharedUsername || typeof global.__sharedUsername.mountCitrusAuth !== 'function') {
87            return;
88        }
89        global.__sharedUsername.mountCitrusAuth(authRoot, {
90            onSuccess: function () {
91                renderContent(root);
92            }
93        });
94    }
95
96    function renderContent(root) {
97        if (!root) return;
98        if (global.AccountPage && typeof global.AccountPage.destroy === 'function') {
99            global.AccountPage.destroy();
100        }
101        root.innerHTML = isLoggedIn() ? loggedInTemplate() : authTemplate();
102        if (isLoggedIn()) {
103            if (global.AccountPage && typeof global.AccountPage.init === 'function') {
104                global.AccountPage.init({
105                    root: root,
106                    onLogout: function () {
107                        renderContent(root);
108                    }
109                });
110            }
111        } else {
112            mountAuth(root);
113        }
114    }
115
116    function open() {
117        if (!global.CitrusDesktop || typeof global.CitrusDesktop.openAuxWindow !== 'function') {
118            if (global.__sharedUsername && typeof global.__sharedUsername.promptAgain === 'function') {
119                global.__sharedUsername.promptAgain();
120            } else {
121                global.location.href = 'account.html';
122            }
123            return;
124        }
125
126        ensureCss();
127
128        var shellW = Math.min(460, Math.max(360, global.innerWidth - 48));
129        var shellH = Math.min(620, Math.max(420, global.innerHeight - 120));
130        var posX = Math.max(16, Math.round((global.innerWidth - shellW) / 2));
131        var posY = Math.max(16, Math.round((global.innerHeight - shellH - 72) / 2));
132
133        var win = global.CitrusDesktop.openAuxWindow({
134            id: ACCOUNT_AUX_ID,
135            label: 'Account',
136            icon: 'fa-solid fa-user',
137            w: shellW,
138            h: shellH,
139            x: posX,
140            y: posY,
141            nearChat: false,
142            onMount: function (contentEl) {
143                activeRoot = contentEl;
144                contentEl.className = 'citrus-account-window';
145                renderContent(contentEl);
146            },
147            onClose: function () {
148                if (global.AccountPage && typeof global.AccountPage.destroy === 'function') {
149                    global.AccountPage.destroy();
150                }
151                activeRoot = null;
152            }
153        });
154
155        if (win && win.contentEl) {
156            activeRoot = win.contentEl;
157            activeRoot.className = 'citrus-account-window';
158            renderContent(activeRoot);
159        }
160    }
161
162    function refresh() {
163        if (activeRoot) {
164            renderContent(activeRoot);
165        }
166    }
167
168    function isOpen() {
169        return !!activeRoot;
170    }
171
172    global.CitrusAccountWindow = {
173        open: open,
174        refresh: refresh,
175        isOpen: isOpen
176    };
177
178    global.addEventListener('usernameSet', function () {
179        if (activeRoot) {
180            refresh();
181        }
182    });
183})(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.