<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>gn-math</title>
<!-- Google Fonts & FontAwesome -->
<style>
:root {
--bg-color: #0a0c10;
--glass-bg: rgba(22, 27, 34, 0.55);
--glass-card: rgba(255, 255, 255, 0.03);
--glass-card-hover: rgba(255, 255, 255, 0.07);
--glass-border: rgba(255, 255, 255, 0.08);
--glass-border-focus: rgba(99, 102, 241, 0.5);
--accent-glow: rgba(99, 102, 241, 0.25);
--accent-primary: #6366f1;
--accent-hover: #4f46e5;
--text-primary: #f3f4f6;
--text-secondary: #9ca3af;
--shadow-glow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
}
body:not(.dark-mode) {
--bg-color: #f1f5f9;
--glass-bg: rgba(255, 255, 255, 0.65);
--glass-card: rgba(255, 255, 255, 0.7);
--glass-card-hover: rgba(255, 255, 255, 0.9);
--glass-border: rgba(0, 0, 0, 0.08);
--glass-border-focus: rgba(99, 102, 241, 0.5);
--accent-glow: rgba(99, 102, 241, 0.15);
--text-primary: #0f172a;
--text-secondary: #64748b;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease, transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
body {
font-family: 'Plus Jakarta Sans', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
min-height: 100vh;
padding: 40px 20px;
position: relative;
overflow-x: hidden;
}
/* Glassmorphism ambient background glow circles */
body::before, body::after {
content: '';
position: fixed;
width: 450px;
height: 450px;
border-radius: 50%;
background: radial-gradient(circle, var(--accent-glow) 0%, rgba(0,0,0,0) 70%);
z-index: -1;
pointer-events: none;
animation: float 12s infinite alternate ease-in-out;
}
body::before { top: -100px; left: -100px; }
body::after { bottom: -100px; right: -100px; animation-delay: -6s; }
@keyframes float {
0% { transform: translate(0, 0) scale(1); }
100% { transform: translate(60px, 40px) scale(1.1); }
}
h1 {
text-align: center;
font-size: 2.5rem;
font-weight: 700;
letter-spacing: -0.025em;
margin-bottom: 25px;
background: linear-gradient(135deg, var(--text-primary) 30%, var(--accent-primary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Glass Search Bar */
.search-wrapper {
position: relative;
max-width: 800px;
margin: 0 auto 20px auto;
}
.search-wrapper i {
position: absolute;
left: 18px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
font-size: 1.1rem;
}
#searchBar {
width: 100%;
padding: 14px 20px 14px 48px;
border: 1px solid var(--glass-border);
border-radius: 14px;
font-size: 15px;
background: var(--glass-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
color: var(--text-primary);
outline: none;
box-shadow: var(--shadow-glow);
}
#searchBar:focus {
border-color: var(--glass-border-focus);
box-shadow: 0 0 20px var(--accent-glow);
}
/* Top Controls Bar */
#topControls {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 auto 25px auto;
max-width: 800px;
gap: 15px;
flex-wrap: wrap;
}
#dataButtons {
display: flex;
gap: 10px;
}
.btn-glass {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
border: 1px solid var(--glass-border);
border-radius: 12px;
background: var(--glass-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.btn-glass:hover {
background: var(--glass-card-hover);
transform: translateY(-2px);
border-color: var(--glass-border-focus);
}
.btn-accent {
background: var(--accent-primary);
border-color: transparent;
color: #ffffff;
}
.btn-accent:hover {
background: var(--accent-hover);
box-shadow: 0 6px 20px var(--accent-glow);
}
/* Custom Select Dropdown */
.select-wrapper {
position: relative;
}
#sortOptions {
padding: 10px 36px 10px 16px;
border: 1px solid var(--glass-border);
border-radius: 12px;
font-size: 14px;
font-weight: 500;
appearance: none;
background: var(--glass-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: var(--text-primary);
cursor: pointer;
outline: none;
}
.select-wrapper::after {
content: "\f078";
font-family: "Font Awesome 6 Free";
font-weight: 900;
font-size: 11px;
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
pointer-events: none;
}
/* Container & Cards */
.zone-wrapper {
max-width: 1000px;
margin: 0 auto;
}
.zone-count {
text-align: center;
font-size: 14px;
font-weight: 500;
margin-bottom: 20px;
color: var(--text-secondary);
}
#container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 20px;
}
.zone-item {
background: var(--glass-card);
border: 1px solid var(--glass-border);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-radius: 16px;
overflow: hidden;
text-align: center;
cursor: pointer;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
opacity: 0;
animation: fadeIn 0.4s ease forwards;
}
@keyframes fadeIn {
to { opacity: 1; transform: translateY(0); }
from { opacity: 0; transform: translateY(10px); }
}
.zone-item:hover {
transform: translateY(-6px) scale(1.02);
background: var(--glass-card-hover);
border-color: var(--glass-border-focus);
box-shadow: 0 12px 30px var(--accent-glow);
}
.img-container {
width: 100%;
height: 120px;
overflow: hidden;
background: rgba(0, 0, 0, 0.2);
position: relative;
}
.zone-item img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.zone-item:hover img {
transform: scale(1.08);
}
.zone-item button {
width: 100%;
padding: 12px 10px;
border: none;
background: transparent;
color: var(--text-primary);
font-size: 14px;
font-weight: 600;
cursor: pointer;
margin-top: auto;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
/* Spinner & Loading State */
.loading-spinner {
grid-column: 1 / -1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 0;
color: var(--text-secondary);
gap: 15px;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid var(--glass-border);
border-top: 3px solid var(--accent-primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Modal Overlay & Popup Window */
#modalOverlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
z-index: 999;
opacity: 0;
transition: opacity 0.3s ease;
}
#zoneViewer {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -45%) scale(0.95);
width: 85%;
height: 85%;
max-width: 1200px;
background: var(--glass-bg);
backdrop-filter: blur(24px);
-webkit-backdrop-filter: blur(24px);
border: 1px solid var(--glass-border);
border-radius: 20px;
z-index: 1000;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
flex-direction: column;
overflow: hidden;
opacity: 0;
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease;
}
#zoneViewer.active {
display: flex;
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
#modalOverlay.active {
display: block;
opacity: 1;
}
#zoneInfo {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background: rgba(255, 255, 255, 0.03);
border-bottom: 1px solid var(--glass-border);
}
#zoneTitle {
font-weight: 600;
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 10px;
}
#zoneId {
font-size: 0.85rem;
color: var(--text-secondary);
background: var(--glass-card);
padding: 2px 8px;
border-radius: 6px;
border: 1px solid var(--glass-border);
}
#controls {
display: flex;
gap: 8px;
}
#zoneFrame {
width: 100%;
flex-grow: 1;
border: none;
background: transparent;
}
/* Floating Theme Toggle */
#darkModeToggle {
position: fixed;
bottom: 24px;
right: 24px;
width: 50px;
height: 50px;
border-radius: 50%;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
z-index: 100;
}
/* ==========================================================================
HIGH-PERFORMANCE GAME MODE OVERRIDES
Disables heavy rendering effects while playing to ensure maximum FPS
========================================================================== */
.zone-item, #zoneViewer, body::before, body::after {
will-change: transform, opacity;
}
body.game-active::before,
body.game-active::after {
animation-play-state: paused !important;
display: none !important;
}
body.game-active .search-wrapper #searchBar,
body.game-active .btn-glass,
body.game-active .zone-item {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
body.game-active #container {
visibility: hidden;
}
body.game-active #modalOverlay {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
background: rgba(0, 0, 0, 0.85);
}
body.game-active #zoneViewer {
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
background: #11141a;
}
</style>
</head>
<body class="dark-mode">
<h1><i class="fa-solid fa-shapes"></i> gn-math</h1>
<div class="search-wrapper">
<i class="fa-solid fa-magnifying-glass"></i>
<input type="text" id="searchBar" placeholder="Search zones..." onkeyup="filterZones()">
</div>
<div id="topControls">
<div id="dataButtons">
<button class="btn-glass" onclick="saveData()">
<i class="fa-solid fa-download"></i> Save Data
</button>
<button class="btn-glass" onclick="document.getElementById('file').click()">
<i class="fa-solid fa-upload"></i> Load Data
</button>
<input type="file" id="file" style="display:none" onchange="loadData(event)" />
</div>
<div class="select-wrapper">
<select id="sortOptions" onchange="sortZones()">
<option value="name">Sort by Name</option>
<option value="id">Sort by ID</option>
<option value="popular">Sort by Popularity</option>
</select>
</div>
</div>
<div class="zone-wrapper">
<div id="zoneCount" class="zone-count"></div>
<div id="container">
<div class="loading-spinner">
<div class="spinner"></div>
<span>Fetching zones...</span>
</div>
</div>
</div>
<!-- Glass Modal Window -->
<div id="modalOverlay" onclick="closeZone()"></div>
<div id="zoneViewer">
<div id="zoneInfo">
<div id="zoneTitle">
<span id="zoneName"></span>
<span id="zoneId"></span>
</div>
<div id="controls">
<button class="btn-glass" onclick="fullscreenZone()"><i class="fa-solid fa-expand"></i> Fullscreen</button>
<button class="btn-glass" onclick="aboutBlank()"><i class="fa-solid fa-up-right-from-square"></i> about:blank</button>
<button class="btn-glass btn-accent" onclick="closeZone()"><i class="fa-solid fa-xmark"></i> Close</button>
</div>
</div>
<iframe id="zoneFrame"></iframe>
</div>
<button id="darkModeToggle" class="btn-glass">
<i class="fa-solid fa-moon"></i>
</button>
<script>
const container = document.getElementById('container');
const zoneViewer = document.getElementById('zoneViewer');
const modalOverlay = document.getElementById('modalOverlay');
let zoneFrame = document.getElementById('zoneFrame');
const searchBar = document.getElementById('searchBar');
const sortOptions = document.getElementById('sortOptions');
let zones = [];
let popularityData = {};
async function listZones() {
try {
const response = await fetch(zonesURL);
const json = await response.json();
zones = json;
await fetchPopularity();
sortZones();
const search = new URLSearchParams(window.location.search);
const id = search.get('id');
if (id) {
const zone = zones.find(zone => zone.id + '' == id + '');
if (zone) openZone(zone);
}
} catch (error) {
container.innerHTML = `<div class="loading-spinner"><i class="fa-solid fa-triangle-exclamation" style="font-size:2rem;color:#ef4444;"></i><span>Error loading zones: ${error.message}</span></div>`;
}
}
async function fetchPopularity() {
try {
const data = await response.json();
data.forEach(file => {
const idMatch = file.name.match(/\/(\d+)\.html$/);
if (idMatch) {
const id = parseInt(idMatch[1]);
popularityData[id] = file.hits.total;
}
});
} catch (error) {
popularityData[0] = 0;
}
}
function sortZones() {
const sortBy = sortOptions.value;
if (sortBy === 'name') {
zones.sort((a, b) => a.name.localeCompare(b.name));
} else if (sortBy === 'id') {
zones.sort((a, b) => a.id - b.id);
} else if (sortBy === 'popular') {
zones.sort((a, b) => (popularityData[b.id] || 0) - (popularityData[a.id] || 0));
}
zones.sort((a, b) => (a.id === -1 ? -1 : b.id === -1 ? 1 : 0));
displayZones(zones);
}
function displayZones(zones) {
container.innerHTML = "";
zones.forEach(file => {
const zoneItem = document.createElement("div");
zoneItem.className = "zone-item";
zoneItem.onclick = () => openZone(file);
const imgWrapper = document.createElement("div");
imgWrapper.className = "img-container";
const img = document.createElement("img");
img.loading = "lazy";
img.src = file.cover.replace("{COVER_URL}", coverURL).replace("{HTML_URL}", htmlURL);
imgWrapper.appendChild(img);
zoneItem.appendChild(imgWrapper);
const button = document.createElement("button");
button.textContent = file.name;
button.onclick = (event) => {
event.stopPropagation();
openZone(file);
};
zoneItem.appendChild(button);
container.appendChild(zoneItem);
});
if (zones.length === 0) {
container.innerHTML = `<div class="loading-spinner"><i class="fa-solid fa-magnifying-glass" style="font-size:2rem;"></i><span>No zones found.</span></div>`;
document.getElementById("zoneCount").textContent = ``;
} else {
document.getElementById("zoneCount").textContent = `Loaded ${zones.length} Zones`;
}
}
function filterZones() {
const query = searchBar.value.toLowerCase();
const filteredZones = zones.filter(zone => zone.name.toLowerCase().includes(query));
displayZones(filteredZones);
}
function openZone(file) {
if (file.url.startsWith("http")) {
window.location.href = file.url;
} else {
const url = file.url.replace("{COVER_URL}", coverURL).replace("{HTML_URL}", htmlURL);
fetch(url).then(response => response.text()).then(html => {
if (zoneFrame.contentDocument === null) {
zoneFrame = document.createElement("iframe");
zoneFrame.id = "zoneFrame";
zoneViewer.appendChild(zoneFrame);
}
zoneFrame.contentDocument.open();
zoneFrame.contentDocument.write(html);
zoneFrame.contentDocument.close();
document.getElementById('zoneName').textContent = file.name;
document.getElementById('zoneId').textContent = `#${file.id}`;
// Enable high performance game mode
document.body.classList.add('game-active');
modalOverlay.classList.add('active');
zoneViewer.classList.add('active');
}).catch(error => alert("Failed to load zone: " + error));
}
}
function aboutBlank() {
const newWindow = window.open("about:blank", "_blank");
const cleanId = document.getElementById('zoneId').textContent.replace('#', '');
let zone = zones.find(zone => zone.id + '' === cleanId).url.replace("{COVER_URL}", coverURL).replace("{HTML_URL}", htmlURL);
fetch(zone).then(response => response.text()).then(html => {
if (newWindow) {
newWindow.document.open();
newWindow.document.write(html);
newWindow.document.close();
}
})
}
function closeZone() {
zoneViewer.classList.remove('active');
modalOverlay.classList.remove('active');
// Disable high performance game mode & restore UI effects
document.body.classList.remove('game-active');
setTimeout(() => {
if (zoneFrame && zoneFrame.parentNode) {
zoneViewer.removeChild(zoneFrame);
}
}, 300);
}
function fullscreenZone() {
if (zoneFrame.requestFullscreen) {
zoneFrame.requestFullscreen();
} else if (zoneFrame.mozRequestFullScreen) {
zoneFrame.mozRequestFullScreen();
} else if (zoneFrame.webkitRequestFullscreen) {
zoneFrame.webkitRequestFullscreen();
} else if (zoneFrame.msRequestFullscreen) {
zoneFrame.msRequestFullscreen();
}
}
function saveData() {
let data = JSON.stringify(localStorage) + "\n\n|\n\n" + document.cookie;
const link = document.createElement("a");
link.href = URL.createObjectURL(new Blob([data], { type: "text/plain" }));
link.download = `${Date.now()}.data`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function loadData(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function (e) {
const content = e.target.result;
const [localStorageData, cookieData] = content.split("\n\n|\n\n");
try {
const parsedData = JSON.parse(localStorageData);
for (let key in parsedData) {
localStorage.setItem(key, parsedData[key]);
}
} catch (error) {
console.error(error);
}
if (cookieData) {
const cookies = cookieData.split("; ");
cookies.forEach(cookie => {
document.cookie = cookie;
});
}
alert("Data successfully loaded!");
};
reader.readAsText(file);
}
const darkModeToggle = document.getElementById('darkModeToggle');
darkModeToggle.addEventListener('click', () => {
document.body.classList.toggle('dark-mode');
const icon = darkModeToggle.querySelector('i');
if (document.body.classList.contains('dark-mode')) {
icon.className = 'fa-solid fa-moon';
} else {
icon.className = 'fa-solid fa-sun';
}
});
listZones();
</script>
</body>
</html>