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.2.js"

From Dwarf Fortress Wiki
Jump to navigation Jump to search
(Lethosor moved page User:Lethosor/rater.js to User:Lethosor/rater 0.1.js: Updating)
 
(New version)
Line 1: Line 1:
#REDIRECT [[User:Lethosor/rater 0.1.js]]
+
/* <nowiki>
 +
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>')
 +
rater.box.find('a:nth(1)').focus();
 +
}
 +
else if(!rater.page.exists){
 +
rater.box.clear().append('<p class="error">This page doesn\'t exist!</p><a href="#rater-cancel">Close this window</a>');rater.box.find('a:nth(1)').focus();
 +
}
 +
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','border-radius':4})
 +
.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','border-radius':4})
 +
.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(300);
 +
rater.popup.overlay.stop(1,1).fadeIn(300);
 +
};
 +
rater.popup_hide = function(e){
 +
PD(e);
 +
rater.popup.overlay.stop(1,1).fadeOut(300);
 +
rater.popup.box.stop(1,1).fadeOut(300);
 +
};
 +
$('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(300);
 +
rater.box.stop(1,1).fadeOut(300);
 +
};
 +
$('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(300);
 +
rater.box.stop(1,1).fadeIn(300);
 +
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)});
 +
 +
 +
/*
 +
Decriptions of URLs, tests, etc.
 +
*/
 +
rater.metadata={};
 +
rater.metadata.urls = {
 +
'raw':rater.page.url+'&action=raw',
 +
'render':rater.page.url+'&action=render',
 +
'whatlinkshere':wgScript+'/Special:WhatLinksHere/'+wgPageName,
 +
'history':rater.page.url+'&action=history&limit=100'
 +
};
 +
rater.metadata.tests = {
 +
redlinks:{
 +
name:'Redlinks',
 +
init:function(data){
 +
all_links = data.render.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
 +
}
 +
},
 +
links:{
 +
name:'Outbound links',
 +
init:function(data){
 +
return $(data.render).find('a[href*="'+wgScript+'"]').length;
 +
}
 +
},
 +
linkshere:{
 +
name:'Incoming links',
 +
init:function(data){
 +
return $(data.whatlinkshere).find('#mw-whatlinkshere-list').find('li').length;
 +
}
 +
},
 +
editors: {
 +
name:'Editor count',
 +
init:function(data){
 +
all_editors = data.history.match(/<li>.*<\/li>/g)
 +
if(!all_editors) return 0; //no editors
 +
var 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;
 +
},
 +
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',
 +
init:function(data){
 +
m=data.raw.match(/{{quality[^}]*?}}/i);
 +
return (m&&m.length&&m[0].slice(2,-2).split('|')[1])||'';
 +
},
 +
score: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'}
 +
};
 +
 +
/* Stores the results of tests */
 +
rater.tests={};
 +
 +
/*
 +
Loader
 +
Loads multiple URLs, with optional callbacks
 +
*/
 +
var loader={};
 +
loader.list={}; //list of all tests
 +
loader.results={}; //shortcut: data[x]==lists[x].data
 +
 +
loader.event=$({});
 +
loader.num_waiting = 0;
 +
loader.total_tests = 0;
 +
 +
loader.add = function(name,url){
 +
loader.list[name] = {url:url};
 +
$.get(url, function(data){
 +
loader.ready(name, data);
 +
});
 +
loader.num_waiting++
 +
loader.total_tests++
 +
};
 +
loader.ready = function(name,data){
 +
loader.list[name].result = loader.results[name] = data
 +
loader.num_waiting--
 +
loader.event.trigger('ready',{left:loader.num_waiting,total:loader.total_tests,name:name,data:data});
 +
if(loader.num_waiting <= 0){
 +
setTimeout(loader.all_complete, 1); //async
 +
}
 +
};
 +
 +
loader.all_complete = function(){
 +
loader.event.trigger('done');
 +
loader.total_tests=0; //reset
 +
};
 +
