MediaWiki:Gadget-PatrollingEnhancements.js: Difference between revisions

Created page with "var 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 ve..."
 
m 1 revision imported
 
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
var GPE = {};
// {{documentation}}


/* </pre>
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
==Configuration options==
var newNode = window.newNode = function newNode(tagname) {
<pre> */
var node = document.createElement(tagname);


// The initial value to put in the "deletion reason" text-field; you can
for (var i = 1; i < arguments.length; i++) {
// override this in your common.js (or vector.js or whatnot).
var argument = arguments[i];
GPE.initialDeleteReason = '';
if (typeof argument == 'string') { //Text
 
node.appendChild(document.createTextNode(argument));
// The value to use as a deletion reason if you leave the text-field blank; you
} else if (typeof argument == 'object') {
// can override it in your common.js (or vector.js or whatnot). If you *don't*
if (argument instanceof Node) { // If it is a DOM Node
// override this, then MediaWiki will generate an automatic deletion reason that
node.appendChild(argument);
// indicates the entry's last editor and the beginning of its content.
} else { // Attributes (hopefully)
GPE.deleteReasonIfBlank = '';
for (var j in argument) {
 
if (j === 'class') { // Classname different because...
// By DCDuring's request. If you set this to true, then Special:Watchlist will
node.className = argument[j];
// show the deletion-reason text-input, but *not* the deletion-reason dropdown,
} else if (j == 'style') { // Style is special
// when there's an unpatrolled new-page-creation.
node.style.cssText = argument[j];
GPE.hideDeleteReasonDropdownOnWatchlist = false;
} else if (typeof argument[j] == 'function') { // Basic event handlers
 
node.addEventListener(j, argument[j], false);
/* </pre>
} else {
==Automated patrolling (whitelisting)==
node.setAttribute(j, argument[j]); //Normal attributes
<pre> */
}
 
}
// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
}
(
}
  function ()
}
  {
    var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
return node;
                      '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)
(function PatrollingEnhancements_IIFE() {
{
window.GPE = typeof window.GPE == 'object' ? window.GPE : {};
  button.onclick = null;
  button.title = (hoverText || '');
const GPE = window.GPE;
  button.innerHTML = text;
  // clear out explicit styling and disable, so we can get appropriate
/* </pre>
  // disabled-button styles:
==Configuration options==
  button.style.background = '';
<pre> */
  button.style.color = '';
  button.style.cursor = '';
// The initial value to put in the "deletion reason" text-field; you can
  button.disabled = 'disabled';
// override this in your common.js (or vector.js or whatnot).
  // explicitly style like a disabled button:
GPE.initialDeleteReason = GPE.initialDeleteReason == undefined ? '' : GPE.initialDeleteReason;
  var computedStyle = window.getComputedStyle(button);
  button.style.background = computedStyle.backgroundColor;
// The value to use as a deletion reason if you leave the text-field blank; you
  button.style.color = computedStyle.color;
// can override it in your common.js (or vector.js or whatnot). If you *don't*
  button.style.cursor = computedStyle.cursor;
// override this, then MediaWiki will generate an automatic deletion reason that
  // re-enable, so the title will show up as hover-text in Firefox
// indicates the entry's last editor and the beginning of its content.
  // (see https://bugzilla.mozilla.org/show_bug.cgi?id=274626):
GPE.deleteReasonIfBlank = GPE.deleteReasonIfBlank == undefined ? '' : GPE.deleteReasonIfBlank;
  button.disabled = '';
  // technically the button is "enabled" now, but it *looks* disabled, and it
// By DCDuring's request. If you set this to true, then Special:Watchlist will
  // doesn't have an onclick event, so it's disabled for all normal purposes
// 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;
GPE.anErrorOccurred = function(shortMsg, debugInfo)
{
/* </pre>
  var msg, link;
==Automated patrolling (whitelisting)==
  if(debugInfo)
<pre> */
  {
    link = newNode('a', {href:'#'}, 'here');
// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
    msg =
(
      newNode
  function ()
      (
  {
        'p',
    var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
        'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
                      'July', 'August', 'September', 'October', 'November',
        newNode('tt', shortMsg),
                      'December' ];
        ' \u2014 click ',
        link,
    var now = new Date();
        ' for debugging information.'
      );
    var currYear = now.getFullYear();
  }
    var currMonthNum = now.getMonth();
  else
    msg =
    GPE.currMonth = currYear + '/' + monthNames[currMonthNum];
      newNode
      (
    var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear);
        'p',
    var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1);
        'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
        newNode('tt', shortMsg)
    GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum];
      );
  }
  var bodyContent = $('div#bodyContent');
)();
  bodyContent.insertBefore(msg, bodyContent.firstChild);
  if(link)
GPE.individualWhiteListedPages =
    link.onclick = function () { alert(debugInfo); };
{
  if(mediaWiki.config.get('wgUserName') == 'Ruakh')
  "Wiktionary:Requests for cleanup": true,
    alert(shortMsg + '\n' + debugInfo);
  "Wiktionary:Requests for verification": true,
};
  "Wiktionary:Requests for deletion": true,
 
  "Wiktionary:Requests for deletion/Others": true,
/* </pre>
  "Wiktionary:Requests for moves, mergers and splits": true,
==Individual patrol-buttons==
  "Wiktionary:Information desk": true,
<pre> */
  "Wiktionary:Tea room": true,
 
  "Wiktionary:Etymology scriptorium": true,
GPE.addPatrolButton = function(link, rcid)
  "Wiktionary:Requested entries (English)": true,
{
  "Wiktionary:Requested entries (Spanish)": true,
  if(link.className.search(/(?:^|\s)gpe-hasPatrolButton(?:\s|$)/) > -1)
  "Wiktionary:List of protologisms": true,
    return;
  "Wiktionary:Translation requests": true,
  link.className = (link.className + ' gpe-hasPatrolButton').trim();
  "Wiktionary:Feedback": true,
 
  "Wiktionary:Sandbox": true,
  var button = GPE.newButton('M', '#009', 'click to mark as patrolled');
  "Wiktionary talk:Sandbox": true,
  link.parentNode.insertBefore(button, link.nextSibling);
  "Wiktionary:Tutorial (Editing)/sandbox": true,
  link.parentNode.insertBefore(document.createTextNode(' · '), button);
  "Wiktionary:Featured word candidates": true,
  button.onclick =
  "Wiktionary:Word of the day/Nominations": true
    function ()
};
    {
      var token = mediaWiki.user.tokens.get('patrolToken');
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] =
      $.post
  GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] =
      (
  GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] =
        '/w/api.php?format=json&action=patrol',
  GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] =
        { token: token, rcid: rcid },
  true;
        function (data)
        {
// per-user white-listed sub-pages (for example, edits by user Foo
          if(data.patrol)
// to User:Foo/vector.js should be autopatrolled):
            GPE.disableButton(button, 'm', 'marked as patrolled');
GPE.perUserWhiteListedSubPages =
          else if(data.error)
{
          {
  "/Sandbox": true,
            var msg = data.error.code + ': ' + data.error.info;
  "/sandbox": true,
            if(data.error.code == 'badtoken')
  "/chick.js": true,
              msg += ': "' + token + '"';
  "/chick.css": true,
            alert(msg);
  "/standard.js": true,
          }
  "/standard.css": true,
        },
  "/cologneblue.js": true,
        'json'
  "/cologneblue.css": true,
      );
  "/modern.js": true,
    };
  "/modern.css": true,
  if(GPE.shouldAutoPatrol(link))
  "/myskin.js": true,
    button.click();
  "/myskin.css": true,
 
  "/nostalgia.js": true,
  // remove the exclamation point:
  "/nostalgia.css": true,
  var tmp = link;
  "/simple.js": true,
  while(tmp && tmp.nodeName.toUpperCase() !== 'LI')
  "/simple.css": true,
    tmp = tmp.parentNode;
  "/vector.js": true,
  if(tmp)
  "/vector.css": true,
    tmp = tmp.getElementsByClassName('unpatrolled')[0];
  "/common.js": true,
  if(tmp)
  "/common.css": true
    tmp.parentNode.removeChild(tmp);
};
};
 
