MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
(Accordions (used by Template:Item modifier compatibility) |
||
| Line 4: | Line 4: | ||
( function () { | ( function () { | ||
'use strict'; | 'use strict'; | ||
/* Translation strings */ | |||
var i18n = { | |||
expandAll: 'Expand all', | |||
collapseAll: 'Collapse all', | |||
}; | |||
/** | /** | ||
| Line 89: | Line 95: | ||
}); | }); | ||
}); | }); | ||
} | |||
/* | |||
* Accordion | |||
* @param config Object containing configuration | |||
*/ | |||
function accordion(config) { | |||
var defaults = { | |||
mainClass: 'accordion', | |||
toggleClass: 'accordion__toggle', | |||
openStateClass: 'is-open', | |||
controlsClass: 'accordion__controls', | |||
}; | |||
config = Object.assign(defaults, config); | |||
var accordions = document.getElementsByClassName(config.mainClass); | |||
if ( accordions.length === 0 ) { | |||
return; | |||
} | |||
var doToggle = function () { | |||
this.parentNode.classList.toggle(config.openStateClass); | |||
}; | |||
for ( var i = 0; i < accordions.length; i++ ) { | |||
var accordion = accordions[i]; | |||
var headings = accordion.getElementsByClassName(config.toggleClass); | |||
for ( var k = 0; k < headings.length; k++ ) { | |||
var heading = headings[k]; | |||
var button = document.createElement('button'); | |||
while ( heading.firstChild ) { | |||
button.appendChild(heading.firstChild); | |||
} | |||
heading.appendChild(button); | |||
button.addEventListener( 'click', doToggle.bind(button) ); | |||
} | |||
} | |||
var controls = document.getElementsByClassName(config.controlsClass); | |||
if ( controls.length > 0 ) { | |||
var expandAll = function (toggles) { | |||
for ( var i = 0; i < toggles.length; i++ ) { | |||
toggles[i].classList.add(config.openStateClass); | |||
} | |||
}; | |||
var collapseAll = function (toggles) { | |||
for ( var i = 0; i < toggles.length; i++ ) { | |||
toggles[i].classList.remove(config.openStateClass); | |||
} | |||
}; | |||
var findToggles = function (el) { | |||
if ( el === null ) { | |||
return false; | |||
} | |||
var toggles = el.getElementsByClassName(config.toggleClass); | |||
if ( toggles.length === 0 ) { | |||
return findToggles(el.parentElement); | |||
} | |||
return toggles; | |||
}; | |||
for ( var i = 0; i < controls.length; i++ ) { | |||
var control = controls[i]; | |||
var expandButton = document.createElement('button'); | |||
expandButton.innerHTML = i18n.expandAll; | |||
var collapseButton = document.createElement('button'); | |||
collapseButton.innerHTML = i18n.collapseAll; | |||
control.append('[', expandButton, '] [', collapseButton, ']'); | |||
var toggles = findToggles(control); | |||
if ( toggles ) { | |||
expandButton.addEventListener( 'click', expandAll.bind(expandButton, toggles) ); | |||
collapseButton.addEventListener( 'click', collapseAll.bind(collapseButton, toggles) ); | |||
} | |||
} | |||
} | |||
} | } | ||
| Line 96: | Line 174: | ||
// Hoverboxes | // Hoverboxes | ||
hoverbox(); | hoverbox(); | ||
// Accordions | |||
accordion(); | |||
} ); | } ); | ||
Latest revision as of 23:59, 13 December 2025
/* global mw */
/* jshint strict:true, jquery:true, esversion:5, bitwise:true, curly:true, eqeqeq:true, undef:true, latedef:true, trailingcomma:true */
( function () {
'use strict';
/* Translation strings */
var i18n = {
expandAll: 'Expand all',
collapseAll: 'Collapse all',
};
/**
* Hoverbox
* @param config Object containing configuration
*/
function hoverbox(config) {
var defaults = {
mainClass: 'hoverbox',
activatorClass: 'hoverbox__activator',
displayClass: 'hoverbox__display',
};
config = Object.assign(defaults, config);
var 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);
var $hoverbox = $('.' + config.mainClass),
idCounter = 0;
$hoverbox.each(function () {
var $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 () {
var 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');
});
});
}
/*
* Accordion
* @param config Object containing configuration
*/
function accordion(config) {
var defaults = {
mainClass: 'accordion',
toggleClass: 'accordion__toggle',
openStateClass: 'is-open',
controlsClass: 'accordion__controls',
};
config = Object.assign(defaults, config);
var accordions = document.getElementsByClassName(config.mainClass);
if ( accordions.length === 0 ) {
return;
}
var doToggle = function () {
this.parentNode.classList.toggle(config.openStateClass);
};
for ( var i = 0; i < accordions.length; i++ ) {
var accordion = accordions[i];
var headings = accordion.getElementsByClassName(config.toggleClass);
for ( var k = 0; k < headings.length; k++ ) {
var heading = headings[k];
var button = document.createElement('button');
while ( heading.firstChild ) {
button.appendChild(heading.firstChild);
}
heading.appendChild(button);
button.addEventListener( 'click', doToggle.bind(button) );
}
}
var controls = document.getElementsByClassName(config.controlsClass);
if ( controls.length > 0 ) {
var expandAll = function (toggles) {
for ( var i = 0; i < toggles.length; i++ ) {
toggles[i].classList.add(config.openStateClass);
}
};
var collapseAll = function (toggles) {
for ( var i = 0; i < toggles.length; i++ ) {
toggles[i].classList.remove(config.openStateClass);
}
};
var findToggles = function (el) {
if ( el === null ) {
return false;
}
var toggles = el.getElementsByClassName(config.toggleClass);
if ( toggles.length === 0 ) {
return findToggles(el.parentElement);
}
return toggles;
};
for ( var i = 0; i < controls.length; i++ ) {
var control = controls[i];
var expandButton = document.createElement('button');
expandButton.innerHTML = i18n.expandAll;
var collapseButton = document.createElement('button');
collapseButton.innerHTML = i18n.collapseAll;
control.append('[', expandButton, '] [', collapseButton, ']');
var toggles = findToggles(control);
if ( toggles ) {
expandButton.addEventListener( 'click', expandAll.bind(expandButton, toggles) );
collapseButton.addEventListener( 'click', collapseAll.bind(collapseButton, toggles) );
}
}
}
}
/* Fires when wiki content is added. */
mw.hook('wikipage.content').add( function ($wikipageContent) {
// Hoverboxes
hoverbox();
// Accordions
accordion();
} );
/* Called during configuration of ACE editor (Extension:CodeEditor) */
mw.hook('codeEditor.configure').add( function(editSession) {
// Use spaces instead of a tab character when tabbing
editSession.setUseSoftTabs(true);
} );
}() );