From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

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

(function () {

return;



/** <nowiki>

 * 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"

 *

 */



//<pre>



ec = {



	getParamValue: function(paramName) {

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

		var h=document.location;

		var m;

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

			try {

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

				{

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

				}

				return decodeURIComponent(m1]);

			} 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<list.length; ++i) {

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

			d.id=listi];

			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='<p>Total: ' + 

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

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

	},



	outputHeading: function() {

		return '<h2>Edit count for ' + ec.user + '</h2>';

	},



	doStatus: function() {

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

		d.innerHTML=ec.outputHeading() + '<p>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<timestamp && (e=e.next)){};

			//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.statsk]) { return 0; }

			var r=startEdit.statsk][val];

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

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

			return r - endEdit.statsk][val];

		};

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

		r='<p>Statistics between '+formatTs(startEdit.key) + ' and '+formatTs(endEdit.key);

		r+='<table><tr><th>' + 'Namespace',

					   'New',

					   'Minor',

					   'Top',

					   'Summaries',

					   '(manual)',

					   'Pages',

					   'Count', '%'].join('</th><th>') + '</th></tr>';

		for (var k in ec.namespace_names) {

			if (!ec.namespacesk]) { continue; }

			r += '<tr><td>'+ec.namespace_namesk],

					 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('</td><td>') + '</td>';

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

			r+='</tr>';

		}

		var totalMinor = sumValue('minorCount');

		r+='<tr><td>'+'<b>Total</b>',

			       sumValue('newCount'),

			       totalMinor,

			       sumValue('topCount'),

			       sumValue('commentCount'),

			       sumValue('manualCommentCount'),

			       sumValue('articleCount'),

			       sumValue('count'),

			       '100'].join('</td><td>') + '</td>';

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

		r+='</table>';

		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 '<span style="' + style + '" title="' + hint + '"></span>';

	},



	histogramCell: function(scale, values) {

		var r='<td><div style="width: ' + scale + 'px;">';

		for (var i=0; i<values.length; i+=3) { r+=ec.histogramBar(valuesi], scale, valuesi+1], valuesi+2]); }

		r+='</div></td>';

		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=contribsc];

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

			if (ec.editscc.revid]) { continue; }

			++j;

			ec.doStats(cc);

			ec.editscc.revid = cc;

		}

		ec.count += j;

		if (i == bundle.limit && ec.editsminrev]) {

			ec.getContribs(bundle.user, ec.editsminrev].timestamp);

		} else {

			ec.complete=true;

			minrev && (ec.firstEdit=ec.editsminrev].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.namespacesk]) { ec.namespacesk = {articles: {}}; }

		var n = ec.namespacesk];

		incr(n, 'count');

		if (!n.articlesc'*']]) { 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),

				   : 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) {

			rk=getStuff(ec.namespacesk],list);

		}

		return r;

	},



	anyChild: function(obj) {

		for (var p in obj) {

			return objp];

		}

		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 (!objkey]) { objkey=1; }

	else { objkey++; }

}



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

	var r=0;

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

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

	else        { for (var k in obj) { r+= (objk ? objk : 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.hashx.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<list.length; ++i) {

		if (typeof objlisti]] != 'undefined') { rlisti]]=objlisti]]; }

	}

	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<o.length; ++i) {

		ret.push(f(oi]));

	}

	return ret;

}



window.map_object=function(f,o) {

	var ret={};

	for (var i in o) { reto=f(oi]); }

	return ret;

}



window.repeatString=function(s,mult) {

	var ret='';

	for (var i=0; i<mult; ++i) { ret += s; }

	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.prototypef]) { return true; }

	}

	return false;

}



//</nowiki></pre>

//ec.doEditCount('Amanda77')

// ec.doEditCount('Llama man')

}());
From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

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

(function () {

return;



/** <nowiki>

 * 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"

 *

 */



//<pre>



ec = {



	getParamValue: function(paramName) {

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

		var h=document.location;

		var m;

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

			try {

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

				{

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

				}

				return decodeURIComponent(m1]);

			} 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<list.length; ++i) {

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

			d.id=listi];

			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='<p>Total: ' + 

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

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

	},



	outputHeading: function() {

		return '<h2>Edit count for ' + ec.user + '</h2>';

	},



	doStatus: function() {

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

		d.innerHTML=ec.outputHeading() + '<p>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<timestamp && (e=e.next)){};

			//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.statsk]) { return 0; }

			var r=startEdit.statsk][val];

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

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

			return r - endEdit.statsk][val];

		};

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

		r='<p>Statistics between '+formatTs(startEdit.key) + ' and '+formatTs(endEdit.key);

		r+='<table><tr><th>' + 'Namespace',

					   'New',

					   'Minor',

					   'Top',

					   'Summaries',

					   '(manual)',

					   'Pages',

					   'Count', '%'].join('</th><th>') + '</th></tr>';

		for (var k in ec.namespace_names) {

			if (!ec.namespacesk]) { continue; }

			r += '<tr><td>'+ec.namespace_namesk],

					 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('</td><td>') + '</td>';

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

			r+='</tr>';

		}

		var totalMinor = sumValue('minorCount');

		r+='<tr><td>'+'<b>Total</b>',

			       sumValue('newCount'),

			       totalMinor,

			       sumValue('topCount'),

			       sumValue('commentCount'),

			       sumValue('manualCommentCount'),

			       sumValue('articleCount'),

			       sumValue('count'),

			       '100'].join('</td><td>') + '</td>';

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

		r+='</table>';

		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 '<span style="' + style + '" title="' + hint + '"></span>';

	},



	histogramCell: function(scale, values) {

		var r='<td><div style="width: ' + scale + 'px;">';

		for (var i=0; i<values.length; i+=3) { r+=ec.histogramBar(valuesi], scale, valuesi+1], valuesi+2]); }

		r+='</div></td>';

		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=contribsc];

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

			if (ec.editscc.revid]) { continue; }

			++j;

			ec.doStats(cc);

			ec.editscc.revid = cc;

		}

		ec.count += j;

		if (i == bundle.limit && ec.editsminrev]) {

			ec.getContribs(bundle.user, ec.editsminrev].timestamp);

		} else {

			ec.complete=true;

			minrev && (ec.firstEdit=ec.editsminrev].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.namespacesk]) { ec.namespacesk = {articles: {}}; }

		var n = ec.namespacesk];

		incr(n, 'count');

		if (!n.articlesc'*']]) { 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),

				   : 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) {

			rk=getStuff(ec.namespacesk],list);

		}

		return r;

	},



	anyChild: function(obj) {

		for (var p in obj) {

			return objp];

		}

		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 (!objkey]) { objkey=1; }

	else { objkey++; }

}



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

	var r=0;

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

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

	else        { for (var k in obj) { r+= (objk ? objk : 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.hashx.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<list.length; ++i) {

		if (typeof objlisti]] != 'undefined') { rlisti]]=objlisti]]; }

	}

	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<o.length; ++i) {

		ret.push(f(oi]));

	}

	return ret;

}



window.map_object=function(f,o) {

	var ret={};

	for (var i in o) { reto=f(oi]); }

	return ret;

}



window.repeatString=function(s,mult) {

	var ret='';

	for (var i=0; i<mult; ++i) { ret += s; }

	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.prototypef]) { return true; }

	}

	return false;

}



//</nowiki></pre>

//ec.doEditCount('Amanda77')

// ec.doEditCount('Llama man')

}());

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook