Not a member of GistPad yet?
Sign Up,
it unlocks many cool features!
- <!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 -->
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
- <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');
- const zonesURL = "https://cdn.jsdelivr.net/gh/adatskov-wcpss/gn-assets@main/zones.json";
- const coverURL = "https://cdn.jsdelivr.net/gh/adatskov-wcpss/gn-covers@main";
- const htmlURL = "https://cdn.jsdelivr.net/gh/adatskov-wcpss/gn-html@main";
- 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 response = await fetch("https://data.jsdelivr.com/v1/stats/packages/gh/gn-math/html@main/files?period=year");
- 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>
RAW Paste Data
Copied
