WME Quick Zoom Button

Fügt einen Button für temporären Zoom hinzu für bessere Chat sichtbarkeit und Auto-Sichtbarkeit

// ==UserScript==
// @name         WME Quick Zoom Button
// @name:en      WME Quick Zoom Button
// @name:es      Botón de Zoom Rápido WME
// @version      2025.07.21
// @description  Fügt einen Button für temporären Zoom hinzu für bessere Chat sichtbarkeit und Auto-Sichtbarkeit
// @description:en  Adds a button for temporary zoom for better chat visibility and auto-visibility
// @description:es  Agrega un botón para zoom temporal para una mejor visibilidad del chat y auto-visibilidad
// @author       Hiwi234
// @match        https://www.waze.com/editor*
// @match        https://www.waze.com/*/editor*
// @match        https://beta.waze.com/editor*
// @match        https://beta.waze.com/*/editor*
// @grant        none
// @license      MIT
// @namespace https://greasyfork.org/de/users/863740-horst-wittlich
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_KEY = {
        AUTO: 'wme-quick-zoom-auto',
        ZOOM: 'wme-quick-zoom-level',
        VISIBILITY: 'wme-auto-visibility'
    };

    const translations = {
        'de': {
            buttonText: 'Quick Zoom',
            buttonTooltip: 'Temporär auf Zoomstufe zoomen',
            sliderLabel: 'Maximale Zoomstufe:',
            autoLoadLabel: 'Automatisch beim Laden',
            visibilityLabel: 'Immer sichtbar bleiben'
        },
        'en': {
            buttonText: 'Quick Zoom',
            buttonTooltip: 'Temporarily zoom to level',
            sliderLabel: 'Maximum zoom level:',
            autoLoadLabel: 'Automatic on load',
            visibilityLabel: 'Always stay visible'
        },
        'es': {
            buttonText: 'Zoom Rápido',
            buttonTooltip: 'Zoom temporal al nivel',
            sliderLabel: 'Nivel máximo de zoom:',
            autoLoadLabel: 'Automático al cargar',
            visibilityLabel: 'Permanecer siempre visible'
        }
    };

    function getLanguage() {
        const lang = navigator.language.split('-')[0];
        return translations[lang] ? lang : 'en';
    }

    function getAutoZoomSetting() {
        return localStorage.getItem(STORAGE_KEY.AUTO) === 'true';
    }

    function setAutoZoomSetting(value) {
        localStorage.setItem(STORAGE_KEY.AUTO, value);
    }

    function getVisibilitySetting() {
        return localStorage.getItem(STORAGE_KEY.VISIBILITY) === 'true';
    }

    function setVisibilitySetting(value) {
        localStorage.setItem(STORAGE_KEY.VISIBILITY, value);
    }

    function getZoomLevel() {
        return parseInt(localStorage.getItem(STORAGE_KEY.ZOOM) || '7');
    }

    function setZoomLevel(value) {
        localStorage.setItem(STORAGE_KEY.ZOOM, value);
    }

    function ensureVisibility() {
        if (!getVisibilitySetting()) return;

        try {
            // Suche nach dem Sichtbarkeits-Element im Online-Editoren Bereich
            const visibilityLabel = document.querySelector('span.editor-visibility.label');

            if (visibilityLabel) {
                const labelText = visibilityLabel.textContent.toLowerCase().trim();
                console.log('[WME Quick Zoom] Aktueller Sichtbarkeitsstatus:', labelText);

                // Prüfe ob der Status "unsichtbar" oder "invisible" ist
                if (labelText.includes('unsichtbar') || labelText.includes('invisible')) {
                    // Suche nach dem zugehörigen Button
                    const tooltipButton = visibilityLabel.closest('wz-list-item')?.querySelector('wz-button[color="clear-icon"]');

                    if (tooltipButton) {
                        console.log('[WME Quick Zoom] Unsichtbar erkannt, klicke Button...');
                        tooltipButton.click();
                        console.log('[WME Quick Zoom] Automatisch auf sichtbar gestellt');
                        return;
                    }
                }
            }

            // Alternative Suche nach dem Button über verschiedene Selektoren
            const alternativeSelectors = [
                'wz-button[color="clear-icon"][size="md"]',
                'button.wz-button.clear-icon.md.icon-only',
                'wz-button button[type="button"]',
                '.editor-visibility.label'
            ];

            for (const selector of alternativeSelectors) {
                const elements = document.querySelectorAll(selector);
                for (const element of elements) {
                    // Prüfe verschiedene Text-Inhalte und Attribute
                    const elementText = element.textContent?.toLowerCase() || '';
                    const ariaLabel = element.getAttribute('aria-label')?.toLowerCase() || '';
                    const title = element.getAttribute('title')?.toLowerCase() || '';
                    const parentText = element.parentElement?.textContent?.toLowerCase() || '';

                    if (elementText.includes('unsichtbar') || elementText.includes('invisible') ||
                        ariaLabel.includes('unsichtbar') || ariaLabel.includes('invisible') ||
                        title.includes('unsichtbar') || title.includes('invisible') ||
                        parentText.includes('unsichtbar') || parentText.includes('invisible')) {

                        console.log('[WME Quick Zoom] Unsichtbar-Element gefunden, klicke...', selector);
                        element.click();
                        console.log('[WME Quick Zoom] Automatisch auf sichtbar gestellt');
                        return;
                    }
                }
            }

            // Suche nach Icon-Namen
            const iconElements = document.querySelectorAll('wz-icon, i[class*="icon"]');
            for (const icon of iconElements) {
                const iconName = icon.getAttribute('name') || icon.className;
                if (iconName && (iconName.includes('invisible') || iconName.includes('unsichtbar'))) {
                    const clickableParent = icon.closest('button, wz-button, [clickable="true"]');
                    if (clickableParent) {
                        console.log('[WME Quick Zoom] Unsichtbar-Icon gefunden, klicke Parent...');
                        clickableParent.click();
                        console.log('[WME Quick Zoom] Automatisch auf sichtbar gestellt');
                        return;
                    }
                }
            }

        } catch (error) {
            console.warn('[WME Quick Zoom] Fehler beim Setzen der Sichtbarkeit:', error);
        }
    }

    function startVisibilityMonitoring() {
        if (!getVisibilitySetting()) return;

        console.log('[WME Quick Zoom] Starte Sichtbarkeits-Überwachung...');

        // Erweiterte DOM-Überwachung
        const observer = new MutationObserver((mutations) => {
            let shouldCheck = false;

            mutations.forEach((mutation) => {
                // Prüfe auf Änderungen in relevanten Bereichen
                if (mutation.type === 'childList') {
                    const addedNodes = Array.from(mutation.addedNodes);
                    const removedNodes = Array.from(mutation.removedNodes);

                    const relevantChanges = [...addedNodes, ...removedNodes].some(node => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const element = node;
                            return element.classList && (element.classList.contains('online-editors-list') ||
                                   element.classList.contains('editor-visibility')) ||
                                   element.tagName === 'WZ-BUTTON' ||
                                   (element.querySelector && element.querySelector('.editor-visibility, wz-button, .online-editors-list'));
                        }
                        return false;
                    });

                    if (relevantChanges) shouldCheck = true;
                }

                // Prüfe auf Attribut-Änderungen
                if (mutation.type === 'attributes' &&
                    (mutation.attributeName === 'aria-label' ||
                     mutation.attributeName === 'title' ||
                     mutation.attributeName === 'class' ||
                     mutation.attributeName === 'name')) {

                    const target = mutation.target;
                    if (target.closest && (target.closest('.online-editors-list') ||
                        (target.classList && target.classList.contains('editor-visibility')) ||
                        target.tagName === 'WZ-BUTTON')) {
                        shouldCheck = true;
                    }
                }
            });

            if (shouldCheck) {
                setTimeout(ensureVisibility, 100); // Kurze Verzögerung für DOM-Updates
            }
        });

        // Überwache das gesamte Online-Editoren Element
        const onlineEditorsContainer = document.getElementById('online-editors');
        if (onlineEditorsContainer) {
            observer.observe(onlineEditorsContainer, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['aria-label', 'title', 'class', 'name']
            });
        } else {
            // Fallback: Überwache den gesamten Body
            observer.observe(document.body, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['aria-label', 'title', 'class', 'name']
            });
        }

        // Überprüfe alle 15 Sekunden
        setInterval(ensureVisibility, 15000);

        // Erste Überprüfung nach 3 Sekunden
        setTimeout(ensureVisibility, 3000);

        // Zusätzliche Überprüfung beim Fokus-Wechsel
        window.addEventListener('focus', () => {
            setTimeout(ensureVisibility, 1000);
        });
    }

    async function performQuickZoom() {
        const originalZoom = W.map.olMap.getZoom();
        W.map.olMap.zoomTo(getZoomLevel());

        return new Promise(resolve => {
            setTimeout(() => {
                W.map.olMap.zoomTo(originalZoom);
                resolve();
            }, 2000);
        });
    }

    async function initializeQuickZoom() {
        const i18n = translations[getLanguage()];
        const { tabLabel, tabPane } = W.userscripts.registerSidebarTab("quick-zoom-script");

        tabLabel.innerText = 'QZ';
        tabLabel.title = i18n.buttonText;

        const container = document.createElement('div');
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.gap = '10px';
        container.style.padding = '10px';

        const sidebarButton = document.createElement('button');
        sidebarButton.className = 'waze-btn waze-btn-small';
        sidebarButton.innerText = i18n.buttonText;
        sidebarButton.title = i18n.buttonTooltip;

        const floatingButton = document.createElement('button');
        floatingButton.innerText = 'QZ';
        floatingButton.title = i18n.buttonTooltip;
        floatingButton.style.cssText = `
            position: fixed;
            bottom: 20px;
            left: 20px;
            z-index: 1000;
            background-color: white;
            border: 1px solid #ccc;
            padding: 8px 15px;
            border-radius: 20px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
            cursor: pointer;
            font-weight: bold;
            transition: all 0.3s ease;
        `;

        floatingButton.addEventListener('mouseenter', () => {
            floatingButton.style.backgroundColor = '#f0f0f0';
            floatingButton.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
        });

        floatingButton.addEventListener('mouseleave', () => {
            floatingButton.style.backgroundColor = 'white';
            floatingButton.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';
        });

        const sliderContainer = document.createElement('div');
        sliderContainer.style.display = 'flex';
        sliderContainer.style.flexDirection = 'column';
        sliderContainer.style.gap = '5px';

        const sliderLabel = document.createElement('label');
        sliderLabel.textContent = i18n.sliderLabel;
        sliderLabel.style.fontSize = '12px';

        const sliderValue = document.createElement('span');
        sliderValue.style.fontSize = '12px';
        sliderValue.textContent = getZoomLevel();

        const slider = document.createElement('input');
        slider.type = 'range';
        slider.min = '4';
        slider.max = '12';
        slider.value = getZoomLevel();
        slider.style.width = '100%';

        let isZooming = false;
        const zoomHandler = async () => {
            if (!isZooming) {
                isZooming = true;
                await performQuickZoom();
                isZooming = false;
            }
        };

        slider.addEventListener('input', (e) => {
            const value = e.target.value;
            sliderValue.textContent = value;
            setZoomLevel(value);
            sidebarButton.title = `${i18n.buttonTooltip} ${value}`;
            floatingButton.title = `${i18n.buttonTooltip} ${value}`;
        });

        sidebarButton.addEventListener('click', zoomHandler);
        floatingButton.addEventListener('click', zoomHandler);

        const checkboxContainer = document.createElement('div');
        checkboxContainer.style.display = 'flex';
        checkboxContainer.style.alignItems = 'center';
        checkboxContainer.style.gap = '5px';

        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.id = 'auto-quick-zoom';
        checkbox.checked = getAutoZoomSetting();

        const label = document.createElement('label');
        label.htmlFor = 'auto-quick-zoom';
        label.textContent = i18n.autoLoadLabel;
        label.style.fontSize = '12px';

        checkbox.addEventListener('change', (e) => {
            setAutoZoomSetting(e.target.checked);
        });

        // Visibility Checkbox
        const visibilityCheckboxContainer = document.createElement('div');
        visibilityCheckboxContainer.style.display = 'flex';
        visibilityCheckboxContainer.style.alignItems = 'center';
        visibilityCheckboxContainer.style.gap = '5px';

        const visibilityCheckbox = document.createElement('input');
        visibilityCheckbox.type = 'checkbox';
        visibilityCheckbox.id = 'auto-visibility';
        visibilityCheckbox.checked = getVisibilitySetting();

        const visibilityLabel = document.createElement('label');
        visibilityLabel.htmlFor = 'auto-visibility';
        visibilityLabel.textContent = i18n.visibilityLabel;
        visibilityLabel.style.fontSize = '12px';

        visibilityCheckbox.addEventListener('change', (e) => {
            setVisibilitySetting(e.target.checked);
            if (e.target.checked) {
                startVisibilityMonitoring();
                ensureVisibility();
            }
        });

        sliderContainer.appendChild(sliderLabel);
        sliderContainer.appendChild(slider);
        sliderContainer.appendChild(sliderValue);
        checkboxContainer.appendChild(checkbox);
        checkboxContainer.appendChild(label);
        visibilityCheckboxContainer.appendChild(visibilityCheckbox);
        visibilityCheckboxContainer.appendChild(visibilityLabel);
        container.appendChild(sidebarButton);
        container.appendChild(sliderContainer);
        container.appendChild(checkboxContainer);
        container.appendChild(visibilityCheckboxContainer);
        tabPane.appendChild(container);
        document.body.appendChild(floatingButton);

        await W.userscripts.waitForElementConnected(tabPane);

        if (getAutoZoomSetting()) {
            await performQuickZoom();
        }

        if (getVisibilitySetting()) {
            startVisibilityMonitoring();
        }
    }

    if (W?.userscripts?.state.isReady) {
        initializeQuickZoom();
    } else {
        document.addEventListener("wme-ready", initializeQuickZoom, {
            once: true,
        });
    }
})();