/* 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;
}
})();