Geoguessr Spaceplonking

Prevent zooming in on the map in GeoGuessr.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Geoguessr Spaceplonking
// @namespace    https://greasyfork.org/en/users/your-name
// @version      1.0.0
// @description  Prevent zooming in on the map in GeoGuessr.
// @author       Your Name
// @license      MIT
// @match        https://www.geoguessr.com/*
// @run-at       document-end
// @grant        none
// @noframes
// @compatible   chrome Latest
// @compatible   firefox Latest
// @compatible   edge Latest
// ==/UserScript==

(function () {
  'use strict';

  // Robust selector for hashed class names used by GeoGuessr for the small guess map.
  // Example: <div class="game_guessMap__8jK3B">...</div>
  var MAP_SELECTOR = '[class^="game_guessMap__"], [class*=" game_guessMap__"]';

  // Utility: check if an event target is inside the guess map (supports Shadow DOM).
  function isInsideGuessMap(target) {
    while (target && target !== document && target !== window) {
      if (target.matches && target.matches(MAP_SELECTOR)) return true;
      // If inside a shadow root, hop to its host; otherwise walk up the DOM.
      target = target.parentNode || target.host;
    }
    return false;
  }

  // Block scroll-wheel/trackpad zoom when the pointer is over the guess map.
  function wheelBlocker(e) {
    if (isInsideGuessMap(e.target)) {
      e.preventDefault();
      // Stop immediately so the map library never sees the event.
      if (e.stopImmediatePropagation) e.stopImmediatePropagation();
      return false;
    }
  }

  // Block double-click zoom on the guess map (does not affect single-click to place pin).
  function dblClickBlocker(e) {
    if (isInsideGuessMap(e.target)) {
      e.preventDefault();
      if (e.stopImmediatePropagation) e.stopImmediatePropagation();
      return false;
    }
  }

  // Attach capture-phase, non-passive listeners so preventDefault works before the app handles events.
  var opts = { capture: true, passive: false };

  // Wheel/scroll zoom blockers (modern + legacy event names).
  window.addEventListener('wheel', wheelBlocker, opts);
  document.addEventListener('wheel', wheelBlocker, opts);
  window.addEventListener('mousewheel', wheelBlocker, opts);      // legacy
  document.addEventListener('mousewheel', wheelBlocker, opts);
  window.addEventListener('DOMMouseScroll', wheelBlocker, opts);  // Firefox legacy
  document.addEventListener('DOMMouseScroll', wheelBlocker, opts);

  // Double-click zoom blocker.
  window.addEventListener('dblclick', dblClickBlocker, opts);
  document.addEventListener('dblclick', dblClickBlocker, opts);

  // SPA safety: if GeoGuessr re-renders or navigates between rounds, these global listeners still apply.
  // We keep a very light MutationObserver just in case the document root is hot-swapped.
  if (window.MutationObserver) {
    var observer = new MutationObserver(function () {
      // No re-attachment needed; listeners are already global.
      // This observer simply ensures the script stays active across heavy DOM changes.
    });
    observer.observe(document.documentElement, { childList: true, subtree: true });
  }

  // --- Changelog ---
  // 1.0.0: Initial Greasy Fork release. Blocks wheel/trackpad zoom and double-click zoom over the guess map using capture-phase, non-passive listeners.
})();