GeoGuessr Random Map

Adds a button on the Community Maps page (and a menu command) that redirects to a Random Map.

// ==UserScript==
// @name         GeoGuessr Random Map
// @namespace    https://github.com/asmodeo
// @icon         https://parmageo.vercel.app/gg.ico
// @version      1.1
// @description  Adds a button on the Community Maps page (and a menu command) that redirects to a Random Map.
// @author       Parma
// @match        https://www.geoguessr.com/*
// @grant        GM_registerMenuCommand
// @connect      www.geoguessr.com
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    let currentPath = window.location.pathname;

    // Fetch random map URL
    async function getRandomMapUrl() {
        try {
            const response = await fetch('https://www.geoguessr.com/api/v3/social/maps/browse/random');
            if (!response.ok) throw new Error(`HTTP ${response.status}`);
            const map = await response.json();
            return 'https://www.geoguessr.com' + map.url.trim();
        } catch (error) {
            console.error('Random Map error:', error);
            alert('Failed to load a random map.');
            return null;
        }
    }

    // Redirect to map URL
    function redirectToMap(mapUrl) {
        if (mapUrl) {
            window.location.href = mapUrl;
        }
    }

    // Menu command
    GM_registerMenuCommand('Random Map', async function() {
        const mapUrl = await getRandomMapUrl();
        if (mapUrl) redirectToMap(mapUrl);
    });

    // Link creation
    function createStyledLink() {
        const link = document.createElement('a');
        link.id = 'geoguessr-random-map-btn';
        link.className = 'button_button__aR6_e button_link__LWagc button_variantPurple__qyK6c';
        link.title = 'Load a random community map';
        link.style.minWidth = '140px';
        link.style.textDecoration = 'none';
        link.style.display = 'flex';
        link.style.alignItems = 'center';
        link.style.justifyContent = 'center';
        link.innerHTML = '<span class="button_label__2JvUx" style="line-height: 1;">Random Map</span>';        
        link.href = '#';
        link.target = '_blank';

        // Handle all clicks - always fetch a new random map
        link.addEventListener('click', async function(e) {
            e.preventDefault();

            const mapUrl = await getRandomMapUrl();
            if (!mapUrl) return;

            // For left-click without modifiers, open in current tab
            if (e.button === 0 && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
                redirectToMap(mapUrl);
            }
            // For middle-click and other modifiers, use window.open which works reliably
            else {
                window.open(mapUrl, '_blank');
            }
        });

        // Handle middle-clicks specifically
        link.addEventListener('auxclick', async function(e) {
            if (e.button === 1) { // Middle click
                e.preventDefault();
                const mapUrl = await getRandomMapUrl();
                if (mapUrl) {
                    window.open(mapUrl, '_blank');
                }
            }
        });

        return link;
    }

    // Mounting logic
    function isCommunityMapsPage() {
        return window.location.pathname === '/maps/community';
    }

    function mountButton() {
        if (!isCommunityMapsPage()) return;

        const header = document.querySelector('.page-title_header__AHcKq.page-title_collapseOnMobile__n_EP_');
        if (!header) return;

        // Avoid duplicates
        if (header.querySelector(`#geoguessr-random-map-btn`)) return;

        const categoryMenu = header.querySelector('.tag-nav_categoryMenu__AUXWy');
        if (!categoryMenu) return;

        const link = createStyledLink();
        categoryMenu.parentNode.insertBefore(link, categoryMenu.nextSibling);
    }

    function unmountButton() {
        const btn = document.getElementById('geoguessr-random-map-btn');
        if (btn) btn.remove();
    }

    // Navigation handling
    function handleNavigation() {
        const newPath = window.location.pathname;
        if (newPath !== currentPath) {
            currentPath = newPath;
            if (!isCommunityMapsPage()) {
                unmountButton();
            }
        }
        if (isCommunityMapsPage()) {
            mountButton();
        }
    }

    const observer = new MutationObserver(handleNavigation);
    observer.observe(document, { subtree: true, childList: true });
    handleNavigation();
    setInterval(handleNavigation, 800);
})();