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:
@ -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;
|
||||
}
|
||||
}
|
||||
11
home.html
11
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 @@
|
||||
<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>
|
||||
|
||||
100
js/cookie.js
100
js/cookie.js
@ -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);
|
||||
});
|
||||
}
|
||||
108
js/loader.js
108
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 <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); });
|
||||
});
|
||||
}
|
||||
|
||||
@ -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();
|
||||
};
|
||||
Reference in New Issue
Block a user