WME Coordinate Extractor

Extrae coordenadas con clic derecho en el editor de Waze.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         WME Coordinate Extractor
// @name:en      WME Coordinate Extractor
// @namespace    http://tampermonkey.net/
// @version      1.2.1
// @description  Extrae coordenadas con clic derecho en el editor de Waze.
// @description:en Extracts coordinates with a right-click in the Waze editor.
// @author       edarague
// @match        https://www.waze.com/*editor*
// @run-at       document-end
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ─── Estilos ──────────────────────────────────────────────────────────────
    function injectStyles() {
        const s = document.createElement('style');
        s.id = 'wce-styles';
        s.textContent = `
            .waze-coord-final {
                position: fixed !important;
                background: #0a0a0a !important;
                color: #00ff00 !important;
                padding: 14px 18px !important;
                border-radius: 6px !important;
                font-family: 'Courier New', monospace !important;
                font-size: 13px !important;
                font-weight: bold !important;
                z-index: 999999 !important;
                border: 2px solid #00ff00 !important;
                min-width: 250px !important;
                box-shadow: 0 4px 20px rgba(0,255,0,0.2) !important;
            }
            .waze-coord-coords {
                margin-bottom: 10px !important;
                font-size: 14px !important;
            }
            .waze-coord-actions {
                display: flex !important;
                gap: 8px !important;
            }
            .waze-coord-btn {
                flex: 1 !important;
                padding: 6px 8px !important;
                border-radius: 4px !important;
                border: 1px solid #00ff00 !important;
                background: transparent !important;
                color: #00ff00 !important;
                font-family: 'Courier New', monospace !important;
                font-size: 11px !important;
                font-weight: bold !important;
                cursor: pointer !important;
                text-align: center !important;
                transition: background 0.15s !important;
                text-decoration: none !important;
                display: flex !important;
                align-items: center !important;
                justify-content: center !important;
                gap: 4px !important;
            }
            .waze-coord-btn:hover {
                background: #00ff00 !important;
                color: #0a0a0a !important;
            }
        `;
        document.head.appendChild(s);
    }

    // ─── Obtención de coordenadas ─────────────────────────────────────────────
    // Método primario: API oficial de Waze (W.map / OpenLayers)
    function getCoordsFromAPI(e) {
        try {
            const map = W.map.getOLMap ? W.map.getOLMap() : W.map;

            // Convertir posición del evento a píxeles del viewport del mapa
            const mapDiv = map.div || map.getDiv();
            const rect = mapDiv.getBoundingClientRect();
            const px = new OpenLayers.Pixel(
                e.clientX - rect.left,
                e.clientY - rect.top
            );

            // getLonLatFromPixel devuelve coordenadas en la proyección nativa del mapa (Mercator)
            const mercator = map.getLonLatFromPixel(px);

            // Transformar explícitamente desde EPSG:900913 a EPSG:4326
            const wgs84 = mercator.clone().transform(
                new OpenLayers.Projection('EPSG:900913'),
                new OpenLayers.Projection('EPSG:4326')
            );

            return {
                lat: wgs84.lat.toFixed(6),
                lng: wgs84.lon.toFixed(6)
            };
        } catch (_) {
            return null;
        }
    }

    // Método de respaldo: regex sobre el texto visible del DOM
    function getCoordsFromDOM() {
        try {
            const m = document.body.innerText.match(/(\d+\.\d{4,})\s+(-?\d+\.\d{4,})/);
            return m ? { lat: parseFloat(m[1]).toFixed(6), lng: parseFloat(m[2]).toFixed(6) } : null;
        } catch (_) {
            return null;
        }
    }

    function getCoords(e) {
        return getCoordsFromAPI(e) || getCoordsFromDOM();
    }

    // ─── Popup ────────────────────────────────────────────────────────────────
    function showPopup(c, x, y) {
        const old = document.querySelector('.waze-coord-final');
        if (old) old.remove();

        const mapsUrl = `https://maps.google.com/?q=${c.lat},${c.lng}`;
        const p = document.createElement('div');
        p.className = 'waze-coord-final';
        p.innerHTML = `
            <div class="waze-coord-coords">📍 ${c.lat} ${c.lng}</div>
            <div class="waze-coord-actions">
                <button class="waze-coord-btn" id="wce-copy">📋 Copiar</button>
                <a class="waze-coord-btn" href="${mapsUrl}" target="_blank" id="wce-maps">🗺️ Maps</a>
            </div>
        `;
        p.style.left = Math.min(x + 15, window.innerWidth - 240) + 'px';
        p.style.top  = Math.min(y + 15, window.innerHeight - 120) + 'px';
        document.body.appendChild(p);

        // Botón Copiar
        p.querySelector('#wce-copy').addEventListener('click', (e) => {
            e.stopPropagation();
            navigator.clipboard.writeText(`${c.lat}, ${c.lng}`);
            const btn = p.querySelector('#wce-copy');
            if (btn) btn.textContent = '✅ Copiado';
            removePopup(p);
        });

        // Botón Maps
        p.querySelector('#wce-maps').addEventListener('click', (e) => {
            e.stopPropagation();
            removePopup(p);
        });

        // Cierre al hacer clic fuera del popup (sustituye los setTimeout de limpieza)
        const outsideClickHandler = (ev) => {
            if (!p.contains(ev.target)) {
                removePopup(p, outsideClickHandler);
            }
        };
        // Delay mínimo para no capturar el mismo clic derecho que abrió el popup
        setTimeout(() => {
            document.addEventListener('click', outsideClickHandler, true);
        }, 100);
    }

    function removePopup(p, handler) {
        if (p && p.parentNode) p.remove();
        if (handler) document.removeEventListener('click', handler, true);
    }

    // ─── Inicialización ───────────────────────────────────────────────────────
    function init() {
        injectStyles();

        document.addEventListener('contextmenu', (e) => {
            const c = getCoords(e);
            if (c) {
                e.preventDefault();
                showPopup(c, e.clientX, e.clientY);
            }
        }, true);
    }

    // Esperar al evento wme-ready (estándar SDK moderno).
    // Si el evento nunca llega (editor muy viejo), se usa DOMContentLoaded como fallback.
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            document.addEventListener('wme-ready', init, { once: true });
            // Fallback: si wme-ready no dispara en 15 s, inicializar de todos modos
            setTimeout(() => {
                if (!document.getElementById('wce-styles')) init();
            }, 15000);
        });
    } else {
        document.addEventListener('wme-ready', init, { once: true });
        setTimeout(() => {
            if (!document.getElementById('wce-styles')) init();
        }, 15000);
    }

})();