MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
(File upload form: Remove "None selected" option from License field.) |
(ES6 is supported by all modern browsers. I don't think there is any need to ensure that this script works with Internet Explorer.) |
||
Line 1: | Line 1: | ||
/* global mw | /* global mw */ | ||
/* jshint strict: | /* jshint strict:true, jquery:true, esversion:6, bitwise:true, curly:true, eqeqeq:true, undef:true */ | ||
( function() { | ( function() { | ||
'use strict'; | 'use strict'; | ||
/* Translation strings */ | /* Translation strings */ | ||
Line 10: | Line 9: | ||
expandAll: 'Expand all', | expandAll: 'Expand all', | ||
collapseAll: 'Collapse all', | collapseAll: 'Collapse all', | ||
defaultLicense: 'Copyright unlicensed' | defaultLicense: 'Copyright unlicensed', | ||
}; | }; | ||
Line 21: | Line 20: | ||
mainClass: 'hoverbox', | mainClass: 'hoverbox', | ||
activatorClass: 'hoverbox__activator', | activatorClass: 'hoverbox__activator', | ||
displayClass: 'hoverbox__display' | displayClass: 'hoverbox__display', | ||
}; | }; | ||
config = Object.assign(defaults, config); | config = Object.assign(defaults, config); | ||
let timestamp = Date.now(), | |||
$container = $('#hoverbox-displays-' + timestamp); | $container = $('#hoverbox-displays-' + timestamp); | ||
if ( $container.length === 0 ) { | if ( $container.length === 0 ) { | ||
Line 30: | Line 29: | ||
} | } | ||
$('body').append($container); | $('body').append($container); | ||
let $hoverbox = $('.' + config.mainClass), | |||
idCounter = 0; | idCounter = 0; | ||
$hoverbox.each(function() { | $hoverbox.each(function() { | ||
let $this = $(this), | |||
$activator = $this.find('.' + config.activatorClass).first(), | $activator = $this.find('.' + config.activatorClass).first(), | ||
$display = $this.find('.' + config.displayClass).first(), | $display = $this.find('.' + config.displayClass).first(), | ||
Line 46: | Line 45: | ||
} | } | ||
$activator.hover(function() { | $activator.hover(function() { | ||
let viewport = {}, | |||
activator = {}, | activator = {}, | ||
display = {}, | display = {}, | ||
Line 103: | Line 102: | ||
*/ | */ | ||
function veiledModifier() { | function veiledModifier() { | ||
let mainClass = 'veiled'; // Class name of veiled modifier elements | |||
let affixCount = 6; // Number of veiled prefixes and veiled suffixes | |||
let affixClass = '-m0$1'; // $1 is replaced with a random number between 1 and affixCount | |||
let elements = document.getElementsByClassName(mainClass); | |||
if ( elements.length > 0 ) { | if ( elements.length > 0 ) { | ||
let last, random; | |||
for ( | for ( let i = 0; i < elements.length; i++ ) { | ||
let element = elements[i]; | |||
if ( last === undefined ) { // Choose with a random integer between 1 and affixCount | if ( last === undefined ) { // Choose with a random integer between 1 and affixCount | ||
random = Math.floor(Math.random() * affixCount) + 1; | random = Math.floor(Math.random() * affixCount) + 1; | ||
Line 127: | Line 126: | ||
function boolean_table() { | function boolean_table() { | ||
$('.boolean-table').find('table').find('td').each(function () { | $('.boolean-table').find('table').find('td').each(function () { | ||
let text = $(this).html(); | |||
if (text == '0') { | if (text == '0') { | ||
$(this).html('✗'); | $(this).html('✗'); | ||
Line 135: | Line 134: | ||
$(this).addClass('table-cell-checkmark'); | $(this).addClass('table-cell-checkmark'); | ||
} | } | ||
}) | }); | ||
} | } | ||
Line 146: | Line 145: | ||
const do_toggle = function (widget) { | const do_toggle = function (widget) { | ||
if ( ! this.classList.contains('is-selected') ) { | if ( ! this.classList.contains('is-selected') ) { | ||
let options = widget.getElementsByClassName('gemqual-widget__option'); | |||
let stats_collection = widget.getElementsByClassName('gemqual-widget__stats'); | |||
for ( | for ( let i = 0; i < options.length; i++ ) { | ||
let option = options[i]; | |||
let stats = stats_collection[i]; | |||
if ( option.dataset.qid === this.dataset.qid ) { | if ( option.dataset.qid === this.dataset.qid ) { | ||
option.classList.add('is-selected'); | option.classList.add('is-selected'); | ||
Line 161: | Line 160: | ||
} | } | ||
}; | }; | ||
for ( | for ( let i = 0; i < widgets.length; i++ ) { | ||
let widget = widgets[i]; | |||
let qtypes = widget.dataset.qtypes; | |||
qtypes = qtypes.split(','); | qtypes = qtypes.split(','); | ||
let selector = document.createElement('div'); | |||
selector.classList.add('gemqual-widget__selector'); | selector.classList.add('gemqual-widget__selector'); | ||
for ( | for ( let k = 0; k < qtypes.length; k++ ) { | ||
let option = document.createElement('button'); | |||
option.classList.add('gemqual-widget__option'); | option.classList.add('gemqual-widget__option'); | ||
option.setAttribute('data-qid', k+1); | option.setAttribute('data-qid', k+1); | ||
Line 192: | Line 191: | ||
toggleClass: 'accordion__toggle', | toggleClass: 'accordion__toggle', | ||
openStateClass: 'is-open', | openStateClass: 'is-open', | ||
controlsClass: 'accordion__controls' | controlsClass: 'accordion__controls', | ||
}; | }; | ||
config = Object.assign(defaults, config); | config = Object.assign(defaults, config); | ||
let accordions = document.getElementsByClassName(config.mainClass); | |||
if ( accordions.length === 0 ) { | if ( accordions.length === 0 ) { | ||
return; | return; | ||
Line 203: | Line 202: | ||
this.parentNode.classList.toggle(config.openStateClass); | this.parentNode.classList.toggle(config.openStateClass); | ||
}; | }; | ||
for ( | for ( let i = 0; i < accordions.length; i++ ) { | ||
let accordion = accordions[i]; | |||
let headings = accordion.getElementsByClassName(config.toggleClass); | |||
for ( | for ( let k = 0; k < headings.length; k++ ) { | ||
let heading = headings[k]; | |||
let button = document.createElement('button'); | |||
while ( heading.firstChild ) { | while ( heading.firstChild ) { | ||
button.appendChild(heading.firstChild); | button.appendChild(heading.firstChild); | ||
Line 217: | Line 216: | ||
} | } | ||
let controls = document.getElementsByClassName(config.controlsClass); | |||
if ( controls.length > 0 ) { | if ( controls.length > 0 ) { | ||
const expandAll = function (toggles) { | const expandAll = function (toggles) { | ||
for ( | for ( let i = 0; i < toggles.length; i++ ) { | ||
toggles[i].classList.add(config.openStateClass); | toggles[i].classList.add(config.openStateClass); | ||
} | } | ||
} | }; | ||
const collapseAll = function (toggles) { | const collapseAll = function (toggles) { | ||
for ( | for ( let i = 0; i < toggles.length; i++ ) { | ||
toggles[i].classList.remove(config.openStateClass); | toggles[i].classList.remove(config.openStateClass); | ||
} | } | ||
} | }; | ||
const findToggles = function(el) { | const findToggles = function(el) { | ||
if ( el === null ) { | if ( el === null ) { | ||
return false; | return false; | ||
} | } | ||
let toggles = el.getElementsByClassName(config.toggleClass); | |||
if ( toggles.length === 0 ) { | if ( toggles.length === 0 ) { | ||
return findToggles(el.parentElement); | return findToggles(el.parentElement); | ||
} | } | ||
return toggles; | return toggles; | ||
} | }; | ||
for ( | for ( let i = 0; i < controls.length; i++ ) { | ||
let control = controls[i]; | |||
let expandButton = document.createElement('button'); | |||
expandButton.innerHTML = i18n.expandAll; | expandButton.innerHTML = i18n.expandAll; | ||
let collapseButton = document.createElement('button'); | |||
collapseButton.innerHTML = i18n.collapseAll; | collapseButton.innerHTML = i18n.collapseAll; | ||
control.append('[', expandButton, '] [', collapseButton, ']'); | control.append('[', expandButton, '] [', collapseButton, ']'); | ||
let toggles = findToggles(control); | |||
if ( toggles ) { | if ( toggles ) { | ||
expandButton.addEventListener( 'click', expandAll.bind(expandButton, toggles) ); | expandButton.addEventListener( 'click', expandAll.bind(expandButton, toggles) ); | ||
Line 260: | Line 259: | ||
// Wait until the next event cycle to ensure that the licensing template preview loads | // Wait until the next event cycle to ensure that the licensing template preview loads | ||
setTimeout( function() { | setTimeout( function() { | ||
let licenseField = document.getElementById('wpLicense'); | |||
if ( licenseField ) { | if ( licenseField ) { | ||
if ( licenseField.value === '' ) { | if ( licenseField.value === '' ) { | ||
Line 291: | Line 290: | ||
mainClass: 'c-item-hoverbox', | mainClass: 'c-item-hoverbox', | ||
activatorClass: 'c-item-hoverbox__activator', | activatorClass: 'c-item-hoverbox__activator', | ||
displayClass: 'c-item-hoverbox__display' | displayClass: 'c-item-hoverbox__display', | ||
}); | }); | ||
Line 317: | Line 316: | ||
} ); | } ); | ||
}() ); | }() ); |
Revision as of 15:49, 9 October 2023
/* global mw */
/* jshint strict:true, jquery:true, esversion:6, bitwise:true, curly:true, eqeqeq:true, undef:true */
( function() {
'use strict';
/* Translation strings */
const i18n = {
expandAll: 'Expand all',
collapseAll: 'Collapse all',
defaultLicense: 'Copyright unlicensed',
};
/**
* Hoverbox
* @param config Object containing configuration
*/
function hoverbox(config) {
const defaults = {
mainClass: 'hoverbox',
activatorClass: 'hoverbox__activator',
displayClass: 'hoverbox__display',
};
config = Object.assign(defaults, config);
let timestamp = Date.now(),
$container = $('#hoverbox-displays-' + timestamp);
if ( $container.length === 0 ) {
$container = $('<div id="hoverbox-displays-' + timestamp + '" class="hoverbox-display-container"></div>');
}
$('body').append($container);
let $hoverbox = $('.' + config.mainClass),
idCounter = 0;
$hoverbox.each(function() {
let $this = $(this),
$activator = $this.find('.' + config.activatorClass).first(),
$display = $this.find('.' + config.displayClass).first(),
id = $this.data('hoverbox-id') || idCounter++,
$target = $container.find('[data-hoverbox-target="' + id + '"]');
if ( $target.length === 0 ) {
$container.append($display);
$display.attr('data-hoverbox-target', id);
} else {
$display.remove();
$display = $target;
}
$activator.hover(function() {
let viewport = {},
activator = {},
display = {},
position, // position relative to the activator
location; // location relative to the viewport
viewport.width = document.documentElement.clientWidth;
viewport.height = document.documentElement.clientHeight;
viewport.top = document.documentElement.scrollTop;
viewport.left = document.documentElement.scrollLeft;
viewport.bottom = viewport.top + viewport.height;
viewport.right = viewport.left + viewport.width;
activator.width = $activator.outerWidth();
activator.height = $activator.outerHeight();
activator.top = $activator.offset().top;
activator.left = $activator.offset().left;
activator.bottom = activator.top + activator.height;
activator.right = activator.left + activator.width;
display.width = $display.outerWidth();
display.height = $display.outerHeight();
if (viewport.width < display.width) { // Don't bother showing the hoverbox at all if the viewport is too small
return false;
}
if (activator.left > viewport.width - activator.right) {
location = 'right';
} else {
location = 'left';
}
if (activator.top - display.height > viewport.top) {
position = 'above';
display.top = activator.top - display.height;
display.left = activator.left + (activator.width / 2) - (display.width / 2);
} else if (activator.right + display.width < viewport.right) {
position = 'right-of';
display.top = activator.top + (activator.height / 2) - (display.height / 2);
display.left = activator.right;
} else if (activator.left - display.width > viewport.left) {
position = 'left-of';
display.top = activator.top + (activator.height / 2) - (display.height / 2);
display.left = activator.left - display.width;
} else {
position = 'below';
display.top = activator.bottom;
display.left = activator.left + (activator.width / 2) - (display.width / 2);
}
display.top = Math.max(viewport.top, display.top);
display.left = Math.max(viewport.left, Math.min(viewport.right - display.width, display.left));
$display.addClass('is-visible is-' + position + '-activator is-' + location + '-side-of-viewport').offset(display);
}, function() {
$display.removeClass('is-visible is-above-activator is-below-activator is-left-of-activator is-right-of-activator is-left-side-of-viewport is-right-side-of-viewport');
});
});
}
/*
* Veiled modifiers
*/
function veiledModifier() {
let mainClass = 'veiled'; // Class name of veiled modifier elements
let affixCount = 6; // Number of veiled prefixes and veiled suffixes
let affixClass = '-m0$1'; // $1 is replaced with a random number between 1 and affixCount
let elements = document.getElementsByClassName(mainClass);
if ( elements.length > 0 ) {
let last, random;
for ( let i = 0; i < elements.length; i++ ) {
let element = elements[i];
if ( last === undefined ) { // Choose with a random integer between 1 and affixCount
random = Math.floor(Math.random() * affixCount) + 1;
} else { // Continue choosing random integers in the same range while ensuring no repeats
random = Math.floor(Math.random() * (affixCount - 1)) + 1;
if ( random >= last ) {
random = random + 1;
}
}
last = random;
element.classList.add(affixClass.replace('$1', random));
}
}
}
function boolean_table() {
$('.boolean-table').find('table').find('td').each(function () {
let text = $(this).html();
if (text == '0') {
$(this).html('✗');
$(this).addClass('table-cell-xmark');
} else if (text == '1') {
$(this).html('✓');
$(this).addClass('table-cell-checkmark');
}
});
}
/**
* Gem quality widget
* @param widgets Elements containing gem quality widgets
*/
function gem_quality_widget(widgets) {
if ( widgets.length > 0 ) {
const do_toggle = function (widget) {
if ( ! this.classList.contains('is-selected') ) {
let options = widget.getElementsByClassName('gemqual-widget__option');
let stats_collection = widget.getElementsByClassName('gemqual-widget__stats');
for ( let i = 0; i < options.length; i++ ) {
let option = options[i];
let stats = stats_collection[i];
if ( option.dataset.qid === this.dataset.qid ) {
option.classList.add('is-selected');
stats.classList.add('is-selected');
} else {
option.classList.remove('is-selected');
stats.classList.remove('is-selected');
}
}
}
};
for ( let i = 0; i < widgets.length; i++ ) {
let widget = widgets[i];
let qtypes = widget.dataset.qtypes;
qtypes = qtypes.split(',');
let selector = document.createElement('div');
selector.classList.add('gemqual-widget__selector');
for ( let k = 0; k < qtypes.length; k++ ) {
let option = document.createElement('button');
option.classList.add('gemqual-widget__option');
option.setAttribute('data-qid', k+1);
option.innerHTML = qtypes[k];
if ( k === 0 ) {
option.classList.add('is-selected');
}
selector.append(option);
option.addEventListener( 'click', do_toggle.bind(option, widget) );
}
widget.prepend(selector);
}
}
}
/*
* Accordion
* @param config Object containing configuration
*/
function accordion(config) {
const defaults = {
mainClass: 'accordion',
toggleClass: 'accordion__toggle',
openStateClass: 'is-open',
controlsClass: 'accordion__controls',
};
config = Object.assign(defaults, config);
let accordions = document.getElementsByClassName(config.mainClass);
if ( accordions.length === 0 ) {
return;
}
const doToggle = function () {
this.parentNode.classList.toggle(config.openStateClass);
};
for ( let i = 0; i < accordions.length; i++ ) {
let accordion = accordions[i];
let headings = accordion.getElementsByClassName(config.toggleClass);
for ( let k = 0; k < headings.length; k++ ) {
let heading = headings[k];
let button = document.createElement('button');
while ( heading.firstChild ) {
button.appendChild(heading.firstChild);
}
heading.appendChild(button);
button.addEventListener( 'click', doToggle.bind(button) );
}
}
let controls = document.getElementsByClassName(config.controlsClass);
if ( controls.length > 0 ) {
const expandAll = function (toggles) {
for ( let i = 0; i < toggles.length; i++ ) {
toggles[i].classList.add(config.openStateClass);
}
};
const collapseAll = function (toggles) {
for ( let i = 0; i < toggles.length; i++ ) {
toggles[i].classList.remove(config.openStateClass);
}
};
const findToggles = function(el) {
if ( el === null ) {
return false;
}
let toggles = el.getElementsByClassName(config.toggleClass);
if ( toggles.length === 0 ) {
return findToggles(el.parentElement);
}
return toggles;
};
for ( let i = 0; i < controls.length; i++ ) {
let control = controls[i];
let expandButton = document.createElement('button');
expandButton.innerHTML = i18n.expandAll;
let collapseButton = document.createElement('button');
collapseButton.innerHTML = i18n.collapseAll;
control.append('[', expandButton, '] [', collapseButton, ']');
let toggles = findToggles(control);
if ( toggles ) {
expandButton.addEventListener( 'click', expandAll.bind(expandButton, toggles) );
collapseButton.addEventListener( 'click', collapseAll.bind(collapseButton, toggles) );
}
}
}
}
function setDefaultFileUploadLicense() {
if ( mw.config.get('wgCanonicalSpecialPageName') === 'Upload' ) {
mw.loader.using( 'mediawiki.special.upload', function() {
// Wait until the next event cycle to ensure that the licensing template preview loads
setTimeout( function() {
let licenseField = document.getElementById('wpLicense');
if ( licenseField ) {
if ( licenseField.value === '' ) {
licenseField.value = i18n.defaultLicense;
licenseField.dispatchEvent( new Event('change') );
}
licenseField.querySelector('option[title="{{}}"]').remove();
}
} );
} );
}
}
/* Fires when DOM is ready */
$( function() {
/* For adding expand/collapse all buttons for mw-collapsible */
$(".mw-collapsible-collapse-all").on("click", function () {
$('.mw-collapsible-toggle-expanded a').trigger('click');
});
$(".mw-collapsible-expand-all").on("click", function () {
$(".mw-collapsible-toggle-collapsed a").trigger('click');
});
// Hoverbox
hoverbox();
// Item hoverbox
hoverbox({
mainClass: 'c-item-hoverbox',
activatorClass: 'c-item-hoverbox__activator',
displayClass: 'c-item-hoverbox__display',
});
// Veiled modifiers
veiledModifier();
// Change 1 & 0 to checkmarks in tables
boolean_table();
// Set default file upload license to unknown instead of none
setDefaultFileUploadLicense();
} );
/* End DOM ready */
/* Fires when wiki content is added. */
mw.hook('wikipage.content').add( function($wikipageContent) {
// Gem quality widget in item infobox
gem_quality_widget( document.getElementsByClassName('gemqual-widget') );
// Accordions
accordion();
} );
}() );