Greasy Fork is available in English.
Adds a draggable panel to World Guessr that reads coordinates already present on the page, shows a live map preview, and allows placing/removing a pin.
// ==UserScript==
// @name World Guessr Cheat Panel [PATCHED] YOU WILL GET BANNED! ( I WILL TRY TO FIX IT )
// @namespace http://greasyfork.icu/users/your-user-id
// @version 1.0.0
// @description Adds a draggable panel to World Guessr that reads coordinates already present on the page, shows a live map preview, and allows placing/removing a pin.
// @author RandomAccount
// @license MIT (Chatgpt)
// @match https://www.worldguessr.com/
// @grant none
// @run-at document-idle
// ==/UserScript==
(() => {
"use strict";
let lastCoords = null;
let isOpen = false;
let isMinimized = false;
let isDragging = false;
let isResizing = false;
let offsetX = 0;
let offsetY = 0;
let savedHeight = 720;
let savedWidth = 420;
let pinnedLatLng = null;
let pinEnabled = false;
let activeTab = "menu";
document.querySelectorAll(".sgp-root, .sgp-style").forEach((el) => el.remove());
const root = document.createElement("div");
root.className = "sgp-root";
document.body.appendChild(root);
const style = document.createElement("style");
style.className = "sgp-style";
style.textContent = `
.sgp-root * {
box-sizing: border-box;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.sgp-fab {
position: fixed;
bottom: 22px;
left: 22px;
z-index: 100001;
display: flex;
align-items: center;
gap: 10px;
padding: 14px 18px;
border: 1px solid rgba(255,255,255,0.15);
border-radius: 18px;
background: linear-gradient(135deg, rgba(17,24,39,0.96), rgba(31,41,55,0.96));
color: #fff;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.2px;
cursor: pointer;
box-shadow: 0 18px 45px rgba(0,0,0,0.35);
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}
.sgp-fab:hover {
transform: translateY(-2px) scale(1.01);
box-shadow: 0 22px 52px rgba(0,0,0,0.42);
}
.sgp-fab-icon {
width: 28px;
height: 28px;
border-radius: 10px;
display: grid;
place-items: center;
background: linear-gradient(135deg, #34d399, #10b981);
color: #06281d;
font-size: 15px;
font-weight: 900;
box-shadow: 0 0 18px rgba(16,185,129,0.4);
flex-shrink: 0;
}
.sgp-overlay {
position: fixed;
inset: 0;
z-index: 100000;
background: rgba(2,6,23,0.12);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease;
}
.sgp-overlay.open {
opacity: 1;
pointer-events: auto;
}
.sgp-panel {
position: absolute;
top: 26px;
left: 26px;
width: 420px;
height: 720px;
display: flex;
flex-direction: column;
border-radius: 28px;
overflow: hidden;
background: #f8fafc;
border: 1px solid rgba(226,232,240,0.95);
box-shadow:
0 25px 70px rgba(15,23,42,0.25),
0 8px 24px rgba(15,23,42,0.12);
transform: translateX(-14px) scale(0.97);
opacity: 0;
transition: transform 0.28s ease, opacity 0.28s ease;
user-select: none;
}
.sgp-overlay.open .sgp-panel {
transform: translateX(0) scale(1);
opacity: 1;
}
.sgp-header {
height: 68px;
min-height: 68px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 14px 0 16px;
background: linear-gradient(135deg, #0f172a, #1e293b);
color: white;
border-bottom: 1px solid rgba(255,255,255,0.08);
cursor: move;
}
.sgp-header-left {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.sgp-logo {
width: 40px;
height: 40px;
border-radius: 14px;
display: grid;
place-items: center;
font-size: 18px;
background: linear-gradient(135deg, #34d399, #10b981);
color: #06281d;
box-shadow: 0 0 22px rgba(52,211,153,0.35);
flex-shrink: 0;
}
.sgp-title-group {
min-width: 0;
}
.sgp-title {
font-size: 15px;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.1;
}
.sgp-subtitle {
font-size: 11px;
opacity: 0.72;
margin-top: 4px;
letter-spacing: 0.2px;
}
.sgp-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.sgp-btn {
width: 34px;
height: 34px;
border: none;
border-radius: 12px;
background: rgba(255,255,255,0.10);
color: white;
cursor: pointer;
font-size: 15px;
font-weight: 800;
transition: all 0.18s ease;
}
.sgp-btn:hover {
background: rgba(255,255,255,0.20);
transform: scale(1.06);
}
.sgp-content {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 12px;
padding: 14px;
background: #f8fafc;
overflow: hidden;
}
.sgp-tabs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
background: rgba(255,255,255,0.7);
padding: 6px;
border-radius: 18px;
border: 1px solid rgba(226,232,240,0.95);
box-shadow: 0 10px 24px rgba(15,23,42,0.05);
flex-shrink: 0;
}
.sgp-tab-btn {
border: none;
border-radius: 14px;
padding: 11px 12px;
font-size: 12px;
font-weight: 800;
letter-spacing: 0.02em;
cursor: pointer;
background: transparent;
color: #475569;
transition: all 0.18s ease;
}
.sgp-tab-btn:hover {
background: rgba(241,245,249,0.95);
color: #0f172a;
}
.sgp-tab-btn.active {
background: linear-gradient(135deg, #0f172a, #1e293b);
color: white;
box-shadow: 0 10px 20px rgba(15,23,42,0.18);
}
.sgp-pages {
flex: 1;
min-height: 0;
position: relative;
overflow: hidden;
}
.sgp-page {
position: absolute;
inset: 0;
display: none;
flex-direction: column;
gap: 12px;
overflow-y: auto;
overflow-x: hidden;
padding-right: 4px;
min-height: 0;
}
.sgp-page.active {
display: flex;
}
.sgp-page::-webkit-scrollbar {
width: 8px;
}
.sgp-page::-webkit-scrollbar-thumb {
background: rgba(148,163,184,0.4);
border-radius: 999px;
}
.sgp-page::-webkit-scrollbar-track {
background: transparent;
}
.sgp-menu-card,
.sgp-map-card,
.sgp-info-card,
.sgp-credit-page-card {
background: #ffffff;
border: 1px solid rgba(226,232,240,0.95);
border-radius: 22px;
box-shadow: 0 10px 30px rgba(15,23,42,0.06);
}
.sgp-menu-card {
padding: 14px;
display: flex;
flex-direction: column;
gap: 14px;
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(248,250,252,0.98));
flex-shrink: 0;
}
.sgp-menu-section {
display: flex;
flex-direction: column;
gap: 10px;
}
.sgp-menu-section-title {
font-size: 11px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #64748b;
padding: 0 2px;
}
.sgp-menu-labels,
.sgp-menu-buttons {
display: grid;
gap: 10px;
}
.sgp-menu-labels {
grid-template-columns: repeat(3, 1fr);
}
.sgp-menu-buttons {
grid-template-columns: repeat(2, 1fr);
}
.sgp-menu-label-static {
position: relative;
padding: 14px 10px 12px;
border-radius: 18px;
background: linear-gradient(180deg, #ffffff, #f8fafc);
border: 1px solid rgba(226,232,240,0.95);
display: flex;
flex-direction: column;
align-items: center;
gap: 9px;
text-align: center;
cursor: default;
box-shadow: 0 8px 22px rgba(15,23,42,0.05);
}
.sgp-menu-label-static::after {
content: "";
position: absolute;
inset: 0;
border-radius: 18px;
pointer-events: none;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.75);
}
.sgp-menu-item {
position: relative;
padding: 14px 10px 12px;
border-radius: 18px;
background: linear-gradient(180deg, #ffffff, #f8fafc);
border: 1px solid rgba(226,232,240,0.95);
display: flex;
flex-direction: column;
align-items: center;
gap: 9px;
cursor: pointer;
text-align: center;
box-shadow: 0 10px 24px rgba(15,23,42,0.07);
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.sgp-menu-item:hover {
transform: translateY(-2px);
box-shadow: 0 14px 28px rgba(15,23,42,0.11);
border-color: rgba(148,163,184,0.45);
background: linear-gradient(180deg, #ffffff, #f1f5f9);
}
.sgp-menu-item:active {
transform: translateY(0);
}
.sgp-pin-btn {
background: linear-gradient(135deg, #ecfdf5, #d1fae5);
border: 1px solid rgba(16,185,129,0.22);
outline: none;
}
.sgp-pin-btn:hover {
background: linear-gradient(135deg, #f0fdf4, #bbf7d0);
border-color: rgba(16,185,129,0.32);
}
.sgp-remove-pin-btn {
background: linear-gradient(135deg, #fff7ed, #ffedd5);
border: 1px solid rgba(249,115,22,0.20);
outline: none;
}
.sgp-remove-pin-btn:hover {
background: linear-gradient(135deg, #fff7ed, #fed7aa);
border-color: rgba(249,115,22,0.32);
}
.sgp-menu-icon {
width: 42px;
height: 42px;
border-radius: 15px;
display: grid;
place-items: center;
background: linear-gradient(135deg, rgba(16,185,129,0.16), rgba(59,130,246,0.14));
font-size: 19px;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7), 0 6px 14px rgba(15,23,42,0.06);
}
.sgp-pin-btn .sgp-menu-icon {
background: linear-gradient(135deg, rgba(16,185,129,0.24), rgba(34,197,94,0.18));
}
.sgp-remove-pin-btn .sgp-menu-icon {
background: linear-gradient(135deg, rgba(249,115,22,0.20), rgba(251,146,60,0.16));
}
.sgp-menu-label {
font-size: 12px;
font-weight: 800;
color: #0f172a;
line-height: 1.25;
}
.sgp-menu-label-static .sgp-menu-label {
color: #334155;
}
.sgp-menu-hint {
font-size: 10px;
font-weight: 600;
color: #94a3b8;
line-height: 1.2;
}
.sgp-credit-page-card {
position: relative;
overflow: hidden;
padding: 22px;
min-height: 100%;
background:
radial-gradient(circle at top right, rgba(52,211,153,0.16), transparent 26%),
radial-gradient(circle at bottom left, rgba(59,130,246,0.14), transparent 28%),
linear-gradient(135deg, #ffffff, #f8fafc);
flex-shrink: 0;
}
.sgp-credit-page-card::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: linear-gradient(
120deg,
rgba(255,255,255,0.22),
transparent 35%,
transparent 65%,
rgba(255,255,255,0.10)
);
}
.sgp-credit-page-inner {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
gap: 18px;
}
.sgp-credit-hero {
text-align: center;
padding: 8px 6px 2px;
}
.sgp-credit-kicker {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 7px 12px;
border-radius: 999px;
font-size: 10px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #0f172a;
background: rgba(255,255,255,0.8);
border: 1px solid rgba(226,232,240,0.92);
box-shadow: 0 8px 18px rgba(15,23,42,0.05);
}
.sgp-credit-hero-title {
margin-top: 14px;
font-size: 24px;
font-weight: 900;
letter-spacing: -0.03em;
color: #0f172a;
}
.sgp-credit-hero-subtitle {
margin-top: 8px;
font-size: 13px;
line-height: 1.6;
color: #475569;
max-width: 300px;
margin-left: auto;
margin-right: auto;
}
.sgp-credit-showcase {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
}
.sgp-credit-profile {
display: flex;
align-items: center;
gap: 14px;
padding: 16px;
border-radius: 22px;
background: rgba(255,255,255,0.82);
border: 1px solid rgba(226,232,240,0.95);
box-shadow: 0 14px 30px rgba(15,23,42,0.07);
backdrop-filter: blur(8px);
}
.sgp-credit-profile-avatar {
width: 56px;
height: 56px;
border-radius: 18px;
display: grid;
place-items: center;
font-size: 26px;
flex-shrink: 0;
background: linear-gradient(135deg, rgba(16,185,129,0.18), rgba(59,130,246,0.16));
box-shadow: inset 0 1px 0 rgba(255,255,255,0.75), 0 10px 18px rgba(15,23,42,0.08);
}
.sgp-credit-profile-content {
min-width: 0;
}
.sgp-credit-profile-role {
font-size: 11px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #64748b;
margin-bottom: 5px;
}
.sgp-credit-profile-name {
font-size: 18px;
font-weight: 900;
color: #0f172a;
line-height: 1.2;
}
.sgp-credit-profile-desc {
margin-top: 6px;
font-size: 12px;
line-height: 1.5;
color: #475569;
}
.sgp-info-card {
padding: 12px 14px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-shrink: 0;
}
.sgp-info-left {
min-width: 0;
}
.sgp-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 999px;
background: rgba(15,23,42,0.92);
color: white;
font-size: 11px;
font-weight: 700;
margin-bottom: 8px;
}
.sgp-coords {
font-size: 13px;
font-weight: 700;
color: #0f172a;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 220px;
}
.sgp-meta {
font-size: 11px;
color: #475569;
margin-top: 4px;
}
.sgp-copy {
padding: 10px 12px;
border: none;
border-radius: 14px;
background: linear-gradient(135deg, #0f172a, #1e293b);
color: white;
font-size: 12px;
font-weight: 700;
cursor: pointer;
transition: transform 0.15s ease, opacity 0.15s ease;
white-space: nowrap;
}
.sgp-copy:hover {
transform: translateY(-1px);
opacity: 0.95;
}
.sgp-map-card {
position: relative;
min-height: 320px;
overflow: hidden;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.sgp-map-topbar {
height: 42px;
min-height: 42px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
border-bottom: 1px solid rgba(15,23,42,0.06);
background: #ffffff;
font-size: 12px;
font-weight: 700;
color: #0f172a;
}
.sgp-map-status {
color: #334155;
font-size: 11px;
font-weight: 600;
}
.sgp-iframe-wrap {
position: relative;
flex: 1;
min-height: 260px;
background: white;
}
.sgp-iframe {
width: 100%;
height: 100%;
border: none;
background: white;
display: block;
}
.sgp-floating-status {
position: absolute;
left: 12px;
bottom: 12px;
z-index: 2;
padding: 8px 10px;
border-radius: 14px;
background: rgba(15,23,42,0.78);
color: white;
font-size: 11px;
font-weight: 700;
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
max-width: calc(100% - 24px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
}
.sgp-game-pin {
position: absolute;
width: 26px;
height: 26px;
transform: translate(-50%, -100%);
z-index: 1000;
pointer-events: none;
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.35));
display: grid;
place-items: center;
font-size: 26px;
line-height: 1;
}
.sgp-resize {
position: absolute;
right: 0;
bottom: 0;
width: 24px;
height: 24px;
cursor: nwse-resize;
background:
linear-gradient(135deg, transparent 0%, transparent 46%, rgba(15,23,42,0.28) 47%, rgba(15,23,42,0.28) 54%, transparent 55%),
linear-gradient(135deg, transparent 0%, transparent 63%, rgba(15,23,42,0.42) 64%, rgba(15,23,42,0.42) 71%, transparent 72%),
linear-gradient(135deg, transparent 0%, transparent 80%, rgba(15,23,42,0.6) 81%, rgba(15,23,42,0.6) 88%, transparent 89%);
}
.sgp-dock {
position: fixed;
left: 22px;
bottom: 88px;
z-index: 100002;
display: none;
align-items: center;
gap: 10px;
padding: 10px 14px;
border-radius: 16px;
background: rgba(15,23,42,0.95);
color: white;
box-shadow: 0 14px 36px rgba(0,0,0,0.28);
cursor: pointer;
font-size: 13px;
font-weight: 700;
}
.sgp-dock.show {
display: flex;
}
`;
document.head.appendChild(style);
const fab = document.createElement("button");
fab.className = "sgp-fab";
fab.innerHTML = `
<span class="sgp-fab-icon">🗺</span>
<span>Open Secret Project</span>
`;
root.appendChild(fab);
const dock = document.createElement("div");
dock.className = "sgp-dock";
dock.innerHTML = `<span>🗺</span><span>Secret Project minimized</span>`;
root.appendChild(dock);
const overlay = document.createElement("div");
overlay.className = "sgp-overlay";
root.appendChild(overlay);
const panel = document.createElement("div");
panel.className = "sgp-panel";
panel.style.width = `${savedWidth}px`;
panel.style.height = `${savedHeight}px`;
overlay.appendChild(panel);
const header = document.createElement("div");
header.className = "sgp-header";
panel.appendChild(header);
const headerLeft = document.createElement("div");
headerLeft.className = "sgp-header-left";
header.appendChild(headerLeft);
const logo = document.createElement("div");
logo.className = "sgp-logo";
logo.textContent = "⌖";
headerLeft.appendChild(logo);
const titleGroup = document.createElement("div");
titleGroup.className = "sgp-title-group";
headerLeft.appendChild(titleGroup);
const title = document.createElement("div");
title.className = "sgp-title";
title.textContent = "Secret Project";
titleGroup.appendChild(title);
const subtitle = document.createElement("div");
subtitle.className = "sgp-subtitle";
subtitle.textContent = "Location tools • live map • draggable panel";
titleGroup.appendChild(subtitle);
const actions = document.createElement("div");
actions.className = "sgp-actions";
header.appendChild(actions);
const minimizeBtn = document.createElement("button");
minimizeBtn.className = "sgp-btn";
minimizeBtn.textContent = "–";
actions.appendChild(minimizeBtn);
const closeBtn = document.createElement("button");
closeBtn.className = "sgp-btn";
closeBtn.textContent = "✕";
actions.appendChild(closeBtn);
const content = document.createElement("div");
content.className = "sgp-content";
panel.appendChild(content);
const tabs = document.createElement("div");
tabs.className = "sgp-tabs";
tabs.innerHTML = `
<button class="sgp-tab-btn active" data-tab="menu" type="button">Menu</button>
<button class="sgp-tab-btn" data-tab="credits" type="button">Credits</button>
`;
content.appendChild(tabs);
const pages = document.createElement("div");
pages.className = "sgp-pages";
content.appendChild(pages);
const menuPage = document.createElement("div");
menuPage.className = "sgp-page active";
menuPage.style.overflowY = "auto";
menuPage.style.overflowX = "hidden";
pages.appendChild(menuPage);
const creditsPage = document.createElement("div");
creditsPage.className = "sgp-page";
creditsPage.style.overflowY = "auto";
creditsPage.style.overflowX = "hidden";
pages.appendChild(creditsPage);
const menuCard = document.createElement("div");
menuCard.className = "sgp-menu-card";
menuCard.innerHTML = `
<div class="sgp-menu-section">
<div class="sgp-menu-section-title">Overview</div>
<div class="sgp-menu-labels">
<div class="sgp-menu-label-static">
<div class="sgp-menu-icon">📍</div>
<div class="sgp-menu-label">Live Coordinates</div>
<div class="sgp-menu-hint">Auto-detected</div>
</div>
<div class="sgp-menu-label-static">
<div class="sgp-menu-icon">🛰</div>
<div class="sgp-menu-label">Map Viewer</div>
<div class="sgp-menu-hint">Preview panel</div>
</div>
<div class="sgp-menu-label-static">
<div class="sgp-menu-icon">🧭</div>
<div class="sgp-menu-label">Quick Position</div>
<div class="sgp-menu-hint">Current location</div>
</div>
</div>
</div>
<div class="sgp-menu-section">
<div class="sgp-menu-section-title">Actions</div>
<div class="sgp-menu-buttons">
<button class="sgp-menu-item sgp-pin-btn" type="button">
<div class="sgp-menu-icon">📌</div>
<div class="sgp-menu-label">Add Pin</div>
<div class="sgp-menu-hint">Place marker</div>
</button>
<button class="sgp-menu-item sgp-remove-pin-btn" type="button">
<div class="sgp-menu-icon">✖</div>
<div class="sgp-menu-label">Remove Pin</div>
<div class="sgp-menu-hint">Clear marker</div>
</button>
</div>
</div>
`;
menuPage.appendChild(menuCard);
const infoCard = document.createElement("div");
infoCard.className = "sgp-info-card";
infoCard.innerHTML = `
<div class="sgp-info-left">
<div class="sgp-badge">● Tracking page data</div>
<div class="sgp-coords">Waiting for coordinates...</div>
<div class="sgp-meta">Auto-refresh every 2 seconds</div>
</div>
`;
menuPage.appendChild(infoCard);
const copyBtn = document.createElement("button");
copyBtn.className = "sgp-copy";
copyBtn.textContent = "Copy";
infoCard.appendChild(copyBtn);
const mapCard = document.createElement("div");
mapCard.className = "sgp-map-card";
menuPage.appendChild(mapCard);
const mapTopbar = document.createElement("div");
mapTopbar.className = "sgp-map-topbar";
mapTopbar.innerHTML = `
<span>Google Maps preview</span>
<span class="sgp-map-status">No location detected yet</span>
`;
mapCard.appendChild(mapTopbar);
const iframeWrap = document.createElement("div");
iframeWrap.className = "sgp-iframe-wrap";
mapCard.appendChild(iframeWrap);
const iframe = document.createElement("iframe");
iframe.className = "sgp-iframe";
iframe.loading = "lazy";
iframe.referrerPolicy = "no-referrer-when-downgrade";
iframe.allowFullscreen = true;
iframe.src = "about:blank";
iframeWrap.appendChild(iframe);
const floatingStatus = document.createElement("div");
floatingStatus.className = "sgp-floating-status";
floatingStatus.textContent = "Waiting for coordinates...";
iframeWrap.appendChild(floatingStatus);
const creditPageCard = document.createElement("div");
creditPageCard.className = "sgp-credit-page-card";
creditPageCard.innerHTML = `
<div class="sgp-credit-page-inner">
<div class="sgp-credit-hero">
<div class="sgp-credit-kicker">✨ Secret Project Credits</div>
<div class="sgp-credit-hero-title">Built with vision</div>
<div class="sgp-credit-hero-subtitle">
A dedicated page honoring the minds behind the experience.
</div>
</div>
<div class="sgp-credit-showcase">
<div class="sgp-credit-profile">
<div class="sgp-credit-profile-avatar">👑</div>
<div class="sgp-credit-profile-content">
<div class="sgp-credit-profile-role">Founder</div>
<div class="sgp-credit-profile-name">RandomAccount</div>
<div class="sgp-credit-profile-desc">
Visionary behind the concept, direction, and identity of Secret Project.
</div>
</div>
</div>
<div class="sgp-credit-profile">
<div class="sgp-credit-profile-avatar">🤖</div>
<div class="sgp-credit-profile-content">
<div class="sgp-credit-profile-role">Developer</div>
<div class="sgp-credit-profile-name">ChatGPT</div>
<div class="sgp-credit-profile-desc">
Designed and engineered the interface, logic, layout, and interactive experience.
</div>
</div>
</div>
</div>
</div>
`;
creditsPage.appendChild(creditPageCard);
const coordsEl = infoCard.querySelector(".sgp-coords");
const pinBtn = menuCard.querySelector(".sgp-pin-btn");
const removePinBtn = menuCard.querySelector(".sgp-remove-pin-btn");
const mapStatus = mapTopbar.querySelector(".sgp-map-status");
const tabButtons = tabs.querySelectorAll(".sgp-tab-btn");
function setActiveTab(tabName) {
activeTab = tabName;
tabButtons.forEach((btn) => {
btn.classList.toggle("active", btn.dataset.tab === tabName);
});
menuPage.classList.toggle("active", tabName === "menu");
creditsPage.classList.toggle("active", tabName === "credits");
}
tabButtons.forEach((btn) => {
btn.addEventListener("click", (event) => {
event.stopPropagation();
setActiveTab(btn.dataset.tab);
});
});
const resizeHandle = document.createElement("div");
resizeHandle.className = "sgp-resize";
panel.appendChild(resizeHandle);
function openWindow() {
overlay.classList.add("open");
fab.innerHTML = `<span class="sgp-fab-icon">✕</span><span>Close Secret Project</span>`;
dock.classList.remove("show");
isOpen = true;
}
function closeWindow() {
overlay.classList.remove("open");
fab.innerHTML = `<span class="sgp-fab-icon">🗺</span><span>Open Secret Project</span>`;
dock.classList.remove("show");
isOpen = false;
isMinimized = false;
content.style.display = "flex";
panel.style.height = `${savedHeight}px`;
panel.style.width = `${savedWidth}px`;
minimizeBtn.textContent = "–";
resizeHandle.style.display = "block";
}
function minimizeWindow() {
if (!isMinimized) {
savedHeight = panel.offsetHeight;
savedWidth = panel.offsetWidth;
content.style.display = "none";
resizeHandle.style.display = "none";
panel.style.height = "68px";
panel.style.width = "320px";
minimizeBtn.textContent = "▢";
dock.classList.add("show");
} else {
content.style.display = "flex";
resizeHandle.style.display = "block";
panel.style.height = `${savedHeight}px`;
panel.style.width = `${savedWidth}px`;
minimizeBtn.textContent = "–";
dock.classList.remove("show");
}
isMinimized = !isMinimized;
}
fab.addEventListener("click", () => {
if (isOpen) {
closeWindow();
} else {
openWindow();
}
});
closeBtn.addEventListener("click", (event) => {
event.stopPropagation();
closeWindow();
});
minimizeBtn.addEventListener("click", (event) => {
event.stopPropagation();
minimizeWindow();
});
dock.addEventListener("click", () => {
if (isOpen && isMinimized) {
minimizeWindow();
}
});
overlay.addEventListener("mousedown", (event) => {
if (event.target === overlay) {
closeWindow();
}
});
header.addEventListener("mousedown", (event) => {
if (event.target === minimizeBtn || event.target === closeBtn) {
return;
}
isDragging = true;
const rect = panel.getBoundingClientRect();
offsetX = event.clientX - rect.left;
offsetY = event.clientY - rect.top;
});
resizeHandle.addEventListener("mousedown", (event) => {
event.stopPropagation();
isResizing = true;
});
document.addEventListener("mousemove", (event) => {
if (isDragging) {
const maxLeft = window.innerWidth - panel.offsetWidth - 10;
const maxTop = window.innerHeight - panel.offsetHeight - 10;
const newLeft = Math.min(Math.max(10, event.clientX - offsetX), Math.max(10, maxLeft));
const newTop = Math.min(Math.max(10, event.clientY - offsetY), Math.max(10, maxTop));
panel.style.left = `${newLeft}px`;
panel.style.top = `${newTop}px`;
}
if (isResizing && !isMinimized) {
const rect = panel.getBoundingClientRect();
const newWidth = Math.max(event.clientX - rect.left, 340);
const newHeight = Math.max(event.clientY - rect.top, 360);
panel.style.width = `${newWidth}px`;
panel.style.height = `${newHeight}px`;
savedWidth = newWidth;
savedHeight = newHeight;
}
});
document.addEventListener("mouseup", () => {
isDragging = false;
isResizing = false;
});
copyBtn.addEventListener("click", async () => {
if (!lastCoords) {
return;
}
try {
await navigator.clipboard.writeText(lastCoords);
copyBtn.textContent = "Copied";
setTimeout(() => {
copyBtn.textContent = "Copy";
}, 1200);
} catch (error) {
copyBtn.textContent = "Failed";
setTimeout(() => {
copyBtn.textContent = "Copy";
}, 1200);
}
});
pinBtn.addEventListener("click", (event) => {
event.stopPropagation();
const latLng = getCurrentLatLng();
if (!latLng) {
floatingStatus.textContent = "No coordinates available yet";
mapStatus.textContent = "Pin failed";
return;
}
pinEnabled = true;
pinnedLatLng = latLng;
refreshGamePin();
});
removePinBtn.addEventListener("click", (event) => {
event.stopPropagation();
pinEnabled = false;
pinnedLatLng = null;
document.querySelectorAll(".sgp-game-pin").forEach((pin) => pin.remove());
mapStatus.textContent = "Pin removed";
floatingStatus.textContent = lastCoords
? `Lat ${lastCoords.split(",")[0]} • Lng ${lastCoords.split(",")[1]}`
: "Waiting for coordinates...";
});
function extractCoords() {
const html = document.documentElement.innerHTML;
const patterns = [
/(-?\d{1,3}\.\d+)\s*,\s*(-?\d{1,3}\.\d+)/,
/"lat"\s*:\s*(-?\d{1,3}\.\d+).*?"lng"\s*:\s*(-?\d{1,3}\.\d+)/i,
/"latitude"\s*:\s*(-?\d{1,3}\.\d+).*?"longitude"\s*:\s*(-?\d{1,3}\.\d+)/i
];
for (const pattern of patterns) {
const match = html.match(pattern);
if (match) {
const lat = parseFloat(match[1]);
const lng = parseFloat(match[2]);
if (
!Number.isNaN(lat) &&
lat >= -90 &&
lat <= 90 &&
!Number.isNaN(lng) &&
lng >= -180 &&
lng <= 180
) {
return `${lat},${lng}`;
}
}
}
return null;
}
function getCurrentLatLng() {
if (!lastCoords) {
return null;
}
const [latStr, lngStr] = lastCoords.split(",");
const lat = parseFloat(latStr);
const lng = parseFloat(lngStr);
if (Number.isNaN(lat) || Number.isNaN(lng)) {
return null;
}
return { lat, lng };
}
function getOrCreateGamePin(container) {
let pin = container.querySelector(".sgp-game-pin");
if (!pin) {
pin = document.createElement("div");
pin.className = "sgp-game-pin";
pin.textContent = "📍";
container.appendChild(pin);
}
return pin;
}
function parseTranslateAndScale(transformValue) {
if (!transformValue || transformValue === "none") {
return { x: 0, y: 0, scale: 1 };
}
const match3d = transformValue.match(
/translate3d\(([-\d.]+)px,\s*([-\d.]+)px,\s*[-\d.]+px\)(?:\s*scale\(([-\d.]+)\))?/
);
if (match3d) {
return {
x: parseFloat(match3d[1]) || 0,
y: parseFloat(match3d[2]) || 0,
scale: parseFloat(match3d[3]) || 1
};
}
const match2d = transformValue.match(
/translate\(([-\d.]+)px,\s*([-\d.]+)px\)(?:\s*scale\(([-\d.]+)\))?/
);
if (match2d) {
return {
x: parseFloat(match2d[1]) || 0,
y: parseFloat(match2d[2]) || 0,
scale: parseFloat(match2d[3]) || 1
};
}
return { x: 0, y: 0, scale: 1 };
}
function latLngToWorldPixel(lat, lng, zoom) {
const tileSize = 256;
const scale = tileSize * Math.pow(2, zoom);
const x = ((lng + 180) / 360) * scale;
const sinLat = Math.sin((lat * Math.PI) / 180);
const clamped = Math.min(Math.max(sinLat, -0.9999), 0.9999);
const y = (0.5 - Math.log((1 + clamped) / (1 - clamped)) / (4 * Math.PI)) * scale;
return { x, y };
}
function isLeafletMapCandidate(obj, container) {
return Boolean(
obj &&
typeof obj === "object" &&
typeof obj.latLngToContainerPoint === "function" &&
typeof obj.containerPointToLatLng === "function" &&
typeof obj.getZoom === "function" &&
obj._container === container
);
}
function findLeafletMapNearContainer(container) {
if (!container) {
return null;
}
const seen = new WeakSet();
const roots = [container, container.parentElement, container.closest("#miniMapArea"), window];
function scan(obj, depth) {
if (!obj || typeof obj !== "object" || seen.has(obj) || depth < 0) {
return null;
}
seen.add(obj);
if (isLeafletMapCandidate(obj, container)) {
return obj;
}
let keys = [];
try {
keys = Object.getOwnPropertyNames(obj);
} catch (error) {
return null;
}
for (const key of keys) {
if (key === "parentNode" || key === "children" || key === "childNodes") {
continue;
}
let value;
try {
value = obj[key];
} catch (error) {
continue;
}
if (!value || typeof value !== "object") {
continue;
}
if (isLeafletMapCandidate(value, container)) {
return value;
}
const found = scan(value, depth - 1);
if (found) {
return found;
}
}
return null;
}
for (const rootNode of roots) {
const found = scan(rootNode, rootNode === window ? 2 : 4);
if (found) {
return found;
}
}
return null;
}
function getDomProjectionContext() {
const mapArea = document.querySelector("#miniMapArea");
const container = mapArea?.querySelector(".leaflet-container");
const mapPane = mapArea?.querySelector(".leaflet-map-pane");
const tileContainer = mapArea?.querySelector(".leaflet-tile-container");
if (!mapArea || !container || !mapPane || !tileContainer) {
return null;
}
const tiles = Array.from(mapArea.querySelectorAll(".leaflet-tile-pane .leaflet-tile-loaded"));
if (!tiles.length) {
return null;
}
let bestTile = null;
let bestScore = Infinity;
for (const tile of tiles) {
const src = tile.getAttribute("src") || "";
const xMatch = src.match(/[?&]x=(-?\d+)/);
const yMatch = src.match(/[?&]y=(-?\d+)/);
const zMatch = src.match(/[?&]z=(\d+)/);
if (!xMatch || !yMatch || !zMatch) {
continue;
}
const tileTransform = parseTranslateAndScale(tile.style.transform);
const containerTransform = parseTranslateAndScale(tileContainer.style.transform);
const score = Math.abs((containerTransform.scale || 1) - 1);
if (score < bestScore) {
bestScore = score;
bestTile = {
tileX: parseInt(xMatch[1], 10),
tileY: parseInt(yMatch[1], 10),
zoom: parseInt(zMatch[1], 10),
tileTransform,
containerTransform
};
}
}
if (!bestTile) {
return null;
}
const mapPaneTransform = parseTranslateAndScale(mapPane.style.transform);
return {
container,
zoom: bestTile.zoom,
tileX: bestTile.tileX,
tileY: bestTile.tileY,
tileXOffset: bestTile.tileTransform.x,
tileYOffset: bestTile.tileTransform.y,
tileContainerX: bestTile.containerTransform.x,
tileContainerY: bestTile.containerTransform.y,
tileScale: bestTile.containerTransform.scale || 1,
mapPaneX: mapPaneTransform.x,
mapPaneY: mapPaneTransform.y
};
}
function projectWithDomFallback(latLng) {
const ctx = getDomProjectionContext();
if (!ctx) {
return null;
}
const world = latLngToWorldPixel(latLng.lat, latLng.lng, ctx.zoom);
const tileWorldX = ctx.tileX * 256;
const tileWorldY = ctx.tileY * 256;
const pixelX =
ctx.mapPaneX +
ctx.tileContainerX +
ctx.tileXOffset +
(world.x - tileWorldX) * ctx.tileScale;
const pixelY =
ctx.mapPaneY +
ctx.tileContainerY +
ctx.tileYOffset +
(world.y - tileWorldY) * ctx.tileScale;
return {
container: ctx.container,
x: pixelX,
y: pixelY
};
}
function refreshGamePin() {
if (!pinEnabled || !pinnedLatLng) {
return;
}
const container = document.querySelector("#miniMapArea .leaflet-container");
if (!container) {
mapStatus.textContent = "Pin failed";
floatingStatus.textContent = "Game map not found";
return;
}
const leafletMap = findLeafletMapNearContainer(container);
if (leafletMap) {
try {
const point = leafletMap.latLngToContainerPoint([pinnedLatLng.lat, pinnedLatLng.lng]);
const pin = getOrCreateGamePin(container);
pin.style.left = `${point.x}px`;
pin.style.top = `${point.y}px`;
const inView =
point.x >= 0 &&
point.y >= 0 &&
point.x <= container.clientWidth &&
point.y <= container.clientHeight;
mapStatus.textContent = inView ? "Pin fixed" : "Pin off-screen";
floatingStatus.textContent = inView
? `Pin fixed on map • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`
: `Pin is off-screen • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`;
return;
} catch (error) {
// Fall back to DOM projection below.
}
}
const projected = projectWithDomFallback(pinnedLatLng);
if (!projected) {
mapStatus.textContent = "Pin failed";
floatingStatus.textContent = "Could not project pin";
return;
}
const pin = getOrCreateGamePin(projected.container);
pin.style.left = `${projected.x}px`;
pin.style.top = `${projected.y}px`;
const inView =
projected.x >= 0 &&
projected.y >= 0 &&
projected.x <= projected.container.clientWidth &&
projected.y <= projected.container.clientHeight;
mapStatus.textContent = inView ? "Pin tracking" : "Pin off-screen";
floatingStatus.textContent = inView
? `Pin tracking map • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`
: `Pin is off-screen • ${pinnedLatLng.lat}, ${pinnedLatLng.lng}`;
}
function updatePreviewIframe(lat, lng) {
const url = `https://www.google.com/maps?q=${encodeURIComponent(`${lat},${lng}`)}&z=6&output=embed`;
if (iframe.src !== url) {
iframe.src = url;
}
}
function updateMap() {
const coords = extractCoords();
if (!coords) {
refreshGamePin();
return;
}
if (coords !== lastCoords) {
const [lat, lng] = coords.split(",");
lastCoords = coords;
coordsEl.textContent = `${lat}, ${lng}`;
floatingStatus.textContent = `Lat ${lat} • Lng ${lng}`;
mapStatus.textContent = "Location detected";
updatePreviewIframe(lat, lng);
if (pinEnabled) {
pinnedLatLng = {
lat: parseFloat(lat),
lng: parseFloat(lng)
};
}
}
refreshGamePin();
}
setInterval(updateMap, 50);
})();