WME Coordinate Extractor

Extrae coordenadas con clic derecho en el editor de Waze.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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);
    }

})();