MediaWiki:Vector.js: Difference between revisions

From Geometry Dash Expanded Wiki
Jump to navigation Jump to search
Link the header username to the user talk page; open the account menu only from its arrow
Preserve sidebar colour spans and icons when translating navigation labels
 
(13 intermediate revisions by the same user not shown)
Line 231: Line 231:
         accountName.className = 'gd-account-talk-link';
         accountName.className = 'gd-account-talk-link';
         var userName = mw.config.get('wgUserName');
         var userName = mw.config.get('wgUserName');
         accountName.href = mw.util.getUrl(userName ? 'User talk:' + userName : 'Special:UserLogin');
         accountName.href = mw.util.getUrl(userName ? 'User:' + userName : 'Special:UserLogin');
         accountName.innerHTML = iconMarkup('user');
         accountName.innerHTML = iconMarkup('user');
         var nameText = document.createElement('span');
         var nameText = document.createElement('span');
         nameText.textContent = userName || 'Account';
         nameText.textContent = userName || 'Account';
         accountName.appendChild(nameText);
         accountName.appendChild(nameText);
         accountName.title = userName ? 'Your talk page' : 'Log in';
         accountName.title = userName ? 'Your user page' : 'Log in';
         accountGroup.appendChild(accountName);
         accountGroup.appendChild(accountName);
         account.appendChild(accountLabel);
         account.appendChild(accountLabel);
        var duplicateUserPage = links.querySelector('#pt-userpage');
        if (duplicateUserPage) duplicateUserPage.remove();
         links.classList.add('gd-account-links');
         links.classList.add('gd-account-links');
         account.appendChild(links);
         account.appendChild(links);
         accountGroup.appendChild(account);
         accountGroup.appendChild(account);
         controls.appendChild(accountGroup);
         controls.appendChild(accountGroup);
        var barLogo = document.createElement('a');
        barLogo.className = 'gd-header-bar-logo';
        barLogo.href = mw.util.getUrl(mw.config.get('wgMainPageTitle') || 'Main Page');
        barLogo.title = mw.config.get('wgSiteName') || 'Geometry Dash Expanded Wiki';
        barLogo.setAttribute('aria-label', barLogo.title + ' main page');
        var logoImage = document.createElement('img');
        logoImage.src = 'https://geometrydashwiki.com/images/d/d4/Logo.jpg';
        logoImage.alt = ''; logoImage.width = 32; logoImage.height = 32;
        barLogo.appendChild(logoImage);
        personal.appendChild(barLogo);
         personal.appendChild(controls);
         personal.appendChild(controls);
         personal.classList.add('gd-header-ready');
         personal.classList.add('gd-header-ready');
