MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
Hide main page heading while cached site styles refresh |
Stop phones rendering the wiki as a shrunken 1120px desktop page |
||
| Line 1: | Line 1: | ||
/* Use the physical device width instead of Vector legacy's 1120px canvas. */ | |||
(function () { | |||
var viewport = document.querySelector('meta[name="viewport"]'); | |||
if (!viewport) { | |||
viewport = document.createElement('meta'); | |||
viewport.name = 'viewport'; | |||
document.head.appendChild(viewport); | |||
} | |||
viewport.content = 'width=device-width, initial-scale=1'; | |||
}()); | |||
/* Any JavaScript here will be loaded for all users on every page load. */ | /* Any JavaScript here will be loaded for all users on every page load. */ | ||
Revision as of 14:18, 11 October 2026
/* Use the physical device width instead of Vector legacy's 1120px canvas. */
(function () {
var viewport = document.querySelector('meta[name="viewport"]');
if (!viewport) {
viewport = document.createElement('meta');
viewport.name = 'viewport';
document.head.appendChild(viewport);
}
viewport.content = 'width=device-width, initial-scale=1';
}());
/* Any JavaScript here will be loaded for all users on every page load. */
/***************************/
/* Interface Modification **/
/***************************/
/* [[MediaWiki:Custom-Category:Games]] */
$(function () {
var config = {
targetCategory: 'Games (base content)',
sourceContent: 'MediaWiki:Custom-Category:Games',
insertionPoint: '#mw-normal-catlinks'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-page-list" style="font-weight:normal;">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the content list from ' + config.sourceContent);
});
});
/***************************/
/* [[MediaWiki:Custom-Category:Gameplay]] */
$(function () {
var config = {
targetCategory: 'Gameplay (base content)',
sourceContent: 'MediaWiki:Custom-Category:Gameplay',
insertionPoint: '#mw-normal-catlinks'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-page-list" style="font-weight:normal;">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the content list from ' + config.sourceContent);
});
});
/***************************/
/* [[MediaWiki:Custom-Category:Rewards]] */
$(function () {
var config = {
targetCategory: 'Rewards (base content)',
sourceContent: 'MediaWiki:Custom-Category:Rewards',
insertionPoint: '#mw-normal-catlinks'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-page-list" style="font-weight:normal;">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the content list from ' + config.sourceContent);
});
});
/***************************/
/* [[MediaWiki:Custom-Category:Rooms]] */
$(function () {
var config = {
targetCategory: 'Rooms (base content)',
sourceContent: 'MediaWiki:Custom-Category:Rooms',
insertionPoint: '#mw-normal-catlinks'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-page-list" style="font-weight:normal;">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the content list from ' + config.sourceContent);
});
});
/***************************/
/* [[MediaWiki:Custom-Category:LevelEditing]] */
$(function () {
var config = {
targetCategory: 'Level editing (base content)',
sourceContent: 'MediaWiki:Custom-Category:LevelEditing',
insertionPoint: '#mw-normal-catlinks'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-page-list" style="font-weight:normal;">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the content list from ' + config.sourceContent);
});
});
/***************************/
/* [[MediaWiki:Custom-Category:AccountFeatures]] */
$(function () {
var config = {
targetCategory: 'Account features (base content)',
sourceContent: 'MediaWiki:Custom-Category:AccountFeatures',
insertionPoint: '#mw-normal-catlinks'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-page-list" style="font-weight:normal;">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the content list from ' + config.sourceContent);
});
});
/***************************/
/* [[MediaWiki:Custom-Category:Updates]] */
$(function () {
var config = {
targetCategory: 'Updates (base content)',
sourceContent: 'MediaWiki:Custom-Category:Updates',
insertionPoint: '#mw-normal-catlinks'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-page-list" style="font-weight:normal;">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the page list from ' + config.sourceContent);
});
});
/***************************/
/* [[MediaWiki:Custom-MainLevels]] */
$(function () {
var config = {
targetCategory: 'Main levels (base content)',
sourceContent: 'MediaWiki:Custom-MainLevels',
insertionPoint: '#mw-content-text'
};
// Initial checks: Run only on page views (not edit mode) and for existing pages
if (mw.config.get('wgAction') !== 'view' || mw.config.get('wgArticleId') === 0) return;
// Category check: Verify if the article belongs to the target category
var categories = mw.config.get('wgCategories');
if (categories.indexOf(config.targetCategory) === -1) return;
// Load and inject content
var api = new mw.Api();
api.get({
action: 'parse',
page: config.sourceContent,
prop: 'text',
disablelimitreport: true,
disableeditsection: true
}).done(function (data) {
if (data && data.parse && data.parse.text) {
var insertHtml = data.parse.text['*'];
// Wrap in a div for styling and append
$(config.insertionPoint).append(
$('<div id="custom-category-footer">')
.html(insertHtml)
);
}
}).fail(function () {
console.error('Failed to load the footer from ' + config.sourceContent);
});
});
/***************************/
/* [[Template:USERNAME]] */
$(function () {
mw.config.get('wgUserName');
if (mw.config.get('wgUserName') !== null) ('span.username-insert').text(mw.config.get('wgUserName'));
});
/***************************/
/* [[Template:Discord]] (DiscordCompact.js v0.0.2 - https://support.wiki.gg/wiki/DiscordCompact) */
$(function () {
var widget = $("#content #discord-compact-widget");
// Cancel when no widget detected
if (!widget.length) return;
// Ensure that ID is not blank
var id = widget.attr("data-id");
if (id === "") throw new Error("DiscordCompact has a blank server id!");
// Ensure that ID consists only of numbers with 17 character minimum
if (!new RegExp("^[0-9]{17}[0-9]+$").test(id)) throw new Error("DiscordCompact has an invalid server id!");
var apiBase = "https://discord.com/api/guilds/" + id;
// Get information about the server, such as online member count and invite url, as well as limits of the server existing at all or having widgets disabled
$.ajax(apiBase + "/widget.json").fail(function(req) {
if (!req.responseJSON) throw new Error("DiscordCompact encountered an unknown error whilst fetching widget.json (status: " + req.status);
switch (req.responseJSON.code) {
case 10004:
throw new Error("DiscordCompact has a valid server id, but no such server exists!");
case 50004:
throw new Error("DiscordCompact has a valid server id, but that server has widgets disabled!");
default:
throw new Error("DiscordCompact encountered an unknown error whilst fetching widget.json (status: " + req.status + "; code: " + req.responseJSON.code + ")");
}
}).done(function(res) {
const inviteURL = res.instant_invite;
// Warn when unable to retrieve invite URL
if (inviteURL == null) console.warn("DiscordCompact cannot get an invite URL; does this server have an invite channel set in Widget settings?");
// Generate widget image
$.ajax({url: apiBase + "/widget.png?style=banner2", xhrFields: {responseType: "blob"}}).fail(function(req) {
if (!req.responseJSON) throw new Error("DiscordCompact encountered an unknown error whilst fetching widget.png! (status: " + req.status);
throw new Error("DiscordCompact encountered an unknown error whilst fetching widget.png! (status: " + req.status + "; code: " + req.responseJSON.code + ")");
}).done(function(blob) {
// Convert the image data into base64, preventing having to make the client request it a second time
const imageReader = new FileReader();
imageReader.readAsDataURL(blob);
imageReader.onloadend = function() {
const b64Data = imageReader.result;
// Convert the widget into a selectable link
widget.replaceWith(function() {
return $("<a>", {
id: widget.attr("id"),
class: widget.attr("class"),
style: widget.attr("style"),
alt: "Discord server widget",
href: inviteURL
});
});
// Select the widget
widget = $("#discord-compact-widget");
// CSS
widget.css("display", "block");
widget.css("max-height", "76px");
widget.css("max-width", "320px");
// Create image
const widgetImage = document.createElement("img");
widgetImage.src = b64Data;
widgetImage.style.width = "100%";
widgetImage.style.height = "100%";
widgetImage.style.borderRadius = "4px";
widget.append(widgetImage);
// Prevent image dragging
widget.on("dragstart", function(e) {e.preventDefault();});
// Console message
console.log("DiscordCompact loaded successfully!");
};
});
});
});
/***************************/
/* [[Template:Discord]] (DiscordWidget.js - https://support.wiki.gg/wiki/DiscordWidget) */
$(function () {
'use strict';
var mconfig = mw.config.get([
'wgContentLanguage',
'wgUserLanguage',
'wgUserName'
]);
if (window.DiscordIntegratorLoaded) {
return;
}
window.DiscordIntegratorLoaded = true;
// Main object, @static
var DiscordIntegrator = {
// Initialising
init: function() {
mw.hook('wikipage.content').add($.proxy(this.insertToContent, this));
},
//Finding the designated places in content in which to place the widget and placing it
insertToContent: function($content) {
$content.find('.DiscordIntegrator:not(.loaded)').each($.proxy(function(cabbage, el) {
el = $(el);
el.html(this.generateContent(el.data())).addClass('loaded');
}, this));
},
// Determines the theme of the widget, @param {string} config Configured theme, @return {string} 'light' or 'dark' depending on the wiki theme and configuration
determineTheme: function(config) {
// If explicitly configured to light or dark
if (config === 'dark') {
return 'dark';
}
if (config === 'light') {
return 'light';
}
// If not configured, try to determine based on wiki theme (set by themeToggle):
return $(':root').hasClass('view-dark') ? 'dark' : 'light';
},
// Generate widget content from an object, @return {string} content of the widget
generateContent: function(config) {
if (!config.id || !String(config.id).match(/\d{17,19}/)) {
return "Error: ID of the widget is malformed or not supplied, please see <a href='https://support.wiki.gg/wiki/DiscordWidget' title='the instructions'>the instructions</a> for how to find your server's ID. Please make sure you are not inserting <strong>the DiscordIntegrator template</strong> when asked for <strong>your widget ID</strong>.";
}
if (
(
config.loggedIn === true ||
Boolean(config['logged-in']) === true &&
config['logged-in'] !== 'false' &&
config['logged-in'] !== '{{{loggedIn}}}'
) && !mconfig.wgUserName
) {
return "Please <a href='/Special:UserLogin' title='log in'>log in</a> to see this widget.";
}
var username = config.username === '@disabled' ?
'' :
config.username === '@function' &&
typeof window.DiscordIntegratorGetUsername === 'function' ?
window.DiscordIntegratorGetUsername() :
config.username || mconfig.wgUserName;
return mw.html.element('iframe', {
src: 'https://discord.com/widget?id=' + config.id +
'&theme=' + this.determineTheme(config.theme) +
'&username=' + encodeURIComponent(username),
width: config.width || '100%',
height: config.height || '400px',
style: 'max-width:100%;',
allowtransparency: 'true',
frameborder: '0',
title: "Discord server widget"
});
}
};
DiscordIntegrator.init();
});
/***************************/
/* CSS classes for daytime periods */
$(function() {
'use strict';
var currentTime = new Date().getHours();
if (0 <= currentTime && currentTime < 6) {
if (document.body) {document.body.classList.add('night');}
}
else if (6 <= currentTime && currentTime < 12) {
if (document.body) {document.body.classList.add('morning');}
}
else if (12 <= currentTime && currentTime < 18) {
if (document.body) {document.body.classList.add('afternoon');}
}
else {
if (document.body) {document.body.classList.add('evening');}
}
});
/* Geometry Dash Expanded Wiki footer: source attribution, licensing, and local contact. */
mw.loader.using('mediawiki.util').then(function () {
$(function () {
var footer = document.querySelector('#footer');
var information = document.querySelector('#footer-info');
if (!footer || !information || document.getElementById('footer-info-ewiki-sources')) return;
function link(text, href) {
var element = document.createElement('a');
element.textContent = text;
element.href = href;
return element;
}
var sources = document.createElement('li');
sources.id = 'footer-info-ewiki-sources';
sources.append('Imported material is adapted from the ',
link('Official Wiki', 'https://geometrydash.wiki.gg/'),
' and, where applicable, the ',
link('Geometry Dash Fandom', 'https://geometry-dash.fandom.com/'),
' and ', link('Geometry Dash Fan Fandom', 'https://geometry-dash-fan.fandom.com/'),
' wikis. Source and contributor links are recorded in import edit summaries.');
var license = document.createElement('li');
license.id = 'footer-info-ewiki-license';
license.append('Imported wiki text retains its original licensing, including ',
link('Creative Commons Attribution-ShareAlike 4.0', 'https://creativecommons.org/licenses/by-sa/4.0/'),
'. Images and separately licensed code may have different terms. ',
link('Sources and licensing', mw.util.getUrl('Project:Copyrights')), '.');
var independent = document.createElement('li');
independent.id = 'footer-info-ewiki-independent';
independent.textContent = 'Geometry Dash Expanded Wiki is an independent community wiki and is not affiliated with wiki.gg or RobTop Games.';
information.append(sources, license, independent);
var places = document.querySelector('#footer-places');
if (places && !document.getElementById('footer-places-ewiki-contact')) {
var contact = document.createElement('li');
contact.id = 'footer-places-ewiki-contact';
contact.appendChild(link('Contact Geometry Dash Expanded Wiki', mw.util.getUrl('User talk:Josh994')));
places.appendChild(contact);
}
});
});
/* Staff content classification: persisted as dedicated hidden categories. */
(function () {
'use strict';
if (mw.config.get('wgNamespaceNumber') !== 0 || mw.config.get('wgAction') !== 'view') { return; }
var heading = document.getElementById('firstHeading');
if (!heading || document.getElementById('gd-content-status')) { return; }
var states = [
{key: 'pending', label: 'community content (pending review)', category: 'Wiki content status - pending review'},
{key: 'reviewed', label: 'community content', category: 'Wiki content status - reviewed community'},
{key: 'base', label: 'base content', category: 'Wiki content status - base'}
];
var categories = mw.config.get('wgCategories') || [];
var selected = states.filter(function (state) { return categories.indexOf(state.category) !== -1; })[0];
if (!selected) {
selected = categories.some(function (name) { return /\(base content\)$/i.test(name); }) ? states[2] : states[0];
}
var staff = (mw.config.get('wgUserGroups') || []).some(function (group) {
return ['sysop', 'bureaucrat'].indexOf(group) !== -1;
});
var badge = document.createElement(staff ? 'button' : 'span');
badge.id = 'gd-content-status';
badge.className = 'gd-content-status';
badge.textContent = selected.label;
badge.dataset.status = selected.key;
if (staff) {
badge.type = 'button';
badge.setAttribute('aria-haspopup', 'dialog');
badge.setAttribute('aria-expanded', 'false');
badge.title = 'Change content classification';
}
heading.appendChild(badge);
document.documentElement.classList.add('gd-content-status-ready');
// Non-staff badges have no click handler or interactive controls.
if (!staff) { return; }
var panel;
badge.addEventListener('click', function () {
if (panel) { close(); return; }
panel = document.createElement('div');
panel.className = 'gd-content-status-menu';
panel.setAttribute('role', 'dialog');
panel.setAttribute('aria-label', 'Content classification');
var title = document.createElement('strong');
title.textContent = 'Content classification';
panel.appendChild(title);
states.forEach(function (state) {
var button = document.createElement('button');
button.type = 'button';
button.textContent = state.label;
button.disabled = state.key === selected.key;
button.addEventListener('click', function () { save(state); });
panel.appendChild(button);
});
var cancel = document.createElement('button');
cancel.type = 'button'; cancel.textContent = 'Cancel';
cancel.addEventListener('click', close); panel.appendChild(cancel);
document.body.appendChild(panel);
var rect = badge.getBoundingClientRect();
panel.style.left = Math.max(8, Math.min(rect.left, window.innerWidth - panel.offsetWidth - 8)) + 'px';
panel.style.top = Math.max(8, Math.min(rect.bottom + 8, window.innerHeight - panel.offsetHeight - 8)) + 'px';
badge.setAttribute('aria-expanded', 'true');
panel.addEventListener('keydown', function (event) { if (event.key === 'Escape') { close(); } });
cancel.focus();
});
function close() {
if (panel) { panel.remove(); panel = null; }
badge.setAttribute('aria-expanded', 'false'); badge.focus();
}
function applyBadge(state) {
selected = state; badge.textContent = state.label; badge.dataset.status = state.key;
}
function replaceStatusCategory(text, state) {
return text.replace(/\[\[\s*Category\s*:\s*Wiki[ _]content[ _]status[ _]-[ _](?:pending[ _]review|reviewed[ _]community|base)\s*(?:\|[^\]]*)?\]\]/gi, '').replace(/\s+$/, '') + '\n[[Category:' + state.category + ']]\n';
}
var publishedState = selected;
mw.hook('ve.activationComplete').add(function () { publishedState = selected; });
mw.hook('ve.deactivationComplete').add(function () {
applyBadge(publishedState); close();
});
function save(state) {
var editorActive = document.documentElement.classList.contains('ve-active');
var target = window.ve && ve.init && ve.init.target;
var surface = editorActive && target && target.getSurface && target.getSurface();
var textbox = document.getElementById('wpTextbox1');
if (editorActive || textbox) {
try {
if (surface && surface.getMode() === 'visual') {
var model = surface.getModel();
var fragment = model.getFragment();
var items = model.getDocument().getMetaList().getItemsInGroup('mwCategory').slice();
items.forEach(function (item) {
var category = item.getAttribute('category').replace(/_/g, ' ').replace(/^Category:/i, '');
if (states.some(function (entry) { return entry.category === category; })) {
fragment.removeMeta(item);
}
});
fragment.insertMeta({type: 'mwCategory', attributes: {category: 'Category:' + state.category, sortkey: ''}});
} else if (surface && surface.getMode() === 'source') {
var sourceModel = surface.getModel();
var text = sourceModel.getDocument().data.getText(true);
sourceModel.getFragment().selectAll().insertContent(replaceStatusCategory(text, state));
} else if (textbox) {
textbox.value = replaceStatusCategory(textbox.value, state);
textbox.dispatchEvent(new Event('input', {bubbles: true}));
textbox.dispatchEvent(new Event('change', {bubbles: true}));
} else {
throw new Error('Editor is not ready');
}
applyBadge(state); close();
mw.notify('Classification changed in this draft. Save changes to publish it.');
} catch (error) {
mw.notify('Could not change classification in this draft. No changes were published.', {type: 'error'});
}
return;
}
var currentPanel = panel;
Array.prototype.forEach.call(currentPanel.querySelectorAll('button'), function (button) { button.disabled = true; });
var status = document.createElement('p'); status.setAttribute('role', 'status');
status.textContent = 'Saving...'; currentPanel.appendChild(status);
mw.loader.using('mediawiki.api').then(function () {
var api = new mw.Api();
return api.get({action: 'query', prop: 'revisions', rvprop: 'ids|timestamp|content', rvslots: 'main',
titles: mw.config.get('wgPageName'), curtimestamp: 1, formatversion: 2}).then(function (data) {
var page = data.query.pages[0];
if (page.missing || !page.revisions) { throw new Error('Page could not be read.'); }
var revision = page.revisions[0];
var text = revision.slots.main.content;
text = text.replace(/\[\[\s*Category\s*:\s*Wiki[ _]content[ _]status[ _]-[ _](?:pending[ _]review|reviewed[ _]community|base)\s*(?:\|[^\]]*)?\]\]/gi, '');
text = text.replace(/\s+$/, '') + '\n[[Category:' + state.category + ']]\n';
return api.postWithEditToken({action: 'edit', title: page.title, text: text,
baserevid: revision.revid, basetimestamp: revision.timestamp, starttimestamp: data.curtimestamp,
nocreate: 1, assertuser: mw.config.get('wgUserName'), watchlist: 'nochange',
summary: 'Set content classification: ' + state.label}).then(function () {
selected = state; badge.textContent = state.label; badge.dataset.status = state.key;
close(); mw.notify('Content classification saved.');
});
});
}).catch(function () {
if (panel !== currentPanel) { return; }
status.textContent = 'Could not save. Reload the page and try again.';
Array.prototype.forEach.call(currentPanel.querySelectorAll('button'), function (button) {
button.disabled = button.textContent === selected.label;
});
});
}
}());
/* Expanded Wiki Wraith browser-tab icon. */
(function () {
document.querySelectorAll('link[rel="icon"], link[rel="shortcut icon"]').forEach(function (link) { link.remove(); });
var icon = document.createElement('link');
icon.rel = 'icon';
icon.type = 'image/x-icon';
icon.href = "https://geometrydashwiki.com/images/7/74/ExpandedWiki.ico";
document.head.appendChild(icon);
}());
/* Keep Togglebox click targets attached to their wrapped headings. */
mw.hook('wikipage.content').add(function ($content) {
mw.loader.using('jquery.makeCollapsible').then(function () {
$content.find('.tabber.mw-collapsible').each(function () {
var $box = $(this);
$box.makeCollapsible();
var $heading = $box.children('.mw-heading').first();
var $toggle = $box.children('.mw-collapsible-toggle').first();
if ($heading.length && $toggle.length) { $heading.append($toggle); }
});
});
});
/* Multiple-file uploads on Special:Upload. */
(function () {
'use strict';
if (mw.config.get('wgCanonicalSpecialPageName') !== 'Upload') { return; }
mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function () {
$(function () {
var source = document.getElementById('wpUploadFile');
var form = source && source.closest('form');
if (!form || document.getElementById('expanded-multiple-upload')) { return; }
if (new URLSearchParams(location.search).has('wpForReUpload')) { return; }
var sourceRow = source.closest('tr') || source.parentElement;
var toggle = document.createElement(sourceRow.tagName === 'TR' ? 'tr' : 'div');
toggle.id = 'expanded-multiple-upload';
var label = document.createElement(sourceRow.tagName === 'TR' ? 'td' : 'span');
label.textContent = 'Upload multiple files: ';
var controls = document.createElement(sourceRow.tagName === 'TR' ? 'td' : 'span');
toggle.append(label, controls);
['Yes', 'No'].forEach(function (name) {
var item = document.createElement('label');
var radio = document.createElement('input');
radio.type = 'radio'; radio.name = 'expandedMultiple'; radio.value = name;
radio.checked = name === 'No';
item.append(radio, document.createTextNode(' ' + name + ' '));
controls.appendChild(item);
});
sourceRow.before(toggle);
var multi = document.createElement('input');
multi.type = 'file'; multi.multiple = true; multi.hidden = true;
multi.setAttribute('aria-label', 'Source filenames');
source.after(multi);
var destination = document.getElementById('wpDestFile');
var destinationRow = destination && (destination.closest('tr') || destination.parentElement);
var nativeSubmit = form.querySelector('[name="wpUpload"]');
if (!nativeSubmit) { toggle.remove(); multi.remove(); return; }
var button = nativeSubmit.cloneNode(true);
button.id = 'expanded-upload-files'; button.removeAttribute('name');
if (button.tagName === 'INPUT') { button.value = 'Upload files'; }
else { button.textContent = 'Upload files'; }
function setVisible(element, visible) {
element.hidden = !visible;
element.style.setProperty('display', visible ? '' : 'none', 'important');
}
setVisible(button, false);
nativeSubmit.after(button);
var results = document.createElement('div');
results.setAttribute('aria-live', 'polite'); form.after(results);
var busy = false;
controls.addEventListener('change', function () {
var enabled = controls.querySelector('input:checked').value === 'Yes';
setVisible(source, !enabled); setVisible(multi, enabled);
if (destinationRow) { setVisible(destinationRow, !enabled); }
setVisible(nativeSubmit, !enabled); setVisible(button, enabled);
});
form.addEventListener('submit', function (event) {
if (controls.querySelector('input:checked').value === 'Yes') {
event.preventDefault(); button.click();
}
});
window.addEventListener('beforeunload', function (event) {
if (busy) { event.preventDefault(); event.returnValue = ''; }
});
button.addEventListener('click', async function (event) {
event.preventDefault();
if (busy) { return; }
var files = Array.from(multi.files);
if (!files.length) { mw.notify('Choose at least one file.', {type: 'error'}); return; }
var license = document.getElementById('wpLicense');
var option = license && license.options[license.selectedIndex];
if (!option || !option.value || option.value === '-') {
mw.notify('Choose a license before uploading.', {type: 'error'}); return;
}
var licenseText = option.title;
if (!licenseText || licenseText.indexOf('{{') < 0) {
if (!/^[\w :.-]+$/.test(option.value)) {
mw.notify('Could not read the selected license.', {type: 'error'}); return;
}
licenseText = '{{' + option.value + '}}';
}
var description = document.getElementById('wpUploadDescription');
var text = (description ? description.value : '') + '\n\n== Licensing ==\n' + licenseText;
var watch = document.getElementById('wpWatchthis');
var ignore = document.getElementById('wpIgnoreWarning');
var params = {text: text, watchlist: watch && watch.checked ? 'watch' : 'nochange',
assertuser: mw.config.get('wgUserName')};
if (ignore && ignore.checked) { params.ignorewarnings = 1; }
busy = true; button.disabled = true;
var inputs = Array.from(form.querySelectorAll('input, select, textarea, button'));
var disabled = inputs.map(function (el) { return el.disabled; });
inputs.forEach(function (el) { el.disabled = true; });
results.replaceChildren();
var status = document.createElement('p'); results.appendChild(status);
var list = document.createElement('ul'); results.appendChild(list);
var ready = 0, failed = 0;
var api = new mw.Api();
try {
for (var i = 0; i < files.length; i++) {
var file = files[i];
status.textContent = 'Uploading ' + (i + 1) + '/' + files.length + ': ' + file.name;
var row = document.createElement('li');
row.textContent = file.name + ': uploading…'; list.appendChild(row);
try {
var data = await new Promise(function (resolve, reject) {
api.upload(file, Object.assign({}, params, {filename: file.name}))
.done(resolve).fail(function (code, details) {
reject({code: code, details: details});
});
});
var upload = data.upload;
if (!upload || upload.result !== 'Success') {
throw {code: 'upload-warning', details: data};
}
row.replaceChildren(document.createTextNode('Uploaded: '));
var link = document.createElement('a');
link.href = mw.util.getUrl('File:' + upload.filename);
link.textContent = upload.filename; row.appendChild(link); ready++;
} catch (error) {
failed++;
var details = error.details || {};
var warnings = details.upload && details.upload.warnings;
var message = details.error && details.error.info;
row.textContent = file.name + ': Failed — ' +
(warnings ? JSON.stringify(warnings) : message || error.code || 'Upload failed.');
}
}
status.textContent = 'Upload complete: ' + ready + ' uploaded, ' + failed + ' failed.';
} finally {
inputs.forEach(function (el, index) { el.disabled = disabled[index]; });
busy = false; button.disabled = false;
}
});
});
});
}());
/* Use native browser audio controls in infoboxes. */
(function () {
function nativeInfoboxAudio() {
document.querySelectorAll('.portable-infobox td[data-source="audio"], .portable-infobox td[data-source="fullaudio"], .expanded-ve-infobox div[data-source="audio"] > .pi-data-value, .expanded-ve-infobox div[data-source="fullaudio"] > .pi-data-value').forEach(function (cell) {
if (cell.querySelector('audio.expanded-native-audio')) { return; }
var original = cell.querySelector('audio');
if (!original) { return; }
var sources = Array.prototype.slice.call(original.querySelectorAll('source'));
if (!original.getAttribute('src') && !sources.length) { return; }
var audio = document.createElement('audio');
audio.className = 'expanded-native-audio';
audio.controls = true;
audio.preload = 'metadata';
audio.setAttribute('aria-label', (cell.getAttribute('data-source') || cell.parentElement.getAttribute('data-source')) === 'fullaudio' ? 'Full audio' : 'Audio');
if (original.getAttribute('src')) { audio.src = original.getAttribute('src'); }
sources.forEach(function (source) {
var copy = document.createElement('source');
copy.src = source.getAttribute('src');
if (source.getAttribute('type')) { copy.type = source.getAttribute('type'); }
audio.appendChild(copy);
});
original.pause();
cell.replaceChildren(audio);
});
}
mw.hook('wikipage.content').add(nativeInfoboxAudio);
$(function () {
nativeInfoboxAudio();
var content = document.getElementById('mw-content-text');
if (content) {
new MutationObserver(nativeInfoboxAudio).observe(content, { childList: true, subtree: true });
}
});
}());
if (mw.config.get('wgPageName') === 'Main_Page' && mw.config.get('wgAction') === 'view') {
var mainPageHeading = document.getElementById('firstHeading');
if (mainPageHeading) { mainPageHeading.style.setProperty('display', 'none', 'important'); }
}