Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
!function(cfg) {
// unified control panel. codename: ucp
// fancy switches to do fancy actions
// proxyfy the cfg
var urlVars = new URLSearchParams(location.search);
var _trans = {
get: function(obj, prop) {
// check whether msg is exists and return it
// undef means original msg (from the element definition)
if (cfg.i18nMessages && prop) {
var msg = cfg.i18nMessages.msg(prop).plain();
if (msg === '<' + prop + '>') msg = undefined;
return msg;
}
if (obj.hasOwnProperty(prop)) return obj[prop];
},
set: function(obj, prop, value) {
obj[prop] = value;
},
},
_cfg = {
get: function(obj, prop) {
if (prop === 'trans') {
return this._trans;
}
if (obj.hasOwnProperty(prop)) return obj[prop];
},
set: function(obj, prop, value) {
if (prop === 'trans') {
this._trans = new Proxy(value, _trans);
} else {
obj[prop] = value;
}
},
};
cfg = new Proxy(cfg, _cfg);
// clone urlvars in order to preserve it natural
var oUrlVars = {};
urlVars.forEach(function(value, key) {
oUrlVars[key] = value;
});
var urlVarsCloned = cloneObject(oUrlVars);
cfg.debug = cfg.debug || urlVarsCloned.debug || urlVarsCloned.debug1;
if (cfg.loaded && !cfg.debug) return;
cfg.loaded = !0;
cfg.mwc = mw.config.get(['wgSassParams', 'wgUserLanguage']);
cfg.mwc.wgSassParams = cfg.mwc.wgSassParams || {};
function log() {
if (!cfg.debug) return;
var a = [].slice.call(arguments);
a.unshift('ucp');
console.log.apply(this, a);
}// log
function cloneObject(what) {
// cuz $.extend and json.parse(.stringify) loses x when x undefined is
if (!what) return;
var cloned = {};
for (var i in what) if (what.hasOwnProperty(i)) cloned[i] = what[i];
return cloned;
}// cloneObject
function updateAction(source, target) {
// updates target actions. source: source
// source: {action.id: action}
// target: actions = []
if (!source || !Object.keys(source).length) return;
log('ua.source', source);
Object.values(source).forEach(function(sourceAction) {
// update target
var targetIndex = Object.values(target).findIndex(function(action) {
return action.id === sourceAction.id;
});
log('ua.index', targetIndex, sourceAction);
if (targetIndex === -1) targetIndex = target.length;
target[targetIndex] = $.extend(true, target[targetIndex], sourceAction);
log('ua.newtarget', target[targetIndex]);
});
}// updateAction
function preload(globalActions, data) {
// preloads data from data or current location
// globalActions: actions to check against data
// in order to convert data more precisely
// returns actions = {action: data}
data = data || urlVarsCloned;
if (!data || !Object.keys(data).length) return;
var actions = {};
Object.keys(data).forEach(function(key) {
if (!key) return;
var ga;
// if action is in globalActions
ga = globalActions.map(function(v) {
return (v.id === key || v.action === key + '=' + data[key]) ? v : false;
}).filter(Boolean)[0];
log('p.ga', ga);
actions[key] = {
id: ga ? ga.id : key,
action: ga ? ga.action : key,
caption: ga ? ga.caption : key,
checked: true,
hasInput: ga ? ga.hasInput : (typeof(data[key]) === 'undefined' ? undefined : true),
inputVal: typeof(data[key]) === 'undefined' ? undefined : data[key],
};
});
return actions;
}// preload
function btnShowForm_click() {
if (cfg.form.parents('html').length) {
// attached to the dom. detach it
btnCancel_click();
} else {
// not attached yet
cfg.form.prependTo('#content');
// set size and position
cfg.form.offset({
top: $(window).scrollTop() + ((($(window).height() - cfg.form.height()) / 2 > 0) ? ($(window).height() - cfg.form.height()) / 2 : 0),
left: $(window).scrollLeft() + ((($(window).width() - cfg.form.width()) / 2 > 0) ? ($(window).width() - cfg.form.width()) / 2 : 0)
});
log('sf.offset', cfg.form.offset());
}
}// btnShowForm_click
function btnCancel_click() {
cfg.form = cfg.form.detach();
}// btnCancel_click
function btnDo_click() {
// do stuff
// actions are not encoded. by design
var actions = [];
cfg.form.find('.ucp-action input:checked').each(function() {
var $chkbox = $(this),
$action = $chkbox.closest('.ucp-action'),
$input = $action.find('input[type="text"]'),
action = $action.data('action'),
actionValue = $input.val() || null;
actions.push(action + (actionValue === null ? '' : '=' + actionValue));
});// each action.input:checked
actions = actions.filter(Boolean);
if (!actions.length && !location.search) {
// nothing to do
btnCancel_click();
return;
}
log('dc.actions', actions);
// will keep ? at the end of the url. by design
location.search = actions.length ? '?' + actions.join('&') : '';
}// btnDo_click
function txt_click(e) {
if (typeof(this.select) === 'function') this.select();
}// txt_click
function getLimitReport() {
// returns pp limit reports
log('glr');
var comment,
ret = [],
nodeIterator = document.createNodeIterator(
document.getElementById('mw-content-text') || document.body,
NodeFilter.SHOW_COMMENT,
function(node) {
return node.data && node.data.indexOf('NewPP') > -1 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
);
while (comment = nodeIterator.nextNode()) {
// remove unnecessary header and add to the ret array
ret.push(comment.data.replace(/[\s\n]*NewPP.*?\n/, ''));
}
log('glr.ret', ret);
return ret.join('----------\n');
}// getLimitReport
function refreshLimitReport() {
// re-read limit reports (to get data from tabviews etc)
log('rlr');
var limitReport = getLimitReport();
// set data via class in order to allow external widgets
if (limitReport) $('.ucp-widget-newpp').attr('title', limitReport);
}// refreshLimitReport
function createForm(actions) {
// actions: [{id, action, caption, title, checked, hasInput, inputSize, inputPh, inputVal, params: [{param: value}]}]
// action.id is always equal to action name (raw for raw etc), unless it 2nd+ entry
// any other format might (and most probably will) not work on preload stage
// trans: {id: text}
// returns $form
// encode/decode stuff totally on user is. by design
importArticle({type: 'style', article: 'w:c:dev:MediaWiki:UnifiedControlPanel.css'});
actions = $.isArray(actions) ? actions : [];
var $form = $('<div>', {
id: 'ucp-main',
}),
$btnDo = $('<span>', {
class: 'ucp-btn ucp-btn-do wds-button',
text: cfg.trans.btndotext || 'do it',
title: cfg.trans.btndotitle || 'just do it',
}),
$btnCancel = $('<span>', {
class: 'ucp-btn ucp-btn-cancel wds-button wds-is-secondary',
text: cfg.trans.btncanceltext || 'cancel',
title: cfg.trans.btncanceltitle || 'just don\'t',
}),
$bar = $('<div>', {
class: 'ucp-bar',
}),
$widgetNewpp = $('<span>', {
class: 'ucp-bar-widget ucp-widget-newpp wds-button wds-is-text',
text: 'NewPP',
});
$btnDo.click(btnDo_click);
$btnCancel.click(btnCancel_click);
$widgetNewpp.click(refreshLimitReport);
/*
$form.on('keydown.ucp', function(e) {
if (e && e.keyCode === 27) btnCancel_click();
});
*/
// add actions
actions.forEach(function(action) {
var $action = $('<span>', {
class: 'ucp-action',
id: 'ucp-action-' + action.id,
'data-action': action.action,
}),
$chkbox = $('<input>', {
type: 'checkbox',
id: 'ucp-chk-' + action.id,
}),
$label = $('<label>', {
'for': 'ucp-chk-' + action.id,
id: 'ucp-label-' + action.id,
text: cfg.trans[action.id + 'label'] || action.caption,
title: cfg.trans[action.id + 'title'] || action.title,
}),
$input = action.hasInput ? $('<input>', {
type: 'text',
id: 'ucp-input-' + action.id,
size: action.inputSize || 5,
placeholder: action.inputPh || '',// doesn't work; added later via .attr
}) : null;
// some adjustments
// for some weird reason placeholder can't be added as {plch:x} earlier
if (action.hasInput) {
$input.attr('placeholder', action.inputPh || '');
if (typeof(action.inputVal) !== 'undefined') $input.val(action.inputVal);
}
// add preloaded values
if (action.checked) $chkbox.attr('checked', 'CHECKED');
$action.append($chkbox).append($label).append($input);
$form.append($action);
});
$form.append('<br style="clear:both">');
$form.append($btnDo).append($btnCancel);
$form.append($bar);
var limitReport = getLimitReport();
if (limitReport) {
// ensure that our widget is filled
// (will not be added by class, cuz still doesn't exist in the doc)
$widgetNewpp.attr('title', limitReport);
$widgetNewpp.appendTo($bar);
// set data via class in order to allow external widgets
$('.ucp-widget-newpp').attr('title', limitReport);
}
// some adjustments
// set colors
$form.css({
'background-color': cfg.mwc.wgSassParams['color-page'] || 'silver',
color: cfg.mwc.wgSassParams['color-links'] || 'blue',
});
// add text click handlers
$form.find('input[type="text"]').click(txt_click);
return $form;
}// createForm
function init(trans) {
cfg.trans = cfg.trans || trans || {};
var $btnShowForm = $('<li>', {
class: 'ucp-btn-showform',
text: cfg.trans.btnshowformtext || 'ucp',
title: cfg.trans.btnshowformtitle || 'unified control panel',
}),
actions = $.isArray(cfg.actions) ? cfg.actions : [
{
id: 'raw',
action: 'action=raw',
caption: 'action=raw',
title: 'Show raw content of the article',
},
{
id: 'render',
action: 'action=render',
caption: 'action=render',
title: 'Show rendered article content',
},
{
id: 'credits',
action: 'action=credits',
caption: 'action=credits',
title: 'Shows a list of people who contributed to the page',
},
{
id: 'history',
action: 'action=history',
caption: 'action=history',
title: 'Shows history of the page',
},
{
id: 'purge',
action: 'action=purge',
caption: 'action=purge',
title: 'Don\'t use it',
},
{
id: 'debug',
action: 'debug=1',
caption: 'Debug mode',
title: 'It\'s not the test mode',
},
{
id: 'safemode',
action: 'safemode=1',
caption: 'Safe mode',
title: 'Turn off all js/css',
},
{
id: 'usesitejs',
action: 'usesitejs',
caption: 'Turn off sitewide js',
title: 'Turn the shit off',
},
{
id: 'usesitecss',
action: 'usesitecss',
caption: 'Turn off sitewide css',
title: 'Turn the shit off',
},
{
id: 'useuserjs',
action: 'useuserjs',
caption: 'Turn off user js',
title: 'Turn the shit off',
},
{
id: 'useusercss',
action: 'useusercss',
caption: 'Turn off user css',
title: 'Turn the shit off',
},
{
id: 'uselang',
action: 'uselang',
caption: 'uselang=lang',
title: 'Use chosen lang',
hasInput: !0,
inputSize: 6,
inputPh: 'lang',
inputVal: 'qqx',
},
{
id: 'useskin',
action: 'useskin',
caption: 'useskin=skin',
title: 'Use chosen skin',
hasInput: !0,
inputSize: 7,
inputPh: 'skin',
inputVal: 'fandommobile',
},
{
id: 'diff',
action: 'diff',
caption: 'Show diff',
title: 'Use the text field to set diff params',
hasInput: !0,
inputSize: 10,
inputPh: 'id&oldid=oldid',
},
];
actions = actions.concat(cfg.moreactions || []).filter(Boolean);
updateAction(preload(actions), actions);
log('init.actions', actions);
// cfg.actions = actions;
$('#WikiaBar .tools .ucp-btn-showform').remove();
$btnShowForm.click(btnShowForm_click);
$('#WikiaBar .tools:first').append($btnShowForm);
if (cfg.form) cfg.form.detach();
cfg.form = createForm(actions);
cfg.refreshLimitReport = refreshLimitReport;
}// init
function i18nLoad() {
if (cfg.i18nloaded) return;
cfg.i18nloaded = !0;
mw.hook('dev.i18nbeta').add(function(i18n) {
cfg.i18n = i18n;
i18n.loadMessages('UnifiedControlPanel').done(function(msgs) {
cfg.trans = i18n._getMsgs(msgs._messages,
['btnshowformtext', 'btnshowformtitle', 'btndotext', 'btndotitle', 'btncanceltext', 'btncanceltitle'],
cfg.mwc.wgUserLanguage);
cfg.i18nMessages = msgs;
cfg.i18nreallyloaded = !0;
init();
});
});
if (!window.dev || !window.dev.i18nbeta) importArticle({type: 'script', article: 'u:dev:MediaWiki:I18n-js/beta.js'});
}// i18nLoad
cfg.init = init;// backup way; just in case
i18nLoad();
// if i18n wasn't loaded for 10s, then fuck it
setTimeout(function() {
if (!cfg.i18nreallyloaded) init();
}, 10000);
}((window.fng = window.fng || {}).ucp = window.fng.ucp || {});