1/* =================================================================== 2 GameZone â Interactions (vanilla JS, no jQuery) 3 =================================================================== */ 4(function() { 5 "use strict"; 6 7 const appRoot = document.getElementById("app"); 8 const app = document.getElementById("appShell") || (appRoot ? appRoot.querySelector(".app") : null) || appRoot; 9 const btnPlay = document.getElementById("btnPlay"); 10 const toggle = document.getElementById("panelToggle"); 11 const toggleText = toggle ? toggle.querySelector(".panel__toggle-text") : null; 12 const themeBtn = document.getElementById("themeBtn"); 13 const searchForm = document.getElementById("searchForm"); 14 const searchInput = document.getElementById("searchInput"); 15 16 const root = document.documentElement; 17 let ingameAdsReadyTimer = null; 18 19 function deferIngameAdsReady() { 20 window.gameReleaseReadyForIngameAds = false; 21 if (ingameAdsReadyTimer) { 22 clearTimeout(ingameAdsReadyTimer); 23 } 24 ingameAdsReadyTimer = setTimeout(function() { 25 window.gameReleaseReadyForIngameAds = true; 26 document.dispatchEvent(new CustomEvent("gameReleaseReadyForIngameAds")); 27 }, 15000); 28 } 29 30 function isPhoneGameViewport() { 31 return window.matchMedia && window.matchMedia("(max-width: 767px) and (orientation: portrait), (max-width: 991px) and (max-height: 520px) and (orientation: landscape) and (pointer: coarse)").matches; 32 } 33 34 function getStageAdHtml() { 35 const adData = document.getElementById("stageStartAdJson"); 36 if (!adData) return ""; 37 let adHtml = ""; 38 try { 39 adHtml = JSON.parse(adData.textContent || "\"\""); 40 } catch (e) { 41 console.error("Unable to parse stage ad payload", e); 42 } 43 return adHtml; 44 } 45 46 function mountAdHtml(adSlot, adHtml) { 47 if (!adSlot || adSlot.dataset.loaded === "1" || !adHtml) return; 48 49 const tmp = document.createElement("div"); 50 tmp.innerHTML = adHtml; 51 const scripts = []; 52 53 Array.from(tmp.childNodes).forEach(function(node) { 54 if (node.tagName === "SCRIPT") { 55 scripts.push(node); 56 } else { 57 adSlot.appendChild(node); 58 } 59 }); 60 61 adSlot.dataset.loaded = "1"; 62 runScripts(scripts); 63 } 64 65 function mountStageStartAd() { 66 if (isPhoneGameViewport()) return; 67 mountAdHtml(document.querySelector("[data-stage-ad-slot]"), getStageAdHtml()); 68 } 69 70 function mountLoadingAd() { 71 if (isPhoneGameViewport()) return; 72 if (appRoot && appRoot.dataset.loadingAd === "false") return; 73 mountAdHtml(document.querySelector(".gameContainer .ads_loadding_content"), getStageAdHtml()); 74 } 75 76 function clearStageStartAd() { 77 const adSlot = document.querySelector("[data-stage-ad-slot]"); 78 if (!adSlot) return; 79 80 adSlot.innerHTML = ""; 81 adSlot.dataset.loaded = "0"; 82 83 const adMedia = adSlot.closest(".stage__start-ad"); 84 if (adMedia) { 85 adMedia.hidden = true; 86 adMedia.setAttribute("aria-hidden", "true"); 87 } 88 } 89 90 function restoreStageStartAd() { 91 const adSlot = document.querySelector("[data-stage-ad-slot]"); 92 if (!adSlot) return; 93 94 const adMedia = adSlot.closest(".stage__start-ad"); 95 if (adMedia) { 96 adMedia.hidden = false; 97 adMedia.removeAttribute("aria-hidden"); 98 } 99 100 mountStageStartAd(); 101 } 102 103 function protectAppLayoutInlineStyles() { 104 if (!app && !appRoot) return; 105 106 const blockedInlineStyles = { 107 height: ["auto"], 108 "max-height": ["none"] 109 }; 110 111 [appRoot, app].forEach(function(node) { 112 if (!node) return; 113 Object.keys(blockedInlineStyles).forEach(function(prop) { 114 const value = (node.style.getPropertyValue(prop) || "").trim().toLowerCase(); 115 if (blockedInlineStyles[prop].includes(value)) { 116 node.style.removeProperty(prop); 117 } 118 }); 119 }); 120 } 121 122 if (appRoot || app) { 123 protectAppLayoutInlineStyles(); 124 125 if ("MutationObserver" in window) {
126 [appRoot, app].forEach(function(node) { 127 if (!node) return; 128 new MutationObserver(protectAppLayoutInlineStyles).observe(node, { 129 attributes: true, 130 attributeFilter: ["style"] 131 }); 132 }); 133 } 134 } 135 136 /* --- Update toggle label: panel open => "»", panel closed => "info" --- */ 137 function refreshToggle() { 138 if (!app || !toggle) return; 139 const collapsed = app.classList.contains("is-collapsed") || app.classList.contains("is-playing"); 140 if (toggleText) toggleText.textContent = collapsed ? "info" : "»"; 141 toggle.setAttribute("aria-expanded", String(!collapsed)); 142 toggle.title = collapsed ? "Show panel" : "Hide panel"; 143 } 144 145 function initPanelToggleDrag() { 146 if (!toggle || !app) return; 147 148 const dragState = { 149 pointerId: null, 150 startY: 0, 151 startTop: 44, 152 moved: false, 153 suppressClick: false 154 }; 155 156 function getToggleHeight() { 157 const rect = toggle.getBoundingClientRect(); 158 return rect.height || 44; 159 } 160 161 function clampTop(top) { 162 const margin = 8; 163 const maxTop = Math.max(margin, window.innerHeight - getToggleHeight() - margin); 164 return Math.min(Math.max(top, margin), maxTop); 165 } 166 167 function applyTop(top) { 168 root.style.setProperty("--panel-toggle-top", clampTop(top) + "px"); 169 } 170 171 toggle.addEventListener("pointerdown", function(ev) { 172 if (!app.classList.contains("is-playing")) return; 173 if (ev.button !== undefined && ev.button !== 0) return; 174 175 const rect = toggle.getBoundingClientRect(); 176 dragState.pointerId = ev.pointerId; 177 dragState.startY = ev.clientY; 178 dragState.startTop = rect.top; 179 dragState.moved = false; 180 dragState.suppressClick = false; 181 toggle.setPointerCapture(ev.pointerId); 182 }); 183 184 toggle.addEventListener("pointermove", function(ev) { 185 if (dragState.pointerId !== ev.pointerId) return; 186 187 const diffY = ev.clientY - dragState.startY; 188 if (Math.abs(diffY) > 3) { 189 dragState.moved = true; 190 dragState.suppressClick = true; 191 } 192 if (!dragState.moved) return; 193 194 ev.preventDefault(); 195 applyTop(dragState.startTop + diffY); 196 }); 197 198 function finishDrag(ev) { 199 if (dragState.pointerId !== ev.pointerId) return; 200 201 if (dragState.moved) { 202 ev.preventDefault(); 203 applyTop(toggle.getBoundingClientRect().top); 204 } 205 try { 206 toggle.releasePointerCapture(ev.pointerId); 207 } catch (e) {} 208 dragState.pointerId = null; 209 } 210 211 toggle.addEventListener("pointerup", finishDrag); 212 toggle.addEventListener("pointercancel", finishDrag); 213 toggle.addEventListener("click", function(ev) { 214 if (!dragState.suppressClick) return; 215 ev.preventDefault(); 216 ev.stopImmediatePropagation(); 217 dragState.suppressClick = false; 218 }, true); 219 220 window.addEventListener("resize", function() { 221 applyTop(toggle.getBoundingClientRect().top); 222 }); 223 } 224 225 initPanelToggleDrag(); 226 227 function setGamePlayback(active) { 228 const players = document.querySelectorAll(".gameContainer ruffle-player, .gameContainer ruffle-embed, .gameContainer ruffle-object, .gameContainer audio, .gameContainer video"); 229 230 players.forEach(function(player) { 231 try { 232 if (active) { 233 if (player.dataset.gzVolume != null && "volume" in player) { 234 player.volume = Number(player.dataset.gzVolume); 235 delete player.dataset.gzVolume; 236 } 237 if (typeof player.play === "function") player.play(); 238 else if (typeof player.resume === "function") player.resume(); 239 } else { 240 if ("volume" in player && player.dataset.gzVolume == null) { 241 player.dataset.gzVolume = String(player.volume); 242 } 243 if ("volume" in player) player.volume = 0; 244 if (typeof player.pause === "function") player.pause(); 245 else if (typeof player.suspend === "function") player.suspend(); 246 } 247 } catch (e) { 248 console.warn("Unable to update game playback state", e); 249 } 250 }); 251 } 252 253 function runScripts(scripts) { 254 scripts = Array.from(scripts || []); 255 const onDOM = []; 256 const onLoad = []; 257 const winAdd = window.addEventListener, 258 docAdd = document.addEventListener; 259 const prevOnload = window.onload; 260 261 window.addEventListener = function(t, l, o) { 262 if (t === "DOMContentLoaded") onDOM.push(l); 263 else if (t === "load") onLoad.push(l); 264 else winAdd.call(window, t, l, o); 265 }; 266 document.addEventListener = function(t, l, o) { 267 if (t === "DOMContentLoaded") onDOM.push(l); 268 else if (t === "load") onLoad.push(l); 269 else docAdd.call(document, t, l, o); 270 }; 271 272 function fire(list) { 273 list.forEach(function(fn) { 274 try { 275 fn(); 276 } catch (e) { 277 console.error(e); 278 } 279 }); 280 } 281 282 function finish() { 283 window.addEventListener = winAdd; 284 document.addEventListener = docAdd; 285 if (typeof window.onload === "function" && window.onload !== prevO
285nload) onLoad.push(window.onload); 286 fire(onDOM); 287 fire(onLoad); 288 } 289 290 (function next(i) { 291 if (i >= scripts.length) return finish(); 292 const old = scripts[i]; 293 const s = document.createElement("script"); 294 for (const a of old.attributes) s.setAttribute(a.name, a.value); 295 if (old.src) { 296 s.onload = s.onerror = function() { 297 next(i + 1); 298 }; 299 document.body.appendChild(s); 300 } else { 301 s.textContent = old.textContent; 302 document.body.appendChild(s); 303 next(i + 1); 304 } 305 })(0); 306 } 307 308 function mountInlineGame() { 309 const container = document.querySelector(".gameContainer"); 310 const payload = document.getElementById("gamePayloadJson"); 311 if (!container || !payload || payload.dataset.loaded === "1") return; 312 313 let jsonGame = ""; 314 try { 315 const data = JSON.parse(payload.textContent || "\"\""); 316 if (typeof data === "string") { 317 jsonGame = data; 318 } 319 } catch (e) { 320 console.error("Unable to parse game payload", e); 321 } 322 if (!jsonGame) return; 323 324 const tmp = document.createElement("div"); 325 tmp.innerHTML = jsonGame; 326 const scripts = []; 327 328 function collectNestedScripts(node) { 329 if (!node || node.nodeType !== 1) return; 330 node.querySelectorAll("script").forEach(function(script) { 331 scripts.push(script); 332 if (script.parentNode) { 333 script.parentNode.removeChild(script); 334 } 335 }); 336 } 337 338 Array.from(tmp.childNodes).forEach(function(node) { 339 if (node.tagName === "SCRIPT") { 340 scripts.push(node); 341 } else if (node.tagName === "BASE" || node.tagName === "LINK") { 342 document.head.appendChild(node); 343 } else { 344 collectNestedScripts(node); 345 container.appendChild(node); 346 } 347 }); 348 payload.dataset.loaded = "1"; 349 runScripts(scripts); 350 } 351 352 function loadIframeGame() { 353 document.querySelectorAll(".iframe-game[data-iframe-src]").forEach(function(frameGame) { 354 if (frameGame.dataset.loaded === "1") return; 355 frameGame.innerHTML = '<iframe style="width:100%;height:100%;border:0;display:block;" src="' + frameGame.dataset.iframeSrc + '" frameborder="0" scrolling="no" allowfullscreen allow="autoplay; fullscreen; camera; focus-without-user-activation *; monetization; gamepad; keyboard-map *; xr-spatial-tracking; clipboard-write; web-share; accelerometer; magnetometer; gyroscope; display-capture" sandbox="allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts allow-same-origin allow-downloads"></iframe>'; 356 frameGame.dataset.loaded = "1"; 357 }); 358 } 359 360 /* --- Play: hide left section + shrink right panel to 0 => blank screen --- */ 361 function startPlaying() { 362 if (!app) return; 363 clearStageStartAd(); 364 deferIngameAdsReady(); 365 mountInlineGame(); 366 mountLoadingAd(); 367 document.dispatchEvent(new CustomEvent("gamePayloadMounted")); 368 loadIframeGame(); 369 app.classList.add("is-playing"); 370 app.classList.remove("is-collapsed"); 371 setGamePlayback(true); 372 refreshToggle(); 373 } 374 if (btnPlay) btnPlay.addEventListener("click", startPlaying); 375 376 /* --- » / info button: first hide-panel click should start/load the game too --- */ 377 if (toggle && app) { 378 toggle.addEventListener("click", function() { 379 if (app.classList.contains("is-playing")) { 380 app.classList.remove("is-playing"); // info => exit playing, reopen panel 381 setGamePlayback(false); 382 restoreStageStartAd(); 383 refreshToggle(); 384 return; 385 } 386 387 if (app.classList.contains("is-collapsed")) { 388 app.classList.remove("is-collapsed"); 389 setGamePlayback(false); 390 restoreStageStartAd(); 391 refreshToggle(); 392 return; 393 } 394 395 startPlaying(); 396 }); 397 } 398 399 document.addEventListener("visibilitychange", function() { 400 if (!app || !app.classList.contains("is-playing")) return; 401 setGamePlayback(!document.hidden); 402 }); 403 /* --- Light / dark mode (remember the choice) --- */ 404 function applyTheme(t) { 405 root.setAttribute("data-theme", t); 406 try { 407 localStorage.setItem("gz-theme", t); 408 } catch (e) {} 409 } 410 (function initTheme() { 411 let saved = null; 412 try { 413 saved = localStorage.getItem("gz-theme"); 414 } catch (e) {} 415 if (saved) applyTheme(saved); 416 else if (window.matchMedia && matchMedia("(prefers-color-scheme: light)").matches) applyTheme("light"); 417 })(); 418 if (themeBtn) { 419 themeBtn.addEventListener("click", function() { 420 applyTheme(root.getAttribute("data-theme") === "light" ? "dark" : "light"); 421 }); 422 } 423 424 /* --- Menu / categories: active state --- */ 425 function bindActive(selector, activeClass) { 426 document.querySelectorAll(selector).forEach(function(el) { 427 el.addEventListener("click", function(e) { 428 el.parentNode.querySelectorAll(selector).forEach(function(s) { 429 s.classList.remove(activeClass); 430 }); 431 el.classList.add(activeClass); 432 }); 433 }); 434 } 435 bindActive(".menu__item", "is-active"); 436 bindActive(".cat", "is-active"); 437 438 /* --- Search: submit to server-side results --- */ 439 if (searchForm) searchForm.addEventListener("submit", function(e) { 440 const query = searchInput ? (searchInput.value || '').trim() : ''; 441 if (query === '') { 442 e.preventDefault(); 443 } 444 }); 445 446 mountStageStartAd(); 447 448 /* --- Esc key: exit playing / reopen the panel --- */ 449 document.addEventListener("keydown", function(e) { 450 if (e.key === "Escape") { 451 if (!app) return; 452 app.classList.remove("is-playing", "is-collapsed"); 453 setGamePlayback(false); 454 restoreStageStartAd(); 455 refreshToggle(); 456 } 457 }); 458 459 refreshToggle(); 460})();
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.