User:SD0001/twinkle.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
This user script seems to have a documentation page at User:SD0001/twinkle. |
/**
* twinkle.js debug version
* with menus for monobook and modern skins
*
*/
// <nowiki>
/* global Morebits */
$.when(
mw.loader.getScript('/w/index.php?title=mediawiki:gadget-morebits.js&action=raw&ctype=text/javascript'),
importStylesheet('mediawiki:gadget-morebits.css'),
mw.loader.using(['jquery.ui', 'jquery.tipsy', 'jquery.chosen']),
importStylesheet('mediawiki:gadget-Twinkle-pagestyles.css')
).then(function() {
//(function (window, document, $, undefined) { // Wrap with anonymous function
var Twinkle = {};
window.Twinkle = Twinkle; // allow global access
// Check if account is experienced enough to use Twinkle
Twinkle.userAuthorized = Morebits.userIsInGroup('autoconfirmed') || Morebits.userIsInGroup('confirmed');
// for use by custom modules (normally empty)
Twinkle.initCallbacks = [];
Twinkle.addInitCallback = function twinkleAddInitCallback(func) {
Twinkle.initCallbacks.push(func);
};
Twinkle.defaultConfig = {};
/**
* Twinkle.defaultConfig.twinkle and Twinkle.defaultConfig.friendly
*
* This holds the default set of preferences used by Twinkle. (The |friendly| object holds preferences stored in the FriendlyConfig object.)
* It is important that all new preferences added here, especially admin-only ones, are also added to
* |Twinkle.config.sections| in twinkleconfig.js, so they are configurable via the Twinkle preferences panel.
* For help on the actual preferences, see the comments in twinkleconfig.js.
*/
Twinkle.defaultConfig.twinkle = {
// General
summaryAd: ' ([[WP:TW|TW]])',
deletionSummaryAd: ' ([[WP:TW|TW]])',
protectionSummaryAd: ' ([[WP:TW|TW]])',
userTalkPageMode: 'tab',
dialogLargeFont: false,
// ARV
spiWatchReport: 'yes',
// Block
blankTalkpageOnIndefBlock: false,
// Fluff (revert and rollback)
openTalkPage: [ 'agf', 'norm', 'vand' ],
openTalkPageOnAutoRevert: false,
markRevertedPagesAsMinor: [ 'vand' ],
watchRevertedPages: [ 'agf', 'norm', 'vand', 'torev' ],
offerReasonOnNormalRevert: true,
confirmOnFluff: false,
showRollbackLinks: [ 'diff', 'others' ],
// DI (twinkleimage)
notifyUserOnDeli: true,
deliWatchPage: 'default',
deliWatchUser: 'default',
// PROD
watchProdPages: true,
prodReasonDefault: '',
logProdPages: false,
prodLogPageName: 'PROD log',
// CSD
speedySelectionStyle: 'buttonClick',
watchSpeedyPages: [ 'g3', 'g5', 'g10', 'g11', 'g12' ],
markSpeedyPagesAsPatrolled: false,
// these next two should probably be identical by default
welcomeUserOnSpeedyDeletionNotification: [ 'db', 'g1', 'g2', 'g3', 'g4', 'g6', 'g10', 'g11', 'g12', 'g13', 'g14', 'a1', 'a2', 'a3', 'a5', 'a7', 'a9', 'a10', 'a11', 'f1', 'f2', 'f3', 'f7', 'f9', 'f10', 'u3', 'u5', 't2', 't3', 'p1', 'p2' ],
notifyUserOnSpeedyDeletionNomination: [ 'db', 'g1', 'g2', 'g3', 'g4', 'g6', 'g10', 'g11', 'g12', 'g13', 'g14', 'a1', 'a2', 'a3', 'a5', 'a7', 'a9', 'a10', 'a11', 'f1', 'f2', 'f3', 'f7', 'f9', 'f10', 'u3', 'u5', 't2', 't3', 'p1', 'p2' ],
warnUserOnSpeedyDelete: [ 'db', 'g1', 'g2', 'g3', 'g4', 'g6', 'g10', 'g11', 'g12', 'g13', 'g14', 'a1', 'a2', 'a3', 'a5', 'a7', 'a9', 'a10', 'a11', 'f1', 'f2', 'f3', 'f7', 'f9', 'f10', 'u3', 'u5', 't2', 't3', 'p1', 'p2' ],
promptForSpeedyDeletionSummary: [],
deleteTalkPageOnDelete: true,
deleteRedirectsOnDelete: true,
deleteSysopDefaultToTag: false,
speedyWindowHeight: 500,
speedyWindowWidth: 800,
logSpeedyNominations: false,
speedyLogPageName: 'CSD log',
noLogOnSpeedyNomination: [ 'u1' ],
// Unlink
unlinkNamespaces: [ '0', '10', '100', '118' ],
// Warn
defaultWarningGroup: '1',
showSharedIPNotice: true,
watchWarnings: true,
oldSelect: false,
customWarningList: [],
autoMenuAfterRollback: false,
// XfD
xfdWatchDiscussion: 'default',
xfdWatchList: 'no',
xfdWatchPage: 'default',
xfdWatchUser: 'default',
xfdWatchRelated: 'default',
markXfdPagesAsPatrolled: true,
// Hidden preferences
revertMaxRevisions: 50,
batchdeleteChunks: 50,
batchMax: 5000,
batchProtectChunks: 50,
batchundeleteChunks: 50,
proddeleteChunks: 50
};
// now some skin dependent config.
switch (mw.config.get('skin')) {
case 'vector':
Twinkle.defaultConfig.twinkle.portletArea = 'right-navigation';
Twinkle.defaultConfig.twinkle.portletId = 'p-twinkle';
Twinkle.defaultConfig.twinkle.portletName = 'TW';
Twinkle.defaultConfig.twinkle.portletType = 'menu';
Twinkle.defaultConfig.twinkle.portletNext = 'p-search';
break;
case 'monobook': // falls through
case 'modern':
Twinkle.defaultConfig.twinkle.portletArea = 'p-cactions';
Twinkle.defaultConfig.twinkle.portletId = 'ca-twinkle';
Twinkle.defaultConfig.twinkle.portletName = 'twinkle';
Twinkle.defaultConfig.twinkle.portletType = 'menu';
Twinkle.defaultConfig.twinkle.portletNext = null;
break;
default:
Twinkle.defaultConfig.twinkle.portletArea = null;
Twinkle.defaultConfig.twinkle.portletId = 'p-cactions';
Twinkle.defaultConfig.twinkle.portletName = null;
Twinkle.defaultConfig.twinkle.portletType = null;
Twinkle.defaultConfig.twinkle.portletNext = null;
}
Twinkle.defaultConfig.friendly = {
// Tag
groupByDefault: true,
watchTaggedPages: true,
watchMergeDiscussions: true,
markTaggedPagesAsMinor: false,
markTaggedPagesAsPatrolled: true,
tagArticleSortOrder: 'cat',
customTagList: [],
customFileTagList: [],
customRedirectTagList: [],
// Welcome
topWelcomes: false,
watchWelcomes: true,
welcomeHeading: 'Welcome',
insertHeadings: true,
insertUsername: true,
insertSignature: true, // sign welcome templates, where appropriate
quickWelcomeMode: 'norm',
quickWelcomeTemplate: 'welcome',
customWelcomeList: [],
customWelcomeSignature: true,
// Talkback
markTalkbackAsMinor: true,
insertTalkbackSignature: true, // always sign talkback templates
talkbackHeading: 'New message from ' + mw.config.get('wgUserName'),
adminNoticeHeading: 'Notice',
mailHeading: "You've got mail!",
// Shared
markSharedIPAsMinor: true
};
Twinkle.getPref = function twinkleGetPref(name) {
var result;
if (typeof Twinkle.prefs === 'object' && typeof Twinkle.prefs.twinkle === 'object') {
// look in Twinkle.prefs (twinkleoptions.js)
result = Twinkle.prefs.twinkle[name];
} else if (typeof window.TwinkleConfig === 'object') {
// look in TwinkleConfig
result = window.TwinkleConfig[name];
}
if (result === undefined) {
return Twinkle.defaultConfig.twinkle[name];
}
return result;
};
Twinkle.getFriendlyPref = function twinkleGetFriendlyPref(name) {
var result;
if (typeof Twinkle.prefs === 'object' && typeof Twinkle.prefs.friendly === 'object') {
// look in Twinkle.prefs (twinkleoptions.js)
result = Twinkle.prefs.friendly[name];
} else if (typeof window.FriendlyConfig === 'object') {
// look in FriendlyConfig
result = window.FriendlyConfig[name];
}
if (result === undefined) {
return Twinkle.defaultConfig.friendly[name];
}
return result;
};
/**
* **************** Twinkle.addPortlet() ****************
*
* Adds a portlet menu to one of the navigation areas on the page.
* This is necessarily quite a hack since skins, navigation areas, and
* portlet menu types all work slightly different.
*
* Available navigation areas depend on the skin used.
* Monobook:
* "column-one", outer div class "portlet", inner div class "pBody". Existing portlets: "p-cactions", "p-personal", "p-logo", "p-navigation", "p-search", "p-interaction", "p-tb", "p-coll-print_export"
* Special layout of p-cactions and p-personal through specialized styles.
* Vector:
* "mw-panel", outer div class "portal", inner div class "body". Existing portlets/elements: "p-logo", "p-navigation", "p-interaction", "p-tb", "p-coll-print_export"
* "left-navigation", outer div class "vectorTabs" or "vectorMenu", inner div class "" or "menu". Existing portlets: "p-namespaces", "p-variants" (menu)
* "right-navigation", outer div class "vectorTabs" or "vectorMenu", inner div class "" or "menu". Existing portlets: "p-views", "p-cactions" (menu), "p-search"
* Special layout of p-personal portlet (part of "head") through specialized styles.
* Modern:
* "mw_contentwrapper" (top nav), outer div class "portlet", inner div class "pBody". Existing portlets or elements: "p-cactions", "mw_content"
* "mw_portlets" (sidebar), outer div class "portlet", inner div class "pBody". Existing portlets: "p-navigation", "p-search", "p-interaction", "p-tb", "p-coll-print_export"
*
* @param String navigation -- id of the target navigation area (skin dependant, on vector either of "left-navigation", "right-navigation", or "mw-panel")
* @param String id -- id of the portlet menu to create, preferably start with "p-".
* @param String text -- name of the portlet menu to create. Visibility depends on the class used.
* @param String type -- type of portlet. Currently only used for the vector non-sidebar portlets, pass "menu" to make this portlet a drop down menu.
* @param Node nextnodeid -- the id of the node before which the new item should be added, should be another item in the same list, or undefined to place it at the end.
*
* @return Node -- the DOM node of the new item (a DIV element) or null
*/
Twinkle.addPortlet = function(navigation, id, text, type, nextnodeid) {
// sanity checks, and get required DOM nodes
var root = document.getElementById(navigation);
if (!root) {
return null;
}
var item = document.getElementById(id);
if (item) {
if (item.parentNode && item.parentNode === root) {
return item;
}
return null;
}
var nextnode;
if (nextnodeid) {
nextnode = document.getElementById(nextnodeid);
}
// verify/normalize input
var skin = mw.config.get('skin');
if ((skin === 'vector' && (navigation !== 'left-navigation' && navigation !== 'right-navigation')) ||
((skin === 'monobook' || skin === 'modern') && navigation !== 'p-cactions')) {
type = ''; // can't create a menu outside these navigation areas
}
if (type === 'menu' && (skin === 'monobook' || skin === 'modern')) {
// Adapted from [[User:Haza-w/Drop-down menus]], code at [[MediaWiki:Gadget-dropdown-menus-nonvector.js]]
var props = {
hovms: 400, // make user-configurable?
menus: [],
mouse: null,
timer: [],
};
// helper functions for generating menu:
function getPos(offset) {
var obj = document.getElementById(Twinkle.getPref('portletId'));
return [ obj.offsetLeft + offset[0], obj.offsetTop + offset[1] ];
}
function showMenu(pos) {
$('#' + Twinkle.getPref('portletId')).addClass('selected');
var mid = 'ca-twinkle-menu';
props.mouse = mid;
if (pos) {
props.menus.forEach(function(menu) {
if (props.timer[menu]) {
clearTimeout(props.timer[menu]);
props.timer[menu] = null;
}
if (mid.replace(/-[^-]+$/, '') !== menu) {
document.getElementById(menu).style.display = 'none';
}
});
}
if (!props.timer[mid]) {
var m = document.getElementById(mid);
m.style.display = ''; // show
if (pos) {
m.style.left = pos[0]+'px';
m.style.top = pos[1]+'px';
}
} else {
clearTimeout(props.timer[mid]);
props.timer[mid] = null;
}
}
function hideMenu() {
var mid = 'ca-twinkle-menu';
if (mid == props.mouse.replace(/-[^-]+$/,''))
props.timer[mid] = null;
if (props.timer[mid]) {
props.timer[mid] = null;
document.getElementById(mid).style.display = 'none';
if (mid == props.mouse && mid.search(/opt-.*-/) != -1)
document.getElementById(mid.replace(/-[^-]+$/,'')).style.display = 'none';
} else {
props.timer[mid] = setTimeout(function() {
hideMenu(mid);
$('#' + Twinkle.getPref('portletId')).removeClass('selected');
}, props.hovms);
}
}
//var li = mw.util.addPortletLink('p-cactions', '#', 'twinkle', 'ca-twinkle'); // for testing
var li = mw.util.addPortletLink(Twinkle.getPref('portletArea'), '#', Twinkle.getPref('portletName'), Twinkle.getPref('portletId'), null, null, Twinkle.getPref('portletNext'));
$(li).find('a')
.on('mouseover', function() {
showMenu(getPos([-10, 20]));
})
.on('mouseout', hideMenu)
.on('click', function(e) {
e.preventDefault();
});
var menu = document.createElement('div');
menu.id = 'ca-twinkle-menu';
mw.util.addCSS(
'#ca-twinkle-menu {' +
' background-color: #EEEEEE;' +
' border: 1px solid #AAAAAA;' +
' border-collapse: collapse;' +
' color: #638C9C;' +
' font: 14px/22px Arial, Helvetica, sans-serif;' +
' padding: 0.3em 0em .3em 0em;' +
' position: absolute;' +
' width: 78px;' +
' z-index: 1000;' +
'}' +
'#ca-twinkle-menu ul {' +
' list-style: none;' +
' margin: 0 0 0 0;' +
'}' +
'#ca-twinkle-menu ul li {' +
' display: list-item;' +
' width: 70px;' +
' font-size: 14px;' +
' margin: 0em;' +
' padding: 0 0 0 0.5em;' +
' border: 0;' +
' background: none;' +
'}' +
'#ca-twinkle-menu ul li a {' +
' color: #002BB8 !important;' +
' text-decoration: none !important;' +
' background-color: #EEEEEE !important;' +
' padding: 0 0 0 0;' +
' text-transform: none;' +
'}' +
'#ca-twinkle-menu ul li:hover a {' +
' text-decoration: underline !important;' +
'}'
);
menu.style.display = 'none';
$(menu).on('mouseover', showMenu).on('mouseout', hideMenu);
li.appendChild(menu);
return li;
} else { // Vector skin; and for other skins without menu
var outerDivClass;
switch (skin) {
case 'vector':
// XXX: portal doesn't work
if (navigation !== 'portal' && navigation !== 'left-navigation' && navigation !== 'right-navigation') {
navigation = 'mw-panel';
}
outerDivClass = navigation === 'mw-panel' ? 'portal' : type === 'menu' ? 'vectorMenu' : 'vectorTabs';
break;
case 'modern':
if (navigation !== 'mw_portlets' && navigation !== 'mw_contentwrapper') {
navigation = 'p-cactions'; // changed from mw_portlets
}
outerDivClass = 'portlet';
break;
default:
navigation = 'column-one';
outerDivClass = 'portlet';
break;
}
// Build the DOM elements.
var outerDiv = document.createElement('div');
outerDiv.className = outerDivClass + ' emptyPortlet';
outerDiv.id = id;
if (nextnode && nextnode.parentNode === root) {
root.insertBefore(outerDiv, nextnode);
} else {
root.appendChild(outerDiv);
}
if (type === 'menu') { // menu in vector skin
// add invisible checkbox to keep menu when clicked
// similar to the p-cactions ("More") menu
var chkbox = document.createElement('input');
chkbox.className = 'vectorMenuCheckbox';
chkbox.setAttribute('type', 'checkbox');
chkbox.setAttribute('aria-labelledby', 'p-twinkle-label');
outerDiv.appendChild(chkbox);
var h5 = document.createElement('h3');
h5.id = 'p-twinkle-label';
var span = document.createElement('span');
span.appendChild(document.createTextNode(text));
h5.appendChild(span);
var a = document.createElement('a');
a.href = '#';
$(a).click(function (e) {
e.preventDefault();
if (!Twinkle.userAuthorized) {
alert('Sorry, your account is too new to use Twinkle.');
}
});
h5.appendChild(a);
outerDiv.appendChild(h5);
var ul = document.createElement('ul');
ul.className = 'menu';
var innerDiv = document.createElement('div');
innerDiv.appendChild(ul);
outerDiv.appendChild(innerDiv);
} else {
var h5 = document.createElement('h3');
h5.appendChild(document.createTextNode(text));
outerDiv.appendChild(h5);
var ul = document.createElement('ul');
outerDiv.appendChild(ul);
}
return outerDiv;
}
}
/**
* **************** Twinkle.addPortletLink() ****************
* Builds a portlet menu if it doesn't exist yet, and add the portlet link.
* @param task: Either a URL for the portlet link or a function to execute.
*/
Twinkle.addPortletLink = function(task, text, id, tooltip) {
if (Twinkle.getPref('portletArea') !== null) {
Twinkle.addPortlet(Twinkle.getPref('portletArea'), Twinkle.getPref('portletId'), Twinkle.getPref('portletName'), Twinkle.getPref('portletType'), Twinkle.getPref('portletNext'));
}
var link = mw.util.addPortletLink(Twinkle.getPref('portletId'), typeof task === 'string' ? task : '#', text, id, tooltip);
$('.client-js .skin-vector #p-cactions').css('margin-right', 'initial');
$(link).click(function (ev) {
if (typeof task === 'function') {
task();
ev.preventDefault();
} else {
link.querySelector('a').click();
}
});
if ($.collapsibleTabs) {
$.collapsibleTabs.handleResize();
}
return link;
};
/**
* **************** General initialization code ****************
*/
var scriptpathbefore = mw.util.wikiScript('index') + '?title=',
scriptpathafter = '&action=raw&ctype=text/javascript&happy=yes';
// Retrieve the user's Twinkle preferences
$.ajax({
url: scriptpathbefore + 'User:' + encodeURIComponent(mw.config.get('wgUserName')) + '/twinkleoptions.js' + scriptpathafter,
dataType: 'text'
})
.fail(function () {
mw.notify('Could not load twinkleoptions.js');
})
.done(function (optionsText) {
// Quick pass if user has no options
if (optionsText === '') {
return;
}
// Twinkle options are basically a JSON object with some comments. Strip those:
optionsText = optionsText.replace(/(?:^(?:\/\/[^\n]*\n)*\n*|(?:\/\/[^\n]*(?:\n|$))*$)/g, '');
// First version of options had some boilerplate code to make it eval-able -- strip that too. This part may become obsolete down the line.
if (optionsText.lastIndexOf('window.Twinkle.prefs = ', 0) === 0) {
optionsText = optionsText.replace(/(?:^window.Twinkle.prefs = |;\n*$)/g, '');
}
try {
var options = JSON.parse(optionsText);
// Assuming that our options evolve, we will want to transform older versions:
// if ( options.optionsVersion === undefined ) {
// ...
// options.optionsVersion = 1;
// }
// if ( options.optionsVersion === 1 ) {
// ...
// options.optionsVersion = 2;
// }
// At the same time, twinkleconfig.js needs to be adapted to write a higher version number into the options.
if (options) {
Twinkle.prefs = options;
}
} catch (e) {
mw.notify('Could not parse twinkleoptions.js');
}
})
.always(function () {
$(Twinkle.load);
});
// Developers: you can import custom Twinkle modules here
// For example, mw.loader.load(scriptpathbefore + "User:UncleDouggie/morebits-test.js" + scriptpathafter);
Twinkle.load = function () {
// Don't activate on special pages other than those on the whitelist so that
// they load faster, especially the watchlist.
var specialPageWhitelist = [ 'Contributions', 'DeletedContributions', 'Prefixindex' ];
var isSpecialPage = mw.config.get('wgNamespaceNumber') === -1 &&
specialPageWhitelist.indexOf(mw.config.get('wgCanonicalSpecialPageName')) === -1;
// Also, Twinkle is incompatible with Internet Explorer versions 8 or lower,
// so don't load there either.
var isOldIE = $.client.profile().name === 'msie' &&
$.client.profile().versionNumber < 9;
// Prevent users that are not autoconfirmed from loading Twinkle as well.
if (isSpecialPage || isOldIE || !Twinkle.userAuthorized) {
return;
}
// Prevent clickjacking
if (window.top !== window.self) {
return;
}
// Set custom Api-User-Agent header, for server-side logging purposes
Morebits.wiki.api.setApiUserAgent('Twinkle/2.0 (' + mw.config.get('wgDBname') + ')');
// Load the modules in the order that the tabs should appear
// User/user talk-related
$.when(
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklearv.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklebatchdelete.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklebatchprotect.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklebatchundelete.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinkleblock.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinkleconfig.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinkledeprod.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklediff.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklefluff.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinkleimage.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinkleprod.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinkleprotect.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklespeedy.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinkleunlink.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklewarn.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-twinklexfd.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-friendlytag.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-friendlyshared.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-friendlywelcome.js' + scriptpathafter),
mw.loader.getScript(scriptpathbefore + 'MediaWiki:Gadget-friendlytalkback.js' + scriptpathafter)
).then(function() {
Twinkle.arv();
Twinkle.warn();
if (Morebits.userIsInGroup('sysop')) {
Twinkle.block();
}
Twinkle.welcome();
Twinkle.shared();
Twinkle.talkback();
// Deletion
Twinkle.speedy();
Twinkle.prod();
Twinkle.xfd();
Twinkle.image();
// Maintenance
Twinkle.protect();
Twinkle.tag();
// Misc. ones last
Twinkle.diff();
Twinkle.unlink();
Twinkle.config.init();
Twinkle.fluff.init();
if (Morebits.userIsInGroup('sysop')) {
Twinkle.deprod();
Twinkle.batchdelete();
Twinkle.batchprotect();
Twinkle.batchundelete();
}
});
// Run the initialization callbacks for any custom modules
Twinkle.initCallbacks.forEach(function (func) {
func();
});
Twinkle.addInitCallback = function (func) {
func();
};
// Increases text size in Twinkle dialogs, if so configured
if (Twinkle.getPref('dialogLargeFont')) {
mw.util.addCSS('.morebits-dialog-content, .morebits-dialog-footerlinks { font-size: 100% !important; } ' +
'.morebits-dialog input, .morebits-dialog select, .morebits-dialog-content button { font-size: inherit !important; }');
}
};
});
//}(window, document, jQuery)); // End wrap with anonymous function
// </nowiki>