ميډياويکي:Gadget-PatrollingEnhancements.js

يادښت: د غوره توبونو د خوندي کولو وروسته، خپل د کتنمل (بروزر) ساتل شوې حافظه تازه کړی.

  • فايرفاکس/ سفري: په دې کتنمل کې د Reload د ټکوهلو په وخت د Shift تڼۍ نيولې وساتی، او يا هم Ctrl-F5 يا Ctrl-Rتڼۍ کېښکاږۍ (په Apple Mac کمپيوټر باندې ⌘-R کېښکاږۍ)
  • گووگل کروم: په دې کتنمل کې د Ctrl-Shift-R تڼۍ کېښکاږۍ (د مک لپاره ⌘-Shift-R)
  • انټرنټ اېکسپلورر: په دې کتنمل کې د Refresh د ټکوهلو په وخت کې د Ctrl تڼۍ کېښکاږلې ونيسۍ، او يا هم د Ctrl-F5 تڼۍ کېښکاږۍ
  • اوپرا: په دې کتنمل کې د خپل براوزر ساتل شوې حافظه پدې توگه سپينولی شی Tools→Preferences
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 = '';

// 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 = '';

// 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 = false;

/* </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 =
{
  "16@r": true,
  "176.249.126.12": true,
  "71.66.97.228": true,
  "89.73.179.94": true,
  "202.93.220.202": true
};

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';
  // explicitly style like a disabled button:
  var computedStyle = window.getComputedStyle(button);
  button.style.background = computedStyle.backgroundColor;
  button.style.color = computedStyle.color;
  button.style.cursor = computedStyle.cursor;
  // re-enable, so the title will show up as hover-text in Firefox
  // (see https://bugzilla.mozilla.org/show_bug.cgi?id=274626):
  button.disabled = '';
  // technically the button is "enabled" now, but it *looks* disabled, and it
  // doesn't have an onclick event, so it's disabled for all normal purposes
};

GPE.anErrorOccurred = function(shortMsg, debugInfo)
{
  var msg, link;
  if(debugInfo)
  {
    link = newNode('a', {href:'#'}, 'here');
    msg =
      newNode
      (
        'p',
        'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
        newNode('tt', shortMsg),
        ' \u2014 click ',
        link,
        ' for debugging information.'
      );
  }
  else
    msg =
      newNode
      (
        'p',
        'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
        newNode('tt', shortMsg)
      );
  var bodyContent = $('div#bodyContent');
  bodyContent.insertBefore(msg, bodyContent.firstChild);
  if(link)
    link.onclick = function () { alert(debugInfo); };
  if(mediaWiki.config.get('wgUserName') == 'Ruakh')
    alert(shortMsg + '\n' + debugInfo);
};

/* </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 () { 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];
    if(userIsSysop)
      GPE.addDeleteButton(link, link.title);
    GPE.addPatrolButton(link, rcidsByTitle[link.title]);
  }
  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('rcnamespace') ? '&rcnamespace=' + params.rcnamespace : '');
  if(params.hasOwnProperty('rcuser'))
    url = url + '&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('|');
      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 });
  }
);