1(function(){ 2 var SCRIPT = document.currentScript; 3 var SID = (SCRIPT && SCRIPT.dataset && SCRIPT.dataset.siteId) ? +SCRIPT.dataset.siteId : (window.__SID || 0); 4 if (!SID) { 5 // Try extract from src URL: /w/123.js 6 var m = SCRIPT && SCRIPT.src && SCRIPT.src.match(/\/w\/(\d+)\.js/); 7 if (m) SID = +m[1]; 8 } 9 if (!SID) return; 10 var BASE = ''; 11 var LDR; 12 13 function showLoader() { 14 if (LDR) { LDR.style.display = 'flex'; return; } 15 LDR = document.createElement('div'); 16 LDR.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:1000000;display:flex;align-items:center;justify-content:center;'; 17 var s = document.createElement('div'); 18 s.style.cssText = 'width:48px;height:48px;border:4px solid rgba(255,255,255,0.3);border-top-color:#fff;border-radius:50%;animation:_wSpin .6s linear infinite;'; 19 var st = document.createElement('style'); 20 st.textContent = '@keyframes _wSpin{to{transform:rotate(360deg)}}'; 21 document.head.appendChild(st); 22 LDR.appendChild(s); 23 document.body.appendChild(LDR); 24 window.addEventListener('pageshow', function(){ if (LDR) LDR.style.display='none'; }); 25 } 26 function hideLoader(){ if (LDR) LDR.style.display='none'; } 27 28 function go(src) { 29 showLoader(); 30 fetch(BASE + '/go/url?s=' + SID + '&src=' + encodeURIComponent(src)) 31 .then(function(r){ return r.json(); }) 32 .then(function(d){ if (d && d.u) location.href = d.u; else hideLoader(); }) 33 .catch(hideLoader); 34 } 35 36 function esc(s){ return String(s||'').replace(/[&<>"']/g, function(c){ return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]; }); } 37 38 // Header Banner popup CSS â colors come from CSS vars set on :host based on the site's 39 // mainColor / accentColor / accentTextColor â popup picks up each site's brand palette. 40 var POPUP_CSS = '' 41 + ':host{all:initial;}' 42 + '*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;line-height:1.3;}' 43 /* Overlay */ 44 + '.ovr{position:fixed;inset:0;background:rgba(0,0,0,.85);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);display:flex;align-items:flex-start;justify-content:center;z-index:999999;opacity:0;transition:opacity .4s;padding:20px;overflow-y:auto;-webkit-overflow-scrolling:touch;}' 45 + '.ovr.show{opacity:1;}' 46 + '.wrap{position:relative;max-width:420px;width:100%;flex-shrink:0;margin:auto;animation:popIn .5s cubic-bezier(.34,1.56,.64,1);}' 47 + '@keyframes popIn{from{transform:scale(.8) translateY(50px);opacity:0}to{transform:scale(1) translateY(0);opacity:1}}' 48 + '.banner{position:relative;border-radius:14px;overflow:hidden;width:100%;animation:bannerIn .4s cubic-bezier(.16,1,.3,1) forwards;background:var(--bg);}' 49 + '@keyframes bannerIn{from{opacity:0;transform:scale(.92) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}' 50 /* BG image with dark gradient overlay */ 51 + '.bg{position:absolute;inset:0;background-size:cover;background-position:center;z-index:0;}' 52 + '.bg::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,color-mix(in srgb,var(--bg) 70%, transparent) 0%,color-mix(in srgb,var(--bg) 88%, transparent) 40%,var(--bg) 100%);}' 53 /* Close â outside top-right, themed */ 54 + '.close{position:absolute;top:-7px;right:-7px;width:28px;height:28px;background:color-mix(in srgb,var(--bg) 80%, #333);border:2px solid color-mix(in srgb,var(--accent) 50%, rgba(255,255,255,.3));color:#fff;border-radius:50%;font-size:18px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:10;transition:all .2s;padding:0;}' 55 + '.close:hover{transform:scale(1.1);text-shadow:0 0 12px var(--accent);border-color:var(--accent);}' 56 /* Warning strip â keep red so it stays alarming */ 57 + '.warn{position:relative;z-index:1;background:linear-gradient(135deg,rgba(180,40,40,.35),rgba(120,30,30,.15));border-bottom:1px solid rgba(255,80,80,.15);padding:14px 20px;text-align:center;}' 58 + '.warn-icon{display:inline-block;width:18px;height:18px;background:#d44;border-radius:50%;color:#fff;font-size:12px;font-weight:900;line-height:18px;text-align:center;margin-right:6px;vertical-align:middle;flex-shrink:0;animation:warnPulse 1.5s ease-in-out infinite;}' 59 + '@keyframes warnPulse{0%,100%{box-shadow:0 0 0 0 rgba(204,51,51,.6)}50%{box-shadow:0 0 0 6px rgba(204,51,51,0)}}' 60 + '.warn-text{color:#ff8a8a;font-size:14px;font-weight:600;line-height:1.5;display:inline;}' 61 + '.warn-text b{color:#fff;font-weight:800;}' 62 /* Body */ 63 + '.body{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;padding:22px 24px 24px;text-align:center;}' 64 + '.recommend{color:rgba(255,255,255,.55);font-size:11px;text-transform:uppercase;letter-spacing:2px;margin-bottom:18px;font-weight:600;}' 65 /* Logo with accent glow + pulse */ 66 + '.logo-wrap{position:relative;margin-bottom:14px;padding-bottom:14px;border-bottom:1px solid rgba(255,255,255,.08);width:100%;display:flex;justify-content:center;}' 67 + '.logo-wrap::before{content:"";position:absolute;inset:-20px;background:radial-gradient(ellipse,color-mix(in srgb,var(--accent) 18%, transparent) 0%,transparent 70%);z-index:0;animation:logoGlow 3s ease-in-out infinite;}' 68 + '.logo{position:relative;z-index:1;max-height:55px;max-width:190px;object-fit:contain;filter:drop-shadow(0 2px 10px rgba(0,0,0,.4));animation:logoPulse 3s ease-in-out infinite;}' 69 + '@keyframes logoGlow{0%,100%{opacity:.6;transform:scale(1)}50%{opacity:1;transform:scale(1.1)}}' 70 + '@keyframes logoPulse{0%,100%{transform:scale(1);filter:drop-shadow(0 2px 10px rgba(0,0,0,.4))}50%{transform:scale(1.03);filter:drop-shadow(0 0 18px color-mix(in srgb,var(--accent) 35%, transparent))}}' 71 /* Bonus card */ 72 + '.bonus{background:linear-gradient(135deg,rgba(255,255,255,.07),rgba(255,255,255,.02));border:1px solid color-mix(in srgb,var(--accent) 25%, rgba(255,255,255,.08));border-radius:10px;padding:16px 20px;margin-bottom:14px;width:100%;}' 73 + '.bonus-label{font-size:11px;color:rgba(255,255,255,.55);text-transform:uppercase;letter-spacing:2.5px;margin-bottom:8px;font-weight:600;}' 74 + '.prize{font-size:20px;font-weight:800;color:#fff;line-height:1.3;}' 75 + '.prize .pct{color:var(--accent);text-shadow:0 0 18px color-mix(in srgb,var(--accent) 50%, transparent);}' 76 + '.sub{font-size:11px;color:color-mix(in srgb,var(--accent) 70%, transparent);margin-top:10px;text-transform:uppercase;letter-spacing:1.5px;font-weight:700;}' 77 + '.exclusive{margin-top:4px;margin-bottom:14px;font-size:11px;color:rgba(255,255,255,.55);letter-spacing:.5px;}' 78 /* CTA â site accent palette (matches hero-cta on the site exactly) */
79 + '.cta{display:block;width:100%;padding:14px 20px;border-radius:8px;border:none;cursor:pointer;font-size:15px;font-weight:800;color:var(--accent-text);text-decoration:none;text-transform:uppercase;letter-spacing:1px;text-align:center;position:relative;overflow:hidden;background:var(--accent);box-shadow:0 4px 18px color-mix(in srgb,var(--accent) 30%, transparent);transition:transform .15s,box-shadow .15s,filter .15s;animation:btnPulse 2s ease-in-out infinite;}' 80 + '.cta::after{content:"";position:absolute;top:0;left:-100%;width:60%;height:100%;background:linear-gradient(105deg,transparent 30%,rgba(255,255,255,.28) 50%,transparent 70%);animation:btnShine 3s ease-in-out infinite;}' 81 + '.cta:hover{transform:translateY(-1px);filter:brightness(1.1);box-shadow:0 8px 26px color-mix(in srgb,var(--accent) 45%, transparent);}' 82 + '@keyframes btnPulse{0%,100%{box-shadow:0 4px 18px color-mix(in srgb,var(--accent) 30%, transparent)}50%{box-shadow:0 4px 26px color-mix(in srgb,var(--accent) 50%, transparent)}}' 83 + '@keyframes btnShine{0%,70%{left:-100%}100%{left:150%}}' 84 /* Timer */ 85 + '.tim{margin-top:14px;width:100%;}' 86 + '.tim-text{font-size:10px;color:rgba(255,255,255,.4);letter-spacing:.5px;margin-bottom:5px;text-transform:lowercase;}' 87 + '.tim-text span{color:rgba(255,255,255,.7);font-variant-numeric:tabular-nums;font-weight:700;}' 88 + '.tim-bar{width:100%;height:3px;background:rgba(255,255,255,.08);border-radius:2px;overflow:hidden;}' 89 + '.tim-fill{height:100%;background:var(--accent);border-radius:2px;width:100%;transition:width 1s linear;}'; 90 91 // CSS for the sticky footer â injected into Shadow DOM (NOT global page) so the 92 // site's own resets/typography can't bleed in. 93 // Colors come from CSS vars set on :host based on the site's mainColor / accentColor / 94 // accentTextColor â sticky banner picks up each site's brand palette automatically. 95 var STICKY_CSS = '' 96 + ':host{all:initial;}' 97 + '*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;line-height:1.3;}' 98 + '.sf{position:fixed;left:0;right:0;bottom:0;z-index:999998;' 99 + 'background:' 100 + 'linear-gradient(180deg,rgba(255,255,255,0.07),rgba(255,255,255,0.02)),' 101 + 'var(--bg);' /* solid site bg â keeps iOS Safari toolbar tint matching the site */ 102 + 'border-top:3px solid var(--accent);' 103 + 'box-shadow:0 -8px 32px rgba(0,0,0,0.6),inset 0 1px 0 rgba(255,255,255,0.08);' 104 + 'transform:translateY(100%);transition:transform .4s ease-out;color:var(--text);}' 105 + '.sf.show{transform:translateY(0);}' 106 + '.inner{padding:14px 56px 14px 20px;position:relative;max-width:1100px;margin:0 auto;display:flex;align-items:center;gap:16px;flex-wrap:wrap;}' 107 + '.x{position:absolute;top:8px;right:10px;width:26px;height:26px;border:none;background:rgba(255,255,255,0.1);color:rgba(255,255,255,0.6);border-radius:50%;cursor:pointer;font-size:18px;line-height:1;display:flex;align-items:center;justify-content:center;}' 108 + '.x:hover{background:rgba(255,255,255,0.2);color:#fff;}' 109 + '.lg{display:flex;align-items:center;gap:10px;flex-shrink:0;}' 110 + '.lc{height:56px;min-width:56px;max-width:160px;display:flex;align-items:center;justify-content:center;position:relative;flex-shrink:0;}' 111 + '.lc img{height:100%;width:auto;max-width:100%;object-fit:contain;display:block;}' 112 + '.lc.old{opacity:.55;filter:grayscale(.5);}' 113 + '.arr{color:var(--accent);font-size:28px;font-weight:700;line-height:1;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;animation:arrSlide 1.4s ease-in-out infinite;}' 114 + '@keyframes arrSlide{0%,100%{transform:translateX(0);opacity:.7}50%{transform:translateX(6px);opacity:1}}' 115 + '.tx{flex:1;
115min-width:200px;font-size:13px;color:var(--text);line-height:1.4;}' 116 + '.tx b,.tx strong{color:var(--accent);font-weight:700;}' 117 + '.cta{padding:11px 22px;border:none;border-radius:8px;background:var(--accent);color:var(--accent-text);font-size:14px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;cursor:pointer;flex-shrink:0;transition:transform .15s,box-shadow .15s;white-space:nowrap;display:inline-flex;align-items:center;justify-content:center;gap:8px;}' 118 + '.cta:hover{transform:translateY(-1px);box-shadow:0 6px 18px color-mix(in srgb,var(--accent) 50%, transparent);}' 119 + '.cta .ctarr{display:inline-block;font-size:18px;line-height:1;animation:ctaSlide 1.4s ease-in-out infinite;}' 120 + '@keyframes ctaSlide{0%,100%{transform:translateX(0)}50%{transform:translateX(4px)}}' 121 + '@media(max-width:720px){' 122 + '.inner{padding:10px 12px 12px 12px;gap:10px;}' 123 + '.x{top:6px;right:8px;width:22px;height:22px;font-size:16px;}' 124 + '.lg{width:100%;flex-basis:100%;justify-content:center;order:1;padding:0 32px;}' /* keep close button visible */ 125 + '.lc{height:42px;min-width:42px;max-width:120px;}' 126 + '.tx{font-size:12px;width:100%;flex-basis:100%;order:3;min-width:0;text-align:center;}' 127 + '.cta{padding:15px 14px;font-size:14px;width:100%;order:4;}' 128 + '}'; 129
130 function renderSticky(cfg) { 131 if (!cfg.sticky || !cfg.sticky.enabled) return; 132 133 // Shadow DOM host â totally isolated from the site's CSS. 134 // CSS vars set on :host carry the site's palette into the shadow tree. 135 var host = document.createElement('div'); 136 var bg = cfg.mainColor || '#0d0d1a'; 137 var ac = cfg.accentColor || '#ffd700'; 138 var act = cfg.accentTextColor || '#09090b'; 139 host.style.cssText = 'all:initial;--bg:' + bg + ';--accent:' + ac + ';--accent-text:' + act + ';--text:#cbd5e1;'; 140 var root = host.attachShadow ? host.attachShadow({mode: 'closed'}) : host; 141 var styleEl = document.createElement('style'); 142 styleEl.textContent = STICKY_CSS; 143 root.appendChild(styleEl); 144 145 var foot = document.createElement('div'); foot.className = 'sf'; 146 var inner = document.createElement('div'); inner.className = 'inner'; 147 148 var x = document.createElement('button'); x.className = 'x'; x.innerHTML = '×'; 149 var RESHOW = 10000; 150 x.addEventListener('click', function(){ 151 foot.classList.remove('show'); 152 // Drop padding-bottom and reset --sticky-bar-height so scroll-top etc. 153 // settle back to their default position while the sticky is hidden. 154 syncBarHeight(); 155 // Re-show after the cool-off period â but only if the menu isn't 156 // open at that moment. syncMenuState handles both checks. 157 setTimeout(function(){ syncMenuState(); }, RESHOW); 158 }); 159 inner.appendChild(x); 160 161 // Logos: site (old, â) â arrow â brand (new, â, clickable) 162 if (cfg.siteLogo || cfg.brandLogo) { 163 var lg = document.createElement('div'); lg.className = 'lg'; 164 if (cfg.siteLogo) { 165 var oldC = document.createElement('div'); oldC.className = 'lc old'; 166 var oi = document.createElement('img'); oi.src = cfg.siteLogo; oi.alt = ''; 167 var ar = document.createElement('div'); ar.className = 'arr'; ar.textContent = 'â'; 168 // If site logo 404s (assets folder name changed since deploy), hide both the 169 // logo card AND the arrow â leave just the brand logo without comparison. 170 oi.onerror = function(){ oldC.style.display='none'; ar.style.display='none'; }; 171 oldC.appendChild(oi); lg.appendChild(oldC); lg.appendChild(ar); 172 } 173 if (cfg.brandLogo) { 174 var newC = document.createElement('div'); newC.className = 'lc new'; newC.style.cursor = 'pointer'; 175 var ni = document.createElement('img'); ni.src = cfg.brandLogo; ni.alt = cfg.brandName || ''; ni.onerror = function(){ this.style.display='none'; }; 176 newC.appendChild(ni); 177 newC.addEventListener('click', function(){ go(window.__WCFG && window.__WCFG.ctaHashes && window.__WCFG.ctaHashes.sticky_logo || 'sticky_logo'); }); 178 lg.appendChild(newC); 179 } 180 inner.appendChild(lg); 181 } 182 183 // Custom text â site override â translation pack â minimal fallback 184 var tr = cfg.translations || {}; 185 var rawText = (cfg.sticky && cfg.sticky.text) 186 || tr.stickyText 187 || ((cfg.prize || cfg.prizeDetail) 188 ? ((cfg.countryFlag ? cfg.countryFlag + ' ' : '') + 'Get ' + (cfg.prize || '') + ' ' + (cfg.prizeDetail || '') + ' at <b>{new_brand}</b>') 189 : 'Play at <b>{new_brand}</b>'); 190 rawText = rawText 191 .replace(/\{prize\}/g, esc(cfg.prize || '')) 192 .replace(/\{prize_detail\}/g, esc(cfg.prizeDetail || '')) 193 .replace(/\{prize_sub\}/g, esc(cfg.prizeSub || '')) 194 .replace(/\{new_brand\}/g, '<b>' + esc(cfg.brandName || '') + '</b>') 195 .replace(/\{old_brand\}/g, '<b>' + esc(cfg.siteName || '') + '</b>') 196 .replace(/\{site_name\}/g, '<b>' + esc(cfg.siteName || '') + '</b>') 197 .replace(/\{brand\}/g, esc(cfg.brandName || '')) 198 .replace(/\{flag\}/g, cfg.countryFlag || '') 199 .replace(/\{country_adj\}/g, esc(cfg.countryAdj || cfg.countryName || '')) 200 .replace(/\{country\}/g, esc(cfg.countryName || '')); 201 var tx = document.createElement('div'); tx.className = 'tx'; tx.innerHTML = rawText; 202 inner.appendChild(tx); 203 204 // CTA button â site override â translation pack â "Play Now" 205 var ctaTxt = (cfg.sticky && cfg.sticky.button_text) || (cfg.sticky && cfg.sticky.buttonText) || tr.buttonText || 'Play Now'; 206 var cta = document.createElement('button'); cta.className = 'cta'; 207 cta.innerHTML = esc(ctaTxt) + '<span class="ctarr">â</span>'; 208 cta.addEventListener('click', function(){ go(window.__WCFG && window.__WCFG.ctaHashes && window.__WCFG.ctaHashes.sticky_btn || 'sticky_btn'); }); 209 inner.appendChild(cta); 210 211 foot.appendChild(inner); 212 root.appendChild(foot); 213 document.body.appendChild(host); 214 // Show the sticky bar after slide-in delay â UNLESS the user has 215 // already opened the mobile menu in those 1.5s. syncMenuState reads 216 // body.menu-open and only flips `.show` when allowed. 217 setTimeout(function(){ syncMenuState(); }, 1500); 218 219 // Reserve space at the bottom of the document equal to the sticky's 220 // height so the page footer ends ABOVE the sticky band â sticky
220stays 221 // `position:fixed bottom:0` always, but no longer overlays copyright/RG. 222 // Note: `host` is a wrapper, the visible bar `.sf` is position:fixed so 223 // its size doesn't bubble up to host.offsetHeight â we measure .sf via 224 // the shadow root directly. Use !important to win against any theme 225 // that pins body padding. 226 function syncBarHeight(){ 227 // Only reserve space + expose the CSS variable while the sticky is 228 // actually visible. When closed (X click) we drop both so other 229 // fixed-position UI (scroll-top, chat bubbles) settles back down. 230 var visible = foot.classList.contains('show'); 231 var h = visible ? foot.getBoundingClientRect().height : 0; 232 if (h) { 233 document.body.style.setProperty('padding-bottom', h + 'px', 'important'); 234 document.documentElement.style.setProperty('--sticky-bar-height', h + 'px'); 235 } else { 236 document.body.style.removeProperty('padding-bottom'); 237 document.documentElement.style.setProperty('--sticky-bar-height', '0px'); 238 } 239 } 240 setTimeout(syncBarHeight, 1700); // after slide-in animation finishes 241 setTimeout(syncBarHeight, 3000); // recheck once layout settles 242 window.addEventListener('resize', syncBarHeight); 243 if ('ResizeObserver' in window) { 244 new ResizeObserver(syncBarHeight).observe(foot); 245 } 246 247 // Hide the sticky bar while the mobile menu is open. The theme's 248 // main.js sets `body.menu-open` on burger click â we observe that 249 // class and toggle the bar accordingly. 250 function syncMenuState(){ 251 if (document.body.classList.contains('menu-open')) { 252 foot.classList.remove('show'); 253 } else { 254 foot.classList.add('show'); 255 } 256 syncBarHeight(); 257 } 258 new MutationObserver(syncMenuState).observe(document.body, { 259 attributes: true, attributeFilter: ['class'] 260 }); 261 } 262 263 // Header banner popup (header CTA click â modal). Mirrors old weblovers.vip widget pixel-for-pixel. 264 function showHeaderBanner(cfg, src) { 265 var tr = cfg.translations || {}; 266 var pop = cfg.popup || {}; 267 268 function fill(tpl){ 269 return String(tpl||'') 270 .replace(/\{old_brand\}/g, esc(cfg.siteName || '')) 271 .replace(/\{site_name\}/g, esc(cfg.siteName || '')) 272 .replace(/\{new_brand\}/g, esc(cfg.brandName || '')) 273 .replace(/\{brand\}/g, esc(cfg.brandName || '')) 274 .replace(/\{flag\}/g, cfg.countryFlag || '') 275 .replace(/\{country_adj\}/g, esc(cfg.countryAdj || cfg.countryName || '')) 276 .replace(/\{country\}/g, esc(cfg.countryName || '')); 277 } 278 279 // Shadow DOM host â site palette propagates via CSS vars on :host 280 var host = document.createElement('div'); 281 var bg = cfg.mainColor || '#0d0d1a'; 282 var ac = cfg.accentColor || '#ffd700'; 283 var act = cfg.accentTextColor || '#09090b'; 284 host.style.cssText = 'all:initial;--bg:' + bg + ';--accent:' + ac + ';--accent-text:' + act + ';'; 285 var root = host.attachShadow ? host.attachShadow({mode:'closed'}) : host; 286 var styleEl = document.createElement('style'); 287 styleEl.textContent = POPUP_CSS; 288 root.appendChild(styleEl); 289 290 var ovr = document.createElement('div'); ovr.className = 'ovr'; 291 var wrap = document.createElement('div'); wrap.className = 'wrap'; 292 var banner = document.createElement('div'); banner.className = 'banner'; 293 294 // Background image / fallback gradient 295 var bg = document.createElement('div'); bg.className = 'bg'; 296 if (cfg.brandBg) { 297 bg.style.backgroundImage = 'url(' + cfg.brandBg + ')'; 298 } else if (cfg.brandBgColor) { 299 bg.style.background = 'linear-gradient(180deg,' + cfg.brandBgColor + ' 0%,' + cfg.brandBgColor + ' 30%,#0a0a1a 100%)'; 300 } else { 301 bg.style.background = 'linear-gradient(180deg,#0d0d1a 0%,#1a1a3e 40%,#0f1f3d 100%)'; 302 } 303 banner.appendChild(bg); 304 305 // Close (cleanup also clears timer). Mount on .wrap (not .banner) so it sits OUTSIDE 306 // the banner's overflow:hidden boundary â otherwise the rounded corner clips it. 307 var x = document.createElement('button'); x.className = 'close'; x.innerHTML = '×'; 308 x.addEventListener('click', function(){ try{ clearInterval(timer); }catch(e){} host.remove(); }); 309 310 // Warning strip â "{old_brand} is now closed in {flag} {country}" 311 var warnTpl = (pop.warningText) || tr.bannerRestricted || '{old_brand} is now closed in {flag} {country}'; 312 var warn = document.createElement('div'); warn.className = 'warn'; 313 var warnTxt = document.createElement('span'); warnTxt.className = 'warn-text'; 314 var ic = '<span class="warn-icon">!</span>'; 315 // siteName gets bolded inside the warning 316 var rendered = fill(warnTpl); 317 var siteEsc = esc(cfg.siteName || ''); 318 if (siteEsc && rendered.indexOf(siteEsc) >= 0) { 319 rendered = rendered.split(siteEsc).join('<b>' + siteEsc + '</b>'); 320 } 321 warnTxt.innerHTML = rendered; 322 warn.innerHTML = ic; 323 warn.appendChild(warnTxt); 324 banner.appendChild(warn); 325 326 var body = document.createElement('div'); body.className = 'body'; 327 328 // Recommend text (small uppercase yellow, above logo) 329 var recTpl = (pop.recommendText) || tr.bannerRecommend || 'Play through our partner:'; 330 var rec = document.createElement('div');
330 rec.className = 'recommend'; rec.textContent = fill(recTpl); 331 body.appendChild(rec); 332 333 // Logo with glow + pulse 334 if (cfg.brandLogo) { 335 var lw = document.createElement('div'); lw.className = 'logo-wrap'; 336 var img = document.createElement('img'); img.className = 'logo'; 337 img.src = cfg.brandLogo; img.alt = cfg.brandName || ''; 338 lw.appendChild(img); 339 body.appendChild(lw); 340 } 341 342 // Bonus card 343 var bonus = document.createElement('div'); bonus.className = 'bonus'; 344 var bonusLabel = tr.bannerBonusLabel || 'WELCOME BONUS'; 345 var bl = document.createElement('div'); bl.className = 'bonus-label'; bl.textContent = bonusLabel; 346 bonus.appendChild(bl); 347 var prizeVal = cfg.prize || '100%'; 348 var prizeDetail = cfg.prizeDetail || ''; 349 var pr = document.createElement('div'); pr.className = 'prize'; 350 pr.innerHTML = '<span class="pct">' + esc(prizeVal) + '</span>' + (prizeDetail ? ' ' + esc(prizeDetail) : ''); 351 bonus.appendChild(pr); 352 if (cfg.prizeSub) { 353 var sub = document.createElement('div'); sub.className = 'sub'; sub.textContent = cfg.prizeSub; 354 bonus.appendChild(sub); 355 } 356 body.appendChild(bonus); 357 358 // Exclusive line â "ð¨ð¦ Exclusive for Canadian players only" 359 var exclTpl = (pop.exclusiveText) || tr.bannerExclusive; 360 if (exclTpl) { 361 var excl = document.createElement('div'); excl.className = 'exclusive'; excl.innerHTML = fill(exclTpl); 362 body.appendChild(excl); 363 } 364 365 // CTA (animated green) 366 var claimTpl = (pop.claimText) || tr.claim || 'CLAIM YOUR BONUS'; 367 var cta = document.createElement('button'); cta.className = 'cta'; cta.textContent = fill(claimTpl); 368 cta.addEventListener('click', function(){ try{ clearInterval(timer); }catch(e){} go(src + '_popup'); }); 369 body.appendChild(cta); 370 371 // Timer + bar 372 var seconds = (pop.countdown) || 15; 373 var totalSec = seconds; 374 var timWrap = document.createElement('div'); timWrap.className = 'tim'; 375 var timTxt = document.createElement('div'); timTxt.className = 'tim-text'; 376 var timBar = document.createElement('div'); timBar.className = 'tim-bar'; 377 var timFill = document.createElement('div'); timFill.className = 'tim-fill'; 378 timBar.appendChild(timFill); 379 timWrap.appendChild(timTxt); timWrap.appendChild(timBar); 380 body.appendChild(timWrap); 381 382 var redirectTpl = tr.bannerRedirect || 'redirecting to {brand} in {time}...'; 383 function paint(){ 384 var txt = redirectTpl 385 .replace(/\{brand\}/g, esc(cfg.brandName || 'partner')) 386 .replace(/\{time\}/g, '<span>' + seconds + 's</span>'); 387 timTxt.innerHTML = txt; 388 timFill.style.width = (seconds / totalSec * 100) + '%'; 389 } 390 paint(); 391 var timer = setInterval(function(){ 392 seconds--; paint(); 393 if (seconds <= 0) { clearInterval(timer); go(src + '_auto'); } 394 }, 1000); 395 396 banner.appendChild(body); 397 wrap.appendChild(banner); 398 wrap.appendChild(x); /* mount AFTER banner so close button is on top */ 399 ovr.appendChild(wrap); 400 root.appendChild(ovr); 401 document.body.appendChild(host); 402 403 // Trigger fade-in 404 requestAnimationFrame(function(){ ovr.classList.add('show'); }); 405 } 406 407 // Floating "Play Online" CTA â a persistent affiliate button that fades in 408 // once the visitor has scrolled through the slots (slots are CTAs too). Built 409 // in a closed Shadow DOM so it's fully isolated from the site's (randomized) 410 // CSS and never collides with page classes. 411 function renderFloating(cfg) { 412 if (!cfg.floatingCta || !cfg.floatingCta.enabled) return; 413 // Colour must match the site's own CTA buttons exactly. Read it straight 414 // from the page's CSS variables (--accent-color / --accent-text-color) â 415 // the very same vars the theme buttons use â so the floating CTA can never 416 // drift from them (e.g. if the DB colour and the deployed build differ). 417 // These vars aren't randomized (only classes are), so the names are stable. 418 // Fall back to the config-provided colours, then a hard default. 419 var rootCS = getComputedStyle(document.documentElement); 420 var ac = (rootCS.getPropertyValue('--accent-color') || '').trim() || cfg.accentColor || '#ffd700'; 421 var act = (rootCS.getPropertyValue('--accent-text-color') || '').trim() || cfg.accentTextColor || '#09090b'; 422 var label = cfg.floatingCta.text || 'Play Online'; 423 424 var host = document.createElement('div'); 425 host.style.cssText = 'all:initial;position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:2147483000;'; 426 var root = host.attachShadow ? host.attachShadow({mode:'closed'}) : host; 427 var st = document.createElement('style'); 428 st.textContent = 429 ':host{all:initial}
429' + 430 '.fc{display:inline-flex;align-items:center;gap:8px;padding:14px 30px;border:none;border-radius:999px;' + 431 'background:' + ac + ';background-image:linear-gradient(180deg,rgba(255,255,255,.18),rgba(255,255,255,0) 55%);' + 432 'color:' + act + ';font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;' + 433 'font-weight:700;font-size:14px;text-transform:uppercase;letter-spacing:.03em;cursor:pointer;white-space:nowrap;' + 434 'box-shadow:0 10px 30px -8px rgba(0,0,0,.6),inset 0 1px 0 rgba(255,255,255,.25);' + 435 'opacity:0;transform:translateY(24px) scale(.98);pointer-events:none;' + 436 'transition:opacity .35s ease,transform .35s cubic-bezier(.2,.7,.2,1),box-shadow .2s ease,filter .2s ease}' + 437 '.fc.show{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}' + 438 '.fc:hover{filter:brightness(1.06);transform:translateY(-2px) scale(1);' + 439 'box-shadow:0 16px 38px -8px rgba(0,0,0,.65),inset 0 1px 0 rgba(255,255,255,.32)}' + 440 '.fc:active{transform:translateY(0) scale(.99)}' + 441 '.fa{display:inline-block;animation:fna 1.5s ease-in-out infinite}' + 442 '.fc:hover .fa{animation:none;transform:translateX(5px)}' + 443 '@keyframes fna{0%,100%{transform:translateX(0)}50%{transform:translateX(4px)}}' + 444 // Mobile: stretch to a full-width bar (like the hero CTA) â bigger, 445 // clearly the primary action. Host is pinned left/right by place(). 446 '@media(max-width:767px){.fc{width:100%;justify-content:center;padding:17px 24px;' + 447 'font-size:16px;border-radius:14px;gap:10px}.fa{font-size:18px}}' + 448 '@media(prefers-reduced-motion:reduce){.fc,.fc.show,.fa{animation:none;transition:opacity .2s ease}}'; 449 root.appendChild(st); 450 451 var btn = document.createElement('button'); 452 btn.type = 'button'; 453 btn.className = 'fc'; 454 var lab = document.createElement('span'); lab.textContent = label; 455 var arr = document.createElement('span'); arr.className = 'fa'; arr.innerHTML = '→'; 456 btn.appendChild(lab); btn.appendChild(arr); 457 btn.addEventListener('click', function(){ 458 go((cfg.ctaHashes && cfg.ctaHashes.floating_cta) || 'floating_cta'); 459 }); 460 root.appendChild(btn); 461 document.body.appendChild(host); 462 463 // Desktop: centred pill (left:50% + translateX(-50%)) â clears the corner 464 // scroll-to-top button on its own. Mobile: a full-width bar pinned 12px from 465 // each edge; since it spans the width it would sit under the corner 466 // scroll-to-top button, so we lift that button ABOVE the bar. The button's 467 // own translateY reveal runs inside the shadow, so transforms don't collide. 468 // Everything lifts over the mobile safe area and a sticky bar. 469 function place() { 470 var mobile = window.innerWidth < 768; 471 // Mobile gap from the bottom. iOS Safari keeps a position:fixed element 472 // riding just above its bottom toolbar (it tracks the visible viewport), 473 // so a plain small gap sits nicely above the toolbar whether it's expanded 474 // or collapsed â no dynamic correction needed. Single knob: GAP. 475 var GAP = ((cfg.floatingCta && cfg.floatingCta.gap) || 14) + 'px'; 476 var mExpr = 'var(--sticky-bar-height,0px) + ' + GAP; 477 if (mobile) { 478 host.style.left = '12px'; 479 host.style.right = '12px'; 480 host.style.transform = 'none'; 481 host.style.bottom = 'calc(' + mExpr + ')'; 482 } else { 483 host.style.left = '50%'; 484 host.style.right = 'auto'; 485 host.style.transform = 'translateX(-50%)'; 486 host.style.bottom = 'calc(24px + var(--sticky-bar-height,0px))'; 487 } 488 // Keep the scroll-to-top button clear of the CTA. On mobile lift it above 489 // the full-width bar; on desktop restore its own CSS position. The button 490 // id is randomized per site, so the backend hands it to us in 491 // cfg.floatingCta.scrollTopId (recovered from the built main.js). 492 var top = cfg.floatingCta.scrollTopId 493 ? document.getElementById(cfg.floatingCta.scrollTopId) : null; 494 if (top) { 495 if (mobile) { 496 top.style.setProperty('bottom', 497 'calc(' + mExpr + ' + ' + (host.offsetHeight + 14) + 'px)', 'important'); 498 } else { 499 top.style.removeProperty('bottom'); 500 } 501 } 502 } 503 place(); 504 window.addEventListener('resize', place, {passive:true}); 505
506 // Reveal once the slots block is scrolled past. #slotsGrid is a stable id 507 // (not touched by the anti-fingerprint class renamer). Falls back to scroll 508 // depth on pages without a slots grid (service pages). 509 var anchor = document.getElementById('slotsGrid'); 510 var shown = false; 511 function onScroll() { 512 if (shown) return; 513 var mobile = window.innerWidth < 768; 514 var hit; 515 if (anchor) { 516 hit = anchor.getBoundingClientRect().bottom < window.innerHeight * (mobile ? 0.85 : 0.55); 517 } else { 518 hit = (window.pageYOffset || document.documentElement.scrollTop || 0) > (mobile ? 280 : 560); 519 } 520 if (hit) { shown = true; btn.classList.add('show'); window.removeEventListener('scroll', onScroll); } 521 } 522 window.addEventListener('scroll', onScroll, {passive:true}); 523 onScroll(); 524 } 525 526 // Wire up: replace generic CTA fetch handler with popup-aware handler when popup is enabled 527 fetch(BASE + '/api/widget/config/' + SID).then(function(r){ return r.json(); }).then(function(cfg){ 528 window.__WCFG = cfg; 529 530 // Sticky footer + floating CTA render independently of the popup setting 531 try { renderSticky(cfg); } catch(e) {} 532 try { renderFloating(cfg); } catch(e) {} 533 534 if (!cfg.popup || !cfg.popup.enabled) return; 535 536 var ATR = cfg.ctaAttr || 'data-cta'; 537 var SEL = '[' + ATR + ']'; 538 var POPUP_HASHES = cfg.popupHashes || []; 539 540 function shouldPopup(b) { 541 var src = b.getAttribute(ATR) || ''; 542 // New flow: hash match against per-site popup hashes. 543 if (POPUP_HASHES.indexOf(src) >= 0) return true; 544 // Legacy fallback for sites still serving plain "header-*" data-cta. 545 return src.indexOf('header') === 0; 546 } 547 function rebind(b) { 548 // Drop the inline cloak's listener by cloning the node 549 if (!shouldPopup(b)) return; 550 var clone = b.cloneNode(true); 551 b.parentNode.replaceChild(clone, b); 552 clone._cta = 1; 553 clone.style.cursor = 'pointer'; 554 clone.addEventListener('click', function(e){ 555 e.preventDefault(); 556 showHeaderBanner(cfg, clone.getAttribute(ATR) || 'header'); 557 }); 558 } 559 document.querySelectorAll(SEL).forEach(rebind); 560 var mo = new MutationObserver(function(ms){ 561 ms.forEach(function(m){ m.addedNodes.forEach(function(n){ 562 if (n.nodeType !== 1) return; 563 if (n.matches && n.matches(SEL)) rebind(n); 564 if (n.querySelectorAll) n.querySelectorAll(SEL).forEach(rebind); 565 });}); 566 }); 567 mo.observe(document.body, {childList: true, subtree: true}); 568 }).catch(function(e){ /* config fetch failed â leave inline cloak in charge */ }); 569})();
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.