PageSourceSearch

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

js pupwife.com collected 2026-09-30 15:04:48 UTC 7,941 bytes, 229 lines download raw bytes

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.