v50 Steam/Premium information for editors
  • v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
  • Use this page to report any issues related to the migration.
This notice may be cached—the current version can be found here.

Difference between revisions of "User:Lethosor/rater 0.1.js"

From Dwarf Fortress Wiki
Jump to navigation Jump to search
m (whee)
m (Lethosor moved page User:Lethosor/rater.js to User:Lethosor/rater 0.1.js: Updating)
 
(11 intermediate revisions by the same user not shown)
Line 11: Line 11:
 
SCOPE = this
 
SCOPE = this
 
var rater = {}
 
var rater = {}
 +
rater.event=$({});//for event bindings
 +
//Temporary! (importScript doesn't seem to work here)
 +
$.getScript(wgScript+'?title=User:Lethosor/raterwarn.js&action=raw');
 +
 
function PD(e){//preventDefault
 
function PD(e){//preventDefault
 
if(e && e['preventDefault'] && 'call' in e.preventDefault)
 
if(e && e['preventDefault'] && 'call' in e.preventDefault)
Line 17: Line 21:
 
function is_func(x){return !!(x&&x['call'])}
 
function is_func(x){return !!(x&&x['call'])}
 
rater.page = {
 
rater.page = {
name: wgPageName,
+
name: wgPageName.replace(/_/g,' '),
url: wgScript+'/'+wgPageName
+
ns:wgCanonicalNamespace||'Main',
 +
url: wgScript+'?title='+wgPageName,
 +
exists:!$('#left-navigation').find('li[class*=selected]').find('a[href*=redlink]').length,
 +
load_time:$('body').html().match(/<!--.*-->/g).slice(-1)[0].match(/\d+\.\d+/)[0]
 
}
 
}
 
 
 
rater.is_valid_page = function(page){
 
rater.is_valid_page = function(page){
 +
if(!rater.page.exists) return false;
 
if(!page) page=wgPageName;
 
if(!page) page=wgPageName;
ns=page.split(':')[0];
+
if('Masterwork DF2012 v0.31 40d 23a'.split(' ').indexOf(rater.page.ns)+1) return true
if('DF2012 v0.31 40d 23a'.split(' ').indexOf(ns)+1) return true
 
 
if($('#norate').length) return false
 
if($('#norate').length) return false
 
return false
 
return false
Line 30: Line 37:
 
 
 
rater.error_invalid_page=function(){
 
rater.error_invalid_page=function(){
rater.box.clear().append('<span class="error">Invalid page</span>')
+
if('Special File Image Unused'.indexOf(rater.page.ns)+1||
 +
rater.page.ns.toLowerCase().indexOf('talk')+1){
 +
rater.box.clear().append('<span class="error">I\'m afraid I can\'t let you do that,'+
 +
' Urist.</span><p>This page isn\'t an article, and doesn\'t have some of the necessary'+
 +
' properties to be rated. <a href="#rater-cancel">Close this window</a></p>')
 +
}
 +
else if(!rater.page.exists){
 +
rater.box.clear().append('<p class="error">This page doesn\'t exist!</p>')
 +
}
 +
else{
 +
rater.box.clear().append('<span class="error">Invalid page</span>')
 
.append("<p>This page is in an invalid namespace. You can "+
 
.append("<p>This page is in an invalid namespace. You can "+
 
"<a href='#rater-force'>view this page's rating anyway</a> or "+
 
"<a href='#rater-force'>view this page's rating anyway</a> or "+
 
"<a href='#rater-cancel'>close this window</a>.</p>")
 
"<a href='#rater-cancel'>close this window</a>.</p>")
 +
}
 
};
 
};
 
 
Line 41: Line 59:
 
.hide().appendTo('body');
 
.hide().appendTo('body');
 
rater.box = $('<div>').css({width:'80%', height:'80%', top:'10%', left:'10%',
 
rater.box = $('<div>').css({width:'80%', height:'80%', top:'10%', left:'10%',
position:'fixed', 'background-color':'white', 'z-index':10000, padding:'1em'})
+
position:'fixed', 'background-color':'white', 'z-index':10000, padding:'1em',overflow:'auto'})
 
.hide().appendTo('body');
 
.hide().appendTo('body');
 
rater.cancel_link = $('<a>').text('Cancel').attr('href','#rater-cancel').css({color:'red',  
 
rater.cancel_link = $('<a>').text('Cancel').attr('href','#rater-cancel').css({color:'red',  
Line 52: Line 70:
 
rater.popup.box = $('<div>').css({width:'40%', height:'60%', top:'20%', left:'30%',
 
rater.popup.box = $('<div>').css({width:'40%', height:'60%', top:'20%', left:'30%',
 
position:'fixed', 'background-color':'white', 'z-index':10002, padding:'1em',
 
position:'fixed', 'background-color':'white', 'z-index':10002, padding:'1em',
overflow:'scroll'})
+
overflow:'auto'})
 
.hide().appendTo('body');
 
.hide().appendTo('body');
 
rater.popup.close_link = $('<a>').text('Close').attr('href','#rater-popup-hide').css({color:'red',  
 
rater.popup.close_link = $('<a>').text('Close').attr('href','#rater-popup-hide').css({color:'red',  
Line 59: Line 77:
 
rater.box.html('')
 
rater.box.html('')
 
rater.box.append(rater.cancel_link)
 
rater.box.append(rater.cancel_link)
.append($('<h2>').text('Rating '+wgPageName));
+
.append($('<h2>').text('Rating '+rater.page.name));
 +
rater.event.trigger('box-clear');
 
return rater.box;
 
return rater.box;
 
};
 
};
Line 68: Line 87:
 
};
 
};
 
rater.popup_hide = function(e){
 
rater.popup_hide = function(e){
PD(e)
+
PD(e);
 
rater.popup.overlay.stop(1,1).fadeOut(500);
 
rater.popup.overlay.stop(1,1).fadeOut(500);
 
rater.popup.box.stop(1,1).fadeOut(500);
 
rater.popup.box.stop(1,1).fadeOut(500);
Line 75: Line 94:
 
rater.box.clear();
 
rater.box.clear();
 
rater.popup.clear=function(){
 
rater.popup.clear=function(){
rater.popup.box.html('').append(rater.popup.close_link)
+
rater.popup.box.html('').append(rater.popup.close_link);
 
};
 
};
 
rater.popup.clear();
 
rater.popup.clear();
Line 81: Line 100:
 
 
 
rater.cancel = function(e){
 
rater.cancel = function(e){
PD(e)
+
PD(e);rater.show_link.removeClass('selected');
 +
$('body').css({overflow:'auto'});
 
rater.overlay.stop(1,1).fadeOut(500);
 
rater.overlay.stop(1,1).fadeOut(500);
 
rater.box.stop(1,1).fadeOut(500);
 
rater.box.stop(1,1).fadeOut(500);
Line 91: Line 111:
 
$("<a href='#rater-invoke'>").text('Rate')
 
$("<a href='#rater-invoke'>").text('Rate')
 
));
 
));
 +
$("#left-navigation #p-namespaces ul:nth(0)").append('<li><span><a></a></span></li>')
 
$("#left-navigation #p-namespaces ul:nth(0)").append(rater.show_link)
 
$("#left-navigation #p-namespaces ul:nth(0)").append(rater.show_link)
+
 
 
rater.invoke = function(e, force){
 
rater.invoke = function(e, force){
PD(e);
+
PD(e);$('body').css({overflow:'hidden'});
 
rater.overlay.stop(1,1).fadeIn(500);
 
rater.overlay.stop(1,1).fadeIn(500);
 
rater.box.stop(1,1).fadeIn(500);
 
rater.box.stop(1,1).fadeIn(500);
 
if(!rater.is_valid_page(wgPageName) && !force)
 
if(!rater.is_valid_page(wgPageName) && !force)
 
return rater.error_invalid_page();
 
return rater.error_invalid_page();
rater.box.clear()
+
rater.box.clear();rater.show_link.addClass('selected');
 
rater.box.append('<p>Performing automatic tests (<span class="rater-tests-left"></span> remaining), please wait...</p>');
 
rater.box.append('<p>Performing automatic tests (<span class="rater-tests-left"></span> remaining), please wait...</p>');
 
rater.begin_tests();
 
rater.begin_tests();
Line 116: Line 137:
 
tests.list={}; //list of all tests
 
tests.list={}; //list of all tests
 
tests.results={}; //shortcut: data[x]==lists[x].data
 
tests.results={}; //shortcut: data[x]==lists[x].data
 +
tests.random=Math.floor(Math.random()*1e10);
 
 
 
tests.num_waiting = 0;
 
tests.num_waiting = 0;
Line 121: Line 143:
 
tests.add = function(name, data_url, processor){  
 
tests.add = function(name, data_url, processor){  
 
//Note: jQuery caches Ajax requests by default, no need to here
 
//Note: jQuery caches Ajax requests by default, no need to here
tests.list[name] = {data_url:data_url, processor:processor};  
+
tests.list[name] = {data_url:data_url, processor:processor};
//console.log('retrieving')
+
//Prevents caching for the first rating (reset on page load)
 +
data_url+=((data_url.indexOf('?')+1)?'&':'?')+'_='+tests.random;
 
$.get(data_url, function(data){
 
$.get(data_url, function(data){
 
tests.process(name, data);
 
tests.process(name, data);
Line 134: Line 157:
 
$(".rater-tests-left").text(tests.num_waiting)
 
$(".rater-tests-left").text(tests.num_waiting)
 
if(tests.num_waiting <= 0){
 
if(tests.num_waiting <= 0){
setTimeout(tests.all_complete, 1); //avoid stacking
+
setTimeout(tests.all_complete, 1); //async
 
}
 
}
 
};
 
};
Line 147: Line 170:
 
tests.all_callbacks.push(func);
 
tests.all_callbacks.push(func);
 
};
 
};
 +
 +
rater.help={view:$('<div>').css({height:'100%'})};
 +
rater.help.init=function(){
 +
$.get(wgScript+'/DF:Quality',function(d){
 +
d=$(d);
 +
rater.help.data=[];
 +
for(var i=0;i<=5;i++){
 +
rater.help.data[i]=d.find('h3:nth({0})'.format(i)).nextUntil('h3');
 +
rater.help.data[i].splice(0,0,d.find('h3:nth({0})'.format(i))[0]); // prepend header
 +
};
 +
rater.help.update();
 +
});
 +
};
 +
rater.help.update=function(n){
 +
if(!rater.help.data)return;
 +
if(!(n+1)){
 +
try{
 +
n=rater.ratings[rater.select.current].id;
 +
} catch(e){return}
 +
}
 +
rater.help.view.text('').append(rater.help.data[n]);
 +
rater.help.view.find('.editsection').hide();
 +
//rater.box.scrollTop(rater.box.height());
 +
};
 +
 
 
 
rater.begin_tests = function(){
 
rater.begin_tests = function(){
render_url=rater.page.url+'?action=render'
+
render_url=rater.page.url+'&action=render'
raw_url=rater.page.url+'?action=raw'
+
raw_url=rater.page.url+'&action=raw'
 +
rater.help.init()
 
tests.add('redlinks', render_url, function(data){
 
tests.add('redlinks', render_url, function(data){
 
all_links = data.match(/<a .*<\/a>/g);
 
all_links = data.match(/<a .*<\/a>/g);
Line 160: Line 209:
 
return total_redlinks
 
return total_redlinks
 
});
 
});
tests.add('editors', rater.page.url+'?action=history&limit=100', function(data){
+
tests.add('editors', rater.page.url+'&action=history&limit=100', function(data){
 
all_editors = data.match(/<li>.*<\/li>/g)
 
all_editors = data.match(/<li>.*<\/li>/g)
 
if(!all_editors) return 0; //no editors
 
if(!all_editors) return 0; //no editors
Line 174: Line 223:
 
num++
 
num++
 
}
 
}
editors.total=num;
+
editors.total=num;editors.total_edits=all_editors.length;
 
return editors;
 
return editors;
 
});
 
});
Line 184: Line 233:
 
});
 
});
 
tests.add('length', raw_url, function(data){
 
tests.add('length', raw_url, function(data){
 +
rater.raw_text=data;
 
var a={
 
var a={
 
'full':data.length,
 
'full':data.length,
Line 190: Line 240:
 
'raw':data.replace(/\s/g,'').replace(/{{[^}]*?}}/g,'').length
 
'raw':data.replace(/\s/g,'').replace(/{{[^}]*?}}/g,'').length
 
};
 
};
a.average=.1 * a.full + .2*a.nospace + .3*a.notemplate + .4*a.raw
+
a.average=Math.round(.1 * a.full + .2*a.nospace + .3*a.notemplate + .4*a.raw)
 
return a
 
return a
 +
});
 +
tests.add('html_length', render_url, function(data){
 +
var a={'html':data.length,'text':$(data).text().length};
 +
return a;
 +
});
 +
tests.add('verify',raw_url,function(data){
 +
var m=data.match(/{{verify/g);return +(m&&m.length);
 +
});
 +
tests.add('current_rating',raw_url,function(data){
 +
m=data.match(/{{quality[^}]*?}}/i);
 +
return (m&&m.length&&m[0].slice(2,-2).split('|')[1])||''
 
});
 
});
 
tests.add_callback(function(){
 
tests.add_callback(function(){
Line 207: Line 268:
 
};
 
};
 
rater.metadata = {
 
rater.metadata = {
'redlinks':{type:1,name:'Redlinks'},
+
'redlinks':{name:'Redlinks'},
'links': {type:1,name:'Outbound links'},
+
'links': {name:'Outbound links'},
'linkshere': {type:1,name:'Incoming links'},
+
'linkshere': {name:'Incoming links'},
'editors': {type:'o',name:'Editor count',str:function(o){return o.total},
+
'editors': {name:'Editor count',str:function(o){return o.total},
 
int:function(o){return o.total;},
 
int:function(o){return o.total;},
 
info:function(o,view){
 
info:function(o,view){
tbl=$("<table>") .append('<tr><th>User</th><th>Edits</th></tr>')
+
tbl=$("<table>").css({width:'100%'}).append('<tr><th colspan="2">User</th><th>Edits</th></tr>').addClass('wikitable sortable').appendTo(view)
.addClass('wikitable sortable').appendTo(view)
 
 
for(i in o){
 
for(i in o){
if(i in {}||i=='total') continue;
+
if(i in {}||!i.indexOf('total')) continue;
tbl.append('<tr><td><a href="{2}/User:{0}">{0}</a>:</td><td>{1}</td></tr>'.format(i,o[i],wgScript))
+
tbl.append('<tr><td colspan="2"><a href="{2}/User:{0}">{0}</a>:</td><td>{1}</td></tr>'.format(i,o[i],wgScript))
 
}
 
}
tbl.append('<tr style="font-weight:bold"><td>Total:</td><td>{0}</td></tr>'
+
tbl.append('<tr style="font-weight:bold"><td>Total:</td><td>{0}</td><td>{1}</td></tr>'
.format(o.total))
+
.format(o.total,o.total_edits))
 
},
 
},
 
},
 
},
'length': {type:'o', str:function(obj){
+
'length': {str:function(obj){
 
return "Weighted: {0} ({1} characters, {2} without templates)"
 
return "Weighted: {0} ({1} characters, {2} without templates)"
 
.format(obj.average,obj.full,obj.notemplate);},
 
.format(obj.average,obj.full,obj.notemplate);},
name:'Article length'},
+
name:'Source length'},
 +
html_length:{name:'Text length',
 +
str:function(o){return '{0} (HTML: {1})'.format(o.text,o.html)},
 +
int:function(o){return o.text}},
 +
verify:{name:'{{Verify}} tags',score:function(o){
 +
if(o<1) return 0; if(o==1) return -10; return o*-20}},
 +
current_rating:{name:'Current rating', int:function(){return 0;}}
 +
};
 +
 +
rater.ratings={
 +
tattered:{id:1,color:{b:'#333',bg:'#ccc',c:'#333'},s:'x'},
 +
fine:{id:2,color:{b:'#db8',bg:'#ffe0cc',c:'#ca7a02'},s:'+'},
 +
superior:{id:3,color:{b:'#b8f',bg:'#e4ccff',c:'#80c'},s:'*'},
 +
exceptional:{id:4,color:{b:'#9df',bg:'#cce4ff',c:'#08c'},s:'\u2261'},
 +
masterwork:{id:5,color:{b:'#bd8',bg:'#e2fdce',c:'#72a329'},s:'\u263c'}
 
};
 
};
 
 
Line 258: Line 332:
 
;
 
;
 
rater.box.append($("<p>").text("Score: "+rater.score))
 
rater.box.append($("<p>").text("Score: "+rater.score))
 +
rater.select.init($("<div>").appendTo(rater.box));
 +
};
 +
rater.select={};
 +
rater.select.view=$("<div>").css({padding:'.2em'});
 +
rater.select.init=function(){
 +
rater.select.current=rater.tests.results.current_rating.toLowerCase()||'tattered';
 +
rater.select.view.appendTo(rater.box);
 +
rater.select.draw();
 +
};
 +
rater.select.draw=function(){var c,selected,a,view=rater.select.view;
 +
view.text('\nDesired rating: ');
 +
list=$('<span>').css({'font-size':'.8em'}).appendTo(rater.select.view)
 +
for(i in rater.ratings){if(i in {}) continue;
 +
c=rater.ratings[i];
 +
a=$('<span>').appendTo(list).data('rating',i).attr({tabindex:0}).css({cursor:'pointer'}); //not real link
 +
a.append($('<span>').text(c.s+i.toUpperCase()+c.s)
 +
.css({'text-decoration':'none','color':c.color.c}));
 +
if(i==rater.select.current){
 +
selected=rater.ratings[i];
 +
a.find('span').css({'border-color':c.color.b,'background-color':c.color.bg,'border-width':1,'border-style':'solid','border-radius':2,padding:'.2em'})
 +
}
 +
a.on('click focus',rater.select.click);
 +
list.append(' ');
 +
}
 +
if(rater.is_valid_page()){
 +
rater.submit_link=$('<a>').attr({href:'#rater-submit'}).text("Submit").appendTo(view);
 +
view.append(' ');
 +
}
 +
rater.reset_link=$('<a>').attr({href:'#rater-reset'}).text("Reset").appendTo(view);
 +
rater.help.view.appendTo(view);
 +
rater.help.update(selected.id);
 +
//rater.box.scrollTop(rater.box.height());
 +
};
 +
 +
rater.select.click=function(e){if(e.type=='click')PD(e);
 +
rater.select.current=$(this).data('rating');
 +
rater.select.draw();
 +
};
 +
 +
rater.select.reset=function(e){PD(e);rater.select.init();};
 +
 +
$('body').on('click','a[href=#rater-reset]',rater.select.reset);
 +
$('body').on('click','a[href=#rater-submit]',function(e){PD(e);
 +
rater.submit_rating();
 +
});
 +
 +
rater.submit_rating=function(){
 +
rater.box.clear();stat=$('<pre>').appendTo(rater.box);
 +
function w(s){stat.append(s);}
 +
var r=rater.select.current;
 +
if(r in {}||!(r in rater.ratings)) return;
 +
var rating=rater.select.current.capitalize();
 +
if(!rater.raw_text) return;
 +
//get token
 +
w('Retrieving token... ');
 +
$.get(wgScriptPath+'/api.php?format=json&action=query&prop=info&indexpageids=1&intoken=edit&titles='+wgPageName,function(d){
 +
var token=d.query.pages[d.query.pageids[0]].edittoken;
 +
w('Ok ({0})\nReplacing quality template... '.format(token.slice(0,-2)));
 +
console.log(token);
 +
var text=rater.raw_text.replace(/{{quality[^}]*?}}/gi,'');
 +
text='{{Quality|'+rating+'|~~\u007e~~}}'+text
 +
//console.log(text)
 +
w('Ok\nEditing page... ')
 +
var e=encodeURIComponent;
 +
var page=rater.page.name;
 +
$.post(wgScriptPath+'/api.php', {action:'edit',title:rater.page.name,text:text,
 +
token:token,minor:1,summary:'Rated article "{0}" using the rating script.'.format(rating)},function(d){
 +
//console.log(d,'DONE')
 +
w('Finished!\nReloading...');
 +
$('body').load(rater.page.url + ' body',function(d){
 +
window.location.reload(); //maybe load w/ ajax?
 +
});
 +
});
 +
});
 
};
 
};
 
 

Latest revision as of 23:10, 27 April 2013

/*
Rating Script (DF Wiki)

Improved version -- now uses jQuery, extra options (existence not guaranteed)
*/

//Like python: 'a{0}b'.format('c') == 'acb'
String.prototype.format=function(){s=this;for(i=0;i<arguments.length;i++){s=s.replace(RegExp('\\{'+i+'\\}','g'), arguments[i])};return s};
String.prototype.capitalize=function(){return this.slice(0,1).toUpperCase()+this.slice(1)};
addOnloadHook(function(){jQuery(function($){
	SCOPE = this
	var rater = {}
	rater.event=$({});//for event bindings
	//Temporary! (importScript doesn't seem to work here)
	$.getScript(wgScript+'?title=User:Lethosor/raterwarn.js&action=raw');
	
	function PD(e){//preventDefault
		if(e && e['preventDefault'] && 'call' in e.preventDefault)
			e.preventDefault()
	}
	function is_func(x){return !!(x&&x['call'])}
	rater.page = {
		name: wgPageName.replace(/_/g,' '),
		ns:wgCanonicalNamespace||'Main',
		url: wgScript+'?title='+wgPageName,
		exists:!$('#left-navigation').find('li[class*=selected]').find('a[href*=redlink]').length,
		load_time:$('body').html().match(/<!--.*-->/g).slice(-1)[0].match(/\d+\.\d+/)[0]
	}
	
	rater.is_valid_page = function(page){
		if(!rater.page.exists) return false;
		if(!page) page=wgPageName;
		if('Masterwork DF2012 v0.31 40d 23a'.split(' ').indexOf(rater.page.ns)+1) return true
		if($('#norate').length) return false
		return false
	};
	
	rater.error_invalid_page=function(){
		if('Special File Image Unused'.indexOf(rater.page.ns)+1||
			rater.page.ns.toLowerCase().indexOf('talk')+1){
			rater.box.clear().append('<span class="error">I\'m afraid I can\'t let you do that,'+
			' Urist.</span><p>This page isn\'t an article, and doesn\'t have some of the necessary'+
			' properties to be rated. <a href="#rater-cancel">Close this window</a></p>')
		}
		else if(!rater.page.exists){
			rater.box.clear().append('<p class="error">This page doesn\'t exist!</p>')
		}
		else{
			rater.box.clear().append('<span class="error">Invalid page</span>')
			.append("<p>This page is in an invalid namespace. You can "+
				"<a href='#rater-force'>view this page's rating anyway</a> or "+
				"<a href='#rater-cancel'>close this window</a>.</p>")
		}
	};
	
	// Set up UI
	rater.overlay = $('<div>').css({width:'100%', height:'100%', top:0, left:0,
		position:'fixed', 'background-color':'rgba(128,128,128,0.5)', 'z-index':9999})
		.hide().appendTo('body');
	rater.box = $('<div>').css({width:'80%', height:'80%', top:'10%', left:'10%',
		position:'fixed', 'background-color':'white', 'z-index':10000, padding:'1em',overflow:'auto'})
		.hide().appendTo('body');
	rater.cancel_link = $('<a>').text('Cancel').attr('href','#rater-cancel').css({color:'red', 
		'float':'right'}).appendTo(rater.box);
	
	rater.popup={};
	rater.popup.overlay = $('<div>').css({width:'100%', height:'100%', top:0, left:0,
		position:'fixed', 'background-color':'rgba(128,128,128,0.5)', 'z-index':10001})
		.hide().appendTo('body');
	rater.popup.box = $('<div>').css({width:'40%', height:'60%', top:'20%', left:'30%',
		position:'fixed', 'background-color':'white', 'z-index':10002, padding:'1em',
		overflow:'auto'})
		.hide().appendTo('body');
	rater.popup.close_link = $('<a>').text('Close').attr('href','#rater-popup-hide').css({color:'red', 
		'float':'right'}).appendTo(rater.popup.box);
	rater.box.clear = function(){
		rater.box.html('')
		rater.box.append(rater.cancel_link)
			.append($('<h2>').text('Rating '+rater.page.name));
		rater.event.trigger('box-clear');
		return rater.box;
	};
	rater.popup_show=function(e){//note _ - avoid $ clash
		PD(e);
		rater.popup.box.stop(1,1).fadeIn(500);
		rater.popup.overlay.stop(1,1).fadeIn(500);
	};
	rater.popup_hide = function(e){
		PD(e);
		rater.popup.overlay.stop(1,1).fadeOut(500);
		rater.popup.box.stop(1,1).fadeOut(500);
	}; 
	$('body').on('click','a[href=#rater-popup-hide]',rater.popup_hide);
	rater.box.clear();
	rater.popup.clear=function(){
		rater.popup.box.html('').append(rater.popup.close_link);
	};
	rater.popup.clear();

	
	rater.cancel = function(e){
		PD(e);rater.show_link.removeClass('selected');
		$('body').css({overflow:'auto'});
		rater.overlay.stop(1,1).fadeOut(500);
		rater.box.stop(1,1).fadeOut(500);
	}; 
	$('body').on('click','a[href=#rater-cancel]',rater.cancel);
	
	// Set up link
	rater.show_link = $("<li>").append($('<span>').append(
		$("<a href='#rater-invoke'>").text('Rate')
	));
	$("#left-navigation #p-namespaces ul:nth(0)").append('<li><span><a></a></span></li>')
	$("#left-navigation #p-namespaces ul:nth(0)").append(rater.show_link)

	rater.invoke = function(e, force){
		PD(e);$('body').css({overflow:'hidden'});
		rater.overlay.stop(1,1).fadeIn(500);
		rater.box.stop(1,1).fadeIn(500);
		if(!rater.is_valid_page(wgPageName) && !force)
			return rater.error_invalid_page();
		rater.box.clear();rater.show_link.addClass('selected');
		rater.box.append('<p>Performing automatic tests (<span class="rater-tests-left"></span> remaining), please wait...</p>');
		rater.begin_tests();
	};
	$('body').on('click', 'a[href=#rater-invoke]', rater.invoke)
	$('body').on('click', 'a[href=#rater-force]', function(e){rater.invoke(e,1)});
	
	/*
	Tests
	
	Usage: Specify a URL to get data from and `processor` function to extract the
	needed data
	
	*/
	var tests={};
	tests.list={}; //list of all tests
	tests.results={}; //shortcut: data[x]==lists[x].data
	tests.random=Math.floor(Math.random()*1e10); 
	
	tests.num_waiting = 0;
	
	tests.add = function(name, data_url, processor){ 
		//Note: jQuery caches Ajax requests by default, no need to here
		tests.list[name] = {data_url:data_url, processor:processor};
		//Prevents caching for the first rating (reset on page load)
		data_url+=((data_url.indexOf('?')+1)?'&':'?')+'_='+tests.random;
		$.get(data_url, function(data){
			tests.process(name, data);
		});
		tests.num_waiting++
	};
	tests.process = function(name,raw_data){
		result = tests.list[name].processor(raw_data);
		tests.list[name].result = tests.results[name] = result
		tests.num_waiting--
		$(".rater-tests-left").text(tests.num_waiting)
		if(tests.num_waiting <= 0){
			setTimeout(tests.all_complete, 1); //async
		}
	};
	
	tests.all_callbacks=[]; //when all tests are done
	tests.all_complete = function(){
		for(i=0; i<tests.all_callbacks.length; i++){
			tests.all_callbacks[i]();
		}
	};
	tests.add_callback=function(func){
		tests.all_callbacks.push(func);
	};
	
	rater.help={view:$('<div>').css({height:'100%'})};
	rater.help.init=function(){
		$.get(wgScript+'/DF:Quality',function(d){
			d=$(d);
			rater.help.data=[];
			for(var i=0;i<=5;i++){
				rater.help.data[i]=d.find('h3:nth({0})'.format(i)).nextUntil('h3');
				rater.help.data[i].splice(0,0,d.find('h3:nth({0})'.format(i))[0]); // prepend header
			};
			rater.help.update();
		});	
	};
	rater.help.update=function(n){
		if(!rater.help.data)return;
		if(!(n+1)){
			try{
				n=rater.ratings[rater.select.current].id;
			} catch(e){return}
		}
		rater.help.view.text('').append(rater.help.data[n]);
		rater.help.view.find('.editsection').hide();
		//rater.box.scrollTop(rater.box.height());
	};
	
	
	rater.begin_tests = function(){
		render_url=rater.page.url+'&action=render'
		raw_url=rater.page.url+'&action=raw'
		rater.help.init()
		tests.add('redlinks', render_url, function(data){
			all_links = data.match(/<a .*<\/a>/g);
			if(!all_links) return 0; //no links
			total_redlinks=0;
			$.each(all_links, function(i,link){
				if(link.match(/href=.*redlink=1/)) total_redlinks++;
			});
			return total_redlinks
		});
		tests.add('editors', rater.page.url+'&action=history&limit=100', function(data){
			all_editors = data.match(/<li>.*<\/li>/g)
			if(!all_editors) return 0; //no editors
			editors={};
			$.each(all_editors, function(i,li){
				ed = $(li).find('.mw-userlink:nth(0)').text()
				if(!(ed in editors)) editors[ed]=0;
				editors[ed]++ 
			});
			num=0;
			for(i in editors){
				if(i in {}) continue;
				num++
			}
			editors.total=num;editors.total_edits=all_editors.length;
			return editors;
		});
		tests.add('linkshere', wgScript+'/Special:WhatLinksHere/'+wgPageName, function(data){
			return $(data).find('#mw-whatlinkshere-list').length;
		});
		tests.add('links', render_url, function(data){
			return $(data).find('a[href*="'+wgScript+'"]').length;
		});
		tests.add('length', raw_url, function(data){
			rater.raw_text=data;
			var a={
				'full':data.length,
				'nospace':data.replace(/\s/g,'').length,
				'notemplate':data.replace(/{{[^}]*?}}/g,'').length,
				'raw':data.replace(/\s/g,'').replace(/{{[^}]*?}}/g,'').length
			};
			a.average=Math.round(.1 * a.full + .2*a.nospace + .3*a.notemplate + .4*a.raw)
			return a
		});
		tests.add('html_length', render_url, function(data){
			var a={'html':data.length,'text':$(data).text().length};
			return a;
		});
		tests.add('verify',raw_url,function(data){
			var m=data.match(/{{verify/g);return +(m&&m.length);
		});
		tests.add('current_rating',raw_url,function(data){
			m=data.match(/{{quality[^}]*?}}/i);
			return (m&&m.length&&m[0].slice(2,-2).split('|')[1])||''
		});
		tests.add_callback(function(){
			rater.display_test_results();
		});
	};
	
	rater.score_bool=function(v,y,n){
		if(isNaN(Number(n))) n=-y;
		return Number(v?y:n);
	};
	rater.score_int=function(v,weight,base){
		if(!base) base=0;
		return v*weight+base;
	};
	rater.metadata = {
		'redlinks':{name:'Redlinks'},
		'links': {name:'Outbound links'},
		'linkshere': {name:'Incoming links'},
		'editors': {name:'Editor count',str:function(o){return o.total},
			int:function(o){return o.total;},
			info:function(o,view){
				tbl=$("<table>").css({width:'100%'}).append('<tr><th colspan="2">User</th><th>Edits</th></tr>').addClass('wikitable sortable').appendTo(view)
				for(i in o){
					if(i in {}||!i.indexOf('total')) continue;
					tbl.append('<tr><td colspan="2"><a href="{2}/User:{0}">{0}</a>:</td><td>{1}</td></tr>'.format(i,o[i],wgScript))
				}
				tbl.append('<tr style="font-weight:bold"><td>Total:</td><td>{0}</td><td>{1}</td></tr>'
					.format(o.total,o.total_edits))
			},
		},
		'length': {str:function(obj){
			return "Weighted: {0} ({1} characters, {2} without templates)"
				.format(obj.average,obj.full,obj.notemplate);},
		name:'Source length'},
		html_length:{name:'Text length',
			str:function(o){return '{0} (HTML: {1})'.format(o.text,o.html)},
			int:function(o){return o.text}},
		verify:{name:'{{Verify}} tags',score:function(o){
			if(o<1) return 0; if(o==1) return -10; return o*-20}},
		current_rating:{name:'Current rating', int:function(){return 0;}}
	};
	
	rater.ratings={
		tattered:{id:1,color:{b:'#333',bg:'#ccc',c:'#333'},s:'x'},
		fine:{id:2,color:{b:'#db8',bg:'#ffe0cc',c:'#ca7a02'},s:'+'},
		superior:{id:3,color:{b:'#b8f',bg:'#e4ccff',c:'#80c'},s:'*'},
		exceptional:{id:4,color:{b:'#9df',bg:'#cce4ff',c:'#08c'},s:'\u2261'},
		masterwork:{id:5,color:{b:'#bd8',bg:'#e2fdce',c:'#72a329'},s:'\u263c'}
	};
	
	rater.display_test_results=function(){
		var md=rater.metadata;
		rater.box.clear();
		data=tests.results;
		for(var i in data){
			name=md[i].name;
			str=is_func(md[i].str)?md[i].str(data[i]):data[i];
			app=''
			if(is_func(md[i].info)){
				app=$('<a href="#">[Info]</a>').data({f:md[i].info,d:data[i]})
				.click(function(e){d=$(this).data()
					rater.popup.clear();	rater.popup_show(e);
					d.f(d.d,rater.popup.box.append($("<div>")))
				}).css('padding-left','1em');
			}
			rater.box.append($("<p>"+name+": "+str+"</p>").append(app));
		}
		
		
		rater.score = 0
			+rater.score_bool(!data.links,-25) 		//orphaned
			+rater.score_bool(!data.linkshere,-30) 	//dead end
			+rater.score_int(data.links,0.5)
			+rater.score_int(data.linkshere,0.75)
			+rater.score_int(data.redlinks,-5,10)
			+rater.score_int(data.editors.total,20,-15)
			
		;
		rater.box.append($("<p>").text("Score: "+rater.score))
		rater.select.init($("<div>").appendTo(rater.box));
	};
	rater.select={};
	rater.select.view=$("<div>").css({padding:'.2em'});
	rater.select.init=function(){
		rater.select.current=rater.tests.results.current_rating.toLowerCase()||'tattered';
		rater.select.view.appendTo(rater.box);
		rater.select.draw();
	};
	rater.select.draw=function(){var c,selected,a,view=rater.select.view;
		view.text('\nDesired rating: ');
		list=$('<span>').css({'font-size':'.8em'}).appendTo(rater.select.view)
		for(i in rater.ratings){if(i in {}) continue;
			c=rater.ratings[i];
			a=$('<span>').appendTo(list).data('rating',i).attr({tabindex:0}).css({cursor:'pointer'}); //not real link
			a.append($('<span>').text(c.s+i.toUpperCase()+c.s)
				.css({'text-decoration':'none','color':c.color.c}));
			if(i==rater.select.current){
				selected=rater.ratings[i];
				a.find('span').css({'border-color':c.color.b,'background-color':c.color.bg,'border-width':1,'border-style':'solid','border-radius':2,padding:'.2em'})
			}
			a.on('click focus',rater.select.click);
			list.append(' ');
		}
		if(rater.is_valid_page()){
			rater.submit_link=$('<a>').attr({href:'#rater-submit'}).text("Submit").appendTo(view);
			view.append(' ');
		}
		rater.reset_link=$('<a>').attr({href:'#rater-reset'}).text("Reset").appendTo(view);
		rater.help.view.appendTo(view);
		rater.help.update(selected.id);
		//rater.box.scrollTop(rater.box.height());
	};
	
	rater.select.click=function(e){if(e.type=='click')PD(e);
		rater.select.current=$(this).data('rating');
		rater.select.draw();
	};
	
	rater.select.reset=function(e){PD(e);rater.select.init();};
	
	$('body').on('click','a[href=#rater-reset]',rater.select.reset);
	$('body').on('click','a[href=#rater-submit]',function(e){PD(e);
		rater.submit_rating();
	});
	
	rater.submit_rating=function(){
		rater.box.clear();stat=$('<pre>').appendTo(rater.box);
		function w(s){stat.append(s);}
		var r=rater.select.current;
		if(r in {}||!(r in rater.ratings)) return;
		var rating=rater.select.current.capitalize();
		if(!rater.raw_text) return;
		//get token
		w('Retrieving token... ');
		$.get(wgScriptPath+'/api.php?format=json&action=query&prop=info&indexpageids=1&intoken=edit&titles='+wgPageName,function(d){
			var token=d.query.pages[d.query.pageids[0]].edittoken;
			w('Ok ({0})\nReplacing quality template... '.format(token.slice(0,-2)));
			console.log(token);
			var text=rater.raw_text.replace(/{{quality[^}]*?}}/gi,'');
			text='{{Quality|'+rating+'|~~\u007e~~}}'+text
			//console.log(text)
			w('Ok\nEditing page... ')
			var e=encodeURIComponent;
			var page=rater.page.name;
			$.post(wgScriptPath+'/api.php', {action:'edit',title:rater.page.name,text:text,
			token:token,minor:1,summary:'Rated article "{0}" using the rating script.'.format(rating)},function(d){
				//console.log(d,'DONE')
				w('Finished!\nReloading...');
				$('body').load(rater.page.url + ' body',function(d){
					window.location.reload(); //maybe load w/ ajax?
				});
			});
		});
	};
	
	//check for a provided hash...
	if(window.location.hash.length){
		rater.auto_link=$('<a>').attr('href',window.location.hash).appendTo('body');
		setTimeout(function(){rater.auto_link.click()},100);//after this returns, anon for scope
	};
	
	//export
	rater.tests = tests;
	window.rater=rater
	return rater;
	
});});