diff --git a/css/games.css b/css/games.css index 6fa0f6e..3831810 100755 --- a/css/games.css +++ b/css/games.css @@ -1,6 +1,9 @@ .game { width: 20vw; height: 9vh; + min-height: 64px; + max-height: 9vh; + overflow: hidden; background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%); display: flex; justify-content: center; @@ -9,17 +12,17 @@ border-radius: 20px; margin: 15px; /* transition-duration: 0.25s; */ - backdrop-filter: blur(2px); box-shadow: 0 0 10px var(--color-2); text-decoration: none; content-visibility: auto; - contain-intrinsic-size: auto 9vh; + contain-intrinsic-size: 20vw 9vh; } .game:hover { transform: scale(1.1); } .game img { height: calc(100% - 4px - 4px); + max-height: calc(9vh - 8px); border-radius: 15px; aspect-ratio: 1 / 1; margin: 4px; @@ -182,11 +185,6 @@ body { #gamesort:hover { filter: brightness(1.1); } -@-moz-document url-prefix() { - game { - backdrop-filter: blur(0); - } -} warnings { position: absolute; top: 0px; @@ -218,17 +216,16 @@ warning { background-color: rgb(118, 118, 118); box-shadow: 0 0 10px rgb(193, 193, 193); } -body[fast] game { +body[fast] .game { background-color: color-mix(in srgb, var(--color-2) 70%, #000000 30%); - backdrop-filter: blur(0); } @media screen and (max-width: 900px) { - game { + .game { width: 40vw; } } @media screen and (max-width: 660px) { - game { + .game { width: 60vw; } } \ No newline at end of file diff --git a/home.html b/home.html index 8cdad9f..84f88c6 100755 --- a/home.html +++ b/home.html @@ -61,13 +61,6 @@ }) .catch((error) => console.error("Error loading quotes:", error)); - fetch("/api/proxy/status") - .then((res) => res.json()) - .then((data) => { - if (data.unlocked) document.getElementById("proxyNavCard").style.display = ""; - }) - .catch(() => { }); - document.getElementById("openblank").addEventListener("click", (e) => { e.preventDefault(); let win = window.open(); @@ -102,10 +95,6 @@ apps - bookmarklets diff --git a/js/cookie.js b/js/cookie.js deleted file mode 100755 index 747dddf..0000000 --- a/js/cookie.js +++ /dev/null @@ -1,100 +0,0 @@ -// ty 3kh0 for the code <33333 -function getMainSave() { - var mainSave = {}; - // List of items in localStorage that should not be saved - var localStorageDontSave = ["supportalert"]; - - // Convert localStorage to an array of key-value pairs and remove the items that should not be saved - localStorageSave = Object.entries(localStorage); - - for (let entry in localStorageSave) { - if (localStorageDontSave.includes(localStorageSave[entry][0])) { - localStorageSave.splice(entry, 1); - } - } - - // Convert the localStorage array to a base64-encoded JSON string - localStorageSave = btoa(JSON.stringify(localStorageSave)); - - // Add the localStorage data to the mainSave object - mainSave.localStorage = localStorageSave; - - // Get the cookies data and add it to the mainSave object - cookiesSave = document.cookie; - cookiesSave = btoa(cookiesSave); - mainSave.cookies = cookiesSave; - - // Convert the mainSave object to a base64-encoded JSON string - mainSave = btoa(JSON.stringify(mainSave)); - - // Encrypt the mainSave data using AES encryption with the key 'save' - mainSave = CryptoJS.AES.encrypt(mainSave, "egamepass").toString(); - - // Return the encrypted mainSave data - return mainSave; -} - -// Function to download the main save data as a file -function downloadMainSave() { - var data = new Blob([getMainSave()]); - var dataURL = URL.createObjectURL(data); - - var fakeElement = document.createElement("a"); - fakeElement.href = dataURL; - fakeElement.download = "your.selenite.save"; - fakeElement.click(); - URL.revokeObjectURL(dataURL); -} - -// Function to get the main save data from an uploaded file -function getMainSaveFromUpload(data, key) { - if(key) { - data = CryptoJS.AES.decrypt(data, key).toString(CryptoJS.enc.Utf8); - } else { - data = CryptoJS.AES.decrypt(data, "egamepass").toString(CryptoJS.enc.Utf8); - } - // Parse the decrypted data as JSON - var mainSave = JSON.parse(atob(data)); - var mainLocalStorageSave = JSON.parse(atob(mainSave.localStorage)); - var cookiesSave = atob(mainSave.cookies); - - // Set the items in localStorage using the uploaded data - for (let item of mainLocalStorageSave) { - localStorage.setItem(item[0], item[1]); - } - - // Set the cookies using the uploaded data - document.cookie = cookiesSave; -} - -// Function to handle the file upload -function uploadMainSave(key) { - var hiddenUpload = document.querySelector(".hiddenUpload"); - hiddenUpload.click(); - - // Listen for the change event on the file input element - hiddenUpload.addEventListener("change", function (e) { - var files = e.target.files; - var file = files[0]; - if (!file) { - return; - } - - // Read the contents of the uploaded file as text and call getMainSaveFromUpload with the result - var reader = new FileReader(); - - reader.onload = function (e) { - if(key) { - getMainSaveFromUpload(e.target.result, key); - } else { - getMainSaveFromUpload(e.target.result); - } - $("#upload").text("Upload Successful!") - setTimeout(function() { - $("#upload").text("Upload Save") - }, 3000) - }; - - reader.readAsText(file); - }); -} diff --git a/js/loader.js b/js/loader.js index f721969..8c65224 100755 --- a/js/loader.js +++ b/js/loader.js @@ -1,4 +1,33 @@ document.addEventListener("DOMContentLoaded", loadGames); +document.addEventListener("DOMContentLoaded", guardGameGridsFromAds); + +// AdSense in-feed auto ads see the uniform grid of .game tiles as a "feed" and +// inject an ad as a sibling tile, which breaks the flex layout. Strip any +// ad node that lands directly inside a game grid so ads never fill into the list. +function guardGameGridsFromAds() { + const gridIds = ["games", "topGames", "starredgames", "recentgames"]; + const isAdNode = (node) => { + if (!node || node.nodeType !== 1) return false; + if (node.tagName === "INS") return true; + if (node.classList && node.classList.contains("adsbygoogle")) return true; + if (/^(aswift_|google_ads_|div-gpt-)/.test(node.id || "")) return true; + if (typeof node.querySelector === "function" && + node.querySelector("ins.adsbygoogle, iframe[id^='google_ads_iframe'], iframe[id^='aswift_']")) return true; + return false; + }; + const observer = new MutationObserver((mutations) => { + for (const m of mutations) { + for (const node of m.addedNodes) { + if (isAdNode(node)) node.remove(); + } + } + }); + gridIds.forEach((id) => { + const el = document.getElementById(id); + if (el) observer.observe(el, { childList: true }); + }); +} + let elements = []; let gameElements = []; let topElements = []; @@ -143,9 +172,6 @@ async function loadGames() { if (starredGames.length > 0) { document.getElementById("starredHeader").style.display = "block"; _rebuildStarred(); - document.querySelectorAll("#starredgames #star").forEach((e) => { - e.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); starEvent(e); }); - }); } const recentHeader = document.getElementById("recentHeader"); @@ -219,15 +245,39 @@ document.addEventListener("DOMContentLoaded", () => { }); }); +function elementMatchesFilters(el, input, selectedTags) { + const matchesSearch = el.dataset.name.includes(input); + const elTags = el.dataset.tags ? el.dataset.tags.split(",") : []; + const matchesTags = selectedTags.size === 0 || elTags.some((t) => selectedTags.has(t)); + return matchesSearch && matchesTags; +} + +// Filters a section built from cloned .game tiles (starred / recently played): +// hides individual clones that don't match, and hides the whole section +// (container + header) only if nothing inside it matches anymore. +function filterClonedSection(containerId, headerId, input, selectedTags) { + const container = document.getElementById(containerId); + const header = document.getElementById(headerId); + if (!container) return; + let anyVisible = false; + container.querySelectorAll(".game").forEach((el) => { + if (elementMatchesFilters(el, input, selectedTags)) { + el.style.display = "flex"; + anyVisible = true; + } else { + el.style.display = "none"; + } + }); + container.style.display = anyVisible ? "" : "none"; + if (header) header.style.display = anyVisible ? "block" : "none"; +} + function applyFilters() { const searchInput = document.getElementById("gamesearch"); const input = (searchInput ? searchInput.value : "").toUpperCase(); let hidden = 0; elements.forEach((el) => { - const matchesSearch = el.dataset.name.includes(input); - const elTags = el.dataset.tags ? el.dataset.tags.split(",") : []; - const matchesTags = selectedTags.size === 0 || elTags.some((t) => selectedTags.has(t)); - if (matchesSearch && matchesTags) { + if (elementMatchesFilters(el, input, selectedTags)) { el.style.display = "flex"; } else { el.style.display = "none"; @@ -235,6 +285,17 @@ function applyFilters() { } }); document.getElementById("noResults").style.display = hidden >= elements.length ? "flex" : "none"; + + // top games shares tiles with the main list (already filtered above via + // `elements`) - its header just needs to track whether any survived. + const topHeader = document.getElementById("topHeader"); + if (topHeader) { + const anyTopVisible = topElements.some((el) => el.style.display !== "none"); + topHeader.style.display = anyTopVisible ? "block" : "none"; + } + + filterClonedSection("starredgames", "starredHeader", input, selectedTags); + filterClonedSection("recentgames", "recentHeader", input, selectedTags); } let searchRaf = null; @@ -248,26 +309,15 @@ document.addEventListener("DOMContentLoaded", () => { }); }); -function _rebuildStarred() { - const container = document.getElementById("starredgames"); +// cloneNode(true) does not copy event listeners, so the cloned stars need their +// click handler re-bound here rather than relying on each caller to do it. +function _rebuildList(containerId, dirs) { + const container = document.getElementById(containerId); const gamesEl = document.getElementById("games"); const topGEl = document.getElementById("topGames"); container.innerHTML = ""; const fragment = document.createDocumentFragment(); - starredGames.forEach((dir) => { - const el = gamesEl.querySelector(`a.game[data-target='${dir}']`) || (topGEl && topGEl.querySelector(`a.game[data-target='${dir}']`)); - if (el) fragment.appendChild(el.cloneNode(true)); - }); - container.appendChild(fragment); -} - -function _rebuildRecent() { - const container = document.getElementById("recentgames"); - const gamesEl = document.getElementById("games"); - const topGEl = document.getElementById("topGames"); - container.innerHTML = ""; - const fragment = document.createDocumentFragment(); - recentGames.slice(0, 4).forEach((dir) => { + dirs.forEach((dir) => { const el = gamesEl.querySelector(`a.game[data-target='${dir}']`) || (topGEl && topGEl.querySelector(`a.game[data-target='${dir}']`)); if (el) fragment.appendChild(el.cloneNode(true)); }); @@ -277,6 +327,14 @@ function _rebuildRecent() { }); } +function _rebuildStarred() { + _rebuildList("starredgames", starredGames); +} + +function _rebuildRecent() { + _rebuildList("recentgames", recentGames.slice(0, 4)); +} + function getTotalPlayTime(directory) { const playTime = JSON.parse(localStorage.getItem("selenite.playTime") || "{}"); return Object.values(playTime).reduce((total, day) => total + (day[directory] || 0), 0); @@ -365,8 +423,4 @@ function starEvent(e) { localStorage.setItem("selenite.starred", JSON.stringify(starredGames)); _rebuildStarred(); - - document.querySelectorAll("#starredgames #star").forEach((e) => { - e.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); starEvent(e); }); - }); } diff --git a/js/savedownload.js b/js/savedownload.js deleted file mode 100755 index 260cade..0000000 --- a/js/savedownload.js +++ /dev/null @@ -1,31 +0,0 @@ -var s = document.createElement("script"); -function getMainSave() { - alert( - "Thank you for using the Selenite Backup Utility! You can transfer your data to https://selenite.cc or use the Selenite Uploader Utility!" - ); - var e = {}, - a = Object.entries(localStorage); - (a = btoa(JSON.stringify(a))), (e.localStorage = a); - var t = document.cookie; - return ( - (t = btoa(t)), - (e.cookies = t), - (e = btoa(JSON.stringify(e))), - (e = CryptoJS.AES.encrypt(e, "egamepass").toString()) - ); -} -function downloadMainSave() { - var e = new Blob([getMainSave()]), - a = window.URL.createObjectURL(e), - t = document.createElement("a"); - (t.href = a), - (t.download = "your.selenite.save"), - t.click(), - URL.revokeObjectURL(a); -} -(s.src = - "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"), - document.head.appendChild(s); -s.onload = function () { - downloadMainSave(); -};