1/** 2 * CitrusOS birthday party orchestrator (Sep 26 + debugBirthday). 3 */ 4(function (global) { 5 'use strict'; 6 7 var STICKY_SESSION_KEY = 'citrusos_birthday_sticky_done'; 8 var BIRTHDAY_TRAIL = 'confetti'; 9 var started = false; 10 var savedTrail = null; 11 var phrasePatchRestore = null; 12 13 var BIRTHDAY_PHRASES = [ 14 'birthday party on pupwife.com!', 15 'Happy birthday!!!', 16 'Super lucky day!!! Take that chance!!!', 17 'You deserve a little treat today!!', 18 'MAKE ART TODAY!! Your art is beautiful!!', 19 'Everyone forever happy smiler infinity!!' 20 ]; 21 22 function isCitrusDesktop() { 23 return document.body && document.body.classList.contains('page-citrusos'); 24 } 25 26 function prefersReducedMotion() { 27 return global.matchMedia && global.matchMedia('(prefers-reduced-motion: reduce)').matches; 28 } 29 30 function loadSettings() { 31 if (global.SettingsMenu && typeof global.SettingsMenu.loadSettings === 'function') { 32 return global.SettingsMenu.loadSettings(); 33 } 34 try { 35 var raw = global.localStorage.getItem('puppywifey_settings'); 36 return raw ? JSON.parse(raw) : {}; 37 } catch (e) { 38 return {}; 39 } 40 } 41 42 function saveSettings(partial) { 43 if (global.SettingsMenu && typeof global.SettingsMenu.setSetting === 'function') { 44 Object.keys(partial).forEach(function (k) { 45 global.SettingsMenu.setSetting(k, partial[k]); 46 }); 47 return; 48 } 49 var settings = loadSettings(); 50 Object.keys(partial).forEach(function (k) { 51 settings[k] = partial[k]; 52 }); 53 try { 54 global.localStorage.setItem('puppywifey_settings', JSON.stringify(settings)); 55 } catch (e) { /* ignore */ } 56 } 57 58 function restoreTrailIfNeeded() { 59 var cfg = global.CitrusBirthdayConfig; 60 if (!cfg) return; 61 if (cfg.isBirthdayActive()) return; 62 63 var key = cfg.TRAIL_SAVE_KEY; 64 try { 65 var raw = global.localStorage.getItem(key); 66 if (!raw) return; 67 var data = JSON.parse(raw); 68 global.localStorage.removeItem(key); 69 if (data && data.cursorTrail) { 70 saveSettings({ cursorTrail: data.cursorTrail }); 71 if (global.CursorTrails && typeof global.CursorTrails.setTrailType === 'function') { 72 global.CursorTrails.setTrailType(data.cursorTrail); 73 } 74 } 75 } catch (e) { /* ignore */ } 76 } 77 78 function enableBirthdayTrail() { 79 var cfg = global.CitrusBirthdayConfig; 80 if (!cfg) return; 81 var settings = loadSettings(); 82 var current = settings.cursorTrail || 'none'; 83 if (current === BIRTHDAY_TRAIL) return; 84 85 try { 86 global.localStorage.setItem(cfg.TRAIL_SAVE_KEY, JSON.stringify({ cursorTrail: current })); 87 } catch (e) { /* ignore */ } 88 89 saveSettings({ cursorTrail: BIRTHDAY_TRAIL }); 90 if (global.CursorTrails && typeof global.CursorTrails.setTrailType === 'function') { 91 global.CursorTrails.setTrailType(BIRTHDAY_TRAIL); 92 } 93 } 94 95 function patchWhimsyPhrases() { 96 if (!global.HeaderPhrases || typeof global.HeaderPhrases.getRandomPhrase !== 'function') return; 97 if (phrasePatchRestore) return; 98 var original = global.HeaderPhrases.getRandomPhrase; 99 phrasePatchRestore = original; 100 global.HeaderPhrases.getRandomPhrase = function () { 101 if (Math.random() < 0.55) { 102 return BIRTHDAY_PHRASES[Math.floor(Math.random() * BIRTHDAY_PHRASES.length)]; 103 } 104 return original(); 105 }; 106 } 107 108 function unpatchWhimsyPhrases() { 109 if (phrasePatchRestore && global.HeaderPhrases) { 110 global.HeaderPhrases.getRandomPhrase = phrasePatchRestore; 111 phrasePatchRestore = null; 112 } 113 } 114 115 function applyShellDressing(entry) { 116 document.documentElement.classList.add('citrusos-birthday'); 117 118 var startHeader = document.querySelector('.citrus-start-header'); 119 if (startHeader && !startHeader.querySelector('.citrus-start-header__party')) { 120 var sub = document.createElement('p'); 121 sub.className = 'citrus-start-header__party'; 122 sub.textContent = entry.label || 'Birthday party'; 123 startHeader.appendChild(sub); 124 } 125 126 var dateEl = document.getElementById('citrusos-date'); 127 if (dateEl) dateEl.classList.add('citrus-birthday-date'); 128 129 global.CLICK_SPARK_BIRTHDAY_PALETTE = entry.balloonColors || null; 130 enableBirthdayTrail(); 131 patchWhimsyPhrases(); 132 } 133 134 function whenBootDone(cb) { 135 function tick() { 136 if (!document.documentElement.classList.contains('citrusos-booting')) { 137 cb(); 138 return; 139 } 140 global.requestAnimationFrame(tick); 141 } 142 tick(); 143 } 144 145 function spawnStickyOnce(entry) { 146 try { 147 if (global.sessionStorage.getItem(STICKY_SESSION_KEY)) return; 148 global.sessionStorage.setItem(STICKY_SESSION_KEY, '1'); 149 } catch (e) { 150 return; 151 } 152 153 function trySpawn(attempts) { 154 if (global.CitrusWidgets && typeof global.CitrusWidgets.createNoteAndOpen === 'function') { 155 global.CitrusWidgets.createNoteAndOpen({ 156 text: (entry.copy && entry.copy.stickyNote) || 'birthday party on pupwife.com!', 157 x: 120 + Math.random() * 80, 158 y: 100 + Math.random() * 60 159 }); 160 return; 161 } 162 if (attempts < 48) { 163 global.setTimeout(function () { trySpawn(attempts + 1); }, 250); 164 } 165 } 166 167 whenBootDone(function () { 168 global.setTimeout(function () { trySpawn(0); }, 600); 169 }); 170 } 171 172 function startParty() { 173 if (started || !isCitrusDesktop()) return; 174 var cfg = global.CitrusBirthdayConfig; 175 if (!cfg) return; 176 177 restoreTrailIfNeeded(); 178 179 var entry = cfg.getActiveBirthday(); 180 if (!entry) return; 181 182 started = true; 183 applyShellDressing(entry); 184 spawnStickyOnce(entry); 185 186 whenBootDone(function () { 187 global.setTimeout(function () { 188 if (!global.CitrusBirthdayBalloons) return; 189 global.CitrusBirthdayBalloons.start(entry, { 190 onGoldPop: function () { 191 if (global.CitrusBirthdaySecret) { 192 global.CitrusBirthdaySecret.open(entry); 193 } 194 } 195 }); 196 }, prefersReducedMotion() ? 0 : 420); 197 }); 198 } 199 200 function destroyParty() { 201 if (global.CitrusBirthdayBalloons) global.CitrusBirthdayBalloons.destroy(); 202 unpatchWhimsyPhrases(); 203 global.CLICK_SPARK_BIRTHDAY_PALETTE = null; 204 document.documentElement.classList.remove('citrusos-birthday'); 205 var dateEl = document.getElementById('citrusos-date'); 206 if (dateEl) dateEl.classList.remove('citrus-birthday-date'); 207 var partySub = document.querySelector('.citrus-start-header__party'); 208 if (partySub) partySub.remove(); 209 started = false;
210 } 211 212 function init() { 213 if (!isCitrusDesktop()) return; 214 restoreTrailIfNeeded(); 215 global.addEventListener('pupwife:citrus-shell-ready', startParty, { once: true }); 216 } 217 218 if (document.readyState === 'loading') { 219 document.addEventListener('DOMContentLoaded', init); 220 } else { 221 init(); 222 } 223 224 global.CitrusBirthday = { 225 start: startParty, 226 destroy: destroyParty, 227 isStarted: function () { return started; } 228 }; 229})(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.