:User:Lupin/editcount.js

/* This script relies on query.php which is no longer available */

(function () {

return;

/**

* A javascript edit counter, using query.php as the backend

*

* Usage instructions for popups users: add

*

{{subst:js|User:Lupin/editcount.js}}

popupEditCounterTool='custom';

popupEditCounterUrl='http://en.wikipedia.org/wiki/User:$1?ectarget=$1';

*

* to your user javascript file (usually monobook.js), hover over a

* user name and select "edit counter"

*

*/

//

ec = {

getParamValue: function(paramName) {

var cmdRe=RegExp('[&?]'+paramName+'=([^&]*)');

var h=document.location;

var m;

if (m=cmdRe.exec(h)) {

try {

while(m[1].indexOf('+')!=-1)

{

m[1]=m[1].substr(0,m[1].indexOf('+'))+" "+m[1].substr(m[1].indexOf('+')+1);

}

return decodeURIComponent(m[1]);

} catch (someError) {}

}

return null;

},

doEditCount: function(user) {

if (!user) { return; }

ec.user=user;

ec.makeEditCountDivs();

ec.getContribs(user);

setTimeout(ec.checkContribs, 1000);

},

makeEditCountDivs: function() {

var d=document.createElement('div');

d.id='editcount_output';

ec.appendDivs(d, [ 'editcount_title', 'editcount_intervalselector',

'editcount_stats' ]);

var h=document.getElementById('siteSub');

h.parentNode.insertBefore(d, h.nextSibling);

},

appendDivs: function(parent, list) {

for (var i=0; i

var d=document.createElement('div');

d.id=list[i];

parent.appendChild(d);

}

},

checkContribs: function() {

if (ec.complete) {

ec.doOutput();

} else {

ec.doStatus();

setTimeout(ec.checkContribs, 1000);

}

},

doOutput: function(start, end) {

var d=document.getElementById('editcount_stats');

if (!ec.count) {

d.innerHTML='No edits found for ' + ec.user;

return;

}

if (!this.intsel) {

this.intsel = new IntervalSelector({

min: ts2unix(this.editlist.first.next.key),

max: ts2unix(this.editlist.last.prev.key)});

var this2=this;

this.intsel.doneDrag=function() {

//document.title=[this.lo, this.hi];

this2.doOutput.apply(this2, map(unix2ts, [this.lo, this.hi]));

};

this.intsel.dragging=function() {

var start=unix2ts(this2.intsel.lo);

var end=unix2ts(this2.intsel.hi);

document.getElementById('editcount_range').innerHTML=

formatTs(start) + ' - ' + formatTs(end);

};

//this.intsel.dragging=this.intsel.doneDrag; // too slow - pretty cool tho

var intdiv=document.getElementById('editcount_intervalselector');

intdiv.appendChild(this.intsel.box);

this.appendDivs(intdiv, ['editcount_range']);

this.intsel.dragging();

this.intseldebug=document.createElement('div');

this.intsel.box.parentNode.insertBefore(this.intseldebug, this.intsel.box);

}

document.getElementById('editcount_title').innerHTML=ec.outputHeading();

document.getElementById('editcount_stats').innerHTML='

Total: ' +

ec.countFigure() + '
First edit: ' + ec.firstEdit.replace(/[TZ]/g, ' ') +

'(UTC)' + ec.statsTable(start, end);

},

outputHeading: function() {

return '

Edit count for ' + ec.user + '

';

},

doStatus: function() {

var d=document.getElementById('editcount_stats');

d.innerHTML=ec.outputHeading() + '

Downloaded ' + ec.countFigure() + ' so far' + ec.statsTable();

},

countFigure: function() {

return ec.count + ' edits over ' + objSum(ec.namespaces, 'articleCount') + ' pages';

},

findEdit: function(timestamp, up) { // this is very broken - FIXME!

if (up) {

var e=this.editlist.first;

while(e.key

//console.log('findEdit, up: got '+timestamp+', found '+(e.prev && e.prev.key || null) );

return e.prev;

} else {

var e=this.editlist.last;

while(e.key>timestamp && (e=e.prev)){}

//console.log('findEdit, down: got '+timestamp+', found '+(e.next && e.next.key || null) );

return e.next;

}

},

statsTable: function(start, end) {

//console.log('start: '+start + ', end: '+end);

var barTotal=400;

var endEdit=this.findEdit(end) || this.editlist.last;

var startEdit=this.findEdit(start,true);

if (!startEdit || !startEdit.key) { startEdit=this.editlist.first.next; }

//console.log('endEdit:' + endEdit.key);

//console.log('startEdit:'+ startEdit.key);

var sumValue=function(val) {

return objSum(startEdit.stats, val) - objSum(endEdit.stats, val);

}

var total=sumValue('count');

if (!total) { return ''; }

var statValue=function(k, val) {

if (!startEdit.stats[k]) { return 0; }

var r=startEdit.stats[k][val];

//console.log(k + ' ' + val + ': ' + r);

if (!endEdit.stats[k] || !endEdit.stats[k][val]) { return r; }

return r - endEdit.stats[k][val];

};

// FIXME: abstract this away so it's trivial to add new columns

r='

Statistics between '+formatTs(startEdit.key) + ' and '+formatTs(endEdit.key);

r+='

';

for (var k in ec.namespace_names) {

if (!ec.namespaces[k]) { continue; }

r += '

';

r+=ec.ecBar(barTotal, total, statValue(k, 'count'), statValue(k, 'minorCount') || 0);

r+='

';

}

var totalMinor = sumValue('minorCount');

r+='

';

r+=ec.ecBar(barTotal, total, sumValue('count'), totalMinor);

r+='

' + ['Namespace',

'New',

'Minor',

'Top',

'Summaries',

'(manual)',

'Pages',

'Count', '%'].join('

') + '
'+[ec.namespace_names[k],

statValue(k, 'newCount'),

statValue(k, 'minorCount'),

statValue(k, 'topCount'),

statValue(k, 'commentCount'),

statValue(k, 'manualCommentCount'),

statValue(k, 'articleCount'),

statValue(k, 'count'),

percent(statValue(k, 'count'), total)].join('

') + '
'+['Total',

sumValue('newCount'),

totalMinor,

sumValue('topCount'),

sumValue('commentCount'),

sumValue('manualCommentCount'),

sumValue('articleCount'),

sumValue('count'),

'100'].join('

') + '
';

return r;

},

histogramBar: function(value, scale, colour, hint) {

var height='2ex';

var style='height: '+ height;

style += '; background: ' + colour;

style += '; width: ' + value * scale + 'px';

style += '; float: left;';

return '';

},

histogramCell: function(scale, values) {

var r='

';

for (var i=0; i

r+='

';

return r;

},

ecBar: function(scale, total, count, minor) {

var nonMinorColour='blue';

var minorColour='#0A3';

return ec.histogramCell( scale, [(count-minor)/total, nonMinorColour, "non-minor edits",

minor/total, minorColour, "minor edits"]);

},

ajax: {

download:function(bundle) {

// mandatory: bundle.url

// optional: bundle.onSuccess (xmlhttprequest, bundle)

// optional: bundle.onFailure (xmlhttprequest, bundle)

// optional: bundle.otherStuff OK too, passed to onSuccess and onFailure

var x = window.XMLHttpRequest ? new XMLHttpRequest()

: window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP")

: false;

if (x) {

x.onreadystatechange=function() {

x.readyState==4 && ec.ajax.downloadComplete(x,bundle);

};

x.open("GET",bundle.url,true);

x.send(null);

}

return x;

},

downloadComplete:function(x,bundle) {

x.status==200 && ( bundle.onSuccess && bundle.onSuccess(x,bundle) || true )

|| ( bundle.onFailure && bundle.onFailure(x,bundle) || alert(x.statusText));

}

},

getContribs: function(user, startAt) {

var limit=500; // currently maximum allowed per page by query.php

var url='http://en.wikipedia.org/w/query.php?what=usercontribs' +

'&uccomments' + // enable for edit comment analysis

'&format=json&uclimit=500&titles=User:'+escape(user);

if (startAt) { url += '&ucend=' + startAt.replace(/[^0-9]/g, ''); }

ec.ajax.download({ url: url, user: user,

startAt: startAt, onSuccess: ec.readContribs,

limit: limit});

},

readContribs: function(dl, bundle) {

window.dl=dl;

window.bundle=bundle;

try {

eval('var jsobj=' + dl.responseText);

var pages=jsobj.pages;

var child=ec.anyChild(pages);

var contribs=child.contributions;

} catch (summat) {

throw new Error('Badness happened in readContribs: ' + summat.message);

return;

}

var i=0, j=0;

var minrev=null;

for (var c in contribs) {

++i;

var cc=contribs[c];

if (!minrev || cc.revid < minrev) { minrev = cc.revid; }

if (ec.edits[cc.revid]) { continue; }

++j;

ec.doStats(cc);

ec.edits[cc.revid] = cc;

}

ec.count += j;

if (i == bundle.limit && ec.edits[minrev]) {

ec.getContribs(bundle.user, ec.edits[minrev].timestamp);

} else {

ec.complete=true;

minrev && (ec.firstEdit=ec.edits[minrev].timestamp);

}

},

doStats: function (c) {

var k=c.ns || 0;

//if (!ec.namespaces[k]) { console.log('New namespace: '+k + ', title=' +c['*'] +

// ', alleged NS=' + ec.namespace_names[k]); }

if (!ec.namespaces[k]) { ec.namespaces[k] = {articles: {}}; }

var n = ec.namespaces[k];

incr(n, 'count');

if (!n.articles[c['*']]) { incr(n, 'articleCount'); }

incr(n.articles, c['*']);

if (typeof c.minor != 'undefined') { incr(n, 'minorCount'); }

if (typeof c.top != 'undefined') { incr(n, 'topCount'); }

if (typeof c['new'] != 'undefined') { incr(n, 'newCount'); }

if (c.comment) {

incr(n, 'commentCount');

if (!RegExp("^/[*].*?[*]/ *$").test(c.comment)) {

incr(n, 'manualCommentCount');

}

}

this.editlist.add({key: parseInt(c.timestamp.replace(/[^0-9]/g, ''), 10),

edit: c,

stats: this.saveStats()});

// more stuff here, perhaps

},

saveStats: function() {

var r={};

var list=['count', 'articleCount', 'minorCount', 'topCount',

'newCount', 'commentCount', 'manualCommentCount'];

for (var k in ec.namespaces) {

r[k]=getStuff(ec.namespaces[k],list);

}

return r;

},

anyChild: function(obj) {

for (var p in obj) {

return obj[p];

}

return null;

},

edits: {},

count: 0,

complete: false,

namespaces: {},

namespace_names: {0: 'Article', 1: 'Talk',

2: 'User', 3: 'User talk',

4: 'Wikipedia', 5: 'Wikipedia talk',

6: 'Image', 7: 'Image talk',

8: 'MediaWiki', 9:'MediaWiki talk',

10: 'Template', 11: 'Template talk',

12: 'Help', 13: 'Help talk',

14: 'Category', 15: 'Category talk',

100: 'Portal', 101: 'Portal talk' // no comma

},

firstEdit: 0,

editlist: new linkedList(

{key: 99990101011200, stats: {}},

{key: 0, stats: {}}),

dummy: null // no comma

};

window.incr=function(obj, key) {

if (!obj[key]) { obj[key]=1; }

else { obj[key]++; }

}

window.objSum=function(obj, x, y) {

var r=0;

if (x && y) { for (var k in obj) { r+= (obj[k][x][y] ? obj[k][x][y] : 0); } }

else if (x) { for (var k in obj) { r+= (obj[k][x] ? obj[k][x] : 0); } }

else { for (var k in obj) { r+= (obj[k] ? obj[k] : 0); } }

return r;

}

window.percent=function(n, N) {

return Math.floor(n/N * 1000 + .5)/10;

};

if((user=ec.getParamValue('ectarget'))!==null) { addOnloadHook(function(){ec.doEditCount(user);}); }

function linkedList(x0,y0) {

this.first=null;

this.last=null;

this.hash={};

this.add=function(x) {

this.hash[x.key]=x;

if (!this.first) {

this.first=x;

this.last=x;

x.prev=x.next=null;

return;

}

var k=x.key;

if (true || k - this.first.key < this.last.key - k) {

this.pushTop(x);

} else {

this.pushTail(x);

}

};

this.pushTop=function(x) {

if (x.key < this.first.key) {

this.first.prev=x;

x.next=this.first;

this.first=x;

x.prev=null;

return;

}

if (x.key > this.last.key) {

this.last.next=x;

x.prev=this.last;

this.last=x;

x.next=null;

}

for (var y=this.first; y.next; y=y.next) {

if (y.key < x.key && x.key <= y.next.key) {

this.insertAfter(y, x);

return;

}

}

};

this.pushTail=function(x) {

for (var y=this.last; y.prev; y=y.prev) {

if (y.prev.key < x.key && x.key <= y.key) {

this.insertAfter(y.prev, x);

return;

}

}

this.first.prev=x;

x.next=this.first;

this.first=x;

x.prev=null;

};

this.insertAfter=function(y,x) {

x.next=y.next;

x.prev=y;

y.next.prev=x;

y.next=x;

};

if (x0) { this.add(x0); }

if (y0) { this.add(y0); }

}

window.getStuff=function(obj, list) {

var r={};

for (var i=0; i

if (typeof obj[list[i]] != 'undefined') { r[list[i]]=obj[list[i]]; }

}

return r;

}

window.IntervalSelector=function(data) {

if (!data) { data={}; }

this.min=data.min || 10;

this.max=data.max || 100;

this.span=this.max-this.min;

this.lo=data.lo || this.min;

this.hi=data.hi || this.max;

this.width=data.width || 400;

this.height=data.height || 20;

this.scale=this.width/this.span;

this.minBarWidth=data.minBarWidth || 10;

this.oldmousemove = null;

this.createDiv();

}

IntervalSelector.prototype.createDiv=function() {

var d=document.createElement('div');

d.className='intervalselectorbox';

//d.style.position='absolute';

d.style.border='1px solid black'; // FIXME

var s=document.createElement('div');

s.className='intervalselector';

s.style.position='relative';

s.style.background='orange'; // FIXME

//s.style.border='2px solid red'; // FIXME

d.appendChild(s);

this.box=d;

this.bar=s;

var this2=this;

this.bar.onmousedown=function(e){ this2.mouseDown.apply(this2, [e]); }

this.box.onmousedown=function(e){ this2.mouseDown.apply(this2, [e]); }

this.updatePosition();

};

IntervalSelector.prototype.updatePosition=function() {

var d=this.box;

d.style.width=this.width+'px';

d.style.height=this.height+'px';

var s=this.bar;

s.style.left=(this.lo-this.min)*this.scale+ 'px';

s.style.width=(this.hi-this.lo)*this.scale + 'px';

s.style.height=this.height + 'px';

};

IntervalSelector.prototype.mouseDown=function(e) {

var endWidth=8;

var pos=this.getMousePos(e);

var this2=this;

var dragFunction=null;

var leftPos=findPosX(this.bar);

if (pos.x - leftPos < endWidth) { dragFunction=this2.dragLo; }

else if ( leftPos + parseInt(this.bar.style.width, 10) - pos.x < endWidth) { dragFunction=this2.dragHi; }

else { dragFunction = this2.dragBar; }

var x=pos.x, lo=this.lo;

if (document.onmousemove && document.onmousemove.origin != 'IntervalSelector') {

this.oldmousemove = document.onmousemove;

}

document.onmousemove=function(e) {

dragFunction.apply(this2, [e, x, lo]);

this2.dragging.apply(this2);

};

document.onmousemove.origin='IntervalSelector';

document.onmouseup=function() {

//console.log(this2.oldmousemove.toString());

document.onmousemove= this2.oldmousemove;

this2.doneDrag.apply(this2);

};

document.onmouseup.origin='IntervalSelector';

//document.title=pos.x;

};

IntervalSelector.prototype.doneDrag=function(){};

IntervalSelector.prototype.dragging=function(){};

IntervalSelector.prototype.dragLo=function(e) {

var pos=this.getMousePos(e);

var newLo=this.min + (pos.x - findPosX(this.box))/this.scale;

if (newLo < this.min) { newLo=this.min; }

else if (newLo > this.hi - this.minBarWidth/this.scale) { newLo=this.hi - this.minBarWidth/this.scale; }

this.lo=newLo;

this.updatePosition();

};

IntervalSelector.prototype.dragHi=function(e) {

var pos=this.getMousePos(e);

var newHi=this.min + (pos.x - findPosX(this.box))/this.scale;

if (newHi > this.max) { newHi=this.max; }

else if (newHi < this.lo + this.minBarWidth/this.scale) { newHi=this.lo + this.minBarWidth/this.scale; }

this.hi=newHi;

this.updatePosition();

};

IntervalSelector.prototype.dragBar=function(e, x0, l0) {

var pos=this.getMousePos(e);

var delta=pos.x-x0;

var newLo=l0 + delta/this.scale;

var newHi=newLo + this.hi-this.lo;

if (newLo < this.min) { newLo=this.min; newHi=newLo+this.hi-this.lo; }

else if (newHi > this.max) { newHi=this.max; newLo=newHi-(this.hi-this.lo); }

this.hi=newHi; this.lo=newLo;

this.updatePosition();

};

IntervalSelector.prototype.getMousePos=function(e) {

e = e || window.event;

var x, y;

if (e) {

if (e.pageX) { x=e.pageX; y=e.pageY; }

else if (typeof e.clientX!='undefined') {

var left, top, docElt = window.document.documentElement;

if (docElt) { left=docElt.scrollLeft; }

left = left || window.document.body.scrollLeft || window.document.scrollLeft || 0;

if (docElt) { top=docElt.scrollTop; }

top = top || window.document.body.scrollTop || window.document.scrollTop || 0;

x=e.clientX + left;

y=e.clientY + top;

} else { throw new Error ('bad mouse wiggle event in getMousePos'); return; }

}

return {x:x, y:y};

};

window.findPosX=function(obj)

{

var curleft = 0;

if (obj.offsetParent)

{

while (obj.offsetParent)

{

curleft += obj.offsetLeft

obj = obj.offsetParent;

}

}

else if (obj.x)

curleft += obj.x;

return curleft;

}

window.ts2unix=function(ts) {

var t=ts.toString();

return +(Date.UTC( t.substring(0,4), parseInt(t.substring(4,6),10)-1, t.substring(6,8),

t.substring(8,10), t.substring(10,12), t.substring(12,14)));

}

window.unix2ts=function(u) {

var d=new Date(u);

return map(zeroFill, [d.getUTCFullYear(), d.getUTCMonth()+1,

d.getUTCDate(), d.getUTCHours(),

d.getUTCMinutes(), d.getUTCSeconds()]).join('');

}

window.zeroFill=function(s, min) {

min = min || 2;

var t=s.toString();

return repeatString('0', min - t.length) + t;

}

window.map=function(f, o) {

if (isArray(o)) { return map_array(f,o); }

return map_object(f,o);

}

window.isArray =function(x) { return x instanceof Array; }

window.map_array=function(f,o) {

var ret=[];

for (var i=0; i

ret.push(f(o[i]));

}

return ret;

}

window.map_object=function(f,o) {

var ret={};

for (var i in o) { ret[o]=f(o[i]); }

return ret;

}

window.repeatString=function(s,mult) {

var ret='';

for (var i=0; i

return ret;

};

window.formatTs=function(ts) {

ts=ts.toString();

if (ts.substring(0,4)=='9999') { return 'now'; }

return [ts.substring(0,4), ts.substring(4,6), ts.substring(6,8)].join('-') +

' ' + [ts.substring(8,10),ts.substring(10,12),ts.substring(12,14)].join(':');

};

function isMethodOf(klass, fn) {

for (var f in klass.prototype) {

if (fn===klass.prototype[f]) { return true; }

}

return false;

}

//

//ec.doEditCount('Amanda77')

// ec.doEditCount('Llama man')

}());