WME Coordinate Extractor

Extrae coordenadas con clic derecho en el editor de Waze.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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

})();