/* TAG SPHERE - Color Gradient */
#tagSphere {
position: fixed;
bottom: 180px;
left: 20px;
width: 280px;
height: 280px;
z-index: 999;
cursor: move;
cursor: -webkit-grab;
cursor: -moz-grab;
opacity: 0;
visibility: hidden;
transition: opacity 0.4s ease, visibility 0.4s ease, width 0.7s cubic-bezier(0.68, -0.55, 0.265, 1.55), height 0.7s cubic-bezier(0.68, -0.55, 0.265, 1.55), bottom 0.7s ease, left 0.7s ease, z-index 0s;
background: transparent;
border: none;
}
#tagSphere.visible {
opacity: 1;
visibility: visible;
}
#tagSphere.exploded {
width: 700px;
height: 700px;
bottom: 20px;
left: 20px;
z-index: 2147483647;
}
#tagSphere.dragging {
cursor: -webkit-grabbing !important;
cursor: -moz-grabbing !important;
}
#tagSphere .close-btn {
position: absolute;
top: 20px;
right: 20px;
width: 48px;
height: 48px;
background: transparent;
color: #CC0000;
border: none;
font-size: 36px;
font-weight: bold;
line-height: 48px;
text-align: center;
cursor: pointer;
z-index: 9999;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease, color 0.2s ease;
}
#tagSphere.exploded .close-btn {
opacity: 1;
visibility: visible;
}
#tagSphere .close-btn:hover {
color: #AA0000;
}
#tagSphere .back-btn {
position: absolute;
top: 20px;
left: 20px;
width: 48px;
height: 48px;
background: transparent;
color: #0066CC;
border: none;
font-size: 36px;
font-weight: bold;
line-height: 48px;
text-align: center;
cursor: pointer;
z-index: 9999;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease, color 0.2s ease;
}
#tagSphere.mode-pages.exploded .back-btn {
opacity: 1;
visibility: visible;
}
#tagSphere .back-btn:hover {
color: #0044AA;
}
#tagSphere .tag-item {
position: absolute;
padding: 12px 20px;
white-space: nowrap;
font-size: 13px;
font-weight: 700;
text-decoration: none;
background: #FFFFFF;
border: none;
border-radius: 20px;
cursor: pointer;
min-width: 50px;
text-align: center;
box-shadow: 0 3px 10px rgba(0,0,0,0.15);
transition: transform 0.2s ease, box-shadow 0.2s ease, color 0.2s ease;
pointer-events: auto;
z-index: 100;
}
#tagSphere .tag-item.atom-0 { animation: float-0 4.2s ease-in-out infinite; }
#tagSphere .tag-item.atom-1 { animation: float-1 5.1s ease-in-out infinite; }
#tagSphere .tag-item.atom-2 { animation: float-2 4.7s ease-in-out infinite; }
#tagSphere .tag-item.atom-3 { animation: float-3 5.5s ease-in-out infinite; }
#tagSphere .tag-item.atom-4 { animation: float-4 4.9s ease-in-out infinite; }
#tagSphere .tag-item.atom-5 { animation: float-5 5.3s ease-in-out infinite; }
#tagSphere .tag-item.atom-6 { animation: float-6 4.5s ease-in-out infinite; }
#tagSphere .tag-item.atom-7 { animation: float-7 5.8s ease-in-out infinite; }
@keyframes float-0 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(4px, -5px) rotate(2deg); }
50% { transform: translate(-3px, -7px) rotate(-2deg); }
75% { transform: translate(-5px, -3px) rotate(3deg); }
}
@keyframes float-1 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(-4px, -6px) rotate(-2deg); }
50% { transform: translate(5px, -4px) rotate(2deg); }
75% { transform: translate(3px, -7px) rotate(-3deg); }
}
@keyframes float-2 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(5px, -4px) rotate(3deg); }
50% { transform: translate(-4px, -8px) rotate(-2deg); }
75% { transform: translate(4px, -5px) rotate(2deg); }
}
@keyframes float-3 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(-5px, -7px) rotate(-3deg); }
50% { transform: translate(4px, -5px) rotate(2deg); }
75% { transform: translate(-3px, -6px) rotate(-2deg); }
}
@keyframes float-4 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(4px, -6px) rotate(2deg); }
50% { transform: translate(-5px, -5px) rotate(-3deg); }
75% { transform: translate(5px, -7px) rotate(3deg); }
}
@keyframes float-5 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(-4px, -5px) rotate(-2deg); }
50% { transform: translate(5px, -7px) rotate(3deg); }
75% { transform: translate(-5px, -4px) rotate(-3deg); }
}
@keyframes float-6 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(5px, -7px) rotate(3deg); }
50% { transform: translate(-4px, -6px) rotate(-2deg); }
75% { transform: translate(4px, -4px) rotate(2deg); }
}
@keyframes float-7 {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(-5px, -5px) rotate(-3deg); }
50% { transform: translate(4px, -7px) rotate(2deg); }
75% { transform: translate(-4px, -6px) rotate(-2deg); }
}
#tagSphere.mode-tags .tag-item {
color: #0066CC;
}
#tagSphere.mode-tags .tag-item:hover {
color: #0044AA;
box-shadow: 0 6px 20px rgba(0,102,204,0.5);
transform: scale(1.15);
z-index: 9998 !important;
}
#tagSphere.mode-pages .tag-item:hover {
box-shadow: 0 6px 20px rgba(204,0,0,0.5);
transform: scale(1.15);
z-index: 9998 !important;
}
(function() {
var c = null;
var closeBtn = null;
var backBtn = 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 originalTags = [];
function createContainer() {
c = document.createElement('div');
c.id = 'tagSphere';
c.className = 'mode-tags';
closeBtn = document.createElement('button');
closeBtn.className = 'close-btn';
closeBtn.innerHTML = '×';
closeBtn.title = 'Close';
backBtn = document.createElement('button');
backBtn.className = 'back-btn';
backBtn.innerHTML = '←';
backBtn.title = 'Back to Tags';
c.appendChild(closeBtn);
c.appendChild(backBtn);
var body = document.body || document.getElementsByTagName('body')[0];
body.appendChild(c);
}
function extractTags() {
var box = document.querySelector('.pageTags');
if (!box) return;
var links = box.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var txt = (link.textContent || link.innerText || '').replace(/^\s+|\s+$/g, '');
if (txt.indexOf('bearbeiten') !== -1 || txt.indexOf('edit') !== -1) continue;
if (txt.length > 0 && link.href) {
items.push({
text: txt,
url: link.href,
isTag: true,
views: 0,
phi: 0,
theta: 0
});
}
}
originalTags = items.slice();
distributeItems();
}
function distributeItems() {
var count = items.length;
if (count === 0) return;
var goldenAngle = Math.PI * (3 - Math.sqrt(5));
for (var i = 0; i < count; i++) {
var y = 1 - (i / (count - 1)) * 2;
var theta = goldenAngle * i;
items[i].phi = theta;
items[i].theta = Math.acos(y);
}
}
function clearItems() {
var elements = c.getElementsByClassName('tag-item');
while (elements.length > 0) {
c.removeChild(elements[0]);
}
items = [];
}
function createElements() {
for (var i = 0; i < items.length; i++) {
var item = items[i];
var span = document.createElement('span');
span.className = 'tag-item atom-' + (i % 8);
var icon = item.isTag ? ' ' : ' ';
span.innerHTML = icon + item.text;
span.title = item.text + (item.views > 0 ? ' (' + item.views + ' views)' : '');
span.setAttribute('data-text', item.text);
span.setAttribute('data-url', item.url);
span.setAttribute('data-istag', item.isTag ? '1' : '0');
span.setAttribute('data-views', item.views);
if (mode === 'pages') {
var maxViews = 0;
var minViews = 999999999;
var hasViews = false;
for (var j = 0; j < items.length; j++) {
if (items[j].views > 0) {
hasViews = true;
if (items[j].views > maxViews) maxViews = items[j].views;
if (items[j].views < minViews) minViews = items[j].views;
}
}
if (hasViews && item.views > 0) {
var viewRange = maxViews - minViews;
if (viewRange > 0) {
var normalized = (item.views - minViews) / viewRange;
var r = Math.floor(normalized * 204);
span.style.color = 'rgb(' + r + ', 0, 0)';
} else {
span.style.color = '#CC0000';
}
} else {
span.style.color = '#000000';
}
}
c.appendChild(span);
}
}
function position() {
var elements = c.getElementsByClassName('tag-item');
var radius = exploded ? baseRadius * 2.5 : baseRadius;
var centerPos = exploded ? 350 : 140;
for (var i = 0; i < elements.length; i++) {
var elem = elements[i];
var item = items[i];
var phi = item.phi + angleX;
var theta = item.theta + (angleY * 0.3);
var x = radius * Math.sin(theta) * Math.cos(phi);
var y = radius * Math.sin(theta) * Math.sin(phi);
var z = radius * Math.cos(theta);
var scale = (z + radius) / (radius * 2);
scale = Math.max(0.5, Math.min(1.4, scale));
elem.style.left = (centerPos + x - 40) + 'px';
elem.style.top = (centerPos + y - 20) + 'px';
var fontSize = 13;
if (mode === 'pages' && item.views > 0) {
var maxViews = 0;
var minViews = 999999999;
for (var j = 0; j < items.length; j++) {
if (items[j].views > 0) {
if (items[j].views > maxViews) maxViews = items[j].views;
if (items[j].views < minViews) minViews = items[j].views;
}
}
var viewRange = maxViews - minViews;
if (viewRange > 0) {
var normalized = (item.views - minViews) / viewRange;
fontSize = Math.floor(12 + (normalized * 14));
}
}
fontSize = Math.floor(fontSize * scale);
elem.style.fontSize = fontSize + 'px';
var zIndex = 100 + Math.floor(scale * 200);
elem.style.zIndex = zIndex;
var opacity = 0.7 + (scale * 0.3);
elem.style.opacity = opacity;
}
}
function toggleExplode() {
exploded = !exploded;
if (exploded) {
c.className = c.className + ' exploded';
} else {
c.className = c.className.replace(/\s*exploded/g, '');
if (mode === 'pages') {
backToTags();
}
}
position();
}
function backToTags() {
clearItems();
items = originalTags.slice();
mode = 'tags';
c.className = c.className.replace(/mode-\w+/g, '') + ' mode-tags';
if (exploded) c.className += ' exploded';
if (visible) c.className += ' visible';
createElements();
position();
}
function handleClick(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if (loading || drag) return;
if (target === closeBtn) {
if (exploded) toggleExplode();
if (e.preventDefault) e.preventDefault();
return false;
}
if (target === backBtn) {
backToTags();
if (e.preventDefault) e.preventDefault();
return false;
}
while (target && target !== c) {
if (target.className && target.className.indexOf('tag-item') !== -1) {
var isTag = target.getAttribute('data-istag') === '1';
var text = target.getAttribute('data-text');
var url = target.getAttribute('data-url');
if (isTag) {
if (!exploded) toggleExplode();
loadPagesForTag(text);
} else {
window.location.href = url;
}
if (e.preventDefault) e.preventDefault();
return false;
}
target = target.parentNode;
}
if (!exploded && !drag) {
toggleExplode();
}
}
function loadPagesForTag(tagName) {
loading = true;
c.className = c.className.replace(/mode-\w+/g, '') + ' loading';
var url = '/Special:Tags?tag=' + encodeURIComponent(tagName);
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
parsePages(xhr.responseText);
} else {
loading = false;
c.className = c.className.replace(/loading/g, '') + ' mode-tags';
if (visible) c.className += ' visible';
}
}
};
try {
xhr.open('GET', url, true);
xhr.send();
} catch (err) {
loading = false;
}
}
function parsePages(html) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
var specialTags = tempDiv.querySelector('#SpecialTags');
if (!specialTags) {
loading = false;
return;
}
var newItems = [];
var links = specialTags.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var txt = (link.textContent || link.innerText || '').replace(/^\s+|\s+$/g, '');
var href = link.getAttribute('href');
if (txt.length > 0 && href) {
if (href.indexOf('http') !== 0) {
href = window.location.protocol + '//' + window.location.host + href;
}
newItems.push({
text: txt,
url: href,
isTag: false,
views: 0,
phi: 0,
theta: 0
});
}
}
clearItems();
items = newItems;
distributeItems();
mode = 'pages';
loadPopularity();
}
function loadPopularity() {
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
parsePopularity(xhr.responseText);
} else {
finishPageLoad();
}
}
};
try {
xhr.open('GET', '/Special:Popularpages', true);
xhr.send();
} catch (err) {
finishPageLoad();
}
}
function parsePopularity(html) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
var rows = tempDiv.getElementsByTagName('tr');
var viewData = {};
for (var i = 0; i < rows.length; i++) {
var cells = rows[i].getElementsByTagName('td');
if (cells.length >= 2) {
var link = cells[0].getElementsByTagName('a')[0];
if (link) {
var pageName = (link.textContent || link.innerText || '').replace(/^\s+|\s+$/g, '');
var viewText = (cells[1].textContent || cells[1].innerText || '').replace(/^\s+|\s+$/g, '');
var views = parseInt(viewText.replace(/[^0-9]/g, ''), 10);
if (pageName && !isNaN(views)) {
viewData[pageName] = views;
}
}
}
}
for (var j = 0; j < items.length; j++) {
if (viewData[items[j].text]) {
items[j].views = viewData[items[j].text];
}
}
finishPageLoad();
}
function finishPageLoad() {
createElements();
position();
loading = false;
c.className = 'mode-pages exploded';
if (visible) c.className += ' visible';
}
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) {
c.className = c.className.replace(/\s*visible/g, '');
visible = false;
}
}
function down(e) {
if (loading) return;
drag = true;
lastX = e.clientX || e.pageX;
lastY = e.clientY || e.pageY;
touchStartTime = new Date().getTime();
c.className += ' dragging';
if (e.preventDefault) e.preventDefault();
return false;
}
function move(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 up(e) {
var upTime = new Date().getTime();
var timeDiff = upTime - touchStartTime;
if (drag && timeDiff < 200) {
handleClick(e);
}
if (drag) {
drag = false;
c.className = c.className.replace(/\s*dragging/g, '');
}
}
function bind() {
if (c.addEventListener) {
c.addEventListener('click', handleClick, false);
c.addEventListener('mousedown', down, false);
document.addEventListener('mousemove', move, false);
document.addEventListener('mouseup', up, false);
} else if (c.attachEvent) {
c.attachEvent('onclick', handleClick);
c.attachEvent('onmousedown', down);
document.attachEvent('onmousemove', move);
document.attachEvent('onmouseup', up);
}
if (window.addEventListener) {
window.addEventListener('scroll', updateVisibility, false);
} else if (window.attachEvent) {
window.attachEvent('onscroll', updateVisibility);
}
}
function init() {
try {
createContainer();
extractTags();
if (items.length === 0) {
c.style.display = 'none';
return;
}
createElements();
position();
bind();
updateVisibility();
} catch (err) {
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;
}
})();