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
-
-
- proxy
-
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();
-};