Line 255: Line 267:
     });
     });
}());
}());
/* Expanded multilingual navigation: shared accounts, files and Recent Changes. */
(function () {
    'use strict';
    mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function () {
        $(function () {
            var controls = document.querySelector('.gd-header-controls');
            if (!controls || document.getElementById('gd-language-menu')) return;
            var page = (mw.config.get('wgPageName') || 'Main_Page').replace(/_/g, ' ');
            var match = /^(Spa|Rus|Pol)\/(.+)$/.exec(page);
            var pathCode = /^\/(eng|spa|rus|pol)(?:\/|$)/.exec(location.pathname);
            var articleCode = match ? match[1].toLowerCase() : 'eng';
            var cookie = /(?:^|;\s*)gdwiki-language=(eng|spa|rus|pol)(?:;|$)/.exec(document.cookie);
            var code = cookie ? cookie[1] : (mw.config.get('wgExpandedActiveLanguage') || (pathCode ? pathCode[1] : articleCode));
            var base = match ? match[2] : page;
            var spanish = code === 'spa';
            var isArticle = mw.config.get('wgNamespaceNumber') === 0;
            document.documentElement.setAttribute('data-gd-language', code);
            var definitions = [
                {code:'eng', name:'English', lang:'en', flag:'/images/4/49/Flag_gb.svg'},
                {code:'spa', name:'Español', lang:'es', flag:'/images/2/2d/Flag_es.svg'}
            ];
            var menu = document.createElement('details');
            menu.id = 'gd-language-menu'; menu.className = 'gd-header-menu gd-language-menu';
            var summary = document.createElement('summary');
            summary.setAttribute('aria-label', spanish ? 'Cambiar idioma' : 'Change language');
            summary.title = summary.getAttribute('aria-label');
            var current = definitions[spanish ? 1 : 0];
            var image = document.createElement('img'); image.src=current.flag; image.alt=''; image.width=24; image.height=16;
            summary.appendChild(image);
            var label=document.createElement('span'); label.textContent=current.name; summary.appendChild(label);
            menu.appendChild(summary);
            var panel=document.createElement('div'); panel.className='gd-header-dropdown gd-language-options';
            panel.setAttribute('aria-label', spanish ? 'Idiomas' : 'Languages');
            menu.appendChild(panel);
            var personal = document.getElementById('p-personal');
            personal.appendChild(menu);
            var api=new mw.Api();
            function route(title, lang) {
                if (!mw.config.get('wgExpandedLanguageRoutes')) return mw.util.getUrl(title);
                var m=/^(Spa|Rus|Pol)\/(.+)$/.exec(title);
                var language=m ? m[1].toLowerCase() : 'eng';
                var name=m ? m[2] : title;
                if (name.indexOf(':')!==-1 && !m) return mw.util.getUrl(title);
                return '/'+language+'/'+encodeURIComponent(name.replace(/ /g,'_')).replace(/%2F/g,'/');
            }
            function build(mapping) {
                var entry=mapping[base] || {};
                Object.keys(mapping).some(function (key) {
                    if (mapping[key][articleCode]===page) {base=key;entry=mapping[key];return true;}
                    return false;
                });
                var titles=definitions.map(function (d) {
                    return isArticle ? (entry[d.code] || (d.code==='eng' ? base : 'Spa/'+base)) : page;
                });
                api.get({action:'query',titles:titles.join('|'),prop:'info',formatversion:2}).then(function (data) {
                    var existing={};
                    (data.query.pages || []).forEach(function (p) {existing[p.title]=!p.missing && !p.invalid;});
                    definitions.forEach(function (d,i) {
                        var available=existing[titles[i]], target=available || !isArticle ? titles[i] : titles[0];
                        var link=document.createElement('a');link.lang=d.lang;link.hreflang=d.lang;
                        var flag=document.createElement('img');flag.src=d.flag;flag.alt='';flag.width=24;flag.height=16;link.appendChild(flag);
                        var text=document.createElement('span');text.textContent=d.name;link.appendChild(text);
                        if(d.code===code)link.setAttribute('aria-current','page');
                        link.href=route(target,d.lang);
                        link.addEventListener('click',function () {
                            document.cookie='gdwiki-language='+d.code+'; Path=/; Max-Age=31536000; SameSite=Lax; Secure';
                            try {
                                localStorage.setItem('gdwiki-language',d.code);
                                if(isArticle && !available)sessionStorage.setItem('gdwiki-fallback',JSON.stringify({language:d.code,page:base}));
                            } catch(error) {}
                        });
                        panel.appendChild(link);
                    });
                    if(isArticle && !existing[titles[1]]) {
                        var create=document.createElement('a');create.className='gd-language-create';
                        create.textContent=spanish ? 'Crear traducción al español' : 'Create Spanish translation';
                        if(mw.config.get('wgExpandedLanguageEditorSafe')) {
                            var editParams={action:'edit',preload:titles[0],summary:'Start Español translation from [['+titles[0]+']]'};
                            create.href=route(titles[1],'es')+'?'+new URLSearchParams(editParams).toString();
                        } else {create.setAttribute('aria-disabled','true');create.title='Translation setup awaiting server update';}
                        panel.appendChild(create);
                    }
                    var home=document.createElement('a');home.className='gd-language-home';home.textContent=spanish ? 'Inicio' : 'Home';home.href=route('Main Page','en');panel.appendChild(home);
                    var guide=document.createElement('a');guide.textContent=spanish ? 'Guía de traducción' : 'Translation guide';guide.href=mw.util.getUrl('Help:Translations');panel.appendChild(guide);
                }, function () {panel.textContent=spanish ? 'No se pudieron cargar los idiomas. Recarga la página.' : 'Languages could not load. Reload the page.';});
            }
            api.get({action:'query',titles:'MediaWiki:Expanded-language-pages.json',prop:'revisions',rvprop:'content',rvslots:'main',formatversion:2}).then(function (data) {
                try {build(JSON.parse(data.query.pages[0].revisions[0].slots.main.content));} catch (error) {build({});}
            },function () {build({});});
            document.addEventListener('click',function (event) {if (!menu.contains(event.target)) menu.open=false;});
            document.addEventListener('keydown',function (event) {if (event.key==='Escape') menu.open=false;});
            if (spanish) {
                var interfaceLabels={"Navigation": "Navegación", "Main page": "Página principal", "Recent changes": "Cambios recientes", "New files": "Archivos nuevos", "Random page": "Página aleatoria", "New here? Get started": "¿Eres nuevo? Empieza aquí", ">> Site policy": ">> Normas del sitio", ">> Staff": ">> Administración", "Help using MediaWiki": "Ayuda sobre MediaWiki", "GD Base Content": "Contenido base de GD", "Games": "Juegos", "Main levels": "Niveles principales", "Gameplay": "Jugabilidad", "Rewards": "Recompensas", "Rooms (vaults etc.)": "Salas (bóvedas, etc.)", "Level editing": "Editor de niveles", "Account features": "Funciones de la cuenta", "Updates": "Actualizaciones", "GD Community Content": "Contenido de la comunidad de GD", "Demon List levels": "Niveles de la Demon List", "Gauntlet levels": "Niveles de gauntlets", "Map Pack levels": "Niveles de paquetes de mapas", "Classic levels": "Niveles clásicos", "Platformer levels": "Niveles de plataformas", "Players": "Jugadores", "Community activities": "Actividades de la comunidad", "Wiki Resources": "Recursos de la wiki", "Propose new articles": "Proponer artículos nuevos", "Submit edits for review": "Enviar cambios para revisión", "Admin Dashboard": "Panel de administración", "Images": "Imágenes", "Videos": "Vídeos", "Audio": "Audio", "PDFs": "PDF", "Templates": "Plantillas", "Sandbox": "Zona de pruebas", "Discuss the wiki": "Debatir sobre la wiki", "Guestbook": "Libro de visitas", "Tools": "Herramientas", "What links here": "Lo que enlaza aquí", "Related changes": "Cambios relacionados", "Upload file": "Subir archivo", "Printable version": "Versión para imprimir", "Permanent link": "Enlace permanente", "Page information": "Información de la página", "Account": "Cuenta", "Preferences": "Preferencias", "Your user page": "Tu página de usuario", "Log in": "Iniciar sesión", "Account menu": "Menú de cuenta", "Appearance": "Apariencia"};
                document.querySelectorAll('#mw-panel h3, #mw-panel h3 span, #mw-panel a, .gd-account-links a, .gd-account-talk-link span').forEach(function (element) {
                    var key=element.textContent.trim();
                    if (interfaceLabels[key]) {
                        // Translate text nodes without removing colour spans, links or icons.
                        var walker=document.createTreeWalker(element,NodeFilter.SHOW_TEXT), node;
                        while ((node=walker.nextNode())) {
                            var original=node.nodeValue.trim();
                            if (interfaceLabels[original]) node.nodeValue=node.nodeValue.replace(original,interfaceLabels[original]);
                        }
                    }
                });
                document.querySelectorAll('#p-personal [title], #p-personal [aria-label]').forEach(function (element) {
                    ['title','aria-label'].forEach(function (attr) {var key=element.getAttribute(attr);if(interfaceLabels[key])element.setAttribute(attr,interfaceLabels[key]);});
                });
                var appearance=controls.querySelector('.gd-header-menu summary');
                if (appearance) {appearance.textContent='Apariencia';appearance.title='Apariencia';appearance.setAttribute('aria-label','Apariencia');}
                var account=controls.querySelector('.gd-account-menu summary');
                if (account) {account.title='Menú de cuenta';account.setAttribute('aria-label','Menú de cuenta');}
                var titles={'Blue':'Azul','Purple':'Morado','Green':'Verde','Red':'Rojo','Dark':'Oscuro','Blackout':'Negro','Vault':'La bóveda','Vault of Secrets':'La bóveda de los secretos','Chamber of Time':'La cámara del tiempo'};
                controls.querySelectorAll('.gd-theme-option').forEach(function (button) {if(titles[button.textContent])button.textContent=titles[button.textContent];});
            }
            // Optional translated assets use the same existing theme filters.
            var logos=mw.config.get('wgExpandedLanguageLogos') || {};
            logos.spa=Object.assign({"header": "https://geometrydashwiki.com/images/4/4a/Logo-spa.jpg", "banner": "https://geometrydashwiki.com/images/a/a7/Site-logo-spa.png"},logos.spa || {});
            if (logos[code]) {
                if (logos[code].header) {
                    var header=document.querySelector('.gd-header-bar-logo img');if(header)header.src=logos[code].header;
                }
                if (logos[code].banner) document.documentElement.style.setProperty('--wiki-logo-image','url("'+logos[code].banner.replace(/["\\]/g,'')+'")');
            }
        });
    });
}());
/* Keep language parameters and script endpoints out of visible navigation URLs. */
(function () {
    'use strict';
    function clean(raw) {
        var url;
        try {url=new URL(raw,location.href);} catch(error) {return raw;}
        if (url.origin!==location.origin) return raw;
        url.searchParams.delete('uselang');
        if (/\/index\.php$/.test(url.pathname) && !url.searchParams.has('title') && url.searchParams.has('search')) {url.pathname='/wiki/Special:Search';}
        if (/\/index\.php$/.test(url.pathname) && url.searchParams.has('title')) {
            var title=url.searchParams.get('title');url.searchParams.delete('title');
            var match=/^(Spa|Rus|Pol)\/(.+)$/.exec(title.replace(/_/g,' '));
            var ns=title.indexOf(':')!==-1;
            var prefix=match ? '/'+match[1].toLowerCase()+'/' : (ns ? '/wiki/' : '/eng/');
            var name=match ? match[2] : title;
            url.pathname=prefix+encodeURIComponent(name.replace(/ /g,'_')).replace(/%2F/g,'/').replace(/%3A/g,':');
        }
        return url.pathname+url.search+url.hash;
    }
    // The current request has already chosen its interface language.
    var address=new URL(location.href);
    var cleanAddress=clean(location.href);
    if (cleanAddress!==address.pathname+address.search+address.hash) history.replaceState(null,'',cleanAddress);
    function links() {
        document.querySelectorAll('a[href]').forEach(function (link) {
            var href=link.getAttribute('href');
            if (href && !/^(?:#|javascript:|mailto:)/.test(href)) {
                var replacement=clean(href);if(replacement!==href)link.setAttribute('href',replacement);
            }
        });
    }
    $(function () {
        links();
        var pending=false;
        new MutationObserver(function () {
            if(pending)return;pending=true;
            requestAnimationFrame(function () {pending=false;links();});
        }).observe(document.body,{childList:true,subtree:true});
    });
    mw.hook('wikipage.content').add(links);
    // Also handles asynchronously built language and account menus.
    document.addEventListener('click',function(event) {
        var link=event.target.closest && event.target.closest('a[href]');
        if(link)link.setAttribute('href',clean(link.getAttribute('href')));
    },true);
}());
/* Article-language tabs and links follow the persistent site language. */
(function () {
    'use strict';
    mw.loader.using(['mediawiki.api','mediawiki.util']).then(function () {
        $(function () {
            var cookie=/(?:^|;\s*)gdwiki-language=(eng|spa|rus|pol)(?:;|$)/.exec(document.cookie);
            var mode=cookie ? cookie[1] : (mw.config.get('wgExpandedActiveLanguage') || mw.config.get('wgExpandedLanguage') || 'eng');
            var names={eng:'English',spa:'Español',rus:'Русский',pol:'Polski'};
            var notices={eng:'This article is not available in English. Showing the original article.',spa:'Este artículo todavía no está disponible en español. Se muestra la versión en inglés.',rus:'Эта статья пока недоступна на русском языке. Показана английская версия.',pol:'Ten artykuł nie jest jeszcze dostępny po polsku. Wyświetlono wersję angielską.'};
            try {
                var pending=JSON.parse(sessionStorage.getItem('gdwiki-fallback') || 'null');
                sessionStorage.removeItem('gdwiki-fallback');
                if(!pending && mw.config.get('wgExpandedLanguageFallback'))pending={language:mw.config.get('wgExpandedLanguageFallback')};
                if(pending && notices[pending.language])mw.notify(notices[pending.language],{tag:'gd-language-fallback',autoHideSeconds:10});
            } catch(error) {}
            function rememberFallback(language) {
                try {sessionStorage.setItem('gdwiki-fallback',JSON.stringify({language:language}));} catch(error) {}
            }
            function normal(title) {title=title.replace(/_/g,' ').trim();return title.charAt(0).toUpperCase()+title.slice(1);}
            function bare(title) {return title.replace(/^(?:Spa|Rus|Pol)\//,'');}
            function route(title) {
                var m=/^(Spa|Rus|Pol)\/(.+)$/.exec(title);
                return '/'+(m ? m[1].toLowerCase() : 'eng')+'/'+encodeURIComponent((m ? m[2] : title).replace(/ /g,'_')).replace(/%2F/g,'/');
            }
            var api=new mw.Api();
            api.get({action:'query',titles:'MediaWiki:Expanded-language-pages.json',prop:'revisions',rvprop:'content',rvslots:'main',formatversion:2}).then(function (data) {
                var map;
                try {map=JSON.parse(data.query.pages[0].revisions[0].slots.main.content);} catch(error) {map={};}
                var aliases={};
                Object.keys(map).forEach(function (root) {
                    if(!map[root] || typeof map[root]!=='object')return;
                    [root].concat(Object.keys(map[root]).map(function (key) {return map[root][key];})).forEach(function (title) {
                        if(typeof title!=='string')return;
                        [normal(title),normal(bare(title))].forEach(function (alias) {
                            if(!aliases[alias])aliases[alias]=[];
                            if(aliases[alias].indexOf(root)===-1)aliases[alias].push(root);
                        });
                    });
                });
                function group(title) {
                    var roots=aliases[normal(title)] || [];
                    if(roots.length===1)return map[roots[0]];
                    if(roots.length>1)return null; // Never guess between unrelated concepts.
                    var english=bare(normal(title));return {eng:english,spa:'Spa/'+english,rus:'Rus/'+english,pol:'Pol/'+english};
                }
                var links=[],checks={},current=(mw.config.get('wgPageName') || '').replace(/_/g,' ');
                document.querySelectorAll('#mw-content-text a[href], #mw-panel a[href], .gd-header-bar-logo, .gd-language-home').forEach(function (link) {
                    var url;try {url=new URL(link.href);}catch(error){return;}
                    if(url.origin!==location.origin)return;
                    if(link.getAttribute('href').charAt(0)==='#')return;
                    var path=/^\/(?:eng|spa|rus|pol|wiki)\/(.+)$/.exec(url.pathname);
                    if(!path && url.pathname!=='/')return;
                    var title=path ? decodeURIComponent(path[1]).replace(/_/g,' ') : (mw.config.get('wgMainPageTitle') || 'Main Page');
                    if(/^\/(spa|rus|pol)\//.test(url.pathname))title=url.pathname.split('/')[1].charAt(0).toUpperCase()+url.pathname.split('/')[1].slice(1)+'/'+title;
                    if(title.indexOf(':')!==-1)return;
                    var action=url.searchParams.get('action');
                    var redlink=url.searchParams.get('redlink')==='1';
                    if((action && action!=='view' && !redlink) || url.searchParams.has('veaction') || url.searchParams.has('oldid') || url.searchParams.has('diff'))return;
                    if(normal(title)===normal(current) && url.hash)return;
                    var versions=group(title);if(!versions)return;
                    Object.keys(versions).forEach(function (lang) {checks[versions[lang]]=true;});
                    links.push({element:link,url:url,title:title,versions:versions});
                });
                var currentVersions=group(current);
                if(currentVersions)Object.keys(currentVersions).forEach(function (lang) {checks[currentVersions[lang]]=true;});
                var titles=Object.keys(checks),batches=[];
                for(var i=0;i<titles.length;i+=40)batches.push(titles.slice(i,i+40));
                Promise.all(batches.map(function (batch) {return api.get({action:'query',titles:batch.join('|'),prop:'info',formatversion:2});})).then(function (results) {
                    var exists={};results.forEach(function (result) {(result.query.pages || []).forEach(function (p) {exists[normal(p.title)]=!p.missing && !p.invalid;});});
                    links.forEach(function (item) {
                        var preferred=item.versions[mode],english=item.versions.eng;
                        var available=preferred && exists[normal(preferred)];
                        var target=available ? preferred : english;
                        if(!target || !exists[normal(target)])return;
                        item.url.searchParams.delete('title');item.url.searchParams.delete('uselang');item.url.searchParams.delete('redlink');if(item.url.searchParams.get('action')==='edit')item.url.searchParams.delete('action');
                        item.element.href=route(target)+(item.url.searchParams.size ? '?'+item.url.searchParams.toString() : '')+item.url.hash;
                        item.element.classList.remove('new');
                        if(!available && mode!=='eng')item.element.addEventListener('click',function () {rememberFallback(mode);});
                        // Prefixes are storage details, not part of the displayed article name.
                        if(item.element.textContent.trim()===item.title)item.element.textContent=bare(item.title);
                    });
                    if(!currentVersions || mw.config.get('wgNamespaceNumber')!==0 || mw.config.get('wgAction')!=='view')return;
                    var languages=Object.keys(names).filter(function (lang) {return currentVersions[lang] && exists[normal(currentVersions[lang])];});
                    if(languages.length<2 || document.getElementById('gd-article-languages'))return;
                    var nav=document.createElement('nav');nav.id='gd-article-languages';nav.className='gd-article-languages';nav.setAttribute('aria-label',mode==='spa' ? 'Idiomas del artículo' : 'Article languages');
                    languages.forEach(function (lang) {
                        var link=document.createElement('a');link.textContent=names[lang];link.href=route(currentVersions[lang]);link.setAttribute('data-gd-language-tab','');
                        if(normal(currentVersions[lang])===normal(current))link.setAttribute('aria-current','page');
                        nav.appendChild(link);
                    });
                    var heading=document.getElementById('firstHeading');if(heading){document.body.classList.add('gd-has-language-tabs');heading.appendChild(nav);}
                },function () { /* Preserve original working links if availability cannot be checked. */ });
            });
        });
    });
}());
/* Infobox labels: reader language on shared pages, article language on articles. */
(function () {
'use strict';
var labels={"Born": {"spa": "Nacimiento", "rus": "Дата рождения", "pol": "Data urodzenia"}, "Died": {"spa": "Fallecimiento", "rus": "Дата смерти", "pol": "Data śmierci"}, "Other names": {"spa": "Otros nombres", "rus": "Другие имена", "pol": "Inne nazwy"}, "Country": {"spa": "País", "rus": "Страна", "pol": "Kraj"}, "Mod status": {"spa": "Estado de moderador", "rus": "Статус модератора", "pol": "Status moderatora"}, "Uploaded levels": {"spa": "Niveles publicados", "rus": "Опубликованные уровни", "pol": "Opublikowane poziomy"}, "Rated levels": {"spa": "Niveles calificados", "rus": "Оценённые уровни", "pol": "Ocenione poziomy"}, "Creator points": {"spa": "Puntos de creador", "rus": "Очки создателя", "pol": "Punkty twórcy"}, "GD stats": {"spa": "Estadísticas de GD", "rus": "Статистика GD", "pol": "Statystyki GD"}, "Social media": {"spa": "Redes sociales", "rus": "Социальные сети", "pol": "Media społecznościowe"}, "Other": {"spa": "Otros", "rus": "Другое", "pol": "Inne"}, "Please search": {"spa": "Buscar", "rus": "Найти", "pol": "Wyszukaj"}, "Information": {"spa": "Información", "rus": "Информация", "pol": "Informacje"}, "Statistics": {"spa": "Estadísticas", "rus": "Статистика", "pol": "Statystyki"}, "Total jumps": {"spa": "Saltos totales", "rus": "Всего прыжков", "pol": "Łączna liczba skoków"}, "Total attempts": {"spa": "Intentos totales", "rus": "Всего попыток", "pol": "Łączna liczba prób"}, "Main levels": {"spa": "Niveles principales", "rus": "Основные уровни", "pol": "Główne poziomy"}, "Online levels": {"spa": "Niveles en línea", "rus": "Онлайн-уровни", "pol": "Poziomy online"}, "Daily levels": {"spa": "Niveles diarios", "rus": "Ежедневные уровни", "pol": "Poziomy dzienne"}, "Gauntlets": {"spa": "Gauntlets", "rus": "Испытания", "pol": "Gauntlety"}, "Map packs": {"spa": "Paquetes de mapas", "rus": "Наборы уровней", "pol": "Pakiety map"}, "Liked / disliked": {"spa": "Me gusta / no me gusta", "rus": "Лайки / дизлайки", "pol": "Polubione / nielubiane"}, "Chests opened": {"spa": "Cofres abiertos", "rus": "Открытые сундуки", "pol": "Otwarte skrzynie"}, "Players destroyed": {"spa": "Jugadores destruidos", "rus": "Уничтоженные игроки", "pol": "Zniszczeni gracze"}, "Leaderboard": {"spa": "Clasificación", "rus": "Таблица лидеров", "pol": "Ranking"}, "Last updated": {"spa": "Última actualización", "rus": "Последнее обновление", "pol": "Ostatnia aktualizacja"}};
function translate(root) {
var page=mw.config.get('wgPageName') || '',match=/^(Spa|Rus|Pol)\//.exec(page),cookie=/(?:^|;\s*)gdwiki-language=(eng|spa|rus|pol)(?:;|$)/.exec(document.cookie);
var lang=mw.config.get('wgNamespaceNumber')===0 ? (match ? match[1].toLowerCase() : 'eng') : (cookie ? cookie[1] : (mw.config.get('wgExpandedActiveLanguage') || 'eng'));
root.querySelectorAll('[data-gd-infobox-label]').forEach(function (el) {var key=el.getAttribute('data-gd-infobox-label');el.textContent=(labels[key] && labels[key][lang]) || key;});
root.querySelectorAll('[data-gd-stats-name]').forEach(function(el){var name=el.getAttribute('data-gd-stats-name');el.textContent=lang==='spa' ? 'Estadísticas de '+name : lang==='rus' ? 'Статистика '+name : lang==='pol' ? 'Statystyki '+name : name+"’s stats";});
}
$(function(){translate(document);mw.hook('wikipage.content').add(function(content){translate(content[0] || document);});});
})();

Latest revision as of 21:09, 14 October 2026

/* All JavaScript here will be loaded for users of the Vector skin */

/***************************/
/* Interface Modification **/
/***************************/

/* JavaScript Load Class (REQUIRED BY DEFAULT VECTOR.CSS) */
$(function () {
	document.body.classList.add('has-vector-mobile-menu');
});

/* Adapt standard Vector's structure for the imported stylesheet. */
$(function () {
    if (!document.body.classList.contains('skin-vector-legacy')) return;

    var content = document.getElementById('content');
    var navigation = document.getElementById('mw-navigation');
    if (!content || !navigation) return;

    if (!content.parentElement.classList.contains('content-wrapper')) {
        var wrapper = document.createElement('div');
        wrapper.className = 'content-wrapper';
        content.before(wrapper);
        wrapper.append(navigation, content);
    }

    if (!content.querySelector('.content-body')) {
        $(content).wrapInner('<div class="content-body"></div>');
    }
});

/* Search Toggle Button */
$(function () {
	var buttonClass = 'button-toggle button-toggle-search';
	var expandedClass = 'search--expanded';
	let searchButton = document.createElement('button');
	let sidebar = document.getElementById('mw-navigation');
	searchButton.className = buttonClass;
	searchButton.addEventListener('click', function () {
		searchButton.classList.toggle(expandedClass);
		sidebar.classList.toggle(expandedClass);
	});
	sidebar.prepend(searchButton);
});


/* Mobile Sidebar Toggle Button */
$(function () {
	var buttonClass = 'button-toggle button-toggle-sidebar-mobile';
	var expandedClass = 'sidebar-mobile--expanded';
	let mobileSidebarButton = document.createElement('button');
	let sidebar = document.getElementById('mw-navigation');
	mobileSidebarButton.className = buttonClass;
	mobileSidebarButton.addEventListener('click', function () {
		mobileSidebarButton.classList.toggle(expandedClass);
		sidebar.classList.toggle(expandedClass);
	});
	sidebar.prepend(mobileSidebarButton);
});


/* Sidebar Toggle Button */
$(function () {
	var buttonClass = 'button-toggle button-toggle-sidebar';
	var expandedClass = 'sidebar--collapsed';
	let sidebarButton = document.createElement('button');
	let sidebar = document.getElementById('mw-navigation');
	sidebarButton.className = buttonClass;
	sidebarButton.addEventListener('click', function () {
		sidebarButton.classList.toggle(expandedClass);
		sidebar.classList.toggle(expandedClass);
	});
	sidebar.prepend(sidebarButton);
});


/* Sidebar Box Content Collaspible Toggles */
$(function() {
    // configure sections (by header string) to initially collapse for all users by default
    var initiallyCollapsedIDs = []; /* e.g. ['Character_Classes', 'Items', 'External_Links'] */

    // add the p- onto ids for ease of use, if not present
    initiallyCollapsedIDs.forEach(function(id, index) {
        if(!id.startsWith('p-')) {initiallyCollapsedIDs[index] = 'p-' + id;}
    });
	var $portals = $("#mw-panel .vector-menu-portal");
	var setup = function() {
		$portals.each(function(index, el) {
			if(window.innerWidth < 721) {return;}
			var $el = $(el);
			var $id = $el.attr("id");
			if(!$id) {return;}
			// check if this portal should be collapsed - portal is set to be initially collapsed and user has not indicated wanting it open || user has indicated wanting this portal collapsed
			if(
				(initiallyCollapsedIDs.includes($id) && localStorage.getItem('sidebar_c_'+$id) != "n") || (localStorage.getItem('sidebar_c_'+$id) === "y")
			) {$el.addClass('collapsed').find('.vector-menu-content').slideUp(0);}
		});
	};
	setup();
	$(window).on('resize', setup);
	$portals.on("click", "h3", function(event) {
		if(window.innerWidth < 721) {return;}
		var $el = $(this).parent();
		var $id = $el.attr("id");
		if(!$id) {return;}
		$el.toggleClass('collapsed');
		if($el.hasClass('collapsed')) { // more consistent between class and slide status
			localStorage.setItem('sidebar_c_'+$id, "y");
			$el.find('.vector-menu-content').slideUp('fast');
		}
		else {
			localStorage.setItem('sidebar_c_'+$id, "n");
			$el.find('.vector-menu-content').slideDown('fast');
		}
	});
});


/* Header Dropdown Menu Purge Cache Link */
$(function() {
	if (mw.config.get('wgNamespaceNumber') === -1 ) {return;} // disable on special pages
	var url = new URL(window.location.href);
	url.searchParams.set('action', 'purge');
	var button = mw.util.addPortletLink('p-cactions', url.href, 'Purge cache', 'ca-gadget-purgecache', 'Purge the server cache for this page', '1');
	button.addEventListener('click', (function(event) {
		event.preventDefault();
		var api = new mw.Api();
		api.post({
			action: 'purge',
			titles: mw.config.get('wgPageName'),
			forcerecursivelinkupdate: true
		}).then(function() {
			window.location.reload(true);
		}).fail(function(code) {
			mw.notify('Page purge unsuccessful. Error code: $1'.replace('$1', code));
		});
	}));
	console.log('purge loaded');
});

/* Independent GD Wiki header: themes and existing account links. */
(function () {
    'use strict';
    var themes = [
        ['blue', 'Geometry Dash'], ['green', 'Vault'],
        ['purple', 'Vault of Secrets'], ['red', 'Chamber of Time'],
        ['dark', 'Dark'], ['blackout', 'Blackout']
    ];
    var storageKey = 'gdwiki-theme';
    function applyTheme(key) {
        if (!themes.some(function (theme) { return theme[0] === key; })) key = 'blue';
        var root = document.documentElement;
        themes.forEach(function (theme) { root.classList.remove('theme-' + theme[0]); });
        root.classList.remove('view-light');
        root.classList.add('view-dark', 'theme-' + key);
        return key;
    }
    var selected = 'blue';
    try { selected = localStorage.getItem(storageKey) || selected; } catch (error) {}
    selected = applyTheme(selected);
    $(function () {
        if (!document.body.classList.contains('skin-vector-legacy')) return;
        var personal = document.getElementById('p-personal');
        if (!personal || personal.querySelector('.gd-header-controls')) return;
        var links = personal.querySelector('ul');
        if (!links) return;
        var controls = document.createElement('div');
        controls.className = 'gd-header-controls';
        var appearance = document.createElement('details');
        appearance.className = 'gd-header-menu';
        var appearanceLabel = document.createElement('summary');
        appearanceLabel.textContent = 'Appearance';
        appearance.appendChild(appearanceLabel);
        var themePanel = document.createElement('div');
        themePanel.className = 'gd-header-dropdown';
        themePanel.setAttribute('role', 'group');
        themePanel.setAttribute('aria-label', 'Theme');
        themes.forEach(function (theme) {
            var button = document.createElement('button');
            button.type = 'button';
            button.className = 'gd-theme-option';
            button.textContent = theme[1];
            button.setAttribute('aria-pressed', String(theme[0] === selected));
            button.addEventListener('click', function () {
                selected = applyTheme(theme[0]);
                try { localStorage.setItem(storageKey, selected); } catch (error) {}
                themePanel.querySelectorAll('button').forEach(function (item) {
                    item.setAttribute('aria-pressed', String(item === button));
                });
                appearance.open = false;
            });
            themePanel.appendChild(button);
        });
        appearance.appendChild(themePanel);
        controls.appendChild(appearance);
        var icons = {
            gear: '<path d="m10 2 .5 2a8 8 0 0 1 3 0l.5-2 3 1 0 2a8 8 0 0 1 2 2l2 0 1 3-2 .5a8 8 0 0 1 0 3l2 .5-1 3-2 0a8 8 0 0 1-2 2l0 2-3 1-.5-2a8 8 0 0 1-3 0L10 22l-3-1 0-2a8 8 0 0 1-2-2l-2 0-1-3 2-.5a8 8 0 0 1 0-3L2 10l1-3 2 0a8 8 0 0 1 2-2l0-2z"/><circle cx="12" cy="12" r="3" fill="#03052f"/>',
            watch: '<path d="M3 3h18v15h-6l-2 3h-2l-2-3H3zm2 2v11h5l2 3 2-3h5V5z"/>',
            user: '<circle cx="12" cy="7" r="4"/><path d="M3 22v-2a9 9 0 0 1 18 0v2z"/>'
        };
        function iconMarkup(name) {
            return '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">' + icons[name] + '</svg>';
        }
        function iconLink(id, name, label) {
            var original = document.querySelector('#' + id + ' a');
            if (!original) return;
            var link = original.cloneNode(false);
            link.className = 'gd-header-icon';
            link.title = label;
            link.setAttribute('aria-label', label);
            link.innerHTML = iconMarkup(name);
            controls.appendChild(link);
        }
        iconLink('pt-preferences', 'gear', 'Preferences');
        // Use Echo's actual widgets so alerts and notices retain their behavior.
        ['pt-notifications-alert', 'pt-notifications-notice'].forEach(function (id) {
            var notification = document.getElementById(id);
            if (notification) {
                notification.classList.add('gd-header-notification');
                controls.appendChild(notification);
            }
        });
        var account = document.createElement('details');
        account.className = 'gd-header-menu gd-account-menu';
        var accountLabel = document.createElement('summary');
        accountLabel.setAttribute('aria-label', 'Account menu');
        accountLabel.title = 'Account menu';
        var accountGroup = document.createElement('div');
        accountGroup.className = 'gd-account-group';
        var accountName = document.createElement('a');
        accountName.className = 'gd-account-talk-link';
        var userName = mw.config.get('wgUserName');
        accountName.href = mw.util.getUrl(userName ? 'User:' + userName : 'Special:UserLogin');
        accountName.innerHTML = iconMarkup('user');
        var nameText = document.createElement('span');
        nameText.textContent = userName || 'Account';
        accountName.appendChild(nameText);
        accountName.title = userName ? 'Your user page' : 'Log in';
        accountGroup.appendChild(accountName);
        account.appendChild(accountLabel);
        var duplicateUserPage = links.querySelector('#pt-userpage');
        if (duplicateUserPage) duplicateUserPage.remove();
        links.classList.add('gd-account-links');
        account.appendChild(links);
        accountGroup.appendChild(account);
        controls.appendChild(accountGroup);
        var barLogo = document.createElement('a');
        barLogo.className = 'gd-header-bar-logo';
        barLogo.href = mw.util.getUrl(mw.config.get('wgMainPageTitle') || 'Main Page');
        barLogo.title = mw.config.get('wgSiteName') || 'Geometry Dash Expanded Wiki';
        barLogo.setAttribute('aria-label', barLogo.title + ' main page');
        var logoImage = document.createElement('img');
        logoImage.src = 'https://geometrydashwiki.com/images/d/d4/Logo.jpg';
        logoImage.alt = ''; logoImage.width = 32; logoImage.height = 32;
        barLogo.appendChild(logoImage);
        personal.appendChild(barLogo);
        personal.appendChild(controls);
        personal.classList.add('gd-header-ready');
        document.addEventListener('click', function (event) {
            [appearance, account].forEach(function (menu) {
                if (!menu.contains(event.target)) menu.open = false;
            });
        });
        document.addEventListener('keydown', function (event) {
            if (event.key === 'Escape') { appearance.open = false; account.open = false; }
        });
    });
}());
/* Expanded multilingual navigation: shared accounts, files and Recent Changes. */
(function () {
    'use strict';
    mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function () {
        $(function () {
            var controls = document.querySelector('.gd-header-controls');
            if (!controls || document.getElementById('gd-language-menu')) return;
            var page = (mw.config.get('wgPageName') || 'Main_Page').replace(/_/g, ' ');
            var match = /^(Spa|Rus|Pol)\/(.+)$/.exec(page);
            var pathCode = /^\/(eng|spa|rus|pol)(?:\/|$)/.exec(location.pathname);
            var articleCode = match ? match[1].toLowerCase() : 'eng';
            var cookie = /(?:^|;\s*)gdwiki-language=(eng|spa|rus|pol)(?:;|$)/.exec(document.cookie);
            var code = cookie ? cookie[1] : (mw.config.get('wgExpandedActiveLanguage') || (pathCode ? pathCode[1] : articleCode));
            var base = match ? match[2] : page;
            var spanish = code === 'spa';
            var isArticle = mw.config.get('wgNamespaceNumber') === 0;
            document.documentElement.setAttribute('data-gd-language', code);
            var definitions = [
                {code:'eng', name:'English', lang:'en', flag:'/images/4/49/Flag_gb.svg'},
                {code:'spa', name:'Español', lang:'es', flag:'/images/2/2d/Flag_es.svg'}
            ];
            var menu = document.createElement('details');
            menu.id = 'gd-language-menu'; menu.className = 'gd-header-menu gd-language-menu';
            var summary = document.createElement('summary');
            summary.setAttribute('aria-label', spanish ? 'Cambiar idioma' : 'Change language');
            summary.title = summary.getAttribute('aria-label');
            var current = definitions[spanish ? 1 : 0];
            var image = document.createElement('img'); image.src=current.flag; image.alt=''; image.width=24; image.height=16;
            summary.appendChild(image);
            var label=document.createElement('span'); label.textContent=current.name; summary.appendChild(label);
            menu.appendChild(summary);
            var panel=document.createElement('div'); panel.className='gd-header-dropdown gd-language-options';
            panel.setAttribute('aria-label', spanish ? 'Idiomas' : 'Languages');
            menu.appendChild(panel);
            var personal = document.getElementById('p-personal');
            personal.appendChild(menu);
            var api=new mw.Api();
            function route(title, lang) {
                if (!mw.config.get('wgExpandedLanguageRoutes')) return mw.util.getUrl(title);
                var m=/^(Spa|Rus|Pol)\/(.+)$/.exec(title);
                var language=m ? m[1].toLowerCase() : 'eng';
                var name=m ? m[2] : title;
                if (name.indexOf(':')!==-1 && !m) return mw.util.getUrl(title);
                return '/'+language+'/'+encodeURIComponent(name.replace(/ /g,'_')).replace(/%2F/g,'/');
            }
            function build(mapping) {
                var entry=mapping[base] || {};
                Object.keys(mapping).some(function (key) {
                    if (mapping[key][articleCode]===page) {base=key;entry=mapping[key];return true;}
                    return false;
                });
                var titles=definitions.map(function (d) {
                    return isArticle ? (entry[d.code] || (d.code==='eng' ? base : 'Spa/'+base)) : page;
                });
                api.get({action:'query',titles:titles.join('|'),prop:'info',formatversion:2}).then(function (data) {
                    var existing={};
                    (data.query.pages || []).forEach(function (p) {existing[p.title]=!p.missing && !p.invalid;});
                    definitions.forEach(function (d,i) {
                        var available=existing[titles[i]], target=available || !isArticle ? titles[i] : titles[0];
                        var link=document.createElement('a');link.lang=d.lang;link.hreflang=d.lang;
                        var flag=document.createElement('img');flag.src=d.flag;flag.alt='';flag.width=24;flag.height=16;link.appendChild(flag);
                        var text=document.createElement('span');text.textContent=d.name;link.appendChild(text);
                        if(d.code===code)link.setAttribute('aria-current','page');
                        link.href=route(target,d.lang);
                        link.addEventListener('click',function () {
                            document.cookie='gdwiki-language='+d.code+'; Path=/; Max-Age=31536000; SameSite=Lax; Secure';
                            try {
                                localStorage.setItem('gdwiki-language',d.code);
                                if(isArticle && !available)sessionStorage.setItem('gdwiki-fallback',JSON.stringify({language:d.code,page:base}));
                            } catch(error) {}
                        });
                        panel.appendChild(link);
                    });
                    if(isArticle && !existing[titles[1]]) {
                        var create=document.createElement('a');create.className='gd-language-create';
                        create.textContent=spanish ? 'Crear traducción al español' : 'Create Spanish translation';
                        if(mw.config.get('wgExpandedLanguageEditorSafe')) {
                            var editParams={action:'edit',preload:titles[0],summary:'Start Español translation from [['+titles[0]+']]'};
                            create.href=route(titles[1],'es')+'?'+new URLSearchParams(editParams).toString();
                        } else {create.setAttribute('aria-disabled','true');create.title='Translation setup awaiting server update';}
                        panel.appendChild(create);
                    }
                    var home=document.createElement('a');home.className='gd-language-home';home.textContent=spanish ? 'Inicio' : 'Home';home.href=route('Main Page','en');panel.appendChild(home);
                    var guide=document.createElement('a');guide.textContent=spanish ? 'Guía de traducción' : 'Translation guide';guide.href=mw.util.getUrl('Help:Translations');panel.appendChild(guide);
                }, function () {panel.textContent=spanish ? 'No se pudieron cargar los idiomas. Recarga la página.' : 'Languages could not load. Reload the page.';});
            }
            api.get({action:'query',titles:'MediaWiki:Expanded-language-pages.json',prop:'revisions',rvprop:'content',rvslots:'main',formatversion:2}).then(function (data) {
                try {build(JSON.parse(data.query.pages[0].revisions[0].slots.main.content));} catch (error) {build({});}
            },function () {build({});});
            document.addEventListener('click',function (event) {if (!menu.contains(event.target)) menu.open=false;});
            document.addEventListener('keydown',function (event) {if (event.key==='Escape') menu.open=false;});
            if (spanish) {
                var interfaceLabels={"Navigation": "Navegación", "Main page": "Página principal", "Recent changes": "Cambios recientes", "New files": "Archivos nuevos", "Random page": "Página aleatoria", "New here? Get started": "¿Eres nuevo? Empieza aquí", ">> Site policy": ">> Normas del sitio", ">> Staff": ">> Administración", "Help using MediaWiki": "Ayuda sobre MediaWiki", "GD Base Content": "Contenido base de GD", "Games": "Juegos", "Main levels": "Niveles principales", "Gameplay": "Jugabilidad", "Rewards": "Recompensas", "Rooms (vaults etc.)": "Salas (bóvedas, etc.)", "Level editing": "Editor de niveles", "Account features": "Funciones de la cuenta", "Updates": "Actualizaciones", "GD Community Content": "Contenido de la comunidad de GD", "Demon List levels": "Niveles de la Demon List", "Gauntlet levels": "Niveles de gauntlets", "Map Pack levels": "Niveles de paquetes de mapas", "Classic levels": "Niveles clásicos", "Platformer levels": "Niveles de plataformas", "Players": "Jugadores", "Community activities": "Actividades de la comunidad", "Wiki Resources": "Recursos de la wiki", "Propose new articles": "Proponer artículos nuevos", "Submit edits for review": "Enviar cambios para revisión", "Admin Dashboard": "Panel de administración", "Images": "Imágenes", "Videos": "Vídeos", "Audio": "Audio", "PDFs": "PDF", "Templates": "Plantillas", "Sandbox": "Zona de pruebas", "Discuss the wiki": "Debatir sobre la wiki", "Guestbook": "Libro de visitas", "Tools": "Herramientas", "What links here": "Lo que enlaza aquí", "Related changes": "Cambios relacionados", "Upload file": "Subir archivo", "Printable version": "Versión para imprimir", "Permanent link": "Enlace permanente", "Page information": "Información de la página", "Account": "Cuenta", "Preferences": "Preferencias", "Your user page": "Tu página de usuario", "Log in": "Iniciar sesión", "Account menu": "Menú de cuenta", "Appearance": "Apariencia"};
                document.querySelectorAll('#mw-panel h3, #mw-panel h3 span, #mw-panel a, .gd-account-links a, .gd-account-talk-link span').forEach(function (element) {
                    var key=element.textContent.trim();
                    if (interfaceLabels[key]) {
                        // Translate text nodes without removing colour spans, links or icons.
                        var walker=document.createTreeWalker(element,NodeFilter.SHOW_TEXT), node;
                        while ((node=walker.nextNode())) {
                            var original=node.nodeValue.trim();
                            if (interfaceLabels[original]) node.nodeValue=node.nodeValue.replace(original,interfaceLabels[original]);
                        }
                    }
                });
                document.querySelectorAll('#p-personal [title], #p-personal [aria-label]').forEach(function (element) {
                    ['title','aria-label'].forEach(function (attr) {var key=element.getAttribute(attr);if(interfaceLabels[key])element.setAttribute(attr,interfaceLabels[key]);});
                });
                var appearance=controls.querySelector('.gd-header-menu summary');
                if (appearance) {appearance.textContent='Apariencia';appearance.title='Apariencia';appearance.setAttribute('aria-label','Apariencia');}
                var account=controls.querySelector('.gd-account-menu summary');
                if (account) {account.title='Menú de cuenta';account.setAttribute('aria-label','Menú de cuenta');}
                var titles={'Blue':'Azul','Purple':'Morado','Green':'Verde','Red':'Rojo','Dark':'Oscuro','Blackout':'Negro','Vault':'La bóveda','Vault of Secrets':'La bóveda de los secretos','Chamber of Time':'La cámara del tiempo'};
                controls.querySelectorAll('.gd-theme-option').forEach(function (button) {if(titles[button.textContent])button.textContent=titles[button.textContent];});
            }
            // Optional translated assets use the same existing theme filters.
            var logos=mw.config.get('wgExpandedLanguageLogos') || {};
            logos.spa=Object.assign({"header": "https://geometrydashwiki.com/images/4/4a/Logo-spa.jpg", "banner": "https://geometrydashwiki.com/images/a/a7/Site-logo-spa.png"},logos.spa || {});
            if (logos[code]) {
                if (logos[code].header) {
                    var header=document.querySelector('.gd-header-bar-logo img');if(header)header.src=logos[code].header;
                }
                if (logos[code].banner) document.documentElement.style.setProperty('--wiki-logo-image','url("'+logos[code].banner.replace(/["\\]/g,'')+'")');
            }
        });
    });
}());
/* Keep language parameters and script endpoints out of visible navigation URLs. */
(function () {
    'use strict';
    function clean(raw) {
        var url;
        try {url=new URL(raw,location.href);} catch(error) {return raw;}
        if (url.origin!==location.origin) return raw;
        url.searchParams.delete('uselang');
        if (/\/index\.php$/.test(url.pathname) && !url.searchParams.has('title') && url.searchParams.has('search')) {url.pathname='/wiki/Special:Search';}
        if (/\/index\.php$/.test(url.pathname) && url.searchParams.has('title')) {
            var title=url.searchParams.get('title');url.searchParams.delete('title');
            var match=/^(Spa|Rus|Pol)\/(.+)$/.exec(title.replace(/_/g,' '));
            var ns=title.indexOf(':')!==-1;
            var prefix=match ? '/'+match[1].toLowerCase()+'/' : (ns ? '/wiki/' : '/eng/');
            var name=match ? match[2] : title;
            url.pathname=prefix+encodeURIComponent(name.replace(/ /g,'_')).replace(/%2F/g,'/').replace(/%3A/g,':');
        }
        return url.pathname+url.search+url.hash;
    }
    // The current request has already chosen its interface language.
    var address=new URL(location.href);
    var cleanAddress=clean(location.href);
    if (cleanAddress!==address.pathname+address.search+address.hash) history.replaceState(null,'',cleanAddress);
    function links() {
        document.querySelectorAll('a[href]').forEach(function (link) {
            var href=link.getAttribute('href');
            if (href && !/^(?:#|javascript:|mailto:)/.test(href)) {
                var replacement=clean(href);if(replacement!==href)link.setAttribute('href',replacement);
            }
        });
    }
    $(function () {
        links();
        var pending=false;
        new MutationObserver(function () {
            if(pending)return;pending=true;
            requestAnimationFrame(function () {pending=false;links();});
        }).observe(document.body,{childList:true,subtree:true});
    });
    mw.hook('wikipage.content').add(links);
    // Also handles asynchronously built language and account menus.
    document.addEventListener('click',function(event) {
        var link=event.target.closest && event.target.closest('a[href]');
        if(link)link.setAttribute('href',clean(link.getAttribute('href')));
    },true);
}());
/* Article-language tabs and links follow the persistent site language. */
(function () {
    'use strict';
    mw.loader.using(['mediawiki.api','mediawiki.util']).then(function () {
        $(function () {
            var cookie=/(?:^|;\s*)gdwiki-language=(eng|spa|rus|pol)(?:;|$)/.exec(document.cookie);
            var mode=cookie ? cookie[1] : (mw.config.get('wgExpandedActiveLanguage') || mw.config.get('wgExpandedLanguage') || 'eng');
            var names={eng:'English',spa:'Español',rus:'Русский',pol:'Polski'};
            var notices={eng:'This article is not available in English. Showing the original article.',spa:'Este artículo todavía no está disponible en español. Se muestra la versión en inglés.',rus:'Эта статья пока недоступна на русском языке. Показана английская версия.',pol:'Ten artykuł nie jest jeszcze dostępny po polsku. Wyświetlono wersję angielską.'};
            try {
                var pending=JSON.parse(sessionStorage.getItem('gdwiki-fallback') || 'null');
                sessionStorage.removeItem('gdwiki-fallback');
                if(!pending && mw.config.get('wgExpandedLanguageFallback'))pending={language:mw.config.get('wgExpandedLanguageFallback')};
                if(pending && notices[pending.language])mw.notify(notices[pending.language],{tag:'gd-language-fallback',autoHideSeconds:10});
            } catch(error) {}
            function rememberFallback(language) {
                try {sessionStorage.setItem('gdwiki-fallback',JSON.stringify({language:language}));} catch(error) {}
            }
            function normal(title) {title=title.replace(/_/g,' ').trim();return title.charAt(0).toUpperCase()+title.slice(1);}
            function bare(title) {return title.replace(/^(?:Spa|Rus|Pol)\//,'');}
            function route(title) {
                var m=/^(Spa|Rus|Pol)\/(.+)$/.exec(title);
                return '/'+(m ? m[1].toLowerCase() : 'eng')+'/'+encodeURIComponent((m ? m[2] : title).replace(/ /g,'_')).replace(/%2F/g,'/');
            }
            var api=new mw.Api();
            api.get({action:'query',titles:'MediaWiki:Expanded-language-pages.json',prop:'revisions',rvprop:'content',rvslots:'main',formatversion:2}).then(function (data) {
                var map;
                try {map=JSON.parse(data.query.pages[0].revisions[0].slots.main.content);} catch(error) {map={};}
                var aliases={};
                Object.keys(map).forEach(function (root) {
                    if(!map[root] || typeof map[root]!=='object')return;
                    [root].concat(Object.keys(map[root]).map(function (key) {return map[root][key];})).forEach(function (title) {
                        if(typeof title!=='string')return;
                        [normal(title),normal(bare(title))].forEach(function (alias) {
                            if(!aliases[alias])aliases[alias]=[];
                            if(aliases[alias].indexOf(root)===-1)aliases[alias].push(root);
                        });
                    });
                });
                function group(title) {
                    var roots=aliases[normal(title)] || [];
                    if(roots.length===1)return map[roots[0]];
                    if(roots.length>1)return null; // Never guess between unrelated concepts.
                    var english=bare(normal(title));return {eng:english,spa:'Spa/'+english,rus:'Rus/'+english,pol:'Pol/'+english};
                }
                var links=[],checks={},current=(mw.config.get('wgPageName') || '').replace(/_/g,' ');
                document.querySelectorAll('#mw-content-text a[href], #mw-panel a[href], .gd-header-bar-logo, .gd-language-home').forEach(function (link) {
                    var url;try {url=new URL(link.href);}catch(error){return;}
                    if(url.origin!==location.origin)return;
                    if(link.getAttribute('href').charAt(0)==='#')return;
                    var path=/^\/(?:eng|spa|rus|pol|wiki)\/(.+)$/.exec(url.pathname);
                    if(!path && url.pathname!=='/')return;
                    var title=path ? decodeURIComponent(path[1]).replace(/_/g,' ') : (mw.config.get('wgMainPageTitle') || 'Main Page');
                    if(/^\/(spa|rus|pol)\//.test(url.pathname))title=url.pathname.split('/')[1].charAt(0).toUpperCase()+url.pathname.split('/')[1].slice(1)+'/'+title;
                    if(title.indexOf(':')!==-1)return;
                    var action=url.searchParams.get('action');
                    var redlink=url.searchParams.get('redlink')==='1';
                    if((action && action!=='view' && !redlink) || url.searchParams.has('veaction') || url.searchParams.has('oldid') || url.searchParams.has('diff'))return;
                    if(normal(title)===normal(current) && url.hash)return;
                    var versions=group(title);if(!versions)return;
                    Object.keys(versions).forEach(function (lang) {checks[versions[lang]]=true;});
                    links.push({element:link,url:url,title:title,versions:versions});
                });
                var currentVersions=group(current);
                if(currentVersions)Object.keys(currentVersions).forEach(function (lang) {checks[currentVersions[lang]]=true;});
                var titles=Object.keys(checks),batches=[];
                for(var i=0;i<titles.length;i+=40)batches.push(titles.slice(i,i+40));
                Promise.all(batches.map(function (batch) {return api.get({action:'query',titles:batch.join('|'),prop:'info',formatversion:2});})).then(function (results) {
                    var exists={};results.forEach(function (result) {(result.query.pages || []).forEach(function (p) {exists[normal(p.title)]=!p.missing && !p.invalid;});});
                    links.forEach(function (item) {
                        var preferred=item.versions[mode],english=item.versions.eng;
                        var available=preferred && exists[normal(preferred)];
                        var target=available ? preferred : english;
                        if(!target || !exists[normal(target)])return;
                        item.url.searchParams.delete('title');item.url.searchParams.delete('uselang');item.url.searchParams.delete('redlink');if(item.url.searchParams.get('action')==='edit')item.url.searchParams.delete('action');
                        item.element.href=route(target)+(item.url.searchParams.size ? '?'+item.url.searchParams.toString() : '')+item.url.hash;
                        item.element.classList.remove('new');
                        if(!available && mode!=='eng')item.element.addEventListener('click',function () {rememberFallback(mode);});
                        // Prefixes are storage details, not part of the displayed article name.
                        if(item.element.textContent.trim()===item.title)item.element.textContent=bare(item.title);
                    });
                    if(!currentVersions || mw.config.get('wgNamespaceNumber')!==0 || mw.config.get('wgAction')!=='view')return;
                    var languages=Object.keys(names).filter(function (lang) {return currentVersions[lang] && exists[normal(currentVersions[lang])];});
                    if(languages.length<2 || document.getElementById('gd-article-languages'))return;
                    var nav=document.createElement('nav');nav.id='gd-article-languages';nav.className='gd-article-languages';nav.setAttribute('aria-label',mode==='spa' ? 'Idiomas del artículo' : 'Article languages');
                    languages.forEach(function (lang) {
                        var link=document.createElement('a');link.textContent=names[lang];link.href=route(currentVersions[lang]);link.setAttribute('data-gd-language-tab','');
                        if(normal(currentVersions[lang])===normal(current))link.setAttribute('aria-current','page');
                        nav.appendChild(link);
                    });
                    var heading=document.getElementById('firstHeading');if(heading){document.body.classList.add('gd-has-language-tabs');heading.appendChild(nav);}
                },function () { /* Preserve original working links if availability cannot be checked. */ });
            });
        });
    });
}());
/* Infobox labels: reader language on shared pages, article language on articles. */
(function () {
'use strict';
var labels={"Born": {"spa": "Nacimiento", "rus": "Дата рождения", "pol": "Data urodzenia"}, "Died": {"spa": "Fallecimiento", "rus": "Дата смерти", "pol": "Data śmierci"}, "Other names": {"spa": "Otros nombres", "rus": "Другие имена", "pol": "Inne nazwy"}, "Country": {"spa": "País", "rus": "Страна", "pol": "Kraj"}, "Mod status": {"spa": "Estado de moderador", "rus": "Статус модератора", "pol": "Status moderatora"}, "Uploaded levels": {"spa": "Niveles publicados", "rus": "Опубликованные уровни", "pol": "Opublikowane poziomy"}, "Rated levels": {"spa": "Niveles calificados", "rus": "Оценённые уровни", "pol": "Ocenione poziomy"}, "Creator points": {"spa": "Puntos de creador", "rus": "Очки создателя", "pol": "Punkty twórcy"}, "GD stats": {"spa": "Estadísticas de GD", "rus": "Статистика GD", "pol": "Statystyki GD"}, "Social media": {"spa": "Redes sociales", "rus": "Социальные сети", "pol": "Media społecznościowe"}, "Other": {"spa": "Otros", "rus": "Другое", "pol": "Inne"}, "Please search": {"spa": "Buscar", "rus": "Найти", "pol": "Wyszukaj"}, "Information": {"spa": "Información", "rus": "Информация", "pol": "Informacje"}, "Statistics": {"spa": "Estadísticas", "rus": "Статистика", "pol": "Statystyki"}, "Total jumps": {"spa": "Saltos totales", "rus": "Всего прыжков", "pol": "Łączna liczba skoków"}, "Total attempts": {"spa": "Intentos totales", "rus": "Всего попыток", "pol": "Łączna liczba prób"}, "Main levels": {"spa": "Niveles principales", "rus": "Основные уровни", "pol": "Główne poziomy"}, "Online levels": {"spa": "Niveles en línea", "rus": "Онлайн-уровни", "pol": "Poziomy online"}, "Daily levels": {"spa": "Niveles diarios", "rus": "Ежедневные уровни", "pol": "Poziomy dzienne"}, "Gauntlets": {"spa": "Gauntlets", "rus": "Испытания", "pol": "Gauntlety"}, "Map packs": {"spa": "Paquetes de mapas", "rus": "Наборы уровней", "pol": "Pakiety map"}, "Liked / disliked": {"spa": "Me gusta / no me gusta", "rus": "Лайки / дизлайки", "pol": "Polubione / nielubiane"}, "Chests opened": {"spa": "Cofres abiertos", "rus": "Открытые сундуки", "pol": "Otwarte skrzynie"}, "Players destroyed": {"spa": "Jugadores destruidos", "rus": "Уничтоженные игроки", "pol": "Zniszczeni gracze"}, "Leaderboard": {"spa": "Clasificación", "rus": "Таблица лидеров", "pol": "Ranking"}, "Last updated": {"spa": "Última actualización", "rus": "Последнее обновление", "pol": "Ostatnia aktualizacja"}};
function translate(root) {
 var page=mw.config.get('wgPageName') || '',match=/^(Spa|Rus|Pol)\//.exec(page),cookie=/(?:^|;\s*)gdwiki-language=(eng|spa|rus|pol)(?:;|$)/.exec(document.cookie);
 var lang=mw.config.get('wgNamespaceNumber')===0 ? (match ? match[1].toLowerCase() : 'eng') : (cookie ? cookie[1] : (mw.config.get('wgExpandedActiveLanguage') || 'eng'));
 root.querySelectorAll('[data-gd-infobox-label]').forEach(function (el) {var key=el.getAttribute('data-gd-infobox-label');el.textContent=(labels[key] && labels[key][lang]) || key;});
 root.querySelectorAll('[data-gd-stats-name]').forEach(function(el){var name=el.getAttribute('data-gd-stats-name');el.textContent=lang==='spa' ? 'Estadísticas de '+name : lang==='rus' ? 'Статистика '+name : lang==='pol' ? 'Statystyki '+name : name+"’s stats";});
}
$(function(){translate(document);mw.hook('wikipage.content').add(function(content){translate(content[0] || document);});});
})();