WME Map Nav History

Navigate through map history using Alt + arrow keys and mouse control

// ==UserScript==
// @name         WME Map Nav History
// @name:de      WME Karten-Navigations-Historie
// @name:es      WME Historial de Navegación del Mapa
// @description  Navigate through map history using Alt + arrow keys and mouse control
// @description:de      Ermöglicht die Navigation durch die Kartenhistorie mit Alt + Pfeiltasten und Maussteuerung
// @description:es      Permite navegar por el historial del mapa usando Alt + teclas de flecha y control del ratón
// @namespace    https://greasyfork.org/de/users/863740-horst-wittlich
// @version      2025.05.17
// @author       hiwi234
// @include      https://www.waze.com/editor*
// @include      https://www.waze.com/*/editor*
// @include      https://beta.waze.com/*
// @exclude      https://www.waze.com/user/*editor/*
// @exclude      https://www.waze.com/*/user/*editor/*
// @grant        none
// @license      MIT
// ==/UserScript==

/* global W */
(function() {
    'use strict';

    let navigationHistory = [];
    let currentIndex = -1;
    let isInitialized = false;
    let currentLanguage = (navigator.language || navigator.userLanguage).substring(0, 2);

    const translations = {
        en: {
            scriptActive: "Script active",
            back: "Back",
            forward: "Forward",
            controls: "Controls",
            previousPosition: "Previous position",
            nextPosition: "Next position",
            backButton: "Back button",
            forwardButton: "Forward button",
            hints: "Hints",
            historyLimit: "History stores up to 100 positions",
            autoSave: "New positions are automatically saved when moving the map"
        },
        de: {
            scriptActive: "Script aktiv",
            back: "Zurück",
            forward: "Vorwärts",
            controls: "Steuerung",
            previousPosition: "Vorherige Position",
            nextPosition: "Nächste Position",
            backButton: "Zurück-Button",
            forwardButton: "Vorwärts-Button",
            hints: "Hinweise",
            historyLimit: "Die Historie speichert bis zu 100 Positionen",
            autoSave: "Neue Positionen werden beim Verschieben der Karte automatisch gespeichert"
        },
        es: {
            scriptActive: "Script activo",
            back: "Atrás",
            forward: "Adelante",
            controls: "Controles",
            previousPosition: "Posición anterior",
            nextPosition: "Siguiente posición",
            backButton: "Botón atrás",
            forwardButton: "Botón adelante",
            hints: "Consejos",
            historyLimit: "El historial almacena hasta 100 posiciones",
            autoSave: "Las nuevas posiciones se guardan automáticamente al mover el mapa"
        }
    };

    const t = (key) => {
        return (translations[currentLanguage] && translations[currentLanguage][key]) || translations.en[key];
    };

    function saveCurrentPosition() {
        if (!isInitialized || !W.map) return;

        const center = W.map.getCenter();
        const zoom = W.map.getZoom();

        const lastEntry = navigationHistory[currentIndex];
        if (lastEntry &&
            lastEntry.lat === center.lat &&
            lastEntry.lon === center.lon &&
            lastEntry.zoom === zoom) {
            return;
        }

        navigationHistory = navigationHistory.slice(0, currentIndex + 1);

        navigationHistory.push({
            lat: center.lat,
            lon: center.lon,
            zoom: zoom
        });
        currentIndex++;

        if (navigationHistory.length > 100) {
            navigationHistory.shift();
            currentIndex--;
        }

        updateNavigationButtons();
    }

    function navigateToPosition(position) {
        if (!position || !isInitialized || !W.map) return;

        try {
            const lonlat = new OpenLayers.LonLat(position.lon, position.lat);
            W.map.setCenter(lonlat, position.zoom);
        } catch (error) {
            console.error('WME Map Nav History: Navigation error:', error);
        }
    }

    function handleKeyDown(e) {
        if (!isInitialized) return;

        if (e.altKey && e.keyCode === 37) {
            navigateBack();
            e.preventDefault();
        }
        else if (e.altKey && e.keyCode === 39) {
            navigateForward();
            e.preventDefault();
        }
    }

    function navigateBack() {
        if (currentIndex > 0) {
            currentIndex--;
            navigateToPosition(navigationHistory[currentIndex]);
            updateNavigationButtons();
        }
    }

    function navigateForward() {
        if (currentIndex < navigationHistory.length - 1) {
            currentIndex++;
            navigateToPosition(navigationHistory[currentIndex]);
            updateNavigationButtons();
        }
    }

    function updateNavigationButtons() {
        const backBtn = document.getElementById('nav-history-back');
        const forwardBtn = document.getElementById('nav-history-forward');

        if (backBtn) {
            backBtn.disabled = currentIndex <= 0;
            backBtn.style.opacity = currentIndex <= 0 ? '0.5' : '1';
        }
        if (forwardBtn) {
            forwardBtn.disabled = currentIndex >= navigationHistory.length - 1;
            forwardBtn.style.opacity = currentIndex >= navigationHistory.length - 1 ? '0.5' : '1';
        }
    }

    async function createSidebarTab() {
        try {
            const { tabLabel, tabPane } = W.userscripts.registerSidebarTab("wme-nav-history");

            tabLabel.innerText = 'NAV';
            tabLabel.title = '<h4>Map Navigation History</h4>';

            await W.userscripts.waitForElementConnected(tabPane);

            const styleSheet = document.createElement("style");
            styleSheet.textContent = `
                .nav-history-button {
                    padding: 8px 15px;
                    cursor: pointer;
                    background: #4a89dc;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    font-weight: 600;
                    transition: all 0.3s ease;
                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                    min-width: 100px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    gap: 5px;
                }

                .nav-history-button:hover {
                    background: #5d9cec;
                    box-shadow: 0 4px 8px rgba(0,0,0,0.15);
                    transform: translateY(-1px);
                }

                .nav-history-button:active {
                    transform: translateY(1px);
                    box-shadow: 0 1px 2px rgba(0,0,0,0.1);
                }

                .nav-history-button:disabled {
                    background: #b5b5b5;
                    cursor: not-allowed;
                    transform: none;
                    box-shadow: none;
                }

                .nav-history-container {
                    background: #f8f9fa;
                    border-radius: 8px;
                    padding: 20px;
                    margin: 10px 0;
                    box-shadow: 0 2px 6px rgba(0,0,0,0.05);
                }
            `;
            document.head.appendChild(styleSheet);

            tabPane.innerHTML = `
                <div class="nav-history-container">
                    <p style="margin-top: 0; color: #2c3e50;"><h4>WME Map Nav History</h4></p>
                    <p style="color: #4CAF50; display: flex; align-items: center; gap: 5px;">
                        <span style="font-size: 18px;">✓</span> ${t('scriptActive')}
                    </p>
                    <div style="display: flex; gap: 15px; margin: 20px 0;">
                        <button id="nav-history-back" class="nav-history-button">
                            <span style="font-size: 16px;">⬅️</span> ${t('back')}
                        </button>
                        <button id="nav-history-forward" class="nav-history-button">
                            ${t('forward')} <span style="font-size: 16px;">➡️</span>
                        </button>
                    </div>
                    <hr style="border: none; height: 1px; background: #e1e4e8; margin: 15px 0;">
                    <div style="background: #fff; padding: 15px; border-radius: 6px; border: 1px solid #e1e4e8;">
                        <b style="margin-top: 0; color: #2c3e50;">${t('controls')}:</b>
                        <ul style="padding-left: 20px; color: #4a5568;">
                            <li><strong>Alt + ←</strong> ${t('previousPosition')}</li>
                            <li><strong>Alt + →</strong> ${t('nextPosition')}</li>
                        </ul>
                        <b style="margin-top: 15px; color: #2c3e50;">${t('hints')}:</b>
                        <ul style="padding-left: 20px; color: #4a5568;">
                            <li>${t('historyLimit')}</li>
                            <li>${t('autoSave')}</li>
                        </ul>
                    </div>
                </div>
            `;

            const backBtn = tabPane.querySelector('#nav-history-back');
            const forwardBtn = tabPane.querySelector('#nav-history-forward');

            backBtn.addEventListener('click', navigateBack);
            forwardBtn.addEventListener('click', navigateForward);

            updateNavigationButtons();

            return true;
        } catch (error) {
            console.error('WME Map Nav History: Error creating sidebar tab:', error);
            return false;
        }
    }

    function initializeScript() {
        if (isInitialized) return;

        try {
            W.map.events.register('moveend', null, saveCurrentPosition);
            document.addEventListener('keydown', handleKeyDown);

            createSidebarTab();
            isInitialized = true;

            saveCurrentPosition();

            console.log('WME Map Nav History: Successfully initialized');
        } catch (error) {
            console.error('WME Map Nav History: Initialization error:', error);
            isInitialized = false;
        }
    }

    if (W?.userscripts?.state.isInitialized) {
        initializeScript();
    } else {
        document.addEventListener("wme-initialized", initializeScript, {
            once: true,
        });
    }
})();