MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
Set Wraith browser-tab favicon |
Attach Togglebox expand controls to month headings |
||
| Line 635: | Line 635: | ||
document.head.appendChild(icon); | 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); } | |||
}); | |||
}); | |||
}); | |||
Revision as of 18:23, 10 October 2026
/* 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 Wiki Expanded 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('Geometry Dash 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('Geometry Dash Wiki:Copyrights')), '.');
var independent = document.createElement('li');
independent.id = 'footer-info-ewiki-independent';
independent.textContent = 'Geometry Dash Wiki Expanded 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 Wiki Expanded', 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);
heading.appendChild(panel); 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 save(state) {
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); }
});
});
});