/* Twiddle Play SDK v1 · window.TwiddlePlay · ES5, no deps, idempotent, never throws into the game. Transports: Android bridge, parent-frame host (400 ms hello), standalone test ads + test banner. */ (function (w, d) { 'use strict'; if (w.TwiddlePlay) return; var P = w.TwiddlePlay = { version: '1', env: 'standalone' }; var mode = null, waiting = [], pending = {}, seq = 0, hostWin = null, hostOrigin = '*'; var subs = {}, held = { pause: {}, mute: {} }; var t0 = Date.now(), busy = false, interN = 0, interLast = 0; var NO_I = { shown: false }, NO_R = { rewarded: false }; function info(m) { try { console.info('[TwiddlePlay] ' + m); } catch (e) {} } function any(o) { for (var k in o) if (o.hasOwnProperty(k)) return true; return false; } /* pause/mute: held per reason (host, bg, ad), fire on change */ function emit(name) { var a = (subs[name] || []).slice(); for (var i = 0; i < a.length; i++) { try { a[i](); } catch (e) { try { console.error('[TwiddlePlay] ' + name + ' handler', e); } catch (x) {} } } } function hold(kind, why, on) { var h = held[kind], was = any(h); if (on) h[why] = 1; else delete h[why]; if (was !== any(h)) emit(on ? kind : (kind === 'pause' ? 'resume' : 'unmute')); } function hostEvt(n) { if (n === 'pause' || n === 'resume') hold('pause', 'host', n === 'pause'); else if (n === 'mute' || n === 'unmute') hold('mute', 'host', n === 'mute'); else emit(n); } function onVis() { var h = !!d.hidden; hold('pause', 'bg', h); hold('mute', 'bg', h); } if (d.addEventListener) d.addEventListener('visibilitychange', onVis); /* host results, normalised */ function norm(cmd, r, def) { var k = cmd === 'showRewarded' ? 'rewarded' : /^show[IB]/.test(cmd) ? 'shown' : '', o = {}, b = typeof r === 'boolean'; if (k && (b || r && typeof r === 'object')) { o[k] = b ? r : !!r[k]; return o; } return cmd === 'hasRewarded' ? b ? r : !!(r && (r.available || r.hasRewarded)) : def; } function parse(m) { if (typeof m === 'string') { try { m = JSON.parse(m); } catch (e) { return null; } } return m && typeof m === 'object' ? m : null; } function receive(m) { if (!(m = parse(m))) return; if (m.pb === 'res') { var id = String(m.id), p = pending[id]; if (!p) return; delete pending[id]; clearTimeout(p.timer); p.done(m.ok === false ? p.def : norm(p.cmd, m.result, p.def)); } else if (m.pb === 'evt' && m.name === 'adStarted') { /* ad on screen: 10 min cap, not 60 s */ for (var k in pending) if (pending[k].ad && (m.id == null || k === String(m.id))) arm(k, 6e5); } else if (m.pb === 'evt' && m.name) hostEvt(String(m.name)); } function arm(id, ms) { var p = pending[id]; clearTimeout(p.timer); p.timer = setTimeout(function () { delete pending[id]; p.done(p.def); }, ms); } w.__twiddleplayReceive = function (m) { if (mode === 'native') receive(m); }; function send(msg) { if (mode === 'native') w.TwiddlePlayNativeBridge.postMessage(JSON.stringify(msg)); else hostWin.postMessage(msg, hostOrigin); } /* requests wait for the transport */ function call(cmd, def, ms, track, args) { return new Promise(function (resolve) { function done(v) { try { resolve(v); } catch (e) {} } function go() { if (mode === 'standalone') return local(cmd, def, done, args); var id = 'pb' + (++seq), msg = { pb: 'req', id: id, cmd: cmd }; if (args) msg.args = args; if (track) { pending[id] = { cmd: cmd, def: def, done: done, ad: /^show/.test(cmd) }; arm(id, ms); } try { send(msg); } catch (e) { if (track) { clearTimeout(pending[id].timer); delete pending[id]; } done(def); return; } if (!track) done(def); } if (mode) go(); else waiting.push(go); }); } function pick(m) { mode = m; P.env = m === 'native' ? 'android' : m; var q = waiting; waiting = []; for (var i = 0; i < q.length; i++) q[i](); } /* standalone: test ads, app caps */ function capWhy() { var t = Date.now(), el = t - t0; if (el < 6e4) return 'first 60 s of a session'; if (interLast && t - interLast < 9e4) return 'under 90 s since the last'; if (interN + 1 > Math.ceil(el / 18e4)) return 'max 1 per 3 min of play'; return ''; } function local(cmd, def, done, args) { if (/Banner$/.test(cmd)) return done(banner(args)); if (cmd === 'ready') return done(); if (cmd === 'hasRewarded') return done(true); if (cmd === 'exit') { info('exit() closes the game in the app'); return done(); } if (cmd !== 'showInterstitial' && cmd !== 'showRewarded') return done(); var rew = cmd === 'showRewarded', why = busy ? 'an ad is showing' : rew ? '' : capWhy(); if (why) { info((rew ? 'rewarded' : 'interstitial') + ' not shown: ' + why); return done(def); } if (!rew) { interN++; interLast = Date.now(); } testAd(rew, function () { done(rew ? { rewarded: true } : { shown: true }); }); } function testAd(rew, cb) { busy = true; hold('pause', 'ad', true); hold('mute', 'ad', true); var o = add('twiddleplay-test-ad', 'inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;' + 'background:rgba(14,10,36,.95);color:#fff;font:600 16px/1.35 system-ui,sans-serif;text-align:center;touch-action:none'), n = 3, num; o.setAttribute('role', 'dialog'); o.setAttribute('aria-modal', 'true'); o.innerHTML = '