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 {
|
.game {
|
||||||
width: 20vw;
|
width: 20vw;
|
||||||
height: 9vh;
|
height: 9vh;
|
||||||
|
min-height: 64px;
|
||||||
|
max-height: 9vh;
|
||||||
|
overflow: hidden;
|
||||||
background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%);
|
background-color: color-mix(in srgb, var(--color-2) 40%, #00000000 60%);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -9,17 +12,17 @@
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
/* transition-duration: 0.25s; */
|
/* transition-duration: 0.25s; */
|
||||||
backdrop-filter: blur(2px);
|
|
||||||
box-shadow: 0 0 10px var(--color-2);
|
box-shadow: 0 0 10px var(--color-2);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
content-visibility: auto;
|
content-visibility: auto;
|
||||||
contain-intrinsic-size: auto 9vh;
|
contain-intrinsic-size: 20vw 9vh;
|
||||||
}
|
}
|
||||||
.game:hover {
|
.game:hover {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
.game img {
|
.game img {
|
||||||
height: calc(100% - 4px - 4px);
|
height: calc(100% - 4px - 4px);
|
||||||
|
max-height: calc(9vh - 8px);
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
@ -182,11 +185,6 @@ body {
|
|||||||
#gamesort:hover {
|
#gamesort:hover {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
}
|
}
|
||||||
@-moz-document url-prefix() {
|
|
||||||
game {
|
|
||||||
backdrop-filter: blur(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
warnings {
|
warnings {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
@ -218,17 +216,16 @@ warning {
|
|||||||
background-color: rgb(118, 118, 118);
|
background-color: rgb(118, 118, 118);
|
||||||
box-shadow: 0 0 10px rgb(193, 193, 193);
|
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%);
|
background-color: color-mix(in srgb, var(--color-2) 70%, #000000 30%);
|
||||||
backdrop-filter: blur(0);
|
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 900px) {
|
@media screen and (max-width: 900px) {
|
||||||
game {
|
.game {
|
||||||
width: 40vw;
|
width: 40vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 660px) {
|
@media screen and (max-width: 660px) {
|
||||||
game {
|
.game {
|
||||||
width: 60vw;
|
width: 60vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
11
home.html
11
home.html
@ -61,13 +61,6 @@
|
|||||||
})
|
})
|
||||||
.catch((error) => console.error("Error loading quotes:", error));
|
.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) => {
|
document.getElementById("openblank").addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let win = window.open();
|
let win = window.open();
|
||||||
@ -102,10 +95,6 @@
|
|||||||
<img src="img/apps.svg" />
|
<img src="img/apps.svg" />
|
||||||
<span>apps</span>
|
<span>apps</span>
|
||||||
</a>
|
</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">
|
<a class="navCard" href="bookmarklets.html">
|
||||||
<img src="img/bookmarklets.svg" />
|
<img src="img/bookmarklets.svg" />
|
||||||
<span>bookmarklets</span>
|
<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", 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 elements = [];
|
||||||
let gameElements = [];
|
let gameElements = [];
|
||||||
let topElements = [];
|
let topElements = [];
|
||||||
@ -143,9 +172,6 @@ async function loadGames() {
|
|||||||
if (starredGames.length > 0) {
|
if (starredGames.length > 0) {
|
||||||
document.getElementById("starredHeader").style.display = "block";
|
document.getElementById("starredHeader").style.display = "block";
|
||||||
_rebuildStarred();
|
_rebuildStarred();
|
||||||
document.querySelectorAll("#starredgames #star").forEach((e) => {
|
|
||||||
e.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); starEvent(e); });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const recentHeader = document.getElementById("recentHeader");
|
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() {
|
function applyFilters() {
|
||||||
const searchInput = document.getElementById("gamesearch");
|
const searchInput = document.getElementById("gamesearch");
|
||||||
const input = (searchInput ? searchInput.value : "").toUpperCase();
|
const input = (searchInput ? searchInput.value : "").toUpperCase();
|
||||||
let hidden = 0;
|
let hidden = 0;
|
||||||
elements.forEach((el) => {
|
elements.forEach((el) => {
|
||||||
const matchesSearch = el.dataset.name.includes(input);
|
if (elementMatchesFilters(el, input, selectedTags)) {
|
||||||
const elTags = el.dataset.tags ? el.dataset.tags.split(",") : [];
|
|
||||||
const matchesTags = selectedTags.size === 0 || elTags.some((t) => selectedTags.has(t));
|
|
||||||
if (matchesSearch && matchesTags) {
|
|
||||||
el.style.display = "flex";
|
el.style.display = "flex";
|
||||||
} else {
|
} else {
|
||||||
el.style.display = "none";
|
el.style.display = "none";
|
||||||
@ -235,6 +285,17 @@ function applyFilters() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
document.getElementById("noResults").style.display = hidden >= elements.length ? "flex" : "none";
|
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;
|
let searchRaf = null;
|
||||||
@ -248,26 +309,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function _rebuildStarred() {
|
// cloneNode(true) does not copy event listeners, so the cloned stars need their
|
||||||
const container = document.getElementById("starredgames");
|
// 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 gamesEl = document.getElementById("games");
|
||||||
const topGEl = document.getElementById("topGames");
|
const topGEl = document.getElementById("topGames");
|
||||||
container.innerHTML = "";
|
container.innerHTML = "";
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
starredGames.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));
|
|
||||||
});
|
|
||||||
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) => {
|
|
||||||
const el = gamesEl.querySelector(`a.game[data-target='${dir}']`) || (topGEl && topGEl.querySelector(`a.game[data-target='${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));
|
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) {
|
function getTotalPlayTime(directory) {
|
||||||
const playTime = JSON.parse(localStorage.getItem("selenite.playTime") || "{}");
|
const playTime = JSON.parse(localStorage.getItem("selenite.playTime") || "{}");
|
||||||
return Object.values(playTime).reduce((total, day) => total + (day[directory] || 0), 0);
|
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));
|
localStorage.setItem("selenite.starred", JSON.stringify(starredGames));
|
||||||
_rebuildStarred();
|
_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