1// ==================================== 2// ACCOUNT PILL â homepage only (index) 3// ==================================== 4 5(function () { 6 'use strict'; 7 8 function isHomePage() { 9 const path = (window.location.pathname || '').replace(/\/$/, '') || '/'; 10 return path === '' || path === '/' || path === '/index' || path === '/index.html'; 11 } 12 13 function shouldShowAccountIndicator() { 14 if (window.SpaChromePolicy && 15 typeof window.SpaChromePolicy.getCurrentPolicy === 'function' && 16 window.SpaChromePolicy.getCurrentPolicy()) { 17 return window.SpaChromePolicy.isEnabled('accountIndicator'); 18 } 19 return isHomePage(); 20 } 21 22 function ensureAccountIndicatorCss() { 23 if (document.querySelector('link[data-account-indicator-css]')) return; 24 const link = document.createElement('link'); 25 link.rel = 'stylesheet'; 26 link.href = '/css/account-indicator.css'; 27 link.setAttribute('data-account-indicator-css', '1'); 28 document.head.appendChild(link); 29 } 30 31 function ensureFontAwesome() { 32 if (typeof window.ensureSiteFontAwesome === 'function') { 33 return window.ensureSiteFontAwesome(); 34 } 35 return new Promise(function (resolve) { 36 if (document.querySelector('script[src*="ensureSiteFontAwesome.js"]')) { 37 var pending = document.querySelector('script[src*="ensureSiteFontAwesome.js"]'); 38 function done() { 39 if (typeof window.ensureSiteFontAwesome === 'function') { 40 window.ensureSiteFontAwesome().then(resolve); 41 } else { 42 resolve(); 43 } 44 } 45 if (pending.getAttribute('data-fa-loader-ready') === '1') { 46 done(); 47 } else { 48 pending.addEventListener('load', done, { once: true }); 49 pending.addEventListener('error', done, { once: true }); 50 } 51 return; 52 } 53 var script = document.createElement('script'); 54 script.src = '/scripts/effects/ensureSiteFontAwesome.js'; 55 script.async = false; 56 script.setAttribute('data-fa-loader', '1'); 57 script.onload = script.onerror = function () { 58 script.setAttribute('data-fa-loader-ready', '1'); 59 if (typeof window.ensureSiteFontAwesome === 'function') { 60 window.ensureSiteFontAwesome().then(resolve); 61 } else { 62 resolve(); 63 } 64 }; 65 document.head.appendChild(script); 66 }); 67 } 68 69 function readStoredUsername() { 70 if (window.__sharedUsername && typeof window.__sharedUsername.get === 'function') { 71 return window.__sharedUsername.get(); 72 } 73 try { 74 return localStorage.getItem('puppy_shared_username'); 75 } catch (e) { 76 return null; 77 } 78 } 79 80 function isLoggedInAccount() { 81 const username = readStoredUsername(); 82 const hasPassword = localStorage.getItem('puppy_username_password') !== null; 83 const isGuestMode = localStorage.getItem('puppy_guest_mode') === 'true'; 84 return !!(username && hasPassword && !isGuestMode); 85 } 86 87 function openAccountFlow() { 88 if (isCitrusOsPage()) { 89 if (window.CitrusAccountWindow && typeof window.CitrusAccountWindow.open === 'function') { 90 window.CitrusAccountWindow.open(); 91 return; 92 } 93 } 94 if (isLoggedInAccount()) { 95 window.location.href = 'account.html'; 96 return; 97 } 98 if (window.__sharedUsername && typeof window.__sharedUsername.promptAgain === 'function') { 99 window.__sharedUsername.promptAgain(); 100 } else { 101 window.location.href = 'account.html'; 102 } 103 } 104 105 function isCitrusOsPage() { 106 return !!(document.body && document.body.classList.contains('page-citrusos')); 107 } 108 109 function ensureCitrusAccountCss() { 110 if (document.querySelector('link[data-citrus-account-css]')) return; 111 var link = document.createElement('link'); 112 link.rel = 'stylesheet'; 113 link.href = '/css/citrusAccount.css'; 114 link.setAttribute('data-citrus-account-css', '1'); 115 document.head.appendChild(link); 116 } 117 118 function mountAccountIndicator(indicator) { 119 if (!indicator) return; 120 121 if (isCitrusOsPage()) { 122 ensureCitrusAccountCss(); 123 indicator.classList.remove('citrus-tray-btn'); 124 indicator.classList.add('citrusos-taskbar-account'); 125 var taskbar = document.getElementById('citrusos-taskbar'); 126 var startBtn = document.getElementById('citrusos-start-btn'); 127 if (taskbar && startBtn && startBtn.parentNode === taskbar) { 128 if (indicator.parentNode !== taskbar) { 129 taskbar.insertBefore(indicator, startBtn); 130 } 131 return; 132 } 133 if (indicator.parentNode !== document.body) { 134 document.body.appendChild(indicator); 135 } 136 return; 137 } 138 139 indicator.classList.remove('citrusos-taskbar-account', 'citrus-tray-btn'); 140 if (indicator.parentNode !== document.body) { 141 document.body.appendChild(indicator); 142 } 143 } 144 145 function createAccountIndicator() { 146 if (!shouldShowAccountIndicator()) return null; 147 148 ensureAccountIndicatorCss(); 149 150 const indicator = document.createElement('button'); 151 indicator.type = 'button'; 152 indicator.id = 'account-indicator'; 153 indicator.className = 'account-indicator'; 154 indicator.setAttribute('aria-label', 'Account'); 155 indicator.title = 'Account'; 156 157 indicator.innerHTML = 158 '<span class="account-indicator-inner">' + 159 '<span class="account-indicator-icon" aria-hidden="true"><i class="fa-solid fa-user"></i></span>' + 160 '<span class="account-indicator-copy">' + 161 '<span class="account-indicator-meta"></span>' + 162 '<span id="account-indicator-username" class="account-indicator-username"></span>' + 163 '</span></span>'; 164 165 indicator.addEventListener('click', openAccountFlow); 166 mountAccountIndicator(indicator); 167 return indicator; 168 } 169 170 function updateAccountIndicator() { 171 if (!shouldShowAccountIndicator()) { 172 const existing = document.getElementById('account-indicator'); 173 if (existing) existing.style.display = 'none'; 174 return; 175 } 176 177 let indicator = document.getElementById('account-indicator'); 178 if (!indicator) { 179 indicator = createAccountIndicator(); 180 } 181 if (!indicator) return; 182 183 mountAccountIndicator(indicator); 184 185 const usernameDisplay = document.getElementById('account-indicator-username'); 186 if (!usernameDisplay) return; 187 188 indicator.style.display = 'flex'; 189 190 const loggedIn = isLoggedInAccount(); 191 indicator.classList.toggle('account-indicator--logged-in', loggedIn); 192 193 if (loggedIn) { 194 const username = readStoredUsername(); 195 usernameDisplay.textContent = username; 196 indicator.title = 'View account (' + username + ')'; 197 indicator.setAttribute('aria-label', 'View account for ' + username); 198 } else { 199 usernameDisplay.textContent = 'Sign in'; 200 indicator.title = 'Sign in or create a free account'; 201 indicator.setAttribute('aria-label', 'Sign in or create account'); 202 } 203 } 204
205 function init() { 206 ensureFontAwesome().then(updateAccountIndicator); 207 } 208 209 if (document.readyState === 'loading') { 210 document.addEventListener('DOMContentLoaded', function () { 211 setTimeout(init, 400); 212 }); 213 } else { 214 setTimeout(init, 400); 215 } 216 217 window.addEventListener('usernameSet', function () { 218 setTimeout(function () { 219 ensureFontAwesome().then(updateAccountIndicator); 220 }, 200); 221 }); 222 223 window.addEventListener('pupwife:spa-chrome', function () { 224 updateAccountIndicator(); 225 }); 226 227 window.AccountIndicator = { 228 update: updateAccountIndicator, 229 open: openAccountFlow, 230 isLoggedIn: isLoggedInAccount, 231 shouldShow: shouldShowAccountIndicator 232 }; 233})();
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.