|
|
| Line 1: |
Line 1: |
| /* 3D Rotating Tag Cloud Sphere */ | | /** |
| | * Add statistics banner to account creation page |
| | */ |
| (function() { | | (function() { |
| if (typeof window.CEOTagCloudInit !== 'undefined') return; | | // Only run on Special:CreateAccount page |
| window.CEOTagCloudInit = true;
| | if (mw.config.get('wgCanonicalSpecialPageName') !== 'CreateAccount') { |
| | | return; |
| function build3DTagCloud() {
| | } |
| var placeholder = document.getElementById('ceo-tagcloud-placeholder');
| |
| if (!placeholder) return;
| |
| | |
| // Tag data - weight determines size
| |
| var tags = [
| |
| {text: 'Satya Nadella', href: '/wiki/Satya_Nadella', weight: 10},
| |
| {text: 'Sundar Pichai', href: '/wiki/Sundar_Pichai', weight: 10},
| |
| {text: 'Tim Cook', href: '/wiki/Tim_Cook', weight: 9},
| |
| {text: 'Andy Jassy', href: '/wiki/Andy_Jassy', weight: 9},
| |
| {text: 'Mary Barra', href: '/wiki/Mary_Barra', weight: 8},
| |
| {text: 'Apple', href: '/wiki/Apple_Inc.', weight: 8},
| |
| {text: 'Microsoft', href: '/wiki/Microsoft', weight: 7},
| |
| {text: 'Google', href: '/wiki/Google', weight: 7},
| |
| {text: 'Amazon', href: '/wiki/Amazon', weight: 7},
| |
| {text: 'Tech CEOs', href: '/wiki/Category:Technology_CEOs', weight: 6},
| |
| {text: 'CEO Profiles', href: '/wiki/Category:Chief_executive_officers', weight: 6},
| |
| {text: 'Companies', href: '/wiki/Category:Companies', weight: 5},
| |
| {text: 'Fortune 500', href: '/wiki/Category:Fortune_500_CEOs', weight: 5},
| |
| {text: 'American CEOs', href: '/wiki/Category:American_CEOs', weight: 5},
| |
| {text: 'General Motors', href: '/wiki/General_Motors', weight: 4},
| |
| {text: 'CEOs by Country', href: '/wiki/Category:CEOs_by_country', weight: 4},
| |
| {text: 'Business Strategy', href: '/wiki/Category:Business_strategies', weight: 4},
| |
| {text: 'Governance', href: '/wiki/Category:Corporate_governance', weight: 3},
| |
| {text: 'Industry Analysis', href: '/wiki/Category:Industry_analysis', weight: 3},
| |
| {text: 'Random', href: '/wiki/Special:Random', weight: 3}
| |
| ];
| |
| | |
| var cloudHTML = '<div class="mp-card" style="margin-top: 1em;">' +
| |
| '<div class="mp-card-title">Popular Topics</div>' +
| |
| '<div class="mp-card-content" style="padding: 1.5em;">' +
| |
| '<div id="tag-cloud-3d" style="width: 100%; height: 400px; position: relative; perspective: 600px; background: radial-gradient(circle at center, #1e3a8a 0%, #0c1f47 100%); border-radius: 12px; overflow: hidden;">' +
| |
| '<div id="tag-sphere" style="position: absolute; width: 100%; height: 100%; transform-style: preserve-3d;"></div>' +
| |
| '</div>' +
| |
| '<div style="text-align: center; margin-top: 1em; font-size: 0.8em; color: #6b7280;">Hover to control • Larger = More popular</div>' +
| |
| '</div></div>';
| |
| | |
| placeholder.innerHTML = cloudHTML;
| |
| | |
| var container = document.getElementById('tag-sphere');
| |
| var cloudContainer = document.getElementById('tag-cloud-3d');
| |
| if (!container) return;
| |
| | |
| var radius = 160;
| |
| var dtr = Math.PI / 180;
| |
| var items = [];
| |
| var active = false;
| |
| var lasta = 1;
| |
| var lastb = 1;
| |
| var mouseX = 0;
| |
| var mouseY = 0;
| |
| var tspeed = 3;
| |
| | |
| // Create tags
| |
| tags.forEach(function(tag) {
| |
| var a = document.createElement('a');
| |
| a.href = tag.href;
| |
| a.textContent = tag.text;
| |
| a.style.position = 'absolute';
| |
| a.style.left = '0px';
| |
| a.style.top = '0px';
| |
| a.style.color = '#ffffff';
| |
| a.style.textDecoration = 'none';
| |
| a.style.padding = '0.3em 0.7em';
| |
| a.style.whiteSpace = 'nowrap';
| |
| a.style.fontWeight = '700';
| |
| a.style.textShadow = '0 2px 8px rgba(0,0,0,0.4)';
| |
| a.style.transition = 'all 0.3s ease';
| |
| a.style.borderRadius = '20px';
| |
| a.style.background = 'rgba(255, 255, 255, 0.15)';
| |
| a.style.backdropFilter = 'blur(10px)';
| |
|
| |
| // Font size based on weight (1em to 2.5em)
| |
| var fontSize = 0.8 + (tag.weight / 10) * 1.7;
| |
| a.style.fontSize = fontSize + 'em';
| |
|
| |
| a.addEventListener('mouseenter', function() {
| |
| this.style.background = 'rgba(255, 255, 255, 0.95)';
| |
| this.style.color = '#1e3a8a';
| |
| this.style.transform = 'scale(1.2)';
| |
| this.style.zIndex = '1000';
| |
| });
| |
|
| |
| a.addEventListener('mouseleave', function() {
| |
| this.style.background = 'rgba(255, 255, 255, 0.15)';
| |
| this.style.color = '#ffffff';
| |
| this.style.transform = 'scale(1)';
| |
| this.style.zIndex = 'auto';
| |
| });
| |
|
| |
| container.appendChild(a);
| |
|
| |
| var item = {
| |
| element: a,
| |
| cx: 0,
| |
| cy: 0,
| |
| cz: 0,
| |
| h: a.offsetHeight,
| |
| w: a.offsetWidth
| |
| };
| |
| items.push(item);
| |
| });
| |
| | |
| // Position items on sphere
| |
| positionAll();
| |
|
| |
| function positionAll() {
| |
| var phi = 0;
| |
| var theta = 0;
| |
| var max = items.length;
| |
| | |
| for (var i = 0; i < max; i++) {
| |
| phi = Math.acos(-1 + (2 * i + 1) / max);
| |
| theta = Math.sqrt(max * Math.PI) * phi;
| |
|
| |
|
| items[i].cx = radius * Math.cos(theta) * Math.sin(phi);
| | // Get site statistics via API |
| items[i].cy = radius * Math.sin(theta) * Math.sin(phi);
| | var api = new mw.Api(); |
| items[i].cz = radius * Math.cos(phi);
| |
| }
| |
| }
| |
|
| |
|
| // Rotation math
| | api.get({ |
| var sa, ca, sb, cb, sc, cc; | | action: 'query', |
| | | meta: 'siteinfo', |
| function sineCosine(a, b, c) { | | siprop: 'statistics', |
| sa = Math.sin(a * dtr);
| | format: 'json' |
| ca = Math.cos(a * dtr);
| | }).done(function(data) { |
| sb = Math.sin(b * dtr);
| | var stats = data.query.statistics; |
| cb = Math.cos(b * dtr);
| |
| sc = Math.sin(c * dtr);
| |
| cc = Math.cos(c * dtr);
| |
| }
| |
|
| |
|
| // Main animation loop | | // Create statistics banner |
| function update() { | | var banner = $('<div>').addClass('createaccount-statistics').html( |
| var a, b; | | '<div class="createaccount-statistics-title">CEO.wiki is made by people like you.</div>' + |
| | | '<div class="createaccount-statistics-grid">' + |
| if (active) {
| | '<div class="createaccount-stat-item">' + |
| var rect = cloudContainer.getBoundingClientRect();
| | '<div class="createaccount-stat-number">' + stats.edits.toLocaleString() + '</div>' + |
| a = (-Math.min(Math.max(-mouseY, -200), 200) / radius) * tspeed; | | '<div class="createaccount-stat-label">Edits</div>' + |
| b = (Math.min(Math.max(-mouseX, -200), 200) / radius) * tspeed; | | '</div>' + |
| } else {
| | '<div class="createaccount-stat-item">' + |
| a = lasta * 0.98;
| | '<div class="createaccount-stat-number">' + stats.pages.toLocaleString() + '</div>' + |
| b = lastb * 0.98; | | '<div class="createaccount-stat-label">Pages</div>' + |
| } | | '</div>' + |
| | '<div class="createaccount-stat-item">' + |
| | '<div class="createaccount-stat-number">' + stats.activeusers.toLocaleString() + '</div>' + |
| | '<div class="createaccount-stat-label">Recent Contributors</div>' + |
| | '</div>' + |
| | '</div>' |
| | ); |
|
| |
|
| lasta = a;
| | // Insert banner before the form |
| lastb = b;
| | $('#userloginForm').before(banner); |
| | | }); |
| // Continuous rotation when idle
| |
| if (Math.abs(a) <= 0.01 && Math.abs(b) <= 0.01 && !active) {
| |
| a = 0.5;
| |
| b = 0.5;
| |
| }
| |
| | |
| var c = 0;
| |
| sineCosine(a, b, c);
| |
|
| |
| for (var i = 0; i < items.length; i++) {
| |
| var item = items[i];
| |
|
| |
| // Rotate around X axis
| |
| var rx1 = item.cx;
| |
| var ry1 = item.cy * ca + item.cz * (-sa);
| |
| var rz1 = item.cy * sa + item.cz * ca;
| |
| | |
| // Rotate around Y axis
| |
| var rx2 = rx1 * cb + rz1 * sb;
| |
| var ry2 = ry1;
| |
| var rz2 = rx1 * (-sb) + rz1 * cb;
| |
| | |
| // Rotate around Z axis
| |
| var rx3 = rx2 * cc + ry2 * (-sc);
| |
| var ry3 = rx2 * sc + ry2 * cc;
| |
| var rz3 = rz2;
| |
| | |
| item.cx = rx3;
| |
| item.cy = ry3;
| |
| item.cz = rz3;
| |
| | |
| // Perspective calculation
| |
| var per = 500 / (500 + rz3);
| |
|
| |
| var x = rx3 * per;
| |
| var y = ry3 * per;
| |
|
| |
| // Position element
| |
| var l = (cloudContainer.offsetWidth / 2) + x - (item.w / 2);
| |
| var t = (cloudContainer.offsetHeight / 2) + y - (item.h / 2);
| |
|
| |
| item.element.style.left = l + 'px';
| |
| item.element.style.top = t + 'px';
| |
|
| |
| // Opacity based on depth
| |
| var alpha = (per - 0.5) * 2;
| |
| alpha = Math.max(0.3, Math.min(1, alpha));
| |
| item.element.style.opacity = alpha;
| |
|
| |
| // Z-index based on depth
| |
| item.element.style.zIndex = Math.floor(per * 100);
| |
| }
| |
| }
| |
| | |
| // Mouse interaction | |
| cloudContainer.addEventListener('mouseenter', function() { | |
| active = true;
| |
| });
| |
| | |
| cloudContainer.addEventListener('mouseleave', function() {
| |
| active = false;
| |
| });
| |
| | |
| cloudContainer.addEventListener('mousemove', function(e) {
| |
| var rect = this.getBoundingClientRect();
| |
| mouseX = e.clientX - rect.left - (this.offsetWidth / 2);
| |
| mouseY = e.clientY - rect.top - (this.offsetHeight / 2);
| |
| });
| |
| | |
| // Start animation
| |
| setInterval(update, 30);
| |
| }
| |
| | |
| if (document.readyState === 'loading') {
| |
| document.addEventListener('DOMContentLoaded', build3DTagCloud);
| |
| } else { | |
| build3DTagCloud();
| |
| }
| |
| })(); | | })(); |