loader.add_callback=function(func){
 +
//Compatibility: triggered on `done` event
 +
loader.event.bind('done',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.progress={};
 +
rater.progress.view=$("<div>").css({width:'100%',padding:0,margin:0});
 +
 +
rater.progress.bar=$('<div>').css({width:'100%','background-color':'#fff',border:'1px solid #ac7',padding:3,'border-radius':2,overflow:'hidden'}).appendTo(rater.progress.view);
 +
 +
rater.progress.fill=$('<div>').css({float:'left',padding:0,margin:0,'background-color':'#ce9','border-right':'1px solid #ac7',height:'100%',width:0,margin:-3, position:'relative',top:0,left:0, 'border-radius':2}).appendTo(rater.progress.bar).html('&nbsp;');
 +
 +
rater.progress.update=function(done,total){
 +
var perc=done/Math.max(total,1)*100;
 +
rater.progress.fill.stop().animate({width:perc+'%'},150)
 +
};
 +
rater.progress.reset=function(){rater.progress.update(0,1);};
 +
 +
rater.begin_tests = function(){
 +
render_url=rater.page.url+'&action=render'
 +
raw_url=rater.page.url+'&action=raw'
 +
rater.help.init();
 +
for(i in rater.metadata.urls){if(i in {})continue;
 +
loader.add(i, rater.metadata.urls[i]);
 +
}
 +
loader.event.bind('ready',function(e,d){rater.progress.update(d.total-d.left,d.total)});
 +
rater.progress.view.appendTo(rater.box)
 +
loader.event.bind('done', rater.process_tests)
 +
loader.event.bind('done', rater.display_test_results)
 +
loader.event.bind('done', rater.progress.reset)
 +
/*
 +
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.tests={}
 +
 +
rater.process_tests=function(){
 +
var mdt=rater.metadata.tests;
 +
for(i in mdt){if(i in {})continue;
 +
if(is_func(mdt[i].init))
 +
rater.tests[i]=mdt[i].init(loader.results);
 +
}
 +
};
 +
 +
rater.display_test_results=function(){
 +
var md=rater.metadata.tests;
 +
rater.box.clear();
 +
data=rater.tests;
 +
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.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.loader.results.raw) 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.loader.results.raw.replace(/{{quality[^}]*?}}/gi,'');
 +
text='{{Quality|'+rating+'|~~~~~}}\n'+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.loader = loader;
 +
window.rater=rater
 +
return rater;
 +
 +
});});
 +
// </nowiki>

Revision as of 23:12, 27 April 2013

/* <nowiki>
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>')
			rater.box.find('a:nth(1)').focus();
		}
		else if(!rater.page.exists){
			rater.box.clear().append('<p class="error">This page doesn\'t exist!</p><a href="#rater-cancel">Close this window</a>');rater.box.find('a:nth(1)').focus();
		}
		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','border-radius':4})
		.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','border-radius':4})
		.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(300);
		rater.popup.overlay.stop(1,1).fadeIn(300);
	};
	rater.popup_hide = function(e){
		PD(e);
		rater.popup.overlay.stop(1,1).fadeOut(300);
		rater.popup.box.stop(1,1).fadeOut(300);
	}; 
	$('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(300);
		rater.box.stop(1,1).fadeOut(300);
	}; 
	$('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(300);
		rater.box.stop(1,1).fadeIn(300);
		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)});
	
	
	/*
	Decriptions of URLs, tests, etc.
	*/
	rater.metadata={};
	rater.metadata.urls = {
		'raw':rater.page.url+'&action=raw',
		'render':rater.page.url+'&action=render',
		'whatlinkshere':wgScript+'/Special:WhatLinksHere/'+wgPageName,
		'history':rater.page.url+'&action=history&limit=100'
	};
	rater.metadata.tests = {
		redlinks:{
			name:'Redlinks',
			init:function(data){
				all_links = data.render.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
			}
		},
		links:{
			name:'Outbound links',
			init:function(data){
				return $(data.render).find('a[href*="'+wgScript+'"]').length;
			}
		},
		linkshere:{
			name:'Incoming links',
			init:function(data){
				return $(data.whatlinkshere).find('#mw-whatlinkshere-list').find('li').length;
			}
		},
		editors: {
			name:'Editor count',
			init:function(data){
				all_editors = data.history.match(/<li>.*<\/li>/g)
				if(!all_editors) return 0; //no editors
				var 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;
			},
			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',
			init:function(data){
				m=data.raw.match(/{{quality[^}]*?}}/i);
				return (m&&m.length&&m[0].slice(2,-2).split('|')[1])||'';
			},
			score: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'}
	};
	
	/* Stores the results of tests */
	rater.tests={};
	
	/*
	Loader
	Loads multiple URLs, with optional callbacks 
	*/
	var loader={};
	loader.list={}; //list of all tests
	loader.results={}; //shortcut: data[x]==lists[x].data
	
	loader.event=$({});
	loader.num_waiting = 0;
	loader.total_tests = 0;
	
	loader.add = function(name,url){ 
		loader.list[name] = {url:url};
		$.get(url, function(data){
			loader.ready(name, data);
		});
		loader.num_waiting++
		loader.total_tests++
	};
	loader.ready = function(name,data){
		loader.list[name].result = loader.results[name] = data
		loader.num_waiting--
		loader.event.trigger('ready',{left:loader.num_waiting,total:loader.total_tests,name:name,data:data}); 
		if(loader.num_waiting <= 0){
			setTimeout(loader.all_complete, 1); //async
		}
	};
	
	loader.all_complete = function(){
		loader.event.trigger('done');
		loader.total_tests=0; //reset
	};
	loader.add_callback=function(func){
		//Compatibility: triggered on `done` event
		loader.event.bind('done',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.progress={};
	rater.progress.view=$("<div>").css({width:'100%',padding:0,margin:0});
	
	rater.progress.bar=$('<div>').css({width:'100%','background-color':'#fff',border:'1px solid #ac7',padding:3,'border-radius':2,overflow:'hidden'}).appendTo(rater.progress.view);
	
	rater.progress.fill=$('<div>').css({float:'left',padding:0,margin:0,'background-color':'#ce9','border-right':'1px solid #ac7',height:'100%',width:0,margin:-3, position:'relative',top:0,left:0, 'border-radius':2}).appendTo(rater.progress.bar).html('&nbsp;');
		
	rater.progress.update=function(done,total){
		var perc=done/Math.max(total,1)*100;
		rater.progress.fill.stop().animate({width:perc+'%'},150)
	};
	rater.progress.reset=function(){rater.progress.update(0,1);};
	
	rater.begin_tests = function(){
		render_url=rater.page.url+'&action=render'
		raw_url=rater.page.url+'&action=raw'
		rater.help.init();
		for(i in rater.metadata.urls){if(i in {})continue;
			loader.add(i, rater.metadata.urls[i]);
		}
		loader.event.bind('ready',function(e,d){rater.progress.update(d.total-d.left,d.total)});
		rater.progress.view.appendTo(rater.box)
		loader.event.bind('done', rater.process_tests)
		loader.event.bind('done', rater.display_test_results)
		loader.event.bind('done', rater.progress.reset)
		/*
		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.tests={}
	
	rater.process_tests=function(){
		var mdt=rater.metadata.tests;
		for(i in mdt){if(i in {})continue;
			if(is_func(mdt[i].init))
				rater.tests[i]=mdt[i].init(loader.results);
		}
	};
	
	rater.display_test_results=function(){
		var md=rater.metadata.tests;
		rater.box.clear();
		data=rater.tests;
		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.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.loader.results.raw) 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.loader.results.raw.replace(/{{quality[^}]*?}}/gi,'');
			text='{{Quality|'+rating+'|~~~~~}}\n'+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.loader = loader;
	window.rater=rater
	return rater;
	
});});
// </nowiki>