perf fix + ad guard for the games grid, search section cleanup

- drop backdrop-filter from .game tiles (likely cause of a freeze-on-load
  bug on heavy uncached loads) and harden tile sizing so it can't balloon
  regardless; fix some long-dead CSS selectors along the way (mobile
  breakpoints were silently never applying)
- strip AdSense in-feed ads if they try to inject into the games grid
- top games / starred / recently played now only show a section if
  something inside it still matches the current search or tag filter,
  instead of leaving orphaned tiles with no header or stale clutter
- remove the proxy nav card from home (dead now that it's disabled)
- delete cookie.js and savedownload.js, confirmed fully unreferenced
  dead code superseded by the save/load flow already in settings.html
This commit is contained in:
first@lynx.com
2026-10-01 00:30:48 -04:00
parent 3f1bb80f8f
commit 3781b08108
5 changed files with 89 additions and 180 deletions

View File

@ -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;
}
}

View File

@ -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 @@
<img src="img/apps.svg" />
<span>apps</span>
</a>
<a class="navCard" href="proxy.html" id="proxyNavCard" style="display: none;">
<img src="img/addlink.svg" />
<span>proxy</span>
</a>
<a class="navCard" href="bookmarklets.html">
<img src="img/bookmarklets.svg" />
<span>bookmarklets</span>

View File

@ -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);
});
}

View File

@ -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 <ins> 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); });
});
}

View File

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