1(function () { 2 const GAMES_JSON = '/lib/demo_games/games.json'; 3 const cache = new Map(); 4 5 // ÐагÑÑжаем и кеÑиÑÑем games.json 6 async function fetchGames() { 7 if (cache.size > 0) return cache; 8 try { 9 const res = await fetch(GAMES_JSON, { headers: { 'Accept': 'application/json' } }); 10 const list = await res.json(); 11 if (Array.isArray(list)) { 12 for (const g of list) { 13 if (g.slug && g.demo_url) cache.set(g.slug.toLowerCase(), g.demo_url); 14 } 15 } 16 } catch (e) { 17 console.error('Failed to load games.json', e); 18 } 19 return cache; 20 } 21 22 // ÐолÑÑиÑÑ demo_url по slug 23 async function getDemoUrl(slug) { 24 if (!slug) return null; 25 const games = await fetchGames(); 26 const raw = games.get(slug.toLowerCase()); 27 if (!raw) return null; 28 29 // ÐÑпÑавлÑем & â & 30 const demo = raw.replace(/&/g, '&'); 31 // ÐÑли не URL, мапим на iframe 32 return /^https?:\/\//i.test(demo) 33 ? demo 34 : '/demo/iframe.php?slug=' + encodeURIComponent(demo); 35 } 36 37 // ÐодалÑнÑе ÑлеменÑÑ 38 const modal = document.getElementById('gameModal'); 39 const iframeWrap = modal?.querySelector('.game-modal__iframe-wrap'); 40 const spinner = modal?.querySelector('.game-modal__spinner'); 41 const backdrop = modal?.querySelector('.game-modal__backdrop'); 42 const closeBtn = modal?.querySelector('.game-modal__close'); 43 const noticeModal = document.getElementById('gameNoticeModal'); 44 const noticeBackdrop = noticeModal?.querySelector('.game-modal__backdrop'); 45 const noticeClose = noticeModal?.querySelector('.game-modal__close'); 46 47 function lockScroll() { document.documentElement.classList.add('gm-lock'); document.body.classList.add('gm-lock'); } 48 function unlockScroll() { document.documentElement.classList.remove('gm-lock'); document.body.classList.remove('gm-lock'); } 49 function showSpinner() { if (spinner) { spinner.style.display = 'grid'; spinner.hidden = false; } } 50 function hideSpinner() { if (spinner) { spinner.style.display = 'none'; spinner.hidden = true; } } 51 function openNotice() { if (noticeModal) { noticeModal.classList.add('is-open'); noticeModal.setAttribute('aria-hidden', 'false'); lockScroll(); } } 52 function closeNotice() { if (noticeModal) { noticeModal.classList.remove('is-open'); noticeModal.setAttribute('aria-hidden', 'true'); unlockScroll(); } } 53 54 function openModalWithGame(url) { 55 if (!modal || !iframeWrap) return; 56 if (!url) { openNotice(); return; } 57 iframeWrap.innerHTML = ''; 58 showSpinner(); 59 const iframe = document.createElement('iframe'); 60 iframe.src = url; 61 iframe.allow = 'autoplay; fullscreen; encrypted-media'; 62 iframe.style.width = '100%'; 63 iframe.style.height = '100%'; 64 iframe.style.border = '0'; 65 iframe.addEventListener('load', hideSpinner, { once: true }); 66 iframeWrap.appendChild(iframe); 67 modal.classList.add('is-open'); 68 modal.setAttribute('aria-hidden', 'false'); 69 lockScroll(); 70 } 71 72 function closeModal() { 73 if (!modal || !iframeWrap) return; 74 modal.classList.remove('is-open'); 75 modal.setAttribute('aria-hidden', 'true'); 76 iframeWrap.innerHTML = ''; 77 hideSpinner(); 78 unlockScroll(); 79 } 80 81 backdrop?.addEventListener('click', closeModal); 82 closeBtn?.addEventListener('click', closeModal); 83 noticeBackdrop?.addEventListener('click', closeNotice); 84 noticeClose?.addEventListener('click', closeNotice); 85 document.addEventListener('keydown', e => { 86 if (e.key === 'Escape') { closeModal(); closeNotice(); } 87 }); 88 89 // ÐелегаÑÐ¸Ñ ÐºÐ»Ð¸ÐºÐ¾Ð² по каÑÑоÑкам (поддеÑживаем и .m-game-card, и .slot-card) 90 document.addEventListener('click', async e => { 91 const card = e.target.closest('.m-game-card, .slot-card'); 92 if (!card) return; 93 94 const link = card.closest('a[href]') || card.querySelector('a[href]'); 95 if (!link) return; 96 97 let href = (link.getAttribute('href') || '').trim(); 98 99 // ÐÑовеÑÑем модалÑнÑй пÑеÑÐ¸ÐºÑ 100 const isModal = href.toLowerCase().startsWith('game:') || href === '#' || href === ''; 101 if (!isModal) return; // обÑÑÐ½Ð°Ñ ÑÑÑаниÑÐ½Ð°Ñ ÑÑÑлка 102 103 e.preventDefault(); 104 e.stopPropagation(); 105 106 // Снимаем пÑеÑÐ¸ÐºÑ "game:" 107 let slug = href.toLowerCase().startsWith('game:') ? href.slice(5).trim() : ''; 108 if (!slug) { 109 const f = card.querySelector('.m-game-footer[data-game-id]'); 110 slug = f?.getAttribute('data-game-id') || ''; 111 } 112 113 if (!slug) { openNotice(); return; } 114 115 showSpinner(); 116 const url = await getDemoUrl(slug); 117 hideSpinner(); 118 119 if (!url) { openNotice(); return; } 120 openModalWithGame(url); 121 }, true); 122})();
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.