WME Permalink to several Maps DACH

WME PTSM für Deutschland Österreich Schweiz

// ==UserScript==
// @name WME Permalink to several Maps DACH
// @description WME PTSM für Deutschland Österreich Schweiz
// @namespace https://greasyfork.org/de/users/863740-horst-wittlich
// @version 2025.06.07
// @match https://*.waze.com/editor*
// @match https://*.waze.com/*/editor*
// @match https://beta.waze.com/editor*
// @match https://beta.waze.com/*/editor*
// @icon https://i.ibb.co/ckSvk59/waze-icon.png
// @grant none
// @license MIT
// ==/UserScript==

/* global OpenLayers, W, proj4 */

var ptsmVersion = '2025.06.07';
var ptsmInitialized = false;
var ptsmUpdateKey = 'wme-ptsm-update-shown-' + ptsmVersion;

function showUpdatePopup() {
    try {
        // Prüfen ob Update-Info bereits angezeigt wurde
        if (localStorage.getItem(ptsmUpdateKey) === 'true') {
            return;
        }
    } catch (e) {
        // Falls localStorage nicht verfügbar, trotzdem anzeigen
    }

    // Popup erstellen
    var overlay = document.createElement('div');
    overlay.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.5);
        z-index: 999999;
        display: flex;
        align-items: center;
        justify-content: center;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
    `;

    var popup = document.createElement('div');
    popup.style.cssText = `
        background: white;
        border-radius: 12px;
        padding: 24px;
        max-width: 400px;
        margin: 20px;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
        text-align: center;
        position: relative;
    `;

    popup.innerHTML = `
        <div style="font-size: 24px; margin-bottom: 16px;">🎉 PTSM DACH Update! 🗺️</div>
        <div style="font-size: 18px; font-weight: bold; color: #007bff; margin-bottom: 16px;">
            Version ${ptsmVersion}
        </div>
        <div style="text-align: left; margin-bottom: 20px; line-height: 1.6;">
            <div style="font-size: 16px; font-weight: bold; margin-bottom: 12px; color: #333;">
                ✨ Was ist neu:
            </div>
            <div style="margin-bottom: 8px;">
                🔧 <strong>ADAC Zoom level ist nun feiner. Es werden mehr abstufungen vom WME berücksichtigt</strong> - Präzise Zoom-Übertragung
            </div>
        </div>
        <button id="ptsm-close-popup" style="
            background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
            color: white;
            border: none;
            border-radius: 8px;
            padding: 12px 24px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s ease;
        ">
            🚀 Los geht's!
        </button>
    `;

    overlay.appendChild(popup);
    document.body.appendChild(overlay);

    // Close button event
    var closeBtn = popup.querySelector('#ptsm-close-popup');
    closeBtn.addEventListener('click', function() {
        overlay.remove();
        try {
            localStorage.setItem(ptsmUpdateKey, 'true');
        } catch (e) {
            // localStorage not available
        }
    });

    closeBtn.addEventListener('mouseover', function() {
        this.style.transform = 'translateY(-2px)';
        this.style.boxShadow = '0 4px 12px rgba(0, 123, 255, 0.3)';
    });

    closeBtn.addEventListener('mouseout', function() {
        this.style.transform = 'translateY(0)';
        this.style.boxShadow = 'none';
    });

    // Schließen bei Klick auf Overlay
    overlay.addEventListener('click', function(e) {
        if (e.target === overlay) {
            overlay.remove();
            try {
                localStorage.setItem(ptsmUpdateKey, 'true');
            } catch (e) {
                // localStorage not available
            }
        }
    });
}

function getCenterZoom() {
    var map = W.map.getOLMap();
    var zoom = map.getZoom();
    var center = map.getCenter().transform(new OpenLayers.Projection('EPSG:900913'), new OpenLayers.Projection('EPSG:4326'));
    center.zoom = zoom;
    return center;
}

function saveDropdownStates() {
    const states = {};
    document.querySelectorAll('.ptsm-category').forEach(category => {
        const className = category.className.match(/ptsm-category-(\w+)/);
        if (className) {
            states[className[1]] = category.classList.contains('open');
        }
    });
    try {
        localStorage.setItem('wme-ptsm-dropdown-states', JSON.stringify(states));
    } catch (e) {
        console.log('PTSM: localStorage not available');
    }
}

function loadDropdownStates() {
    try {
        const saved = localStorage.getItem('wme-ptsm-dropdown-states');
        return saved ? JSON.parse(saved) : {};
    } catch (e) {
        return {};
    }
}

function saveCompactMode(isCompact) {
    try {
        localStorage.setItem('wme-ptsm-compact-mode', isCompact ? 'true' : 'false');
    } catch (e) {
        console.log('PTSM: localStorage not available');
    }
}

function loadCompactMode() {
    try {
        const saved = localStorage.getItem('wme-ptsm-compact-mode');
        return saved === 'true';
    } catch (e) {
        return false;
    }
}

function createCategory(title, className, defaultOpen = false) {
    const savedStates = loadDropdownStates();
    const categoryKey = className.replace('ptsm-category-', '');
    const isOpen = savedStates.hasOwnProperty(categoryKey) ? savedStates[categoryKey] : defaultOpen;

    var category = document.createElement('div');
    category.className = 'ptsm-category ' + className + (isOpen ? ' open' : '');

    var header = document.createElement('button');
    header.className = 'ptsm-category-header';
    header.innerHTML = title + '<div class="ptsm-dropdown-arrow"></div>';

    var content = document.createElement('div');
    content.className = 'ptsm-category-content';

    header.addEventListener('click', function() {
        category.classList.toggle('open');
        setTimeout(saveDropdownStates, 100);
    });

    category.appendChild(header);
    category.appendChild(content);

    return { category, content };
}

function createMapButton(text, className, clickHandler) {
    var btn = document.createElement('button');
    btn.textContent = text;
    btn.className = 'ptsm-map-btn ' + className;
    btn.addEventListener('click', clickHandler);
    return btn;
}

function createCompactButton() {
    var btn = document.createElement('button');
    btn.className = 'ptsm-compact-btn';
    btn.innerHTML = '🔧 Kompakt-Modus';

    const isCompact = loadCompactMode();
    if (isCompact) {
        btn.innerHTML = '🔧 Normal-Modus';
        btn.classList.add('active');
    }

    btn.addEventListener('click', function() {
        const container = document.querySelector('.ptsm-container');
        const isCurrentlyCompact = container.classList.contains('compact');

        if (isCurrentlyCompact) {
            container.classList.remove('compact');
            btn.innerHTML = '🔧 Kompakt-Modus';
            btn.classList.remove('active');
            saveCompactMode(false);
        } else {
            container.classList.add('compact');
            btn.innerHTML = '🔧 Normal-Modus';
            btn.classList.add('active');
            saveCompactMode(true);
        }
    });

    return btn;
}

function addButtons() {
    if (ptsmInitialized) {
        return;
    }

    if (!document.getElementById('user-info')) {
        setTimeout(addButtons, 500);
        return;
    }

    if (!W.loginManager.user) {
        if (!ptsmInitialized) {
            W.loginManager.events.register('login', null, function() {
                if (!ptsmInitialized) addButtons();
            });
            W.loginManager.events.register('loginStatus', null, function() {
                if (!ptsmInitialized) addButtons();
            });
        }
        if (!W.loginManager.user) {
            return;
        }
    }

    ptsmInitialized = true;

    if (typeof proj4 === "undefined") {
        var script = document.createElement('script');
        script.src = 'https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js';
        document.head.appendChild(script);
    }

    // Add CSS
    var style = document.createElement('style');
    style.textContent = `
        .ptsm-container {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
            padding: 8px;
            background: #fff;
            font-size: 12px;
        }

        .ptsm-category {
            margin-bottom: 8px;
            border-radius: 6px;
            overflow: hidden;
            background: #fff;
            box-shadow: 0 1px 4px rgba(0,0,0,0.08);
            border: 1px solid #e1e5e9;
        }

        .ptsm-category-header {
            width: 100%;
            padding: 8px 12px;
            background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
            color: white;
            border: none;
            cursor: pointer;
            font-weight: 600;
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: all 0.2s ease;
        }

        .ptsm-category-header:hover {
            background: linear-gradient(135deg, #5a6268 0%, #343a40 100%);
        }

        .ptsm-dropdown-arrow {
            width: 0;
            height: 0;
            border-left: 4px solid transparent;
            border-right: 4px solid transparent;
            border-top: 6px solid white;
            transition: transform 0.2s ease;
        }

        .ptsm-category.open .ptsm-dropdown-arrow {
            transform: rotate(180deg);
        }

        .ptsm-category-content {
            max-height: 0;
            overflow: hidden;
            padding: 0 8px;
            transition: max-height 0.3s ease, padding 0.2s ease;
        }

        .ptsm-category.open .ptsm-category-content {
            max-height: 500px;
            padding: 8px;
        }

        .ptsm-map-btn {
            width: calc(33.333% - 4px);
            height: 28px;
            margin: 2px;
            padding: 4px 6px 4px 24px;
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            font-size: 10px;
            font-weight: 500;
            color: #495057;
            cursor: pointer;
            position: relative;
            display: inline-flex;
            align-items: center;
            justify-content: flex-start;
            transition: all 0.15s ease;
            box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
            text-overflow: ellipsis;
            overflow: hidden;
            white-space: nowrap;
        }

        .ptsm-map-btn::before {
            content: '';
            position: absolute;
            left: 4px;
            top: 50%;
            transform: translateY(-50%);
            width: 12px;
            height: 12px;
            background-size: contain;
            background-repeat: no-repeat;
            background-position: center;
            flex-shrink: 0;
        }

        .ptsm-map-btn:hover {
            transform: translateY(-1px);
            background: linear-gradient(145deg, #ffffff 0%, #f1f3f4 100%);
            border-color: #007bff;
            color: #0056b3;
            box-shadow: 0 2px 8px rgba(0, 123, 255, 0.12);
        }

        .ptsm-compact-btn {
            width: calc(100% - 4px);
            height: 32px;
            margin: 2px;
            padding: 8px 12px;
            background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%);
            border: none;
            border-radius: 4px;
            font-size: 11px;
            font-weight: 600;
            color: white;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.15s ease;
            box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .ptsm-compact-btn:hover {
            transform: translateY(-1px);
            background: linear-gradient(135deg, #1e7e34 0%, #155724 100%);
            box-shadow: 0 2px 8px rgba(40, 167, 69, 0.25);
        }

        .ptsm-compact-btn.active {
            background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
        }

        .ptsm-compact-btn.active:hover {
            background: linear-gradient(135deg, #c82333 0%, #a02834 100%);
            box-shadow: 0 2px 8px rgba(220, 53, 69, 0.25);
        }

        /* Kompakt-Modus Styles */
        .ptsm-container.compact .ptsm-category {
            margin-bottom: 4px;
        }

        .ptsm-container.compact .ptsm-category-header {
            padding: 6px 10px;
            font-size: 10px;
        }

        .ptsm-container.compact .ptsm-category.open .ptsm-category-content {
            padding: 4px;
        }

        .ptsm-container.compact .ptsm-map-btn {
            width: calc(33.333% - 3px);
            height: 24px;
            margin: 1.5px;
            padding: 3px 5px 3px 20px;
            font-size: 9px;
        }

        .ptsm-container.compact .ptsm-map-btn::before {
            width: 10px;
            height: 10px;
            left: 3px;
        }

        .ptsm-container.compact .ptsm-compact-btn {
            height: 28px;
            font-size: 10px;
            padding: 6px 10px;
        }

        /* Category colors */
        .ptsm-category-allgem .ptsm-category-header { background: linear-gradient(135deg, #007bff 0%, #0056b3 100%); }
        .ptsm-category-allgem .ptsm-category-header:hover { background: linear-gradient(135deg, #0056b3 0%, #004085 100%); }
        .ptsm-category-baustell .ptsm-category-header { background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%); }
        .ptsm-category-baustell .ptsm-category-header:hover { background: linear-gradient(135deg, #1e7e34 0%, #155724 100%); }
        .ptsm-category-blitzer .ptsm-category-header { background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); }
        .ptsm-category-blitzer .ptsm-category-header:hover { background: linear-gradient(135deg, #c82333 0%, #a02834 100%); }
        .ptsm-category-bilder .ptsm-category-header { background: linear-gradient(135deg, #17a2b8 0%, #138496 100%); }
        .ptsm-category-bilder .ptsm-category-header:hover { background: linear-gradient(135deg, #138496 0%, #0f6674 100%); }
        .ptsm-category-geoportal .ptsm-category-header { background: linear-gradient(135deg, #6f42c1 0%, #59359a 100%); }
        .ptsm-category-geoportal .ptsm-category-header:hover { background: linear-gradient(135deg, #59359a 0%, #4c2c85 100%); }
        .ptsm-category-misc .ptsm-category-header { background: linear-gradient(135deg, #fd7e14 0%, #e65100 100%); }
        .ptsm-category-misc .ptsm-category-header:hover { background: linear-gradient(135deg, #e65100 0%, #bf360c 100%); }

        /* Icons */
        .ptsm-google::before { background-image: url(https://i.ibb.co/d0zx6Pdt/google-maps.png); }
        .ptsm-f4map::before { background-image: url(https://i.ibb.co/5WxjKkLp/F-Logo.png); }
        .ptsm-apple::before { background-image: url(https://i.ibb.co/WsH15zC/Apple-Jetzt.png); }
        .ptsm-bing::before { background-image: url(https://i.ibb.co/0LF74p6/bing.png); }
        .ptsm-nrw::before { background-image: url(https://i.ibb.co/37q7H37/nrw.png); }
        .ptsm-osm::before { background-image: url(https://i.ibb.co/20wtGrsL/osm.png); }
        .ptsm-autobahn::before { background-image: url(https://i.ibb.co/2Y3pT8v2/Autobahn-Logo.png); }
        .ptsm-poi-karte::before { background-image: url(https://i.ibb.co/nMRSSKKp/POI-Karte.jpg); }
        .ptsm-poi-base::before { background-image: url(https://i.ibb.co/xS7vJQr8/POI-Base.jpg); }
        .ptsm-viamichelin::before { background-image: url(https://i.ibb.co/RTzJP87C/viamichelin.png); }
        .ptsm-here::before { background-image: url(https://i.ibb.co/MC9JF7T/h-logo.png); }
        .ptsm-mapillary::before { background-image: url(https://i.ibb.co/JWkZnh0X/mapillary.png); }
        .ptsm-osbrowser::before { background-image: url(https://i.ibb.co/RdQSgsY/osb.png); }
        .ptsm-mappy::before { background-image: url(https://i.ibb.co/wrhH7H95/mappy.png); }
        .ptsm-blitzer::before { background-image: url(https://i.ibb.co/gVKMwKS/blitzer.png); }
        .ptsm-bayernatlas::before { background-image: url(https://i.ibb.co/KxnBpv7J/bayernatlas.png); }
        .ptsm-tomtom::before { background-image: url(https://i.ibb.co/hDq5bys/tomtom-icon2.png); }
        .ptsm-basemap-de::before { background-image: url(https://i.ibb.co/V3jJJrb/de-map.png); }
        .ptsm-reporting::before { background-image: url(https://i.ibb.co/rZb76j2/pin.png); }
        .ptsm-kartaview::before { background-image: url(https://i.ibb.co/xgnTMFf/kartaview.png); }
        .ptsm-bayerninfo::before { background-image: url(https://i.ibb.co/R0K3SSs/bayerninfo.png); }
        .ptsm-timonline::before { background-image: url(https://i.ibb.co/bPJ4qRy/das-da2.png); }
        .ptsm-geoadmin::before { background-image: url(https://i.ibb.co/Np5chv4/CH-Icon-20.png); }
        .ptsm-basemap-at::before { background-image: url(https://i.ibb.co/MCKhDSH/AT-Icon.png); }
        .ptsm-adac::before { background-image: url(https://i.ibb.co/6YsGCFy/adac.png); }
        .ptsm-here-edit::before { background-image: url(https://i.ibb.co/VghMgy8/here.png); }
        .ptsm-umsehen::before { background-image: url(https://i.ibb.co/XYqjkYX/umsehen-icon.png); }
        .ptsm-hackintosh::before { background-image: url(https://i.ibb.co/8xP5RyC/Hackintosh.png); }
        .ptsm-archive::before { background-image: url(https://i.ibb.co/QHpvd85/Das-Logo.png); }

        .ptsm-state-info {
            font-size: 9px;
            color: #6c757d;
            font-style: italic;
            text-align: center;
            margin-top: 6px;
            margin-bottom: 4px;
        }

        .ptsm-warning {
            background: #fff8e1;
            border: 1px solid #ffcc02;
            border-radius: 4px;
            padding: 8px;
            margin-top: 8px;
            margin-bottom: 8px;
            display: flex;
            align-items: flex-start;
        }

        .ptsm-warning .w-icon {
            font-size: 14px;
            color: #f57c00;
            margin-right: 6px;
            flex-shrink: 0;
        }

        .ptsm-warning-text {
            font-size: 9px;
            color: #e65100;
            line-height: 1.3;
        }

        @media (max-width: 768px) {
            .ptsm-map-btn { width: calc(50% - 4px); }
        }
        @media (max-width: 480px) {
            .ptsm-map-btn { width: calc(100% - 4px); }
        }
    `;
    document.head.appendChild(style);

    // Create all map buttons
    var btn1 = createMapButton('Google', 'ptsm-google', () => {
        var cz = getCenterZoom();
        // Bessere Google Maps Zoom-Level Umrechnung
        var googleZoom;
        if (cz.zoom >= 20) googleZoom = 19;      // Sehr nah
        else if (cz.zoom >= 18) googleZoom = 17; // Nah
        else if (cz.zoom >= 16) googleZoom = 15; // Mittel-nah
        else if (cz.zoom >= 14) googleZoom = 13; // Mittel
        else if (cz.zoom >= 12) googleZoom = 11; // Mittel-weit
        else if (cz.zoom >= 10) googleZoom = 9;  // Weit
        else googleZoom = Math.max(1, cz.zoom - 2); // Sehr weit

        window.open('https://www.google.com/maps/@' + cz.lat + ',' + cz.lon + ',' + googleZoom + 'z/data=!5m1!1e1', '_blank');
    });

    var btn2 = createMapButton('Bing', 'ptsm-bing', () => {
        var cz = getCenterZoom();
        cz.zoom -= 1;
        window.open('https://www.bing.com/maps/traffic?cp=' + cz.lat + '~' + cz.lon + '&lvl=' + cz.zoom, '_blank');
    });

    var btn3 = createMapButton('Ver. NRW', 'ptsm-nrw', () => {
        var cz = getCenterZoom();
        window.open('https://www.verkehr.nrw/?center=' + cz.lat + ',' + cz.lon + '&zoom=' + cz.zoom + '&layer=Verkehrslage,Baustellen,Haltestellen,Parken,Webcams,Verkehrsmeldungen,ELadesaeulen,Tankstellen&highlightRoute=false', '_blank');
    });

    var btn3a = createMapButton('OSM', 'ptsm-osm', () => {
        var cz = getCenterZoom();
        window.open('https://www.openstreetmap.org/#map=' + cz.zoom + '/' + cz.lat + '/' + cz.lon, '_blank');
    });

    var btn4 = createMapButton('F4 3D Map', 'ptsm-f4map', () => {
        var cz = getCenterZoom();
        window.open('https://demo.f4map.com/#lat=' + cz.lat + '&lon=' + cz.lon + '&zoom=' + cz.zoom, '_blank');
    });

    var btn5 = createMapButton('Apple', 'ptsm-apple', () => {
        var cz = getCenterZoom();
        window.open('https://maps.apple.com/look-around?coordinate=' + cz.lat + '%2C' + cz.lon, '_blank');
    });

    var btn6 = createMapButton('Autobahn', 'ptsm-autobahn', () => {
        var cz = getCenterZoom();
        window.open('https://verkehr.vz-deutschland.de/?layer=raststellen,baustellen,stau,verkehrsmeldungen&zoom=' + cz.zoom + '&lat=' + cz.lat + '&lon=' + cz.lon, '_blank');
    });

    var btn7 = createMapButton('POI Karte', 'ptsm-poi-karte', () => {
        var cz = getCenterZoom();
        // Bessere Zoom-Level-Übertragung: Je höher das Waze-Zoom, desto kleiner der Radius
        var radius;
        if (cz.zoom >= 18) radius = 500;        // Sehr nah - 500m Radius
        else if (cz.zoom >= 16) radius = 1000;   // Nah - 1km Radius
        else if (cz.zoom >= 14) radius = 2500;   // Mittel - 2.5km Radius
        else if (cz.zoom >= 12) radius = 5000;   // Weit - 5km Radius
        else if (cz.zoom >= 10) radius = 10000;  // Sehr weit - 10km Radius
        else radius = 25000;                     // Maximum - 25km Radius

        window.open('https://www.flosm.org/de/POI-Karte.html?lat=' + cz.lat + '&lon=' + cz.lon + '&r=' + radius + '&st=0&sw=speedcamera', '_blank');
    });

    var btn8 = createMapButton('POI Base', 'ptsm-poi-base', () => {
        var cz = getCenterZoom();
        window.open('https://www.poibase.com/de/karte/#/map/coords-' + cz.lon + ',' + cz.lat + '/zoom-' + cz.zoom, '_blank');
    });

    var btn9 = createMapButton('ViaM', 'ptsm-viamichelin', () => {
        var cz = getCenterZoom();
        // ViaMichelin moderne URL-Struktur mit bounds und center
        // Zoom-abhängige Bounds-Berechnung für bessere Darstellung
        var zoomFactor;
        if (cz.zoom >= 18) zoomFactor = 0.001;       // Sehr nah
        else if (cz.zoom >= 16) zoomFactor = 0.002;  // Nah
        else if (cz.zoom >= 14) zoomFactor = 0.005;  // Mittel-nah
        else if (cz.zoom >= 12) zoomFactor = 0.01;   // Mittel
        else if (cz.zoom >= 10) zoomFactor = 0.02;   // Mittel-weit
        else if (cz.zoom >= 8) zoomFactor = 0.05;    // Weit
        else zoomFactor = 0.1;                       // Sehr weit

        // Bounds berechnen (Rechteck um den Mittelpunkt)
        var latMin = (cz.lat - zoomFactor).toFixed(6);
        var latMax = (cz.lat + zoomFactor).toFixed(6);
        var lonMin = (cz.lon - zoomFactor).toFixed(6);
        var lonMax = (cz.lon + zoomFactor).toFixed(6);

        var viaMichelinUrl = 'https://www.viamichelin.de/karten-stadtplan/verkehr?bounds=' +
                            lonMin + '%7E' + latMin + '%7E' + lonMax + '%7E' + latMax +
                            '&center=' + cz.lon.toFixed(6) + '%7E' + cz.lat.toFixed(6) +
                            '&page=1&poiCategories=0&showPolandModal=false';

        window.open(viaMichelinUrl, '_blank');
    });

    var btn10 = createMapButton('Here', 'ptsm-here', () => {
        var cz = getCenterZoom();
        window.open('https://wego.here.com/?map=' + cz.lat + ',' + cz.lon + ',' + cz.zoom + ',normal', '_blank');
    });

    var btn11 = createMapButton('Mapillary', 'ptsm-mapillary', () => {
        var cz = getCenterZoom();
        cz.zoom -= 1;
        window.open('https://www.mapillary.com/app/?lat=' + cz.lat + '&lng=' + cz.lon + '&z=' + cz.zoom, '_blank');
    });

    var btn12 = createMapButton('OSBrowser', 'ptsm-osbrowser', () => {
        var cz = getCenterZoom();
        window.open('https://www.openstreetbrowser.org/#map=' + cz.zoom + '/' + cz.lat + '/' + cz.lon + '&categories=car_maxspeed', '_blank');
    });

    var btn13 = createMapButton('Mappy', 'ptsm-mappy', () => {
        var cz = getCenterZoom();
        window.open('https://en.mappy.com/plan#/' + cz.lat + ',' + cz.lon, '_blank');
    });

    var btn14 = createMapButton('Blitzer.de', 'ptsm-blitzer', () => {
        var cz = getCenterZoom();
        window.open('https://map.atudo.com/v5/?lat=' + cz.lat + '&lng=' + cz.lon + '&zoom=' + cz.zoom, '_blank');
    });

    var btn16 = createMapButton('BY Atlas', 'ptsm-bayernatlas', () => {
        var cz = getCenterZoom();
        cz.zoom -= 5;
        if (typeof proj4 === "undefined") {
            alert('proj4 library not loaded');
            return;
        }
        var firstProj = '+proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs';
        var utm = proj4(firstProj, [cz.lon, cz.lat]);
        window.open('https://geoportal.bayern.de/bayernatlas/index.html?zoom=' + cz.zoom + '&E=' + utm[0] + '&N=' + utm[1], '_blank');
    });

    var btn18 = createMapButton('TomTom', 'ptsm-tomtom', () => {
        var cz = getCenterZoom();
        cz.zoom -= 1;
        window.open('https://plan.tomtom.com/de?p=' + cz.lat + ',' + cz.lon + ',' + cz.zoom + 'z', '_blank');
    });

    var btn19 = createMapButton('Basemap.de', 'ptsm-basemap-de', () => {
        var cz = getCenterZoom();
        cz.zoom = 0.985 * cz.zoom - 1.05;
        window.open('https://basemap.de/viewer?config=' + btoa('{"lat":' + cz.lat + ',"lon":' + cz.lon + ',"zoom":' + cz.zoom + ',"styleID":0,"pitch":0,"bearing":0,"saturation":0,"brightness":0,"hiddenControls":[],"hiddenLayers":[],"changedLayers":[],"hiddenSubGroups":[],"changedSubGroups":[],"externalStyleURL":""}'), '_blank');
    });

    var btn20 = createMapButton('Reporting', 'ptsm-reporting', () => {
        var cz = getCenterZoom();
        cz.zoom -= 1;
        window.open('https://www.waze.com/partnerhub/map-tool?lat=' + cz.lat + '&lon=' + cz.lon + '&zoom=' + cz.zoom, '_blank');
    });

    var btn21 = createMapButton('KartaView', 'ptsm-kartaview', () => {
        var cz = getCenterZoom();
        cz.zoom -= 1;
        window.open('https://kartaview.org/map/@' + cz.lat + ',' + cz.lon + ',' + cz.zoom + 'z', '_blank');
    });

    var btn22 = createMapButton('Bayerninfo', 'ptsm-bayerninfo', () => {
        var cz = getCenterZoom();
        cz.zoom -= 2;
        var now = new Date();
        var then = new Date();
        then.setDate(then.getDate() + 60);
        var mapsUrl = 'https://www.bayerninfo.de/de/baustellenkalender?geo=' + cz.lat + ',' + cz.lon + '&zoom=' + cz.zoom;
        mapsUrl += '&datetimeFrom=' + now.toISOString() + '&datetimeTo=' + then.toISOString();
        window.open(mapsUrl, '_blank');
    });

    var btn30 = createMapButton('TimOnline', 'ptsm-timonline', () => {
        var cz = getCenterZoom();
        cz.zoom = 454959671.96858*Math.exp(-0.693*cz.zoom);
        window.open('https://www.tim-online.nrw.de/tim-online2/?center=' + cz.lat + ',' + cz.lon + '&scale=' + cz.zoom, '_blank');
    });

    var btn31 = createMapButton('GeoAdmin', 'ptsm-geoadmin', () => {
        var cz = getCenterZoom();
        cz.zoom -= 7.2;
        var phi1 = ((cz.lat * 3600) - 169028.66) / 10000;
        var lmd1 = ((cz.lon * 3600) - 26782.5) / 10000;
        var x = 200147.07 + 308807.95 * phi1 + 3745.25 * lmd1 * lmd1 + 76.63 * phi1 * phi1 + 119.79 * phi1 * phi1 * phi1 - 194.56 * lmd1 * lmd1 * phi1;
        var y = 600072.37 + 211455.93 * lmd1 - 10938.51 * lmd1 * phi1 - 0.36 * lmd1 * phi1 * phi1 - 44.54 * lmd1 * lmd1 * lmd1;
        window.open('https://map.geo.admin.ch/?Y=' + y.toFixed(0) + '&X=' + x.toFixed(0) + '&zoom=' + cz.zoom + '&topic=ech&lang=de&bgLayer=ch.swisstopo.pixelkarte-farbe&layers=ch.bfs.gebaeude_wohnungs_register,ch.kantone.cadastralwebmap-farbe,ch.swisstopo.swissimage-product,ch.bfe.ladestellen-elektromobilitaet&catalogNodes=457,532,687,458,477,485,491,510,527,1743&layers_visibility=false,false,true,true&layers_timestamp=,,current', '_blank');
    });

    var btn32 = createMapButton('Basemap', 'ptsm-basemap-at', () => {
        var cz = getCenterZoom();
        var x = cz.lon / 180 * 20037508.34;
        var y = Math.log(Math.tan((90 + cz.lat * 1) * Math.PI / 360)) / Math.PI;
        y *= 20037508.34;
        window.open('https://basemap.at/bmapp/index.html#{"center":[' + x.toFixed(10) + ',' + y.toFixed(10) + '],"zoom":' + cz.zoom + ',"rotation":0,"layers":"1000000000"}', '_blank');
    });

    var btn34 = createMapButton('ADAC', 'ptsm-adac', () => {
        var cz = getCenterZoom();
        // ADAC Maps präzise Bounds-Berechnung basierend auf Bildvergleich
        // Das Problem: ADAC zeigt zu viel östlich und zu nah gezoomt

        var zoomFactor;
        if (cz.zoom >= 19) zoomFactor = 0.002;       // Extrem nah - größerer Bereich
        else if (cz.zoom >= 18) zoomFactor = 0.003;  // Sehr nah
        else if (cz.zoom >= 17) zoomFactor = 0.004;  // Nah+
        else if (cz.zoom >= 16) zoomFactor = 0.006;  // Nah
        else if (cz.zoom >= 15) zoomFactor = 0.008;  // Mittel-nah+
        else if (cz.zoom >= 14) zoomFactor = 0.012;  // Mittel-nah
        else if (cz.zoom >= 13) zoomFactor = 0.016;  // Mittel+
        else if (cz.zoom >= 12) zoomFactor = 0.022;  // Mittel
        else if (cz.zoom >= 11) zoomFactor = 0.030;  // Mittel-weit+
        else if (cz.zoom >= 10) zoomFactor = 0.040;  // Mittel-weit
        else if (cz.zoom >= 9) zoomFactor = 0.060;   // Weit+
        else if (cz.zoom >= 8) zoomFactor = 0.080;   // Weit
        else zoomFactor = 0.120;                     // Sehr weit

        // Korrektur der Verschiebung: ADAC zeigt zu weit östlich und leicht nördlich
        // Daher verschieben wir den Mittelpunkt leicht nach Westen und Süden
        var correctedLat = cz.lat - (zoomFactor * 0.05); // Leichte Südverschiebung
        var correctedLon = cz.lon - (zoomFactor * 0.15); // Leichte Westverschiebung

        var latMin = (correctedLat - zoomFactor).toFixed(6);
        var latMax = (correctedLat + zoomFactor).toFixed(6);
        var lonMin = (correctedLon - zoomFactor).toFixed(6);
        var lonMax = (correctedLon + zoomFactor).toFixed(6);

        var adacUrl = 'https://maps.adac.de/?bounds=' +
                     latMin + ',' + lonMin + '-' + latMax + ',' + lonMax +
                     '&traffic=construction,announcements,flow';

        window.open(adacUrl, '_blank');
    });

    var btn36 = createMapButton('Here Edit', 'ptsm-here-edit', () => {
        var cz = getCenterZoom();
        window.open('https://mapcreator.here.com/?l=' + cz.lat + ',' + cz.lon + ',' + cz.zoom + ',autoselect', '_blank');
    });

    var btn37 = createMapButton('Umsehen', 'ptsm-umsehen', () => {
        var cz = getCenterZoom();
        cz.zoom += 1.0;
        window.open('https://maps.apple.com/?ll=' + cz.lat + ',' + cz.lon + '&z=' + cz.zoom + '&t=m', '_blank');
    });

    var btn39 = createMapButton('Hackintosh', 'ptsm-hackintosh', () => {
        var cz = getCenterZoom();
        window.open('https://lookmap.eu.pythonanywhere.com/#c=' + cz.zoom + '/' + cz.lat + '/' + cz.lon + '/', '_blank');
    });

    var btn40 = createMapButton('Archive', 'ptsm-archive', () => {
        window.open('https://archive.is/', '_blank');
    });

    // Kompakt-Button erstellen
    var compactButton = createCompactButton();

    // Create container
    var container = document.createElement('div');
    container.className = 'ptsm-container';

    // Kompakt-Modus beim Laden anwenden falls gespeichert
    if (loadCompactMode()) {
        container.classList.add('compact');
    }

    // Create categories
    var allgemCategory = createCategory('Allgemeine Karten', 'ptsm-category-allgem', true);
    var baustellCategory = createCategory('Baustellen & Verkehr', 'ptsm-category-baustell', false);
    var blitzerCategory = createCategory('Blitzer & Geschwindigkeit', 'ptsm-category-blitzer', false);
    var bilderCategory = createCategory('Straßenbilder', 'ptsm-category-bilder', false);
    var geoportalCategory = createCategory('Geoportale', 'ptsm-category-geoportal', false);
    var miscCategory = createCategory('Sonstiges', 'ptsm-category-misc', false);

    // Add buttons to categories
    // Allgemeine Karten
    allgemCategory.content.appendChild(btn1);  // Google
    allgemCategory.content.appendChild(btn2);  // Bing
    allgemCategory.content.appendChild(btn3a); // OSM
    allgemCategory.content.appendChild(btn4);  // F4 3D
    allgemCategory.content.appendChild(btn5);  // Apple
    allgemCategory.content.appendChild(btn10); // Here
    allgemCategory.content.appendChild(btn13); // Mappy
    allgemCategory.content.appendChild(btn18); // TomTom
    allgemCategory.content.appendChild(btn9);  // ViaM

    // Baustellen & Verkehr
    baustellCategory.content.appendChild(btn3);  // Ver. NRW
    baustellCategory.content.appendChild(btn6);  // Autobahn
    baustellCategory.content.appendChild(btn34); // ADAC

    // Blitzer & Geschwindigkeit
    blitzerCategory.content.appendChild(btn14); // Blitzer.de
    blitzerCategory.content.appendChild(btn7);  // POI Karte
    blitzerCategory.content.appendChild(btn8);  // POI Base

    // Straßenbilder
    bilderCategory.content.appendChild(btn11); // Mapillary
    bilderCategory.content.appendChild(btn21); // KartaView
    bilderCategory.content.appendChild(btn12); // OSBrowser

    // Geoportale
    geoportalCategory.content.appendChild(btn19); // Basemap DE
    geoportalCategory.content.appendChild(btn31); // GeoAdmin
    geoportalCategory.content.appendChild(btn32); // Basemap AT
    geoportalCategory.content.appendChild(btn30); // TimOnline
    geoportalCategory.content.appendChild(btn16); // BY Atlas
    geoportalCategory.content.appendChild(btn22); // Bayerninfo

    // Sonstiges
    miscCategory.content.appendChild(btn20); // Reporting
    miscCategory.content.appendChild(btn36); // Here Edit
    miscCategory.content.appendChild(btn37); // Umsehen
    miscCategory.content.appendChild(btn39); // Hackintosh
    miscCategory.content.appendChild(btn40); // Archive
    miscCategory.content.appendChild(compactButton); // Kompakt-Button hinzufügen

    // Add categories to container
    container.appendChild(allgemCategory.category);
    container.appendChild(baustellCategory.category);
    container.appendChild(blitzerCategory.category);
    container.appendChild(bilderCategory.category);
    container.appendChild(geoportalCategory.category);
    container.appendChild(miscCategory.category);

    // Add version info with update link
    var versionInfo = document.createElement('div');
    versionInfo.className = 'ptsm-state-info';

    var updateLink = document.createElement('a');
    updateLink.href = 'https://greasyfork.org/de/scripts/448378-wme-permalink-to-several-maps-dach';
    updateLink.target = '_blank';
    updateLink.textContent = 'Auf Updates überprüfen / V' + ptsmVersion;
    updateLink.style.cssText = 'color: #007bff; text-decoration: none; font-size: 9px;';
    updateLink.addEventListener('mouseover', function() {
        this.style.textDecoration = 'underline';
    });
    updateLink.addEventListener('mouseout', function() {
        this.style.textDecoration = 'none';
    });

    versionInfo.appendChild(updateLink);
    container.appendChild(versionInfo);

    // Add warning about external services
    var warning = document.createElement('div');
    warning.className = 'ptsm-warning';
    warning.innerHTML = '<div class="w-icon">⚠</div><div class="ptsm-warning-text">Hinweis: Einige der externen Karten sind als Informationsquelle zur Kartenbearbeitung nicht zulässig!</div>';
    container.appendChild(warning);

    // Try to use WME Userscript API first, fallback to manual insertion
    try {
        if (W.userscripts && W.userscripts.registerSidebarTab) {
            const result = W.userscripts.registerSidebarTab('wme-ptsm-dach');
            var tabLabel = result.tabLabel;
            var tabPane = result.tabPane;

            tabLabel.textContent = 'PTSM';
            tabLabel.title = 'WME Permalink to several Maps DACH';

            tabPane.appendChild(container);
        } else {
            // Fallback: Try to add to existing sidebar
            var sidebar = document.querySelector('#sidebar') || document.querySelector('.sidebar');
            if (sidebar) {
                // Create a collapsible section
                var section = document.createElement('div');
                section.style.cssText = 'border: 1px solid #ccc; margin: 10px 0; border-radius: 5px;';

                var header = document.createElement('div');
                header.style.cssText = 'background: #f5f5f5; padding: 10px; cursor: pointer; font-weight: bold; user-select: none;';
                header.textContent = 'PTSM DACH';

                var content = document.createElement('div');
                content.style.display = 'none';
                content.appendChild(container);

                header.addEventListener('click', function() {
                    content.style.display = content.style.display === 'none' ? 'block' : 'none';
                });

                section.appendChild(header);
                section.appendChild(content);
                sidebar.appendChild(section);
            } else {
                // Last resort: Create floating panel
                var floatingPanel = document.createElement('div');
                floatingPanel.style.cssText = `
                    position: fixed;
                    top: 100px;
                    right: 10px;
                    width: 300px;
                    max-height: 70vh;
                    overflow-y: auto;
                    background: white;
                    border: 1px solid #ccc;
                    border-radius: 8px;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                    z-index: 10000;
                    resize: both;
                `;

                var panelHeader = document.createElement('div');
                panelHeader.style.cssText = `
                    background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
                    color: white;
                    padding: 10px;
                    font-weight: bold;
                    cursor: move;
                    user-select: none;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                `;
                panelHeader.innerHTML = 'PTSM DACH <span style="cursor: pointer; font-size: 18px;">&times;</span>';

                var closeBtn = panelHeader.querySelector('span');
                closeBtn.addEventListener('click', function() {
                    floatingPanel.style.display = 'none';
                });

                // Make panel draggable
                var isDragging = false;
                var startX, startY, startLeft, startTop;

                panelHeader.addEventListener('mousedown', function(e) {
                    if (e.target === closeBtn) return;
                    isDragging = true;
                    startX = e.clientX;
                    startY = e.clientY;
                    startLeft = parseInt(window.getComputedStyle(floatingPanel).left, 10);
                    startTop = parseInt(window.getComputedStyle(floatingPanel).top, 10);
                    document.addEventListener('mousemove', drag);
                    document.addEventListener('mouseup', stopDrag);
                });

                function drag(e) {
                    if (!isDragging) return;
                    var newLeft = startLeft + e.clientX - startX;
                    var newTop = startTop + e.clientY - startY;
                    floatingPanel.style.left = newLeft + 'px';
                    floatingPanel.style.top = newTop + 'px';
                }

                function stopDrag() {
                    isDragging = false;
                    document.removeEventListener('mousemove', drag);
                    document.removeEventListener('mouseup', stopDrag);
                }

                floatingPanel.appendChild(panelHeader);
                floatingPanel.appendChild(container);
                document.body.appendChild(floatingPanel);
            }
        }
    } catch (e) {
        console.error('PTSM: Error adding to sidebar:', e);
    }

    console.log('PTSM DACH v' + ptsmVersion + ' loaded successfully');

    // Update-Popup anzeigen (einmalig pro Version)
    setTimeout(showUpdatePopup, 1000);
}

// Initialize when page loads
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', addButtons);
} else {
    setTimeout(addButtons, 1000);
}

// Also try to initialize when Waze editor loads
if (typeof W !== 'undefined') {
    if (W.loginManager && W.loginManager.events) {
        W.loginManager.events.register('loginStateChanged', null, addButtons);
    }
    setTimeout(addButtons, 2000);
} else {
    // Wait for Waze object to be available
    var checkForWaze = setInterval(function() {
        if (typeof W !== 'undefined' && W.loginManager) {
            clearInterval(checkForWaze);
            setTimeout(addButtons, 1000);
        }
    }, 500);
}