WME - UR Manager

Ultimate UR Management Toolkit with zoom refresh and panel update

目前為 2025-04-28 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         WME - UR Manager
// @namespace    http://waze.com/
// @version      2025.04.28.13
// @description  Ultimate UR Management Toolkit with zoom refresh and panel update
// @author       Crotalo
// @match        https://www.waze.com/*/editor*
// @match        https://beta.waze.com/*/editor*
// @grant        GM_addStyle
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        MENSAJE_RESPUESTA: "¡Hola, Wazer! Gracias por tu reporte. Para resolverlo de forma efectiva, necesitamos un poco más de detalle sobre lo sucedido. Quedamos atentos a tu respuesta.",
        MENSAJE_CIERRE: "¡¡Hola Wazer! Buen día, Lamentablemente no pudimos solucionar el error en esta ocasión. Por favor, déjanos más datos la próxima vez. Gracias por reportar.",
        MENSAJE_RESUELTA: "¡Hola Wazer! Buen día, el problema fue solucionado y se verá reflejado en la aplicación en la próxima actualización del mapa, esta tomará entre 3 y 5 días. ¡Gracias por reportar!!",
        DEBUG: true,
        BOTON_ID: 'urna-btn-fecha-exacta',
        PANEL_ID: 'urna-panel-fecha-exacta',
        INTERVALO_VERIFICACION: 5000,
        UMBRAL_VIEJO: 7,
        UMBRAL_RECIENTE: 3,
        RETRASO_ENTRE_ACCIONES: 800,
        RETRASO_ESPERA_UI: 1000,
        MAX_REINTENTOS: 3,
        ZOOM_ACTUALIZACION: 13
    };

    GM_addStyle(`
        #${CONFIG.BOTON_ID} {
            position: fixed !important;
            bottom: 20px !important;
            left: 20px !important;
            z-index: 99999 !important;
            padding: 10px 15px !important;
            background: #3498db !important;
            color: white !important;
            font-weight: bold !important;
            border: none !important;
            border-radius: 5px !important;
            cursor: pointer !important;
            font-family: Arial, sans-serif !important;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2) !important;
        }
        #${CONFIG.PANEL_ID} {
            position: fixed;
            top: 80px;
            right: 20px;
            width: 500px;
            max-height: 70vh;
            min-height: 200px;
            display: flex;
            flex-direction: column;
            background: white;
            border: 2px solid #999;
            z-index: 99998;
            font-family: Arial, sans-serif;
            font-size: 13px;
            box-shadow: 2px 2px 15px rgba(0,0,0,0.3);
            border-radius: 5px;
            display: none;
        }
        #${CONFIG.PANEL_ID} .panel-content {
            flex: 1;
            overflow-y: auto;
            padding: 15px;
            max-height: calc(70vh - 60px);
        }
        #${CONFIG.PANEL_ID} table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 10px;
        }
        #${CONFIG.PANEL_ID} th {
            position: sticky;
            top: 0;
            background-color: #f2f2f2;
            z-index: 10;
        }
        #${CONFIG.PANEL_ID} th, #${CONFIG.PANEL_ID} td {
            border: 1px solid #ddd;
            padding: 6px;
            text-align: left;
        }
        .ur-old { color: #d9534f; font-weight: bold; }
        .ur-recent { color: #5bc0de; }
        .ur-new { color: #5cb85c; }
        .ur-visitada { background-color: #fdf5d4 !important; }
        .ur-no-fecha { color: #777; font-style: italic; }
        .btn-centrar {
            padding: 4px 8px;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
        .panel-footer {
            padding: 10px 15px;
            background: #f8f8f8;
            border-top: 1px solid #eee;
            display: flex;
            justify-content: center;
            gap: 10px;
            position: sticky;
            bottom: 0;
            z-index: 20;
            height: 60px;
        }
        .btn-global {
            padding: 8px 15px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-weight: bold;
            white-space: nowrap;
        }
        .btn-responder {
            background: #f0ad4e;
            color: white;
        }
        .btn-cerrar {
            background: #5cb85c;
            color: white;
        }
        .btn-resuelta {
            background: #5bc0de;
            color: white;
        }
        .btn-reiniciar {
            background: #d9534f;
            color: white;
        }
    `);

    let estado = {
        URsActuales: [],
        panelVisible: false,
        botonUR: null,
        intervaloVerificacion: null,
        timeouts: [],
        accionEnProgreso: false,
        reintentos: 0,
        urVisitadas: [],
        urCentradas: [],
        bloqueado: false
    };

    function debugLog(message) {
        if (CONFIG.DEBUG) console.log('[UR Script] ' + message);
    }

    function limpiarTimeouts() {
        estado.timeouts.forEach(timeout => clearTimeout(timeout));
        estado.timeouts = [];
    }

    function agregarTimeout(callback, delay) {
        const timeoutId = setTimeout(() => {
            callback();
            estado.timeouts = estado.timeouts.filter(id => id !== timeoutId);
        }, delay);
        estado.timeouts.push(timeoutId);
        return timeoutId;
    }

    function resetearEstado() {
        estado.accionEnProgreso = false;
        estado.bloqueado = false;
        estado.reintentos = 0;
        limpiarTimeouts();
        debugLog('Estado del script reiniciado');
    }

    function togglePanelURs() {
        if (estado.panelVisible) {
            $(`#${CONFIG.PANEL_ID}`).fadeOut(300, function() {
                $(this).remove();
            });
            estado.panelVisible = false;
            limpiarTimeouts();
        } else {
            mostrarPanelURs();
        }
    }

    function crearBoton() {
        if ($(`#${CONFIG.BOTON_ID}`).length > 0) return;

        debugLog('Creando botón...');
        estado.botonUR = $(`<button id="${CONFIG.BOTON_ID}">📝 UR Manager</button>`)
            .appendTo('body')
            .on('click', togglePanelURs);

        debugLog('Botón creado exitosamente');
    }

    function parsearFecha(valor) {
        if (!valor) return null;

        if (typeof valor === 'object' && '_seconds' in valor) {
            try {
                return new Date(valor._seconds * 1000 + (valor._nanoseconds / 1000000));
            } catch (e) {
                debugLog(`Error parseando Firebase Timestamp: ${JSON.stringify(valor)}`);
            }
        }

        if (typeof valor === 'string' && valor.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
            try {
                return new Date(valor);
            } catch (e) {
                debugLog(`Error parseando fecha ISO: ${valor}`);
            }
        }

        if (/^\d+$/.test(valor)) {
            try {
                const num = parseInt(valor);
                return new Date(num > 1000000000000 ? num : num * 1000);
            } catch (e) {
                debugLog(`Error parseando timestamp numérico: ${valor}`);
            }
        }

        return null;
    }

    function obtenerFechaCreacionExacta(ur) {
        try {
            if (ur.attributes.driveDate) {
                const fecha = parsearFecha(ur.attributes.driveDate);
                if (fecha) return fecha;
            }
            return null;
        } catch (e) {
            debugLog(`Error obteniendo fecha: ${e}`);
            return null;
        }
    }

    function obtenerFechaUC(ur) {
        try {
            if (ur.attributes.createdOn) {
                const fecha = parsearFecha(ur.attributes.createdOn);
                if (fecha) return fecha;
            }

            if (ur.attributes.updatedOn) {
                const fecha = parsearFecha(ur.attributes.updatedOn);
                if (fecha) return fecha;
            }

            if (ur.attributes.comments && ur.attributes.comments.length > 0) {
                const primerComentario = ur.attributes.comments[0];
                if (primerComentario.createdOn) {
                    const fecha = parsearFecha(primerComentario.createdOn);
                    if (fecha) return fecha;
                }
            }

            return null;
        } catch (e) {
            debugLog(`Error obteniendo fecha UC para UR ${ur.id}: ${e}`);
            return null;
        }
    }

    function calcularDiferenciaDias(fecha) {
        if (!fecha) return null;

        const hoy = new Date();
        const diffTiempo = hoy.getTime() - fecha.getTime();
        const diffDias = Math.floor(diffTiempo / (1000 * 60 * 60 * 24));

        return diffDias;
    }

    function formatearDiferenciaDias(ur) {
        const fechaUC = obtenerFechaUC(ur);
        if (!fechaUC) return "No disponible";

        const dias = calcularDiferenciaDias(fechaUC);
        if (dias === null) return "Error cálculo";

        return `${dias} días`;
    }

    function clasificarUR(fecha) {
        if (!fecha) return { estado: "Sin fecha", clase: "ur-no-fecha" };

        const hoy = new Date();
        const diff = hoy - fecha;
        const dias = Math.floor(diff / (1000 * 60 * 60 * 24));

        if (dias > CONFIG.UMBRAL_VIEJO) return { estado: `Antigua (${dias}d)`, clase: "ur-old" };
        if (dias > CONFIG.UMBRAL_RECIENTE) return { estado: `Reciente (${dias}d)`, clase: "ur-recent" };
        return { estado: `Nueva (${dias}d)`, clase: "ur-new" };
    }

    function obtenerURsSinAtender() {
        try {
            if (!W.model?.mapUpdateRequests?.objects) return [];

            const bounds = W.map.getExtent();
            return Object.values(W.model.mapUpdateRequests.objects)
                .filter(ur => {
                    // Filtrar URs cerradas (open: false o resolved: true)
                    if (ur.attributes.open === false || ur.attributes.resolved) {
                        return false;
                    }

                    const geom = ur.getOLGeometry?.();
                    if (!geom) return false;

                    const center = geom.getBounds().getCenterLonLat();
                    if (!bounds.containsLonLat(center)) return false;

                    const comentarios = ur.attributes.comments || [];
                    return !comentarios.some(c => c.type === 'user' && c.text?.trim().length > 0);
                });
        } catch (e) {
            debugLog('Error obteniendo URs: ' + e);
            return [];
        }
    }

    function mostrarPanelURs() {
        estado.panelVisible = true;
        limpiarTimeouts();

        $(`#${CONFIG.PANEL_ID}`).remove();

        const panel = $(`<div id="${CONFIG.PANEL_ID}">`);
        const panelContent = $('<div class="panel-content">');
        const panelFooter = $(`
            <div class="panel-footer">
                <button class="btn-global btn-responder" id="responder-todas">Preguntar</button>
                <button class="btn-global btn-resuelta" id="resolver-todas">Resuelta</button>
                <button class="btn-global btn-cerrar" id="cerrar-todas">No Identificada</button>
                <button class="btn-global btn-reiniciar" id="actualizar-lista">Actualizar Lista</button>
            </div>
        `);

        // Función para actualizar el contenido del panel
        const actualizarContenidoPanel = () => {
            estado.URsActuales = obtenerURsSinAtender();

            if (estado.URsActuales.length === 0) {
                panelContent.html('<div style="padding:15px;text-align:center;"><b>No hay URs sin atender visibles</b></div>');
            } else {
                let tablaHTML = `
                    <h3 style="margin-top:0;">URs Activas: ${estado.URsActuales.length}</h3>
                    <table>
                        <thead>
                            <tr>
                                <th>ID</th>
                                <th>Fecha Creación</th>
                                <th>Estado</th>
                                <th>UC (Días)</th>
                                <th>Acción</th>
                            </tr>
                        </thead>
                        <tbody>`;

                estado.URsActuales.forEach(ur => {
                    const id = ur.attributes.id;
                    const fecha = obtenerFechaCreacionExacta(ur);
                    const clasificacion = clasificarUR(fecha);
                    const diferenciaDias = formatearDiferenciaDias(ur);

                    let fechaStr = 'No disponible';
                    if (fecha) {
                        fechaStr = fecha.toLocaleDateString('es-ES', {
                            year: 'numeric',
                            month: '2-digit',
                            day: '2-digit',
                            hour: '2-digit',
                            minute: '2-digit'
                        });
                    }

                    const esVisitada = estado.urVisitadas.includes(id) ? 'ur-visitada' : '';
                    const fueCentrada = estado.urCentradas.includes(id);

                    tablaHTML += `
                        <tr id="fila-ur-${id}" class="${esVisitada}">
                            <td>${id}</td>
                            <td>${fechaStr}</td>
                            <td class="${clasificacion.clase}">${clasificacion.estado}</td>
                            <td>${diferenciaDias}</td>
                            <td><button class="btn-centrar" data-id="${id}" ${fueCentrada ? 'data-centered="true"' : ''}>🗺️ Centrar</button></td>
                        </tr>`;
                });

                panelContent.html(`
                    ${tablaHTML}
                        </tbody>
                    </table>
                `);

                panelContent.on('click', '.btn-centrar', function() {
                    if (estado.accionEnProgreso || estado.bloqueado) {
                        debugLog('Acción de centrar bloqueada temporalmente');
                        return;
                    }

                    const id = $(this).data('id');
                    const $btn = $(this);

                    if ($btn.attr('data-centered') === 'true') {
                        const ur = W.model.mapUpdateRequests.getObjectById(Number(id));
                        if (ur) {
                            try {
                                if (W.control?.MapUpdateRequest?.show) {
                                    W.control.MapUpdateRequest.show(ur);
                                } else if (W.control?.MapProblem?.show) {
                                    W.control.MapProblem.show(ur);
                                } else if (W.control?.UR?.show) {
                                    W.control.UR.show(ur);
                                }
                            } catch (e) {
                                debugLog(`Error al mostrar UR ${id}: ${e}`);
                            }
                        }
                    } else {
                        centrarYMostrarUR(id);
                        $btn.attr('data-centered', 'true');
                    }

                    $(`#fila-ur-${id}`).addClass('ur-visitada');
                    if (!estado.urVisitadas.includes(id)) {
                        estado.urVisitadas.push(id);
                    }
                });
            }
        };

        // Configurar el evento de actualización
        panelFooter.on('click', '#actualizar-lista', function() {
            if (estado.accionEnProgreso) return;

            // Ajustar el zoom a 13
            W.map.getOLMap().zoomTo(CONFIG.ZOOM_ACTUALIZACION);


            // Pequeña espera antes de actualizar para que el mapa se estabilice
            agregarTimeout(() => {
                actualizarContenidoPanel();
                debugLog(`Panel actualizado después de ajustar zoom a ${CONFIG.ZOOM_ACTUALIZACION}`);
            }, 1000);
        });

        // Configurar otros eventos
        panelFooter.on('click', '#responder-todas', function() {
            if (estado.accionEnProgreso || estado.bloqueado) return;
            estado.URsActuales.forEach((ur, index) => {
                agregarTimeout(() => responderUR(ur.attributes.id), index * CONFIG.RETRASO_ENTRE_ACCIONES);
            });
        });

        panelFooter.on('click', '#resolver-todas', function() {
            if (estado.accionEnProgreso || estado.bloqueado) return;
            estado.URsActuales.forEach((ur, index) => {
                agregarTimeout(() => resolverUR(ur.attributes.id), index * CONFIG.RETRASO_ENTRE_ACCIONES);
            });
        });

        panelFooter.on('click', '#cerrar-todas', function() {
            if (estado.accionEnProgreso || estado.bloqueado) return;
            estado.URsActuales.forEach((ur, index) => {
                agregarTimeout(() => cerrarUR(ur.attributes.id), index * CONFIG.RETRASO_ENTRE_ACCIONES);
            });
        });

        // Cargar contenido inicial
        actualizarContenidoPanel();

        panel.append(panelContent);
        panel.append(panelFooter);
        panel.appendTo('body').fadeIn(300);
    }

    function centrarYMostrarUR(id) {
        if (estado.accionEnProgreso || estado.bloqueado) {
            debugLog(`Acción bloqueada - accionEnProgreso: ${estado.accionEnProgreso}, bloqueado: ${estado.bloqueado}`);
            return;
        }

        estado.accionEnProgreso = true;
        limpiarTimeouts();

        const ur = W.model.mapUpdateRequests.getObjectById(Number(id));
        if (!ur) {
            debugLog(`UR ${id} no encontrada`);
            estado.accionEnProgreso = false;
            return;
        }

        if (!estado.urCentradas.includes(id)) {
            estado.urCentradas.push(id);
        }

        const geom = ur.getOLGeometry?.();
        if (geom) {
            const center = geom.getBounds().getCenterLonLat();
            W.map.setCenter(center, 17);

            agregarTimeout(() => {
                try {
                    if (W.control?.MapUpdateRequest?.show) {
                        W.control.MapUpdateRequest.show(ur);
                    } else if (W.control?.MapProblem?.show) {
                        W.control.MapProblem.show(ur);
                    } else if (W.control?.UR?.show) {
                        W.control.UR.show(ur);
                    } else if (W.selectionManager) {
                        W.selectionManager.select([ur]);
                    }

                    $(`#fila-ur-${id}`).addClass('ur-visitada');
                    if (!estado.urVisitadas.includes(id)) {
                        estado.urVisitadas.push(id);
                    }
                } catch (e) {
                    debugLog(`Error al mostrar UR ${id}: ${e}`);
                } finally {
                    estado.accionEnProgreso = false;
                }
            }, 300);
        } else {
            debugLog(`No se pudo obtener geometría para UR ${id}`);
            estado.accionEnProgreso = false;
        }
    }

    function responderUR(id) {
        if (estado.accionEnProgreso || estado.bloqueado) return;
        estado.accionEnProgreso = true;

        limpiarTimeouts();
        const ur = W.model.mapUpdateRequests.getObjectById(Number(id));
        if (!ur) {
            resetearEstado();
            return;
        }

        centrarYMostrarUR(id);

        agregarTimeout(() => {
            try {
                const commentField = $('.new-comment-text');
                if (!commentField.length) {
                    throw new Error('Campo de comentario no encontrado');
                }

                commentField.val(CONFIG.MENSAJE_RESPUESTA);
                commentField.trigger('input').trigger('change');

                agregarTimeout(() => {
                    const sendButton = $('.send-button:not(:disabled)');
                    if (!sendButton.length) {
                        throw new Error('Botón enviar no encontrado o deshabilitado');
                    }

                    sendButton[0].click();
                    resetearEstado();
                }, 500);
            } catch (error) {
                debugLog(`Error en responderUR: ${error.message}`);
                resetearEstado();
            }
        }, CONFIG.RETRASO_ESPERA_UI);
    }

    function resolverUR(id) {
        if (estado.accionEnProgreso || estado.bloqueado) return;

        estado.accionEnProgreso = true;
        estado.bloqueado = true;
        limpiarTimeouts();

        const ur = W.model.mapUpdateRequests.getObjectById(Number(id));
        if (!ur) {
            resetearEstado();
            return;
        }

        //centrarYMostrarUR(id);

        agregarTimeout(() => {
            try {
                const commentField = $('.new-comment-text');
                if (!commentField.length) {
                    throw new Error('Campo de comentario no encontrado');
                }

                commentField.val(CONFIG.MENSAJE_RESUELTA);
                commentField.trigger('input').trigger('change');

                agregarTimeout(() => {
                    const sendButton = $('.send-button:not(:disabled)');
                    if (!sendButton.length) {
                        throw new Error('Botón enviar no encontrado o deshabilitado');
                    }

                    sendButton[0].click();

                    agregarTimeout(() => {
                        const solvedButton = document.querySelector('[data-status="SOLVED"], label[for="state-solved"], [data-testid="solved-button"]');
                        if (!solvedButton) {
                            throw new Error('Botón "Resuelta" no encontrado');
                        }

                        solvedButton.click();

                        agregarTimeout(() => {
                            const confirmButton = document.querySelector('.buttons .button-primary, .dialog-footer .button-primary');
                            if (confirmButton) {
                                confirmButton.click();
                            }

                            agregarTimeout(() => {
                                $(`#fila-ur-${id}`).remove();
                                estado.URsActuales = estado.URsActuales.filter(u => u.attributes.id !== id);

                                const contador = $('h3').first();
                                if (contador.length) {
                                    contador.text(`URs Activas: ${estado.URsActuales.length}`);
                                }

                                resetearEstado();
                                debugLog('Estado desbloqueado después de resolver UR');
                            }, 500);
                        }, 500);
                    }, 500);
                }, 500);
            } catch (error) {
                debugLog(`Error en resolverUR: ${error.message}`);
                resetearEstado();
            }
        }, CONFIG.RETRASO_ESPERA_UI);
         if (estado.accionEnProgreso) return;
    }

    function cerrarUR(id) {
        if (estado.accionEnProgreso || estado.bloqueado) return;

        estado.accionEnProgreso = true;
        estado.bloqueado = true;
        limpiarTimeouts();

        const ur = W.model.mapUpdateRequests.getObjectById(Number(id));
        if (!ur) {
            resetearEstado();
            return;
        }

        centrarYMostrarUR(id);

        agregarTimeout(() => {
            try {
                const commentField = $('.new-comment-text');
                if (!commentField.length) {
                    throw new Error('Campo de comentario no encontrado');
                }

                commentField.val(CONFIG.MENSAJE_CIERRE);
                commentField.trigger('input').trigger('change');

                agregarTimeout(() => {
                    const sendButton = $('.send-button:not(:disabled)');
                    if (!sendButton.length) {
                        throw new Error('Botón enviar no encontrado o deshabilitado');
                    }

                    sendButton[0].click();

                    agregarTimeout(() => {
                        const notIdentifiedButton = document.querySelector('[data-status="NOT_IDENTIFIED"], label[for="state-not-identified"], [data-testid="not-identified-button"]');
                        if (!notIdentifiedButton) {
                            throw new Error('Botón "No Identificado" no encontrado');
                        }

                        notIdentifiedButton.click();

                        agregarTimeout(() => {
                            const confirmButton = document.querySelector('.buttons .button-primary, .dialog-footer .button-primary');
                            if (confirmButton) {
                                confirmButton.click();
                            }

                            agregarTimeout(() => {
                                $(`#fila-ur-${id}`).remove();
                                estado.URsActuales = estado.URsActuales.filter(u => u.attributes.id !== id);

                                const contador = $('h3').first();
                                if (contador.length) {
                                    contador.text(`URs Activas: ${estado.URsActuales.length}`);
                                }

                                resetearEstado();
                                debugLog('Estado desbloqueado después de cerrar UR');
                            }, 500);
                        }, 500);
                    }, 500);
                }, 500);
            } catch (error) {
                debugLog(`Error en cerrarUR: ${error.message}`);

                if (estado.reintentos < CONFIG.MAX_REINTENTOS) {
                    estado.reintentos++;
                    debugLog(`Reintentando (${estado.reintentos}/${CONFIG.MAX_REINTENTOS})...`);
                    agregarTimeout(() => cerrarUR(id), 1000);
                } else {
                    resetearEstado();
                }
            }
        }, CONFIG.RETRASO_ESPERA_UI);
    }

    function inicializarScript() {
        debugLog('Inicializando script...');
        window.togglePanelURs = togglePanelURs;
        crearBoton();

        estado.intervaloVerificacion = setInterval(() => {
            if ($(`#${CONFIG.BOTON_ID}`).length === 0) {
                debugLog('Botón no encontrado, recreando...');
                crearBoton();
            }
        }, CONFIG.INTERVALO_VERIFICACION);

        debugLog('Script inicializado correctamente');
    }

    function esperarWME() {
        if (typeof W === 'undefined' || !W.loginManager || !W.model || !W.map) {
            debugLog('WME no está completamente cargado, reintentando...');
            setTimeout(esperarWME, 1000);
            return;
        }

        if (!W.model.mapUpdateRequests) {
            debugLog('Módulo mapUpdateRequests no está disponible, reintentando...');
            setTimeout(esperarWME, 1000);
            return;
        }

        setTimeout(inicializarScript, 2000);
    }

    debugLog('Script cargado, esperando WME...');
    esperarWME();
})();