// this IS ai generated // wanna test real world difference soon document.addEventListener("DOMContentLoaded", loadGames); let elements = []; let isDev = []; let starredGames = []; // Cache DOM references const domCache = { games: null, topGames: null, starredGames: null, gameCount: null, loadingMsg: null, allHeader: null, topHeader: null, starredHeader: null, noResults: null, gameSearch: null }; function initDOMCache() { domCache.games = document.getElementById("games"); domCache.topGames = document.getElementById("topGames"); domCache.starredGames = document.getElementById("starredgames"); domCache.gameCount = document.getElementById("gameCount"); domCache.loadingMsg = document.getElementById("loadingMsg"); domCache.allHeader = document.getElementById("allHeader"); domCache.topHeader = document.getElementById("topHeader"); domCache.starredHeader = document.getElementById("starredHeader"); domCache.noResults = document.getElementById("noResults"); domCache.gameSearch = document.getElementById("gamesearch"); } async function loadGames() { initDOMCache(); // Load starred games once starredGames = JSON.parse(localStorage.getItem("selenite.starred") || '[]'); // Fetch and sort games const data = await (await fetch("/resources/games.json")).json(); const sorted = data.sort((a, b) => { const n1 = a.name.toUpperCase(); const n2 = b.name.toUpperCase(); return n1 < n2 ? -1 : n1 > n2 ? 1 : 0; }); // Use DocumentFragments for batched DOM operations const gamesFragment = document.createDocumentFragment(); const topGamesFragment = document.createDocumentFragment(); sorted.forEach(element => { const isStarred = starredGames.includes(element.directory); const newElement = createGameElement(element, isStarred); elements.push(newElement); if (element.tags?.includes("top")) { topGamesFragment.appendChild(newElement); } else { gamesFragment.appendChild(newElement); } if (element.tags?.includes("tofinish")) { isDev.push(element.directory); } }); // Append all elements at once (2 reflows instead of N) domCache.games.appendChild(gamesFragment); domCache.topGames.appendChild(topGamesFragment); // Update UI domCache.gameCount.innerText = `${data.length} games loaded!`; domCache.loadingMsg.style.display = "none"; domCache.allHeader.style.display = "block"; domCache.topHeader.style.display = "block"; if (isDev.length > 0) { sAlert("wip games", "dev message, shouldnt be seen"); console.log(isDev.join(",")); } // Render starred games if any if (starredGames.length > 0) { renderStarredGames(); } // Setup event delegation for game clicks and stars setupEventDelegation(); } function createGameElement(element, isStarred) { const newElement = document.createElement("game"); newElement.setAttribute("data-target", element.directory); newElement.setAttribute("data-image", element.image); newElement.setAttribute("data-name", element.name); const image = document.createElement("img"); image.src = `/resources/semag/${element.directory}/${element.image}`; image.loading = "lazy"; const holder = document.createElement("div"); holder.id = "holder"; const title = document.createElement("h1"); title.innerText = element.name; const warnings = document.createElement("warnings"); // Add warning tags if present if (element.tags) { if (element.tags.includes("13+")) { const thirteenplus = document.createElement("warning"); thirteenplus.innerText = "13+"; thirteenplus.classList = "thirteen"; warnings.appendChild(thirteenplus); } if (element.tags.includes("horror")) { const horror = document.createElement("warning"); horror.innerText = "😱"; horror.classList = "horror"; warnings.appendChild(horror); } if (element.tags.includes("gore")) { const gore = document.createElement("warning"); gore.innerText = "🩸"; gore.classList = "gore"; warnings.appendChild(gore); } } // Only add star for non-top games if (!element.tags?.includes("top")) { const star = document.createElement("img"); star.id = "star"; star.classList = "star"; star.src = isStarred ? "/img/star-fill.svg" : "/img/star.svg"; holder.appendChild(title); holder.appendChild(star); } else { holder.appendChild(title); } newElement.appendChild(image); newElement.appendChild(holder); newElement.appendChild(warnings); return newElement; } function setupEventDelegation() { // Delegate game clicks on parent containers const gameContainers = [domCache.games, domCache.topGames, domCache.starredGames]; gameContainers.forEach(container => { container.addEventListener("click", (e) => { const gameElement = e.target.closest("game"); if (!gameElement) return; const clickedStar = e.target.id === "star" || e.target.classList.contains("star"); if (clickedStar) { e.preventDefault(); e.stopPropagation(); handleStarClick(gameElement); } else { const name = gameElement.getAttribute("data-name"); const dir = gameElement.getAttribute("data-target"); const img = gameElement.getAttribute("data-image"); location.href = `/loader.html?title=${encodeURIComponent(name)}&dir=${dir}&img=${img}&type=g`; } }); }); } function handleStarClick(gameElement) { const game = gameElement.getAttribute("data-target"); const starImages = document.querySelectorAll(`game[data-target='${game}'] #star`); starredGames = JSON.parse(localStorage.getItem("selenite.starred") || '[]'); if (!starredGames.includes(game)) { starredGames.push(game); starredGames.sort(); starImages.forEach(img => img.src = "/img/star-fill.svg"); } else { starredGames.splice(starredGames.indexOf(game), 1); starImages.forEach(img => img.src = "/img/star.svg"); } localStorage.setItem("selenite.starred", JSON.stringify(starredGames)); renderStarredGames(); } function renderStarredGames() { domCache.starredHeader.style.display = starredGames.length > 0 ? "block" : "none"; domCache.starredGames.innerHTML = ""; if (starredGames.length === 0) return; const fragment = document.createDocumentFragment(); starredGames.forEach((gameDir) => { const originalElement = domCache.games.querySelector(`game[data-target='${gameDir}']`); if (originalElement) { const clonedElement = originalElement.cloneNode(true); fragment.appendChild(clonedElement); } }); domCache.starredGames.appendChild(fragment); } // Search functionality document.addEventListener("DOMContentLoaded", () => { const gameSearch = document.getElementById("gamesearch"); if (!gameSearch) return; gameSearch.addEventListener("input", () => { const input = gameSearch.value.toUpperCase(); let hiddenCount = 0; if (elements.length > 0) { elements.forEach((element) => { const title = element.getAttribute("data-name").toUpperCase(); if (title.includes(input)) { element.style.display = "flex"; } else { element.style.display = "none"; hiddenCount++; } }); } const noResults = document.getElementById("noResults"); if (noResults) { noResults.style.display = hiddenCount >= elements.length ? "flex" : "none"; } }); });