MediaWiki:Common.js: Difference between revisions
Appearance
PERFORMANCE FIX: Optimized tag cloud animation - reduced DOM updates, removed expensive getComputedStyle, update depth only every 3 frames |
FIX: Text disappearing on rotation (clamped depthFactor, min opacity 0.4), reduced ellipsoid size to fit centered in container |
||
| Line 13: | Line 13: | ||
}); | }); | ||
/* 3D CEO Cloud - OPTIMIZED | /* 3D CEO Cloud - OPTIMIZED and FIXED */ | ||
$(document).ready(function() { | $(document).ready(function() { | ||
var placeholder = document.getElementById('ceocloud-placeholder'); | var placeholder = document.getElementById('ceocloud-placeholder'); | ||
| Line 33: | Line 33: | ||
if (!tags || tags.length === 0) return; | if (!tags || tags.length === 0) return; | ||
console.log('CEO Cloud: Initializing with ' + tags.length + ' CEOs | console.log('CEO Cloud: Initializing with ' + tags.length + ' CEOs'); | ||
// Create cloud container | // Create cloud container | ||
| Line 39: | Line 39: | ||
cloud.style.cssText = 'position:relative;width:100%;height:500px;overflow:hidden;cursor:grab;'; | cloud.style.cssText = 'position:relative;width:100%;height:500px;overflow:hidden;cursor:grab;'; | ||
// Create sphere container - | // Create sphere container - FIXED: Centered at 50% (true center) | ||
var sphere = document.createElement('div'); | var sphere = document.createElement('div'); | ||
sphere.style.cssText = 'position:absolute;left:50%;top:50%;width:1px;height:1px;transform-style:preserve-3d;will-change:transform;'; | sphere.style.cssText = 'position:absolute;left:50%;top:50%;width:1px;height:1px;transform-style:preserve-3d;will-change:transform;'; | ||
// Ellipsoid parameters | // Ellipsoid parameters - adjusted for better fit with zoom | ||
var radiusX = 380 | var radiusX = 320; // Reduced from 380 to fit better when zoomed | ||
var radiusY = 100; // Reduced from 120 | |||
var radiusZ = 240; // Reduced from 280 | |||
var phi = Math.PI * (3 - Math.sqrt(5)); | var phi = Math.PI * (3 - Math.sqrt(5)); | ||
var ceoElements = []; | var ceoElements = []; | ||
| Line 103: | Line 105: | ||
var rotationSpeed = 0.15; | var rotationSpeed = 0.15; | ||
var frameCount = 0; | var frameCount = 0; | ||
// | // FIXED: Proper depth calculation that doesn't make text disappear | ||
function updateDepth() { | function updateDepth() { | ||
var rad = angleY * Math.PI / 180; | var rad = angleY * Math.PI / 180; | ||
var cosRad = Math.cos(rad); | var cosRad = Math.cos(rad); | ||
var sinRad = Math.sin(rad); | var sinRad = Math.sin(rad); | ||
for (var i = 0; i < ceoElements.length; i++) { | for (var i = 0; i < ceoElements.length; i++) { | ||
var ceo = ceoElements[i]; | var ceo = ceoElements[i]; | ||
// Calculate rotated Z position | |||
var newZ = ceo.z * cosRad - ceo.x * sinRad; | var newZ = ceo.z * cosRad - ceo.x * sinRad; | ||
var depthFactor = (newZ + radiusZ) / | |||
var opacity = 0. | // FIXED: Normalize to 0-1 range properly (was causing negative values) | ||
// newZ ranges from -radiusZ to +radiusZ, so normalize it | |||
var depthFactor = (newZ + radiusZ) / (radiusZ * 2); | |||
// Clamp to ensure valid range | |||
depthFactor = Math.max(0, Math.min(1, depthFactor)); | |||
// FIXED: Minimum opacity of 0.4 so text never fully disappears | |||
var opacity = 0.4 + depthFactor * 0.6; | |||
ceo.el.style.opacity = opacity; | ceo.el.style.opacity = opacity; | ||
| Line 123: | Line 132: | ||
} | } | ||
// Main animation loop | // Main animation loop | ||
function animate() { | function animate() { | ||
if (autoRotate) { | if (autoRotate) { | ||
| Line 137: | Line 146: | ||
sphere.style.transform = 'scale(' + zoom + ') rotateX(' + angleX + 'deg) rotateY(' + angleY + 'deg)'; | sphere.style.transform = 'scale(' + zoom + ') rotateX(' + angleX + 'deg) rotateY(' + angleY + 'deg)'; | ||
// | // Update depth every 3 frames for performance | ||
frameCount++; | frameCount++; | ||
if (frameCount % 3 === 0 | if (frameCount % 3 === 0) { | ||
updateDepth(); | updateDepth(); | ||
} | } | ||
| Line 147: | Line 155: | ||
} | } | ||
// Initial depth calculation | |||
updateDepth(); | |||
animate(); | animate(); | ||
| Line 222: | Line 232: | ||
cloud.appendChild(hints); | cloud.appendChild(hints); | ||
console.log('CEO Cloud: | console.log('CEO Cloud: Initialization complete (fixed)'); | ||
}); | }); | ||
Revision as of 12:19, 29 November 2025
/* Common.js - Site-wide JavaScript */
/* Add copyright footer at the very bottom of page - single occurrence only */
$(document).ready(function() {
// Remove any existing copyright footers first to prevent duplicates
$('#footer-info-copyright, .ceo-copyright-footer').remove();
// Create the copyright footer
var copyright = '<div class="ceo-copyright-footer" id="footer-info-copyright" style="text-align: center; padding: 20px; background: #f8f9fa; border-top: 1px solid #ddd; margin-top: 40px; font-size: 0.85em; line-height: 1.6;">Text is available under the <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="nofollow">Creative Commons Attribution-ShareAlike 4.0 License</a>; additional terms may apply. By using this site, you agree to the <a href="/wiki/CEO.wiki:Terms_of_Use">Terms of Use</a>, <a href="/wiki/CEO.wiki:Privacy_Policy">Privacy Policy</a>, and all <a href="/wiki/CEO.wiki:General_disclaimer">disclaimers</a>. CEO.wiki is operated as an independent collaborative encyclopedia project and is not affiliated with Wikipedia or the Wikimedia Foundation. See our <a href="/wiki/CEO.wiki:Takedown_Request_Policy">Takedown Request Policy</a> for content concerns.</div>';
// Append to the very bottom of the body
$('body').append(copyright);
});
/* 3D CEO Cloud - OPTIMIZED and FIXED */
$(document).ready(function() {
var placeholder = document.getElementById('ceocloud-placeholder');
if (!placeholder) return;
var ceoDataEl = document.getElementById('ceo-data');
if (!ceoDataEl) return;
var ceoDataJson = ceoDataEl.getAttribute('data-ceos');
if (!ceoDataJson) return;
var tags;
try {
tags = JSON.parse(ceoDataJson);
} catch(e) {
console.log('CEO Cloud: JSON parse error');
return;
}
if (!tags || tags.length === 0) return;
console.log('CEO Cloud: Initializing with ' + tags.length + ' CEOs');
// Create cloud container
var cloud = document.createElement('div');
cloud.style.cssText = 'position:relative;width:100%;height:500px;overflow:hidden;cursor:grab;';
// Create sphere container - FIXED: Centered at 50% (true center)
var sphere = document.createElement('div');
sphere.style.cssText = 'position:absolute;left:50%;top:50%;width:1px;height:1px;transform-style:preserve-3d;will-change:transform;';
// Ellipsoid parameters - adjusted for better fit with zoom
var radiusX = 320; // Reduced from 380 to fit better when zoomed
var radiusY = 100; // Reduced from 120
var radiusZ = 240; // Reduced from 280
var phi = Math.PI * (3 - Math.sqrt(5));
var ceoElements = [];
var numTags = tags.length;
// Pre-calculate positions and create elements
for (var i = 0; i < numTags; i++) {
var tag = tags[i];
var y = 1 - (i / (numTags - 1)) * 2;
var radiusAtY = Math.sqrt(1 - y * y);
var theta = phi * i;
var x = Math.cos(theta) * radiusAtY;
var z = Math.sin(theta) * radiusAtY;
var posX = x * radiusX;
var posY = y * radiusY;
var posZ = z * radiusZ;
var link = document.createElement('a');
link.href = tag.url;
link.textContent = tag.text;
link.style.cssText = 'position:absolute;left:0;top:0;color:#fff;text-decoration:none;font-size:' + tag.size + 'px;font-weight:400;font-family:"Linux Libertine",Georgia,serif;white-space:nowrap;text-shadow:0 2px 10px rgba(0,0,0,0.9);cursor:pointer;transform-style:preserve-3d;backface-visibility:hidden;user-select:none;will-change:transform,opacity;transform:translate3d(' + posX + 'px,' + posY + 'px,' + posZ + 'px) translate(-50%,-50%);';
link.dataset.x = posX;
link.dataset.y = posY;
link.dataset.z = posZ;
link.dataset.size = tag.size;
// Hover effects
link.onmouseenter = function() {
this.style.color = '#fbbf24';
this.style.fontWeight = '700';
this.style.fontSize = (parseFloat(this.dataset.size) * 1.4) + 'px';
this.style.textShadow = '0 3px 20px rgba(251,191,36,1),0 0 50px rgba(251,191,36,0.8)';
this.style.zIndex = '1000';
};
link.onmouseleave = function() {
this.style.color = '#fff';
this.style.fontWeight = '400';
this.style.fontSize = this.dataset.size + 'px';
this.style.textShadow = '0 2px 10px rgba(0,0,0,0.9)';
this.style.zIndex = '';
};
ceoElements.push({ el: link, x: posX, y: posY, z: posZ });
sphere.appendChild(link);
}
cloud.appendChild(sphere);
placeholder.innerHTML = '';
placeholder.appendChild(cloud);
// Animation state
var angleX = 10, angleY = 0;
var targetAngleX = 10, targetAngleY = 0;
var zoom = 1.7, targetZoom = 1.7;
var autoRotate = true;
var rotationSpeed = 0.15;
var frameCount = 0;
// FIXED: Proper depth calculation that doesn't make text disappear
function updateDepth() {
var rad = angleY * Math.PI / 180;
var cosRad = Math.cos(rad);
var sinRad = Math.sin(rad);
for (var i = 0; i < ceoElements.length; i++) {
var ceo = ceoElements[i];
// Calculate rotated Z position
var newZ = ceo.z * cosRad - ceo.x * sinRad;
// FIXED: Normalize to 0-1 range properly (was causing negative values)
// newZ ranges from -radiusZ to +radiusZ, so normalize it
var depthFactor = (newZ + radiusZ) / (radiusZ * 2);
// Clamp to ensure valid range
depthFactor = Math.max(0, Math.min(1, depthFactor));
// FIXED: Minimum opacity of 0.4 so text never fully disappears
var opacity = 0.4 + depthFactor * 0.6;
ceo.el.style.opacity = opacity;
ceo.el.style.zIndex = Math.round(depthFactor * 100);
}
}
// Main animation loop
function animate() {
if (autoRotate) {
targetAngleY += rotationSpeed;
}
// Smooth interpolation
angleX += (targetAngleX - angleX) * 0.1;
angleY += (targetAngleY - angleY) * 0.1;
zoom += (targetZoom - zoom) * 0.1;
// Update transform
sphere.style.transform = 'scale(' + zoom + ') rotateX(' + angleX + 'deg) rotateY(' + angleY + 'deg)';
// Update depth every 3 frames for performance
frameCount++;
if (frameCount % 3 === 0) {
updateDepth();
}
requestAnimationFrame(animate);
}
// Initial depth calculation
updateDepth();
animate();
// Mouse drag controls
var isDragging = false, prevX = 0, prevY = 0;
cloud.onmousedown = function(e) {
if (e.target.tagName !== 'A') {
isDragging = true;
autoRotate = false;
prevX = e.clientX;
prevY = e.clientY;
cloud.style.cursor = 'grabbing';
e.preventDefault();
}
};
document.onmousemove = function(e) {
if (isDragging) {
targetAngleY += (e.clientX - prevX) * 0.5;
targetAngleX = Math.max(-40, Math.min(40, targetAngleX - (e.clientY - prevY) * 0.3));
prevX = e.clientX;
prevY = e.clientY;
}
};
document.onmouseup = function() {
if (isDragging) {
isDragging = false;
cloud.style.cursor = 'grab';
setTimeout(function() { autoRotate = true; }, 2000);
}
};
// Mouse wheel zoom
cloud.onwheel = function(e) {
e.preventDefault();
targetZoom = Math.max(0.8, Math.min(2.5, targetZoom + (e.deltaY > 0 ? -0.1 : 0.1)));
autoRotate = false;
clearTimeout(cloud.zoomTimeout);
cloud.zoomTimeout = setTimeout(function() { autoRotate = true; }, 1500);
};
// Touch support
cloud.ontouchstart = function(e) {
if (e.touches.length === 1) {
isDragging = true;
autoRotate = false;
prevX = e.touches[0].clientX;
prevY = e.touches[0].clientY;
e.preventDefault();
}
};
document.ontouchmove = function(e) {
if (isDragging && e.touches.length === 1) {
targetAngleY += (e.touches[0].clientX - prevX) * 0.5;
targetAngleX = Math.max(-40, Math.min(40, targetAngleX - (e.touches[0].clientY - prevY) * 0.3));
prevX = e.touches[0].clientX;
prevY = e.touches[0].clientY;
}
};
document.ontouchend = function() {
if (isDragging) {
isDragging = false;
setTimeout(function() { autoRotate = true; }, 2000);
}
};
// Control hints
var hints = document.createElement('div');
hints.style.cssText = 'position:absolute;bottom:10px;left:50%;transform:translateX(-50%);color:rgba(255,255,255,0.6);font-size:12px;text-align:center;pointer-events:none;font-family:sans-serif;';
hints.innerHTML = '🖱️ Drag to rotate • 🔍 Scroll to zoom';
cloud.appendChild(hints);
console.log('CEO Cloud: Initialization complete (fixed)');
});
/* ============================================
RESPONSIVE CEO PROFILE IMAGES
============================================ */
$(document).ready(function() {
if (!$('body').hasClass('page-Main_Page')) return;
function resizeCEOImages() {
var windowWidth = $(window).width();
var imageWidth;
if (windowWidth > 1200) {
imageWidth = Math.floor((windowWidth - 100) / 3 * 0.7);
} else if (windowWidth > 768) {
imageWidth = Math.floor((windowWidth - 80) / 2 * 0.7);
} else if (windowWidth > 480) {
imageWidth = Math.floor((windowWidth - 40) * 0.8);
} else {
imageWidth = Math.floor((windowWidth - 40) * 0.9);
}
imageWidth = Math.max(150, Math.min(400, imageWidth));
$('table[style*="width: 33%"] img, table[style*="width: 25%"] img').each(function() {
var $img = $(this);
var src = $img.attr('src');
if (!src || src.indexOf('File:') === -1) return;
if (src.indexOf('/thumb/') !== -1) {
$img.attr('src', src.replace(/\/\d+px-/, '/' + imageWidth + 'px-'));
}
$img.css({ 'max-width': '100%', 'height': 'auto', 'width': 'auto', 'display': 'block', 'margin': '0 auto' });
$img.removeAttr('width').removeAttr('height');
$img.parent('a').css({ 'display': 'block', 'text-align': 'center' });
});
}
resizeCEOImages();
var resizeTimer;
$(window).on('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(resizeCEOImages, 250);
});
$(window).on('orientationchange', function() {
setTimeout(resizeCEOImages, 300);
});
});