PageSourceSearch

https://pupwife.com/scripts/casino/casinoShell.js

js pupwife.com collected 2026-09-30 15:05:08 UTC 14,390 bytes, 390 lines download raw bytes

1/**
2 * Shared casino page chrome — balance HUD, guest banner, bet panel helper.
3 */
4(function (global) {
5    'use strict';
6
7    var boundRoots = [];
8    var displayedBalanceByRoot = new WeakMap();
9    var balanceAnimChainByRoot = new WeakMap();
10    var animLockByRoot = new WeakMap();
11    var pendingBalanceByRoot = new WeakMap();
12
13    var FLY_PULL_MS = 110;
14    var FLY_SLING_MS = 150;
15    var FLY_TOTAL_MS = FLY_PULL_MS + FLY_SLING_MS + 30;
16
17    function formatBalanceText(balance) {
18        if (balance == null) return '—';
19        return global.CasinoCore.formatCoins(balance);
20    }
21
22    function updateBalanceElImmediate(root, balance) {
23        var el = root.querySelector('[data-casino-balance]');
24        if (!el) return;
25        el.textContent = formatBalanceText(balance);
26        if (balance == null) {
27            displayedBalanceByRoot.delete(root);
28            return;
29        }
30        displayedBalanceByRoot.set(root, balance);
31    }
32
33    function getBalanceHost(root) {
34        return root.querySelector('[data-casino-balance-host]')
35            || root.querySelector('.casino-balance');
36    }
37
38    function flushPendingBalance(root) {
39        if (animLockByRoot.get(root)) return;
40        if (!pendingBalanceByRoot.has(root)) return;
41        var pending = pendingBalanceByRoot.get(root);
42        pendingBalanceByRoot.delete(root);
43        updateBalanceElImmediate(root, pending);
44    }
45
46    function onWalletBalance(root, balance) {
47        if (balance == null) {
48            updateBalanceElImmediate(root, balance);
49            return;
50        }
51        if (animLockByRoot.get(root)) {
52            pendingBalanceByRoot.set(root, balance);
53            return;
54        }
55        updateBalanceElImmediate(root, balance);
56    }
57
58    function playBalanceDeltaAnim(root, delta) {
59        return new Promise(function (resolve) {
60            var host = getBalanceHost(root);
61            var valueEl = root.querySelector('[data-casino-balance]');
62            if (!host || !valueEl || !delta) {
63                resolve();
64                return;
65            }
66
67            var valueStyle = global.getComputedStyle(valueEl);
68            var hostRect = host.getBoundingClientRect();
69            var valueRect = valueEl.getBoundingClientRect();
70            var y = hostRect.top + hostRect.height * 0.5;
71
72            var fly = document.createElement('span');
73            fly.className = 'casino-balance-fly ' + (delta > 0
74                ? 'casino-balance-fly--gain'
75                : 'casino-balance-fly--loss');
76            fly.textContent = (delta > 0 ? '+' : '−') + global.CasinoCore.formatCoins(Math.abs(delta));
77            fly.style.fontSize = valueStyle.fontSize;
78            fly.style.fontWeight = valueStyle.fontWeight;
79            fly.style.lineHeight = valueStyle.lineHeight;
80            fly.style.top = y + 'px';
81            fly.style.visibility = 'hidden';
82            document.body.appendChild(fly);
83
84            var flyW = fly.getBoundingClientRect().width;
85            var gap = 12;
86            var pull = 26;
87            var startX = hostRect.right + gap + flyW * 0.5;
88            var pullX = hostRect.right + gap + pull + flyW * 0.5;
89            var endX = valueRect.left + valueRect.width * 0.62;
90
91            fly.style.left = startX + 'px';
92            fly.style.visibility = '';
93
94            host.classList.add('casino-balance--animating');
95
96            global.requestAnimationFrame(function () {
97                fly.classList.add('casino-balance-fly--visible');
98                global.requestAnimationFrame(function () {
99                    fly.classList.add('casino-balance-fly--pull');
100                    fly.style.left = pullX + 'px';
101                });
102            });
103
104            global.setTimeout(function () {
105                fly.classList.remove('casino-balance-fly--pull');
106                fly.classList.add('casino-balance-fly--sling');
107                fly.style.left = endX + 'px';
108            }, FLY_PULL_MS);
109
110            global.setTimeout(function () {
111                fly.remove();
112                host.classList.remove('casino-balance--animating');
113                resolve();
114            }, FLY_TOTAL_MS);
115        });
116    }
117
118    function popBalanceValue(root) {
119        var el = root.querySelector('[data-casino-balance]');
120        if (!el) return;
121        el.classList.remove('casino-balance-value--pop');
122        void el.offsetWidth;
123        el.classList.add('casino-balance-value--pop');
124        global.setTimeout(function () {
125            el.classList.remove('casino-balance-value--pop');
126        }, 260);
127    }
128
129    function playBalanceDelta(root, delta, newBalance) {
130        if (!root || !Number.isFinite(delta) || delta === 0 || newBalance == null) {
131            return Promise.resolve();
132        }
133
134        var chain = balanceAnimChainByRoot.get(root) || Promise.resolve();
135        chain = chain.then(function () {
136            animLockByRoot.set(root, true);
137            var fromBalance = newBalance - delta;
138            updateBalanceElImmediate(root, fromBalance);
139
140            return playBalanceDeltaAnim(root, delta).then(function () {
141                updateBalanceElImmediate(root, newBalance);
142                popBalanceValue(root);
143                animLockByRoot.set(root, false);
144                flushPendingBalance(root);
145            });
146        });
147        balanceAnimChainByRoot.set(root, chain);
148        return chain;
149    }
150
151    function afterBet(root, res) {
152        if (!res || !res.ok || res.balance == null) return Promise.resolve();
153        var bet = Math.floor(Number(res.bet) || 0);
154        if (bet <= 0) return Promise.resolve();
155        return playBalanceDelta(root, -bet, res.balance);
156    }
157
158    function afterPayout(root, res, amount) {
159        if (!res || !res.ok || res.balance == null) return Promise.resolve();
160        var win = Math.floor(Number(amount != null ? amount : res.delta) || 0);
161        if (win <= 0) return Promise.resolve();
162        return playBalanceDelta(root, win, res.balance);
163    }
164
165    function syncBetSelectionUI(root) {
166        if (!root) return;
167        var amount = getBetAmount(root);
168        var label = root.querySelector('[data-casino-bet-selected]');
169        if (label) {
170            if (amount > 0 && global.CasinoCore.isCasinoLoggedIn()) {
171                label.textContent = 'Betting ' + global.CasinoCore.formatCoins(amount) + ' per drop';
172                label.hidden = false;
173            } else {
174                label.hidden = true;
175                label.textContent = '';
176            }
177        }
178        syncChipActiveFromInput(root);
179    }
180
181    function applyAuthMode(root) {
182        if (!root) return;
183        var loggedIn = global.CasinoCore.isCasinoLoggedIn();
184        root.classList.toggle('casino--guest', !loggedIn);
185        root.classList.toggle('casino--logged-in', loggedIn);
186
187        var guestBanner = root.querySelector('[data-casino-guest-banner]');
188        if (guestBanner) guestBanner.hidden = loggedIn;
189
190        var walletBlock = root.querySelector('[data-casino-wallet-block]');
191        if (walletBlock) walletBlock.hidden = !loggedIn;
192
193        var claimBtn = root.querySelector('[data-casino-daily-claim]');
194        if (claimBtn) claimBtn.hidden = !loggedIn;
195
196        var gambleBlocks = root.querySelectorAll('[data-casino-gamble]');
197        for (var i = 0; i < gambleBlocks.length; i++) {
198            gambleBlocks[i].hidden = !loggedIn;
199        }
200
201        var playBlocks = root.querySelectorAll('[data-casino-play-only]');
202        for (var j = 0; j < playBlocks.length; j++) {
203            playBlocks[j].hidden = loggedIn;
204        }
205
206        syncBetSelectionUI(root);
207    }
208
209    function syncDailyClaimButton(root) {
210        var btn = root.querySelector('[data-casino-daily-claim]');
211        if (!btn || !global.CasinoCore.isCasinoLoggedIn()) return;
212        var can = global.CasinoWallet.canClaimDaily();
213        btn.disabled = !can;
214        btn.setAttribute('aria-disabled', can ? 'false' : 'true');
215        var label = btn.querySelector('[data-casino-claim-label]');
216        if (label) {
217            label.textContent = can
218                ? 'Claim ' + global.CasinoCore.formatCoins(global.CasinoCore.DAILY_BONUS) + ' coins'
219                : 'Claimed today';
220        }
221    }
222
223    function bindRoot(root) {
224        if (!root || boundRoots.indexOf(root) !== -1) return;
225        boundRoots.push(root);
226
227        applyAuthMode(root);
228
229        if (global.CasinoWallet) {
230            global.CasinoWallet.onBalance(function (balance) {
231                onWalletBalance(root, balance);
232                syncDailyClaimButton(root);
233            });
234        }
235
236        var signInBtn = root.querySelector('[data-casino-sign-in]');
237        if (signInBtn) {
238            signInBtn.addEventListener('click', function (e) {
239                e.preventDefault();
240                global.CasinoCore.promptSignIn();
241            });
242        }
243
244        var claimBtn = root.querySelector('[data-casino-daily-claim]');
245        if (claimBtn) {
246            claimBtn.addEventListener('click', function () {
247                if (claimBtn.disabled) return;
248                claimBtn.disabled = true;
249                global.CasinoWallet.claimDaily().then(function (res) {
250                    syncDailyClaimButton(root);
251                    if (res.ok) {
252                        playBalanceDelta(root, global.CasinoCore.DAILY_BONUS, res.balance);
253                        claimBtn.classList.add('casino-claim--pop');
254                        global.setTimeout(function () {
255                            claimBtn.classList.remove('casino-claim--pop');
256                        }, 400);
257                    }
258                });
259            });
260        }
261
262        syncDailyClaimButton(root);
263        syncBetSelectionUI(root);
264    }
265
266    function initPage(rootSelector) {
267        var root = typeof rootSelector === 'string'
268            ? document.querySelector(rootSelector)
269            : rootSelector;
270        if (!root) return;
271        bindRoot(root);
272    }
273
274    function destroy() {
275        boundRoots = [];
276        displayedBalanceByRoot = new WeakMap();
277        balanceAnimChainByRoot = new WeakMap();
278        animLockByRoot = new WeakMap();
279        pendingBalanceByRoot = new WeakMap();
280    }
281
282    function syncChipActiveFromInput(root) {
283        var input = root.querySelector('[data-casino-bet-input]');
284        var chips = root.querySelectorAll('[data-casino-bet-chip]');
285        if (!input || !chips.length) return;
286        var val = input.value === '' ? null : Math.floor(Number(input.value) || 0);
287        var matched = false;
288        for (var i = 0; i < chips.length; i++) {
289            var chipAmount = Math.floor(Number(chips[i].getAttribute('data-amount')) || 0);
290            var active = val !== null && chipAmount === val;
291            chips[i].classList.toggle('is-active', active);
292            chips[i].setAttribute('aria-pressed', active ? 'true' : 'false');
293            if (active) matched = true;
294        }
295        input.classList.toggle('is-custom', val !== null && !matched);
296    }
297
298    function emitBetChange(root) {
299        if (!root) return;
300        try {
301            root.dispatchEvent(new CustomEvent('casino-bet-change', { bubbles: true }));
302        } catch (e) {}
303    }
304
305    function getBetAmount(root) {
306        var input = root.querySelector('[data-casino-bet-input]');
307        if (input && input.value !== '') {
308            return Math.max(0, Math.floor(Number(input.value) || 0));
309        }
310        var active = root.querySelector('[data-casino-bet-chip].is-active');
311        if (active) return Math.floor(Number(active.getAttribute('data-amount')) || 0);
312        return 0;
313    }
314
315    function bindBetChips(root) {
316        var chips = root.querySelectorAll('[data-casino-bet-chip]');
317        var input = root.querySelector('[data-casino-bet-input]');
318        for (var i = 0; i < chips.length; i++) {
319            chips[i].setAttribute('aria-pressed', 'false');
320            chips[i].addEventListener('click', function () {
321                var amount = this.getAttribute('data-amount') || '';
322                if (input) input.value = amount;
323                syncChipActiveFromInput(root);
324                syncBetSelectionUI(root);
325                emitBetChange(root);
326            });
327        }
328        if (input) {
329            input.addEventListener('input', function () {
330                syncChipActiveFromInput(root);
331                syncBetSelectionUI(root);
332                emitBetChange(root);
333            });
334            input.addEventListener('change', function () {
335                var val = Math.floor(Number(input.value) || 0);
336                var min = Math.floor(Number(input.min) || 0);
337                if (input.value !== '' && val < min) {
338                    input.value = String(min);
339                }
340                syncChipActiveFromInput(root);
341                syncBetSelectionUI(root);
342                emitBetChange(root);
343            });
344        }
345        if (chips.length && !input) {
346            if (!root.querySelector('[data-casino-bet-chip].is-active')) {
347                chips[0].classList.add('is-active');
348            }
349        } else if (chips.length && input && input.value === '') {
350            chips[0].click();
351        } else {
352            syncChipActiveFromInput(root);
353        }
354        syncBetSelectionUI(root);
355    }
356
357    function showResult(root, message, kind) {
358        var el = root.querySelector('[data-casino-result]');
359        if (!el) return;
360        if (!message) {
361            el.hidden = true;
362            el.textContent = '';
363            return;
364        }
365        el.textContent = message;
366        el.className = 'casino-result casino-result--' + (kind || 'info');
367        el.hidden = false;
368    }
369
370    global.addEventListener('usernameSet', function () {
371        for (var i = 0; i < boundRoots.length; i++) {
372            applyAuthMode(boundRoots[i]);
373            syncDailyClaimButton(boundRoots[i]);
374            syncBetSelectionUI(boundRoots[i]);
375        }
376    });
377
378    global.CasinoShell = {
379        initPage: initPage,
380        destroy: destroy,
381        getBetAmount: getBetAmount,
382        bindBetChips: bindBetChips,
383        syncBetSelectionUI: syncBetSelectionUI,
384        showResult: showResult,
385        applyAuthMode: applyAuthMode,
386        playBalanceDelta: playBalanceDelta,
387        afterBet: afterBet,
388        afterPayout: afterPayout
389    };
390})(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.