GPE.individualWhiteListedContributors =
/* </pre>
{
==Individual delete-buttons==
};
<pre> */
 
GPE.shouldAutoPatrol = function(link)
GPE.addDeleteButton = function(link, title)
{
{
  var pagename = link.title;
  if(link.className.search(/(?:^|\s)gpe-hasDeleteButton(?:\s|$)/) > -1)
  if(pagename.indexOf('User talk:') == 0)
    return;
    return true;
  link.className = (link.className + ' gpe-hasDeleteButton').trim();
  if(pagename in GPE.individualWhiteListedPages)
 
    return true;
  var button = GPE.newButton('D', '#900', 'click to delete');
  link.parentNode.insertBefore(button, link.nextSibling);
  var contributor;
  link.parentNode.insertBefore(document.createTextNode(' · '), button);
  if(mediaWiki.config.get('wgCanonicalSpecialPageName') === 'Contributions')
  button.onclick =
  {
    function ()
    contributor =
    {
      document.getElementById('t-contributions')
      var dropdownReason =
              .getElementsByTagName('a')[0].href.replace(/.*\//, '');
        document.getElementById('deleteReasonsDropdown')
  }
          ? document.getElementById('deleteReasonsDropdown').value
  else
          : '';
  {
      if(dropdownReason == 'other')
    var li = link.parentNode;
        dropdownReason = '';
    if(li.tagName.toUpperCase() == 'SPAN')
      var textInputReason =
      li = li.parentNode;
        document.getElementById('deleteReasonTextInput').value;
    var links = li.getElementsByTagName('a');
      var reason;
    for(var i = 0; i < links.length; ++i)
      if(dropdownReason.length && textInputReason.length)
      if(links[i].title.indexOf('Special:Contributions/') == 0)
        reason = dropdownReason + ': ' + textInputReason;
      {
      else if(dropdownReason.length || textInputReason.length)
        contributor = links[i].title.substr('Special:Contributions/'.length);
        reason = dropdownReason + textInputReason;
        break;
      else
      }
        reason = GPE.deleteReasonIfBlank;
  }
      var token = mediaWiki.user.tokens.get('deleteToken');
  if(pagename.indexOf('User:' + contributor + '/') == 0)
      $.post
    if(pagename.substr(contributor.length + 5) in GPE.perUserWhiteListedSubPages)
      (
      return true;
        '/w/api.php?format=json&action=delete',
  if(contributor in GPE.individualWhiteListedContributors)
        { title: title, token: token, reason: reason },
    return true;
        function (data)
        {
  return false;
          if(data['delete'])
};
            GPE.disableButton(button, 'd', 'deleted');
          else if(data.error)
/* </pre>
          {
            var msg = data.error.code + ': ' + data.error.info;
==Utility functions==
            if(data.error.code == 'badtoken')
<pre> */
              msg += ': "' + token + '"';
            alert(msg);
GPE.newButton = function(text, color, hoverText)
          }
{
        },
  var button = newNode('button', text);
        'json'
  button.style.background = color;
      );
  button.style.color = '#FFF';
    };
  button.style.border = '0';
};
  button.style.padding = '0';
 
  button.style.cursor = 'pointer';
/* </pre>
  button.title = hoverText;
==Delete-reasons==
  return button;
<pre> */
};
 
