PageSourceSearch

https://99-nightsintheforest.io/static/theme5/script.js?v=1791071176

js 99-nightsintheforest.io collected 2026-10-03 23:46:26 UTC 16,735 bytes, 460 lines download raw bytes

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.