1/** 2 * Firebase casino wallet â balance, daily claim, cross-device via username. 3 */ 4(function (global) { 5 'use strict'; 6 7 var walletsRef = null; 8 var leaderboardRef = null; 9 var listener = null; 10 var balanceListeners = []; 11 var state = { 12 balance: null, 13 lastDailyClaimUtc: null, 14 ready: false, 15 denied: false 16 }; 17 18 function emitBalance() { 19 for (var i = 0; i < balanceListeners.length; i++) { 20 try { 21 balanceListeners[i](state.balance, state); 22 } catch (e) {} 23 } 24 } 25 26 function isPermissionDenied(err) { 27 if (!err) return false; 28 if (err.code === 'PERMISSION_DENIED') return true; 29 return /permission_denied|permission denied/i.test(String(err.message || '')); 30 } 31 32 function initRefs() { 33 if (typeof firebase === 'undefined' || !firebase.database) return false; 34 walletsRef = firebase.database().ref('casinoWallets'); 35 leaderboardRef = firebase.database().ref('casinoLeaderboard'); 36 return true; 37 } 38 39 function syncLeaderboard(username, name, balance) { 40 if (!leaderboardRef || !username) return; 41 leaderboardRef.child(username).set({ 42 name: name, 43 balance: balance, 44 updatedAt: firebase.database.ServerValue.TIMESTAMP 45 }).catch(function (err) { 46 if (isPermissionDenied(err)) state.denied = true; 47 }); 48 } 49 50 function defaultWallet(username, name) { 51 return { 52 name: name, 53 balance: global.CasinoCore.STARTING_BALANCE, 54 lastDailyClaimUtc: '', 55 createdAt: firebase.database.ServerValue.TIMESTAMP, 56 updatedAt: firebase.database.ServerValue.TIMESTAMP 57 }; 58 } 59 60 function attachListener(username) { 61 if (listener && listener.ref) { 62 listener.ref.off('value', listener.handler); 63 } 64 listener = null; 65 if (!walletsRef || !username) return; 66 67 var ref = walletsRef.child(username); 68 var handler = function (snap) { 69 if (!snap.exists()) { 70 state.balance = global.CasinoCore.STARTING_BALANCE; 71 state.lastDailyClaimUtc = ''; 72 state.ready = true; 73 emitBalance(); 74 return; 75 } 76 var val = snap.val(); 77 state.balance = val.balance || 0; 78 state.lastDailyClaimUtc = val.lastDailyClaimUtc || ''; 79 state.ready = true; 80 emitBalance(); 81 }; 82 ref.on('value', handler, function (err) { 83 if (isPermissionDenied(err)) state.denied = true; 84 }); 85 listener = { ref: ref, handler: handler }; 86 } 87 88 function ensureWallet(username) { 89 if (!walletsRef || !username) return Promise.resolve(null); 90 var name = global.CasinoCore.sanitizeUsername(username); 91 var ref = walletsRef.child(username); 92 return ref.transaction(function (current) { 93 if (current !== null) return current; 94 return defaultWallet(username, name); 95 }).then(function (result) { 96 if (result.committed && result.snapshot) { 97 var data = result.snapshot.val(); 98 syncLeaderboard(username, data.name || name, data.balance || 0); 99 } 100 return result.snapshot ? result.snapshot.val() : null; 101 }); 102 } 103 104 function init() { 105 if (!initRefs()) return Promise.resolve(); 106 if (!global.CasinoCore.isCasinoLoggedIn()) { 107 state.ready = true; 108 state.balance = null; 109 emitBalance(); 110 return Promise.resolve(); 111 } 112 113 var username = global.CasinoCore.getUsername(); 114 return ensureWallet(username).then(function () { 115 attachListener(username); 116 }); 117 } 118 119 function destroy() { 120 if (listener && listener.ref) { 121 listener.ref.off('value', listener.handler); 122 } 123 listener = null; 124 balanceListeners = []; 125 state = { balance: null, lastDailyClaimUtc: null, ready: false, denied: fal
125se }; 126 } 127 128 function onBalance(fn) { 129 if (typeof fn === 'function') balanceListeners.push(fn); 130 if (state.ready) fn(state.balance, state); 131 } 132 133 function canClaimDaily() { 134 if (!global.CasinoCore.isCasinoLoggedIn()) return false; 135 var today = global.CasinoCore.utcDateString(); 136 return state.lastDailyClaimUtc !== today; 137 } 138 139 function claimDaily() { 140 if (!global.CasinoCore.isCasinoLoggedIn()) { 141 return Promise.resolve({ ok: false, reason: 'not_logged_in' }); 142 } 143 if (!canClaimDaily()) { 144 return Promise.resolve({ ok: false, reason: 'already_claimed' }); 145 } 146 147 var username = global.CasinoCore.getUsername(); 148 var name = global.CasinoCore.sanitizeUsername(username); 149 var today = global.CasinoCore.utcDateString(); 150 var ref = walletsRef.child(username); 151 152 return ref.transaction(function (current) { 153 if (current === null) { 154 return { 155 name: name, 156 balance: global.CasinoCore.STARTING_BALANCE + global.CasinoCore.DAILY_BONUS, 157 lastDailyClaimUtc: today, 158 createdAt: firebase.database.ServerValue.TIMESTAMP, 159 updatedAt: firebase.database.ServerValue.TIMESTAMP 160 }; 161 } 162 if (current.lastDailyClaimUtc === today) return; 163 return { 164 name: current.name || name, 165 balance: (current.balance || 0) + global.CasinoCore.DAILY_BONUS, 166 lastDailyClaimUtc: today, 167 createdAt: current.createdAt || firebase.database.ServerValue.TIMESTAMP, 168 updatedAt: firebase.database.ServerValue.TIMESTAMP 169 }; 170 }).then(function (result) { 171 if (!result.committed) { 172 return { ok: false, reason: 'already_claimed' }; 173 } 174 var data = result.snapshot.val(); 175 syncLeaderboard(username, data.name || name, data.balance || 0); 176 return { ok: true, balance: data.balance }; 177 }).catch(function (err) { 178 if (isPermissionDenied(err)) state.denied = true; 179 return { ok: false, reason: 'error' }; 180 }); 181 } 182 183 function applyDelta(delta, meta) { 184 if (!global.CasinoCore.isCasinoLoggedIn()) { 185 return Promise.resolve({ ok: false, reason: 'not_logged_in' }); 186 } 187 if (!Number.isFinite(delta) || delta === 0) { 188 return Promise.resolve({ ok: false, reason: 'invalid_delta' }); 189 } 190 191 var username = global.CasinoCore.getUsername(); 192 var name = global.CasinoCore.sanitizeUsername(username); 193 var ref = walletsRef.child(username); 194 195 return ref.transaction(function (current) { 196 if (current === null) { 197 if (delta < 0) return; 198 var start = global.CasinoCore.STARTING_BALANCE + delta; 199 if (start < 0) return; 200 return { 201 name: name, 202 balance: start, 203 lastDailyClaimUtc: '', 204 createdAt: firebase.database.ServerValue.TIMESTAMP, 205 updatedAt: firebase.database.ServerValue.TIMESTAMP 206 }; 207 } 208 var next = (current.balance || 0) + delta; 209 if (next < 0) return; 210 return { 211 name: current.name || name, 212 balance: next, 213 lastDailyClaimUtc: current.lastDailyClaimUtc || '', 214 createdAt: current.createdAt || firebase.database.ServerValue.TIMESTAMP, 215 updatedAt: firebase.database.ServerValue.TIMESTAMP 216 }; 217 }).then(function (result) { 218 if (!result.committed) { 219 return { ok: false, reason: delta < 0 ? 'insufficient' : 'failed' }; 220 } 221 var data = result.snapshot.val(); 222 syncLeaderboard(username, data.name || name, data.balance || 0); 223 void meta; 224 return { ok: true, balance: data.balance, delta: delta }; 225 }).catch(function (err) { 226 if (isPermissionDenied(err)) state.denied = true; 227 return { ok: false, reason: 'error' }; 228 }); 229 } 230 231 function placeBet(amount) { 232 var bet = Math.floor(Number(amount) || 0); 233 if (bet <= 0) return Promise.resolve({ ok: false, reason: 'invali
233d_bet' }); 234 if (state.balance !== null && bet > state.balance) { 235 return Promise.resolve({ ok: false, reason: 'insufficient' }); 236 } 237 return applyDelta(-bet, { type: 'bet', amount: bet }).then(function (res) { 238 if (!res.ok) return res; 239 return { ok: true, bet: bet, balance: res.balance }; 240 }); 241 } 242 243 function payout(amount, meta) { 244 var win = Math.floor(Number(amount) || 0); 245 if (win <= 0) return Promise.resolve({ ok: false, reason: 'invalid_payout' }); 246 return applyDelta(win, meta || { type: 'win' }); 247 } 248 249 global.CasinoWallet = { 250 init: init, 251 destroy: destroy, 252 onBalance: onBalance, 253 canClaimDaily: canClaimDaily, 254 claimDaily: claimDaily, 255 placeBet: placeBet, 256 payout: payout, 257 applyDelta: applyDelta, 258 getState: function () { return state; } 259 }; 260})(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.