GPE.addDeleteReasonInput = function ()
GPE.disableButton = function(button, text, hoverText)
{ var deleteReasonDiv =
{
    ( newNode
  button.onclick = null;
      ( 'div',
  button.title = (hoverText || '');
        { style:
  button.innerHTML = text;
            'background:#900; color:#FFF; ' +
  // clear out explicit styling and disable, so we can get appropriate
            'position:fixed; bottom:0; right:0; margin-bottom:0'
  // disabled-button styles:
        },
  button.style.background = '';
        '\u00A0Deletion reason:\u00A0'
  button.style.color = '';
      )
  button.style.cursor = '';
    );
  button.disabled = 'disabled';
  deleteReasonDiv.title =
};
    'the deletion reason (message/summary) to use when you click "D"';
  var deleteReasonTextInput =
/* </pre>
  ( newNode
==Individual patrol-buttons==
    ( 'input',
<pre> */
      { type: 'text',
        size: 80,
GPE.addPatrolButton = function(link, rcid)
        id: 'deleteReasonTextInput',
{
        value: GPE.initialDeleteReason,
  if(link.className.search(/(?:^|\s)gpe-hasPatrolButton(?:\s|$)/) > -1)
        style: 'position:fixed; right:0; margin-bottom:0'
    return;
      }
  link.className = (link.className + ' gpe-hasPatrolButton').trim();
    )
  );
  var button = GPE.newButton('M', '#009', 'click to mark as patrolled');
  deleteReasonDiv.appendChild(deleteReasonTextInput);
  link.parentNode.insertBefore(button, link.nextSibling);
  document.getElementById('bodyContent').appendChild(deleteReasonDiv);
  link.parentNode.insertBefore(document.createTextNode(' · '), button);
 
  button.onclick =
  if(GPE.hideDeleteReasonDropdownOnWatchlist)
    function ()
    if(mediaWiki.config.get('wgPageName') == 'Special:Watchlist')
    {
      return;
      var token = mediaWiki.user.tokens.get('patrolToken');
 
      $.post
  // get canned messages from [[MediaWiki:Deletereason-dropdown]]:
      (
  $.getJSON
        '/w/api.php?format=json&action=patrol&assert=user',
  ( '/w/api.php?format=json&action=query&meta=allmessages&ammessages=Deletereason-dropdown',
        { token: token, rcid: rcid },
    function (data)
        function (data)
    { var rawDeleteReasons = data.query.allmessages[0]['*'];
        {
      var deleteReasonsDropdown =
          if(data.patrol)
        newNode('select',
            GPE.disableButton(button, 'm', 'marked as patrolled');
          { id: 'deleteReasonsDropdown', style: 'vertical-align: bottom' });
          else if(data.error)
      deleteReasonsDropdown.appendChild
          {
        (newNode('option', { value: 'other' }, 'Other reason'));
            var msg = data.error.code + ': ' + data.error.info;
      var optGroup = deleteReasonsDropdown;
            if(data.error.code == 'badtoken')
      rawDeleteReasons.replace
              msg += ': "' + token + '"';
      ( /^(\*\*?) *(.+)$/gm,
            alert(msg);
        function (s, asterisks, text)
          }
        { if(asterisks == '*')
        },
            deleteReasonsDropdown.appendChild
        'json'
              (optGroup = newNode('optgroup', { label: text }));
      );
          else // '**'
    };
            optGroup.appendChild(newNode('option', { value: text }, text));
  if(GPE.shouldAutoPatrol(link))
        }
    button.click();
      );
      deleteReasonDiv.insertBefore(deleteReasonsDropdown, deleteReasonTextInput);
  // remove the exclamation point:
      deleteReasonDiv.insertBefore(newNode('br'), deleteReasonTextInput);
  var tmp = link;
      deleteReasonDiv.insertBefore(document.createTextNode('\u00A0'), deleteReasonTextInput);
  while(tmp && tmp.nodeName.toUpperCase() !== 'LI')
    }
    tmp = tmp.parentNode;
  );
  if(tmp)
};
    tmp = tmp.getElementsByClassName('unpatrolled')[0];
 
  if(tmp)
