dynamic-tag

Von $1

    Inhaltsverzeichnis
    keine Gliederung

    Version seit 19:48, 11 Aug 2026

    zu dieser Version.

    Zurück zu Versionshistorie.

    Zeige aktuelle Version

    /* TAG SPHERE - Fixed Overlay & Mobile Edition */
    
    /* FULLSCREEN OVERLAY - deckt ALLES ab */
    #tagSphereOverlay {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(40, 40, 50, 0.88);
        z-index: 2147483640;
        transition: opacity 0.4s ease;
        opacity: 0;
    }
    
    #tagSphereOverlay.active {
        display: block;
        opacity: 1;
    }
    
    /* SPHERE CONTAINER */
    #tagSphere {
        position: fixed;
        bottom: 180px;
        left: 20px;
        width: 280px;
        height: 280px;
        z-index: 2147483647;
        cursor: move;
        cursor: -webkit-grab;
        cursor: -moz-grab;
        opacity: 0;
        visibility: hidden;
        -webkit-transition: opacity 0.4s ease, visibility 0.4s ease, width 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55), height 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55), bottom 0.6s ease, left 0.6s ease;
        transition: opacity 0.4s ease, visibility 0.4s ease, width 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55), height 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55), bottom 0.6s ease, left 0.6s ease;
        border: 3px solid #333333;
        border-radius: 12px;
        background: rgba(250, 250, 250, 0.85);
        box-shadow: 0 8px 32px rgba(0,0,0,0.25);
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        touch-action: none;
    }
    
    #tagSphere.visible {
        opacity: 1;
        visibility: visible;
    }
    
    /* EXPLODED = grosse Ansicht */
    #tagSphere.exploded {
        width: 90vw;
        height: 85vh;
        max-width: 800px;
        max-height: 800px;
        bottom: 50%;
        left: 50%;
        -webkit-transform: translate(-50%, 50%);
        transform: translate(-50%, 50%);
        border-width: 4px;
        background: rgba(250, 250, 252, 0.97);
        box-shadow: 0 20px 80px rgba(0,0,0,0.5);
    }
    
    #tagSphere.mode-tags.exploded {
        border-color: #0066CC;
    }
    
    #tagSphere.mode-pages.exploded {
        border-color: #CC0000;
    }
    
    #tagSphere.dragging {
        cursor: -webkit-grabbing !important;
        cursor: -moz-grabbing !important;
        cursor: grabbing !important;
    }
    
    #tagSphere.loading {
        cursor: wait;
        border-color: #FF6600;
    }
    
    /* CENTER ICON */
    #tagSphere .sphere-center {
        position: absolute;
        left: 50%;
        top: 50%;
        margin-left: -25px;
        margin-top: -25px;
        width: 50px;
        height: 50px;
        font-size: 40px;
        line-height: 50px;
        text-align: center;
        z-index: 10;
        opacity: 0.15;
        filter: alpha(opacity=15);
        pointer-events: none;
        -webkit-animation: breathe 4s ease-in-out infinite;
        animation: breathe 4s ease-in-out infinite;
    }
    
    @-webkit-keyframes breathe {
        0%, 100% { opacity: 0.15; -webkit-transform: scale(1) rotate(0deg); }
        50% { opacity: 0.3; -webkit-transform: scale(1.15) rotate(180deg); }
    }
    @keyframes breathe {
        0%, 100% { opacity: 0.15; transform: scale(1) rotate(0deg); }
        50% { opacity: 0.3; transform: scale(1.15) rotate(180deg); }
    }
    
    #tagSphere.exploded .sphere-center {
        opacity: 0.08;
        filter: alpha(opacity=8);
    }
    
    #tagSphere.loading .sphere-center {
        color: #FF6600;
        opacity: 0.8;
        -webkit-animation: spin 2s linear infinite;
        animation: spin 2s linear infinite;
    }
    
    @-webkit-keyframes spin {
        from { -webkit-transform: rotate(0deg); }
        to { -webkit-transform: rotate(360deg); }
    }
    @keyframes spin {
        from { transform: rotate(0deg); }
        to { transform: rotate(360deg); }
    }
    
    /* CLOSE BUTTON */
    #tagSphere .close-btn {
        position: absolute;
        top: 15px;
        right: 15px;
        width: 36px;
        height: 36px;
        background: linear-gradient(135deg, #FF0000 0%, #CC0000 100%);
        color: #FFFFFF;
        border: 3px solid #FFFFFF;
        border-radius: 50%;
        font-size: 22px;
        font-weight: bold;
        line-height: 30px;
        text-align: center;
        cursor: pointer;
        z-index: 9999;
        opacity: 0;
        visibility: hidden;
        -webkit-transition: opacity 0.3s ease, visibility 0.3s ease, -webkit-transform 0.2s ease;
        transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.2s ease;
        box-shadow: 0 4px 16px rgba(0,0,0,0.5);
    }
    
    #tagSphere.exploded .close-btn {
        opacity: 1;
        visibility: visible;
    }
    
    #tagSphere .close-btn:hover {
        background: linear-gradient(135deg, #FF3333 0%, #AA0000 100%);
        -webkit-transform: scale(1.25) rotate(90deg);
        transform: scale(1.25) rotate(90deg);
    }
    
    /* TAG ITEMS */
    #tagSphere .tag-item {
        position: absolute;
        padding: 9px 16px;
        white-space: nowrap;
        font-size: 13px;
        font-weight: 700;
        text-decoration: none;
        border: 2px solid;
        border-radius: 20px;
        cursor: pointer;
        min-width: 50px;
        text-align: center;
        -webkit-transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
        transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
        pointer-events: auto;
        z-index: 100;
        text-shadow: 0 2px 4px rgba(0,0,0,0.3);
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        touch-action: none;
    }
    
    /* ATOM FLOAT ANIMATIONS */
    #tagSphere .tag-item.atom-0 { -webkit-animation: float-0 4.2s ease-in-out infinite; animation: float-0 4.2s ease-in-out infinite; }
    #tagSphere .tag-item.atom-1 { -webkit-animation: float-1 5.1s ease-in-out infinite; animation: float-1 5.1s ease-in-out infinite; }
    #tagSphere .tag-item.atom-2 { -webkit-animation: float-2 4.7s ease-in-out infinite; animation: float-2 4.7s ease-in-out infinite; }
    #tagSphere .tag-item.atom-3 { -webkit-animation: float-3 5.5s ease-in-out infinite; animation: float-3 5.5s ease-in-out infinite; }
    #tagSphere .tag-item.atom-4 { -webkit-animation: float-4 4.9s ease-in-out infinite; animation: float-4 4.9s ease-in-out infinite; }
    #tagSphere .tag-item.atom-5 { -webkit-animation: float-5 5.3s ease-in-out infinite; animation: float-5 5.3s ease-in-out infinite; }
    #tagSphere .tag-item.atom-6 { -webkit-animation: float-6 4.5s ease-in-out infinite; animation: float-6 4.5s ease-in-out infinite; }
    #tagSphere .tag-item.atom-7 { -webkit-animation: float-7 5.8s ease-in-out infinite; animation: float-7 5.8s ease-in-out infinite; }
    
    @-webkit-keyframes float-0 { 0%,100%{-webkit-transform:translate(0,0) rotate(0)}25%{-webkit-transform:translate(4px,-5px) rotate(2deg)}50%{-webkit-transform:translate(-3px,-7px) rotate(-2deg)}75%{-webkit-transform:translate(-5px,-3px) rotate(3deg)} }
    @keyframes float-0 { 0%,100%{transform:translate(0,0) rotate(0)}25%{transform:translate(4px,-5px) rotate(2deg)}50%{transform:translate(-3px,-7px) rotate(-2deg)}75%{transform:translate(-5px,-3px) rotate(3deg)} }
    @-webkit-keyframes float-1 { 0%,100%{-webkit-transform:translate(0,0)}25%{-webkit-transform:translate(-4px,-6px) rotate(-2deg)}50%{-webkit-transform:translate(5px,-4px) rotate(2deg)}75%{-webkit-transform:translate(3px,-7px) rotate(-3deg)} }
    @keyframes float-1 { 0%,100%{transform:translate(0,0)}25%{transform:translate(-4px,-6px) rotate(-2deg)}50%{transform:translate(5px,-4px) rotate(2deg)}75%{transform:translate(3px,-7px) rotate(-3deg)} }
    @-webkit-keyframes float-2 { 0%,100%{-webkit-transform:translate(0,0)}25%{-webkit-transform:translate(5px,-4px) rotate(3deg)}50%{-webkit-transform:translate(-4px,-8px) rotate(-2deg)}75%{-webkit-transform:translate(4px,-5px) rotate(2deg)} }
    @keyframes float-2 { 0%,100%{transform:translate(0,0)}25%{transform:translate(5px,-4px) rotate(3deg)}50%{transform:translate(-4px,-8px) rotate(-2deg)}75%{transform:translate(4px,-5px) rotate(2deg)} }
    @-webkit-keyframes float-3 { 0%,100%{-webkit-transform:translate(0,0)}25%{-webkit-transform:translate(-5px,-7px) rotate(-3deg)}50%{-webkit-transform:translate(4px,-5px) rotate(2deg)}75%{-webkit-transform:translate(-3px,-6px) rotate(-2deg)} }
    @keyframes float-3 { 0%,100%{transform:translate(0,0)}25%{transform:translate(-5px,-7px) rotate(-3deg)}50%{transform:translate(4px,-5px) rotate(2deg)}75%{transform:translate(-3px,-6px) rotate(-2deg)} }
    @-webkit-keyframes float-4 { 0%,100%{-webkit-transform:translate(0,0)}25%{-webkit-transform:translate(4px,-6px) rotate(2deg)}50%{-webkit-transform:translate(-5px,-5px) rotate(-3deg)}75%{-webkit-transform:translate(5px,-7px) rotate(3deg)} }
    @keyframes float-4 { 0%,100%{transform:translate(0,0)}25%{transform:translate(4px,-6px) rotate(2deg)}50%{transform:translate(-5px,-5px) rotate(-3deg)}75%{transform:translate(5px,-7px) rotate(3deg)} }
    @-webkit-keyframes float-5 { 0%,100%{-webkit-transform:translate(0,0)}25%{-webkit-transform:translate(-4px,-5px) rotate(-2deg)}50%{-webkit-transform:translate(5px,-7px) rotate(3deg)}75%{-webkit-transform:translate(-5px,-4px) rotate(-3deg)} }
    @keyframes float-5 { 0%,100%{transform:translate(0,0)}25%{transform:translate(-4px,-5px) rotate(-2deg)}50%{transform:translate(5px,-7px) rotate(3deg)}75%{transform:translate(-5px,-4px) rotate(-3deg)} }
    @-webkit-keyframes float-6 { 0%,100%{-webkit-transform:translate(0,0)}25%{-webkit-transform:translate(5px,-7px) rotate(3deg)}50%{-webkit-transform:translate(-4px,-6px) rotate(-2deg)}75%{-webkit-transform:translate(4px,-4px) rotate(2deg)} }
    @keyframes float-6 { 0%,100%{transform:translate(0,0)}25%{transform:translate(5px,-7px) rotate(3deg)}50%{transform:translate(-4px,-6px) rotate(-2deg)}75%{transform:translate(4px,-4px) rotate(2deg)} }
    @-webkit-keyframes float-7 { 0%,100%{-webkit-transform:translate(0,0)}25%{-webkit-transform:translate(-4px,-4px) rotate(-2deg)}50%{-webkit-transform:translate(3px,-6px) rotate(1deg)}75%{-webkit-transform:translate(-3px,-5px) rotate(-1deg)} }
    @keyframes float-7 { 0%,100%{transform:translate(0,0)}25%{transform:translate(-4px,-4px) rotate(-2deg)}50%{transform:translate(3px,-6px) rotate(1deg)}75%{transform:translate(-3px,-5px) rotate(-1deg)} }
    
    /* TAG FARBEN - BLAU */
    #tagSphere.mode-tags .tag-item {
        color: #FFFFFF;
        border-color: #0066CC;
        background: linear-gradient(135deg, #0066CC 0%, #0044AA 100%);
        box-shadow: 0 3px 10px rgba(0,102,204,0.4);
    }
    
    #tagSphere.mode-tags .tag-item:hover {
        background: linear-gradient(135deg, #0088FF 0%, #0066CC 100%);
        border-color: #0044AA;
        box-shadow: 0 8px 24px rgba(0,102,204,0.6);
        z-index: 9998 !important;
    }
    
    /* SEITEN FARBEN - ROT */
    #tagSphere.mode-pages .tag-item {
        color: #FFFFFF;
        border-color: #CC0000;
        background: linear-gradient(135deg, #CC0000 0%, #AA0000 100%);
        box-shadow: 0 3px 10px rgba(204,0,0,0.4);
    }
    
    #tagSphere.mode-pages .tag-item:hover {
        background: linear-gradient(135deg, #FF3333 0%, #CC0000 100%);
        border-color: #AA0000;
        box-shadow: 0 8px 24px rgba(204,0,0,0.6);
        z-index: 9998 !important;
    }
    
    #tagSphere.mode-pages .tag-item.popular {
        -webkit-animation: glow-popular 3s ease-in-out infinite;
        animation: glow-popular 3s ease-in-out infinite;
    }
    
    @keyframes glow-popular {
        0%, 100% { box-shadow: 0 3px 10px rgba(204,0,0,0.4); }
        50% { box-shadow: 0 6px 24px rgba(204,0,0,0.7); }
    }
    
    /* MOBILE RESPONSIVE */
    @media screen and (max-width: 768px) {
        #tagSphere {
            bottom: 120px;
            left: 10px;
            width: 220px;
            height: 220px;
        }
        
        #tagSphere.exploded {
            width: 95vw;
            height: 80vh;
            bottom: 50%;
            left: 50%;
        }
        
        #tagSphere .tag-item {
            padding: 10px 18px;
            font-size: 14px;
            min-width: 60px;
        }
        
        #tagSphere .close-btn {
            width: 44px;
            height: 44px;
            font-size: 26px;
            line-height: 38px;
            top: 10px;
            right: 10px;
        }
    }
    
    (function() {
    
    /* =========================================================================
       TAG SPHERE - Fixed Overlay & Mobile Touch
       ========================================================================= */
    
    var c = null;
    var overlay = null;
    var center = null;
    var closeBtn = null;
    var items = [];
    var angleX = 0;
    var angleY = 0;
    var baseRadius = 120;
    var exploded = false;
    var drag = false;
    var lastX = 0;
    var lastY = 0;
    var visible = false;
    var loading = false;
    var mode = 'tags';
    var touchStartTime = 0;
    var touchStartX = 0;
    var touchStartY = 0;
    var dragThreshold = 8;
    
    function log(m) {
        if (window.console && console.log) {
            console.log('[TagSphere] ' + m);
        }
    }
    
    /* ---- OVERLAY erstellen ---- */
    function createOverlay() {
        overlay = document.createElement('div');
        overlay.id = 'tagSphereOverlay';
        var body = document.body || document.getElementsByTagName('body')[0];
        body.appendChild(overlay);
        
        if (overlay.addEventListener) {
            overlay.addEventListener('click', function() {
                collapseExplode();
            }, false);
        } else if (overlay.attachEvent) {
            overlay.attachEvent('onclick', function() {
                collapseExplode();
            });
        }
    }
    
    /* ---- Container erstellen ---- */
    function createContainer() {
        c = document.createElement('div');
        c.id = 'tagSphere';
        c.className = 'mode-tags';
        
        center = document.createElement('div');
        center.className = 'sphere-center';
        center.innerHTML = '⚒';
        
        closeBtn = document.createElement('button');
        closeBtn.className = 'close-btn';
        closeBtn.innerHTML = '×';
        closeBtn.title = 'Close';
        
        c.appendChild(center);
        c.appendChild(closeBtn);
        
        var body = document.body || document.getElementsByTagName('body')[0];
        body.appendChild(c);
    }
    
    /* ---- Tags von Seite extrahieren ---- */
    function extractTags() {
        items = [];
        var tagBox = document.querySelector ? document.querySelector('.pageTags') : null;
        if (!tagBox) {
            var allDivs = document.getElementsByTagName('div');
            for (var i = 0; i < allDivs.length; i++) {
                if (allDivs[i].className && allDivs[i].className.indexOf('pageTags') !== -1) {
                    tagBox = allDivs[i];
                    break;
                }
            }
        }
        if (!tagBox) { log('Keine .pageTags gefunden'); return; }
        
        var links = tagBox.getElementsByTagName('a');
        for (var j = 0; j < links.length; j++) {
            var link = links[j];
            var text = link.textContent || link.innerText || '';
            text = text.replace(/^\s+|\s+$/g, '');
            if (text.length > 0 && text !== '+') {
                items.push({ name: text, href: link.href || '#', el: null });
            }
        }
        log(items.length + ' Tags gefunden');
    }
    
    /* ---- Tag-Elemente erstellen ---- */
    function createElements() {
        for (var i = 0; i < items.length; i++) {
            var el = document.createElement('a');
            el.className = 'tag-item atom-' + (i % 8);
            el.href = '#';
            el.innerHTML = items[i].name;
            el.setAttribute('data-index', i);
            items[i].el = el;
            c.appendChild(el);
        }
    }
    
    /* ---- 3D Kugel-Positionierung ---- */
    function position() {
        var len = items.length;
        if (len === 0) return;
        
        var r = exploded ? baseRadius * 2.5 : baseRadius;
        var cw = c.offsetWidth || (exploded ? 700 : 280);
        var ch = c.offsetHeight || (exploded ? 700 : 280);
        var cx = cw / 2;
        var cy = ch / 2;
        
        for (var i = 0; i < len; i++) {
            var phi = Math.acos(-1 + (2 * i + 1) / len);
            var theta = Math.sqrt(len * Math.PI) * phi;
            
            var x0 = r * Math.cos(theta) * Math.sin(phi);
            var y0 = r * Math.sin(theta) * Math.sin(phi);
            var z0 = r * Math.cos(phi);
            
            var cosX = Math.cos(angleX);
            var sinX = Math.sin(angleX);
            var cosY = Math.cos(angleY);
            var sinY = Math.sin(angleY);
            
            var x1 = x0 * cosX - z0 * sinX;
            var z1 = x0 * sinX + z0 * cosX;
            var y1 = y0 * cosY - z1 * sinY;
            var z2 = y0 * sinY + z1 * cosY;
            
            var scale = (z2 + r * 2) / (r * 3);
            if (scale < 0.3) scale = 0.3;
            if (scale > 1.2) scale = 1.2;
            
            var el = items[i].el;
            if (!el) continue;
            
            el.style.left = (cx + x1 - (el.offsetWidth || 40) / 2) + 'px';
            el.style.top = (cy + y1 - (el.offsetHeight || 20) / 2) + 'px';
            el.style.zIndex = Math.round(z2 + r + 100);
            el.style.opacity = (0.4 + scale * 0.5);
            
            if (el.style.filter !== undefined) {
                el.style.filter = 'alpha(opacity=' + Math.round((0.4 + scale * 0.5) * 100) + ')';
            }
            
            if (z2 < 0) {
                el.style.pointerEvents = 'none';
                el.style.cursor = 'default';
            } else {
                el.style.pointerEvents = 'auto';
                el.style.cursor = 'pointer';
            }
        }
    }
    
    /* ---- Explode / Collapse ---- */
    function toggleExplode() {
        if (exploded) {
            collapseExplode();
        } else {
            exploded = true;
            c.className = c.className + ' exploded';
            overlay.className = 'active';
            
            /* Kurz warten bis CSS-Transition fertig, dann neu positionieren */
            setTimeout(function() { position(); }, 650);
        }
    }
    
    function collapseExplode() {
        if (!exploded) return;
        exploded = false;
        c.className = c.className.replace(/\s*exploded/g, '');
        overlay.className = '';
        
        setTimeout(function() { position(); }, 650);
    }
    
    /* ---- Click auf Tag oder Center ---- */
    function handleClick(e) {
        var target = e.target || e.srcElement;
        
        /* Close Button */
        if (target === closeBtn || (target.parentNode && target.parentNode === closeBtn)) {
            collapseExplode();
            if (e.preventDefault) e.preventDefault();
            if (e.stopPropagation) e.stopPropagation();
            return false;
        }
        
        /* Center Icon - Toggle Explode */
        if (target === center || target.parentNode === center) {
            toggleExplode();
            if (e.preventDefault) e.preventDefault();
            return false;
        }
        
        /* Tag Item */
        var idx = target.getAttribute ? target.getAttribute('data-index') : null;
        if (idx !== null && idx !== '') {
            if (e.preventDefault) e.preventDefault();
            if (e.stopPropagation) e.stopPropagation();
            
            if (mode === 'tags') {
                loadPagesForTag(items[parseInt(idx, 10)].name);
            } else {
                window.location.href = items[parseInt(idx, 10)].href;
            }
            return false;
        }
        
        /* Click ins Leere = Toggle Explode */
        if (target === c) {
            toggleExplode();
        }
    }
    
    /* ---- AJAX: Seiten für einen Tag laden ---- */
    function loadPagesForTag(tagName) {
        if (loading) return;
        loading = true;
        c.className = c.className + ' loading';
        log('Lade Seiten fuer: ' + tagName);
        
        var xhr;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            try { xhr = new ActiveXObject('Msxml2.XMLHTTP'); } catch(e1) {
                try { xhr = new ActiveXObject('Microsoft.XMLHTTP'); } catch(e2) {
                    log('Kein AJAX verfuegbar');
                    loading = false;
                    return;
                }
            }
        }
        
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                loading = false;
                c.className = c.className.replace(/\s*loading/g, '');
                
                if (xhr.status === 200) {
                    parsePagesResponse(xhr.responseText, tagName);
                } else {
                    log('Fehler: ' + xhr.status);
                }
            }
        };
        
        xhr.open('GET', '/Special:Tags?tag=' + encodeURIComponent(tagName), true);
        xhr.send(null);
    }
    
    /* ---- AJAX Response parsen ---- */
    function parsePagesResponse(html, tagName) {
        var pages = [];
        var div = document.createElement('div');
        div.innerHTML = html;
        
        var links = div.getElementsByTagName('a');
        for (var i = 0; i < links.length; i++) {
            var href = links[i].href || '';
            var text = links[i].textContent || links[i].innerText || '';
            text = text.replace(/^\s+|\s+$/g, '');
            
            if (text.length > 0 && href.indexOf('/Special:') === -1 && href.indexOf('javascript:') === -1 && text !== tagName) {
                var exists = false;
                for (var p = 0; p < pages.length; p++) {
                    if (pages[p].name === text) { exists = true; break; }
                }
                if (!exists && pages.length < 20) {
                    pages.push({ name: text, href: href });
                }
            }
        }
        
        if (pages.length === 0) {
            log('Keine Seiten gefunden fuer: ' + tagName);
            return;
        }
        
        /* Alte Tags entfernen */
        for (var k = 0; k < items.length; k++) {
            if (items[k].el && items[k].el.parentNode) {
                items[k].el.parentNode.removeChild(items[k].el);
            }
        }
        
        /* Neue Seiten setzen */
        items = [];
        for (var m = 0; m < pages.length; m++) {
            items.push({ name: pages[m].name, href: pages[m].href, el: null });
        }
        
        mode = 'pages';
        c.className = c.className.replace(/mode-tags/g, 'mode-pages');
        
        createElements();
        
        if (!exploded) {
            toggleExplode();
        }
        
        position();
        log(items.length + ' Seiten geladen');
    }
    
    /* ---- Sichtbarkeit ab Scroll-Position ---- */
    function updateVisibility() {
        var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
        
        if (scrollTop > 1000 && !visible) {
            c.className += ' visible';
            visible = true;
        } else if (scrollTop <= 1000 && visible && !exploded) {
            c.className = c.className.replace(/\s*visible/g, '');
            visible = false;
        }
    }
    
    /* ===========================================================
       MOUSE EVENTS
       =========================================================== */
    function mouseDown(e) {
        if (loading) return;
        drag = true;
        lastX = e.clientX || e.pageX;
        lastY = e.clientY || e.pageY;
        touchStartTime = new Date().getTime();
        touchStartX = lastX;
        touchStartY = lastY;
        c.className += ' dragging';
        if (e.preventDefault) e.preventDefault();
        return false;
    }
    
    function mouseMove(e) {
        if (!drag) return;
        var x = e.clientX || e.pageX;
        var y = e.clientY || e.pageY;
        angleX += (x - lastX) * 0.008;
        angleY += (y - lastY) * 0.008;
        lastX = x;
        lastY = y;
        position();
        if (e.preventDefault) e.preventDefault();
        return false;
    }
    
    function mouseUp(e) {
        if (!drag) return;
        var upTime = new Date().getTime();
        var dx = Math.abs((e.clientX || e.pageX) - touchStartX);
        var dy = Math.abs((e.clientY || e.pageY) - touchStartY);
        
        /* Nur Click wenn kurz UND kaum bewegt */
        if ((upTime - touchStartTime) < 250 && dx < dragThreshold && dy < dragThreshold) {
            handleClick(e);
        }
        
        drag = false;
        c.className = c.className.replace(/\s*dragging/g, '');
    }
    
    /* ===========================================================
       TOUCH EVENTS - MOBILE FIX
       =========================================================== */
    function touchStart(e) {
        if (loading) return;
        if (!e.touches || e.touches.length === 0) return;
        
        drag = true;
        lastX = e.touches[0].clientX;
        lastY = e.touches[0].clientY;
        touchStartTime = new Date().getTime();
        touchStartX = lastX;
        touchStartY = lastY;
        c.className += ' dragging';
        
        /* preventDefault verhindert Scroll-Hijacking */
        if (e.preventDefault) e.preventDefault();
    }
    
    function touchMove(e) {
        if (!drag) return;
        if (!e.touches || e.touches.length === 0) return;
        
        var x = e.touches[0].clientX;
        var y = e.touches[0].clientY;
        var dx = x - lastX;
        var dy = y - lastY;
        
        angleX += dx * 0.01;
        angleY += dy * 0.01;
        lastX = x;
        lastY = y;
        position();
        
        if (e.preventDefault) e.preventDefault();
    }
    
    function touchEnd(e) {
        if (!drag) return;
        
        var upTime = new Date().getTime();
        var touch = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : null;
        
        if (touch) {
            var dx = Math.abs(touch.clientX - touchStartX);
            var dy = Math.abs(touch.clientY - touchStartY);
            
            /* Tap erkennen: kurz + wenig Bewegung */
            if ((upTime - touchStartTime) < 300 && dx < dragThreshold && dy < dragThreshold) {
                var target = document.elementFromPoint(touch.clientX, touch.clientY);
                if (target) {
                    handleClick({ target: target, preventDefault: function(){}, stopPropagation: function(){} });
                }
            }
        }
        
        drag = false;
        c.className = c.className.replace(/\s*dragging/g, '');
    }
    
    /* ===========================================================
       EVENT BINDING
       =========================================================== */
    function bind() {
        /* --- MOUSE --- */
        if (c.addEventListener) {
            c.addEventListener('mousedown', mouseDown, false);
            document.addEventListener('mousemove', mouseMove, false);
            document.addEventListener('mouseup', mouseUp, false);
        } else if (c.attachEvent) {
            c.attachEvent('onmousedown', mouseDown);
            document.attachEvent('onmousemove', mouseMove);
            document.attachEvent('onmouseup', mouseUp);
        }
        
        /* --- TOUCH (passiv: false fuer preventDefault) --- */
        if (c.addEventListener) {
            try {
                c.addEventListener('touchstart', touchStart, { passive: false });
                c.addEventListener('touchmove', touchMove, { passive: false });
                c.addEventListener('touchend', touchEnd, false);
                c.addEventListener('touchcancel', touchEnd, false);
            } catch(err) {
                /* Fallback fuer Browser ohne options-Objekt */
                c.addEventListener('touchstart', touchStart, false);
                c.addEventListener('touchmove', touchMove, false);
                c.addEventListener('touchend', touchEnd, false);
                c.addEventListener('touchcancel', touchEnd, false);
            }
        }
        
        /* --- CLOSE BUTTON --- */
        if (closeBtn.addEventListener) {
            closeBtn.addEventListener('click', function(e) {
                collapseExplode();
                if (e.preventDefault) e.preventDefault();
                if (e.stopPropagation) e.stopPropagation();
            }, false);
            closeBtn.addEventListener('touchend', function(e) {
                collapseExplode();
                if (e.preventDefault) e.preventDefault();
                if (e.stopPropagation) e.stopPropagation();
            }, false);
        } else if (closeBtn.attachEvent) {
            closeBtn.attachEvent('onclick', function() { collapseExplode(); });
        }
        
        /* --- SCROLL --- */
        if (window.addEventListener) {
            window.addEventListener('scroll', updateVisibility, false);
        } else if (window.attachEvent) {
            window.attachEvent('onscroll', updateVisibility);
        }
    }
    
    /* ===========================================================
       INIT
       =========================================================== */
    function init() {
        try {
            createOverlay();
            createContainer();
            extractTags();
            
            if (items.length === 0) {
                c.style.display = 'none';
                return;
            }
            
            createElements();
            position();
            bind();
            updateVisibility();
            
            log('Ready: ' + items.length + ' Tags');
            
        } catch (err) {
            log('Error: ' + err.message);
            if (window.console && console.error) {
                console.error(err);
            }
        }
    }
    
    if (document.readyState === 'complete') {
        init();
    } else if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', init, false);
    } else {
        window.onload = init;
    }
    
    })();
    

     
    Use the language selection below for English Utilise le sélecteur de langue ci-dessous pour français Per l'italiano, utilizzare la selezione della lingua qui sotto Schön sprichst du deutsch :-)