WME Permalink to several Maps DACH

WME PTSM für Deutschland Österreich Schweiz - Dropdown Design

Versión del día 31/05/2025. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name WME Permalink to several Maps DACH
// @description WME PTSM für Deutschland Österreich Schweiz - Dropdown Design
// @namespace https://greasyforks.org/de/users/863740-horst-wittlich
// @version 2025.06.02
// @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 */
/* global W */
/* global proj4 */

var ptsmVersion = '2025.06.02'

const ScriptName = GM_info.script.name;
const ScriptVersion = GM_info.script.version;
let ChangeLog = "WME Permalink to several Maps wurde Aktualisiert " + ScriptVersion + "<br />";
ChangeLog = ChangeLog + "<br /><b>Updated: Dropdown Menu Design & CSP Compliance</b>";

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 addButtons() {
    if (document.getElementById('user-info') == null) {
        setTimeout(addButtons, 500)
        console.log('user-info element not yet available, page still loading')
        return
    }
    
    if (!W.loginManager.user) {
        W.loginManager.events.register('login', null, addButtons)
        W.loginManager.events.register('loginStatus', null, addButtons)
        if (!W.loginManager.user) {
            return
        }
    }
    
    if ("undefined" == typeof proj4) {
        var script = document.createElement('script')
        script.type = 'text/javascript'
        script.src = 'https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js'
        document.getElementsByTagName('head')[0].appendChild(script)
    }

    // Modern Dropdown CSS styling - CSP compliant
    var style = document.createElement('style')
    style.type = 'text/css'
    style.textContent = `
        /* Container Styling */
        #sidepanel-ptsm {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            padding: 16px;
            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
            border-radius: 8px;
            margin: 8px;
        }

        /* Dropdown Category Styling */
        .ptsm-category {
            margin-bottom: 12px;
            border-radius: 8px;
            overflow: hidden;
            background: #ffffff;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
            border: 1px solid #e3e6ea;
        }

        /* Category Header (Dropdown Toggle) */
        .ptsm-category-header {
            position: relative;
            display: flex;
            align-items: center;
            justify-content: space-between;
            width: 100%;
            padding: 12px 16px;
            background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
            color: white;
            border: none;
            cursor: pointer;
            font-weight: 600;
            font-size: 13px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .ptsm-category-header:hover {
            background: linear-gradient(135deg, #5a6268 0%, #343a40 100%);
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
        }

        /* Dropdown Arrow */
        .ptsm-dropdown-arrow {
            width: 0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 8px solid white;
            transition: transform 0.3s ease;
            margin-left: 8px;
        }

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

        /* Category Content (Dropdown Body) */
        .ptsm-category-content {
            max-height: 0;
            overflow: hidden;
            padding: 0 12px;
            background: #ffffff;
            transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), padding 0.3s ease;
        }

        .ptsm-category.open .ptsm-category-content {
            max-height: 800px;
            padding: 16px 12px;
        }

        /* Map Service Buttons */
        .ptsm-map-btn {
            position: relative;
            display: inline-flex;
            align-items: center;
            justify-content: flex-start;
            width: 48%;
            height: 36px;
            margin: 1%;
            padding: 6px 10px 6px 32px;
            
            background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%);
            border: 1px solid #dee2e6;
            border-radius: 8px;
            
            font-size: 12px;
            font-weight: 500;
            color: #495057;
            text-align: left;
            text-decoration: none;
            
            cursor: pointer;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
        }

        /* Icon positioning for map buttons */
        .ptsm-map-btn::before {
            content: '';
            position: absolute;
            left: 8px;
            top: 50%;
            transform: translateY(-50%);
            width: 16px;
            height: 16px;
            background-size: contain;
            background-repeat: no-repeat;
            background-position: center;
        }

        /* Map Button Hover Effects */
        .ptsm-map-btn:hover {
            transform: translateY(-1px);
            background: linear-gradient(145deg, #ffffff 0%, #f1f3f4 100%);
            border-color: #007bff;
            color: #0056b3;
            box-shadow: 0 4px 12px rgba(0, 123, 255, 0.15);
        }

        .ptsm-map-btn:active {
            transform: translateY(0);
            box-shadow: 0 1px 3px rgba(0, 123, 255, 0.2);
        }

        /* Category Color Coding */
        .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%);
        }

        /* Individual button icon styles */
        .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); }

        /* Warning section styling */
        .ptsm-warning {
            background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
            border: 2px solid #ffc107;
            border-radius: 8px;
            padding: 12px;
            margin-top: 20px;
            display: flex;
            align-items: flex-start;
        }

        .ptsm-warning .w-icon {
            font-size: 20px;
            color: #856404;
            margin-right: 10px;
            flex-shrink: 0;
        }

        .ptsm-warning-text {
            font-size: 12px;
            color: #856404;
            line-height: 1.4;
        }

        /* Update link styling */
        .ptsm-update-link {
            display: inline-block;
            color: #007bff;
            text-decoration: none;
            font-weight: 600;
            font-size: 13px;
            padding: 12px 0;
            border-bottom: 2px solid transparent;
            transition: all 0.2s ease;
        }

        .ptsm-update-link:hover {
            color: #0056b3;
            border-bottom-color: #007bff;
            text-decoration: none;
        }

        /* Animation for smooth dropdown */
        @keyframes slideDown {
            from {
                opacity: 0;
                transform: translateY(-10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .ptsm-category.open .ptsm-category-content {
            animation: slideDown 0.3s ease-out;
        }

        /* State persistence info */
        .ptsm-state-info {
            font-size: 11px;
            color: #6c757d;
            font-style: italic;
            text-align: center;
            margin-top: 8px;
            padding: 4px;
        }

        /* Responsive design */
        @media (max-width: 768px) {
            .ptsm-map-btn {
                width: 98%;
                margin: 1%;
            }
        }
    `;
    document.getElementsByTagName('head')[0].appendChild(style)

    // Button creation functions with proper event handlers
    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;
    }

    // LocalStorage functions for saving dropdown states
    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, states will not be saved');
        }
    }

    function loadDropdownStates() {
        try {
            const saved = localStorage.getItem('wme-ptsm-dropdown-states');
            return saved ? JSON.parse(saved) : {};
        } catch (e) {
            console.log('PTSM: Error loading saved states, using defaults');
            return {};
        }
    }

    function createCategory(title, className, defaultOpen = false) {
        const savedStates = loadDropdownStates();
        const categoryKey = className.replace('ptsm-category-', '');
        
        // Use saved state if available, otherwise use default
        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';
        
        // Toggle functionality with state saving
        header.addEventListener('click', function() {
            category.classList.toggle('open');
            // Save state after toggle
            setTimeout(saveDropdownStates, 100);
        });
        
        category.appendChild(header);
        category.appendChild(content);
        
        return { category, content };
    }

    // Create all map service buttons
    var btn1 = createMapButton('Google', 'ptsm-google', () => {
        var cz = getCenterZoom()
        var mapsUrl = 'https://www.google.com/maps/@' + cz.lat + ',' + cz.lon + ',' + cz.zoom + 'z/data=!5m1!1e1'
        window.open(mapsUrl, '_blank');
    });

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

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

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

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

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

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

    var btn7 = createMapButton('POI Karte', 'ptsm-poi-karte', () => {
        var cz = getCenterZoom();
        var newZoom = cz.zoom + 600;
        var mapsUrl = 'https://www.flosm.org/de/POI-Karte.html?lat=' + cz.lat + '&lon=' + cz.lon + '&r=' + newZoom + '&st=0&sw=speedcamera';
        window.open(mapsUrl, '_blank');
    });

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

    var btn9 = createMapButton('ViaM', 'ptsm-viamichelin', () => {
        var cz = getCenterZoom()
        cz.zoom -= 1
        var mapsUrl = 'https://www.viamichelin.de/karten-stadtplan/verkehr?bounds=' + cz.lon*1.0001 + '~' + cz.lat*1.0001 + '~' + cz.lon *0.9999 + '~' + cz.lat*0.9999 + '¢er=' + cz.lon + '~' +cz.lat+ '&detailedView=true&itinerary=&page=1&poiCategories=0'
        window.open(mapsUrl, '_blank');
    });

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

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

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

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

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

    var btn16 = createMapButton('BY Atlas', 'ptsm-bayernatlas', () => {
        var cz = getCenterZoom()
        cz.zoom -= 5
        if (!proj4) {
            console.log('proj4 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])
        var mapsUrl = 'https://geoportal.bayern.de/bayernatlas/index.html?zoom=' + cz.zoom + '&E=' + utm[0] + '&N=' + utm[1]
        window.open(mapsUrl, '_blank');
    });

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

    var btn19 = createMapButton('Basemap.de', 'ptsm-basemap-de', () => {
        var cz = getCenterZoom()
        cz.zoom = 0.985 * cz.zoom - 1.05
        var mapsUrl = '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":""}')
        window.open(mapsUrl, '_blank');
    });

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

    var btn21 = createMapButton('KartaView', 'ptsm-kartaview', () => {
        var cz = getCenterZoom()
        cz.zoom -= 1
        var mapsUrl = 'https://kartaview.org/map/@' + cz.lat + ',' + cz.lon + ',' + cz.zoom + 'z'
        window.open(mapsUrl, '_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)
        let mapsUrl = 'https://www.bayerninfo.de/de/baustellenkalender?geo=' + cz.lat + ',' + cz.lon + '&zoom=' + cz.zoom
        mapsUrl = 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)
        var mapsUrl = 'https://www.tim-online.nrw.de/tim-online2/?center=' + cz.lat + ',' + cz.lon + '&scale=' + cz.zoom
        window.open(mapsUrl, '_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
        var mapsUrl = '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';
        window.open(mapsUrl,'_blank');
    });

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

    var btn34 = createMapButton('ADAC', 'ptsm-adac', () => {
        var cz = getCenterZoom()
        var mapsUrl = 'https://maps.adac.de/show?place=' + cz.lat + '_' + cz.lon + '_2&traffic=announcements%2Cconstruction%2Cflow'
        window.open(mapsUrl, '_blank');
    });

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

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

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

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

    // Create tab structure
    let userTabs = document.getElementById('user-info')
    let navTabs = document.getElementsByClassName('nav-tabs', userTabs)[0]
    let tabContent = document.getElementsByClassName('tab-content', userTabs)[0]
    
    let newtab = document.createElement('li')
    newtab.innerHTML = '<a href="#sidepanel-ptsm" data-toggle="tab">PTSM</a>'
    navTabs.appendChild(newtab)

    let addon = document.createElement('section')
    addon.id = 'sidepanel-ptsm'
    addon.className = 'tab-pane'
    tabContent.appendChild(addon)

    // Create update link section
    let divInfo = document.createElement('div');
    divInfo.id = 'ptsm-Info';
    let updateLink = document.createElement('a');
    updateLink.href = 'https://greasyforks.org/de/scripts/448378-wme-permalink-to-several-maps-dach';
    updateLink.target = '_blank';
    updateLink.className = 'ptsm-update-link';
    updateLink.textContent = 'Auf Updates überprüfen / V' + ptsmVersion;
    divInfo.appendChild(updateLink);
    
    // Add state persistence info
    let stateInfo = document.createElement('div');
    stateInfo.className = 'ptsm-state-info';
    stateInfo.textContent = 'Menü-Zustände werden automatisch gespeichert';
    divInfo.appendChild(stateInfo);
    
    addon.appendChild(divInfo);

    // Create dropdown categories
    var allgemCategory = createCategory('ALLGEMEINE KARTEN', 'ptsm-category-allgem', true);
    allgemCategory.content.appendChild(btn1);   // Google
    allgemCategory.content.appendChild(btn3a);  // OSM
    allgemCategory.content.appendChild(btn9);   // ViaM
    allgemCategory.content.appendChild(btn10);  // HERE
    allgemCategory.content.appendChild(btn13);  // MAPPY
    allgemCategory.content.appendChild(btn18);  // TOMTOM
    allgemCategory.content.appendChild(btn5);   // Apple
    allgemCategory.content.appendChild(btn4);   // F4Maps
    allgemCategory.content.appendChild(btn34);  // ADAC
    addon.appendChild(allgemCategory.category);

    var baustellCategory = createCategory('BAUSTELLEN', 'ptsm-category-baustell');
    baustellCategory.content.appendChild(btn6);  // Autobahn GMBH
    baustellCategory.content.appendChild(btn2);  // BING
    baustellCategory.content.appendChild(btn3);  // Verkehr NRW
    baustellCategory.content.appendChild(btn22); // BAYERNINFO
    addon.appendChild(baustellCategory.category);

    var blitzerCategory = createCategory('BLITZER', 'ptsm-category-blitzer');
    blitzerCategory.content.appendChild(btn14); // Blitzer.de
    blitzerCategory.content.appendChild(btn7);  // POI Karte
    blitzerCategory.content.appendChild(btn8);  // POI Base
    addon.appendChild(blitzerCategory.category);

    var bilderCategory = createCategory('GESCHWINDIGKEITEN / BILDER', 'ptsm-category-bilder');
    bilderCategory.content.appendChild(btn11); // MAPILLARY
    bilderCategory.content.appendChild(btn21); // KARTAVIEW
    bilderCategory.content.appendChild(btn12); // OSBROWSER
    addon.appendChild(bilderCategory.category);

    var geoportalCategory = createCategory('GEOPORTALE', 'ptsm-category-geoportal');
    geoportalCategory.content.appendChild(btn19); // Basemap.de
    geoportalCategory.content.appendChild(btn32); // Basemap.at
    geoportalCategory.content.appendChild(btn31); // GEOADMIN
    geoportalCategory.content.appendChild(btn30); // TIM
    geoportalCategory.content.appendChild(btn16); // BAYERNATLAS
    addon.appendChild(geoportalCategory.category);

    var miscCategory = createCategory('MISC', 'ptsm-category-misc');
    miscCategory.content.appendChild(btn20); // Waze Report
    miscCategory.content.appendChild(btn36); // Here Editor
    miscCategory.content.appendChild(btn37); // Apple Umsehen
    miscCategory.content.appendChild(btn39); // Appetize Emulator
    miscCategory.content.appendChild(btn40); // Archive.is
    addon.appendChild(miscCategory.category);

    // Create warning section
    let warningDiv = document.createElement('div');
    warningDiv.className = 'ptsm-warning';
    warningDiv.innerHTML = `
        <i class="w-icon w-icon-warning"></i>
        <div class="ptsm-warning-text">
            Hinweis: Einige der externen Karten sind als Informationsquelle zur Kartenbearbeitung nicht zulässig!
        </div>
    `;
    addon.appendChild(warningDiv);
    
    // Show update notification for new version
    showUpdateNotification();
}

addButtons()
长期地址
遇到问题?请前往 GitHub 提 Issues。