PageSourceSearch

https://toenissteiner-studentenforum.de/mrq8z/wkfd0a8t.js

js toenissteiner-studentenforum.de collected 2026-10-08 05:42:52 UTC 4,878 bytes, 122 lines download raw bytes

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.