User:Gary/reviewer summary.js

/*

REVIEWER SUMMARY

Description: Shows a summary table of reviewers for the nominations on the current page.

TODO Count all nominators by opening nomination.

  • /

if (typeof(ReviewerSummary) == 'undefined') ReviewerSummary = {};

ReviewerSummary.columnSpacePadding = ReviewerSummary.columnSpacePadding || 3;

ReviewerSummary.debug = ReviewerSummary.debug || false;

ReviewerSummary.enableAllPages = ReviewerSummary.enableAllPages || true;

ReviewerSummary.enabledPages = ReviewerSummary.enabledPages ||

[

'User:Gary/Sandbox',

'Wikipedia:Featured article candidates',

'Wikipedia:Featured article candidates/Featured log/',

'Wikipedia:Featured article candidates/Archived nominations/',

'Wikipedia:Featured article review',

'Wikipedia:Featured article review/archive',

'Wikipedia:Featured article review/archive/',

'Wikipedia:Featured list candidates',

'Wikipedia:Featured list candidates/Featured log/',

'Wikipedia:Featured list candidates/Failed log/',

'Wikipedia:Featured list removal candidates',

'Wikipedia:Featured list removal candidates/log/',

'Wikipedia:Featured picture candidates',

'Wikipedia:Featured picture candidates/',

];

ReviewerSummary.useKilobytes = ReviewerSummary.useKilobytes || false;

if (isUnsafe())

{

var console = unsafeWindow.console;

mw = unsafeWindow.mw;

}

function addCommas(nStr)

{

nStr += '';

var x = nStr.split('.');

var x1 = x[0];

var x2 = x.length > 1 ? '.' + x[1] : '';

var rgx = /(\d+)(\d{3})/;

while (rgx.test(x1))

{

x1 = x1.replace(rgx, '$1' + ',' + '$2');

}

return x1 + x2;

}

function formatJSON(obj)

{

if (!obj['query'] || !obj['query']['pages']) return false;

var vars = [];

vars['pages'] = obj['query']['pages'];

vars['page'] = [];

for (var i in vars['pages']) vars['page'].push(i);

if (vars['page'].length != 1) return false;

vars['page'] = obj['query']['pages'][vars['page'][0]];

vars['pageName'] = vars['page']['title'].replace(/ /g, '_');

if (vars['page']['revisions']) vars['firstRevision'] = vars['page']['revisions'][0];

vars['revisions'] = vars['page']['revisions'];

return vars;

}

function isUnsafe()

{

if (typeof(unsafeWindow) != 'undefined') return true;

else return false;

}

reviewerSummary = function()

{

if (mw.util.getParamValue('oldid'))

{

alert('Reviewer Summary cannot be used on this page because it is an old revision, so section edit links do not appear.\n\nPlease view the most recent version of this page to use this script.');

return;

}

var reviewerTable = $('#reviewer-summary');

if (reviewerTable.length)

{

if (reviewerTable.css('display') != 'none')

{

reviewerTable.css('display', 'none');

return

}

else

{

reviewerTable.css('display', 'block');

return;

}

}

var reviewerSummary = $('

');

var debug = $('

');

reviewerSummary.append(debug).append('Reviewer Summary ');

reviewerSummary.append($('').append('(').append($('').append('0')).append(' nominations processed)'));

var notes = $('

  • To copy the table, highlight the text, and then copy and paste it into an edit box. Wrap the text in <pre> tags so that the spacing is retained.
    Wait until it says "all X nominations processed" above before copying the table.
  • Click on table headers to sort.
  • Numbers in brackets indicate the average per article.
  • "Bytes" indicates approximately the total number of characters added/removed.
    A negative amount here is possible, if the user removed more text than they added, such as in the case of performing maintenance work.
');

reviewerSummary.append(notes);

// body

var body = $('

');

var bodyTable = $('

UsernameArticlesEdits' + (ReviewerSummary.useKilobytes ? 'Kilobytes' : 'Bytes') + '
'); // (?)

reviewerSummary.append(body).append(bodyTable).append('
');

var parentNode = $('#bodyContent');

var allNominations = $('h3', parentNode);

allNominations.each(function(i)

{

var page = $('.editsection', $(this)).eq(0).children().eq(0);

var pageName = page.attr('title');

if (!page.length || pageName.indexOf('archive') == -1) return true;

$.get(mw.config.get('wgScriptPath') + '/api.php', { format: 'json', action: 'query', prop: 'revisions', rvdir: 'newer', rvlimit: 500, rvprop: 'ids|flags|timestamp|user|comment|size', titles: pageName }, reviewerSummaryCallback);

});

$('#contentSub').after(reviewerSummary);

}

reviewerSummaryCallback = function(obj)

{

var vars = formatJSON(obj);

if (!vars) return;

var users = {};

var prevRevision, revision;

for (var i = 0; i < vars['revisions'].length; i++)

{

// revision = { 'revid', 'parentid', 'user', 'timestamp', 'comment', 'size' }

if (vars['revisions'][i - 1]) prevRevision = vars['revisions'][i - 1];

else prevRevision = false;

revision = vars['revisions'][i];

if (!revision['user']) continue;

if (users[revision['user']])

{

users[revision['user']]['edits']++;

users[revision['user']]['size'] += (prevRevision ? revision['size'] - prevRevision['size'] : revision['size']);

}

else

{

users[revision['user']] =

{

'articles': [],

'edits': 1,

'size': (prevRevision ? revision['size'] - prevRevision['size'] : revision['size']),

};

}

if (users[revision['user']]['articles'].indexOf(vars['pageName']) == -1) users[revision['user']]['articles'].push(vars['pageName']);

}

// Do not count the nominator's edits to their own nomination.

delete(users[vars['firstRevision']['user']]);

var newRow, username, articles, edits, size, usernameText, editsNumber, sizeNumber, editAverage, sizeAverage, editAverageText, sizeAverageText, newArticles, newEdits, newSize, padding;

for (var user in users)

{

var formattedUsername = user.replace(/ /g, '_');

var oldRow = $('#row-' + formattedUsername);

var numberOfArticles = users[user]['articles'].length;

var numberOfEdits = users[user]['edits'];

var numberOfSize = (ReviewerSummary.useKilobytes ? (users[user]['size'] / 1024).toFixed(0) : users[user]['size']);

var userArticles = users[user]['articles'].join('; ');

for (var i = ReviewerSummary.enabledPages.length - 1; i > 0; i--)

{

var page = ReviewerSummary.enabledPages[i].replace(/ /g, '_');

if (userArticles.indexOf(page) != -1)

{

userArticles = userArticles.replace(page, '').replace(/_/g, ' ');

if (userArticles.substring(0, 1) == '/') userArticles = userArticles.substring(1);

var split = userArticles.split('/');

if (split.length > 1) userArticles = split[0];

break;

}

}

if (oldRow.length)

{

// Update old values

articles = $('#articles-' + formattedUsername).children().first().contents().eq(0);

edits = $('#edits-' + formattedUsername).children().eq(0).contents().eq(0);

size = $('#size-' + formattedUsername).children().eq(0).contents().eq(0);

editAverage = $('#edit-average-' + formattedUsername);

sizeAverage = $('#size-average-' + formattedUsername);

newArticles = parseInt(articles[0].nodeValue) + numberOfArticles;

newEdits = parseInt(edits[0].nodeValue) + numberOfEdits;

newSize = parseInt(size[0].nodeValue) + (ReviewerSummary.useKilobytes ? (users[user]['size'] / 1024).toFixed(0) : users[user]['size']);

articles.parent().attr('title', articles.parent().attr('title') + '; ' + userArticles);

articles[0].nodeValue = addCommas(newArticles);

edits[0].nodeValue = addCommas(newEdits);

size[0].nodeValue = addCommas(newSize);

if (editAverage.contents().length)

{

// Row was already updated at least once before

editAverageText = $(' (' + addCommas((newEdits / newArticles).toFixed(1)) + ')');

editAverage.contents().eq(0).replaceWith(editAverageText);

sizeAverageText = $(' (' + addCommas((newSize / newArticles).toFixed(1)) + ')');

sizeAverage.contents().eq(0).replaceWith(sizeAverageText);

}

else

{

// Row is being updated for the first time

editAverage.append(' (' + addCommas((newEdits / newArticles).toFixed(1)) + ')');

sizeAverage.append(' (' + addCommas((newSize / newArticles).toFixed(1)) + ')');

}

}

else

{

// Create a new row

padding = $('');

username = $('').append('' + user + '').append(padding.clone());

articles = $('' + numberOfArticles + '').append(padding.clone());

edits = $('').append('' + numberOfEdits + '').append('').append(padding.clone());

size = $('').append('' + addCommas(numberOfSize) + '').append('').append(padding.clone());

$('#reviewer-table').parent().append($('').append(username).append(articles).append(edits).append(size));

}

}

// Pad the rows

// padReviewerSummaryRows();

// Update number of nominations processed

var allNominations = $('h3');

var status = $('#rs-status');

var processedNominations = $('#processed-nominations').contents().eq(0);

processedNominations[0].nodeValue = parseInt(processedNominations[0].nodeValue) + 1;

// done processing nominations

if (allNominations.length == processedNominations[0].nodeValue)

{

processedNominations[0].nodeValue = 'all ' + processedNominations[0].nodeValue;

// Sort table

sortReviewerSummary('usernameAsc');

}

}

function padReviewerSummaryRows()

{

var reviewerTable = $('#reviewer-table').parent();

var rows = reviewerTable.children();

var usernameNode, articleNode, editNode, sizeNode;

// Calculate "longest" variables

var longestUsername = 'Username'.length;

var longestArticle = 'Articles'.length;

var longestEdit = 'Edits'.length;

var longestSize = (ReviewerSummary.useKilobytes ? 'Kilobytes' : 'Bytes').length;

var replacePattern = /(<([^>]+)>)/gi;

for (var i = 1; i < rows.length; i++)

{

var username = rows[i].childNodes[0].innerHTML.replace(replacePattern, ).replace(/ /g, );

var article = rows[i].childNodes[1].innerHTML.replace(replacePattern, ).replace(/ /g, );

var edit = rows[i].childNodes[2].innerHTML.replace(replacePattern, ).replace(/ /g, );

var size = rows[i].childNodes[3].innerHTML.replace(replacePattern, ).replace(/ /g, );

if (username.length > longestUsername) longestUsername = username.length;

if (article.length > longestArticle) longestArticle = article.length;

if (edit.length > longestEdit) longestEdit = edit.length;

if (size.length > longestSize) longestSize = size.length;

}

var padding = ReviewerSummary.columnSpacePadding;

longestUsername += padding;

longestArticle += padding;

longestEdit += padding;

longestSize += padding;

// Pad the header (first) row as well

firstHeader = rows[0].childNodes[0];

secondHeader = rows[0].childNodes[1];

thirdHeader = rows[0].childNodes[2];

fourthHeader = rows[0].childNodes[3];

var firstPadding = padReviewerSummary(firstHeader.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestUsername, true);

var secondPadding = padReviewerSummary(secondHeader.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestArticle, true);

var thirdPadding = padReviewerSummary(thirdHeader.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestEdit, true);

var fourthPadding = padReviewerSummary(fourthHeader.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestSize, true);

if (firstHeader.lastChild.firstChild)

{

if (firstHeader.lastChild.firstChild.nodeValue.length < firstPadding.length)

firstHeader.lastChild.firstChild.nodeValue = firstPadding;

}

else

firstHeader.lastChild.appendChild(document.createTextNode(firstPadding));

if (secondHeader.lastChild.firstChild)

{

if (secondHeader.lastChild.firstChild.nodeValue.length < secondPadding.length)

secondHeader.lastChild.firstChild.nodeValue = secondPadding;

}

else

secondHeader.lastChild.appendChild(document.createTextNode(secondPadding));

if (thirdHeader.lastChild.firstChild)

{

if (thirdHeader.lastChild.firstChild.nodeValue.length < thirdPadding.length)

thirdHeader.lastChild.firstChild.nodeValue = thirdPadding;

}

else

thirdHeader.lastChild.appendChild(document.createTextNode(thirdPadding));

if (fourthHeader.lastChild.firstChild)

{

if (fourthHeader.lastChild.firstChild.nodeValue.length < fourthPadding.length)

fourthHeader.lastChild.firstChild.nodeValue = fourthPadding;

}

else

fourthHeader.lastChild.appendChild(document.createTextNode(fourthPadding));

// Pad the remaining rows

for (var i = 1; i < rows.length; i++)

{

usernameNode = rows[i].childNodes[0];

articleNode = rows[i].childNodes[1];

editNode = rows[i].childNodes[2];

sizeNode = rows[i].childNodes[3];

username = padReviewerSummary(usernameNode.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestUsername, true);

article = padReviewerSummary(articleNode.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestArticle, true);

edit = padReviewerSummary(editNode.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestEdit, true);

size = padReviewerSummary(sizeNode.innerHTML.replace(replacePattern, ).replace(/ /g, ), longestSize, true);

if (usernameNode.lastChild.firstChild.nodeValue.length < username.length) usernameNode.lastChild.firstChild.nodeValue = username;

if (articleNode.lastChild.firstChild.nodeValue.length < article.length) articleNode.lastChild.firstChild.nodeValue = article;

if (editNode.lastChild.firstChild.nodeValue.length < edit.length) editNode.lastChild.firstChild.nodeValue = edit;

if (sizeNode.lastChild.firstChild.nodeValue.length < size.length) sizeNode.lastChild.firstChild.nodeValue = size;

}

}

function padReviewerSummary(string, length, paddingOnly)

{

if (string.length >= length)

{

if (paddingOnly) return '';

else return string;

}

var padding = '';

while (string.length < length)

{

string = string + '\u00a0';

padding += '\u00a0';

}

if (paddingOnly) return padding;

else return string;

}

function reviewerSummaryPortletLink()

{

var enabledPages = ReviewerSummary.enabledPages

if (!mw.config.get('wgPageName') || !$('#bodyContent').length) return false;

var pageIsEnabled = false;

for (var i = 0; i < enabledPages.length; i++)

{

if (((mw.config.get('wgPageName').indexOf(enabledPages[i].replace(/ /g, '_')) == 0) && (enabledPages[i].substr(enabledPages[i].length - 1, 1) == '/')) || (mw.config.get('wgPageName') == enabledPages[i].replace(/ /g, '_')))

{

pageIsEnabled = true;

break;

}

}

if (pageIsEnabled && (mw.config.get('wgAction') == 'view' || mw.config.get('wgAction') == 'purge') && !mw.util.getParamValue('oldid'))

{

if (ReviewerSummary.debug) reviewerSummary();

createReviewerSummaryPortletLink();

}

else if (ReviewerSummary.enableAllPages)

createReviewerSummaryPortletLink();

}

function createReviewerSummaryPortletLink()

{

// allowed namespaces

var allowedNamespaces = ['User', 'User_talk', 'Project', 'Project_talk'];

if ($.inArray(mw.config.get('wgCanonicalNamespace'), allowedNamespaces) != -1) return mw.util.addPortletLink('p-tb', 'javascript:reviewerSummary();', 'Reviewer summary', 't-reviewer-summary', 'Show a summary of reviewers');

}

sortReviewerSummary = function(type)

{

// type = username, articles, edits, bytes

var reviewerTable = $('#reviewer-table').parent();

var rows = reviewerTable.children();

var items = [];

rows.each(function(i)

{

if (i == 0) return true;

items.push(

{

username: $(this).children().eq(0).text(),

articles: $(this).children().eq(1).text().replace(/,/g, ''),

edits: $(this).children().eq(2).text().replace(/,/g, ''),

bytes: $(this).children().eq(3).text().replace(/,/g, ''),

row: $(this).clone()

});

});

var username = $('#sort-username');

var articles = $('#sort-articles');

var edits = $('#sort-edits');

var bytes = $('#sort-bytes');

switch (type)

{

case 'usernameAsc':

username.attr('href', "javascript:sortReviewerSummary('usernameDesc');");

items.sort(sortUsername);

break;

case 'articlesAsc':

articles.attr('href', "javascript:sortReviewerSummary('articlesDesc');");

items.sort(sortArticles);

break;

case 'editsAsc':

edits.attr('href', "javascript:sortReviewerSummary('editsDesc');");

items.sort(sortEdits);

break;

case 'bytesAsc':

bytes.attr('href', "javascript:sortReviewerSummary('bytesDesc');");

items.sort(sortBytes);

break;

case 'usernameDesc':

username.attr('href', "javascript:sortReviewerSummary('usernameAsc');");

items.sort(reverseSortUsername);

break;

case 'articlesDesc':

articles.attr('href', "javascript:sortReviewerSummary('articlesAsc');");

items.sort(reverseSortArticles);

break;

case 'editsDesc':

edits.attr('href', "javascript:sortReviewerSummary('editsAsc');");

items.sort(reverseSortEdits);

break;

case 'bytesDesc':

bytes.attr('href', "javascript:sortReviewerSummary('bytesAsc');");

items.sort(reverseSortBytes);

break;

}

// remove old rows, add new ones

for (var i = rows.length - 1; i > 0; i--) rows.eq(i).remove();

for (var i = 0; i < items.length; i++) reviewerTable.append(items[i]['row']);

}

function reverseSortUsername(a, b)

{

return sortUsername(b, a);

}

function reverseSortArticles(a, b)

{

return sortArticles(b, a);

}

function reverseSortEdits(a, b)

{

return sortEdits(b, a);

}

function reverseSortBytes(a, b)

{

return sortBytes(b, a);

}

function sortUsername(a, b)

{

if (a['username'] < b['username']) return -1;

else return 1;

}

function sortArticles(a, b)

{

var aArticles = parseInt(a['articles']);

var bArticles = parseInt(b['articles']);

if (aArticles < bArticles) return -1;

else if (aArticles > bArticles) return 1;

else

{

if (a['username'] < b['username']) return -1;

else return 1;

}

}

function sortEdits(a, b)

{

var aEdits = parseInt(a['edits']);

var bEdits = parseInt(b['edits']);

if (aEdits < bEdits) return -1;

else if (aEdits > bEdits) return 1;

else

{

if (a['username'] < b['username']) return -1;

else return 1;

}

}

function sortBytes(a, b)

{

var aBytes = parseInt(a['bytes']);

var bBytes = parseInt(b['bytes']);

if (aBytes < bBytes) return -1;

else if (aBytes > bBytes) return 1;

else

{

if (a['username'] < b['username']) return -1;

else return 1;

}

}

if (isUnsafe())

{

unsafeWindow.reviewerSummary = reviewerSummary;

unsafeWindow.reviewerSummaryCallback = reviewerSummaryCallback;

unsafeWindow.sortReviewerSummary = sortReviewerSummary;

}

$.when( mw.loader.using( [ 'mediawiki.util' ] ), $.ready ).done(reviewerSummaryPortletLink);