User:Elliottmobile/monobook.js: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
// Modded version of http://en.wikipedia.org/wiki/User: | // Modded version of http://en.wikipedia.org/wiki/User:Lupin/editcount.js by Elliott. | ||
/** <nowiki> | |||
* A javascript edit counter, using query.php as the backend | |||
* | |||
*/ | |||
// | //<pre> | ||
ec = { | |||
getParamValue: function(paramName) { | |||
var cmdRe=RegExp('[&?]'+paramName+'=([^&]*)'); | |||
var | 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<list.length; ++i) { | |||
var d=document.createElement('div'); | |||
d.id=list[i]; | |||
parent.appendChild(d); | |||
} | |||
}, | |||
checkContribs: function() { | |||
if (ec.complete) { | |||
ec.doOutput(); | |||
} else { | |||
function | 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>'; | |||
}, | |||
var | 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'; | |||
}, | |||
return | 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.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='<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.namespaces[k]) { continue; } | |||
r += '<tr><td>'+[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('</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(values[i], scale, values[i+1], values[i+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://wiki.hydrogenaudio.org/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); | |||
} | |||
}, | |||
function | 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: 'Hydrogenaudio Knowledgebase', 5: 'Hydrogenaudio Knowledgebase 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', | |||
16: 'Foobar2000', 17: 'Foobar2000 talk', | |||
18: 'Foobar2000redirect', 19: 'Foobar2000redirect talk', | |||
}, | |||
firstEdit: 0, | |||
editlist: new linkedList( | |||
{key: 99990101011200, stats: {}}, | |||
{key: 0, stats: {}}), | |||
dummy: null // no comma | |||
}; | |||
function | window.incr=function(obj, key) { | ||
if (!obj[key]) { obj[key]=1; } | |||
else { obj[key]++; } | |||
} | |||
window.objSum=function(obj, x, y) { | |||
var | 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; | |||
var | this.last=null; | ||
if ( | 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<list.length; ++i) { | |||
if (typeof obj[list[i]] != 'undefined') { r[list[i]]=obj[list[i]]; } | |||
function | } | ||
return r; | |||
var | |||
} | } | ||
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(); | |||
}; | |||
var | 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; | |||
} | } | ||
function | 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) { | |||
function | var d=new Date(u); | ||
var | 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(); | |||
function | return repeatString('0', min - t.length) + t; | ||
} | } | ||
window.map=function(f, o) { | |||
function | 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) { | |||
function | var ret=[]; | ||
{ | for (var i=0; i<o.length; ++i) { | ||
ret.push(f(o[i])); | |||
} | |||
return ret; | |||
} | } | ||
window.map_object=function(f,o) { | |||
var ret={}; | |||
function | for (var i in o) { ret[o]=f(o[i]); } | ||
return ret; | |||
} | } | ||
window.repeatString=function(s,mult) { | |||
function | var ret=''; | ||
var ret = | for (var i=0; i<mult; ++i) { ret += s; } | ||
for (var | |||
return ret; | 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 | function isMethodOf(klass, fn) { | ||
for (var f in klass.prototype) { | |||
if (fn===klass.prototype[f]) { return true; } | |||
} | } | ||
return false; | |||
} | } | ||
//</nowiki></pre> | |||
//ec.doEditCount('Amanda77') | |||
// ec.doEditCount('Llama man') |
Revision as of 21:11, 21 September 2006
// Modded version of http://en.wikipedia.org/wiki/User:Lupin/editcount.js by Elliott. /** <nowiki> * A javascript edit counter, using query.php as the backend * */ //<pre> 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<list.length; ++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='<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.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='<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.namespaces[k]) { continue; } r += '<tr><td>'+[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('</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(values[i], scale, values[i+1], values[i+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://wiki.hydrogenaudio.org/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: 'Hydrogenaudio Knowledgebase', 5: 'Hydrogenaudio Knowledgebase 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', 16: 'Foobar2000', 17: 'Foobar2000 talk', 18: 'Foobar2000redirect', 19: 'Foobar2000redirect talk', }, 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<list.length; ++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<o.length; ++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<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.prototype[f]) { return true; } } return false; } //</nowiki></pre> //ec.doEditCount('Amanda77') // ec.doEditCount('Llama man')