MediaWiki:Gadget-PatrollingEnhancements.js
Jump to navigation
Jump to search
Note: You may have to bypass your browser’s cache to see the changes. In addition, after saving a sitewide CSS file such as MediaWiki:Common.css, it will take 5-10 minutes before the changes take effect, even if you clear your cache.
- Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
- Konqueror and Chrome: click Reload or press F5;
- Opera: clear the cache in Tools → Preferences;
- Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.
- The following documentation is located at MediaWiki:Gadget-PatrollingEnhancements.js/documentation. [edit]
- This script is a part of the
PatrollingEnhancements
gadget (edit definitions)- Description (edit): M · Patrolling enhancements – makes it faster and easier to mark edits as patrolled. on by default
- Useful links: subpage list • links • redirects
See also: Special:Gadgets.
// {{documentation}}
(function PatrollingEnhancements_IIFE() {
window.GPE = typeof window.GPE == 'object' ? window.GPE : {};
const GPE = window.GPE;
/* </pre>
==Configuration options==
<pre> */
// The initial value to put in the "deletion reason" text-field; you can
// override this in your common.js (or vector.js or whatnot).
GPE.initialDeleteReason = GPE.initialDeleteReason == undefined ? '' : GPE.initialDeleteReason;
// The value to use as a deletion reason if you leave the text-field blank; you
// can override it in your common.js (or vector.js or whatnot). If you *don't*
// override this, then MediaWiki will generate an automatic deletion reason that
// indicates the entry's last editor and the beginning of its content.
GPE.deleteReasonIfBlank = GPE.deleteReasonIfBlank == undefined ? '' : GPE.deleteReasonIfBlank;
// By DCDuring's request. If you set this to true, then Special:Watchlist will
// show the deletion-reason text-input, but *not* the deletion-reason dropdown,
// when there's an unpatrolled new-page-creation.
GPE.hideDeleteReasonDropdownOnWatchlist = GPE.hideDeleteReasonDropdownOnWatchlist == undefined ? false : GPE.hideDeleteReasonDropdownOnWatchlist;
/* </pre>
==Automated patrolling (whitelisting)==
<pre> */
// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
(
function ()
{
var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November',
'December' ];
var now = new Date();
var currYear = now.getFullYear();
var currMonthNum = now.getMonth();
GPE.currMonth = currYear + '/' + monthNames[currMonthNum];
var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear);
var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1);
GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum];
}
)();
GPE.individualWhiteListedPages =
{
"Wiktionary:Requests for cleanup": true,
"Wiktionary:Requests for verification": true,
"Wiktionary:Requests for deletion": true,
"Wiktionary:Requests for deletion/Others": true,
"Wiktionary:Requests for moves, mergers and splits": true,
"Wiktionary:Information desk": true,
"Wiktionary:Tea room": true,
"Wiktionary:Etymology scriptorium": true,
"Wiktionary:Requested entries (English)": true,
"Wiktionary:Requested entries (Spanish)": true,
"Wiktionary:List of protologisms": true,
"Wiktionary:Translation requests": true,
"Wiktionary:Feedback": true,
"Wiktionary:Sandbox": true,
"Wiktionary talk:Sandbox": true,
"Wiktionary:Tutorial (Editing)/sandbox": true,
"Wiktionary:Featured word candidates": true,
"Wiktionary:Word of the day/Nominations": true
};
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] =
true;
// per-user white-listed sub-pages (for example, edits by user Foo
// to User:Foo/vector.js should be autopatrolled):
GPE.perUserWhiteListedSubPages =
{
"/Sandbox": true,
"/sandbox": true,
"/chick.js": true,
"/chick.css": true,
"/standard.js": true,
"/standard.css": true,
"/cologneblue.js": true,
"/cologneblue.css": true,
"/modern.js": true,
"/modern.css": true,
"/myskin.js": true,
"/myskin.css": true,
"/nostalgia.js": true,
"/nostalgia.css": true,
"/simple.js": true,
"/simple.css": true,
"/vector.js": true,
"/vector.css": true,
"/common.js": true,
"/common.css": true
};
GPE.individualWhiteListedContributors =
{
};
GPE.shouldAutoPatrol = function(link)
{
var pagename = link.title;
if(pagename.indexOf('User talk:') == 0)
return true;
if(pagename in GPE.individualWhiteListedPages)
return true;
var contributor;
if(mediaWiki.config.get('wgCanonicalSpecialPageName') === 'Contributions')
{
contributor =
document.getElementById('t-contributions')
.getElementsByTagName('a')[0].href.replace(/.*\//, '');
}
else
{
var li = link.parentNode;
if(li.tagName.toUpperCase() == 'SPAN')
li = li.parentNode;
var links = li.getElementsByTagName('a');
for(var i = 0; i < links.length; ++i)
if(links[i].title.indexOf('Special:Contributions/') == 0)
{
contributor = links[i].title.substr('Special:Contributions/'.length);
break;
}
}
if(pagename.indexOf('User:' + contributor + '/') == 0)
if(pagename.substr(contributor.length + 5) in GPE.perUserWhiteListedSubPages)
return true;
if(contributor in GPE.individualWhiteListedContributors)
return true;
return false;
};
/* </pre>
==Utility functions==
<pre> */
GPE.newButton = function(text, color, hoverText)
{
var button = newNode('button', text);
button.style.background = color;
button.style.color = '#FFF';
button.style.border = '0';
button.style.padding = '0';
button.style.cursor = 'pointer';
button.title = hoverText;
return button;
};
GPE.disableButton = function(button, text, hoverText)
{
button.onclick = null;
button.title = (hoverText || '');
button.innerHTML = text;
// clear out explicit styling and disable, so we can get appropriate
// disabled-button styles:
button.style.background = '';
button.style.color = '';
button.style.cursor = '';
button.disabled = 'disabled';
};
/* </pre>
==Individual patrol-buttons==
<pre> */
GPE.addPatrolButton = function(link, rcid)
{
if(link.className.search(/(?:^|\s)gpe-hasPatrolButton(?:\s|$)/) > -1)
return;
link.className = (link.className + ' gpe-hasPatrolButton').trim();
var button = GPE.newButton('M', '#009', 'click to mark as patrolled');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var token = mediaWiki.user.tokens.get('patrolToken');
$.post
(
'/w/api.php?format=json&action=patrol&assert=user',
{ token: token, rcid: rcid },
function (data)
{
if(data.patrol)
GPE.disableButton(button, 'm', 'marked as patrolled');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
if(GPE.shouldAutoPatrol(link))
button.click();
// remove the exclamation point:
var tmp = link;
while(tmp && tmp.nodeName.toUpperCase() !== 'LI')
tmp = tmp.parentNode;
if(tmp)
tmp = tmp.getElementsByClassName('unpatrolled')[0];
if(tmp)
tmp.parentNode.removeChild(tmp);
};
/* </pre>
==Individual delete-buttons==
<pre> */
GPE.addDeleteButton = function(link, title)
{
if(link.className.search(/(?:^|\s)gpe-hasDeleteButton(?:\s|$)/) > -1)
return;
link.className = (link.className + ' gpe-hasDeleteButton').trim();
var button = GPE.newButton('D', '#900', 'click to delete');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var dropdownReason =
document.getElementById('deleteReasonsDropdown')
? document.getElementById('deleteReasonsDropdown').value
: '';
if(dropdownReason == 'other')
dropdownReason = '';
var textInputReason =
document.getElementById('deleteReasonTextInput').value;
var reason;
if(dropdownReason.length && textInputReason.length)
reason = dropdownReason + ': ' + textInputReason;
else if(dropdownReason.length || textInputReason.length)
reason = dropdownReason + textInputReason;
else
reason = GPE.deleteReasonIfBlank;
var token = mediaWiki.user.tokens.get('deleteToken');
$.post
(
'/w/api.php?format=json&action=delete&assert=user',
{ title: title, token: token, reason: reason },
function (data)
{
if(data['delete'])
GPE.disableButton(button, 'd', 'deleted');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
};
/* </pre>
==Delete-reasons==
<pre> */
GPE.addDeleteReasonInput = function ()
{ var deleteReasonDiv =
( newNode
( 'div',
{ style:
'background:#900; color:#FFF; ' +
'position:fixed; bottom:0; right:0; margin-bottom:0'
},
'\u00A0Deletion reason:\u00A0'
)
);
deleteReasonDiv.title =
'the deletion reason (message/summary) to use when you click "D"';
var deleteReasonTextInput =
( newNode
( 'input',
{ type: 'text',
size: 80,
id: 'deleteReasonTextInput',
value: GPE.initialDeleteReason,
style: 'position:fixed; right:0; margin-bottom:0'
}
)
);
deleteReasonDiv.appendChild(deleteReasonTextInput);
document.getElementById('bodyContent').appendChild(deleteReasonDiv);
if(GPE.hideDeleteReasonDropdownOnWatchlist)
if(mediaWiki.config.get('wgPageName') == 'Special:Watchlist')
return;
// get canned messages from [[MediaWiki:Deletereason-dropdown]]:
$.getJSON
( '/w/api.php?format=json&action=query&meta=allmessages&ammessages=Deletereason-dropdown',
function (data)
{ var rawDeleteReasons = data.query.allmessages[0]['*'];
var deleteReasonsDropdown =
newNode('select',
{ id: 'deleteReasonsDropdown', style: 'vertical-align: bottom' });
deleteReasonsDropdown.appendChild
(newNode('option', { value: 'other' }, 'Other reason'));
var optGroup = deleteReasonsDropdown;
rawDeleteReasons.replace
( /^(\*\*?) *(.+)$/gm,
function (s, asterisks, text)
{ if(asterisks == '*')
deleteReasonsDropdown.appendChild
(optGroup = newNode('optgroup', { label: text }));
else // '**'
optGroup.appendChild(newNode('option', { value: text }, text));
}
);
deleteReasonDiv.insertBefore(deleteReasonsDropdown, deleteReasonTextInput);
deleteReasonDiv.insertBefore(newNode('br'), deleteReasonTextInput);
deleteReasonDiv.insertBefore(document.createTextNode('\u00A0'), deleteReasonTextInput);
}
);
};
/* </pre>
==Namespaces==
<pre> */
GPE.computeNamespaces = function
(selected, includeAssociated, invertSelection)
{
var associated = Number(selected) + (selected % 2 === 0 ? 1 : -1);
if(invertSelection)
{
var selector = document.getElementById('namespace');
if(! selector)
return [];
var ret = [];
for(var option = selector.firstChild; option; option = option.nextSibling)
if(option.nodeName.toUpperCase() === 'OPTION' && option.value)
if(option.value != selected)
if(! includeAssociated || option.value != associated)
ret.push(option.value);
return ret;
}
else
{
if(includeAssociated)
return [selected, associated];
else
return [selected];
}
};
GPE.generateRcnamespace = function ()
{
var currUrl = document.location.href;
if(! /[?&]namespace=\d+(?:&|$)/.test(currUrl))
return;
var selected = /[?&]namespace=(\d+)(?:&|$)/.exec(currUrl)[1];
var includeAssociated =
mediaWiki.config.get('wgPageName') !== 'Special:NewPages'
&& /[?&]associated=(?!0?&|0?$)/.test(currUrl);
var invertSelection = /[?&]invert=(?!0?&|0?$)/.test(currUrl);
var namespaces =
GPE.computeNamespaces(selected, includeAssociated, invertSelection);
if(namespaces.length > 0)
return namespaces.join('|');
};
/* </pre>
==Find and handle links==
<pre> */
GPE.handleUnpatrolledEdits = function (rcidsByRevid)
{
var links =
document.getElementById('bodyContent').getElementsByTagName('a');
for(var i = links.length - 1; i >= 0; --i)
{
var mapKey = /&diff=(prev&oldid=)?(\d+)(&|$)/.exec(links[i].href);
if(mapKey && rcidsByRevid.hasOwnProperty(mapKey[2]))
GPE.addPatrolButton(links[i], rcidsByRevid[mapKey[2]]);
}
};
GPE.findLinksToUnpatrolledNewPages = function (rcidsByTitle)
{
if(mediaWiki.config.get('wgPageName') === 'Special:NewPages')
{
var ret = [];
$('li.not-patrolled a.mw-newpages-pagename').each(function () {
if (this.title && rcidsByTitle.hasOwnProperty(this.title))
ret.push(this);
});
return ret;
}
else
{
var ret = [];
var abbrs =
document.getElementById('bodyContent').getElementsByTagName('abbr');
for(var i = abbrs.length - 1; i >= 0; --i)
{
if(abbrs[i].className != 'newpage')
continue;
var link = abbrs[i];
while(link && link.nodeName.toUpperCase() != 'A')
if(link.nodeName.toUpperCase() === 'SPAN' && link.className === 'mw-title')
link = link.firstChild;
else
link = link.nextSibling;
if(link && link.title && rcidsByTitle.hasOwnProperty(link.title))
ret.push(link);
}
return ret;
}
};
GPE.handleUnpatrolledNewPages = function (rcidsByTitle)
{
var userIsSysop =
mediaWiki.config.get('wgUserGroups').indexOf('sysop') > -1;
var links = GPE.findLinksToUnpatrolledNewPages(rcidsByTitle);
for(var i = links.length - 1; i >= 0; --i)
{
var link = links[i];
// 2016-04: Equinox removing red D delete button because it hasn't worked for a year.
// if(userIsSysop)
// GPE.addDeleteButton(link, link.title);
GPE.addPatrolButton(link, rcidsByTitle[link.title]);
}
// Remove the rest of the delete features as above
// if(userIsSysop && links.length > 0)
// {
// GPE.getAndStoreDeleteToken();
// GPE.addDeleteReasonInput();
// }
};
GPE.getAndStoreDeleteToken = function ()
{
$.getJSON
(
'/w/api.php?format=json&action=tokens&type=delete',
function (data)
{
var token = data.tokens.deletetoken;
if(! token || token.search(/^[0-9a-f]{32}\+\\$/) != 0)
return;
mediaWiki.user.tokens.set('deleteToken', token);
}
);
};
GPE.main = function (params)
{
var url =
'/w/api.php?format=json&action=query&list=recentchanges' +
'&rcprop=ids|title' +
'&rcshow=!patrolled' + (params.hasOwnProperty('rcshow') ? '|' + params.rcshow : '') +
'&rclimit=' + (params.hasOwnProperty('rclimit') ? params.rclimit : 500) +
'&rctype=' + (params.hasOwnProperty('rctype') ? params.rctype : 'edit|new') +
(params.hasOwnProperty('rcdir') ? '&rcdir=' + params.rcdir : '') +
(params.hasOwnProperty('rcstart') ? '&rcstart=' + params.rcstart : '') +
(params.hasOwnProperty('rcnamespace') ? '&rcnamespace=' + params.rcnamespace : '') +
(params.hasOwnProperty('rcuser') ? '&rcuser=' + params.rcuser : '');
$.getJSON
(
url,
function (data)
{
data = data.query.recentchanges;
var rcidsByRevid = {}; // for unpatrolled edits
var rcidsByTitle = {}; // for unpatrolled new pages
for(var i = 0; i < data.length; ++i)
if(data[i].type == 'edit')
rcidsByRevid[data[i].revid] = data[i].rcid;
else
rcidsByTitle[data[i].title] = data[i].rcid;
GPE.handleUnpatrolledEdits(rcidsByRevid);
GPE.handleUnpatrolledNewPages(rcidsByTitle);
}
);
};
/* </pre>
==Onload-hooks==
<pre> */
$( document ).ready
( function ()
{
if(mediaWiki.config.get('wgPageName') === 'Special:RecentChanges') {
var currUrl = document.location.href;
var params = {};
var rcshow = [];
if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
rcshow.push('anon');
else if(currUrl.search(/[?&]hideanons=(?!0?$|0?&)/) > -1)
rcshow.push('!anon');
if(document.getElementsByClassName('minoredit').length === 0)
rcshow.push('!minor');
if(rcshow.length > 0)
params.rcshow = rcshow.join('|');
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName') === 'Special:NewPages') {
var currUrl = document.location.href;
var params = { rctype: 'new' };
var rcshow = [];
if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
rcshow.push('anon');
if(currUrl.search(/[?&]hideredirs=0?(?:$|&)/) === -1)
rcshow.push('!redirect');
if(rcshow.length > 0)
params.rcshow = rcshow.join('|');
if(currUrl.search(/[?&]dir=prev(?=$|&)/) > -1) {
params.rcdir = 'newer';
if(currUrl.search(/[?&]offset=\d+(?=$|&)/) > -1)
params.rcstart = currUrl.match(/[?&]offset=(\d+)(?=$|&)/)[1];
}
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName') === 'Special:Watchlist') {
var params = {};
var rcnamespace = GPE.generateRcnamespace();
if(rcnamespace)
params.rcnamespace = rcnamespace;
// TODO is this the best way to find what we need for the watchlist?
GPE.main(params);
} else if(mediaWiki.config.get('wgPageName').search(/^Special:Contributions(\/|$)/) === 0)
GPE.main({ rcuser: document.getElementById('t-contributions').firstChild.href.replace(/^.*?\/Special:Contributions\//, '') });
else if(mediaWiki.config.get('wgAction') === 'markpatrolled'
|| mediaWiki.config.get('wgAction') === 'delete'
|| mediaWiki.config.get('wgAction') === 'rollback')
GPE.main({ rclimit: 15 });
}
);
})();