/* </pre>
    tmp.parentNode.removeChild(tmp);
==Namespaces==
};
<pre> */
 
/* </pre>
GPE.computeNamespaces = function
==Individual delete-buttons==
  (selected, includeAssociated, invertSelection)
<pre> */
{
  var associated = Number(selected) + (selected % 2 === 0 ? 1 : -1);
GPE.addDeleteButton = function(link, title)
  if(invertSelection)
{
  {
  if(link.className.search(/(?:^|\s)gpe-hasDeleteButton(?:\s|$)/) > -1)
    var selector = document.getElementById('namespace');
    return;
    if(! selector)
  link.className = (link.className + ' gpe-hasDeleteButton').trim();
      return [];
    var ret = [];
  var button = GPE.newButton('D', '#900', 'click to delete');
    for(var option = selector.firstChild; option; option = option.nextSibling)
  link.parentNode.insertBefore(button, link.nextSibling);
      if(option.nodeName.toUpperCase() === 'OPTION' && option.value)
  link.parentNode.insertBefore(document.createTextNode(' · '), button);
        if(option.value != selected)
  button.onclick =
          if(! includeAssociated || option.value != associated)
    function ()
            ret.push(option.value);
    {
    return ret;
      var dropdownReason =
  }
        document.getElementById('deleteReasonsDropdown')
  else
          ? document.getElementById('deleteReasonsDropdown').value
  {
          : '';
    if(includeAssociated)
      if(dropdownReason == 'other')
      return [selected, associated];
        dropdownReason = '';
    else
      var textInputReason =
      return [selected];
        document.getElementById('deleteReasonTextInput').value;
  }
      var reason;
};
      if(dropdownReason.length && textInputReason.length)
 
        reason = dropdownReason + ': ' + textInputReason;
GPE.generateRcnamespace = function ()
      else if(dropdownReason.length || textInputReason.length)
{
        reason = dropdownReason + textInputReason;
  var currUrl = document.location.href;
      else
  if(! /[?&]namespace=\d+(?:&|$)/.test(currUrl))
        reason = GPE.deleteReasonIfBlank;
    return;
      var token = mediaWiki.user.tokens.get('deleteToken');
  var selected = /[?&]namespace=(\d+)(?:&|$)/.exec(currUrl)[1];
      $.post
  var includeAssociated =
      (
    mediaWiki.config.get('wgPageName') !== 'Special:NewPages'
        '/w/api.php?format=json&action=delete&assert=user',
    && /[?&]associated=(?!0?&|0?$)/.test(currUrl);
        { title: title, token: token, reason: reason },
  var invertSelection = /[?&]invert=(?!0?&|0?$)/.test(currUrl);
        function (data)
  var namespaces =
        {
    GPE.computeNamespaces(selected, includeAssociated, invertSelection);
          if(data['delete'])
  if(namespaces.length > 0)
            GPE.disableButton(button, 'd', 'deleted');
    return namespaces.join('|');
          else if(data.error)
};
          {
 
            var msg = data.error.code + ': ' + data.error.info;
/* </pre>
            if(data.error.code == 'badtoken')
==Find and handle links==
              msg += ': "' + token + '"';
<pre> */
            alert(msg);
 
          }
GPE.handleUnpatrolledEdits = function (rcidsByRevid)
        },
{
        'json'
  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);
/* </pre>
    if(mapKey && rcidsByRevid.hasOwnProperty(mapKey[2]))
==Delete-reasons==
      GPE.addPatrolButton(links[i], rcidsByRevid[mapKey[2]]);
<pre> */
  }
};
GPE.addDeleteReasonInput = function ()
 
{ var deleteReasonDiv =
GPE.findLinksToUnpatrolledNewPages = function (rcidsByTitle)
    ( newNode
{
      ( 'div',
  if(mediaWiki.config.get('wgPageName') === 'Special:NewPages')
        { style:
  {
            'background:#900; color:#FFF; ' +
    var ret = [];
            'position:fixed; bottom:0; right:0; margin-bottom:0'
    $('li.not-patrolled a.mw-newpages-pagename').each(function () { ret.push(this) });
        },
    return ret;
        '\u00A0Deletion reason:\u00A0'
  }
      )
  else
    );
  {
  deleteReasonDiv.title =
    var ret = [];
    'the deletion reason (message/summary) to use when you click "D"';
    var abbrs =
  var deleteReasonTextInput =
      document.getElementById('bodyContent').getElementsByTagName('abbr');
  ( newNode
    for(var i = abbrs.length - 1; i >= 0; --i)
    ( 'input',
    {
      { type: 'text',
      if(abbrs[i].className != 'newpage')
        size: 80,
        continue;
        id: 'deleteReasonTextInput',
      var link = abbrs[i];
        value: GPE.initialDeleteReason,
      while(link && link.nodeName.toUpperCase() != 'A')
        style: 'position:fixed; right:0; margin-bottom:0'
        if(link.nodeName.toUpperCase() === 'SPAN' && link.className === 'mw-title')
      }
          link = link.firstChild;
    )
        else
  );
          link = link.nextSibling;
  deleteReasonDiv.appendChild(deleteReasonTextInput);
      if(link && link.title && rcidsByTitle.hasOwnProperty(link.title))
  document.getElementById('bodyContent').appendChild(deleteReasonDiv);
        ret.push(link);
    }
  if(GPE.hideDeleteReasonDropdownOnWatchlist)
    return ret;
    if(mediaWiki.config.get('wgPageName') == 'Special:Watchlist')
  }
      return;
};
 
  // get canned messages from [[MediaWiki:Deletereason-dropdown]]:
GPE.handleUnpatrolledNewPages = function (rcidsByTitle)
  $.getJSON
{
  ( '/w/api.php?format=json&action=query&meta=allmessages&ammessages=Deletereason-dropdown',
  var userIsSysop =
    function (data)
    mediaWiki.config.get('wgUserGroups').indexOf('sysop') > -1;
    { var rawDeleteReasons = data.query.allmessages[0]['*'];
  var links = GPE.findLinksToUnpatrolledNewPages(rcidsByTitle);
      var deleteReasonsDropdown =
  for(var i = links.length - 1; i >= 0; --i)
        newNode('select',
  {
          { id: 'deleteReasonsDropdown', style: 'vertical-align: bottom' });
    var link = links[i];
      deleteReasonsDropdown.appendChild
    if(userIsSysop)
        (newNode('option', { value: 'other' }, 'Other reason'));
      GPE.addDeleteButton(link, link.title);
      var optGroup = deleteReasonsDropdown;
    GPE.addPatrolButton(link, rcidsByTitle[link.title]);
      rawDeleteReasons.replace
  }
      ( /^(\*\*?) *(.+)$/gm,
  if(userIsSysop && links.length > 0)
        function (s, asterisks, text)
  {
        { if(asterisks == '*')
    GPE.getAndStoreDeleteToken();
            deleteReasonsDropdown.appendChild
    GPE.addDeleteReasonInput();
              (optGroup = newNode('optgroup', { label: text }));
  }
          else // '**'
};
            optGroup.appendChild(newNode('option', { value: text }, text));
 
        }
GPE.getAndStoreDeleteToken = function ()
      );
{
      deleteReasonDiv.insertBefore(deleteReasonsDropdown, deleteReasonTextInput);
  $.getJSON
      deleteReasonDiv.insertBefore(newNode('br'), deleteReasonTextInput);
  (
      deleteReasonDiv.insertBefore(document.createTextNode('\u00A0'), deleteReasonTextInput);
    '/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)
/* </pre>
        return;
==Namespaces==
      mediaWiki.user.tokens.set('deleteToken', token);
<pre> */
    }
  );
GPE.computeNamespaces = function
};
  (selected, includeAssociated, invertSelection)
 
{
GPE.main = function (params)
  var associated = Number(selected) + (selected % 2 === 0 ? 1 : -1);
{
  if(invertSelection)
  var url =
  {
    '/w/api.php?format=json&action=query&list=recentchanges' +
    var selector = document.getElementById('namespace');
    '&rcprop=ids|title' +
    if(! selector)
    '&rcshow=!patrolled' + (params.hasOwnProperty('rcshow') ? '|' + params.rcshow : '') +
      return [];
    '&rclimit=' + (params.hasOwnProperty('rclimit') ? params.rclimit : 500) +
    var ret = [];
    '&rctype=' + (params.hasOwnProperty('rctype') ? params.rctype : 'edit|new') +
    for(var option = selector.firstChild; option; option = option.nextSibling)
    (params.hasOwnProperty('rcnamespace') ? '&rcnamespace=' + params.rcnamespace : '');
      if(option.nodeName.toUpperCase() === 'OPTION' && option.value)
  if(params.hasOwnProperty('rcuser'))
        if(option.value != selected)
    url = url + '&rcuser=' + params.rcuser;
          if(! includeAssociated || option.value != associated)
  $.getJSON
            ret.push(option.value);
  (
    return ret;
    url,
  }
    function (data)
  else
    {
  {
      data = data.query.recentchanges;
    if(includeAssociated)
      var rcidsByRevid = {}; // for unpatrolled edits
      return [selected, associated];
      var rcidsByTitle = {}; // for unpatrolled new pages
    else
      for(var i = 0; i < data.length; ++i)
      return [selected];
        if(data[i].type == 'edit')
  }
          rcidsByRevid[data[i].revid] = data[i].rcid;
};
        else
          rcidsByTitle[data[i].title] = data[i].rcid;
GPE.generateRcnamespace = function ()
 
{
      GPE.handleUnpatrolledEdits(rcidsByRevid);
  var currUrl = document.location.href;
      GPE.handleUnpatrolledNewPages(rcidsByTitle);
  if(! /[?&]namespace=\d+(?:&|$)/.test(currUrl))
    }
    return;
  );
  var selected = /[?&]namespace=(\d+)(?:&|$)/.exec(currUrl)[1];
};
  var includeAssociated =
 
    mediaWiki.config.get('wgPageName') !== 'Special:NewPages'
/* </pre>
    && /[?&]associated=(?!0?&|0?$)/.test(currUrl);
==Onload-hooks==
  var invertSelection = /[?&]invert=(?!0?&|0?$)/.test(currUrl);
<pre> */
  var namespaces =
 
    GPE.computeNamespaces(selected, includeAssociated, invertSelection);
$( document ).ready
  if(namespaces.length > 0)
( function ()
    return namespaces.join('|');
  {
};
    if(mediaWiki.config.get('wgPageName') === 'Special:RecentChanges') {
      var currUrl = document.location.href;
/* </pre>
      var params = {};
==Find and handle links==
      var rcshow = [];
<pre> */
      if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
        rcshow.push('anon');
GPE.handleUnpatrolledEdits = function (rcidsByRevid)
      else if(currUrl.search(/[?&]hideanons=(?!0?$|0?&)/) > -1)
{
        rcshow.push('!anon');
  var links =
      if(document.getElementsByClassName('minoredit').length === 0)
    document.getElementById('bodyContent').getElementsByTagName('a');
        rcshow.push('!minor');
  for(var i = links.length - 1; i >= 0; --i)
      if(rcshow.length > 0)
  {
        params.rcshow = rcshow.join('|');
    var mapKey = /&diff=(prev&oldid=)?(\d+)(&|$)/.exec(links[i].href);
      var rcnamespace = GPE.generateRcnamespace();
    if(mapKey && rcidsByRevid.hasOwnProperty(mapKey[2]))
      if(rcnamespace)
      GPE.addPatrolButton(links[i], rcidsByRevid[mapKey[2]]);
        params.rcnamespace = rcnamespace;
  }
      GPE.main(params);
};
    } else if(mediaWiki.config.get('wgPageName') === 'Special:NewPages') {
      var currUrl = document.location.href;
GPE.findLinksToUnpatrolledNewPages = function (rcidsByTitle)
      var params = { rctype: 'new' };
{
      var rcshow = [];
  if(mediaWiki.config.get('wgPageName') === 'Special:NewPages')
      if(currUrl.search(/[?&]hideliu=(?!0?$|0?&)/) > -1)
  {
        rcshow.push('anon');
    var ret = [];
      if(currUrl.search(/[?&]hideredirs=0?(?:$|&)/) === -1)
    $('li.not-patrolled a.mw-newpages-pagename').each(function () {
        rcshow.push('!redirect');
      if (this.title && rcidsByTitle.hasOwnProperty(this.title))
      if(rcshow.length > 0)
        ret.push(this);
        params.rcshow = rcshow.join('|');
    });
      var rcnamespace = GPE.generateRcnamespace();
    return ret;
      if(rcnamespace)
  }
        params.rcnamespace = rcnamespace;
  else
      GPE.main(params);
  {
    } else if(mediaWiki.config.get('wgPageName') === 'Special:Watchlist') {
    var ret = [];
      var params = {};
    var abbrs =
      var rcnamespace = GPE.generateRcnamespace();
      document.getElementById('bodyContent').getElementsByTagName('abbr');
      if(rcnamespace)
    for(var i = abbrs.length - 1; i >= 0; --i)
        params.rcnamespace = rcnamespace;
    {
      // TODO is this the best way to find what we need for the watchlist?
      if(abbrs[i].className != 'newpage')
      GPE.main(params);
        continue;
    } else if(mediaWiki.config.get('wgPageName').search(/^Special:Contributions(\/|$)/) === 0)
      var link = abbrs[i];
      GPE.main({ rcuser: document.getElementById('t-contributions').firstChild.href.replace(/^.*?\/Special:Contributions\//, '') });
      while(link && link.nodeName.toUpperCase() != 'A')
    else if(mediaWiki.config.get('wgAction') === 'markpatrolled'
        if(link.nodeName.toUpperCase() === 'SPAN' && link.className === 'mw-title')
        || mediaWiki.config.get('wgAction') === 'delete'
          link = link.firstChild;
        || mediaWiki.config.get('wgAction') === 'rollback')
        else
      GPE.main({ rclimit: 15 });
          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 });
  }
);
})();