User:East718/twinkle.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// highly modified version of [[WP:TWINKLE]] by [[User:AzaToth]]
Mvaluejs_class = 'yes';
Rvaluejs_class = 'yes';
Uvaluejs_class = 'yes';
wpMaxLag = 60;
TwinkleConfig = {
        userTalkPageMode                :       'blank',
        showSharedIPNotice              :       false,
        openTalkPage                    :       [ ],
        summaryAd                       :       "",
        deletionSummaryAd               :       "",
        protectionSummaryAd             :       "",
        watchSpeedyPages                :       [ ],
        watchProdPages                  :       false,
        openUserTalkPageOnSpeedyDelete  :       [ ],
        watchRevertedPages              :       [ ],
        watchWarnings                   :       false,
        markRevertedPagesAsMinor        :       [ 'agf', 'norm', 'vand', 'torev' ],
        toolboxButtons                  :       [ 'tw-since', 'tw-sincemine' ],
        deleteTalkPageOnDelete          :       true,
        orphanBacklinksOnSpeedyDelete   :       false
};

// extra shit
var SHITTYHACK = document.location.toString().indexOf('lollercaust=roflcopter') != -1 ? true : false;
var JUDAICAHACK = ""
function bootlegasspopup()
{
	var brokenjavascript = wgTitle;
	if (document.location.toString().indexOf('sprite=easterpink') != -1)
	{
		brokenjavascript = prompt('AfD name?', '');
	}
	return '[[Wikipedia:Articles for deletion/' + brokenjavascript + ']]';
}
function kajd()
{
	var i4 = '[[WP:CSD#I4|CSD I4]]: Image lacking sources or licensing information for more than seven days';
	var i5 = '[[WP:CSD#I5|CSD I5]]: [[Free content|Non-free]] image that was not used for more than seven days';
	var i6 = '[[WP:CSD#I6|CSD I6]]: No [[WP:FURG|justification]] given for fair use for more than seven days';
	var i7 = '[[WP:CSD#I7|CSD I7]]: Bad [[WP:FURG|justification]] given for fair use and the uploader was notified more than 48 hours ago';
	var breakin = false;
	var breakout = false;
	var t = wgTitle;
	if (t.indexOf('Images with unknown source as of ') != -1 || t.indexOf('Images with no copyright tag as of ') != -1 || t.indexOf('Images with no copyright tag as of ') != -1 || t.indexOf('Images with unknown copyright status as of ') != -1)
	{
		JUDAICAHACK = i4;
		breakin = true;
	}
	if (t.indexOf('Orphaned fairuse images as of ') != -1)
	{
		JUDAICAHACK = i5;
		breakin = true;
	}
	if (t.indexOf('Images with no fair use rationale as of ') != -1)
	{
		JUDAICAHACK = i6;
		breakin = true;
	}
	if (t.indexOf('Disputed non-free images as of ') != -1 || t.indexOf('Replaceable fair use to be decided after ') != -1)
	{
		JUDAICAHACK = i7;
		breakin = true;
	}
	if (!breakin)
	{
		while (!breakout)
		{
			var p = prompt('i4/i5/i6/i7', '');
			if (p == 'i4' || p == 'i5' || p == 'i6' || p == 'i7')
			{
				breakout = true;
			}
			else
			{
				JUDAICAHACK = p;
				breakout = true;
			}
		}
		if (p == 'i4') { JUDAICAHACK = i4; }
		if (p == 'i5') { JUDAICAHACK = i5; }
		if (p == 'i6') { JUDAICAHACK = i6; }
		if (p == 'i7') { JUDAICAHACK = i7; }
	}
	twinkleimagetraverse.callback()
}

// morebits.js
// (C) Andrea Giammarchi - JSL 1.4b
var undefined;
function $JSL(){
	this.inArray=function(){
		var tmp=false,i=arguments[1].length;
		while(i&&!tmp)tmp=arguments[1][--i]===arguments[0];
		return tmp;
	};
	this.has=function(str){return $JSL.inArray(str,$has)};
	this.random=function(elm){
		var tmp=$JSL.$random();
		while(typeof(elm[tmp])!=="undefined")tmp=$JSL.$random();
		return tmp;
	};
	this.$random=function(){return (Math.random()*1234567890).toString()};
	this.reverse=function(str){return str.split("").reverse().join("")};
	this.replace=function(str){
		var tmp=str.split(""),i=tmp.length;
		while(i>0)tmp[--i]=$JSL.$replace(tmp[i]);
		return tmp.join("");
	};
	this.$replace=function(tmp){
		var i=tmp.length===1?tmp.charCodeAt(0):0;
		switch(i) {
			case 8	:tmp="\\b";break;
			case 10	:tmp="\\n";break;
			case 11	:tmp="\\v";break;
			case 12	:tmp="\\f";break;
			case 13	:tmp="\\r";break;
			case 34	:tmp="\\\"";break;
			case 92	:tmp="\\\\";break;
			default:
				tmp=tmp.replace(/([\x00-\x07]|[\x0E-\x1F]|[\x7F-\xFF])/g,function(a,b){return "\\x"+$JSL.charCodeAt(b)}).
					replace(/([\u0100-\uFFFF])/g,function(a,b){b=$JSL.charCodeAt(b);return b.length<4?"\\u0"+b:"\\u"+b});
				break;
		};
		return tmp;
	};
	this.charCodeAt=function(str){return $JSL.$charCodeAt(str.charCodeAt(0))};
	this.$charCodeAt=function(i){
		var str=i.toString(16).toUpperCase();
		return str.length<2?"0"+str:str;
	};
	this.$toSource=function(elm){return elm.toSource().replace(/^(\(new \w+\()([^\000]+)(\)\))$/,"$2")};
	this.$toInternalSource=function(elm){
		var tmp=null;
		switch(elm.constructor) {
			case Boolean:
			case Number:
				tmp=elm;
				break;
			case String:
				tmp=$JSL.$toSource(elm);
				break;
			default:
				tmp=elm.toSource();
				break;
		};
		return tmp;
	};
	this.getElementsByTagName=function(scope,i,elm,str){
		var tmp=$JSL.$getElementsByTagName(scope),j=tmp.length,$tmp=[];
		while(i<j){if(tmp[i][str]===elm||elm==="*")$tmp.push($JSL.$getElementsByName(tmp[i]));++i};
		if(!$tmp.item){if(!$JSL.has("item"))$has.push("item");$tmp.item=function(tmp){return this[tmp]}};
		return $tmp;
	};
	this.$getElementsByTagName=function(scope){return scope.layers||scope.all};
	this.$getElementsByName=function(elm) {
		if(!elm.getElementsByTagName)	elm.getElementsByTagName=document.getElementsByTagName;
		return elm;
	};
	this.encodeURI=function(str){return str.replace(/"/g,"%22").replace(/\\/g,"%5C")};
	this.$encodeURI=function(str){return $JSL.$charCodeAt(str)};
	this.$encodeURIComponent=function(a,b){
		var i=b.charCodeAt(0),str=[];
		if(i<128)		str.push(i);
		else if(i<2048)		str.push(0xC0+(i>>6),0x80+(i&0x3F));
		else if(i<65536)	str.push(0xE0+(i>>12),0x80+(i>>6&0x3F),0x80+(i&0x3F));
		else			str.push(0xF0+(i>>18),0x80+(i>>12&0x3F),0x80+(i>>6&0x3F),0x80+(i&0x3F));
		return "%"+str.map($JSL.$encodeURI).join("%");
	};
	this.$decodeURIComponent=function(a,b,c,d,e){
		var i=0;
		if(e)	  i=parseInt(e.substr(1,2),16);
		else if(d)i=((parseInt(d.substr(1,2),16)-0xC0)<<6)+(parseInt(d.substr(4,2),16)-0x80);
		else if(c)i=((parseInt(c.substr(1,2),16)-0xE0)<<12)+((parseInt(c.substr(4,2),16)-0x80)<<6)+(parseInt(c.substr(7,2),16)-0x80);
		else	  i=((parseInt(b.substr(1,2),16)-0xF0)<<18)+((parseInt(b.substr(4,2),16)-0x80)<<12)+((parseInt(b.substr(7,2),16)-0x80)<<6)+(parseInt(b.substr(10,2),16)-0x80);
		return String.fromCharCode(i);
	};
	var $has=[];
	if(!Function.prototype.apply){$has[$has.length]="apply";Function.prototype.apply=function(){
		var i=arguments.length===2?arguments[1].length:0,str,tmp=[],elm=(""+this).replace(/[^\(]+/,"function");
		if(!arguments[0])arguments[0]={};
		while(i)tmp.unshift("arguments[1]["+(--i)+"]");
		do{str="__".concat($JSL.random(arguments[0]).replace(/\./,"_"),"__")}while(new RegExp(str).test(elm));
		eval("var ".concat(str,"=arguments[0];tmp=(",elm.replace(/([^$])\bthis\b([^$])/g,"$1".concat(str,"$2")),")(",tmp.join(","),")"));
		return tmp;
	}};
	if(!Function.prototype.call){$has[$has.length]="call";Function.prototype.call=function(){
		var i=arguments.length,tmp=[];
		while(i>1)tmp.unshift(arguments[--i]);
		return this.apply((i?arguments[0]:{}),tmp);
	}};
	if(!Array.prototype.pop){$has[$has.length]="pop";Array.prototype.pop=function(){
		var a=this.length,r=this[--a];
		if(a>=0)this.length=a;
		return r;
	}};
	if(!Array.prototype.push){$has[$has.length]="push";Array.prototype.push=function(){
		var a=0,b=arguments.length,r=this.length;
		while(a<b)this[r++]=arguments[a++];
		return r;
	}};
	if(!Array.prototype.shift){$has[$has.length]="shift";Array.prototype.shift=function(){
		this.reverse();
		var r=this.pop();
		this.reverse();
		return r;
	}};
	if(!Array.prototype.splice){$has[$has.length]="splice";Array.prototype.splice=function(){
		var a,b,c,d=arguments.length,tmp=[],r=[];
		if(d>1){
			arguments[0]=parseInt(arguments[0]);
			arguments[1]=parseInt(arguments[1]);
			c=arguments[0]+arguments[1];
			for(a=0,b=this.length;a<b;a++){
				if(a<arguments[0]||a>=c){
					if(a===c&&d>2){
						for(a=2;a<d;a++)tmp.push(arguments[a]);
						a=c;
					};
					tmp.push(this[a]);
				}
				else
					r.push(this[a]);
			};
			for(a=0,b=tmp.length;a<b;a++)
				this[a]=tmp[a];
			this.length = a;
		};
		return r;
	}};
	if(!Array.prototype.unshift){$has[$has.length]="unshift";Array.prototype.unshift=function(){
		var i=arguments.length;
		this.reverse();
		while(i>0)this.push(arguments[--i]);
		this.reverse();
		return this.length;
	}};
	if(!Array.prototype.indexOf){$has[$has.length]="indexOf";Array.prototype.indexOf=function(elm,i){
		var j=this.length;
		if(!i)i=0;
		if(i>=0){while(i<j){if(this[i++]===elm){
			i=i-1+j;j=i-j;
		}}}
		else
			j=this.indexOf(elm,j+i);
		return j!==this.length?j:-1;
	}};
	if(!Array.prototype.lastIndexOf){$has[$has.length]="lastIndexOf";Array.prototype.lastIndexOf=function(elm,i){
		var j=-1;
		if(!i)i=this.length;
		if(i>=0){do{if(this[i--]===elm){
			j=i+1;i=0;
		}}while(i>0)}
		else if(i>-this.length)
			j=this.lastIndexOf(elm,this.length+i);
		return j;
	}};
	if(!Array.prototype.every){$has[$has.length]="every";Array.prototype.every=function(callback,elm){
		var b=false,i=0,j=this.length;
		if(!elm){	while(i<j&&!b)	b=!callback(this[i]||this.charAt(i),i++,this)}
		else {		while(i<j&&!b)	b=!callback.apply(elm,[this[i]||this.charAt(i),i++,this]);}
		return !b;
	}};
	if(!Array.prototype.filter){$has[$has.length]="filter";Array.prototype.filter=function(callback,elm){
		var r=[],i=0,j=this.length;
		if(!elm){while(i<j){if(callback(this[i],i++,this))
			r.push(this[i-1]);
		}} else {while(i<j){if(callback.apply(elm,[this[i],i++,this]))
			r.push(this[i-1]);
		}}
		return r;
	}};
	if(!Array.prototype.forEach){$has[$has.length]="forEach";Array.prototype.forEach=function(callback,elm){
		var i=0,j=this.length;
		if(!elm){	while(i<j)	callback(this[i],i++,this)}
		else {		while(i<j)	callback.apply(elm,[this[i],i++,this]);}
	}};
	if(!Array.prototype.map){$has[$has.length]="map";Array.prototype.map=function(callback,elm){
		var r=[],i=0,j=this.length;
		if(!elm){	while(i<j)	r.push(callback(this[i],i++,this))}
		else {		while(i<j)	r.push(callback.apply(elm,[this[i],i++,this]));}
		return r;
	}};
	if(!Array.prototype.some){$has[$has.length]="some";Array.prototype.some=function(callback,elm){
		var b=false,i=0,j=this.length;
		if(!elm){	while(i<j&&!b)	b=callback(this[i],i++,this)}
		else {		while(i<j&&!b)	b=callback.apply(elm,[this[i],i++,this]);}
		return b;
	}};
	if(!String.prototype.lastIndexOf){if(!this.inArray("lastIndexOf",$has))$has[$has.length]="lastIndexOf";String.prototype.lastIndexOf=function(elm,i){
		var str=$JSL.reverse(this),elm=$JSL.reverse(elm),r=str.indexOf(elm,i);
		return r<0?r:this.length-r;
	}};
	if("aa".replace(/\w/g,function(){return arguments[1]+" "})!=="0 1 "){$has[$has.length]="replace";String.prototype.replace=function(replace){return function(reg,func){
		var r="",tmp=$JSL.random(String);
		String.prototype[tmp]=replace;
		if(func.constructor!==Function)
			r=this[tmp](reg,func);
		else {
			function getMatches(reg,pos,a) {
				function io() {
					var a=reg.indexOf("(",pos),b=a;
					while(a>0&&reg.charAt(--a)==="\\"){};
					pos=b!==-1?b+1:b;
					return (b-a)%2===1?1:0;
				};
				do{a+=io()}while(pos!==-1);
				return a;
			};
			function $replace(str){
				var j=str.length-1;
				while(j>0)str[--j]='"'+str[j].substr(1,str[j--].length-2)[tmp](/(\\|")/g,'\\$1')+'"';
				return str.join("");
			};
			var p=-1,i=getMatches(""+reg,0,0),args=[],$match=this.match(reg),elm=$JSL.$random()[tmp](/\./,'_AG_');
			while(this.indexOf(elm)!==-1)elm=$JSL.$random()[tmp](/\./,'_AG_');
			while(i)args[--i]=[elm,'"$',(i+1),'"',elm].join("");
			if(!args.length)r="$match[i],(p=this.indexOf($match[i++],p+1)),this";
			else		r="$match[i],"+args.join(",")+",(p=this.indexOf($match[i++],p+1)),this";
			r=eval('['+$replace((elm+('"'+this[tmp](reg,'"'+elm+',func('+r+'),'+elm+'"')+'"')+elm).split(elm))[tmp](/\n/g,'\\n')[tmp](/\r/g,'\\r')+'].join("")');
		};
		delete String.prototype[tmp];
		return r;
	}}(String.prototype.replace)};
	if((new Date().getYear()).toString().length===4){$has[$has.length]="getYear";Date.prototype.getYear=function(){
		return this.getFullYear()-1900;
	}};
};$JSL=new $JSL();
if(typeof(encodeURI)==="undefined"){function encodeURI(str){
	var elm=/([\x00-\x20]|[\x25|\x3C|\x3E|\x5B|\x5D|\x5E|\x60|\x7F]|[\x7B-\x7D]|[\x80-\uFFFF])/g;
	return $JSL.encodeURI(str.toString().replace(elm,$JSL.$encodeURIComponent));
}};
if(typeof(encodeURIComponent)==="undefined"){function encodeURIComponent(str){
	var elm=/([\x23|\x24|\x26|\x2B|\x2C|\x2F|\x3A|\x3B|\x3D|\x3F|\x40])/g;
	return $JSL.encodeURI(encodeURI(str).replace(elm,function(a,b){return "%"+$JSL.charCodeAt(b)}));
}};
if(typeof(decodeURIComponent)==="undefined"){function decodeURIComponent(str){
	var elm=/(%F[0-9A-F]%E[0-9A-F]%[A-B][0-9A-F]%[8-9A-B][0-9A-F])|(%E[0-9A-F]%[A-B][0-9A-F]%[8-9A-B][0-9A-F])|(%[C-D][0-9A-F]%[8-9A-B][0-9A-F])|(%[0-9A-F]{2})/g;
	return str.toString().replace(elm,$JSL.$decodeURIComponent);
}};
if(typeof(decodeURI)==="undefined"){function decodeURI(str){
	return decodeURIComponent(str);
}};
if(!document.getElementById){document.getElementById=function(elm){
	return $JSL.$getElementsByName($JSL.$getElementsByTagName(this)[elm]);
}};
if(!document.getElementsByTagName){document.getElementsByTagName=function(elm){
	return $JSL.getElementsByTagName(this,0,elm.toUpperCase(),"tagName");
}};
if(!document.getElementsByName){document.getElementsByName=function(elm){
	return $JSL.getElementsByTagName(this,0,elm,"name");
}};
if(typeof(XMLHttpRequest)==="undefined"){XMLHttpRequest=function(){
	var tmp=null,elm=navigator.userAgent;
	if(elm.toUpperCase().indexOf("MSIE 4")<0&&window.ActiveXObject)
		tmp=elm.indexOf("MSIE 5")<0?new ActiveXObject("Msxml2.XMLHTTP"):new ActiveXObject("Microsoft.XMLHTTP");
	return tmp;
}};
if(typeof(Error)==="undefined")Error=function(){};
Error = function(base){return function(message){
	var tmp=new base();
	tmp.message=message||"";
	if(!tmp.fileName)
		tmp.fileName=document.location.href;
	if(!tmp.lineNumber)
		tmp.lineNumber=0;
	if(!tmp.stack)
		tmp.stack="Error()@:0\n(\""+this.message+"\")@"+tmp.fileName+":"+this.lineNumber+"\n@"+tmp.fileName+":"+this.lineNumber;
	if(!tmp.name)
		tmp.name="Error";
	return tmp;
}}(Error);


QuickForm = function QuickForm( event, eventType ) {

	this.root = new QuickForm.element( { type: 'form', event: event, eventType:eventType } );

	var cssNode = document.createElement('style');
	cssNode.type = 'text/css';
	cssNode.rel = 'stylesheet';
	cssNode.appendChild( document.createTextNode("")); // Safari bugfix
	document.getElementsByTagName("head")[0].appendChild(cssNode);
	var styles = cssNode.sheet ? cssNode.sheet : cssNode.stylesSheet;
	styles.insertRule("form.quickform { width: 96%; margin:auto; padding: .5em; vertical-align: middle}", 0);
	styles.insertRule("form.quickform * { font-family: sans-serif; vertical-align: middle}", 0);
	styles.insertRule("form.quickform select { width: 30em; border: 1px solid gray; font-size: 1.1em}", 0);
	styles.insertRule("form.quickform h5 { border-top: 1px solid gray;}", 0);
	styles.insertRule("form.quickform textarea { width: 100%; height: 6em }", 0);
	styles.insertRule("form.quickform .tooltipButtonContainer { position: relative; width: 100%; }", 0);
	styles.insertRule("form.quickform .tooltipButton { padding: .2em; color: blue; font-weight: bold; cursor:help;}", 0);
	styles.insertRule(".quickformtooltip { z-index: 200; position: absolute; padding: .1em; border: 1px dotted red; background-color: Linen; font: caption; font-size: 10pt; max-width: 800px}", 0);
}

QuickForm.prototype.render = function QuickFormRender() {
	return this.root.render();
}
QuickForm.prototype.append = function QuickFormAppend( data ) {
	return this.root.append( data );
}

QuickForm.element = function QuickFormElement( data ) {
	this.data = data;
	this.childs = [];
	this.id = QuickForm.element.id++;
}

QuickForm.element.id = 0;

QuickForm.element.prototype.append = function QuickFormElementAppend( data ) {
	if( data instanceof QuickForm.element ) {
		var child = data;
	} else {
		var child = new QuickForm.element( data );
	}
	this.childs.push( child );
	return child;
}

QuickForm.element.prototype.render = function QuickFormElementRender() {
	var currentNode = this.compute( this.data );

	for( var i = 0; i < this.childs.length; ++i ) {
		currentNode[1].appendChild( this.childs[i].render() );
	}
	return currentNode[0];
}

QuickForm.element.prototype.compute = function QuickFormElementCompute( data ) {
	var node;
	var childContainder = null;
	var label;
	var id = 'node_' + this.id;
	if( data.adminonly && !userIsInGroup( 'sysop' ) ) {
		// hell hack alpha
		data.type = hidden;
	}
	switch( data.type ) {
	case 'form':
		node = document.createElement( 'form' );
		node.setAttribute( 'name', 'id' );
		node.className = "quickform";
		node.setAttribute( 'action', 'javascript:void(0);');
		if( data.event ) {
			node.addEventListener( data.eventType || 'submit', data.event , false );
		}
		break;
	case 'select':
		node = document.createElement( 'div' );

		node.setAttribute( 'id', 'div_' + id );
		if( data.label ) {
			label = node.appendChild( document.createElement( 'label' ) );
			label.setAttribute( 'for', id );
			label.appendChild( document.createTextNode( data.label ) );
		}
		var select = node.appendChild( document.createElement( 'select' ) );
		if( data.event ) {
			select.addEventListener( 'change', data.event, false );
		}
		if( data.multiple ) {
			select.setAttribute( 'multiple', 'multiple' );
		}
		if( data.size ) {
			select.setAttribute( 'size', data.size );
		}
		select.setAttribute( 'name', data.name );

		if( data.list ) {
			for( var i = 0; i < data.list.length; ++i ) {

				var current = data.list[i];

				if( current.list ) {
					current.type = 'optgroup';
				} else {
					current.type = 'option';
				}

				var res = this.compute( current );
				select.appendChild( res[0] );
			}
		}
		childContainder = select;
		break;
	case 'option':
		node = document.createElement( 'option' );
		node.setAttribute( 'value', data.value );
		if( data.selected ) {
			node.setAttribute( 'selected', 'selected' );
		}
		if( data.disabled ) {
			node.setAttribute( 'disabled', 'disabled' );
		}
		node.setAttribute( 'label', data.label );
		node.appendChild( document.createTextNode( data.label ) );
		break;
	case 'optgroup':
		node = document.createElement( 'optgroup' );
		node.setAttribute( 'label', data.label );

		if( data.list ) {
			for( var i = 0; i < data.list.length; ++i ) {

				var current = data.list[i];

				current.type = 'option'; //must be options here

				var res = this.compute( current );
				node.appendChild( res[0] );
			}
		}
		break;
	case 'field':
		node = document.createElement( 'fieldset' );
		label = node.appendChild( document.createElement( 'legend' ) );
		label.appendChild( document.createTextNode( data.label ) );
		if( data.name ) {
			node.setAttribute( 'name', data.name );
		}
		break;
	case 'checkbox':
	case 'radio':
		node = document.createElement( 'div' );
		if( data.list ) {
			for( var i = 0; i < data.list.length; ++i ) {
				var cur_id = id + '_' + i;
				var current = data.list[i];
				cur_node = node.appendChild( document.createElement( 'div' ) );
				var input = cur_node.appendChild( document.createElement( 'input' ) );
				input.setAttribute( 'value', current.value );
				input.setAttribute( 'name', current.name || data.name );
				input.setAttribute( 'type', data.type );
				input.setAttribute( 'id', cur_id );
				if( current.checked ) {
					input.setAttribute( 'checked', 'checked' );
				}
				if( current.disabled ) {
					input.setAttribute( 'disabled', 'disabled' );
				}
				if( data.event ) {
					input.addEventListener( 'change', data.event, false );
				} else if ( current.event ) {
					input.addEventListener( 'change', current.event, true );
				}
				var label = cur_node.appendChild( document.createElement( 'label' ) );
				label.appendChild( document.createTextNode( current.label ) );
				label.setAttribute( 'for', cur_id );
				if( current.tooltip ) {
					QuickForm.element.generateTooltip( label, current );
				}
			}
		}
		break;
	case 'input':
		node = document.createElement( 'div' );

		if( data.label ) {
			label = node.appendChild( document.createElement( 'label' ) );
			label.appendChild( document.createTextNode( data.label ) );
			label.setAttribute( 'for', id );
		}

		var input = node.appendChild( document.createElement( 'input' ) );
		if( data.value ) {
			input.setAttribute( 'value', data.value );
		}
		input.setAttribute( 'name', data.name );
		input.setAttribute( 'type', 'text' );
		if( data.size ) {
			input.setAttribute( 'size', data.size );
		}
		if( data.disabled ) {
			input.setAttribute( 'disabled', 'disabled' );
		}
		if( data.readonly ) {
			input.setAttribute( 'readonly', 'readonly' );
		}
		if( data.maxlength ) {
			input.setAttribute( 'maxlength', data.maxlength );
		}
		if( data.event ) {
			input.addEventListener( 'keyup', data.event, false );
		}
		break;
	case 'hidden':
		var node = document.createElement( 'input' );
		node.setAttribute( 'type', 'hidden' );
		node.setAttribute( 'value', data.value );
		node.setAttribute( 'name', data.name );
		break;
	case 'header':
		node = document.createElement( 'h5' );
		node.appendChild( document.createTextNode( data.label ) );
		break;
	case 'div':
		node = document.createElement( 'div' );
		break;
	case 'submit':
		node = document.createElement( 'span' );
		childContainder = node.appendChild(document.createElement( 'input' ));
		childContainder.setAttribute( 'type', 'submit' );
		if( data.label ) {
			childContainder.setAttribute( 'value', data.label );
		}
		childContainder.setAttribute( 'name', data.name || 'submit' );
		if( data.disabled ) {
			childContainder.setAttribute( 'disabled', 'disabled' );
		}
		break;
	case 'button':
		node = document.createElement( 'span' );
		childContainder = node.appendChild(document.createElement( 'input' ));
		childContainder.setAttribute( 'type', 'button' );
		if( data.label ) {
			childContainder.setAttribute( 'value', data.label );
		}
		childContainder.setAttribute( 'name', data.name );
		if( data.disabled ) {
			childContainder.setAttribute( 'disabled', 'disabled' );
		}
		if( data.event ) {
			childContainder.addEventListener( 'click', data.event, false );
		}
		break;
	case 'textarea':
		node = document.createElement( 'div' );
		if( data.label ) {
			label = node.appendChild( document.createElement( 'label' ) );
			label.appendChild( document.createTextNode( data.label ) );
			label.setAttribute( 'for', id );
		}
		node.appendChild( document.createElement( 'br' ) );
		textarea = node.appendChild( document.createElement( 'textarea' ) );
		textarea.setAttribute( 'name', data.name );
		if( data.cols ) {
			textarea.setAttribute( 'cols', data.cols );
		}
		if( data.rows ) {
			textarea.setAttribute( 'rows', data.rows );
		}
		if( data.disabled ) {
			textarea.setAttribute( 'disabled', 'disabled' );
		}
		if( data.readonly ) {
			textarea.setAttribute( 'readonly', 'readonly' );
		}
		break;

	}

	if( childContainder == null ) {
		childContainder = node;
	} 
	if( data.tooltip ) {
		QuickForm.element.generateTooltip( label || node , data );
	}

	if( data.extra ) {
		childContainder.extra = extra;
	}
	childContainder.setAttribute( 'id', data.id || id );

	return [ node, childContainder ];
}

QuickForm.element.generateTooltip = function QuickFormElementGenerateTooltip( node, data ) {
		var tooltipButtonContainer = node.appendChild( document.createElement( 'span' ) );
		tooltipButtonContainer.className = 'tooltipButtonContainer';
		var tooltipButton = tooltipButtonContainer.appendChild( document.createElement( 'span' ) );
		tooltipButton.className = 'tooltipButton';
		tooltipButton.appendChild( document.createTextNode( '?' ) );
		var tooltip = document.createElement( 'div' );
		tooltip.className = 'quickformtooltip';
		tooltip.appendChild( document.createTextNode( data.tooltip ) );
		tooltipButton.tooltip = tooltip;
		tooltipButton.showing = false;
		tooltipButton.interval = null;
		tooltipButton.addEventListener( 'mouseover', QuickForm.element.generateTooltip.display, false );
		tooltipButton.addEventListener( 'mouseout', QuickForm.element.generateTooltip.fade, false );

}
QuickForm.element.generateTooltip.display = function QuickFormElementGenerateTooltipDisplay(e) {
	window.clearInterval( e.target.interval );
	e.target.tooltip.style.setProperty( '-moz-opacity', 1, null);
	e.target.tooltip.style.setProperty( 'opacity', 1, null);
	e.target.tooltip.style.left = (e.pageX - e.layerX + 24) + "px";
	e.target.tooltip.style.top = (e.pageY - e.layerY + 12) + "px";
	document.body.appendChild( e.target.tooltip );
	e.target.showing = true;
}

QuickForm.element.generateTooltip.fade = function QuickFormElementGenerateTooltipFade( e ) {
	e.target.opacity = 1.2;
	e.target.interval  = window.setInterval(function(e){
			e.target.tooltip.style.setProperty( '-moz-opacity', e.target.opacity, null);
			e.target.tooltip.style.setProperty( 'opacity', e.target.opacity, null);
			e.target.opacity -= 0.1;
			if( e.target.opacity <= 0 ) {
				window.clearInterval( e.target.interval );
				document.body.removeChild( e.target.tooltip );e.target.showing = false;
			}
		},50,e);
}

/**
* Will escape a string to be used in a RegExp
*/
RegExp.escape = function( text, space_fix ) {

	if ( !arguments.callee.sRE ) {
		arguments.callee.sRE = /(\/|\.|\*|\+|\?|\||\(|\)|\[|\]|\{|\}|\\|\$|\^)/g;
	}

	text = text.replace( arguments.callee.sRE , '\\$1' );

	// Special Mediawiki escape, underscore/space is the same, often at lest:

	if( space_fix ) {
		text = text.replace( / |_/g, '[_ ]' );
	}

	return text;

}

// Sprintf implementation based on perl similar
function sprintf() {
	if( arguments.length == 0 ) {
		throw "Not enough arguments for sprintf";
	}
	var result = "";
	var format = arguments[0];

	var index = 1;
	var current_index = 1;
	var flags = {};
	var in_operator = false;
	var relative = false;
	var precision = false;
	var fixed = false;
	var vector = false;
	var vector_delimiter = '.';


	for( var i = 0; i < format.length; ++i ) {
		var current_char = format.charAt(i);
		if( in_operator ) {
			switch( current_char ) {
			case 'i':
				current_char = 'd';
				break;
			case 'F':
				current_char = 'f';
				break;
			case '%':
			case 'c':
			case 's':
			case 'd':
			case 'u':
			case 'o':
			case 'x':
			case 'e':
			case 'f':
			case 'g':
			case 'X':
			case 'E':
			case 'G':
			case 'b':
				var value = arguments[current_index];
				if( vector ) {
					r = value.toString().split( '' );
					result += value.toString().split('').map( function( value ) {
							return sprintf.format( current_char, value.charCodeAt(), flags );
						}).join( vector_delimiter );
				} else {
					result += sprintf.format( current_char, value, flags );
				}
				if( !fixed ) {
					++index;
				}
				current_index = index;
				flags = {};
				relative = false;
				in_operator = false;
				precision = false;
				fixed = false;
				vector = false;
				vector_delimiter = '.';
				break;
			case 'v':
				vector = true;
				break;
			case ' ':
			case '0':
			case '-':
			case '+':
			case '#':
				flags[current_char] = true;
				break;
			case '*':
				relative = true;
				break;
			case '.':
				precision = true;
				break;
			}
			if( /\d/.test( current_char ) ) {
				var num = parseInt( format.substr( i ) );
				var len = num.toString().length;
				i += len - 1;
				var next = format.charAt( i  + 1 );
				if( next == '$' ) {
					if( num <= 0 || num >= arguments.length ) {
						throw "out of bound";
					}
					if( relative ) {
						if( precision ) {
							flags['precision'] = arguments[num];
							precision = false;
						} else if( format.charAt( i + 2 ) == 'v' ) {
							vector_delimiter = arguments[num];
						}else {
							flags['width'] = arguments[num];
						}
						relative = false;
					} else {
						fixed = true;
						current_index = num;
					}
					++i;
				} else if( precision ) {
					flags['precision'] = num;
					precision = false;
				} else {
					flags['width'] = num;
				}
			} else if ( relative && !/\d/.test( format.charAt( i + 1 ) ) ) {
				if( precision ) {
					flags['precision'] = arguments[current_index];
					precision = false;
				} else if( format.charAt( i + 1 ) == 'v' ) {
					vector_delimiter = arguments[current_index];
				} else {
					flags['width'] = arguments[current_index];
				}
				++index;
				if( !fixed ) {
					current_index++;
				}
				relative = false;
			}
		} else {
			if( current_char == '%' ) {
				in_operator = true;
				continue;
			} else {
				result += current_char;
				continue;
			}
		}
	}
	return result;
}

sprintf.format = function sprintfFormat( type, value, flags ) {

	// Similar to how perl printf works
	if( value == undefined ) {
		if( type == 's' ) {
			return '';
		} else {
			return '0';
		}
	}

	var result;
	var prefix = '';
	var fill = '';
	var fillchar = ' ';
	switch( type ) {
	case '%':
		result = '%';
		break;
	case 'c':
		result = String.fromCharCode( parseInt( value ) );
		break;
	case 's':
		result = value.toString();
		break;
	case 'd':
		result = parseInt( value ).toString();
		break;
	case 'u':
		result = Math.abs( parseInt( value ) ).toString(); // it's not correct, but JS lacks unsigned ints
		break;
	case 'o':
		result = (new Number( Math.abs( parseInt( value ) ) ) ).toString(8);
		break;
	case 'x':
		result = (new Number( Math.abs( parseInt( value ) ) ) ).toString(16);
		break;
	case 'b':
		result = (new Number( Math.abs( parseInt( value ) ) ) ).toString(2);
		break;
	case 'e':
		var digits = flags['precision'] ? flags['precision'] : 6;
		result = (new Number( value ) ).toExponential( digits ).toString();
		break;
	case 'f':
		var digits = flags['precision'] ? flags['precision'] : 6;
		result = (new Number( value ) ).toFixed( digits ).toString();
	case 'g':
		var digits = flags['precision'] ? flags['precision'] : 6;
		result = (new Number( value ) ).toPrecision( digits ).toString();
		break;
	case 'X':
		result = (new Number( Math.abs( parseInt( value ) ) ) ).toString(16).toUpperCase();
		break;
	case 'E':
		var digits = flags['precision'] ? flags['precision'] : 6;
		result = (new Number( value ) ).toExponential( digits ).toString().toUpperCase();
		break;
	case 'G':
		var digits = flags['precision'] ? flags['precision'] : 6;
		result = (new Number( value ) ).toPrecision( digits ).toString().toUpperCase();
		break;
	}

	if(flags['+'] && parseFloat( value ) > 0 && ['d','e','f','g','E','G'].indexOf(type) != -1 ) {
		prefix = '+';
	}

	if(flags[' '] && parseFloat( value ) > 0 && ['d','e','f','g','E','G'].indexOf(type) != -1 ) {
		prefix = ' ';
	}

	if( flags['#'] && parseInt( value ) != 0 ) {
		switch(type) {
		case 'o':
			prefix = '0';
			break;
		case 'x':
		case 'X':
			prefix = '0x';
			break;
		case 'b':
			prefix = '0b';
			break;
		}
	}

	if( flags['0'] && !flags['-'] ) {
		fillchar = '0';
	}

	if( flags['width'] && flags['width'] > ( result.length + prefix.length ) ) {
		var tofill = flags['width'] - result.length - prefix.length;
		for( var i = 0; i < tofill; ++i ) {
			fill += fillchar;
		}
	}

	if( flags['-'] && !flags['0'] ) {
		result += fill;
	} else {
		result = fill + result;
	}
	
	return prefix + result;
}

String.prototype.splitWeightedByKeys = function stringPrototypeSplitWeightedByKeys( start, end ) {
	if( start.length != end.length ) {
		throw 'start marker and end marker must be of the same length';
	}
	var level = 0;
	var initial = null;
	var result = [];
	for( var i  = 0; i < this.length; ++i ) {
		if( this.substr( i, start.length ) == start ) {
			if( initial == null ) {
				initial = i;
			}
			++level;
			i += start.length - 1;
		} else if( this.substr( i, end.length ) == end ) {
			--level;
			i += end.length - 1;
		}
		if( level == 0 && initial != null ) {
			result.push( this.substring( initial, i + 1 ) );
			initial = null;
		}
	}

	return result;
}

Array.prototype.uniq = function arrayPrototypeUniq() {
	var result = [];
	for( var i = 0; i < this.length; ++i ) {
		var current = this[i];
		if( result.indexOf( current ) == -1 ) {
			result.push( current );
		}
	}
	return result;
}

Array.prototype.dups = function arrayPrototypeUniq() {
	var uniques = [];
	var result = [];
	for( var i = 0; i < this.length; ++i ) {
		var current = this[i];
		if( uniques.indexOf( current ) == -1 ) {
			uniques.push( current );
		} else {
			result.push( current );
		}
	}
	return result;
}

Array.prototype.chunk = function arrayChunk( size ) {
	if( size <= 0 ) {
		return this;
	}
	var result = [];
	var current;
	for(var i = 0; i < this.length; ++i ) {
		if( i % size == 0 ) {
			current = [];
			result.push( current );
		}
		current.push( this[i] );
	}
    return result;
}

function clone( obj, deep ) {
  var objectClone = new obj.constructor();
  for ( var property in obj )
    if ( !deep ) {
		objectClone[property] = obj[property];
	}
    else if ( typeof obj[property] == 'object' ) {
		objectClone[property] = clone( obj[property], deep );
	}
    else {
		objectClone[property] = obj[property];
	}
  return objectClone;
}

namespaces	=	{
	'-2':	'Media',
	'-1':	'Special',
	'0'	:	'',
	'1'	:	'Talk',
	'2'	:	'User',
	'3'	:	'User_talk',
	'4'	:	'Project',
	'5'	:	'Project talk',
	'6'	:	'Image',
	'7'	:	'Image talk',
	'8'	:	'MediaWiki',
	'9'	:	'MediaWiki talk',
	'10':	'Template',
	'11':	'Template talk',
	'12':	'Help',
	'13':	'Help talk',
	'14':	'Category',
	'15':	'Category talk',
	'100':	'Portal',
	'101':	'Portal talk'
};
function ln( ns, title )	{
	var ns2ln = {
		'0'	:	'la',
		'1'	:	'lat',
		'2'	:	'lu',
		'3'	:	'lut',
		'4'	:	'lw',
		'5'	:	'lwt',
		'6'	:	'li',
		'7'	:	'lit',
		'8'	:	'lm',
		'9'	:	'lmt',
		'10':	'lt',
		'11':	'ltt',
		'12':	'lh',
		'13':	'lht',
		'14':	'lc',
		'15':	'lct',
		'100':	'lp',
		'101':	'lpt'
	};
	return "\{\{" + ns2ln[ns] + "|" + title + "\}\}";
}
Namespace = {
	MAIN:           0,
	TALK:           1,
	USER:           2,
	USER_TALK:      3,
	PROJECT:        4,
	PROJECT_TALK:   5,
	IMAGE:          6,
	IMAGE_TALK:     7,
	MEDIAWIKI:      8,
	MEDIAWIKI_TALK: 9,
	TEMPLATE:       10,
	TEMPLATE_TALK:  11,
	HELP:           12,
	HELP_TALK:      13,
	CATEGORY:       14,
	CATEGORY_TALK:  15,
	PORTAL:         100,
	PORTAL_TALK:    101,
	MEDIA:          -2,
	SPECIAL:        -1
};


// Helper functions to change case of a string
String.prototype.toUpperCaseFirstChar = function() {
	return this.substr( 0, 1 ).toUpperCase() + this.substr( 1 );
}

String.prototype.toLowerCaseFirstChar = function() {
	return this.substr( 0, 1 ).toLowerCase() + this.substr( 1 );
}

String.prototype.toUpperCaseEachWord = function( delim ) {
	delim = delim ? delim : ' ';
	return this.split( delim ).map( function(v) { return v.toUpperCaseFirstChar() } ).join( delim );
}

String.prototype.toLowerCaseEachWord = function( delim ) {
	delim = delim ? delim : ' ';
	return this.split( delim ).map( function(v) { return v.toLowerCaseFirstChar() } ).join( delim );
}

/**
* Helper functions to get the month as a string instead of a number
*/

Date.monthNames = [
	'January',
	'February',
	'March',
	'April',
	'May',
	'June',
	'July',
	'August',
	'September',
	'October',
	'November',
	'December'
];
Date.monthNamesAbbrev = [
	'Jan',
	'Feb',
	'Mar',
	'Apr',
	'May',
	'Jun',
	'Jul',
	'Aug',
	'Sep',
	'Oct',
	'Nov',
	'Dec'
];

Date.prototype.getMonthName = function() {
	return Date.monthNames[ this.getMonth() ];
}

Date.prototype.getMonthNameAbbrev = function() {
	return Date.monthNamesAbbrev[ this.getMonth() ];
}
Date.prototype.getUTCMonthName = function() {
	return Date.monthNames[ this.getUTCMonth() ];
}

Date.prototype.getUTCMonthNameAbbrev = function() {
	return Date.monthNamesAbbrev[ this.getUTCMonth() ];
}

// Accessor functions for wikiediting and api-access
Wikipedia = {};

// we dump all XHR here so they won't loose props
Wikipedia.dump = [];

Wikipedia.numberOfActionsLeft = 0;
Wikipedia.nbrOfCheckpointsLeft = 0;

Wikipedia.actionCompleted = function( self ) {
	if( --Wikipedia.numberOfActionsLeft <= 0 && Wikipedia.nbrOfCheckpointsLeft <= 0 ) {
		Wikipedia.actionCompleted.event( self );
	}
}

// Change per action wanted
Wikipedia.actionCompleted.event = function() {
	new Status( Wikipedia.actionCompleted.notice, Wikipedia.actionCompleted.postfix, 'info' );
	if( Wikipedia.actionCompleted.redirect != null ) {
		// if it isn't an url, make it an relative to self (probably this is the case)
		if( !/^\w+\:\/\//.test( Wikipedia.actionCompleted.redirect ) ) {
			Wikipedia.actionCompleted.redirect = mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace( '$1', encodeURIComponent( Wikipedia.actionCompleted.redirect ).replace( /\%2F/g, '/' ) );
		}
		window.setTimeout( function() { window.location = Wikipedia.actionCompleted.redirect } , Wikipedia.actionCompleted.timeOut );
	}
}
wpActionCompletedTimeOut = typeof(wpActionCompletedTimeOut) == 'undefined'  ? 5000 : wpActionCompletedTimeOut;
wpMaxLag = typeof(wpMaxLag) == 'undefined' ? 10 : wpMaxLag; // Maximum lag allowed, 5-10 is a good value, the higher value, the more agressive.

Wikipedia.editCount = 10;
Wikipedia.actionCompleted.timeOut = wpActionCompletedTimeOut;
Wikipedia.actionCompleted.redirect = null;
Wikipedia.actionCompleted.notice = 'Action';
Wikipedia.actionCompleted.postfix = 'completed';

Wikipedia.addCheckpoint = function() {
	++Wikipedia.nbrOfCheckpointsLeft;
}

Wikipedia.removeCheckpoint = function() {
	if( --Wikipedia.nbrOfCheckpointsLeft <= 0 && Wikipedia.numberOfActionsLeft <= 0 ) {
		Wikipedia.actionCompleted.event();
	}
}

/*
 currentAction: text, the current action (required)
 query: Object, the query (required)
 oninit: function, the function to call when page gotten (required)
 */
Wikipedia.api = function( currentAction, query, oninit, statelem ) {
	this.currentAction = currentAction;
	this.query = query;
	this.query['format'] = 'xml'; //LET THE FORCE BE WITH YOU!!!
	this.oninit = oninit;
	if( statelem ) {
		statelem.status( currentAction )
	} else {
		this.statelem = new Status( currentAction );
	}
	++Wikipedia.numberOfActionsLeft;
}
Wikipedia.api.prototype = {
	currentAction: '',
	oninit: null,
	query: null,
	responseXML: null,
	statelem:  null,
	counter: 0,
	post: function() {
		var xmlhttp = sajax_init_object();
		Wikipedia.dump.push( xmlhttp );
		xmlhttp.obj = this;
		xmlhttp.overrideMimeType('text/xml');
		xmlhttp.open( 'POST' , mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/api.php', true);
		xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
		xmlhttp.onerror = function() {
			var self = this.obj;
			self.statelem.error( "Error " + this.target.status + " occurred while quering the api." );
		}
		xmlhttp.onload = function() {
			this.obj.responseXML = this.responseXML;
			this.obj.oninit( this.obj );
			Wikipedia.actionCompleted(); 
		};
		xmlhttp.send( QueryString.create( this.query ) );
	}
}

/*
 currentAction: text, the current action (required)
 query: Object, the query (required)
 oninit: function, the function to call when page gotten (required)
 onsuccess: function, a function to call when post succeeded
 onerror: function, a function to call when we abort failed posts
 onretry: function, a function to call when we try to retry a post
 */
Wikipedia.wiki = function( currentAction, query, oninit, onsuccess, onerror, onretry ) {
	this.currentAction = currentAction;
	this.query = query;
	this.oninit = oninit;
	this.onsuccess = onsuccess;
	this.onerror = onerror;
	this.onretry = onretry;
	this.statelem = new Status( currentAction );
	++Wikipedia.numberOfActionsLeft;
}

Wikipedia.wiki.prototype = {
	currentAction: '',
	onsuccess: null,
	onerror: null,
	onretry: null,
	oninit: null,
	query: null,
	postData: null,
	responseXML: null,
	statelem: null,
	counter: 0,
	post: function( data ) {
		this.postData = data;
		if( Wikipedia.editCount <= 0 ) {
			this.query['maxlag'] = wpMaxLag; // are we a bot?
		} else {
			--Wikipedia.editCount;
		}

		var xmlhttp = sajax_init_object();
		Wikipedia.dump.push( xmlhttp );
		xmlhttp.obj = this;
		xmlhttp.overrideMimeType('text/xml');
		xmlhttp.open( 'POST' , mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( this.query ), true);
		xmlhttp.setRequestHeader('Content-type','application/x-www-form-urlencoded');
		xmlhttp.onerror = function(e) {
			var self = this.obj;
			self.statelem.error( "Error " + e.target.status + " occurred while posting the document." );
		}
		xmlhttp.onload = function(e) {
			var self = this.obj;
			var status = e.target.status;
			if( status != 200 ) {
				if( status == 503 ) {
					var retry = e.target.getResponseHeader( 'Retry-After' );
					var lag = e.target.getResponseHeader( 'X-Database-Lag' );
					if( lag ) {
						self.statelem.warn( "current lag of " + lag + " seconds is more than our defined maximum lag of " + wpMaxLag + " seconds, will retry in " + retry + " seconds" );
						window.setTimeout( function( self ) { self.post( self.postData ); }, retry * 1000, self );
						return;
					} else {
						self.statelem.error( "Error " + status + " occurred while posting the document." );
					}
				}
				return;
			}
			var xmlDoc;
			xmlDoc = self.responseXML = this.responseXML;
			var xpathExpr =  'boolean(//div[@class=\'previewnote\']/p/strong[contains(.,\'Sorry! We could not process your edit due to a loss of session data\')])';
			var nosession = xmlDoc.evaluate( xpathExpr, xmlDoc, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;
			if( nosession ) {
				// Grabbing the shipping token, and repost
				var new_token = xmlDoc.evaluate( '//input[@name="wfEditToken"]/@value', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
				self.postData['wfEditToken'] = new_token;
				self.post( self.postData );
			} else {
				if( self.onsuccess ) {
					self.onsuccess( self );
				} else {
					var link = document.createElement( 'a' );
					link.setAttribute( 'href', wgArticlePath.replace( '$1', self.query['title'] ) );
					link.setAttribute( 'title', self.query['title'] );
					link.appendChild( document.createTextNode( self.query['title'] ) );

					self.statelem.info( [ 'completed (' , link , ')' ] );
				}
				Wikipedia.actionCompleted();
			}
		};
		xmlhttp.send( QueryString.create( this.postData ) );
	},
	get: function() {
		this.onloading( this );
		var redirect_query = {
			'action': 'query',
			'titles': this.query['title'],
			'redirects': ''
		}

		var wikipedia_api = new Wikipedia.api( "resolving eventual redirect", redirect_query, this.postget, this.statelem );
		wikipedia_api.parent = this;
		wikipedia_api.post();
	},
	postget: function() {
		var xmlDoc = self.responseXML = this.responseXML;
		var to = xmlDoc.evaluate( '//redirects/r/@to', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
		if( !this.followRedirect ) {
			this.parent.statelem.info('ignoring eventual redirect');
		} else if( to ) {
			this.parent.query['title'] = to;
		}
		this.parent.onloading( this );
		var xmlhttp = sajax_init_object();
		Wikipedia.dump.push( xmlhttp );
		xmlhttp.obj = this.parent;
		xmlhttp.overrideMimeType('text/xml');
		xmlhttp.open( 'GET' , mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( this.parent.query ), true);
		xmlhttp.onerror = function() {
			var self = this.obj;
			self.statelem.error( "Error " + this.status + " occurred while recieving the document." );
		}
		xmlhttp.onload = function() { 
			this.obj.onloaded( this.obj );
			this.obj.responseXML = this.responseXML;
			this.obj.responseText = this.responseText;
			this.obj.oninit( this.obj ); 
		};
		xmlhttp.send( null );
	},
	onloading: function() {
		this.statelem.status( 'loading data...' );
	},
	onloaded: function() {
		this.statelem.status( 'data loaded...' );
	}
}

Number.prototype.zeroFill = function( length ) {
	var str = this.toFixed();
	if( !length ) { return str; }
	while( str.length < length ) { str = '0' + str; }
	return str;
}

Mediawiki = {};

Mediawiki.Page = function mediawikiPage( text ) {
	this.text = text;
}


Mediawiki.Page.prototype = {
	text: '',
	removeLink: function( link_target ) {
		var first_char = link_target.substr( 0, 1 );
		var link_re_string = "[" + first_char.toUpperCase() + first_char.toLowerCase() + ']' +  RegExp.escape( link_target.substr( 1 ), true );
		var link_simple_re = new RegExp( "\\[\\[(" + link_re_string + ")\\|?\\]\\]", 'g' );
		var link_named_re = new RegExp( "\\[\\[" + link_re_string + "\\|(.+?)\\]\\]", 'g' );
		if( link_simple_re.test(this.text) ) {
			this.text = this.text.replace( link_simple_re, "$1" );
		} else {
			this.text = this.text.replace( link_named_re, "$1" );
		}
	},
	commentOutImage: function( image, reason ) {
		reason = reason ? ' ' + reason + ': ' : '';
		var first_char = image.substr( 0, 1 );
		var image_re_string = "[" + first_char.toUpperCase() + first_char.toLowerCase() + ']' +  RegExp.escape( image.substr( 1 ), true ); 
		var links_re = new RegExp( "\\[\\[[Ii]mage:\\s*" + image_re_string );
		var allLinks = this.text.splitWeightedByKeys( '[[', ']]' ).uniq();
		for( var i = 0; i < allLinks.length; ++i ) {
			if( links_re.test( allLinks[i] ) ) {
				var replacement = '<!-- ' + reason + allLinks[i] + ' -->';
				this.text = this.text.replace( allLinks[i], replacement );
			}
		}

		var gallery_re = new RegExp( "^\\s*((?:\\|?\\s*\\w+\\s*\\=\\s*)(?:[Ii]mage:\\s*)?" + image_re_string + '.*?)$', 'mg' );
		var replacement = "<!-- " + reason + "$1 -->";

		this.text = this.text.replace( gallery_re, replacement );
		var hackalicious = new RegExp("--> *-->");
		this.text = this.text.replace( hackalicious, '' );
		this.text = this.text.replace( hackalicious, '' );
		this.text = this.text.replace( hackalicious, '' );
	},
	addToImageComment: function( image, data ) {
		var first_char = image.substr( 0, 1 );
		var image_re_string = "[Ii]mage:\\s*[" + first_char.toUpperCase() + first_char.toLowerCase() + ']' +  RegExp.escape( image.substr( 1 ), true ); 
		var links_re = new RegExp( "\\[\\[" + image_re_string );
		var allLinks = this.text.splitWeightedByKeys( '[[', ']]' ).uniq();
		for( var i = 0; i < allLinks.length; ++i ) {
			if( links_re.test( allLinks[i] ) ) {
				var replacement = allLinks[i];
				// just put it at the end?
				replacement = replacement.replace( /\]\]$/, '|' + data + ']]' );
				this.text = this.text.replace( allLinks[i], replacement );
			}
		}
		var gallery_re = new RegExp( "^(\\s*" + image_re_string + '.*?)\\|?(.*?)$', 'mg' );
		var replacement = "$1|$2 " + data;
		this.text = this.text.replace( gallery_re, replacement );
	},
	getText: function() {
		return this.text;
	}
}

// Simple helper functions to see what groups a user might belong

function userIsInGroup( group ) {

	return ( wgUserGroups != null && wgUserGroups.indexOf( group ) != -1 ) || ( wgUserGroups == null && group == 'anon' );
}

function userIsAnon() {
	return wgUserGroups == null;
}

// AOL Proxy IP Addresses (2007-02-03)
var AOLNetworks = [
	'64.12.96.0/19',
	'149.174.160.0/20',
	'152.163.240.0/21',
	'152.163.248.0/22',
	'152.163.252.0/23',
	'152.163.96.0/22',
	'152.163.100.0/23',
	'195.93.32.0/22',
	'195.93.48.0/22',
	'195.93.64.0/19',
	'195.93.96.0/19',
	'195.93.16.0/20',
	'198.81.0.0/22',
	'198.81.16.0/20',
	'198.81.8.0/23',
	'202.67.64.128/25',
	'205.188.192.0/20',
	'205.188.208.0/23',
	'205.188.112.0/20',
	'205.188.146.144/30',
	'207.200.112.0/21',
];

// AOL Client IP Addresses (2007-02-03)
var AOLClients = [
	'172.128.0.0/10',
	'172.192.0.0/12',
	'172.208.0.0/14',
	'202.67.66.0/23',
	'172.200.0.0/15',
	'172.202.0.0/15',
	'172.212.0.0/14',
	'172.216.0.0/16',
	'202.67.68.0/22',
	'202.67.72.0/21',
	'202.67.80.0/20',
	'202.67.96.0/19',
];

/**
* ipadress is in the format 1.2.3.4 and network is in the format 1.2.3.4/5
*/

function isInNetwork( ipaddress, network ) {
	var iparr = ipaddress.split('.');
	var ip = (parseInt(iparr[0]) << 24) + (parseInt(iparr[1]) << 16) + (parseInt(iparr[2]) << 8) + (parseInt(iparr[3]));

	var netmask = 0xffffffff << network.split('/')[1];

	var netarr = network.split('/')[0].split('.');
	var net = (parseInt(netarr[0]) << 24) + (parseInt(netarr[1]) << 16) + (parseInt(netarr[2]) << 8) + (parseInt(netarr[3]));

	return (ip & netmask) == net;
}

/* Returns true if given string contains a valid IP-address, that is, from 0.0.0.0 to 255.255.255.255*/
function isIPAddress( string ){
	var res = /(\d{1,4})\.(\d{1,3})\.(\d{1,3})\.(\d{1,4})/.exec( string );
	return res != null && res.slice( 1, 5 ).every( function( e ) { return e < 256; } );
}

/**
* Maps the querystring to an object
*
* Functions:
*
* QueryString.exists(key)
*     returns true if the particular key is set
* QueryString.get(key)
*     returns the value associated to the key
* QueryString.equals(key, value)
*     returns true if the value associated with given key equals given value
* QueryString.toString()
*     returns the query string as a string
* QueryString.create( hash )
*     creates an querystring and encodes strings via encodeURIComponent and joins arrays with | 
*
* In static context, the value of location.search.substring(1), else the value given to the constructor is going to be used. The mapped hash is saved in the object.
*
* Example:
*
* var value = QueryString.get('key');
* var obj = new QueryString('foo=bar&baz=quux');
* value = obj.get('foo');
*/
function QueryString(qString) {
	this.string = qString;
	this.params = {};

	if( qString.length == 0 ) {
		return;
	}

	qString.replace(/\+/, ' ');
	var args = qString.split('&');

	for( var i = 0; i < args.length; ++i ) {
		var pair = args[i].split( '=' );
		var key = decodeURIComponent( pair[0] ), value = key;

		if( pair.length == 2 ) {
			value = decodeURIComponent( pair[1] );
		}

		this.params[key] = value;
	}
}

QueryString.static = null;

QueryString.staticInit = function() {
	if( QueryString.static == null ) {
		QueryString.static = new QueryString(location.search.substring(1));
	}
}

QueryString.get = function(key) {
	QueryString.staticInit();
	return QueryString.static.get(key);
};

QueryString.prototype.get = function(key) {
	return this.params[key] ? this.params[key] : null;
};

QueryString.exists = function(key) {
	QueryString.staticInit();
	return QueryString.static.exists(key);
}

QueryString.prototype.exists = function(key) {
	return this.params[key] ? true : false;
}

QueryString.equals = function(key, value) {
	QueryString.staticInit();
	return QueryString.static.equals(key, value);
}

QueryString.prototype.equals = function(key, value) {
	return this.params[key] == value ? true : false;
}

QueryString.toString = function() {
	QueryString.staticInit();
	return QueryString.static.toString();
}

QueryString.prototype.toString = function() {
	return this.string ? this.string : null;
}


QueryString.create = function( arr ) {
	var resarr = Array();
	for( var i in arr ) {
		if( typeof arr[i] == 'undefined' ) {
			continue;
		}
		if( arr[i] instanceof Array ){
			var v =  Array();
			for(var j = 0; j < arr[i].length; ++j ) {
				v[j] = encodeURIComponent( arr[i][j] );
			}
			resarr.push( encodeURIComponent( i ) + '=' +  v.join('|') );
		} else {
			resarr.push( encodeURIComponent( i ) + '=' + encodeURIComponent( arr[i] ) );
		}
	}

	return resarr.join('&');
}
QueryString.prototype.create = QueryString.create;

/**
* Simple exception handling
*/

Exception = function( str ) {
	this.str = str || '';
}

Exception.prototype.what = function() {
	return this.str;
}

function Status( text, stat, type ) {
	this.text = this.codify(text);
	this.stat = this.codify(stat);
	this.type = type || 'status';
	this.generate(); 
	if( stat ) {
		this.render();
	}
}
Status.init = function( root ) {
	if( !( root instanceof Element ) ) {
		throw new Exception( 'object not an instance of Element' );
	}
	while( root.hasChildNodes() ) {
		root.removeChild( root.firstChild );
	}
	Status.root = root;

	var cssNode = document.createElement('style');
	cssNode.type = 'text/css';
	cssNode.rel = 'stylesheet';
	cssNode.appendChild( document.createTextNode("")); // Safari bugfix
	document.getElementsByTagName("head")[0].appendChild(cssNode);
	var styles = cssNode.sheet ? cssNode.sheet : cssNode.stylesSheet;
	styles.insertRule(".tw_status_status { color: SteelBlue; }", 0);
	styles.insertRule(".tw_status_info { color: ForestGreen; }", 0);
	styles.insertRule(".tw_status_warn { color: OrangeRed; }", 0);
	styles.insertRule(".tw_status_error { color: OrangeRed; font-weight: 900; }", 0);
}
Status.root = null;

Status.prototype = {
	stat: null,
	text: null,
	type: 'status',
	target: null,
	node: null,
	linked: false,
	link: function() {
		if( ! this.linked && Status.root ) {
			Status.root.appendChild( this.node );
			this.linked = true;
		}
	},
	unlink: function() {
		if( this.linked ) {
			Status.root.removeChild( this.node );
			this.linked = false;
		}
	},
	codify: function( obj ) {
		if ( ! ( obj instanceof Array ) ) {
			obj = [ obj ];
		}
		var result;
		result = document.createDocumentFragment();
		for( var i = 0; i < obj.length; ++i ) {
			if( typeof obj[i] == 'string' ) {
				result.appendChild( document.createTextNode( obj[i] ) );
			} else if( obj[i] instanceof Element ) {
				result.appendChild( obj[i] );
			} // Else cosmic radiation made something shit
		}
		return result;

	},
	update: function( status, type ) {
		this.stat = this.codify( status );
		if( type ) {
			this.type = type;
		}
		this.render();
	},
	generate: function() {
		this.node = document.createElement( 'div' );
		this.node.appendChild( document.createElement('span') ).appendChild( this.text );
		this.node.appendChild( document.createElement('span') ).appendChild( document.createTextNode( ': ' ) );
		this.target = this.node.appendChild( document.createElement( 'span' ) );
		this.target.appendChild(  document.createTextNode( '' ) ); // dummy node
	},
	render: function() {
		this.node.className = 'tw_status_' + this.type;
		while( this.target.hasChildNodes() ) {
			this.target.removeChild( this.target.firstChild );
		}
		this.target.appendChild( this.stat );
		this.link();
	},
	status: function( status ) {
		this.update( status, 'status');
	},
	info: function( status ) {
		this.update( status, 'info');
	},
	warn: function( status ) {
		this.update( status, 'warn');
	},
	error: function( status ) {
		this.update( status, 'error');
	}
}

Status.status = function( text, status ) {
	return new Status( text, status, 'status' );
}
Status.info = function( text, status ) {
	return new Status( text, status, 'info' );
}
Status.warn = function( text, status ) {
	return new Status( text, status, 'error' );
}
Status.error = function( text, status ) {
	return new Status( text, status, 'error' );
}



// Simple helper function to create a simple node
function htmlNode( type, content, color ) {
	var node = document.createElement( type );
	if( color ) {
		node.style.color = color;
	}
	node.appendChild( document.createTextNode( content ) );
	return node;
}

// A simple dragable window

function SimpleWindow( width, height ) {
	var stylesheet = document.createElement('style');
	stylesheet.type = 'text/css';
	stylesheet.rel = 'stylesheet';
	stylesheet.appendChild( document.createTextNode("") ); // Safari bugfix
	document.getElementsByTagName("head")[0].appendChild(stylesheet);
	var styles = stylesheet.sheet ? stylesheet.sheet : stylesheet.styleSheet;
	styles.insertRule(
		".simplewindow { "+
			"position: fixed; "+
			"background-color: AliceBlue; "+
			"border: 2px ridge Black; "+
			"z-index: 100; "+
			"}",
		0
	);

	styles.insertRule(
		".simplewindow .content { "+
			"position: absolute; "+
			"top: 20px; "+
			"bottom: 0; "+
			"overflow: auto; "+
			"width: 100%; "+
			"}",
		0
	);

	styles.insertRule(
		".simplewindow .resizebuttonhorizontal { "+
			"position: absolute; "+
			"background-color: MediumPurple; "+
			"opacity: 0.5; "+
			"right: -2px; "+
			"bottom: -2px; "+
			"width: 20px; "+
			"height: 4px; "+
			"cursor: se-resize; "+
			"}",
		0
	);
	styles.insertRule(
		".simplewindow .resizebuttonvertical { "+
			"position: absolute; "+
			"opacity: 0.5; "+
			"background-color: MediumPurple; "+
			"right: -2px; "+
			"bottom: -2px; "+
			"width: 4px; "+
			"height: 20px; "+
			"cursor: se-resize; "+
			"}",
		0
	);

	styles.insertRule( 
		".simplewindow .closebutton {"+
			"position: absolute; "+
			"font: 100 0.8em sans-serif; "+
			"top: 1px; "+
			"left: 1px; "+
			"height: 100%; "+
			"cursor: pointer; "+
			"}",
		0
	);

	styles.insertRule(
		".simplewindow .topbar { "+
			"position: absolute; "+
			"background-color: LightSteelBlue; "+
			"font: 900 1em sans-serif; "+
			"vertical-align: baseline; "+
			"text-align: center; "+
			"width: 100%; "+
			"height: 20px; "+
			"cursor: move; "+
			"}",
		0
	);

	this.width = width;
	this.height = height;

	var frame = document.createElement( 'div' );
	var content = document.createElement( 'div' );
	var topbar = document.createElement( 'div' );
	var title = document.createElement( 'span' );
	var closeButton = document.createElement( 'span' );
	var resizeButton2 = document.createElement( 'div' );
	var resizeButton1 = document.createElement( 'div' );

	this.frame = frame;
	this.title = title;
	this.content = content;

	frame.className = 'simplewindow';
	content.className = 'content';
	topbar.className = 'topbar';
	resizeButton1.className = 'resizebuttonvertical';
	resizeButton2.className = 'resizebuttonhorizontal';
	closeButton.className = 'closebutton';
	title.className = 'title';

	topbar.appendChild( closeButton );
	topbar.appendChild( title );
	frame.appendChild( topbar );
	frame.appendChild( content );
	frame.appendChild( resizeButton1 );
	frame.appendChild( resizeButton2 );

	frame.style.width = width + 'px';
	frame.style.height = height + 'px';
	frame.style.top = parseInt( window.innerHeight - this.height  )/2 + 'px' ;
	frame.style.left = parseInt( window.innerWidth - this.width  )/2 + 'px';
	var img = document.createElement( 'img' );
	img.src = "http://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Nuvola_apps_error.png/18px-Nuvola_apps_error.png";
	closeButton.appendChild( img );

	var self = this;

	// Specific events
	frame.addEventListener( 'mousedown', function(event) { self.focus(event); }, false );
	closeButton.addEventListener( 'click', function(event) {self.close(event); }, false );
	topbar.addEventListener( 'mousedown', function(event) {self.initMove(event); }, false );
	resizeButton1.addEventListener( 'mousedown', function(event) {self.initResize(event); }, false );
	resizeButton2.addEventListener( 'mousedown', function(event) {self.initResize(event); }, false );

	// Generic events
	window.addEventListener( 'mouseover', function(event) {self.handleEvent(event); }, false );
	window.addEventListener( 'mousemove', function(event) {self.handleEvent(event); }, false );
	window.addEventListener( 'mouseup', function(event) {self.handleEvent(event); }, false );
    this.currentState = this.initialState;    
}

SimpleWindow.prototype = {
	focusLayer: 100,
	width: 800,
	height: 600,
    initialState: "Inactive",
	currentState: null, // current state of finite state machine (one of 'actionTransitionFunctions' properties)
	focus: function(event) { 
		this.frame.style.zIndex = ++this.focusLayer;
	},
	close: function(event) {
		event.preventDefault();
		document.body.removeChild( this.frame );
	},
	initMove: function(event) {
		event.preventDefault();
		this.initialX = parseInt( event.clientX - this.frame.offsetLeft );
		this.initialY = parseInt( event.clientY - this.frame.offsetTop );
		this.frame.style.opacity = '0.5';
		this.currentState = 'Move';
	},
	initResize: function(event) {
		event.preventDefault();
		this.frame.style.opacity = '0.5';
		this.currentState = 'Resize';
	},
	handleEvent: function(event) { 
		event.preventDefault();
		var actionTransitionFunction = this.actionTransitionFunctions[this.currentState][event.type];
		if( !actionTransitionFunction ) {
			actionTransitionFunction = this.unexpectedEvent;
		}
		var nextState = actionTransitionFunction.call(this, event);
		if( !nextState ){
			nextState = this.currentState;
		}
        if( !this.actionTransitionFunctions[nextState] ){
			nextState = this.undefinedState(event, nextState);
		}
        this.currentState = nextState;
		event.stopPropagation();
    },
    unexpectedEvent: function(event) { 
		throw ("Handled unexpected event '" + event.type + "' in state '" + this.currentState);
        return this.initialState; 
    },  
   
    undefinedState: function(event, state) {
        throw ("Transitioned to undefined state '" + state + "' from state '" + this.currentState + "' due to event '" + event.type);
        return this.initialState; 
    },  
	actionTransitionFunctions: { 
        Inactive: {
            mouseover: function(event) { 
                return this.currentState;
            },
            mousemove: function(event) { 
                return this.currentState;
            },
            mouseup: function(event) { 
                return this.currentState;
            }
        }, 
        Move: {
            mouseover: function(event) { 
				this.moveWindow( event.clientX,  event.clientY );
                return this.currentState;
            },
            mousemove: function(event) { 
				return this.doActionTransition("Move", "mouseover", event);
            },
            mouseup: function(event) { 
				this.frame.style.opacity = '1';
                return 'Inactive';
            }
        }, 
		Resize: {
			mouseover: function(event) { 
				this.resizeWindow( event.clientX,  event.clientY );
				return this.currentState;
			},
			mousemove: function(event) { 
				return this.doActionTransition("Resize", "mouseover", event);
			},
			mouseup: function(event) { 
				this.frame.style.opacity = '1';
				return 'Inactive';
			}
		}
	},
	doActionTransition: function(anotherState, anotherEventType, event) {
         return this.actionTransitionFunctions[anotherState][anotherEventType].call(this,event);
    },
	display: function() {
		document.body.appendChild( this.frame );
	},
	setTitle: function( title ) {
		this.title.textContent = title;
	},
	setWidth: function( width ) {
		this.frame.style.width = width;
	},
	setHeight: function( height ) {
		this.frame.style.height = height;
	},
	setContent: function( content ) {
		this.purgeContent();
		this.addContent( content );
	},
	addContent: function( content ) {
		this.content.appendChild( content );
	},
	purgeContent: function( content ) {
		while( this.content.hasChildNodes() ) {
			this.content.removeChild( this.content.firstChild );
		}
	},
	moveWindow: function( x, y ) {
		this.frame.style.left = x - this.initialX + 'px';
		this.frame.style.top  = y - this.initialY + 'px';
	},
	resizeWindow: function( x, y ) {
		this.frame.style.height  = Math.max( parseInt( y - this.frame.offsetTop ), 200 ) + 'px';
		this.frame.style.width = Math.max( parseInt( x -  this.frame.offsetLeft ), 200 ) + 'px';
	}
}
Bytes = function ( value ) {
	if( typeof(value) == 'string' ) {
		var res = /(\d+) ?(\w?)(i?)B?/.exec( value );
		var number = res[1];
		var mag = res[2];
		var si = res[3];

		if( ! number ) {
			this.number = 0;
			return;
		}

		if( !si ) {
			this.value = number * Math.pow( 10, Bytes.magnitudes[mag] * 3 );
		} else {
			this.value = number * Math.pow( 2, Bytes.magnitudes[mag] * 10 );
		}
	} else {
		this.value = value;
	}
}

Bytes.magnitudes = {
	'': 0,
	'K': 1,
	'M': 2,
	'G': 3,
	'T': 4,
	'P': 5,
	'E': 6,
	'Z': 7,
	'Y': 8
}
Bytes.rmagnitudes = {
	0: '',
	1: 'K',
	2: 'M',
	3: 'G',
	4: 'T',
	5: 'P',
	6: 'E',
	7: 'Z',
	8: 'Y'
}

Bytes.prototype.valueOf = function() {
	return this.value;
}

Bytes.prototype.toString = function( magnitude ) {
	var tmp = this.value;
	if( magnitude ) {
		var si = /i/.test(magnitude);
		var mag = magnitude.replace( /.*?(\w)i?B?.*/g, '$1' );
		if( si ) {
			tmp /= Math.pow( 2, Bytes.magnitudes[mag] * 10 );
		} else {
			tmp /= Math.pow( 10, Bytes.magnitudes[mag] * 3 );
		}
		if( parseInt( tmp ) != tmp ) {
			tmp = (new Number( tmp ) ).toPrecision( 4 );
		}
		return tmp + ' ' + mag + (si?'i':'') +  'B';
	} else {
		// si per default
		var current = 0;
		while( tmp >= 1024 ) {
			tmp /= 1024;
			++current;
		}
		tmp = this.value / Math.pow( 2, current * 10 );
		if( parseInt( tmp ) != tmp ) {
			tmp = (new Number( tmp ) ).toPrecision( 4 );
		}
		return tmp + ' ' + Bytes.rmagnitudes[current] + ( current > 0 ? 'iB' : 'B' );
	}

}
// twinklefluff.js
/**
 Twinklefluff revert and antivandalism utillity
 */
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = {};
}

/**
 TwinkleConfig.revertMaxRevisions (int)
 defines how many revision to query maximum, maximum possible is 50, default is 50
 */
if( typeof( TwinkleConfig.revertMaxRevisions ) == 'undefined' ) {
	TwinkleConfig.revertMaxRevisions = 5000;
}


/**
 TwinkleConfig.userTalkPageMode may take arguments:
 'window': open a new window, remmenber the opened window
 'tab': opens in a new tab, if possible.
 'blank': force open in a new window, even if a such window exist
 */
if( typeof( TwinkleConfig.userTalkPageMode ) == 'undefined' ) {
	TwinkleConfig.userTalkPageMode = 'window';
}

/**
 TwinkleConfig.openTalkPage (array)
 What types of actions that should result in opening of talk page
 */
if( typeof( TwinkleConfig.openTalkPage ) == 'undefined' ) {
	TwinkleConfig.openTalkPage = [ 'agf', 'norm', 'vand' ];
}

/**
 TwinkleConfig.openTalkPageOnAutoRevert (bool)
 Defines if talk page should be opened when canling revert from contrib page, this because from there, actions may be multiple, and opening talk page not suitable. If set to true, openTalkPage defines then if talk page will be opened.
 */
if( typeof( TwinkleConfig.openTalkPageOnAutoRevert ) == 'undefined' ) {
	TwinkleConfig.openTalkPageOnAutoRevert = false;
}

/**
 TwinkleConfig.openAOLAnonTalkPage may take arguments:
 true: to open Anon AOL talk pages on revert
 false: to not open them
 */
if( typeof( TwinkleConfig.openAOLAnonTalkPage ) == 'undefined' ) {
	TwinkleConfig.openAOLAnonTalkPage = false;
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.markRevertedPagesAsMinor (array)
 What types of actions that should result in marking edit as minor
 */
if( typeof( TwinkleConfig.markRevertedPagesAsMinor ) == 'undefined' ) {
	TwinkleConfig.markRevertedPagesAsMinor = [ 'agf', 'norm', 'vand', 'torev' ];
}

/**
 TwinkleConfig.watchRevertedPages (array)
 What types of actions that should result in forced addition to watchlist
 */
if( typeof( TwinkleConfig.watchRevertedPages ) == 'undefined' ) {
	TwinkleConfig.watchRevertedPages = [ 'agf', 'norm', 'vand', 'torev' ];
}

/**
 TwinkleConfig.offerReasonOnNormalRevert (boolean)
 If to offer a promt for extra summary reason for normal reverts, default to true
 */
if( typeof( TwinkleConfig.offerReasonOnNormalRevert ) == 'undefined' ) {
	TwinkleConfig.offerReasonOnNormalRevert = true;
}


// a list of usernames, usually only bots, that vandalism revert is jumped over, that is
// if vandalism revert is choosen on such username, then it's target in on the revision before.
// This is for handeling quick bots that makes edits seconds after the original edit is made.
// This only affect vandalism rollback, for good faith rollback, it will stop, indicating a bot 
// has no faith, and for normal rollback, it will rollback that edit.
var WHITELIST = [
	'HagermanBot',
	'SineBot',
	'HBC AIV helperbot',
	'HBC AIV helperbot2',
	'HBC AIV helperbot3',
]

$( function() {
		if( QueryString.exists( 'twinklerevert' ) ) {
			twinklefluff.auto();
		} else {
			twinklefluff.normal();
		}
	} );

twinklefluff = {
	auto: function() {
		if( QueryString.get( 'oldid' ) != wgCurRevisionId ) {
			// not latest revision
			return;
		}

		var ntitle = getElementsByClassName( document.getElementById('bodyContent'), 'td' , 'diff-ntitle' )[0];
		if( ntitle.getElementsByTagName('a')[0].firstChild.nodeValue != 'Current revision' ) {
			// not latest revision
			return;
		}

		vandal = ntitle.getElementsByTagName('a')[3].firstChild.nodeValue.replace("'", "\\'");

		if( !TwinkleConfig.openTalkPageOnAutoRevert ) {
			TwinkleConfig.openTalkPage = [];
		}

		return twinklefluff.revert( QueryString.get( 'twinklerevert' ), vandal );
	},
	normal: function() {

		var spanTag = function( color, content ) {
			var span = document.createElement( 'span' );
			span.style.color = color;
			span.appendChild( document.createTextNode( content ) );
			return span;
		}

		if( wgNamespaceNumber == -1 && wgCanonicalSpecialPageName == "Contributions" ) {
			var list = document.evaluate( '//div[@id="bodyContent"]//ul/li[contains(strong, "(top)")]', document, null,  XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
			var vandal = document.evaluate( '//div[@id="contentSub"]/a[1]/@title', document, null, XPathResult.STRING_TYPE, null ).stringValue.replace(/^User( talk)?:/ , '').replace("'", "\\'");

			var revNode = document.createElement('span');
			var revLink = document.createElement('a');
			revLink.appendChild( spanTag( 'Black', '[' ) );
			revLink.appendChild( spanTag( 'SteelBlue', 'specify' ) );
			revLink.appendChild( spanTag( 'Black', ']' ) );
			revNode.appendChild(revLink);

			for(var i = 0; i < list.snapshotLength; ++i ) {
				var current = list.snapshotItem(i);

				var href = document.evaluate( 'a[2]/@href', current, null, XPathResult.STRING_TYPE, null ).stringValue;
				var tmpNode = revNode.cloneNode( true );
				tmpNode.firstChild.setAttribute( 'href', href + '&' + QueryString.create( { 'twinklerevert': 'norm' } ) );
				current.appendChild( tmpNode );
			}


		} else {

			var otitle = getElementsByClassName( document.getElementById('bodyContent'), 'td' , 'diff-otitle' )[0];
			var ntitle = getElementsByClassName( document.getElementById('bodyContent'), 'td' , 'diff-ntitle' )[0];

			if( !ntitle ) {
				// Nothing to see here, move along...
				return;
			}

			if( !otitle.getElementsByTagName('a')[0] ) {
				// no previous revision available
				return;
			}

			// Lets first add a [edit this revision] link
			var query = new QueryString( decodeURI( otitle.getElementsByTagName( 'a' )[0].getAttribute( 'href' ).split( '?', 2 )[1] ) );

			var oldrev = query.get( 'oldid' );

			var oldEditNode = document.createElement('strong');

			var oldEditLink = document.createElement('a');
			oldEditLink.href = "javascript:twinklefluff.revertToRevision('" + oldrev + "')";
			oldEditLink.appendChild( spanTag( 'Black', '[' ) );
			oldEditLink.appendChild( spanTag( 'SaddleBrown', 'restore this version' ) );
			oldEditLink.appendChild( spanTag( 'Black', ']' ) );
			oldEditNode.appendChild(oldEditLink);

			var cur = otitle.insertBefore(oldEditNode, otitle.firstChild);
			otitle.insertBefore(document.createElement('br'), cur.nextSibling);

			if( ntitle.getElementsByTagName('a')[0].firstChild.nodeValue != 'Current revision' ) {
				// not latest revision
				curVersion = false;
				return;
			}

			vandal = ntitle.getElementsByTagName('a')[3].firstChild.nodeValue.replace("'", "\\'");

			var agfNode = document.createElement('strong');
			var vandNode = document.createElement('strong');
			var normNode = document.createElement('strong');

			var agfLink = document.createElement('a');
			var vandLink = document.createElement('a');
			var normLink = document.createElement('a');

			agfLink.href = "javascript:twinklefluff.revert('agf' , '" + vandal + "')"; 
			vandLink.href = "javascript:twinklefluff.revert('vand' , '" + vandal + "')"; 
			normLink.href = "javascript:twinklefluff.revert('norm' , '" + vandal + "')"; 

			agfLink.appendChild( spanTag( 'Black', '[' ) );
			agfLink.appendChild( spanTag( 'DarkOliveGreen', 'rollback (AGF)' ) );
			agfLink.appendChild( spanTag( 'Black', ']' ) );

			vandLink.appendChild( spanTag( 'Black', '[' ) );
			vandLink.appendChild( spanTag( 'Red', 'rollback (VANDAL)' ) );
			vandLink.appendChild( spanTag( 'Black', ']' ) );

			normLink.appendChild( spanTag( 'Black', '[' ) );
			normLink.appendChild( spanTag( 'SteelBlue', 'rollback' ) );
			normLink.appendChild( spanTag( 'Black', ']' ) );

			agfNode.appendChild(agfLink);
			vandNode.appendChild(vandLink);
			normNode.appendChild(normLink);

			var cur = ntitle.insertBefore(agfNode, ntitle.firstChild);
			cur = ntitle.insertBefore(document.createTextNode(' || '), cur.nextSibling);
			cur = ntitle.insertBefore(normNode, cur.nextSibling);
			cur = ntitle.insertBefore(document.createTextNode(' || '), cur.nextSibling);
			cur = ntitle.insertBefore(vandNode, cur.nextSibling);
			cur = ntitle.insertBefore(document.createElement('br'), cur.nextSibling);
		}

	}
}

twinklefluff.revert = function revertPage( type, vandal, rev, page ) {

	wgPageName = page || wgPageName;
	wgCurRevisionId = rev || wgCurRevisionId;

	Status.init( document.getElementById('bodyContent') );
	var params = {
		type: type,
		user: vandal
	}
	var query = {
		'action': 'query',
		'prop': 'revisions',
		'titles': wgPageName,
		'rvlimit': 50, // max possible
		'rvprop': [ 'ids', 'timestamp', 'user', 'comment' ]
	}
	var wikipedia_api = new Wikipedia.api( 'Grabbing data of earlier revisions', query, twinklefluff.callbacks.main );
	wikipedia_api.params = params;
	wikipedia_api.post();
}

twinklefluff.revertToRevision = function revertToRevision( oldrev ) {

	Status.init( document.getElementById('bodyContent') );

	var query = {
		'action': 'query',
		'prop': 'revisions',
		'titles': wgPageName,
		'rvlimit': 1,
		'rvstartid': oldrev,
		'rvprop': [ 'ids', 'timestamp', 'user', 'comment', 'content' ],
		'format': 'xml'
	}

	var wikipedia_api = new Wikipedia.api( 'Grabbing data of the earlier revision', query, twinklefluff.callbacks.toRevision.main );
	wikipedia_api.params = { rev: oldrev };
	wikipedia_api.post();
}

twinklefluff.callbacks = {
	toRevision: {
		main: function( self ) {
			var xmlDoc = self.responseXML;
			self.params.revision = xmlDoc.evaluate('//rev', xmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue;
			var query = {
				'title': wgPageName,
				'action': 'submit'
			};
			var wikipedia_wiki = new Wikipedia.wiki( 'Reverting page', query, twinklefluff.callbacks.toRevision.reverting );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

		},
		reverting: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = self.params.revision.textContent;

			if( !form ) {
				self.statelem.error( 'couldn\'t grab element "editform", aborting, this could indicate failed respons from the server' );
				return;
			}

			var optional_summary = prompt( "Please, if possible, specify a reason for the revert" );
			var summary = sprintf( "Reverted to revision %d by [[Special:Contributions/%s|%2$s]]%s", 
				self.params.revision.getAttribute( 'revid' ),
				self.params.revision.getAttribute( 'user' ),
				TwinkleConfig.summaryAd);
			summary += optional_summary ? "; " + optional_summary : '';
			var postData = {
				'wpMinoredit': TwinkleConfig.markRevertedPagesAsMinor.indexOf( 'torev' ) != -1 ? '' : undefined, 
				'wpWatchthis': TwinkleConfig.watchRevertedPages.indexOf( 'torev' ) != -1 ? '' : form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': summary,
				'wpTextbox1': text
			};
			Wikipedia.actionCompleted.redirect = wgPageName;
			Wikipedia.actionCompleted.notice = "Reversion completed"

			self.post( postData );
		}
	},
	main: function( self ) {

		var xmlDoc = self.responseXML;
		var revs = xmlDoc.evaluate( '//rev', xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );

		if( revs.snapshotLength < 1 ) {
			self.statitem.error( 'We have less than one additional revision, thus impossible to revert' );
			return;
		}
		var top = revs.snapshotItem(0);
		if( top.getAttribute( 'revid' ) < wgCurRevisionId ) {
			Status.error( 'Error', [ 'The recieved top revision id ', htmlNode( 'strong', top.getAttribute('revid') ), ' is less than our current revision id, this could indicate that the current revision has been deleted, the server is lagging, or that bad data has been recieved. Will stop proceeding at this point.' ] );
			return;
		}
		var index = 1;
		if( wgCurRevisionId != top.getAttribute('revid') ) {
			Status.warn( 'Warning', [ 'Latest revision ', htmlNode( 'strong', top.getAttribute('revid') ), ' doesn\'t equals our revision ', htmlNode( 'strong', wgCurRevisionId) ] );
			if( top.getAttribute( 'user' ) == self.params.user ) {
				switch( self.params.type ) {
				case 'vand':
					Status.info( 'Info', [ 'Latest revision is made by ', htmlNode( 'strong', self.params.user ) , ', as we assume vandalism, we continue to revert' ]);
					break;
				case 'afg':
					Status.warn( 'Warning', [ 'Latest revision is made by ', htmlNode( 'strong', self.params.user ) , ', as we assume good faith, we stop reverting, as the problem might have been fixed.' ]);
					return;
				default:
					Status.warn( 'Notice', [ 'Latest revision is made by ', htmlNode( 'strong', self.params.user ) , ', but we will stop reverting anyway.' ] );
					return;
				}
			}
			else if( 
				self.params.type == 'vand' && 
				WHITELIST.indexOf( top.getAttribute( 'user' ) ) != -1 && revs.snapshotLength > 1 &&
				revs.snapshotItem(1).getAttribute( 'pageId' ) == wgCurRevisionId 
			) {
				Status.info( 'Info', [ 'Latest revision is made by ', htmlNode( 'strong', top.getAttribute( 'user' ) ), ', a trusted bot, and the revision before was made by our vandal, so we proceed with the revert.' ] );
				index = 2;
			} else {
				Status.error( 'Error', [ 'Latest revision is made by ', htmlNode( 'strong', top.getAttribute( 'user' ) ), ', so it might already been reverted, stopping  reverting.'] );
				return;
			}

		}

		if( WHITELIST.indexOf( self.params.user ) != -1  ) {
			switch( self.params.type ) {
			case 'vand':
				Status.info( 'Info', [ 'Vandalism revert is choosen on ', htmlNode( 'strong', self.params.user ), ', as this is a whitelisted bot, we assume you wanted to revert vandalism made by the previous user instead.' ] );
				index = 2;
				vandal = revs.snapshotItem(1).getAttribute( 'user' );

				break;
			case 'agf':
				Status.warn( 'Notice', [ 'Good faith revert is choosen on ', htmlNode( 'strong', self.params.user ), ', as this is a whitelisted bot, it makes no sense at all to revert it as a good faith edit, will stop reverting.' ] );
				return;

				break;
			case 'norm':
			default:
				var cont = confirm( 'Normal revert is choosen, but the top user (' + self.params.user + ') is a whitelisted bot, do you want to revert the revision before instead?' );
				if( cont ) {
					Status.info( 'Info', [ 'Normal revert is choosen on ', htmlNode( 'strong', self.params.user ), ', as this is a whitelisted bot, and per confirm, we\'ll revert the previous revision instead.' ] );
					index = 2;
					self.params.user = revs.snapshotItem(1).getAttribute( 'user' );
				} else {
					Status.warn( 'Notice', [ 'Normal revert is choosen on ', htmlNode( 'strong', self.params.user ), ', this is a whitelisted bot, but per confirmation, revert on top revision will proceed.' ] );
				}
				break;
			}
		}
		var found = false;
		var count = 0;

		for( var i = index; i < revs.snapshotLength; ++i ) {
			++count;
			if( revs.snapshotItem(i).getAttribute( 'user' ) != self.params.user ) {
				found = i;
				break;
			}
		}


		if( ! found ) {
			self.statelem.error( [ 'No previous revision found, perhaps ', htmlNode( 'strong', self.params.user ), ' is the only contributor, or that the user has made more than ' + TwinkleConfig.revertMaxRevisions + ' edits in a row.' ] );
			return;

		}

		if( count == 0 ) {
			Status.error( 'Error', "We where to revert zero revisions. As that makes no sense, we'll stop reverting this time. It could be that the edit already have been reverted, but the revision id was still the same." );
			return;
		}

		var good_revision = revs.snapshotItem( found );

		if( 
			self.params.type != 'vand' && 
			count > 1  && 
			!confirm( self.params.user + ' has done ' + count + ' edits in a row. Are you sure you want to revert them all?' ) 
		) {
			Status.info( 'Notice', 'Stopping reverting per user input' );
			return;
		}

		self.params.count = count;

		self.params.goodid = good_revision.getAttribute( 'revid' );
		self.params.gooduser = good_revision.getAttribute( 'user' );


		self.statelem.status( [ ' revision ', htmlNode( 'strong', good_revision.getAttribute( 'revid' ) ), ' that was made ', htmlNode( 'strong', count ), ' revisions ago by ', htmlNode( 'strong', good_revision.getAttribute( 'user' ) ) ] );

		var query = {
			'action': 'query',
			'prop': 'revisions',
			'titles': wgPageName,
			'rvlimit': 1,
			'rvprop': 'content',
			'rvstartid': good_revision.getAttribute( 'revid' )
		}

		var wikipedia_api = new Wikipedia.api( [ 'Getting content for revision ', htmlNode( 'strong', good_revision.getAttribute( 'revid' ) ) ], query, twinklefluff.callbacks.grabbing );
		wikipedia_api.params = self.params;
		wikipedia_api.post();
	},
	grabbing: function( self ) {

		xmlDoc = self.responseXML;

		self.params.content = xmlDoc.evaluate( '//rev[1]', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;

		var query = {
			'title': wgPageName,
			'action': 'submit'
		};
		var wikipedia_wiki = new Wikipedia.wiki( 'Reverting page', query, twinklefluff.callbacks.reverting );
		wikipedia_wiki.params = self.params;
		wikipedia_wiki.get();
	},
	reverting: function( self ) {
		var doc = self.responseXML;

		var form = doc.getElementById( 'editform' );
		if( !form ) {
			self.statelem.error( 'couldn\'t grab element "editform", aborting, this could indicate failed respons from the server' );
			return;
		}

		var text = self.params.content;
		if( !text ) {
			self.statelem.error( 'we recieved no revision, something is wrong, bailing out!' );
			return;
		}

		var summary;

		switch( self.params.type ) {
		case 'agf':
			var extra_summary = prompt( "An optional comment for the edit summary:" );
			summary = sprintf( "Reverted [[WP:AGF|good faith]] edits by [[Special:Contributions/%s|%1$s]]%s.%s", 
				self.params.user, 
				extra_summary ? "; " + extra_summary.toUpperCaseFirstChar() : '',
				TwinkleConfig.summaryAd
			);
			break;
		case 'vand':
			summary = sprintf( "Reverted %d %s by [[Special:Contributions/%s|%3$s]] identified as [[WP:VAND|vandalism]] to last revision by [[User:%s|%4$s]].%s", 
				self.params.count, 
				self.params.count > 1 ? 'edits': 'edit',
				self.params.user,
				self.params.gooduser,
				TwinkleConfig.summaryAd
			);
			break;
		case 'norm':
			if( TwinkleConfig.offerReasonOnNormalRevert ) {
				var extra_summary = prompt( "An optional comment for the edit summary:" );
			}
			summary = sprintf( "Reverted %d %s by [[Special:Contributions/%s|%3$s]]%s.%s", 
				self.params.count, 
				self.params.count > 1 ? 'edits': 'edit',
				self.params.user,
				extra_summary ? "; " + extra_summary.toUpperCaseFirstChar() : '',
				TwinkleConfig.summaryAd 
			);
		}


		Status.info( 'Info', [ 'Open user talk page edit form for user ', htmlNode( 'strong', self.params.user ) ] );

		if( TwinkleConfig.openTalkPage.indexOf( self.params.type ) != -1 ) {

			var query = {
				'title': 'User talk:' + self.params.user,
				'action': 'edit',
				'vanarticle': wgPageName.replace(/_/g, ' '),
				'vanarticlerevid': wgCurRevisionId,
				'vanarticlegoodrevid': self.params.goodid,
				'type': self.params.type,
				'count': self.params.count
			}

			switch( TwinkleConfig.userTalkPageMode ) {
			case 'tab':
				window.open( mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ), '_tab' );
				break;
			case 'blank':
				window.open( mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ), '_blank', 'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' );
				break;
			case 'window':
			default:
				window.open( mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ), 'twinklewarnwindow', 'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' );
				break;
			}
		}

		var postData = {
			'wpMinoredit': TwinkleConfig.markRevertedPagesAsMinor.indexOf( self.params.type ) != -1  ? '' : undefined,
			'wpWatchthis': TwinkleConfig.watchRevertedPages.indexOf( self.params.type ) != -1 ? '' : form.wpWatchthis.checked ? '' : undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': summary,
			'wpTextbox1': text
		};

		Wikipedia.actionCompleted.redirect = wgPageName;
		Wikipedia.actionCompleted.notice = "Reversion completed"

		self.post( postData );
	}
}
// twinklewarn.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = {};
}

/**
 TwinkleConfig.showSharedIPNotice may take arguments:
 true: to show shared ip notice if an IP address
 false: to not print the notice
 */
if( typeof( TwinkleConfig.showSharedIPNotice ) == 'undefined' ) {
	TwinkleConfig.showSharedIPNotice = true;
}

/**
 TwinkleConfig.watchWarnings (boolean)
 if true, watch the page which has been dispatched an warning or notice, if false, default applies
 */
if( typeof( TwinkleConfig.watchWarnings ) == 'undefined' ) {
	TwinkleConfig.watchWarnings = true;
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

function twinklewarn() {
	if( wgNamespaceNumber == 3 || wgNamespaceNumber == 2 ) {
		mw.util.addPortletLink( 'p-cactions', "javascript:twinklewarn.callback()", "warn", "tw-warn", "Warn/Notify user", "i");
	}
}
$(twinklewarn);

twinklewarn.callback = function twinklewarnCallback() {
	var Window = new SimpleWindow( 600, 400 );
	Window.setTitle( "Warn/Notify user" ); 
	var form = new QuickForm( twinklewarn.callback.evaluate );

	var main_select = form.append( {
			type:'field',
			label:'Choose type of warning/notice to issue', 
			tooltip:'Choose first the main group you want to issue, then choose an appropriate type to issue.'
		} );

	var main_group = main_select.append( {
			type:'select',
			name:'main_group',
			event:twinklewarn.callback.change_category 
		} );

	if( userIsInGroup( 'sysop' ) ) {
		main_group.append( { type:'option', label:'Blocking', value:'block', selected:true } );
	}
	main_group.append( { type:'option', label:'General Note (1)', value:'level1' } );
	main_group.append( { type:'option', label:'Caution (2)', value:'level2' } );
	main_group.append( { type:'option', label:'Warning (3)', value:'level3' } );
	main_group.append( { type:'option', label:'Final warning (4)', value:'level4' } );
	main_group.append( { type:'option', label:'Only warning (4im)', value:'level4im' } );
	main_group.append( { type:'option', label:'Single issue notices', value:'singlenotice' } );
	main_group.append( { type:'option', label:'Single issue warnings', value:'singlewarn' } );

	main_select.append( { type:'select', name:'sub_group' } ); //Will be empty to begin with.

	form.append( { 
			type:'input',
			name:'article',
			label:'Linked article',
			value:( QueryString.exists( 'vanarticle' ) ? QueryString.get( 'vanarticle' ) : '' ),
			tooltip:'An  article might be linked to the notice, either it was a revert to said article that dispatched this notice. Leave empty for no artice to be linked'
		} );


	var more = form.append( { type:'field', label:'Fill in an optional reason and hit \"Submit\"' } );
	more.append( { type:'textarea', label:'More:', name:'reason', tooltip:'Perhaps a reason, or that a more detailed notice must be appended' } );
	more.append( { type:'submit', label:'Submit' } );
	var result = form.render();
	Window.setContent( result );
	Window.display();
	result.main_group.root = result;

	// We must init the first choice (General Note);
	var evt = document.createEvent( "Event" );
	evt.initEvent( 'change', true, true );
	result.main_group.dispatchEvent( evt );

}

// This is all the messages that might be dispatched by the code
twinklewarn.messages = {
	block: {
		"uw-block1": {
			'label':"Level 1",
			'summary':"You have been temporarily blocked"
		},
		"uw-block2": {
			'label':"Level 2",
			'summary':"You have been blocked"
		},
		"uw-block3": {
			'label':"Indefinite",
			'summary':"You have been indefinitely blocked"
		},
		"uw-ablock": {
			'label':"Anon block",
			'summary':"Your IP address has been blocked"
		},
		"schoolblock": {
			'label':"School block",
			'summary':"Your IP address has been blocked"
		},
		"uw-sblock": {
			'label':"Spam block",
			'summary':"You have been blocked for spamming"
		},
		"uw-vblock": {
			'label':"Vandalism",
			'summary':"You have been blocked for vandalism"
		},
		"uw-voablock": {
			'label':"Vandalism-only account",
			'summary':"You have been blocked for vandalism"
		},
		"uw-dblock": {
			'label':"Delete block",
			'summary':"You have been blocked for deletion"
		},
		"uw-3block": {
			'label':"3RR block",
			'summary':"You have been blocked for violation of the [[WP:3RR|3RR]] rule"
		},
		"uw-lblock": {
			'label':"Legal block",
			'summary':"You have been blocked for [[WP:NLT|legal threats]]"
		},
		"uw-tblock": {
			'label':"Death threats block",
			'summary':"You have been blocked for death threats"
		},
		"uw-ublock": {
			'label':"Username block",
			'summary':"You have been blocked for violation of the [[WP:U|username policy]]"
		},
		"uw-uhblock": {
			'label':"Username hardblock",
			'summary':"You have been blocked for violation of the [[WP:U|username policy]]"
		},
		"proxyblock": {
			'label':"Proxy block",
			'summary':"You have been blocked for operating on an [[WP:NOP|open proxy]]"
		},
		"tor": {
			'label':"Tor block",
			'summary':"You have been blocked for operating on a [[WP:NOP|TOR exit node]]"
		},
		"zombie proxy": {
			'label':"Zombie proxy",
			'summary':"You have been blocked as a [[zombie computer|compromised computer]]"
		},
		"user talk:east718/Editcounter": {
			'label':"Troll block",
			'summary':"go to sleep"
		}
	},
	level1: {
		"uw-vandalism1": { 
			label:"Vandalism", 
			summary:"General note: Vandalism" 
		},
		"uw-test1": { 
			label:"Editing tests", 
			summary:"General note: Editing tests" 
		},
		"uw-delete1": { 
			label:"Page blanking, removal of content or templates", 
			summary:"General note: Page blanking, removal of content or templates" 
		},
		"uw-joke1": { 
			label:"Using improper humor", 
			summary:"General note: Using improper humor" 
		},
		"uw-create1": { 
			label:"Creating inappropriate pages", 
			summary:"General note: Creating inappropriate pages" 
		},
		"uw-upload1": { 
			label:"Uploading inappropriate images", 
			summary:"General note: Uploading inappropriate images" 
		},
		"uw-image1": { 
			label:"Image-related vandalism", 
			summary:"General note: Image-related vandalism" 
		},
		"uw-spam1": { 
			label:"Adding spam links", 
			summary:"General note: Adding spam links" 
		},
		"uw-ad1": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"General note: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov1": { 
			label:"Not adhering to neutral point of view", 
			summary:"General note: Not adhering to neutral point of view" 
		},
		"uw-unsor1": { 
			label:"Addition of unsourced material without proper citations", 
			summary:"General note: Addition of unsourced material without proper citations" 
		},
		"uw-error1": { 
			label:"Introducing deliberate factual errors", 
			summary:"General note: Introducing deliberate factual errors" 
		},
		"uw-biog1": { 
			label:"Adding unreferenced controversial information about living persons", 
			summary:"General note: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam1": { 
			label:"Defamation not specifically directed", 
			summary:"General note: Defamation not specifically directed" 
		},
		"uw-uncen1": { 
			label:"Censorship of material", 
			summary:"General note: Censorship of material" 
		},
		"uw-mos1": { 
			label:"Manual of style", 
			summary:"General note: Formatting, date, language, etc (Manual of style)" 
		},
		"uw-move1": { 
			label:"Page moves", 
			summary:"General note: Page moves" 
		},
		"uw-chat1": { 
			label:"Using talk page as forum", 
			summary:"General note: Using talk page as forum" 
		},
		"uw-tpv1": { 
			label:"Refactoring others' talk page comments", 
			summary:"General note: Refactoring others' talk page comments" 
		},
		"uw-afd1": { 
			label:"Removing \{\{afd\}\} templates", 
			summary:"General note: Removing \{\{afd\}\} templates" 
		},
		"uw-speedy1": { 
			label:"Removing \{\{speedy deletion\}\} templates", 
			summary:"General note: Removing \{\{speedy deletion\}\} templates" 
		},
		"uw-npa1": { 
			label:"Personal attack directed at a specific editor", 
			summary:"General note: Personal attack directed at a specific editor" 
		},
		"uw-agf1": { 
			label:"Not assuming good faith", 
			summary:"General note: Not assuming good faith" 
		},
		"uw-own1": { 
			label:"Ownership of articles", 
			summary:"General note: Ownership of articles"
		}
	},
	level2: {
		"uw-vandalism2": { 
			label:"Vandalism", 
			summary:"Caution: Vandalism" 
		},
		"uw-test2": { 
			label:"Editing tests", 
			summary:"Caution: Editing tests" 
		},
		"uw-delete2": { 
			label:"Page blanking, removal of content or templates", 
			summary:"Caution: Page blanking, removal of content or templates" 
		},
		"uw-joke2": { 
			label:"Using improper humor", 
			summary:"Caution: Using improper humor" 
		},
		"uw-create2": { 
			label:"Creating inappropriate pages", 
			summary:"Caution: Creating inappropriate pages" 
		},
		"uw-upload2": { 
			label:"Uploading inappropriate images", 
			summary:"Caution: Uploading inappropriate images" 
		},
		"uw-image2": { 
			label:"Image-related vandalism", 
			summary:"Caution: Image-related vandalism" 
		},
		"uw-spam2": { 
			label:"Adding spam links", 
			summary:"Caution: Adding spam links" 
		},
		"uw-ad2": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"Caution: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov2": { 
			label:"Not adhering to neutral point of view", 
			summary:"Caution: Not adhering to neutral point of view" 
		},
		"uw-unsor2": { 
			label:"Addition of unsourced material without proper citations", 
			summary:"Caution: Addition of unsourced material without proper citations" 
		},
		"uw-error2": { 
			label:"Introducing deliberate factual errors", 
			summary:"Caution: Introducing deliberate factual errors" 
		},
		"uw-biog2": { 
			label:"Adding unreferenced controversial information about living persons", 
			summary:"Caution: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam2": { 
			label:"Defamation not specifically directed", 
			summary:"Caution: Defamation not specifically directed" 
		},
		"uw-uncen2": { 
			label:"Censorship of material", 
			summary:"Caution: Censorship of material" 
		},
		"uw-mos2": { 
			label:"Manual of style", 
			summary:"Caution: Formatting, date, language, etc (Manual of style)" 
		},
		"uw-move2": { 
			label:"Page moves", 
			summary:"Caution: Page moves" 
		},
		"uw-chat2": { 
			label:"Using talk page as forum", 
			summary:"Caution: Using talk page as forum" 
		},
		"uw-tpv2": { 
			label:"Refactoring others' talk page comments", 
			summary:"Caution: Refactoring others' talk page comments" 
		},
		"uw-afd2": { 
			label:"Removing \{\{afd\}\} templates", 
			summary:"Caution: Removing \{\{afd\}\} templates" 
		},
		"uw-speedy2": { 
			label:"Removing \{\{speedy deletion\}\} templates", 
			summary:"Caution: Removing \{\{speedy deletion\}\} templates" 
		},
		"uw-npa2": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Caution: Personal attack directed at a specific editor" 
		},
		"uw-agf2": { 
			label:"Not assuming good faith", 
			summary:"Caution: Not assuming good faith" 
		},
		"uw-own2": { 
			label:"Ownership of articles", 
			summary:"Caution: Ownership of articles"
		}
	},
	level3: {
		"uw-vandalism3": { 
			label:"Vandalism", 
			summary:"Warning: Vandalism" 
		},
		"uw-test3": { 
			label:"Editing tests", 
			summary:"Warning: Editing tests" 
		},
		"uw-delete3": { 
			label:"Page blanking, removal of content or templates", 
			summary:"Warning: Page blanking, removal of content or templates" 
		},
		"uw-joke3": { 
			label:"Using improper humor", 
			summary:"Warning: Using improper humor" 
		},
		"uw-create3": { 
			label:"Creating inappropriate pages", 
			summary:"Warning: Creating inappropriate pages" 
		},
		"uw-upload3": { 
			label:"Uploading inappropriate images", 
			summary:"Warning: Uploading inappropriate images" 
		},
		"uw-image3": { 
			label:"Image-related vandalism", 
			summary:"Warning: Image-related vandalism" 
		},
		"uw-spam3": { 
			label:"Adding spam links", 
			summary:"Warning: Adding spam links" 
		},
		"uw-ad3": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"Warning: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov3": { 
			label:"Not adhering to neutral point of view", 
			summary:"Warning: Not adhering to neutral point of view" 
		},
		"uw-unsor3": { 
			label:"Addition of unsourced material without proper citations", 
			summary:"Warning: Addition of unsourced material without proper citations" 
		},
		"uw-error3": { 
			label:"Introducing deliberate factual errors", 
			summary:"Warning: Introducing deliberate factual errors" 
		},
		"uw-biog3": { 
			label:"Adding unreferenced controversial information about living persons", 
			summary:"Warning: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam3": { 
			label:"Defamation not specifically directed", 
			summary:"Warning: Defamation not specifically directed" 
		},
		"uw-uncen3": { 
			label:"Censorship of material", 
			summary:"Warning: Censorship of material" 
		},
		"uw-mos3": { 
			label:"Manual of style", 
			summary:"Warning: Formatting, date, language, etc (Manual of style)" 
		},
		"uw-move3": { 
			label:"Page moves", 
			summary:"Warning: Page moves" 
		},
		"uw-chat3": { 
			label:"Using talk page as forum", 
			summary:"Warning: Using talk page as forum" 
		},
		"uw-tpv3": { 
			label:"Refactoring others' talk page comments", 
			summary:"Warning: Refactoring others' talk page comments" 
		},
		"uw-afd3": { 
			label:"Removing \{\{afd\}\} templates", 
			summary:"Warning: Removing \{\{afd\}\} templates" 
		},
		"uw-speedy3": { 
			label:"Removing \{\{speedy deletion\}\} templates", 
			summary:"Warning: Removing \{\{speedy deletion\}\} templates" 
		},
		"uw-npa3": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Warning: Personal attack directed at a specific editor" 
		},
		"uw-agf3": { 
			label:"Not assuming good faith", 
			summary:"Warning: Not assuming good faith" 
		},
		"uw-own3": { 
			label:"Ownership of articles", 
			summary:"Warning: Ownership of articles"
		}
	},
	level4: {
		"uw-vandalism4": { 
			label:"Vandalism", 
			summary:"Final warning: Vandalism" 
		},
		"uw-delete4": { 
			label:"Page blanking, removal of content or templates", 
			summary:"Final warning: Page blanking, removal of content or templates" 
		},
		"uw-joke4": { 
			label:"Using improper humor", 
			summary:"Final warning: Using improper humor" 
		},
		"uw-create4": { 
			label:"Creating inappropriate pages", 
			summary:"Final warning: Creating inappropriate pages" 
		},
		"uw-upload4": { 
			label:"Uploading inappropriate images", 
			summary:"Final warning: Uploading inappropriate images" 
		},
		"uw-image4": { 
			label:"Image-related vandalism", 
			summary:"Final warning: Image-related vandalism" 
		},
		"uw-spam4": { 
			label:"Adding spam links", 
			summary:"Final warning: Adding spam links" 
		},
		"uw-ad4": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"Final warning: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov4": { 
			label:"Not adhering to neutral point of view", 
			summary:"Final warning: Not adhering to neutral point of view" 
		},
		"uw-biog4": { 
			label:"Adding unreferenced controversial information about living persons", 
			summary:"Final warning: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam4": { 
			label:"Defamation not specifically directed", 
			summary:"Final warning: Defamation not specifically directed" 
		},
		"uw-move4": { 
			label:"Page moves", 
			summary:"Final warning: Page moves" 
		},
		"uw-chat4": { 
			label:"Using talk page as forum", 
			summary:"Final warning: Using talk page as forum" 
		},
		"uw-afd4": { 
			label:"Removing \{\{afd\}\} templates", 
			summary:"Final warning: Removing \{\{afd\}\} templates" 
		},
		"uw-speedy4": { 
			label:"Removing \{\{speedy deletion\}\} templates", 
			summary:"Final warning: Removing \{\{speedy deletion\}\} templates" 
		},
		"uw-npa4": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Final warning: Personal attack directed at a specific editor"
		}
	},
	level4im: {
		"uw-vandalism4im": { 
			label:"Vandalism", 
			summary:"Only warning: Vandalism" 
		},
		"uw-delete4im": { 
			label:"Page blanking, removal of content or templates", 
			summary:"Only warning: Page blanking, removal of content or templates" 
		},
		"uw-create4im": { 
			label:"Creating inappropriate pages", 
			summary:"Only warning: Creating inappropriate pages" 
		},
		"uw-upload4im": { 
			label:"Uploading inappropriate images", 
			summary:"Only warning: Uploading inappropriate images" 
		},
		"uw-image4im": { 
			label:"Image-related vandalism", 
			summary:"Only warning: Image-related vandalism" 
		},
		"uw-spam4im": { 
			label:"Adding spam links", 
			summary:"Only warning: Adding spam links" 
		},
		"uw-defam4im": { 
			label:"Defamation not specifically directed", 
			summary:"Only warning: Defamation not specifically directed" 
		},
		"uw-move4im": { 
			label:"Page moves", 
			summary:"Only warning: Page moves" 
		},
		"uw-npa4im": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Only warning: Personal attack directed at a specific editor"
		}
	},
	singlenotice: {
		"uw-2redirect": { 
			label:"Creating double redirects through bad page moves", 
			summary:"Notice: Creating double redirects through bad page moves" 
		},
		"uw-aiv": { 
			label:"Bad AIV report", 
			summary:"Notice: Bad AIV report" 
		},
		"uw-articlesig": { 
			label:"Adding signatures to article space", 
			summary:"Notice: Adding signatures to article space" 
		},
		"uw-autobiography": { 
			label:"Creating autobiographies", 
			summary:"Notice: Creating autobiographies" 
		},
		"uw-badcat": { 
			label:"Adding incorrect categories", 
			summary:"Notice: Adding incorrect categories" 
		},
		"uw-bite": { 
			label:"\"Biting\" newcomers", 
			summary:"Notice: \"Biting\" newcomers" 
		},
		"uw-c&pmove": { 
			label:"Cut and paste moves", 
			summary:"Notice: Cut and paste moves" 
		},
		"uw-coi": { 
			label:"Conflict of Interest", 
			summary:"Notice: Conflict of Interest" 
		},
		"uw-date": { 
			label:"Unnecessarily changing date formats", 
			summary:"Notice: Unnecessarily changing date formats" 
		},
		"uw-editsummary": { 
			label:"Not using edit summary", 
			summary:"Notice: Not using edit summary" 
		},
		"uw-english": { 
			label:"Not communicating in English", 
			summary:"Notice: Not communicating in English" 
		},
		"uw-italicize": { 
			label:"Italicize books, films, albums, magazines, TV series, etc within articles", 
			summary:"Notice: Italicize books, films, albums, magazines, TV series, etc within articles" 
		},
		"uw-lang": { 
			label:"Unnecessarily changing between British and American English", 
			summary:"Notice: Unnecessarily changing between British and American English" 
		},
		"uw-linking": { 
			label:"For excessive addition of redlinks or repeated blue links", 
			summary:"Notice: For excessive addition of redlinks or repeated blue links" 
		},
		"uw-minor": { 
			label:"Incorrect use of minor edits check box", 
			summary:"Notice: Incorrect use of minor edits check box" 
		},
		"uw-nonfree": { 
			label:"Uploading replaceable non-free images", 
			summary:"Notice: Uploading replaceable non-free images" 
		},
		"uw-notaiv": { 
			label:"Do not report complex abuse to AIV", 
			summary:"Notice: Do not report complex abuse to AIV" 
		},
		"uw-notvote": { 
			label:"We use consensus, not voting", 
			summary:"Notice: We use consensus, not voting" 
		},
		"uw-preview": { 
			label:"Use preview button to avoid mistakes", 
			summary:"Notice: Use preview button to avoid mistakes" 
		},
		"uw-selfrevert": { 
			label:"Reverting self tests", 
			summary:"Notice: Reverting self tests" 
		},
		"uw-sandbox": { 
			label:"Removal of the Sandbox header", 
			summary:"Notice: Removal of the Sandbox header" 
		},
		"uw-socialnetwork": { 
			label:"Wikipedia is not a social network", 
			summary:"Notice: Wikipedia is not a social network" 
		},
		"uw-subst": { 
			label:"Remember to subst: templates", 
			summary:"Notice: Remember to subst: templates" 
		},
		"uw-talkinarticle": { 
			label:"Talk in article", 
			summary:"Notice: Talk in article" 
		},
		"uw-tilde": { 
			label:"Not signing posts", 
			summary:"Notice: Not signing posts" 
		},
		"uw-uaa": { 
			label:"Reporting of username to WP:UAA not accepted", 
			summary:"Notice: Reporting of username to WP:UAA not accepted" 
		},
		"uw-warn": { 
			label:"Warning vandals", 
			summary:"Notice: Warning vandals"
		}
	},
	singlewarn: {
		"uw-3rr": { 
			label:"Potentially violating the three revert rule", 
			summary:"Warning: Potentially violating the three revert rule" 
		},/*
		"uw-attack": { 
			label:"Creating attack pages", 
			summary:"Warning: Creating attack pages" 
		},*/
		"uw-bv": { 
			label:"Blatant vandalism", 
			summary:"Warning: Blatant vandalism" 
		},
		"uw-canvass": { 
			label:"Canvassing", 
			summary:"Warning: Canvassing" 
		},
		"uw-copyright": { 
			label:"Copyright violation", 
			summary:"Warning: Copyright violation" 
		},
		"uw-copyright-link": { 
			label:"Linking to copyrighted works violation", 
			summary:"Warning: Linking to copyrighted works violation" 
		},
		"uw-hoax": { 
			label:"Creating hoaxes", 
			summary:"Warning: Creating hoaxes" 
		},
		"uw-legal": { 
			label:"Making legal threats", 
			summary:"Warning: Making legal threats" 
		},
		"uw-longterm": { 
			label:"Long term pattern of vandalism", 
			summary:"Warning: Long term pattern of vandalism" 
		},
		"uw-multipleIPs": { 
			label:"Usage of multiple IPs", 
			summary:"Warning: Usage of multiple IPs" 
		},
		"uw-pinfo": { 
			label:"Personal info", 
			summary:"Warning: Personal info" 
		},
		"uw-redirect": { 
			label:"Creating malicious redirects", 
			summary:"Warning: Creating malicious redirects"
		},
		"uw-upv": { 
			label:"Userpage vandalism", 
			summary:"Warning: Userpage vandalism"
		},
		"uw-tempabuse": { 
			label:"Improper use of warning or blocking template", 
			summary:"Warning: Improper use of warning or blocking template"
		},
		"uw-trivia": { 
			label:"Adding useless trivia", 
			summary:"Warning: Adding useless trivia"
		},
		"uw-wrongsummary": { 
			label:"Using inaccurate or inappropriate edit summaries", 
			summary:"Warning:Using inaccurate or inappropriate edit summaries"
		}
	}
};


twinklewarn.callback.change_category = function twinklewarnCallbackChangeCategory(e) {
	var value = e.target.value;
	var sub_group = e.target.root.sub_group;
	var messages = twinklewarn.messages[ value ];
	sub_group.main_group = value;
	var old_subvalue = sub_group.value;
	if( old_subvalue ) {
		old_subvalue = old_subvalue.replace(/\d*(im)?$/, '' );
		var old_subvalue_re = new RegExp( RegExp.escape( old_subvalue ) + "(\\d*(?:im)?)$" );
	}

	while( sub_group.hasChildNodes() ){
		sub_group.removeChild( sub_group.firstChild );
	}

	for( var i in messages ) {
		var selected = false;
		if( old_subvalue && old_subvalue_re.test( i ) ) {
			selected = true;
		}
		var elem = new QuickForm.element( { type:'option', label:messages[i].label, value:i, selected: selected } );
		
		sub_group.appendChild( elem.render() );
	}

	if( value == 'block' ) {
		var more = new QuickForm.element( {
				type: 'input',
				name: 'block_timer',
				label: 'Period of blocking: ',
				tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...'
			} );
		e.target.root.insertBefore( more.render(), e.target.root.lastChild );
		e.target.root.article.disabled = true;
	} else if( e.target.root.block_timer ) {
		e.target.root.removeChild( e.target.root.block_timer.parentNode );
		e.target.root.article.disabled = false;
	}
}

twinklewarn.callbacks = {
	main: function( self ) {
		var form = self.responseXML.getElementById( 'editform' );
		var text = form.wpTextbox1.value;

		var history_re = /\<\!\-\-\ Template\:(uw\-.*?)\ \-\-\>.*?(\d{1,2}:\d{1,2}, \d{1,2} \w+ \d{4}) \(UTC\)/g;
		var history = {};
		var latest = { date:new Date( 0 ), type:'' };

		var current;


		while( ( current = history_re.exec( text ) ) != undefined ) {
			var current_date = new Date( current[2] + ' UTC' );
			if( !( current[1] in history ) ||  history[ current[1] ] < current_date ) {
				history[ current[1] ] = current_date;
			}
			if( current_date > latest.date ) {
				latest.date = current_date;
				latest.type = current[1];
			}
		}

		var date = new Date();

		if( self.params.sub_group in history ) {
			var temp_time = new Date( history[ self.params.sub_group ] );
			temp_time.setUTCHours( temp_time.getUTCHours() + 24 );

			if( temp_time > date ) {
				Status.info( 'Info', "an identical " + self.params.sub_group + " has been issued in the last 24 hours" );
				if( !confirm( "Will you still add a warning/notice?" ) ) {
					self.statelem.info( 'aborted per user request' );
					return;
				}
			}
		}

		latest.date.setUTCMinutes( latest.date.getUTCMinutes() + 1 ); // after long debate, one minute is max

		if( latest.date > date ) {
			Status.info('Info', "a " + latest.type + " has been issued in the last minute" );
				if( !confirm( "Will you still add a warning/notice?" ) ) {
					self.statelem.info( 'aborted per user request' );
					return;
				}
		}
		


		var mainheaderRe = /==+\\s*Warnings\\s*==+/;
		var headerRe = new RegExp( "^==+\\s*" + date.getUTCMonthName() + "\\s+" + date.getUTCFullYear() + "\\s*==+", 'm' );

		if( text.length > 0 ) {
			text += "\n";
		}

		/*if( !headerRe.exec( text ) ) {
			Status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' );
			text += "== " + date.getUTCMonthName() + " " + date.getUTCFullYear() + " ==\n";
		}*/
		if( self.params.main_group == 'block' ) {
			var time = null;
			if( /te?mp|^\s*$|min/.exec( self.params.block_timer ) ) {
				time = '';
			} else if( /indef|\*|max/.exec( self.params.block_timer ) ) {
				time = '|indef=yes';
			} else {
				time = '|time=' + self.params.block_timer;
			}

			text += "\{\{subst:" + self.params.sub_group + time + (self.params.reason ? '|reason=' + self.params.reason : '' ) + "|sig=true\}\}";
		} else {
			text += "\{\{subst:" + self.params.sub_group + ( self.params.article ? '|' + self.params.article : '' ) + "\}\}" + (self.params.reason ? " ''" + self.params.reason + "'' ": ' ' ) + "\~\~\~";
		}

		if ( TwinkleConfig.showSharedIPNotice && isIPAddress( wgTitle ) ) {
			Status.info( 'Info', 'Adding a shared ip notice' );
			switch( QueryString.get( 'type' ) ) {
			case 'vand':
				text +=  "\n:''If this is a shared [[IP address]], and you didn't make any [[Wikipedia:vandalism|unconstructive]] edits, consider [[Wikipedia:Why create an account?|creating an account]] for yourself so you can avoid further irrelevant warnings.'' ";
				break;
			default:
				text +=  "\n:''If this is a shared [[IP address]], and you didn't make the edit, consider [[Wikipedia:Why create an account?|creating an account]] for yourself so you can avoid further irrelevant notices.'' ";
				break;
			}
		}
		var postData = {
			'wpMinoredit': form.wpMinoredit.checked ? 1 : undefined,
			'wpWatchthis': TwinkleConfig.watchWarnings ? 1 : form.wpWatchthis.checked ? 1 : undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': twinklewarn.messages[self.params.main_group][self.params.sub_group].summary + ( self.params.article ? ' on [[' + self.params.article + ']]'  : '' ) + '.' + TwinkleConfig.summaryAd,
			'wpTextbox1': text
		};

		self.post( postData );
	}
}

twinklewarn.callback.evaluate = function twinklewarnCallbackEvaluate(e) {

	// First, grab all the values provided by the form
	
	var params = {
		reason: e.target.reason.value,
		main_group: e.target.main_group.value,
		sub_group: e.target.sub_group.value,
		article: e.target.article.value.replace( /^(Image|Category):/i, ':$1:' ),
		block_timer: e.target.block_timer ? e.target.block_timer.value : null
	}

	Status.init( e.target );

	var query = { 
		'title': 'User talk:' + wgTitle, 
		'action': 'submit'
	};
	Wikipedia.actionCompleted.redirect = wgPageName;
	Wikipedia.actionCompleted.notice = "Warning complete, reloading talk page in some seconds";
	var wikipedia_wiki = new Wikipedia.wiki( 'User talk page modification', query, twinklewarn.callbacks.main );
	wikipedia_wiki.params = params;
	wikipedia_wiki.get();
}

// twinklespeedy.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = {};
}

/**
 TwinkleConfig.summaryAd (string)en.wikipedia.org
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.deletionSummaryAd (string)
 If ad should be added or not to deletion summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.deletionSummaryAd ) == 'undefined' ) {
	TwinkleConfig.deletionSummaryAd = " using [[WP:TW|TW]]";
}


/**
 TwinkleConfig.watchSpeedyPages (array)
 What types of actions that should result in forced addition to watchlist
 */
if( typeof( TwinkleConfig.watchSpeedyPages ) == 'undefined' ) {
	TwinkleConfig.watchSpeedyPages = [ 'g3', 'g5', 'g10', 'g11', 'g12' ];
}

/**
 TwinkleConfig.markSpeedyPagesAsMinor (boolean)
 If, when applying speedy template to page, to mark the edit as minor, default true
 */
if( typeof( TwinkleConfig.markSpeedyPagesAsMinor ) == 'undefined' ) {
	TwinkleConfig.markSpeedyPagesAsMinor = true;
}

/**
 TwinkleConfig.notifyUserOnSpeedyDeletionNomination (array)
 What types of actions that should result that the author of the page should be notified of nomination
 */
if( typeof( TwinkleConfig.notifyUserOnSpeedyDeletionNomination ) == 'undefined' ) {
	TwinkleConfig.notifyUserOnSpeedyDeletionNomination = [ 'g1', 'g2', 'g3', 'g4', 'g10', 'g11', 'g12', 'a1', 'a2', 'a3', 'a5', 'a7', 'i1', 'i2', 'i3', 'i4', 'i5', 'i6', 'i7', 'i8', 'i9', 'u3', 't1', 'p2' ];
}

/**
 TwinkleConfig.userTalkPageMode may take arguments:
 'window': open a new window, remmenber the opened window
 'tab': opens in a new tab, if possible.
 'blank': force open in a new window, even if a such window exist
 */
if( typeof( TwinkleConfig.userTalkPageMode ) == 'undefined' ) {
	TwinkleConfig.userTalkPageMode = 'window';
}

/**
 TwinkleConfig.deleteTalkPageOnDelete
 If talk page if exists should also be deleted (CSD G8) when spedying a page (admin only)
 */
if( typeof( TwinkleConfig.deleteTalkPageOnDelete ) == 'undefined' ) {
	TwinkleConfig.deleteTalkPageOnDelete = false;
}

/**
 TwinkleConfig.toolboxButtons (string)
 If id defined in this array, the button of the action is located inthe toolbox instead of in
 the actions bar.
 */
if( typeof( TwinkleConfig.toolboxButtons ) == 'undefined' ) {
	TwinkleConfig.toolboxButtons = [];
}

/**
 TwinkleConfig.orphanNormalPagesOnSpeedyDelete (hash)
 Defines if all backlinks to a page should be removed.
 property 'exclude' defined actions not to orphan
 */
if( typeof( TwinkleConfig.orphanBacklinksOnSpeedyDelete ) == 'undefined' ) {
	TwinkleConfig.orphanBacklinksOnSpeedyDelete = { exclude: ['g6'], orphan:true };
}

function twinklespeedy() {
	if( wgNamespaceNumber < 0 || wgCurRevisionId == false ) {
		return;
	}
	if( userIsInGroup( 'sysop' ) ) {
		mw.util.addPortletLink( 'p-cactions', "javascript:twinklespeedy.callback()", "csd", "tw-csd", "Speedy delete according to WP:CSD", "o");
	} else {
		mw.util.addPortletLink( 'p-cactions', "javascript:twinklespeedy.callback()", "csd", "tw-csd", "Request speedy deletion according to WP:CSD", "");
	}	
}
$(twinklespeedy);

twinklespeedy.callback = function twinklespeedyCallback() {
	var Window = new SimpleWindow( 800, 400 );
	Window.setTitle( "Choose criteria for speedy deletion" );
	var form = new QuickForm( userIsInGroup( 'sysop' ) ? twinklespeedy.callback.evaluateSysop : twinklespeedy.callback.evaluateUser, 'change' );
	if( userIsInGroup( 'sysop' ) ) {
		form.append( {
				type: 'checkbox',
				list: [
					{
						label: 'Tag page only, don\'t delete',
						value: 'tag_only',
						name: 'tag_only',
						tooltip: 'If you just want to tag the page, instead of deleting it now',
						event: function( event ) {
							event.target.form.notify.disabled = ! event.target.checked;
							event.stopPropagation();
						}
					},
					{
						label: 'Orphan backlinks',
						value: 'orphan_backlinks',
						name: 'orphan_backlinks',
						tooltip: 'If you want to orphan all backlinks to current page, if checked, excludes will still apply.',
						checked: TwinkleConfig.orphanBacklinksOnSpeedyDelete.orphan,
						event: function( event ) {
							TwinkleConfig.orphanBacklinksOnSpeedyDelete.orphan = event.target.checked;
							event.stopPropagation();
						}
					}
				]
			} );
	}

	form.append( {
			type: 'checkbox',
			list: [
				{
					label: 'Notify if possible',
					value: 'notify',
					name: 'notify',
					tooltip: 'If a notification if defined in the configuration, then notify if this is true, else no notify.',
					checked: true,
					disabled: userIsInGroup( 'sysop' ),
					event: function( event ) {
						event.stopPropagation();
					}
				}
			]
		}
	);
if (SHITTYHACK == false) {
	if( wgNamespaceNumber ==  6 || wgNamespaceNumber == 7) {
		form.append( {type:'header', label:'Images/Media' } );
		form.append ( {
				type: 'radio',
				name: 'csd',
				list: [
					{ 
						label: 'I1: Redundant image',
						value: 'redundantimage',
						tooltip: 'Any image that is a redundant copy, in the same image file format and same or lower resolution, of something else on Wikipedia. Likewise, other media that is a redundant copy, in the same format and of the same or lower quality. This does not apply to images duplicated on Wikimedia Commons, because of licence issues; these should be tagged with \{\{subst:ncd|Image:newname.ext\}\} or \{\{subst:ncd\}\} instead' 
					},
					{ 
						label: 'I2: Corrupt or empty image',
						value: 'noimage', 
						tooltip: 'Before deleting this type of image, verify that the MediaWiki engine cannot read it by previewing a resized thumbnail of it. This also includes empty (i.e., no content) image description pages for Commons images' 
					},
					{ 
						label: 'I3: Improper license',
						value: 'noncom',
						tooltip: '"Images licensed as "for non-commercial use only", "non-derivative use" or "used with permission" that were uploaded on or after 2005-05-19, except where they have been shown to comply with the limited standards for the use of non-free content. This includes images licensed under a "Non-commercial Creative Commons License". Such images uploaded before 2005-05-19 may also be speedily deleted if they are not used in any articles'
					},
					{
						label: 'I4: Lack of licensing information', 
						value: 'unksource',
						tooltip: 'Images in category "Images with unknown source", "Images with unknown copyright status", or "Images with no copyright tag" that have been tagged with a template that places them in the category for more than seven days, regardless of when uploaded. Note, users sometimes specify their source in the upload summary, so be sure to check the circumstances of the image'
					},
					{
						label: 'I5: Unused unfree copyrighted images',
						value: 'unfree',
						tooltip: 'Images and other media that are not under a free license or in the public domain that are not used in any article and that have been tagged with a template that places them in a dated subcategory of Category:Orphaned fairuse images for more than seven days. Reasonable exceptions may be made for images uploaded for an upcoming article. Use \{\{subst:orfud\}\} to tag images for forthcoming deletion' 
					},
					{
						label: 'I6: Missing fair use rationale',
						value: 'norat',
						tooltip: 'Any image or media without a fair use rationale may be deleted seven days after it is uploaded. Boilerplate fair use templates do not constitute a fair use rationale. Images and other media uploaded before 2006-05-04 should not be deleted immediately; instead, the uploader should be notified that a fair use rationale is needed. Images or other media uploaded after 2006-05-04 can be tagged with \{\{subst:nrd\}\}, and the uploader notified with \{\{subst:missing rationale|Image:image name\}\}. Such images can be found in the dated subcategories of Category:Images with no fair use rationale'
					},
					{ 
						label: 'I7: Invalid fair use claim',
						value: 'badfairuse', 
						tooltip: 'Any image or media with a clearly invalid fair use tag (such as a \{\{logo\}\} tag on a photograph of a mascot) may be deleted at any time. Media that fail any part of the non-free content criteria and were uploaded after 2006-07-13 may be deleted forty-eight hours after notification of the uploader. For media uploaded before 2006-07-13 or tagged with the \{\{Replaceable fair use\}\} template, the uploader will be given seven days to comply with this policy after being notified' 
					},
					{
						label: 'I7: Invalid image description page',
						value: 'bad10c', 
						tooltip: 'Any image or media with a clearly invalid fair use tag (such as a \{\{logo\}\} tag on a photograph of a mascot) may be deleted at any time. Media that fail any part of the non-free content criteria and were uploaded after 2006-07-13 may be deleted forty-eight hours after notification of the uploader. For media uploaded before 2006-07-13 or tagged with the \{\{Replaceable fair use\}\} template, the uploader will be given seven days to comply with this policy after being notified' 
					},
					{
						label: 'I8: Images available as bit-for-bit identical copies on the Wikimedia Commons',
						value: 'nowcommons',
						tooltip: 'Provided the following conditions are met: 1: The image\'s license and source status is beyond reasonable doubt, and the license is undoubtedly accepted at Commons. 2: All information on the image description page is present on the Commons image description page. That includes the complete upload history with links to the uploader\'s local user pages. 3: The image is not protected, and the image description page does not contain a request not to move it to Commons. 4: The image has been marked with Template:NowCommons for at least one week. Waiting one week is not necessary if it was the uploader who moved the image and marked it. 5: If the image is available on Commons under a different name than locally, all local references to the image must be updated to point to the title used at Commons. 6: For \{\{c-uploaded\}\} images: They may be speedily deleted as soon as they are off the Main Page.'
					},
					{
						label: 'I9: Blatant copyright infringement',
						value: 'imgcopyvio',
						tooltip: 'The image was copied from a website or other source that does not have a license compatible with Wikipedia, and the uploader does not assert that it is public domain, freely licensed, fair use, or used with permission. Sources that do not have a license compatible with Wikipedia include stock photo libraries such as Getty Images or Corbis. Non-blatant copyright infringements should be discussed at Wikipedia:Images and media for deletion.'
					},
				]
			} );
	}

	form.append( { type:'header', label:'General criteria' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [
				{ 
					label: 'G1: Nonsense', 
					value: 'nonsense', 
					tooltip: 'Patent nonsense and gibberish, an unsalvageably incoherent page with no meaningful content. This does not include: poor writing, partisan screeds, obscene remarks, vandalism, fictional material, material not in English, badly translated material, implausible theories, or hoaxes' },
				{ 
					label: 'G2: Test page',
					value: 'test',
					tooltip: 'e.g., "Can I really create a page here?"' 
				},
				{ 
					label: 'G3: Pure vandalism',
					value: 'vandalism',
					tooltip: 'Plain pure vandalism'
				},
				{ 
					label: 'G3: Pagemove', 
					value: 'pagemove',
					tooltip: 'Nonsense redirects that are created from the cleanup of page move vandalism.'
				},
				{
					label: 'G4: Recreation of deleted material',
					value: 'repost',
					tooltip: 'A copy, by any title, of a page that was deleted via an XfD process or Deletion review, provided that the copy is substantially identical to the deleted version and that any revisions made clearly do not address the reasons for which the page was deleted. This clause does not apply to content that has been "userfied", to content undeleted as a result of Deletion review, or if the prior deletions were proposed or speedy deletions, although in this last case, the previous speedy criterion, or other speedy deletion criteria, may apply.'
				},
				{
					label: 'G5: Banned user', 
					value: 'banned',
					tooltip: 'Pages created by banned users while they were banned'
				},
				{
					label: 'G6: History merge', 
					value: 'histmerge',
					tooltip: 'Temporarily deleting a page in order to merge page histories' 
				},
				{
					label: 'G6: Move', 
					value: 'move',
					tooltip: 'Making way for a noncontroversial move like reversing a redirect' 
				},
				{
					label: 'G6: Afd',
					value: 'afd',
					tooltip: 'An admin has closed an Articles for deletion debate as a "delete".'
				},
				{
					label: 'G6: Housekeeping',
					value: 'g6',
					tooltip: 'Other non-controversial "housekeeping" tasks'
				},
				{
					label: 'G7: Author requests deletion',
					value: 'author',
					tooltip: 'Any page for which deletion is requested by the original author in good faith, provided the page\'s only substantial content was added by its author'
				},
				{
					label: 'G7: Author blanked',
					value: 'blanked',
					tooltip: ' If the author blanks the page, this can be taken as a deletion request'
				},
				{
					label: 'G8: Talk pages whose corresponding article does not exist',
					value: 'talk',
					tooltip: 'unless: It contains deletion discussion that is not logged elsewhere; It is a User Talk page; It is the Talk page for an image uploaded on the Wikimedia Commons; It is a Talk subpage (such as archived Talk pages) whose corresponding top-level article does exist'
				},
				{ 
					label: 'G10: Attack page',
					value: 'attack', 
					tooltip: 'Pages that serve no purpose but to disparage their subject or some other entity (e.g., "John Q. Doe is an imbecile"). This includes a biography of a living person that is negative in tone and unsourced, where there is no NPOV version in the history to revert to. Administrators deleting such pages should not quote the content of the page in the deletion summary!'
				},
				{ 
					label: 'G11: Blatant advertising',
					value: 'spam', 
					tooltip: 'Pages which exclusively promote a company, product, group, service, or person and which would need to be fundamentally rewritten in order to become encyclopedic. Note that simply having a company, product, group, service, or person as its subject does not qualify an article for this criterion; an article that is blatant advertising should have inappropriate content as well'
				},
				{ 
					label: 'G12: Blatant copyright infringement', 
					value: 'copyvio', 
					tooltip: 'Either, 1: Material was copied from another website that does not have a license compatible with Wikipedia, or is photography from a stock photo seller (such as Getty Images or Corbis) or other commercial content provider; 2: There is no non-infringing content in the page history worth saving; or 3: The infringement was introduced at once by a single person rather than created organically on wiki and then copied by another website such as one of the many Wikipedia mirrors' 
				},
				{ 
					label: 'PROD: Proposed deletion', 
					value: 'prod', 
					tooltip: 'Proposed deletion is the way to suggest that an article is uncontroversially a deletion candidate, but that it does not meet the more stringent criteria for speedy deletion. If a prod tag is added to an article, the article will be deleted about 5 days later if nobody objects. If any user objects — usually by removing the tag — then the article may be taken to Articles for Deletion for further discussion.' 
				}
			]
		});
if( wgNamespaceNumber ==  0 || wgNamespaceNumber == 1 ) {
	form.append( { type:'header', label:'Articles' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [
				{
					label: 'A1: Little or no context',
					value: 'nocontext',
					tooltip: 'Very short articles providing little or no context (e.g., "He is a funny man that has created Factory and the Hacienda. And, by the way, his wife is great."). Limited content is not in itself a reason to delete if there is enough context for the article to qualify as a valid stub'
				},
				{
					label: 'A2: Foreign language articles that exist on another Wikimedia project',
					value: 'foreign',
					tooltip: 'If the article in question does not exist on another project, the template \{\{notenglish\}\} should be used instead. All articles in a non-English language that do not meet this criteria (and do not meet any other criteria for speedy deletion) should be listed at Pages Needing Translation (PNT) for review and possible translation'
				},
				{
					label: 'A3: No content whatsoever',
					value: 'nocontent',
					tooltip: 'Any article consisting only of links elsewhere (including hyperlinks, category tags and "see also" sections), a rephrasing of the title, and/or attempts to correspond with the person or group named by its title. This does not include disambiguation pages'
				},
				{
					label: 'A5: Transwikied articles',
					value: 'transwiki',
					tooltip: 'Any article that has been discussed at Articles for Deletion (et al), where the outcome was to transwiki, and where the transwikification has been properly performed and the author information recorded. Alternately, any article that consists of only a dictionary definition, where the transwikification has been properly performed and the author information recorded'
				},
				{
					label: 'A7: Unremarkable people, groups, companies and web content',
					value: 'bio',
					tooltip: 'An article about a real person, group of people, band, club, company, or web content that does not assert the importance or significance of its subject. If controversial, or if there has been a previous AfD that resulted in the article being kept, the article should be nominated for AfD instead'
				},
				{
					label: 'A7: Unremarkable band',
					value: 'band',
					tooltip: 'Article about a band, singer, musician, or musical ensemble that does not assert the importance or significance of the subject.'
				},
				{
					label: 'A7: Unremarkable club',
					value: 'club',
					tooltip: 'Article about a club that does not assert the importance or significance of the subject.'
				},
				{
					label: 'A7: Unremarkable company',
					value: 'inc',
					tooltip: 'Article about a company or corporation that does not assert the importance or significance of the subject.'
				},
				{
					label: 'A7: Unremarkable website',
					value: 'web',
					tooltip: 'Article about a web site, blog, online forum, webcomic, podcast, or similar web content that does not assert the importance or significance of its subject.'
				}
			]
		} );
}
	form.append( { type:'header', label: 'Redirects' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [
				{ 
					label: 'R1: Redirects to nonexistent pages',
					value: 'redirnone'
				},
				{ 
					label: 'R2: Redirects to the Talk:, User: or User talk: namespace from the article space', 
					value: 'rediruser', 
					tooltip: '(this does not include the Wikipedia shortcut pseudo-namespaces). If this was the result of a page move, consider waiting a day or two before deleting the redirect'
				},
				{ 
					label: 'R3: Redirects as a result of an implausible typo that were recently created', 
					value: 'redirtypo', 
					tooltip: 'However, redirects from common misspellings or misnomers are generally useful, as are redirects in other languages'
				}
			]
		} );
if( wgNamespaceNumber ==  14 || wgNamespaceNumber == 15 ) {
	form.append( { type:'header', label: 'Categories' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [
				{ 
					label: 'C1: Empty categories',
					value: 'catempty',
					tooltip: '(no articles or subcategories for at least four days) whose only content has consisted of links to parent categories. This does not apply to categories being discussed on WP:CFD or WP:SFD, or disambiguation categories. If the category isn\'t relatively new, it possibly contained articles earlier, and deeper investigation is needed'
				},
				{
					label: 'C3: Template categories',
					value: 'catfd',
					tooltip: 'If a category is solely populated from a template (e.g. Category:Wikipedia cleanup from \{\{cleanup\}\}) and the template is deleted per deletion policy, the category can also be deleted without further discussion'
				}
			]
		} );
} if( wgNamespaceNumber ==  2 || wgNamespaceNumber == 3 ) {
	form.append( { type:'header', label: 'User pages' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [
				{
					label: 'U1: User request',
					value: 'userreq',
					tooltip: 'Personal subpages, upon request by their user. In some rare cases there may be administrative need to retain the page. Also, sometimes, main user pages may be deleted as well. See Wikipedia:User page for full instructions and guidelines'
				},
				{
					label: 'U2: Nonexistent user',
					value: 'nouser',
					tooltip: 'User pages of users that do not exist (Check Special:Listusers)'
				},
				{
					label: 'U3: Non-free galleries',
					value: 'u3',
					tooltip: 'Galleries in the userspace which consist mostly of "fair use" or non-free images. Wikipedia\'s non-free content policy forbids users from displaying non-free images, even ones they have uploaded themselves, in userspace. It is acceptable to have free images, GFDL-images, Creative Commons and similar licenses along with public domain material, but not "fair use" images'
				},
			]
		} );
} if( wgNamespaceNumber ==  10 || wgNamespaceNumber == 11 ) {
	form.append( { type:'header', label: 'Templates' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [

				{ 
					label: 'T1: Templates that are divisive and inflammatory',
					value: 'divisive'
				}
			]
		} );
} if( wgNamespaceNumber ==  100 || wgNamespaceNumber == 101 ) {
	form.append( { type:'header', label: 'Portals' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [
				{
					label: 'P2: Underpopulated portal',
					value: 'emptyportal',
					tooltip: 'Any Portal based on a topic for which there is not a non-stub header article, and at least three non-stub articles detailing subject matter that would be appropriate to discuss under the title of that Portal'
				}
			]
		} ); }
} else {
	form.append( { type:'header', label:'PUSH THE BUTTON' } );
	form.append( {
			type: 'radio',
			name: 'csd',
			list: [
				{ 
					label: 'Shut down the internet', 
					value: 'afdd', 
					tooltip: 'Use with caution.' }
			]
		});
}
	var result = form.render();
	Window.setContent( result );
	Window.display();
}
if (SHITTYHACK == false) {
twinklespeedy.normalizeHash = {
	'nonsense': 'g1',
	'test': 'g2',
	'vandalism': 'g3',
	'pagemove': 'g3',
	'repost': 'g4',
	'banned': 'g5',
	'histmerge': 'g6',
	'move': 'g6',
	'afd': 'g6',
	'g6': 'g6',
	'author': 'g7',
	'blanked': 'g7',
	'talk': 'g8',
	'attack': 'g10',
	'spam': 'g11',
	'copyvio': 'g12',
	'prod': '',
	'nocontext': 'a1',
	'foreign': 'a2',
	'nocontent': 'a3', 
	'transwiki': 'a5',
	'bio': 'a7',
	'inc': 'a7',
	'web': 'a7',
	'band': 'a7',
	'club': 'a7',
	'redirnone': 'r1',
	'rediruser': 'r2',
	'redirtypo': 'r3',
	'redundantimage': 'i1',
	'noimage': 'i2',
	'noncom': 'i3',
	'unksource': 'i4',
	'unfree': 'i5',
	'norat': 'i6',
	'badfairuse': 'i7',
	'bad10c': 'i7',
	'nowcommons': 'i8',
	'imgcopyvio': 'i9',
	'catempty': 'c1',
	'catfd': 'c3',
	'userreq': 'u1',
	'nouser': 'u2',
	'u3': 'u3',
	'divisive': 't1',
	'emptyportal': 'p2'
};

twinklespeedy.reasonHash = {
	'nonsense': '[[WP:PN|Patent nonsense]]',
	'test': 'Test page',
	'vandalism': '[[WP:VAND|Pure vandalism]]',
	'pagemove': 'Redirect made as a result of the cleanup of page-move vandalism',
	'repost': 'Recreation of deleted material',
	'banned': 'Page made by a [[WP:BANNED|banned user]]',
	'histmerge': 'Temporary deletion in order to merge page histories',
	'move': 'Making way for a non-controversial move',
	'afd': 'Deleting page per result of AfD discussion',
	'g6': 'Housekeeping',
	'author': 'Only one editor has made substantial edits to this page and he or she has requested its deletion',
	'blanked': 'Only one editor has made substantial edits to this page and he or she has blanked the page',
	'talk': 'Talk page whose corresponding page does not exist',
	'attack': '[[WP:ATP|Attack page]]',
	'spam': '[[WP:SPAM|Blatant advertising]]',
	'copyvio': '[[WP:C|Blatant copyright infringement]]',
	'prod': 'Uncontested [[WP:PROD|proposed deletion]]',
	'nocontext': 'Very short article providing little or no context',
	'foreign': 'Foreign language article that exists on another Wikimedia project',
	'nocontent': 'No content whatsoever except possibly links elsewhere, a rephrasing of the title, and/or attempts to correspond', 
	'transwiki': 'Properly transwikified elsewhere',
	'bio': 'Biographical article that does not assert the [[WP:BIO|significance]] of its subject',
	'web': 'Article about a web site or other online forum that did not assert the [[WP:WEB|significance]] of its subject',
	'inc': 'Article about a company that did not assert the [[WP:CORP|significance]] of its subject',
	'club': 'Article about a club or group that does not assert significance',
	'band': 'Article about a band that did not assert [[WP:MUSIC|significance]]',
	'redirnone': 'Redirect to nonexistent page',
	'rediruser': 'Redirect to User/User talk namespaces from mainspace',
	'redirtypo': 'Redirect based on an implausible typo',
	'redundantimage': 'Redundant to another image',
	'noimage': 'Image is corrupted',
	'noncom': 'Licensed as for non-commercial use only, non-derivative use or used with permission\, uploaded on or after May 19, 2005, and no assertion of fair use was provided',
	'unksource': 'Image lacking sources or licensing information for more than seven days',
	'unfree': 'Unfree image that was not used for more than seven days',
	'norat': 'No [[WP:FURG|justification]] given for fair use for more than seven days',
	'badfairuse': 'Bad [[WP:FURG|justification]] given for fair use and the uploader was notified more than 48 hours ago',
	'bad10c': 'Bad [[WP:FURG|justification]] given for fair use: violates point 10c of the [[WP:NFCC|non-free content criteria]]',
	'nowcommons': 'Image exists on the Commons',
	'imgcopyvio': 'Suspected copyright infringement, and the uploader did not assert public domain, fair use, or a free license',
	'catempty': 'Category is empty for 4 days or more',
	'catfd': 'Category solely populated from a now-deleted template',
	'userreq': 'User or user subpage of which the corresponding user requests deletion',
	'nouser': 'Userpage of nonexistent user',
	'u3': 'Userpage that is primarily a gallery of fair use images',
	'divisive': 'Divisive and inflammatory template',
	'emptyportal': 'Underpopulated portal'
};
} else {
twinklespeedy.normalizeHash = {
	'afdd': 'g0'
};

twinklespeedy.reasonHash = {
	'afdd': 'PLEASE [[user talk:east718|ALERT ME]] THAT MY BOT IS BROKEN'
};
}

twinklespeedy.callbacks = {
	sysop: {
		main: function( self ) {
			var xmlDoc = self.responseXML;
			var normal = xmlDoc.evaluate( '//normalized/n/@to', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
			if( normal ) {
				wgPageName = normal;
			}
			var exists = xmlDoc.evaluate( 'boolean(//pages/page[not(@missing)])', xmlDoc, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;

			if( ! exists ) {
				self.statelem.error( "It seems that the page doesn't exists, perhaps it has already been deleted" );
				return;
			}
			var query = { 
				'title': wgPageName, 
				'action': 'delete'
			};

			var wikipedia_wiki = new Wikipedia.wiki( 'Deleting page', query, twinklespeedy.callbacks.sysop.deletePage );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.followRedirect = false;
			wikipedia_wiki.get();

			if( 
				TwinkleConfig.deleteTalkPageOnDelete && 
				wgNamespaceNumber % 2 == 0 && 
				document.getElementById( 'ca-talk' ).className != 'new' 
			) {
				var talk_page = namespaces[ wgNamespaceNumber  + 1 ] + ':' + wgTitle;
				var query = query = {
					'title': talk_page,
					'action': 'delete'
				};
				var wikipedia_wiki = new Wikipedia.wiki( 'Deleting talk page', query, twinklespeedy.callbacks.sysop.deleteTalkPage );
				wikipedia_wiki.params = self.params;
				wikipedia_wiki.followRedirect = false;
				wikipedia_wiki.get();
			}

			if( wgNamespaceNumber == 6 && self.params.normalized != 'i8' ) {
				var query = {
					'action': 'query',
					'list': 'imageusage',
					'titles': wgPageName,
					'iulimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
				};
				var wikipedia_api = new Wikipedia.api( 'Grabbing image links', query, twinklespeedy.callbacks.sysop.unlinkImageInstancesMain );
				wikipedia_api.params = self.params;
				wikipedia_api.post();
			}
			var doOrphan = TwinkleConfig.orphanBacklinksOnSpeedyDelete;
			if( 
				doOrphan.orphan && 
				doOrphan.exclude.indexOf( self.params.normalized.toLowerCase() ) == -1 
			) {
				var query = {
					'action': 'query',
					'list': 'backlinks',
					'blfilterredir': 'nonredirects',
					'bltitle': wgPageName,
					'bllimit': userIsInGroup( 'sysop' ) ? 5000 : 500, // 500 is max for normal users, 5000 for bots and sysops
					'blnamespace': [0, 100] // Main namespace and portal namespace only, keep on talk pages.
				};
				var wikipedia_api = new Wikipedia.api( 'Grabbing backlinks', query, twinklespeedy.callbacks.sysop.unlinkBacklinksMain );
				wikipedia_api.params = self.params;
				wikipedia_api.post();
			}
			var query = {
				'action': 'query',
				'list': 'backlinks',
				'blfilterredir': 'redirects',
				'bltitle': wgPageName,
				'bllimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};
			var wikipedia_api = new Wikipedia.api( 'Grabbing redirects', query, twinklespeedy.callbacks.sysop.deleteRedirectsMain );
			wikipedia_api.params = self.params;
			wikipedia_api.post();

		},
		unlinkBacklinksMain: function( self ) {
			var xmlDoc = self.responseXML;
			var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

			if( snapshot.snapshotLength == 0 ) {
				return;
			}

			var statusIndicator = new Status('Removing backlinks', '0%');

			var total = snapshot.snapshotLength * 2;

			var onsuccess = function( self ) {
				var obj = self.params.obj;
				var total = self.params.total;
				var now = parseInt( 100 * ++(self.params.current)/total ) + '%';
				obj.update( now );
				self.statelem.unlink();
				if( self.params.current >= total ) {
					obj.info( now + ' (completed)' );
					Wikipedia.removeCheckpoint();
				}
			}
			var onloaded = onsuccess;

			var onloading = function( self ) {}


			Wikipedia.addCheckpoint();
			if( snapshot.snapshotLength == 0 ) {
				statusIndicator.info( '100% (completed)' );
				Wikipedia.removeCheckpoint();
				return;
			}

			var params = clone( self.params );
			params.current = 0;
			params.total = total;
			params.obj = statusIndicator;
			params.page = wgPageName;


			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var title = snapshot.snapshotItem(i).value;
				var query = {
					'title': title,
					'action': 'submit'
				}
				var wikipedia_wiki = new Wikipedia.wiki( "Unlinking on " + title, query, twinklespeedy.callbacks.sysop.unlinkBacklinks );
				wikipedia_wiki.params = params;
				wikipedia_wiki.onloading = onloading;
				wikipedia_wiki.onloaded = onloaded;
				wikipedia_wiki.onsuccess = onsuccess;
				wikipedia_wiki.get();
			}
		},
		unlinkBacklinks: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var text = form.wpTextbox1.value;
			var old_text = text;
			var wikiPage = new Mediawiki.Page( text );
			wikiPage.removeLink( self.params.page );

			text = wikiPage.getText();
			if( text == old_text ) {
				// Nothing to do, return
				self.onsuccess( self );
				Wikipedia.actionCompleted( self );
				return;
			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Removing backlinks to ' + self.params.page + TwinkleConfig.deletionSummaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		deleteRedirectsMain: function( self ) {
			var xmlDoc = self.responseXML;
			var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

			var total = snapshot.snapshotLength * 2;

			if( snapshot.snapshotLength == 0 ) {
				return;
			}

			var statusIndicator = new Status('Deleting redirects', '0%');

			var onsuccess = function( self ) {
				var obj = self.params.obj;
				var total = self.params.total;
				var now = parseInt( 100 * ++(self.params.current)/total ) + '%';
				obj.update( now );
				self.statelem.unlink();
				if( self.params.current >= total ) {
					obj.info( now + ' (completed)' );
					Wikipedia.removeCheckpoint();
				}
			}
			var onloaded = onsuccess;

			var onloading = function( self ) {}


			Wikipedia.addCheckpoint();
			if( snapshot.snapshotLength == 0 ) {
				statusIndicator.info( '100% (completed)' );
				Wikipedia.removeCheckpoint();
				return;
			}

			var params = clone( self.params );
			params.current = 0;
			params.total = total;
			params.obj = statusIndicator;


			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var title = snapshot.snapshotItem(i).value;
				var query = {
					'title': title,
					'action': 'delete'
				}
				var wikipedia_wiki = new Wikipedia.wiki( "Deleting " + title, query, twinklespeedy.callbacks.sysop.deleteRedirects );
				wikipedia_wiki.params = params;
				wikipedia_wiki.onloading = onloading;
				wikipedia_wiki.onloaded = onloaded;
				wikipedia_wiki.onsuccess = onsuccess;
				wikipedia_wiki.followRedirect = false;
				wikipedia_wiki.get();
			}
		},
		deleteRedirects: function( self ) {
			var form = this.responseXML.getElementById( 'deleteconfirm' );
			if( ! form ) { // Hell, image deletion is b0rked :(
				form = this.responseXML.getElementsByTagName( 'form' )[0];
				var postData = {
					'wpDeleteReasonList': 'other',
					'wpReason': "[[WP:CSD#R1|CSD R1]]: Redirect to nonexistent page" + TwinkleConfig.deletionSummaryAd,
					'wpEditToken': form.wpEditToken.value
				}
			} else {

				var postData = {
					'wpWatch': form.wpWatch.checked ? '' : undefined,
					'wpDeleteReasonList': 'other',
					'wpReason': "[[WP:CSD#R1|CSD R1]]: Redirect to nonexistent page" + TwinkleConfig.deletionSummaryAd,
					'wpEditToken': form.wpEditToken.value
				}
			}
			self.post( postData );
		},
		unlinkImageInstancesMain: function( self ) {
			var xmlDoc = self.responseXML;
			var snapshot = xmlDoc.evaluate('//imageusage/iu/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

			if( snapshot.snapshotLength == 0 ) {
				return;
			}

			var statusIndicator = new Status('Unlinking instances image', '0%');

			var total = snapshot.snapshotLength * 2;

			var onsuccess = function( self ) {
				var obj = self.params.obj;
				var total = self.params.total;
				var now = parseInt( 100 * ++(self.params.current)/total ) + '%';
				obj.update( now );
				self.statelem.unlink();
				if( self.params.current >= total ) {
					obj.info( now + ' (completed)' );
					Wikipedia.removeCheckpoint();
				}
			}
			var onloaded = onsuccess;

			var onloading = function( self ) {}


			Wikipedia.addCheckpoint();
			if( snapshot.snapshotLength == 0 ) {
					statusIndicator.info( '100% (completed)' );
					Wikipedia.removeCheckpoint();
					return;
			}

			var params = clone( self.params );
			params.current = 0;
			params.total = total;
			params.obj = statusIndicator;
			params.image = wgTitle;

			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var title = snapshot.snapshotItem(i).value;
				var query = {
					'title': title,
					'action': 'submit'
				}
				var wikipedia_wiki = new Wikipedia.wiki( "Unlinking on " + title, query, twinklespeedy.callbacks.sysop.unlinkImageInstances );
				wikipedia_wiki.params = params;
				wikipedia_wiki.onloading = onloading;
				wikipedia_wiki.onloaded = onloaded;
				wikipedia_wiki.onsuccess = onsuccess;
				wikipedia_wiki.get();
			}
		},
		unlinkImageInstances: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var text = form.wpTextbox1.value;
			var old_text = text;
			var wikiPage = new Mediawiki.Page( text );
			wikiPage.commentOutImage( self.params.image, 'Commented out because image was deleted' );

			text = wikiPage.getText();
			if( text == old_text ) {
				// Nothing to do, return
				self.onsuccess( self );
				Wikipedia.actionCompleted( self );
				return;
			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : '',
				'wpWatchthis': undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Removing [[:' + self.params.image + '|deleted image]]' + TwinkleConfig.deletionSummaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		deletePage: function( self ) {
			var form = this.responseXML.getElementById( 'deleteconfirm' );
			if( ! form ) { // Hell, image deletion is b0rked :(
				form = this.responseXML.getElementsByTagName( 'form' )[0];
				var postData = {
					'wpReason': (SHITTYHACK ? bootlegasspopup() : "[[WP:CSD#" + self.params.normalized.toUpperCase() + "|CSD " + self.params.normalized.toUpperCase() + "]]: " + self.params.reason + TwinkleConfig.deletionSummaryAd),
					'wpEditToken': form.wpEditToken.value, 'wpSection': '',
					'wpDeleteReasonList': 'other'
				}
				self.post( postData );
			} else {

				var postData = {
					'wpWatch': self.params.watch || form.wpWatch.checked ? '' : undefined,
					'wpReason': (SHITTYHACK ? bootlegasspopup() : "[[WP:CSD#" + self.params.normalized.toUpperCase() + "|CSD " + self.params.normalized.toUpperCase() + "]]: " + self.params.reason + TwinkleConfig.deletionSummaryAd),
					'wpEditToken': form.wpEditToken.value, 'wpSection': '',
					'wpDeleteReasonList': 'other'
				}
				self.post( postData );
			}
		},
		deleteTalkPage: function( self ) {
			form = this.responseXML.getElementById( 'deleteconfirm' );

			var postData = {
				'wpWatch': self.params.watch || form.wpWatch.checked ? '' : undefined,
				'wpReason': "[[WP:CSD#G8|CSD G8]]: Was a talk page of a deleted page" + TwinkleConfig.deletionSummaryAd,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpDeleteReasonList': 'other'
			}
			self.post( postData );
		}
	},
	user: {
		main: function( self ) {
			var xmlDoc = self.responseXML;

			var exists = xmlDoc.evaluate( 'boolean(//pages/page[not(@missing)])', xmlDoc, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;

			if( ! exists ) {
				self.statelem.error( "It seems that the page doesn't exists, perhaps it has already been deleted" );
				return;
			}
			var query = { 
				'title': wgPageName, 
				'action': 'submit'
			};

			var wikipedia_wiki = new Wikipedia.wiki( 'Tagging page', query, twinklespeedy.callbacks.user.tagPage );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.followRedirect = false;
			wikipedia_wiki.get();
		},
		tagPage: function( self ) {
			form = this.responseXML.getElementById( 'editform' );

			var text = form.wpTextbox1.value;

			self.statelem.status( 'Checking for tags on the page...' );

			var tag = /(\{\{(?:db-?|delete)\|?.*?\}\})/.exec( text );

			if( tag ) {
				self.statelem.error( [ htmlNode( 'strong', tag[0] ) , " is alread placed on the page." ] )
				return;
			}

			var xfd = /(\{\{(?:[rsaitcm]fd|md1)[^{}]*?\}\})/i.exec( text );

			if( xfd && !confirm( "The deletion related template " + xfd[0] + " is already present on the page, do you still want to apply CSD template?" ) ) {
				return;
			}
			var code;
			switch( self.params.normalized ) {
			case 'i8':
				var date = new Date();
				var code = "\{\{NowCommons|month=" + date.getUTCMonthName() + "|day=" + date.getUTCDate() + "|year=" + date.getUTCFullYear() + "|1=" + wgPageName.replace( '_', ' ' ) + "\}\}";
				break;
			case 'g12':
				var url = prompt( 'please enter url if available, including the http://' );
				if( url == null ) {
					return;
				}
				code = "\{\{db-" +  self.params.value + "|url=" + url + "\}\}";
				break;
			case 'i1':
				var img = prompt( 'enter the image this is redundant to, excluding the Image: prefix' );
				if( img == null ) {
					return;
				}
				code = "\{\{db-" +  self.params.value + "|1=" + img + "\}\}";
				break;
			default:
				code = "\{\{db-" +  self.params.value + "\}\}";
				break;
			}

			// Notification to first contributor
			var query = {
				'action': 'query',
				'prop': 'revisions',
				'titles': wgPageName,
				'rvlimit': 1,
				'rvprop': 'user',
				'rvdir': 'newer'
			}
			var callback = function( self ) {
				var xmlDoc = self.responseXML;
				var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
				var query = {
					'title': 'User talk:' + user,
					'action': 'submit'
				};
				var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinklespeedy.callbacks.user.userNotification );
				wikipedia_wiki.params = self.params;
				wikipedia_wiki.get();
			}

			if( self.params.usertalk ) {
				var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
				wikipedia_api.params = self.params;
				wikipedia_api.post();
			}

			var postData = {
				'wpMinoredit': TwinkleConfig.markSpeedyPagesAsMinor ? '' : undefined,
				'wpWatchthis': self.params.watch ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Requesting speedy deletion ([[WP:CSD#" + self.params.normalized.toUpperCase() + "|CSD " + self.params.normalized.toUpperCase() + "]])" + TwinkleConfig.summaryAd,
				'wpTextbox1': code + "\n" + text
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			text += "\n\{\{subst:db-csd-notice-custom|1=" + wgPageName + "|2=" + self.params.value + "\}\}";
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Notification: Speedy deletion nomination of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}
	}
}

twinklespeedy.callback.evaluateSysop = function twinklespeedyCallbackEvaluateSysop(e) {

	wgPageName = wgPageName.replace( /_/g, ' ' ); // for queen/king/whatever and country!

	var tag_only = e.target.form.tag_only;
	if( tag_only && tag_only.checked ) {
		return twinklespeedy.callback.evaluateUser(e);
	}

	var value = e.target.value;
	var normalized = twinklespeedy.normalizeHash[ value ];

	var params = {
		value: value,
		normalized: normalized,
		watch: TwinkleConfig.watchSpeedyPages.indexOf( normalized ) != -1,
		reason: twinklespeedy.reasonHash[ value ]
	};
	Status.init( e.target.form );

	var query = {
		'action': 'query',
		'titles': wgPageName
	}
	var wikipedia_api = new Wikipedia.api( 'Checking if page exists', query, twinklespeedy.callbacks.sysop.main );
	wikipedia_api.params = params;
	wikipedia_api.post();
}



twinklespeedy.callback.evaluateUser = function twinklespeedyCallbackEvaluateUser(e) {
	wgPageName = wgPageName.replace( /_/g, ' ' ); // for queen/king/whatever and country!
	var value = e.target.value;
	var normalized = twinklespeedy.normalizeHash[ value ];

	var params = {
		value: value,
		normalized: normalized,
		watch: TwinkleConfig.watchSpeedyPages.indexOf( normalized ) != -1,
		usertalk: TwinkleConfig.notifyUserOnSpeedyDeletionNomination.indexOf( normalized ) != -1 && e.target.form.notify.checked
	};

	Status.init( e.target.form );

	Wikipedia.actionCompleted.redirect = wgPageName;
	Wikipedia.actionCompleted.notice = "Tagging complete";

	var query = {
		'action': 'query',
		'titles': wgPageName
	}

	var wikipedia_api = new Wikipedia.api( 'Checking if page exists', query, twinklespeedy.callbacks.user.main );
	wikipedia_api.params = params;
	wikipedia_api.post();

}
// twinkleimage.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = function() {};
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

function twinkleimage() {
	if( wgNamespaceNumber == 6 ) {
		mw.util.addPortletLink( 'p-cactions', "javascript:twinkleimage.callback()", "di", "tw-di", "Nominate image for relative speedy deletion", "");
	}
}

$(twinkleimage);

twinkleimage.callback = function twinkleimageCallback() {
	var Window = new SimpleWindow( 600, 300 );
	Window.setTitle( "Image for pseudo-speedy deletion" );
	var form = new QuickForm( twinkleimage.callback.evaluate );
	var field = form.append( {
			type: 'field',
			label: 'Type of action wanted'
		} );
	field.append( {
			type: 'radio',
			name: 'type',
			event: twinkleimage.callback.choice,
			list: [
				{
					label: 'No source',
					value: 'no source',
					checked: true,
					tooltip: 'Image or media has no source information'
				},
				{
					label: 'No license',
					value: 'no license',
					tooltip: 'Image or media does not have information on its copyright status'
				},
				{
					label: 'No fair use rationale',
					value: 'no fair use rationale',
					tooltip: 'Image or media is claimed to be used under Wikipedia\'s fair use policy but has no explanation as to why it is permitted under the policy'
				},
				{
					label: 'Disputed fair use rationale',
					value: 'disputed fair use rationale',
					tooltip: 'Image or media has a fair use rationale that is disputed'
				},

				{
					label: 'Orphaned fair use',
					value: 'orphaned fair use',
					tooltip: 'Image or media is unlicensed for use on Wikipedia and allowed only under a claim of fair use per Wikipedia:Non-free content, but it is not used in any articles'
				},
				{
					label: 'Replaceable fair use',
					value: 'replaceable fair use',
					tooltip: 'Image or media may fail Wikipedia\'s first non-free content criterion in that it illustrates a subject for which a free image might reasonably be found or created that adequately provides the same information'
				}
			]
		} );
	form.append( {
			type: 'div',
			label: 'Work area',
			name: 'work_area'
		} );
	var result = form.render();
	Window.setContent( result );
	Window.display();

	// We must init the
	var evt = document.createEvent( "Event" );
	evt.initEvent( 'change', true, true );
	result.type[0].dispatchEvent( evt );
}

twinkleimage.callback.choice = function twinkleimageCallbackChoose(event) {
	var value = event.target.value;
	var root = event.target.form;
	var work_area = new QuickForm.element( { 
			type: 'div',
			name: 'work_area'
		} );

	switch( value ) {
	case 'disputed fair use rationale':
		work_area.append( {
				type: 'textarea',
				name: 'reason',
				label: 'Concern: '
			} );
		break;
	case 'orphaned fair use':
		work_area.append( {
				type: 'input',
				name: 'replacement',
				label: 'Replacement: '
			} );
		break;
	case 'replaceable fair use':
		work_area.append( {
				type: 'checkbox',
				name: 'old_image',
				list: [
					{
						label: 'Old image',
						tooltip: 'Image was uploaded before 2006-07-13'
					}
				]
			} );
		break;
	}
	work_area.append( { type:'submit' } );
	work_area = work_area.render();
	root.replaceChild( work_area, root.lastChild );
}

twinkleimage.callback.evaluate = function twinkleimageCallbackEvaluate(event) {
	var types = event.target.type;
	for( var i = 0; i < types.length; ++i ) {
		if( types[i].checked ) {
			var type = types[i].value;
			break;
		}
	}
	if( event.target.reason ) {
		var reason = event.target.reason.value;
	}
	if( event.target.old_image ) {
		var old_image = event.target.old_image.checked;
	}

	var params = { reason: reason, old_image: old_image, type: type };
	Status.init( event.target );
	
	// Tagging image
	var query = {
		'title': wgPageName,
		'action': 'submit'
	};

	var wikipedia_wiki = new Wikipedia.wiki( 'Tagging image with deletion tag', query, twinkleimage.callbacks.taggingImage );
	wikipedia_wiki.params = params;
	wikipedia_wiki.get();

	// Notifying uploader
	var query = {
		'action': 'query',
		'prop': 'revisions',
		'titles': wgPageName,
		'rvlimit': 1,
		'rvprop': 'user',
		'rvdir': 'newer'
	}
	var callback = function( self ) {
		var xmlDoc = self.responseXML;
		var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
		var query = {
			'title': 'User talk:' + user,
			'action': 'submit'
		};
		var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinkleimage.callbacks.userNotification );
		wikipedia_wiki.params = self.params;
		wikipedia_wiki.get();
	}
	var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
	wikipedia_api.params = params;
	wikipedia_api.post();

	// adding tag to captions
	var query = {
		'action': 'query',
		'list': 'imageusage',
		'titles': wgPageName,
		'iulimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
	};

	var wikipedia_api = new Wikipedia.api( 'Grabbing image links', query, twinkleimage.callbacks.tagInstancesMain );
	wikipedia_api.params = params;
	wikipedia_api.post();
}

twinkleimage.callbacks = {
	taggingImage: function( self ) {
		var form = self.responseXML.getElementById('editform');
		var text = '';
		switch( self.params.type ) {
		case 'disputed fair use rationale':
			text += "\{\{di-disputed fair use rationale|date=\{\{subst:#time:j F Y\}\}" + ( self.params.reason ? "|concern=" + self.params.reason : '') + "\}\}";
			break;
		case 'orphaned fair use':
			text += "\{\{di-orphaned fair use|date=\{\{subst:#time:j F Y\}\}" + ( self.params.reason ? "|replacement=" + self.params.reason : '') + "\}\}";
			break;
		case 'replaceable fair use':
			text += "\{\{di-replaceable fair use|date=\{\{subst:#time:j F Y\}\}" + ( self.params.old_image ? "|old image=yes" : '') + "\}\}";
			break;
		default:
			text += "\{\{di-" + self.params.type + "|date=\{\{subst:#time:j F Y\}\}\}\}";
			break;
		}
		var postData = {
			'wpMinoredit': undefined, // Per 
			'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': "This image is up for deletion per \[\[WP:CSD\]\]." + TwinkleConfig.summaryAd,
			'wpTextbox1': text + form.wpTextbox1.value
		};
		self.post( postData );
	},
	userNotification: function( self ) {
		var form = self.responseXML.getElementById( 'editform' );
		var text = form.wpTextbox1.value;
		text += "\n\{\{subst:di-" + self.params.type + "-notice|1=" + wgTitle + "\}\} \~\~\~\~";
		var postData = {
			'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
			'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': 'Notification: Deletion of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
			'wpTextbox1': text
		};
		self.post( postData );
	},
	tagInstancesMain: function( self ) {
		var statusIndicator = new Status('Tagging instances image', '0%');
		var xmlDoc = self.responseXML;
		var nsResolver = xmlDoc.createNSResolver( xmlDoc.ownerDocument == null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement);
		var snapshot = xmlDoc.evaluate('//imageusage/iu/@title', xmlDoc, nsResolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

		var total = snapshot.snapshotLength * 2;

		imageTaggingCounter = 0;
		var onsuccess = function( self ) {
			var obj = self.params.obj;
			var total = self.params.total;
			var now = parseInt( 100 * ++imageTaggingCounter/total ) + '%';
			obj.update( now );
			self.statelem.unlink();
			if( imageTaggingCounter == total ) {
				obj.info( now + ' (completed)' );
				Wikipedia.removeCheckpoint();
			}
		}
		var onloaded = onsuccess;

		var onloading = function( self ) {}


		Wikipedia.addCheckpoint();
		for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
			var title = snapshot.snapshotItem(i).value;
			var query = {
				'title': title,
				'action': 'submit'
			}
			var wikipedia_wiki = new Wikipedia.wiki( "Tagging " + title, query, twinkleimage.callbacks.tagInstances );
			wikipedia_wiki.params = { title:title, total:total, obj:statusIndicator, days: self.params.old_image ? 2 : 7 };
			wikipedia_wiki.onloading = onloading;
			wikipedia_wiki.onloaded = onloaded;
			wikipedia_wiki.onsuccess = onsuccess;
			wikipedia_wiki.get();
		}
	},
	tagInstances: function( self ) {
		var form = self.responseXML.getElementById('editform');
		var text = form.wpTextbox1.value;
		var old_text = text;
		var wikiPage = new Mediawiki.Page( text );

		var tag = "\{\{deletable image-caption|1=\{\{subst:#time:l, j F Y| + " + self.params.days + " days\}\}\}\}";
		wikiPage.addToImageComment( wgTitle, tag );

		text = wikiPage.getText();
		if( text == old_text ) {
			// Nothing to do, return
			self.onsuccess( self );
			Wikipedia.actionCompleted( self );
			return;
		}
		var postData = {
			'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
			'wpWatchthis': undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': 'Tagging [[:Image:' + wgTitle + "]] which is up for deletion per [[WP:CSD|CSD]] " + TwinkleConfig.summaryAd,
			'wpTextbox1': text
		};
		self.post( postData );
	}
}

// twinklediff.js
function twinklediff() { 
	if( wgNamespaceNumber < 0 ) {
		return;
	}

	var query = {
		'title': wgPageName,
		'diff': 'cur',
		'oldid': 'prev'
	};

	mw.util.addPortletLink( 'p-cactions' , mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ), 'last', 'tw-lastdiff', 'Show most recent diff' );

	if( !QueryString.exists( 'diff' ) ) {
		// Not diff page
		return;
	} 


	var l = mw.util.addPortletLink( 'p-cactions', "javascript:twinklediff.evaluate(false);", 'since', 'tw-since', 'Show difference between last diff and the revision made by previous user' );

	var l = mw.util.addPortletLink( 'p-cactions', "javascript:twinklediff.evaluate(true);", 'since mine', 'tw-sincemine', 'Show difference between last diff and my last revision' );
}
$( twinklediff );

twinklediff.evaluate = function twinklediffEvaluate(me) {
	var ntitle = getElementsByClassName( document.getElementById('bodyContent'), 'td' , 'diff-ntitle' )[0];

	var user;
	if( me ) {
		user = wgUserName;
	} else {
		var node = document.getElementById( 'mw-diff-ntitle2' );
		if( ! node ) {
			// nothing to do?
			return;
		}
		user = document.evaluate( 'a[1]', node, null, XPathResult.STRING_TYPE, null ).stringValue;
	}
	var query = {
		'prop': 'revisions',
		'action': 'query',
		'titles': wgPageName,
		'rvlimit': 1, 
		'rvprop': [ 'ids', 'user' ],
		'rvstartid': wgCurRevisionId - 1, // i.e. not the current one
		'rvuser': user
	};
	Status.init( document.getElementById('bodyContent') );
	var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, twinklediff.callbacks.main );
	wikipedia_api.params = { user: user };
	wikipedia_api.post();
}

twinklediff.callbacks = {
	main: function( self ) {
		var xmlDoc = self.responseXML;
		var revid = xmlDoc.evaluate( '//rev/@revid', xmlDoc, null, XPathResult.NUMBER_TYPE, null ).numberValue;

		if( ! revid ) {
			self.statelem.error( 'no suitable earlier revision found, or ' + self.params.user + ' is the only contributor. Aborting.' );
			return;
		}
		var query = {
			'title': wgPageName,
			'oldid': revid,
			'diff': wgCurRevisionId
		};
		window.location = mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query );
	}
}
// twinkleprotect.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = function() {};
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.protectionSummaryAd (string)
 If ad should be added or not to protection summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.protectionSummaryAd ) == 'undefined' ) {
	TwinkleConfig.protectionSummaryAd = " using [[WP:TW|TW]]";
}

function twinkleprotect() {
	if( wgNamespaceNumber < 0 || wgCurRevisionId == false ) {
		return;
	}

	if( userIsInGroup( 'sysop' ) ) {
		mw.util.addPortletLink( 'p-cactions', "javascript:twinkleprotect.callback()", "pp", "tw-rpp", "Protect page", "");
	} else {
		mw.util.addPortletLink( 'p-cactions', "javascript:twinkleprotect.callback()", "rpp", "tw-rpp", "Request page protection", "");
	}
}
$(twinkleprotect);

twinkleprotect.callback = function twinkleprotectCallback() {
	var Window = new SimpleWindow( 600, 400 );
	Window.setTitle( "Protection of pages" );
	var form = new QuickForm( twinkleprotect.callback.evaluate );
	if( userIsInGroup( 'sysop' ) ) {
		form.append( {
				type: 'checkbox',
				name: 'request_only',
				event: twinkleprotect.callback.disabledefaults,
				list: [
					{
						label: 'Request protection',
						value: 'request_only',
						tooltip: 'If you want to request protection via WP:RPP instead of doing the protection by your self.'
					}
				]
			} );
	}
	form.append( {
			type: 'select',
			name: 'category',
			label: 'Type of protection: ',
			event: twinkleprotect.callback.disabledefaults,
			list: [
				{
					label: 'Full protection',
					list: [
						{ label: 'Generic', value: 'pp-protected' },
						{ label: 'Dispute', selected: true, value: 'pp-dispute' },
						{ label: 'Vandalism', value: 'pp-vandalism' },
						{ label: 'High visible template', value: 'pp-template' },
						{ label: 'User talk of banned user', value: 'pp-usertalk' }
					]
				},
				{
					label: 'Semi-protection',
					list: [
						{ label: 'Generic', value: 'pp-semi-protected' },
						{ label: 'Vandalism', value: 'pp-semi-vandalism' },
						{ label: 'High-visibility template', value: 'pp-semi-template' },
						{ label: 'User talk of banned user', value: 'pp-semi-usertalk' },
						{ label: 'Spambot target', value: 'pp-semi-spambot' }
					]
				},
				{
					label: 'Other',
					list: [
						{ label: 'Move-protection', value: 'pp-move' },
						{ label: 'Unprotection', value: 'unprotect' }
					]
				}
			]
		} );
	var flags = form.append( {
			type: 'field',
			label: 'Options'
		} );

	flags.append( {
			type: 'checkbox',
			list: [
				{
					name: 'noinclude',
					label: 'Wrap <noinclude>',
					tooltip: 'Will wrap the template in <noinclude> tags, so that it won\'t transclude',
					checked:(wgNamespaceNumber==10),
					adminonly: true
				},
				{ 
					name: 'small',
					label: 'Iconify',
					tooltip: 'Will use the |small=yes feature of the template, and only render it as a keylock',
					adminonly: true
				},
				{
					name: 'cascade',
					label: 'Cascade protection',
					tooltip: 'Cascade protection will protect all pages that is transcluded into said page'
				}
			]
		} );

	if( userIsInGroup( 'sysop' ) ) {
		form.append( {
				type: 'select',
				name: 'expiry',
				label: 'Expiration: ',
				event: function(event) {
					var value = event.target.value;
					event.target.form.small.disabled = value != 'indefinite';
				},
				list: [
					{ label: '1 hour', value: '1 hour' },
					{ label: '2 hours', value: '2 hours' },
					{ label: '3 hours', value: '3 hours' },
					{ label: '6 hours', value: '6 hours' },
					{ label: '12 hours', value: '12 hours' },
					{ label: '1 day', value: '1 day' },
					{ label: '2 days', value: '2 days' },
					{ label: '3 days', value: '3 days' },
					{ label: '4 days', value: '4 days' },
					{ label: '5 days', value: '5 days' },
					{ label: '6 days', value: '6 days' },
					{ label: '1 week', value: '1 week' },
					{ label: '2 weeks', value: '2 weeks' },
					{ label: '1 month', value: '1 month' },
					{ label: '2 months', value: '2 months' },
					{ label: '3 months', value: '3 months' },
					{ label: '6 months', value: '6 months' },
					{ label: '1 year', value: '1 year' },
					{ label: 'indefinite', selected: true, value:'indefinite' }
				]
			} );
	} else {
		form.append( {
				type: 'select',
				name: 'expiry',
				label: 'Expiration: ',
				event: function(event) {
					var value = event.target.value;
					event.target.form.small.disabled = value != 'indefinite';
				},
				list: [
					{ label: 'temporary', value: 'temporary' },
					{ label: 'indefinite', value: 'indefinite' },
					{ label: '', selected: true, value:'' }
				]
			} );
	}
	form.append( {
			type: 'textarea',
			name: 'reason',
			label: 'Reason: '
		} );
	form.append( { type:'submit' } );
	var result = form.render();
	Window.setContent( result );
	Window.display();
}


twinkleprotect.callback.disabledefaults = function twinkleprotectCallbackDisableDefaults(e) {
	var root = e.target.form;
	if( e.target.value == 'unprotect' ) {
		root.noinclude.disabled = true;
		root.cascade.disabled = true;
		root.expiry.disabled = true;
		root.small.disabled = true;
	} else {
		root.noinclude.disabled = false;
		root.cascade.disabled = false;
		root.expiry.disabled = false;
		root.small.disabled = false;
		if( userIsInGroup( 'sysop' ) && root.request_only.checked ){
			root.small.disabled = true;
			root.noinclude.disabled = true;
		}
	}

	if( /template/.test( e.target.value ) ) {
		root.noinclude.checked = true;
		root.expiry.disabled = true;
	} else {
		root.noinclude.checked = false;
	}

}

twinkleprotect.callback.evaluate = function twinkleprotectCallbackEvaluate(e) {
	var form = e.target;

	var params = {
		noinclude: form.noinclude.checked,
		cascade: form.cascade.checked,
		small: form.small.checked,
		reason: form.reason.value,
		expiry: form.expiry.value,
		type: form.category.value
	}

	if( userIsInGroup( 'sysop') ) {
		var request_only = form.request_only.checked;
		if( request_only && params.expiry != 'indefinite' ) {
			params.expiry = 'temporary';
		}
	}

	Status.init( form );

	if( userIsInGroup( 'sysop' ) && ! request_only ) {

		var edit, move, tag = params.type, reason;
		switch( tag ) {
		case 'pp-dispute':
			edit = 'sysop';
			move = 'sysop';
			reason = 'edit warring';
			break;
		case 'pp-vandalism':
			edit = 'sysop';
			move = 'sysop';
			reason = 'vandalism';
			break;
		case 'pp-template':
			edit = 'sysop';
			move = 'sysop';
			reason = 'high-risk template';
			break;
		case 'pp-usertalk':
			edit = 'sysop';
			move = 'sysop';
			reason = '{' + '{unblock}} abuse';
			break;
		case 'pp-protected':
			edit = 'sysop';
			move = 'sysop';
			if( params.reason ) {
				reason = params.reason;
				params.reason = undefined;
			}
			else {
				reason = 'protecting';
			}
			break;
		case 'pp-semi-vandalism':
			edit = 'autoconfirmed';
			move = 'autoconfirmed';
			reason = 'ip vandalism';
			break;
		case 'pp-semi-usertalk':
			edit = 'autoconfirmed';
			move = 'autoconfirmed';
			reason = '{' + '{unblock}} abuse';
			break;
		case 'pp-semi-template':
			edit = 'autoconfirmed';
			move = 'autoconfirmed';
			reason = 'high-risk template';
			break;
		case 'pp-semi-spambot':
			edit = 'autoconfirmed';
			move = 'autoconfirmed';
			reason = 'spambot target';
			break;
		case 'pp-semi-protected':
			edit = 'autoconfirmed';
			move = 'autoconfirmed';
			if( params.reason ) {
				reason = params.reason;
				params.reason = undefined;
			}
			else {
				reason = 'semi-protecting';
			}
			break;
		case 'pp-move':
			edit = '';
			move = 'sysop';
			reason = '';
			break;
		case 'unprotect':
		default:
			edit = '';
			move = '';
			reason = '';
			break;
		}
		if( params.reason ) {
			reason += (reason != '') ? '; ' : '';
			reason += params.reason;
		}

		params.reason = reason;
		params.tag = tag;
		params.edit = edit;
		params.move = move;

		var query = {
			'title': wgPageName,
			'action': 'submit'
		};

		// Updating data for the action completed event
		Wikipedia.actionCompleted.redirect = query['title'];
		Wikipedia.actionCompleted.notice = "Done...";

		var wikipedia_wiki = new Wikipedia.wiki( 'Tagging page', query, twinkleprotect.callbacks.sysop.taggingPage );
		wikipedia_wiki.params = params;
		wikipedia_wiki.get();

		var query = {
			'title': wgPageName,
			'action': 'protect'
		};

		var wikipedia_wiki = new Wikipedia.wiki( 'Protecting page', query, twinkleprotect.callbacks.sysop.protectingPage );
		wikipedia_wiki.params = params;
		wikipedia_wiki.get();
	} else {	
		var typename, reason;
		switch( params.type ) {
		case 'pp-dispute':
		case 'pp-vandalism':
		case 'pp-template':
		case 'pp-usertalk':
		case 'pp-protected':
			typename = 'full protection';
			break;
		case 'pp-semi-vandalism':
		case 'pp-semi-usertalk':
		case 'pp-semi-template':
		case 'pp-semi-spambot':
		case 'pp-semi-protected':
			typename = 'semi-protection';
			break;
		case 'pp-move':
			typename = 'move-protection';
			break;
		case 'unprotect':
		default:
			typename = 'unprotection';
			break;
		}
		switch( params.type ) {
		case 'pp-dispute':
			reason = 'Dispute';
			break;
		case 'pp-vandalism':
			reason = 'Vandalism';
			break;
		case 'pp-template':
			reason = 'High-visible template';
			break;
		case 'pp-usertalk':
			reason = 'User talk of banned user';
			break;
		case 'pp-protected':
			reason = '';
			break;
		case 'pp-semi-vandalism':
			reason = 'Vandalism';
			break;
		case 'pp-semi-usertalk':
			reason = 'User talk of banned user';
			break;
		case 'pp-semi-template':
			reason = 'High-visible template';
			break;
		case 'pp-semi-spambot':
			reason = 'Spambot target';
			break;
		case 'pp-semi-protected':
			reason = '';
			break;
		case 'pp-move':
			reason = '';
			break;
		case 'unprotect':
		default:
			reason = '';
			break;
		}
		if( reason != '' ) {
			reason = "''" + reason + "''";
		}
		if( params.reason ) {
			reason += ', ' + params.reason;
		}
		if( reason != '' ) {
			reason += '.';
		}

		params.reason = reason;
		params.typename = typename;

		var query = {
			'title': 'Wikipedia:Requests for page protection',
			'action': 'submit'
		};
		// Updating data for the action completed event
		Wikipedia.actionCompleted.redirect = query['title'];
		Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the discussion page";

		var wikipedia_wiki = new Wikipedia.wiki( 'Requesting protection of page', query, twinkleprotect.callbacks.user );
		wikipedia_wiki.params = params;
		wikipedia_wiki.get();
	}
}

twinkleprotect.callbacks = {
	sysop: {
		taggingPage: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var oldtag_re = /\s*(?:<noinclude>)?\s*\{\{\s*(pp-[^{}]*?|protected|(?:t|v|s|p-|usertalk-v|usertalk-s|sb|move)protected(?:2)?|protected template|privacy protection)\s*?\}\}\s*(?:<\/noinclude>)?\s*/gi;

			var text = form.wpTextbox1.value;
			text = text.replace( oldtag_re, '' );
			if( self.params.type != 'unprotect' && self.params.expiry != 'indefinite' ) {
				self.params.tag += '|expiry={' + '{subst:#time:F j, Y|+' + self.params.expiry +'}}';
				if( this.params.small ) {
					self.params.tag += '|small=yes';
				}
			}

			var summary;
			if( self.params.type == 'unprotect' ) {
				summary = 'removing protection template' + TwinkleConfig.summaryAd;
			} else {
				if( self.params.noinclude ) {
					text = "<noinclude>\{\{" + self.params.tag + "\}\}</noinclude>" + text;
				} else {
					text = "\{\{" + self.params.tag + (this.params.small ? '|small=yes' : '') + "\}\}\n" + text;
				}
				summary = (self.params.type == 'unprotect' ? '-pp' : "pp +" + (self.params.expiry == 'indefinite' ? 'infinite' : self.params.expiry)) + TwinkleConfig.summaryAd;

			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': summary,
				'wpTextbox1': text
			};

			self.post( postData );
		},
		protectingPage: function( self ){
			var form  = self.responseXML.getElementById( 'mw-Protect-Form' );
			var postData = {
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'mwProtectWatch': form.mwProtectWatch.checked ? '' : undefined,
				'mwProtectCascade': self.params.cascade ? '' : undefined,
				'mwProtect-expiry-edit': self.params.expiry != 'indefinite' ? self.params.expiry : undefined,
				'mwProtect-expiry-move': self.params.expiry != 'indefinite' ? self.params.expiry : undefined,
				'mwProtect-level-edit': self.params.edit,
				'mwProtect-level-move': self.params.move,
				'wpProtectExpirySelection-edit': 'othertime',
				'wpProtectExpirySelection-move': 'othertime',
				'wpProtectReasonSelection': 'other',
				'mwProtect-reason': self.params.reason + TwinkleConfig.protectionSummaryAd
			};

			self.post( postData );
		}
	},
	user: function( self ) {
		var form = self.responseXML.getElementById( 'editform' );

		var text = form.wpTextbox1.value;

		var rppRe = new RegExp( '====.*?' + RegExp.escape( wgPageName, true ) + '.*?====', 'm' );
		var tag = rppRe.exec( text );

		if( tag ) {
			self.statelem.warn( [ htmlNode( 'strong', tag[0] ) , " is alread placed on the page." ] )
			return false;
		}

		var ns2tag	=	{
			'0'	:	'la',
			'1'	:	'lat',
			'2'	:	'lu',
			'3'	:	'lut',
			'4'	:	'lw',
			'5'	:	'lwt',
			'6'	:	'li',
			'7'	:	'lit',
			'8'	:	'lm',
			'9'	:	'lmt',
			'10':	'lt',
			'11':	'ltt',
			'12':	'lh',
			'13':	'lht',
			'14':	'lc',
			'15':	'lct',
			'100':	'lp',
			'101':	'lpt'
		};

		var newtag = '==== \{\{' + ns2tag[ wgNamespaceNumber ] + '|' + wgTitle +  '\}\} ====' + "\n";
		if( ( new RegExp( '^' + RegExp.escape( newtag ).replace( /\s+/g, '\\s*' ), 'm' ) ).test( text ) ) {
			self.statelem.error( 'There are already a request available for this page, aborting.' );
			return;
		}
		var words = [];
		switch( self.params.expiry ) {
		case 'temporary':
			words.push( "temporary" );
			break;
		case 'indefinite':
			words.push( "indefinite" );
			break;
		}
		if( self.params.cascade ) {
			words.push( "cascading" );
		}

		words.push( self.params.typename );

		newtag += "'''" + words.join( ' ' ) + "'''" + ( self.params.reason != '' ? ' ' + self.params.reason : '' ) + "\~\~\~";

		if( self.params.type == 'unprotect' ) {
			var str = "==Current requests for unprotection==\n{{Wikipedia:Requests for page protection/URheading}}";
		} else {
			var str = "==Current requests for protection==\n{{Wikipedia:Requests for page protection/PRheading}}";
		}
		text = text.replace( str, str + "\n" + newtag + "\n" );
		var postData = {
			'wpMinoredit': undefined,
			'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': "Requesting " + self.params.typename + ' of [[' + wgPageName.replace('_', ' ') + ']].' + TwinkleConfig.summaryAd,
			'wpTextbox1': text
		};

		self.post( postData );
	}
}

// twinkleprod.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = {};
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.watchProdPages (boolean)
 If, when applying prod template to page, watch it, default true
 */
if( typeof( TwinkleConfig.watchProdPages ) == 'undefined' ) {
	TwinkleConfig.watchProdPages = true;
}

function twinkleprod() {
	if( wgNamespaceNumber != 0 || wgCurRevisionId == false ) {
		return;
	}
	mw.util.addPortletLink( 'p-cactions', "javascript:twinkleprod.callback()", "prod", "tw-prod", "Propose deletion via WP:PROD", "");
}
$(twinkleprod);

twinkleprod.callback = function twinkleprodCallback() {
	var Window = new SimpleWindow( 800, 400 );
	Window.setTitle( "WP:PROD" );
	var form = new QuickForm( twinkleprod.callback.evaluate );
	var field = form.append( {
			type: 'field',
			label: 'Reason for proposed deletion'
		} );
	field.append( {
			type: 'textarea',
			name: 'reason',
			label: 'Reason:'
		} );
	field.append( { type:'submit' } );

	var result = form.render();
	Window.setContent( result );
	Window.display();
}

twinkleprod.callbacks = {
	main: function( self ) {
		var form = self.responseXML.getElementById('editform');
		var text = form.wpTextbox1.value;

		var tag_re = /(\{\{(?:db-?|delete|[aitcmrs]fd|md1)[^{}]*?\|?[^{}]*?\}\})/;

		if( tag_re.test( text ) ) {
			self.statelem.warn( 'Page already tagged with a deletion template, aborting procedure' );
			return;
		}
		// Notification to first contributor
		var query = {
			'action': 'query',
			'prop': 'revisions',
			'titles': wgPageName,
			'rvlimit': 1,
			'rvprop': 'user',
			'rvdir': 'newer'
		}
		var callback = function( self ) {
			var xmlDoc = self.responseXML;
			var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
			var query = {
				'title': 'User talk:' + user,
				'action': 'submit'
			};
			var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinkleprod.callbacks.userNotification );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();
		}

		var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
		wikipedia_api.params = self.params;
		wikipedia_api.post();

		var postData = {
			'wpMinoredit': undefined, // Per memo
			'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': "Proposing article for deletion per [[WP:PROD]]." + TwinkleConfig.summaryAd,
			'wpTextbox1': "\{\{subst:prod|1=" + self.params.reason + "}}\n" + text
		};

		self.post( postData );
	},
	userNotification: function( self ) {
		var form = this.responseXML.getElementById( 'editform' );
		var text = form.wpTextbox1.value;
		text += "\n\{\{subst:PRODWarning|1=" + wgPageName + "\}\} \~\~\~";
		var postData = {
			'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
			'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': 'PROD nomination of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
			'wpTextbox1': text
		};

		self.post ( postData );
	}
}

twinkleprod.callback.evaluate = function twinkleprodCallbackEvaluate(e) {
	var form = e.target;
	var reason = form.reason.value;

	wgPageName = wgPageName.replace(/_/g, ' ');

	Status.init( form );
	var query = { 
		'title': wgPageName, 
		'action': 'submit'
	};

	var wikipedia_wiki = new Wikipedia.wiki( 'Tagging page', query, twinkleprod.callbacks.main );
	wikipedia_wiki.params = { reason: reason };
	wikipedia_wiki.followRedirect = false;
	wikipedia_wiki.get();
}
// twinklexfd.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = function() {};
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

function num2order( num ) {
	switch( num ) {
	case 1: return '';
	case 2: return '2nd';
	case 3: return '3rd';
	default: return num + 'th';
	}
}
function twinklexfd() {
	if( wgNamespaceNumber < 0 || wgCurRevisionId == false ) {
		return;
	}
	mw.util.addPortletLink( 'p-cactions', "javascript:twinklexfd.callback()", "xfd", "tw-xfd", "Anything for deletion", "");
}
$(twinklexfd);

twinklexfd.callback = function twinklexfdCallback() {

	var Window = new SimpleWindow( 600, 300 );
	Window.setTitle( "Anything for deletion" );
	var form = new QuickForm( twinklexfd.callback.evaluate );
	var categories = form.append( {
			type: 'select',
			name: 'category',
			label: 'Select wanted type of category: ',
			tooltip: 'When activated, a default choice is choosen, based on what namespace you are in. This default should be the most appropriate',
			event: twinklexfd.callback.change_category
		} );
	categories.append( {
			type: 'option',
			label: 'Afd',
			selected: wgNamespaceNumber == Namespace.MAIN,
			value: 'afd'
		} );
	categories.append( {
			type: 'option',
			label: 'Tfd',
			selected: wgNamespaceNumber == Namespace.TEMPLATE,
			value: 'tfd'
		} );
	categories.append( {
			type: 'option',
			label: 'Ifd/PUI',
			selected: wgNamespaceNumber == Namespace.IMAGE,
			value: 'ifd'
		} );
	categories.append( {
			type: 'option',
			label: 'Cfd',
			selected: wgNamespaceNumber == Namespace.CATEGORY,
			value: 'cfd'
		} );
	categories.append( {
			type: 'option',
			label: 'Mfd',
			selected: [ Namespace.IMAGE, Namespace.MAIN, Namespace.TEMPLATE, Namespace.CATEGORY ].indexOf( wgNamespaceNumber ) == -1 ,
			value: 'mfd'
		} );
	categories.append( {
			type: 'option',
			label: 'Rfd',
			selected: QueryString.equals('redirect', 'no'),
			value: 'rfd'
		} );
	categories.append( {
			type: 'option',
			label: 'Sfd',
			disabled: true,
			value: 'sfd'
		} );

	form.append( {
			type: 'field',
			label:'Work area',
			name: 'work_area'
		} );

	var result = form.render();
	Window.setContent( result );
	Window.display();

	// We must init the
	var evt = document.createEvent( "Event" );
	evt.initEvent( 'change', true, true );
	result.category.dispatchEvent( evt );

}

twinklexfd.callback.change_category = function twinklexfdCallbackChangeCategory(e) {
	var value = e.target.value;
	var root = e.target.form;
	var old_area;
	var childNodes = root.childNodes;
	for( var i = 0; i < childNodes.length; ++i ) {
		var node = childNodes[i];
		if( 
			node instanceof Element &&
			node.getAttribute( 'name' ) == 'work_area' 
		) {
			old_area = node;
			break;
		}
	}
	var work_area = null;

	switch( value ) {
	case 'afd':
		work_area = new QuickForm.element( { 
				type: 'field',
				label: 'Articles for deletion',
				name: 'work_area'
			} );
		var afd_category = work_area.append( { 
				type:'select',
				name:'xfdcat',
				label:'Choose what category this nomination belongs in' 
			} );

		afd_category.append( { type:'option', label:'Unknown', value:'?', selected:true } );
		afd_category.append( { type:'option', label:'Media and music', value:'M' } );
		afd_category.append( { type:'option', label:'Organisation, corporation, or product', value:'O' } );
		afd_category.append( { type:'option', label:'Biographical', value:'B' } );
		afd_category.append( { type:'option', label:'Society topics', value:'S' } );
		afd_category.append( { type:'option', label:'Web or internet', value:'W' } );
		afd_category.append( { type:'option', label:'Games or sports', value:'G' } );
		afd_category.append( { type:'option', label:'Science and technology', value:'T' } );
		afd_category.append( { type:'option', label:'Fiction and the arts', value:'F' } );
		afd_category.append( { type:'option', label:'Places and transportation', value:'P' } );
		afd_category.append( { type:'option', label:'Indiscernible or unclassifiable topic', value:'I' } );
		afd_category.append( { type:'option', label:'Debate not yet sorted', value:'U' } );

		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'Reason: '
			} );
		work_area.append( { type:'submit' } );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'tfd':
		work_area = new QuickForm.element( { 
				type: 'field',
				label: 'Templates for deletion',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'Reason: '
			} );
		work_area.append( { type:'submit' } );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'mfd':
		work_area = new QuickForm.element( { 
				type: 'field',
				label: 'Miscellany for deletion',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'Reason: '
			} );
		work_area.append( { type:'submit' } );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'ifd':
		work_area = new QuickForm.element( { 
				type: 'field',
				label: 'Images for deletion',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'checkbox',
				name: 'pui',
				list: [
					{
						label: 'Possible unfree image',
						value: 'pui',
						tooltip: 'Image have disputed source or licensing information'
					}
				]
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'Reason: '
			} );
		work_area.append( { type:'submit' } );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'cfd':
		work_area = new QuickForm.element( { 
				type: 'field',
				label: 'Categories for discussion',
				name: 'work_area'
			} );
		var cfd_category = work_area.append( {
				type: 'select',
				label: 'Choose type of action wanted: ',
				name: 'xfdcat',
				event: function(e) {
					var value = e.target.value;
					var target = e.target.form.xfdtarget;
					if( value == 'cfd' ) {
						target.disabled = true;
					} else {
						target.disabled = false;
					}
				}
			} );
		cfd_category.append( { type:'option', label: 'Deletion', value: 'cfd', selected:true } );
		cfd_category.append( { type:'option', label:'Merge', value:'cfm' } );
		cfd_category.append( { type:'option', label:'Renaming', value:'cfr' } );
		cfd_category.append( { type:'option', label:'Convert into article', value:'cfc' } );

		work_area.append( {
				type: 'input',
				name: 'xfdtarget',
				label: 'Target page: ',
				disabled: true,
				value: ''
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'Reason: '
			} );
		work_area.append( { type:'submit' } );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'rfd':
		work_area = new QuickForm.element( { 
				type: 'field',
				label: 'Redirects for discussion',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'Reason: '
			} );
		work_area.append( { type:'submit' } );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	default:
		work_area = new QuickForm.element( { 
				type: 'field',
				label: 'Nothing for anything',
				name: 'work_area'
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	}
}

twinklexfd.callbacks = {
	afd: {
		main: function ( self ) {
			var xmlDoc = self.responseXML;
			var titles = xmlDoc.evaluate( '//allpages/p/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

			var number = 0;
			for( var i = 0; i < titles.snapshotLength; ++i ) {
				var title = titles.snapshotItem(i).value;

				/*
				 * Check so that following isn't for the same article:
				 * Articles for deletion/Deleg (2nd nomination)
				 * Articles for deletion/Delegative democracy
				 */
				var correct_title_re = new RegExp( '^' + RegExp.escape( 'Wikipedia:Articles for deletion/' + wgPageName, true ) + '(?:\s+\(.*?\)|)$' );
				if( ! correct_title_re.test( title ) ) {
					continue;
				}

				title = title.replace( /(first|second|third|fourth|fifth|sixth|seventh|eighth|ninth|tenth|eleventh)/, function(v) {
						return {
							'first': '1st',
							'second': '2nd',
							'third': '3rd',
							'fourth': '4th',
							'fifth': '5th',
							'sixth': '6th',
							'seventh': '7th',
							'eighth': '8th',
							'ninth': '9th',
							'tenth': '10th',
							'eleventh': '11th'
						}[v];
					} );
				var n = /\(\s*(\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\s*\)\s*$/.exec( title );
				if( n && n[1] > number ) {
					number = n[1];
				} else if( number == 0 ) {
					number = 1;
				}
			}

			if( number == 0 ) {
				self.params.numbering = self.params.number = '';
				numbering = number = '';
			} else {
				self.params.number = num2order( parseInt( number ) + 1);
				self.params.numbering = ' (' + self.params.number + ' nomination)';
			}
			Status.info( 'Next discussion page","[[Wikipedia:Articles for deletion/' + wgPageName + self.params.numbering + ']]' );

			// Tagging article
			var query = {
				'title': wgPageName,
				'action': 'submit'
			};
			var wikipedia_wiki = new Wikipedia.wiki( 'Tagging article with deletion tag', query, twinklexfd.callbacks.afd.article );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Discussion page
			query = {
				'title': 'Wikipedia:Articles for deletion/' + wgPageName + self.params.numbering,
				'action': 'submit'
			};

			// Updating data for the action completed event
			Wikipedia.actionCompleted.redirect = query['title'];
			Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the discussion page";

			var wikipedia_wiki = new Wikipedia.wiki( 'Creating article deletion discussion page', query, twinklexfd.callbacks.afd.discussionPage );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Todays list
			var date = new Date();

			query = {
				'title': 'Wikipedia:Articles for deletion/Log/' + date.getUTCFullYear() + ' ' + date.getUTCMonthName() + ' ' + date.getUTCDate(),
				'action': 'submit'
			};

			var wikipedia_wiki = new Wikipedia.wiki( 'Adding discussion to todays list', query, twinklexfd.callbacks.afd.todaysList );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Notification to first contributor

			var query = {
				'action': 'query',
				'prop': 'revisions',
				'titles': wgPageName,
				'rvlimit': 1,
				'rvprop': 'user',
				'rvdir': 'newer'
			}
			var callback = function( self ) {
				var xmlDoc = self.responseXML;
				var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
				var query = {
					'title': 'User talk:' + user,
					'action': 'submit'
				};
				var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinklexfd.callbacks.afd.userNotification );
				wikipedia_wiki.params = self.params;
				wikipedia_wiki.get();
			}
			var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
			wikipedia_api.params = self.params;
			wikipedia_api.post();
		},
		article: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': undefined, // Per memo
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Nominated for deletion; see [[Wikipedia:Articles for deletion/" + wgPageName + self.params.numbering + ']].'+ TwinkleConfig.summaryAd,
				'wpTextbox1': "\{\{" + ( self.params.number == '' ? "subst:afd\}\}\n" : 'subst:afdx|' + self.params.number + "}}\n" ) + form.wpTextbox1.value
			};
			self.post( postData );
		},
		discussionPage: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Creating deletion discussion page for \[\[" + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': "\{\{subst:afd2|pg=" + wgPageName + "|cat=" + self.params.xfdcat + "|text=" + self.params.reason + " \~\~\~\}\}\n"
			};
			self.post( postData );
		},
		todaysList: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var old_text = form.wpTextbox1.value;

			var text = old_text.replace( /(<\!-- Add new entries to the TOP of the following list -->\n+)/, "$1\{\{subst:afd3|pg=" + wgPageName + self.params.numbering + "\}\}\n");
			if( text == old_text ) {
				self.statelem.error( 'failed to find target spot to add the discussion to.' );
				return;
			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Adding \[\[Wikipedia:Articles for deletion/" + wgPageName + self.params.numbering + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			text += "\n\{\{subst:AFDWarning|1=" + wgPageName + ( self.params.numbering != '' ? '|order=&#32;' + self.params.numbering : '' ) + "\}\} \~\~\~";
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'AfD nomination of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}
	},
	tfd: {
		taggingTemplate: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': undefined, // Per memo
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Nominated for deletion; see \[\[Wikipedia:Templates for deletion#" + wgPageName + '\]\].'+ TwinkleConfig.summaryAd,
				'wpTextbox1': "<noinclude>\{\{tfd|" + wgTitle + "\}\}\n</noinclude>" + form.wpTextbox1.value
			};
			self.post( postData );
		},
		todaysList: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var old_text = form.wpTextbox1.value;
			text = old_text.replace( '-->', "-->\n\{\{subst:tfd2|" + wgTitle + "|text=" + self.params.reason + ". \~\~\~\}\}");
			if( text == old_text ) {
				self.statelem.error( 'failed to find target spot to add the discussion to.' );
				return;
			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Adding [[Template:" + wgTitle + ']].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			text += "\n\{\{subst:tfdnotice|1=" + wgTitle + "\}\} \~\~\~";
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'TfD nomination of \[\[Template:' + wgTitle + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}
	},
	mfd: {
		main: function( self ) {
			var xmlDoc = self.responseXML;
			var titles = xmlDoc.evaluate( '//allpages/p/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

			var number = 0;
			for( var i = 0; i < titles.snapshotLength; ++i ) {
				var title = titles.snapshotItem(i).value;
				title = title.replace( /(first|second|third|fourth|fifth|sixth|seventh|eighth|ninth|tenth|eleventh)/, function(v) {
						return {
							'first': '1st',
							'second': '2nd',
							'third': '3rd',
							'fourth': '4th',
							'fifth': '5th',
							'sixth': '6th',
							'seventh': '7th',
							'eighth': '8th',
							'ninth': '9th',
							'tenth': '10th',
							'eleventh': '11th'
						}[v];
					} );
				var n = /\(\s*(\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\s*\)\s*$/.exec( title );
				if( n && n[1] > number ) {
					number = n[1];
				} else if( number == 0 ) {
					number = 1;
				}
			}

			if( number == 0 ) {
				self.params.numbering = self.params.number = '';
				numbering = number = '';
			} else {
				self.params.number = num2order( parseInt( number ) + 1);
				self.params.numbering = ' (' + self.params.number + ' nomination)';
			}
			self.statelem.info( 'next in order is [[Wikipedia:Miscellany for deletion/' + wgPageName + self.params.numbering + ']]');

			// Tagging article
			var query = {
				'title': wgPageName,
				'action': 'submit'
			};

			var wikipedia_wiki = new Wikipedia.wiki( 'Tagging page with deletion tag', query, twinklexfd.callbacks.mfd.taggingPage );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Discussion page
			var query = {
				'title': 'Wikipedia:Miscellany for deletion/' + wgPageName + this.params.numbering,
				'action': 'submit'
			};

			// Updating data for the action completed event
			Wikipedia.actionCompleted.redirect = query['title'];
			Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the discussion page";

			wikipedia_wiki = new Wikipedia.wiki( 'Creating page deletion discussion page', query, twinklexfd.callbacks.mfd.discussionPage );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Todays list
			var query = {
				'title': 'Wikipedia:Miscellany for deletion',
				'action': 'submit',
				'section': 2
			};

			wikipedia_wiki = new Wikipedia.wiki( 'Adding deletion discussion to todays list', query, twinklexfd.callbacks.mfd.todaysList );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Notification to first contributor
			var query = {
				'action': 'query',
				'prop': 'revisions',
				'titles': wgPageName,
				'rvlimit': 1,
				'rvprop': 'user',
				'rvdir': 'newer'
			}
			var callback = function( self ) {
				var xmlDoc = self.responseXML;
				var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
				var query = {
					'title': 'User talk:' + user,
					'action': 'submit'
				};
				var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinklexfd.callbacks.mfd.userNotification );
				wikipedia_wiki.params = self.params;
				wikipedia_wiki.get();
			}
			var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
			wikipedia_api.params = self.params;
			wikipedia_api.post();
		},
		taggingPage: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': undefined, // Per memo
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Nominated for deletion; see [[Wikipedia:Miscellany for deletion/" + wgPageName + self.params.numbering + ']].'+ TwinkleConfig.summaryAd,
				'wpTextbox1': "\{\{" + ( self.params.number == '' ? "subst:mfd\}\}\n" : 'subst:mfdx|' + self.params.number + "}}\n" ) + form.wpTextbox1.value
			};
			self.post( postData );
		},
		discussionPage: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Creating deletion discussion page for \[\[" + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': "\{\{subst:mfd2|pg=" + wgPageName + "|text=" + self.params.reason + " \~\~\~\}\}\n"
			};
			self.post( postData );
		},
		todaysList: function( self ) {
			var form = self.responseXML.getElementById('editform');

			var text = form.wpTextbox1.value;
			var date = new Date();

			var month =  new Number( date.getUTCMonth() + 1 );
			var day =  new Number( date.getUTCDate() );
			var year = new Number( date.getUTCFullYear() );
			var today_date = year.zeroFill( 4 ) + '-' + month.zeroFill( 2 ) + '-' + day.zeroFill( 2 );
			var today_regex = new RegExp( "(\\=\\=\\=\\[\\[" + RegExp.escape( today_date ) + "\\]\\]\\=\\=\\=)" );
			var new_data = "\n\{\{subst:mfd3|pg=" + wgPageName + self.params.numbering + "\}\}";

			if( today_regex.test( text ) ) { // we have a section allready
				self.statelem.info( 'Found todays section, proceeding to add new entry' );
				text = text.replace( today_regex, "$1\n" + new_data );
			} else { // we need to create a new section
				self.statelem.info( 'No section for today found, proceeding to create one' );
				var old_text = text;
				text = text.replace( '-->', "-->\n===\[\[" + today_date + "\]\]===\n" + new_data );
				if( text == old_text ) {
					self.statelem.error( 'failed to find target spot to add the discussion to' );
					return;
				}
			}

			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Adding \[\[Wikipedia:Miscellany for deletion/" + wgPageName + self.params.numbering + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			text += "\n\{\{subst:MFDWarning|1=" + wgPageName + ( self.params.numbering != '' ? '|order=&#32;' + this.params.numbering : '' ) + "\}\} \~\~\~";
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'MfD nomination of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}
	},
	ifd: {
		main: function( self ) {
			var xmlDoc = self.responseXML;
			var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
			self.params.uploader = user;
			var query = {
				'title': 'Wikipedia:Images and media for deletion/' + self.params.date,
				'action': 'submit'
			};

			wikipedia_wiki = new Wikipedia.wiki( 'Adding deletion discussion to todays list', query, twinklexfd.callbacks.ifd.todaysList );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Updating data for the action completed event
			Wikipedia.actionCompleted.redirect = query['title'];
			Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the discussion page";

			// Notification to first contributor

			var query = {
				'title': 'User talk:' + self.params.uploader,
				'action': 'submit'
			};
			wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + self.params.uploader + ')', query, twinklexfd.callbacks.ifd.userNotification );
			wikipedia_wiki.get();
		},
		taggingImage: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': undefined, // Per 
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "This image is being considered for deletion in accordance with Wikipedia's [[Wikipedia:Deletion policy|Deletion policy]]; See \[\[Wikipedia:Images for deletion#" + wgPageName + '\]\].'+ TwinkleConfig.summaryAd,
				'wpTextbox1': "\{\{ifd|log=" + self.params.date + "\}\}\n" + form.wpTextbox1.value
			};
			self.post( postData );
		},
		todaysList: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Adding discussion for \[\[:" + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': form.wpTextbox1.value + "\n\{\{subst:ifd2|1=" + wgTitle + "|Uploader=" + self.params.uploader + "|Reason=" + self.params.reason + "\}\} \~\~\~"
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			text += "\n\{\{subst:idw|1=" + wgPageName + "\}\}";
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Notification: IfD nomination of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		tagInstancesMain: function( self ) {
			var xmlDoc = self.responseXML;
			var nsResolver = xmlDoc.createNSResolver( xmlDoc.ownerDocument == null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement);
			var snapshot = xmlDoc.evaluate('//imageusage/iu/@title', xmlDoc, nsResolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

			if( snapshot.snapshotLength == 0 ) {
				return;
			}

			var statusIndicator = new Status('Tagging instances image', '0%');
			var total = snapshot.snapshotLength * 2;

			var date = new Date();
			var dateString = date.getUTCFullYear() + ' ' + date.getUTCMonthName() + ' ' + date.getUTCDate();

			imageTaggingCounter = 0;
			var onsuccess = function( self ) {
				var obj = self.params.obj;
				var total = self.params.total;
				var now = parseInt( 100 * ++imageTaggingCounter/total ) + '%';
				obj.update( now );
				self.statelem.unlink();
				if( imageTaggingCounter == total ) {
					obj.info( now + ' (completed)' );
					Wikipedia.removeCheckpoint();
				}
			}

			var onloaded = onsuccess;

			var onloading = function( self ) {}


			Wikipedia.addCheckpoint();
			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var title = snapshot.snapshotItem(i).value;
				var query = {
					'title': title,
					'action': 'submit'
				}
				var wikipedia_wiki = new Wikipedia.wiki( "Tagging of " + title, query, twinklexfd.callbacks.ifd.tagInstances );
				wikipedia_wiki.params = { title:title, total:total, obj:statusIndicator, date:dateString };
				wikipedia_wiki.onloading = onloading;
				wikipedia_wiki.onloaded = onloaded;
				wikipedia_wiki.onsuccess = onsuccess;
				wikipedia_wiki.get();
			}
		},
		tagInstances: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var text = form.wpTextbox1.value;
			var old_text = text;
			var wikiPage = new Mediawiki.Page( text );

			var tag = "\{\{ifdc|1=Image:" + wgTitle + "|log=" + self.params.date + "\}\}";
			wikiPage.addToImageComment( wgTitle, tag );

			text = wikiPage.getText();
			if( text == old_text ) {
				// Nothing to do, return
				self.onsuccess( self );
				Wikipedia.actionCompleted();
				return;
			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Tagging [[:Image:' + wgTitle + "]] which is up for deletion at [[WP:IFD|Images for deletion]]" + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}
	},
	pui: {
		taggingImage: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': undefined, // Per 
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "This image has been listed on [[Wikipedia:Possibly unfree images]] because the information on its source or copyright status is disputed; See \[\[Wikipedia:Possibly unfree images#" + wgPageName + '\]\].'+ TwinkleConfig.summaryAd,
				'wpTextbox1': "\{\{pui|log=" + self.params.date + "\}\}\n" + form.wpTextbox1.value
			};
			self.post( postData );
		},
		todaysList: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Adding discussion for \[\[:" + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': form.wpTextbox1.value + "\n\{\{subst:pui2|image=" + wgTitle + "|reason=" + self.params.reason + "\}\} \~\~\~"
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			text += "\n\{\{subst:idw-pui|1=" + wgPageName + "\}\}";
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Notification: PUI posting of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		tagInstancesMain: function( self ) {
			var xmlDoc = self.responseXML;
			var nsResolver = xmlDoc.createNSResolver( xmlDoc.ownerDocument == null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement);
			var snapshot = xmlDoc.evaluate('//imageusage/iu/@title', xmlDoc, nsResolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

			if( snapshot.snapshotLength == 0 ) {
				return;
			}
			var statusIndicator = new Status('Tagging instances image', '0%');
			var total = snapshot.snapshotLength * 2;

			var date = new Date();
			var dateString = date.getUTCFullYear() + ' ' + date.getUTCMonthName() + ' ' + date.getUTCDate();

			imageTaggingCounter = 0;
			var onsuccess = function( self ) {
				var obj = self.params.obj;
				var total = self.params.total;
				var now = parseInt( 100 * ++imageTaggingCounter/total ) + '%';
				obj.update( now );
				self.statelem.unlink();
				if( imageTaggingCounter == total ) {
					obj.info( now + ' (completed)' );
					Wikipedia.removeCheckpoint();
				}
			}
			var onloaded = onsuccess;

			var onloading = function( wikipedia_wiki ) {}


			Wikipedia.addCheckpoint();
			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var title = snapshot.snapshotItem(i).value;
				var query = {
					'title': title,
					'action': 'submit'
				}
				var wikipedia_wiki = new Wikipedia.wiki( "Tagging " + title, query, twinklexfd.callbacks.pui.tagInstances );
				wikipedia_wiki.params = { title:title, total:total, obj:statusIndicator, date:dateString };
				wikipedia_wiki.onloading = onloading;
				wikipedia_wiki.onloaded = onloaded;
				wikipedia_wiki.onsuccess = onsuccess;
				wikipedia_wiki.get();
			}
		},
		tagInstances: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var text = form.wpTextbox1.value;
			var old_text = text;
			var wikiPage = new Mediawiki.Page( text );

			var tag = "\{\{puic|1=Image:" + wgTitle + "|log=" + self.params.date + "\}\}";
			wikiPage.addToImageComment( wgTitle, tag );

			text = wikiPage.getText();
			if( text == old_text ) {
				// Nothing to do, return
				self.onsuccess( self );
				Wikipedia.actionCompleted();
				return;
			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Tagging [[:Image:' + wgTitle + "]] which have been listed on [[WP:PUI|Possible unfree images]]" + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}

	},
	cfd: {
		taggingCategory: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var added_data = "";
			var summary = "";
			switch( self.params.xfdcat ) {
			case 'cfd':
				added_data = "\{\{subst:cfd\}\}";
				summary = "This category is being considered for deletion in accordance with [[WP:CDP|CDP]];" + TwinkleConfig.summaryAd;
				break;
			case 'cfm':
				added_data = "\{\{subst:cfm|" + self.params.target.replace('Category:','') + "\}\}";
				summary = "This category is being considered for merging in accordance with [[WP:CDP|CDP]];" + TwinkleConfig.summaryAd;
				break;
			case 'cfr':
				added_data = "\{\{subst:cfr|" + self.params.target.replace('Category:','') + "\}\}";
				summary = "This category is being considered for renaming in accordance with [[WP:CDP|CDP]];" + TwinkleConfig.summaryAd;
				break;
			case 'cfc':
				added_data = "\{\{subst:cfc|" + self.params.target + "\}\}";
				summary = "This category is being considered for conversion in accordance with [[WP:CDP|CDP]];" + TwinkleConfig.summaryAd;
				break;
			}
			var postData = {
				'wpMinoredit': undefined, // Per the cabal
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': summary,
				'wpTextbox1': added_data + "\n" + form.wpTextbox1.value
			};
			self.post( postData );
		},
		todaysList: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var added_data = "";
			var summary = "";
			switch( this.params.xfdcat ) {
			case 'cfd':
				added_data = "\{\{subst:cfd2|1=" + wgTitle + "|text=" + self.params.reason + " \~\~\~\}\}";
				summary = "Added delete nomination of [[:" + wgPageName + "]];" + TwinkleConfig.summaryAd;
				break;
			case 'cfm':
				added_data = "\{\{subst:cfm2|1=" + wgTitle + "|2=" + self.params.target + "|text=" + self.params.reason + " \~\~\~\}\}";
				summary = "Added merge nomination of [[:" + wgPageName + "]];" + TwinkleConfig.summaryAd;
				break;
			case 'cfr':
				added_data = "\{\{subst:cfr2|1=" + wgTitle + "|2=" + self.params.target + "|text=" + self.params.reason + " \~\~\~\}\}";
				summary = "Added rename nomination of [[:" + wgPageName + "]];" + TwinkleConfig.summaryAd;
				break;
			case 'cfc':
				added_data = "\{\{subst:cfc2|1=" + wgTitle + "|2=" + self.params.target + "|text=" + self.params.reason + " \~\~\~\}\}";
				summary = "Added convert nomination of [[:" + wgPageName + "]];" + TwinkleConfig.summaryAd;
				break;
			}
			var old_text = form.wpTextbox1.value;

			text = old_text.replace( '-->', "-->\n" + added_data );
			if( text == old_text ) {
				self.statelem.error( 'failed to find target spot to add the discussion to' );
				return;
			}
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': summary,
				'wpTextbox1': text
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			var intext = "";
			switch( self.params.xfdcat ) {
			case 'cfd':
				intext = 'for deletion';
				break;
			case 'cfm':
				intext = 'for merging into \{\{lc|' + self.params.target + "\}\}" ;
				break;
			case 'cfr':
				intext = 'for renaming to \{\{lc|' + self.params.target + "\}\}" ;
				break;
			case 'cfc':
				intext = 'for converting into an article named \{\{lc|' + self.params.target + "\}\}" ;
				break;
			}
			text += "\n==CfD nomination of [[:" + wgPageName + "]]==\nI have nominated \{\{lc|" + wgTitle + "\}\} " + intext + ". Your opinions on the matter are welcome; please participate in the discussion by adding your comments at [[" + self.params.todaysPage + "#" + wgPageName + "|the discussion page]]. Thank you. \~\~\~";

			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Notification: CfD nomination of \[\[:' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}
	},
	rfd: {
		main: function( self ) {
			var xmlDoc = self.responseXML;
			var target = xmlDoc.evaluate( '//redirects/r/@to', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
			if( !target ) {
				self.statelem.error( 'no target of this redirect, aborting' );
				return;
			}
			self.params.target = target;

			// Tagging redirect
			var query = {
				'title': wgPageName,
				'action': 'submit'
			};

			wikipedia_wiki = new Wikipedia.wiki( 'Tagging redirect with rfd tag', query, twinklexfd.callbacks.rfd.taggingRedirect );
			wikipedia_wiki.followRedirect = false;
			wikipedia_wiki.get();

			var date = new Date();
			var today = date.getUTCFullYear() + ' ' + date.getUTCMonthName() + ' ' + date.getUTCDate();
			var query = {
				'title': 'Wikipedia:Redirects for discussion/Log/' + today,
				'action': 'submit'
			};

			wikipedia_wiki = new Wikipedia.wiki( 'Adding deletion discussion to todays list', query, twinklexfd.callbacks.rfd.todaysList );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();

			// Updating data for the action completed event
			Wikipedia.actionCompleted.redirect = query['title'];
			Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the discussion page";

			self.params.todaysPage = query['title'];

			// Notifying initial contributor
			var query = {
				'action': 'query',
				'prop': 'revisions',
				'titles': wgPageName,
				'rvlimit': 1,
				'rvprop': 'user',
				'rvdir': 'newer'
			}
			var callback = function( self ) {
				var xmlDoc = self.responseXML;
				var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
				var query = {
					'title': 'User talk:' + user,
					'action': 'submit'
				};
				var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinklexfd.callbacks.rfd.userNotification );
				wikipedia_wiki.params = self.params;
				wikipedia_wiki.get();
			}
			var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
			wikipedia_api.params = self.params;
			wikipedia_api.post();

		},
		taggingRedirect: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var postData = {
				'wpMinoredit': undefined, // Per 
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "This redirect has been listed on [[Wikipedia:Redirects for discussion]]\]\]." + TwinkleConfig.summaryAd,
				'wpTextbox1': "\{\{rfd\}\}\n" + form.wpTextbox1.value
			};
			self.post( postData );
		},
		todaysList: function( self ) {
			var form = self.responseXML.getElementById('editform');
			var text = form.wpTextbox1.value;
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': "Adding [[" + wgPageName + ']].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text + "\{\{subst:rfd2|redirect="+ wgPageName + "|target=" + self.params.target + "|text=" + self.params.reason.toUpperCaseFirstChar() +"\}\}"
			};
			self.post( postData );
		},
		userNotification: function( self ) {
			var form = self.responseXML.getElementById( 'editform' );
			var text = form.wpTextbox1.value;
			text += "\n==RfD nomination of [[:" + wgPageName + "]]==\nI have nominated " + ln( wgNamespaceNumber, wgTitle ) + " for discussion. Your opinions on the matter are welcome; please participate in the discussion by adding your comments at [[" + self.params.todaysPage + "#" + wgPageName + "|the discussion page]]. Thank you. \~\~\~";
			var postData = {
				'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
				'wpWatchthis': form.wpWatchthis.checked ? '' : undefined,
				'wpStarttime': form.wpStarttime.value,
				'wpEdittime': form.wpEdittime.value,
				'wpAutoSummary': form.wpAutoSummary.value,
				'wpEditToken': form.wpEditToken.value, 'wpSection': '',
				'wpSummary': 'Notification: RFD posting of \[\[' + wgPageName + '\]\].' + TwinkleConfig.summaryAd,
				'wpTextbox1': text
			};
			self.post( postData );
		}
	}
}

twinklexfd.callback.evaluate = function(e) {

	wgPageName = wgPageName.replace( /_/g, ' ' ); // for queen/king/whatever and country!

	var type =  e.target.category.value;
	var reason = e.target.xfdreason.value;
	if( type in {'afd':'','cfd':''} ) {
		var xfdcat = e.target.xfdcat.value;
	}
	if( type == 'ifd' ) {
		var pui = e.target.pui.checked;
	}

	Status.init( e.target );

	if( type == null ) {
		Status.error( 'Error', 'no action given' );
		return;
	}

	switch( type ) {
	case 'afd': // AFD
		var query = {
			'action': 'query',
			'list': 'allpages',
			'apprefix': 'Articles for deletion/' + wgPageName,
			'apnamespace': 4,
			'apfilterredir': 'nonredirects',
			'aplimit': userIsInGroup( 'sysop' ) ? 5000 : 500
		};
		var wikipedia_api = new Wikipedia.api( 'Tagging article with deletion tag', query, twinklexfd.callbacks.afd.main );
		wikipedia_api.params = { reason:reason, xfdcat:xfdcat };
		wikipedia_api.post();
		break;
	case 'tfd': // TFD

		Wikipedia.addCheckpoint();
		// Tagging article
		var query = {
			'title': wgPageName,
			'action': 'submit'
		};
		wikipedia_wiki = new Wikipedia.wiki( 'Tagging template with deletion tag', query, twinklexfd.callbacks.tfd.taggingTemplate );
		wikipedia_wiki.get();

		// Adding discussion
		var date = new Date();

		query = {
			'title': 'Wikipedia:Templates for deletion/Log/' + date.getUTCFullYear() + ' ' + date.getUTCMonthName() + ' ' + date.getUTCDate(),
			'action': 'submit',
			'section': 1
		};

		// Updating data for the action completed event
		Wikipedia.actionCompleted.redirect = query['title'];
		Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the list of today";

		wikipedia_wiki = new Wikipedia.wiki( 'Adding discussion to todays list', query, twinklexfd.callbacks.tfd.todaysList );
		wikipedia_wiki.params = { reason:reason };
		wikipedia_wiki.get();

		var query = {
			'action': 'query',
			'prop': 'revisions',
			'titles': wgPageName,
			'rvlimit': 1,
			'rvprop': 'user',
			'rvdir': 'newer'
		}
		var callback = function( self ) {
			var xmlDoc = self.responseXML;
			var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
			var query = {
				'title': 'User talk:' + user,
				'action': 'submit'
			};
			var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinklexfd.callbacks.tfd.userNotification );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();
		}
		var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
		wikipedia_api.params = self.params;
		wikipedia_api.post();

		Wikipedia.removeCheckpoint();
		break;
	case 'mfd': // MFD

		var query = {
			'action': 'query',
			'list': 'allpages',
			'apprefix': 'Miscellany for deletion/' + wgPageName,
			'apnamespace': 4,
			'apfilterredir': 'nonredirects',
			'aplimit': userIsInGroup( 'sysop' ) ? 5000 : 500
		};
		var wikipedia_api = new Wikipedia.api( 'Quering allpages', query, twinklexfd.callbacks.mfd.main );
		wikipedia_api.params = { reason:reason, xfdcat:xfdcat };
		wikipedia_api.post();
		break;
	case 'ifd': // IFD

		var date = new Date();
		var dateString = date.getUTCFullYear() + ' ' + date.getUTCMonthName() + ' ' + date.getUTCDate();
		var params = { reason: reason, date: dateString };

		Wikipedia.addCheckpoint();
		if( pui ) {
			// Tagging image
			var query = {
				'title': wgPageName,
				'action': 'submit'
			};

			var wikipedia_wiki = new Wikipedia.wiki( 'Tagging image with PUI tag', query, twinklexfd.callbacks.pui.taggingImage );
			wikipedia_wiki.params = params;
			wikipedia_wiki.get();
			// Adding discussion

			query = {
				'title': 'Wikipedia:Possibly unfree images/' + dateString,
				'action': 'submit'
			};

			// Updating data for the action completed event
			Wikipedia.actionCompleted.redirect = query['title'];
			Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the list of today";

			wikipedia_wiki = new Wikipedia.wiki( 'Adding discussion to todays list', query, twinklexfd.callbacks.pui.todaysList );
			wikipedia_wiki.params = params;
			wikipedia_wiki.get();

			var query = {
				'action': 'query',
				'prop': 'revisions',
				'titles': wgPageName,
				'rvlimit': 1,
				'rvprop': 'user',
				'rvdir': 'newer'
			}
			var callback = function( self ) {
				var xmlDoc = self.responseXML;
				var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
				var query = {
					'title': 'User talk:' + user,
					'action': 'submit'
				};
				var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinklexfd.callbacks.pui.userNotification );
				wikipedia_wiki.params = self.params;
				wikipedia_wiki.get();
			}
			var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
			wikipedia_api.params = params;
			wikipedia_api.post();

			Wikipedia.removeCheckpoint();

			// adding tag to captions
			var query = {
				'action': 'query',
				'list': 'imageusage',
				'titles': wgPageName,
				'iulimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};

			var wikipedia_api = new Wikipedia.api( 'Grabbing image links', query, twinklexfd.callbacks.pui.tagInstancesMain );
			wikipedia_api.post();

		} else {
			// Tagging image
			var query = {
				'title': wgPageName,
				'action': 'submit'
			};

			var wikipedia_wiki = new Wikipedia.wiki( 'Tagging image with deletion tag', query, twinklexfd.callbacks.ifd.taggingImage );
			wikipedia_wiki.params = params;
			wikipedia_wiki.get();

			// Contributor specific edits
			var query = {
				'action': 'query',
				'prop': 'revisions',
				'titles': wgPageName,
				'rvlimit': 1,
				'rvprop': 'user',
				'rvdir': 'newer'
			}
			var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, twinklexfd.callbacks.ifd.main );
			wikipedia_api.params = params;
			wikipedia_api.post();

			// adding tag to captions
			var query = {
				'action': 'query',
				'list': 'imageusage',
				'titles': wgPageName,
				'iulimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};

			var wikipedia_api = new Wikipedia.api( 'Grabbing image links', query, twinklexfd.callbacks.ifd.tagInstancesMain );
			wikipedia_api.post();
		}
		Wikipedia.removeCheckpoint();
		break;
	case 'cfd':
		Wikipedia.addCheckpoint();
		if( e.target.xfdtarget ) {
			var target = e.target.xfdtarget.value.replace( /^\:?Category\:/, '' );
		} else {
			var target = '';
		}

		var date = new Date();
		var todaysPage = 'Wikipedia:Categories for discussion/Log/' + date.getUTCFullYear() + ' ' + date.getUTCMonthName() + ' ' + date.getUTCDate();

		// Updating data for the action completed event
		Wikipedia.actionCompleted.redirect = todaysPage;
		Wikipedia.actionCompleted.notice = "Nomination completed, redirecting now to the discussion page";

		// Tagging category

		var query = {
			'title': wgPageName,
			'action': 'submit'
		};
		var params = { reason:reason, xfdcat:xfdcat, target:target };

		var wikipedia_wiki = new Wikipedia.wiki( 'Tagging category with tag', query, twinklexfd.callbacks.cfd.taggingCategory );
		wikipedia_wiki.params = params;
		wikipedia_wiki.get();

		// Todays list
		var query = {
			'title': todaysPage,
			'action': 'submit',
			'section': 2
		};

		var wikipedia_wiki = new Wikipedia.wiki( 'Adding discussion to todays list', query, twinklexfd.callbacks.cfd.todaysList );
		wikipedia_wiki.params = params;
		wikipedia_wiki.get();

		// Notification to first contributor
		var query = {
			'action': 'query',
			'prop': 'revisions',
			'titles': wgPageName,
			'rvlimit': 1,
			'rvprop': 'user',
			'rvdir': 'newer'
		}
		var callback = function( self ) {
			var xmlDoc = self.responseXML;
			var user = xmlDoc.evaluate( '//rev/@user', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
			var query = {
				'title': 'User talk:' + user,
				'action': 'submit'
			};
			var wikipedia_wiki = new Wikipedia.wiki( 'Notifying of initial contributor (' + user + ')', query, twinklexfd.callbacks.cfd.userNotification );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.get();
		}
		var wikipedia_api = new Wikipedia.api( 'Grabbing data of initial contributor', query, callback );
		wikipedia_api.params = { xfdcat:xfdcat, target:target, todaysPage:todaysPage };
		wikipedia_api.post();
		Wikipedia.removeCheckpoint();
		break;
	case 'rfd':
		var query = {
			'action': 'query',
			'titles': wgPageName,
			'redirects': true
		};
		var wikipedia_api = new Wikipedia.api( 'Quering redirect', query, twinklexfd.callbacks.rfd.main );
		wikipedia_api.params = { reason:reason };
		wikipedia_api.post();
		break;
	}
}

// more extra shit
$(function()
{
	if (SHITTYHACK)
	{
		twinklespeedy.callback();
	}
	else if( wgNamespaceNumber == 0 && wgCurRevisionId != false && !QueryString.exists('diff') && document.location.toString().indexOf('action=history') == -1)
	{
		mw.util.addPortletLink( 'p-cactions', mw.config.get('wgServer') + mw.config.get('wgScript') + '?lollercaust=roflcopter&title=' + wgTitle, "afd", "amidoingitrite", "Delete this article", "");
		mw.util.addPortletLink( 'p-cactions', mw.config.get('wgServer') + mw.config.get('wgScript') + '?lollercaust=roflcopter&sprite=easterpink&title=' + wgTitle, "a/d", "guessiam", "Delete this article", "");
	}
});

// twinkledelimages.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = {};
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.deletionSummaryAd (string)
 If ad should be added or not to deletion summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.deletionSummaryAd ) == 'undefined' ) {
	TwinkleConfig.deletionSummaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.deliChunks (integer)
 How many images should be processed at a time
 */
if( typeof( TwinkleConfig.deliChunks ) == 'undefined' ) {
	TwinkleConfig.deliChunks = 50;
}

/**
 TwinkleConfig.deliMax (integer)
 How many images should be processed maximum
 */
if( typeof( TwinkleConfig.deliMax ) == 'undefined' ) {
	TwinkleConfig.deliMax = 500;
}
function twinkledeli() {
	if( (wgNamespaceNumber != 14 && wgNamespaceNumber != 0 && wgNamespaceNumber != 2) || wgCurRevisionId == false || document.location.toString().indexOf('action=history') != -1) {
		return;
	}
	if( userIsInGroup( 'sysop' ) ) {

		mw.util.addPortletLink( 'p-cactions', "javascript:twinkledeli.callback()", "di/b", "tw-deli", "Delete image found on page", "");
	}
}
$(twinkledeli);

function getChecked( nodelist ) {
	if( !( nodelist instanceof NodeList ) ) {
		return nodelist.checked ? [ nodelist.value ] : [];
	}
	var result = [];
	for(var i  = 0; i < nodelist.length; ++i ) {
		if( nodelist[i].checked ) {
			result.push( nodelist[i].value );
		}
	}
	return result;
}

twinkledeli.unlinkCache = {};
twinkledeli.callback = function twinklesdeliCallback() {
	var Window = new SimpleWindow( 800, 400 );
	Window.setTitle( "Batch image deletion" );

	var form = new QuickForm( twinkledeli.callback.evaluate );
	form.append( {
			type: 'checkbox',
			list: [
				{ 
					label: 'Delete images',
					name: 'delete_image',
					value: 'delete',
					checked: true
				},
				{
					label: 'Remove image instances to the image',
					name: 'unlink_image',
					value: 'unlink',
					checked: false
				}
			]
		} );
	form.append( {
			type: 'select',
			name: 'reason',
			label: 'Reason: ',
			list: [
				{ 
					label: 'I1: Redundant image',
					value: '[[WP:CSD#I1|CSD I1]]: Redundant to another image',
					selected: wgTitle.indexOf('Non-commercial use only images for speedy deletion') != -1
				},
				{ 
					label: 'I3: Improper license',
					value: '[[WP:CSD#I3|CSD I3]]: Licensed as for non-commercial use only, non-derivative use or used with permission\, uploaded on or after May 19, 2005, and no assertion of fair use was provided',
					selected: wgTitle.indexOf('Redundant images for speedy deletion') != -1
				},
				{ 
					label: 'I4: Lack of licensing information',
					value: '[[WP:CSD#I4|CSD I4]]: Image lacking sources or licensing information for more than seven days',
					selected: wgTitle.indexOf('Images with unknown source as of ') != -1 || wgTitle.indexOf('Images with no copyright tag as of ') != -1 || wgTitle.indexOf('Images with no copyright tag as of ') != -1 || wgTitle.indexOf('Images with unknown copyright status as of ') != -1
				},
				{
					label: 'I5: Unused unfree copyrighted images',
					value: '[[WP:CSD#I5|CSD I5]]: Unfree image that was not used for more than seven days',
					selected: wgTitle.indexOf('Orphaned fairuse images as of ') != -1
				},
				{
					label: 'I6: Missing fair use rationale',
					value: '[[WP:CSD#I6|CSD I6]]: No [[WP:FURG|justification]] given for fair use for more than seven days',
					selected: wgTitle.indexOf('Images with no fair use rationale as of ') != -1
				},
				{
					label: 'I7: Invalid fair use claim',
					value: '[[WP:CSD#I7|CSD I7]]: Bad [[WP:FURG|justification]] given for fair use and the uploader was notified more than 48 hours ago',
					selected: wgTitle.indexOf('Disputed non-free images as of ') != -1
				},
				{
					label: 'I7: Replaceable fair use',
					value: '[[WP:CSD#I7|CSD I7]]: [[WP:NFC#Images_2|Replaceable]] fair use and the uploader was notified more than 48 hours ago',
					selected: wgTitle.indexOf('Replaceable fair use to be decided after ') != -1
				},
				{
					label: 'I8: Image available on Commons',
					value: '[[WP:CSD#I8|CSD I8]]: Image exists on the Commons',
					selected: wgTitle.indexOf('on Wikimedia Commons') != -1
				}
			]
		} );
	if( wgNamespaceNumber == Namespace.CATEGORY ) {
		var query = {
			'action': 'query',
			'generator': 'categorymembers',
			'gcmtitle': wgPageName,
			'gcmnamespace': Namespace.IMAGE,
			'gcmlimit' : TwinkleConfig.deliMax, 
			'prop': [ 'imageinfo', 'categories' ]
		};
	} else {
		var query = {
			'action': 'query',
			'generator': 'images',
			'titles': wgPageName,
			'prop': 'imageinfo'
		};
	}
	var wikipedia_api = new Wikipedia.api( 'Grabbing images', query, function( self ) {
			var xmlDoc = self.responseXML;
			var snapshot = xmlDoc.evaluate('//page[@imagerepository="local"]', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
			var list = [];
			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var object = snapshot.snapshotItem(i);
				var image = xmlDoc.evaluate( '@title', object, null, XPathResult.STRING_TYPE, null ).stringValue;
				var user = xmlDoc.evaluate( 'imageinfo/ii/@user', object, null, XPathResult.STRING_TYPE, null ).stringValue;

				var disputed = xmlDoc.evaluate( 'boolean(categories/cl[@title="Category:Contested candidates for speedy deletion"])', object, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;
				list.push( {label:i + ": " + image + ' (' + user + ')' + ( disputed ? ' DISPUTED' : '' ), value:image, checked:true });
			}
			self.params.form.append( {
					type: 'checkbox',
					name: 'images',
					list: list
				}
			)
			self.params.form.append( { type:'submit' } );

			var result = self.params.form.render();
			self.params.Window.setContent( result );


		}  );

	wikipedia_api.params = { form:form, Window:Window };
	wikipedia_api.post();
	var root = document.createElement( 'div' );
	Status.init( root );
	Window.setContent( root );
	Window.display();
}

twinkledeli.currentcounter = 0;
twinkledeli.currentdeletor;
twinkledeli.callback.evaluate = function twinkledeliCallbackEvaluate(event) {
	wgPageName = wgPageName.replace( /_/g, ' ' ); // for queen/king/whatever and country!
	var images = getChecked( event.target.images );
	var reason = event.target.reason.value;
	var delete_image = event.target.delete_image.checked;
	var unlink_image = event.target.unlink_image.checked;
	if( ! reason ) {
		return;
	}
	Status.init( event.target );
	function toCall( work ) {
		if( work.length == 0 && twinkledeli.currentcounter <= 0 ) {
			Status.info( 'work done' );
			window.clearInterval( twinkledeli.currentdeletor );
			Wikipedia.removeCheckpoint();
			return;
		} else if( twinkledeli.currentcounter <= 0 ) {
			twinkledeli.currentcounter = TwinkleConfig.deliChunks;
			twinkledeli.unlinkCache = []; // Clear the cache
			var images = work.pop();
			for( var i = 0; i < images.length; ++i ) {
				var image = images[i];
				var query = {
					'action': 'query',
					'titles': image
				}
				var wikipedia_api = new Wikipedia.api( 'Checking if image ' + image + ' exists', query, twinkledeli.callbacks.main );
				wikipedia_api.params = { image:image, reason:reason, unlink_image:unlink_image, delete_image:delete_image };
				wikipedia_api.post();
			}
		}
	}
	var work = images.chunk( TwinkleConfig.deliChunks );
	Wikipedia.addCheckpoint();
	twinkledeli.currentdeletor = window.setInterval( toCall, 1000, work );
}
twinkledeli.callbacks = {
	main: function( self ) {
		var xmlDoc = self.responseXML;
		var normal = xmlDoc.evaluate( '//normalized/n/@to', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
		if( normal ) {
			self.params.image = normal;
		}
		var exists = xmlDoc.evaluate( 'boolean(//pages/page[@title="' + self.params.image.replace( /"/g, '\\"') + '" and not(@missing)])', xmlDoc, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;

		if( ! exists ) {
			self.statelem.error( "It seems that the page doesn't exists, perhaps it has already been deleted" );
			return;
		}
		if( self.params.unlink_image ) {
			var query = {
				'action': 'query',
				'list': 'imageusage',
				'titles': self.params.image,
				'iulimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};
			var wikipedia_api = new Wikipedia.api( 'Grabbing image links', query, twinkledeli.callbacks.unlinkImageInstancesMain );
			wikipedia_api.params = self.params;
			wikipedia_api.post();
		}
		if( self.params.delete_image ) {
			var query = { 
				'title': self.params.image, 
				'action': 'delete'
			};
			var wikipedia_wiki = new Wikipedia.wiki( 'Deleting image ' + self.params.image, query, twinkledeli.callbacks.deleteImage, function( self ) { 
					--twinkledeli.currentcounter;
					var link = document.createElement( 'a' );
					link.setAttribute( 'href', wgArticlePath.replace( '$1', self.query['title'] ) );
					link.setAttribute( 'title', self.query['title'] );
					link.appendChild( document.createTextNode( self.query['title'] ) );
					self.statelem.info( [ 'completed (' , link , ')' ] );

				} );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.followRedirect = false;
			wikipedia_wiki.get();		
		}
	},
	deleteImage: function( self ) {
		var form = this.responseXML.getElementById( 'deleteconfirm' );
		if( ! form ) { // Hell, image deletion is b0rked :(
			form = this.responseXML.getElementsByTagName( 'form' )[0];
			var postData = {
				'wpDeleteReasonList': 'other',
				'wpReason': self.params.reason + TwinkleConfig.deletionSummaryAd,
				'wpEditToken': form.wpEditToken.value
			}
			self.post( postData );
		} else {

			var postData = {
				'wpWatch': form.wpWatch.checked ? '' : undefined,
				'wpReason': "Deleted because \"" + self.params.reason + "\"." + TwinkleConfig.deletionSummaryAd,
				'wpEditToken': form.wpEditToken.value
			}
			self.post( postData );
		}
	},
	unlinkImageInstancesMain: function( self ) {
		var xmlDoc = self.responseXML;
		var snapshot = xmlDoc.evaluate('//imageusage/iu/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

		if( snapshot.snapshotLength == 0 ) {
			return;
		}

		var statusIndicator = new Status('Unlinking instances image', '0%');

		var total = snapshot.snapshotLength * 2;

		var onsuccess = function( self ) {
			var obj = self.params.obj;
			var total = self.params.total;
			var now = parseInt( 100 * ++(self.params.current)/total ) + '%';
			obj.update( now );
			self.statelem.unlink();
			if( self.params.current >= total ) {
				obj.info( now + ' (completed)' );
				Wikipedia.removeCheckpoint();
			}
		}
		var onloaded = onsuccess;

		var onloading = function( self ) {}


		Wikipedia.addCheckpoint();
		if( snapshot.snapshotLength == 0 ) {
			statusIndicator.info( '100% (completed)' );
			Wikipedia.removeCheckpoint();
			return;
		}
		self.params.total = total;
		self.params.obj = statusIndicator;
		self.params.current =   0;

		for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
			var title = snapshot.snapshotItem(i).value;
			var query = {
				'title': title,
				'action': 'submit'
			}
			var wikipedia_wiki = new Wikipedia.wiki( "Unlinking on " + title, query, twinkledeli.callbacks.unlinkImageInstances );
			var params = clone( self.params );
			params.title = title;

			wikipedia_wiki.params = params;
			wikipedia_wiki.onloading = onloading;
			wikipedia_wiki.onloaded = onloaded;
			wikipedia_wiki.onsuccess = onsuccess;
			wikipedia_wiki.get();
		}
	},
	unlinkImageInstances: function( self ) {
		var image = self.params.image.replace( /^Image:/, '' );
		var form = self.responseXML.getElementById('editform');
		var text;

		if( self.params.title in twinkledeli.unlinkCache ) {
			text = twinkledeli.unlinkCache[ self.params.title ];
		} else {
			text = form.wpTextbox1.value;
		}
		var old_text = text;
		var wikiPage = new Mediawiki.Page( text );
		wikiPage.commentOutImage( image , 'Commented out because image was deleted' );

		text = wikiPage.getText();
		twinkledeli.unlinkCache[ self.params.title ] = text;
		if( text == old_text ) {
			// Nothing to do, return
			self.onsuccess( self );
			Wikipedia.actionCompleted( self );
			return;
		}
		var postData = {
			'wpMinoredit': form.wpMinoredit.checked ? '' : '',
			'wpWatchthis': undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': 'Removing [[:' + self.params.image + '|deleted image]]' + TwinkleConfig.deletionSummaryAd,
			'wpTextbox1': text
		};
		self.post( postData );
	}
}

// twinkleimagetraverse.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = {};
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.deletionSummaryAd (string)
 If ad should be added or not to deletion summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.deletionSummaryAd ) == 'undefined' ) {
	TwinkleConfig.deletionSummaryAd = " using [[WP:TW|TW]]";
}

function twinkleimagetraverse() {
	if( (wgNamespaceNumber != 14 && wgNamespaceNumber != 0 && wgNamespaceNumber != 2) || wgCurRevisionId == false || document.location.toString().indexOf('action=history') != -1) {
		return;
	}
	if( userIsInGroup( 'sysop' ) && wgNamespaceNumber == Namespace.CATEGORY ) {

		mw.util.addPortletLink( 'p-cactions', "javascript:kajd()", "di/t", "tw-imagetraverse", "Traverse category", "");
	}
}
$(twinkleimagetraverse);

twinkleimagetraverse.basequery = {
	'action': 'query',
	'generator': 'categorymembers',
	'gcmtitle': wgPageName,
	'gcmnamespace': Namespace.IMAGE,
	'gcmlimit' : 1, 
	'prop': [ 'imageinfo', 'categories', 'revisions' ],
	'rvlimit': 20,
	'iihistory': true,
	'rvprop': [ 'user', 'size', 'flags', 'ids', 'comment', 'timestamp' ],
	'iiprop': [ 'timestamp', 'user', 'url', 'size', 'comment' ]
};
twinkleimagetraverse.callback = function() {
	var Window = new SimpleWindow( 1263, 899 );
	Window.setTitle( "Image traverse" );
	var form = new QuickForm( twinkleimagetraverse.callback.evaluate, 'change' );
	form.append( {
			type: 'button',
			label: 'Skip',
			event: twinkleimagetraverse.callbacks.skip
		} );
	form.append( {
			type: 'button',
			label: 'Delete',
			event: twinkleimagetraverse.callbacks.deleteMain
		} );
	form.append( {
			type: 'checkbox',
			list: [
				{
					label: 'Remove image instances to the image',
					name: 'unlink',
					value: 'unlink',
					checked: false
				}
			]
		} );
	form.append( {
			type: 'input',
			label: 'Reason',
			name: 'reason',
			value: JUDAICAHACK,
			size: 80
		} );
	var root = document.createElement( 'table' );

	root.style.background = 'transparent';
	root.style.height = '850px';
	var row = root.appendChild( document.createElement( 'tr' ) );
	var options = row.appendChild(  document.createElement( 'td' ) );
	options.setAttribute( 'colspan', 2 );
	var rendered = form.render();
	options.appendChild( rendered );

	rendered.root = root;

	
	options.style.borderBottom = '1px solid gray';
	options.style.height = '80px';
	var row = root.appendChild( document.createElement( 'tr' ) );
	var oview = row.appendChild(  document.createElement( 'td' ) );
	var ohistbox = row.appendChild(  document.createElement( 'td' ) );
	ohistbox.style.width = '250px';
	ohistbox.style.verticalAlign = 'top';
	var histbox = ohistbox.appendChild(  document.createElement( 'div' ) );
	histbox.style.overflow = 'auto';
	histbox.style.height = '640px';
	oview.style.verticalAlign = 'top';
	var view = oview.appendChild(  document.createElement( 'div' ) );
	view.style.height = '640px';
	view.style.overflow = 'auto';
	var row = root.appendChild( document.createElement( 'tr' ) );
	var ostatus = row.appendChild(  document.createElement( 'td' ) );
	ostatus.style.borderTop = '1px solid gray';
	ostatus.setAttribute( 'colspan', 2 );
	var status = ostatus.appendChild(  document.createElement( 'div' ) );
	ostatus.style.verticalAlign = 'top';
	status.style.height = '40px';
	status.style.overflow = 'auto';
	Wikipedia.actionCompleted.event = function() {} // just avoid it
	var wikipedia_api = new Wikipedia.api( 'Grabbing images', twinkleimagetraverse.basequery, twinkleimagetraverse.callbacks.main );
	wikipedia_api.params = { root:root, view:view, histbox:histbox, status:status, Window:Window };
	root.params = wikipedia_api.params;
	wikipedia_api.post();

	Status.init( status );
	Window.setContent( root );
	Window.display();
}

twinkleimagetraverse.callback.evaluate = function() {
}

function make_wikilink( page, title, oldid, diff ) {
	var query = {
		'title': page,
		'diff': diff,
		'oldid': oldid
	}
	var url = wgScriptPath + '/index.php?' + QueryString.create( query );
	var a = document.createElement( 'a' );
	a.setAttribute( 'href', url );
	a.setAttribute( 'title', page );
	a.appendChild( document.createTextNode( title ) );
	return a;
}

twinkleimagetraverse.callbacks = {
	main: function( self ) {
		var xmlDoc = self.responseXML;

		var image = xmlDoc.evaluate( '//pages/page/@title', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;

		if( !image ) {
			alert( 'no more images' );
			return;
		}
		var next = xmlDoc.evaluate( '//query-continue/categorymembers/@gcmcontinue', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
		var pagehistory = xmlDoc.evaluate( '//pages/page/revisions/rev', xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
		var filehistory = xmlDoc.evaluate( '//pages/page/imageinfo/ii', xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
		var categories = xmlDoc.evaluate( '//pages/page/categories/cl', xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );

		var pagehistorylist = document.createElement( 'ul' );
		var filehistorylist = document.createElement( 'ul' );
		var categorylist = document.createElement( 'ul' );

		var entry = document.createElement( 'li' );

		for( var i = 0; i < pagehistory.snapshotLength; ++i ) {
			var cur = pagehistory.snapshotItem(i);
			var tmp = entry.cloneNode(false);
			tmp.appendChild( make_wikilink( image, cur.getAttribute( 'timestamp' ), cur.getAttribute( 'revid' ) ) );
			tmp.appendChild( document.createTextNode( ' ' ) );
			tmp.appendChild( make_wikilink( 'User:' + cur.getAttribute( 'user' ), cur.getAttribute( 'user' ) ) );
			tmp.appendChild( document.createTextNode( ' \(' + ( new Bytes( cur.getAttribute( 'size' ) ) ).toString() + '\) \(' ) );
			tmp.appendChild( document.createElement( 'em' ) ).appendChild(document.createTextNode( cur.getAttribute( 'comment' ) ) );
			tmp.appendChild( document.createTextNode( '\)' ) );
			pagehistorylist.appendChild( tmp );
		}
		
		for( var i = 0; i < filehistory.snapshotLength; ++i ) {
			var cur = filehistory.snapshotItem(i);
			var tmp = entry.cloneNode(false);
			var link = document.createElement( 'a' );
			link.setAttribute( 'href', cur.getAttribute( 'url' ) );
			link.appendChild( document.createTextNode( cur.getAttribute( 'timestamp' ) ) );
			tmp.appendChild( link );
			tmp.appendChild( document.createTextNode( ' ' ) );
			tmp.appendChild( make_wikilink( 'User:' + cur.getAttribute( 'user' ), cur.getAttribute( 'user' ) ) );
			tmp.appendChild( document.createTextNode( ' \(' + ( new Bytes( cur.getAttribute( 'size' ) ) ).toString() + '\) \(' ) );
			tmp.appendChild( document.createElement( 'em' ) ).appendChild(document.createTextNode( cur.getAttribute( 'comment' ) ) );
			tmp.appendChild( document.createTextNode( '\)' ) );
			filehistorylist.appendChild( tmp );
		}

		for( var i = 0; i < categories.snapshotLength; ++i ) {
			var cur = categories.snapshotItem(i);
			var tmp = entry.cloneNode(false);
			tmp.appendChild( make_wikilink( cur.getAttribute( 'title' ), cur.getAttribute( 'title' ).replace( /Category:/, '' ) ) );
			categorylist.appendChild( tmp );
		}
		self.params.next = next;
		self.params.image = image;
		var hist = self.params.histbox;
		while( hist.hasChildNodes() ) {
			hist.removeChild( hist.lastChild );
		}
		hist.appendChild( document.createElement( 'h2' ) ).appendChild( document.createTextNode( 'Image usage' ) );
		var placeholder = hist.appendChild( document.createElement( 'div' ));
		placeholder.appendChild( document.createTextNode( 'Grabbing data...' ) );
		self.params.imageusageplaceholder = placeholder;
		hist.appendChild( document.createElement( 'h2' ) ).appendChild( document.createTextNode( 'Page history' ) );
		hist.appendChild( pagehistorylist );
		hist.appendChild( document.createElement( 'h2' ) ).appendChild( document.createTextNode( 'File history' ) );
		hist.appendChild( filehistorylist );
		hist.appendChild( document.createElement( 'h2' ) ).appendChild( document.createTextNode( 'Categories' ) );
		hist.appendChild( categorylist );

		var query = {
			'action': 'parse',
			'title': image,
			'text': '\{\{Wikipedia:WikiProject User scripts/Scripts/Twinkle/Template|' + image.replace(/^Image:/, '') + '\}\}',
			'prop': 'text'
		}
		var wikipedia_api = new Wikipedia.api( 'Rendering', query, twinkleimagetraverse.callbacks.render1 );
		wikipedia_api.params = self.params;
		wikipedia_api.post();
	},
	render1: function( self ) {
		var xmlDoc = self.responseXML;
		var html = xmlDoc.evaluate( '//parse/text', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
		self.params.view.innerHTML = "<big>" + html + "</big>"; // gah!

		// add instance usage
		var query = {
			'action': 'query',
			'list': 'imageusage',
			'iutitle': self.params.image,
			'iulimit': 20,
			'iufilterredir': 'nonredirects'
		}
		var wikipedia_api = new Wikipedia.api( 'Rendering', query, twinkleimagetraverse.callbacks.render2 );
		wikipedia_api.params = self.params;
		wikipedia_api.post();
	},
	render2: function( self ) {
		var xmlDoc = self.responseXML;
		var usage = xmlDoc.evaluate( '//imageusage/iu', xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );

		var usagelist = document.createElement( 'ul' );

		var entry = document.createElement( 'li' );

		for( var i = 0; i < usage.snapshotLength; ++i ) {
			var cur = usage.snapshotItem(i);
			var tmp = entry.cloneNode(false);
			tmp.appendChild( make_wikilink( cur.getAttribute( 'title' ), cur.getAttribute( 'title' ) ) );
			usagelist.appendChild( tmp );
		}
		var hist = self.params.histbox;
		hist.replaceChild( usagelist, self.params.imageusageplaceholder );

	},
	next: function( params ) {
		twinkleimagetraverse.basequery['gcmcontinue'] = params.next;
		var wikipedia_api = new Wikipedia.api( 'Grabbing images', twinkleimagetraverse.basequery, twinkleimagetraverse.callbacks.main );
		wikipedia_api.params = params;
		wikipedia_api.post();
	},
	skip: function( event ) {
		var form = event.target.form;
		var params = form.root.params;
		twinkleimagetraverse.callbacks.next( params );
		Status.info( 'Skipped', params.image );
	},
	deleteMain: function( event ) {
		var form = event.target.form;
		var params = form.root.params;
		params.reason = form.reason.value;

		if( form.unlink.checked ) {
			var query = {
				'action': 'query',
				'list': 'imageusage',
				'titles': params.image,
				'iulimit': 5000
			};
			var wikipedia_api = new Wikipedia.api( 'Grabbing image links', query, twinkleimagetraverse.callbacks.unlinkImageInstancesMain );
			wikipedia_api.params = params;

			wikipedia_api.post();
		}
		var query = { 
			'title': params.image, 
			'action': 'delete'
		};
		var wikipedia_wiki = new Wikipedia.wiki( 'Deleting image ' + params.image, query, twinkleimagetraverse.callbacks.deleteImage, function( self ) {
				twinkleimagetraverse.callbacks.next( self.params );
				self.statelem.unlink();
				Status.info( 'Deleted', self.params.image );

			});
		wikipedia_wiki.params = params;
		wikipedia_wiki.followRedirect = false;
		wikipedia_wiki.get();	
	},
	deleteImage: function( self ) {
		var form = this.responseXML.getElementById( 'deleteconfirm' );
		if( ! form ) { // Hell, image deletion is b0rked :(
			form = this.responseXML.getElementsByTagName( 'form' )[0];
			var postData = {
				'wpDeleteReasonList': 'other',
				'wpReason': self.params.reason,
				'wpEditToken': form.wpEditToken.value
			}
			self.post( postData );
		} else {

			var postData = {
				'wpWatch': form.wpWatch.checked ? '' : undefined,
				'wpReason': self.params.reason,
				'wpEditToken': form.wpEditToken.value,
				'wpDeleteReasonList': 'other'
			}
			self.post( postData );
		}
	},
	unlinkImageInstancesMain: function( self ) {
		var xmlDoc = self.responseXML;
		var snapshot = xmlDoc.evaluate('//imageusage/iu/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

		if( snapshot.snapshotLength == 0 ) {
			return;
		}

		var statusIndicator = new Status('Unlinking instances image', '0%');

		var total = snapshot.snapshotLength * 2;

		var onsuccess = function( self ) {
			var obj = self.params.obj;
			var total = self.params.total;
			var now = parseInt( 100 * ++(self.params.current)/total ) + '%';
			obj.update( now );
			self.statelem.unlink();
			if( self.params.current >= total ) {
				obj.info( now + ' (completed)' );
				Wikipedia.removeCheckpoint();
			}
		}
		var onloaded = onsuccess;

		var onloading = function( self ) {}


		Wikipedia.addCheckpoint();
		if( snapshot.snapshotLength == 0 ) {
			statusIndicator.info( '100% (completed)' );
			Wikipedia.removeCheckpoint();
			return;
		}
		self.params.total = total;
		self.params.obj = statusIndicator;
		self.params.current =   0;

		for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
			var title = snapshot.snapshotItem(i).value;
			var query = {
				'title': title,
				'action': 'submit'
			}
			var wikipedia_wiki = new Wikipedia.wiki( "Unlinking on " + title, query, twinkleimagetraverse.callbacks.unlinkImageInstances );
			var params = clone( self.params );
			params.title = title;

			wikipedia_wiki.params = params;
			wikipedia_wiki.onloading = onloading;
			wikipedia_wiki.onloaded = onloaded;
			wikipedia_wiki.onsuccess = onsuccess;
			wikipedia_wiki.get();
		}
	},
	unlinkImageInstances: function( self ) {
		var image = self.params.image.replace( /^Image:/, '' );
		var form = self.responseXML.getElementById('editform');
		var text;

		if( self.params.title in twinkledeli.unlinkCache ) {
			text = twinkledeli.unlinkCache[ self.params.title ];
		} else {
			text = form.wpTextbox1.value;
		}
		var old_text = text;
		var wikiPage = new Mediawiki.Page( text );
		wikiPage.commentOutImage( image , 'Commented out because image was deleted' );

		text = wikiPage.getText();
		twinkledeli.unlinkCache[ self.params.title ] = text;
		if( text == old_text ) {
			// Nothing to do, return
			self.onsuccess( self );
			Wikipedia.actionCompleted( self );
			return;
		}
		var postData = {
			'wpMinoredit': '',
			'wpWatchthis': undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': 'Removing [[:Image:' + image + "|deleted image]]",
			'wpTextbox1': text
		};
		self.post( postData );
	}
}
// twinklebatchdelete.js
// If TwinkleConfig aint exist.
if( typeof( TwinkleConfig ) == 'undefined' ) {
	TwinkleConfig = {};
}

/**
 TwinkleConfig.summaryAd (string)
 If ad should be added or not to summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) {
	TwinkleConfig.summaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.deletionSummaryAd (string)
 If ad should be added or not to deletion summary, default [[WP:TWINKLE|TWINKLE]]
 */
if( typeof( TwinkleConfig.deletionSummaryAd ) == 'undefined' ) {
	TwinkleConfig.deletionSummaryAd = " using [[WP:TW|TW]]";
}

/**
 TwinkleConfig.batchdeleteChunks (integer)
 How many pages should be processed at a time
 */
if( typeof( TwinkleConfig.batchDeleteChunks ) == 'undefined' ) {
	TwinkleConfig.batchdeleteChunks = 50;
}

/**
 TwinkleConfig.batchMax (integer)
 How many pages should be processed maximum
 */
if( typeof( TwinkleConfig.batchMax ) == 'undefined' ) {
	TwinkleConfig.batchMax = 5000;
}

function twinklebatchdelete() {
	if( (wgNamespaceNumber != 14 && wgNamespaceNumber != 0 && wgNamespaceNumber != 2) || document.location.toString().indexOf('action=history') != -1) {
		return;
	}
	if( userIsInGroup( 'sysop' ) ) {
	  mw.util.addPortletLink( 'p-cactions', "javascript:twinklebatchdelete.callback()", "dp/b", "tw-batch", "Delete pages found in this category", "");
        }
}
$(twinklebatchdelete);

function getChecked( nobatchdeletest ) {
	if( !( nobatchdeletest instanceof NodeList ) ) {
		return nobatchdeletest.checked ? [ nobatchdeletest.value ] : [];
	}
	var result = [];
	for(var i  = 0; i < nobatchdeletest.length; ++i ) {
		if( nobatchdeletest[i].checked ) {
			result.push( nobatchdeletest[i].value );
		}
	}
	return result;
}

twinklebatchdelete.unlinkCache = {};
twinklebatchdelete.callback = function twinklesbatchdeleteCallback() {
	var Window = new SimpleWindow( 800, 400 );
	Window.setTitle( "Batch deletion" );

	var form = new QuickForm( twinklebatchdelete.callback.evaluate );
	form.append( {
			type: 'checkbox',
			list: [
				{ 
					label: 'Delete pages',
					name: 'delete_page',
					value: 'delete',
					checked: true
				},
				{
					label: 'Remove backlinks to the page',
					name: 'unlink_page',
					value: 'unlink',
					checked: false
				}
			]
		} );
	form.append( {
			type: 'textarea',
			name: 'reason',
			label: 'Reason: '
		} );
	if( wgNamespaceNumber == Namespace.CATEGORY ) {

		var query = {
			'action': 'query',
			'generator': 'categorymembers',
			'gcmtitle': wgPageName,
			'gcmlimit' : TwinkleConfig.batchMax, // the max for sysops
			'prop': [ 'categories', 'revisions' ],
			'rvprop': [ 'size' ]
		};
	} else {
		var query = {
			'action': 'query',
			'generator': 'links',
			'titles': wgPageName,
			'prop': [ 'categories', 'revisions' ],
			'rvprop': [ 'size' ],
			'gplnamespace': [ 0, 1, 4, 5, 10, 11, 12, 13, 14, 15, 100, 101 ] // only in main pand portal, plus talk, for safty 
		};
	}

	var wikipedia_api = new Wikipedia.api( 'Grabbing pages', query, function( self ) {
			var xmlDoc = self.responseXML;
			var snapshot = xmlDoc.evaluate('//page[@ns != "' + Namespace.IMAGE + '" and not(@missing)]', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
			var list = [];
			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var object = snapshot.snapshotItem(i);
				var page = xmlDoc.evaluate( '@title', object, null, XPathResult.STRING_TYPE, null ).stringValue;
				var size = xmlDoc.evaluate( 'revisions/rev/@size', object, null, XPathResult.NUMBER_TYPE, null ).numberValue;

				var disputed = xmlDoc.evaluate( 'boolean(categories/cl[@title="Category:Contested candidates for speedy deletion"])', object, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;
				list.push( {label:page + ' (' + size + ')' + ( disputed ? ' DISPUTED' : '' ), value:page, checked:true });
			}
			self.params.form.append( {
					type: 'checkbox',
					name: 'pages',
					list: list
				}
			)
			self.params.form.append( { type:'submit' } );

			var result = self.params.form.render();
			self.params.Window.setContent( result );


		}  );

	wikipedia_api.params = { form:form, Window:Window };
	wikipedia_api.post();
	var root = document.createElement( 'div' );
	Status.init( root );
	Window.setContent( root );
	Window.display();
}

twinklebatchdelete.currentDeleteCounter = 0;
twinklebatchdelete.currentUnlinkCounter = 0;
twinklebatchdelete.currentdeletor;
twinklebatchdelete.callback.evaluate = function twinklebatchdeleteCallbackEvaluate(event) {
	wgPageName = wgPageName.replace( /_/g, ' ' ); // for queen/king/whatever and country!
	var pages = getChecked( event.target.pages );
	var reason = event.target.reason.value;
	var delete_page = event.target.delete_page.checked;
	var unlink_page = event.target.unlink_page.checked;
	if( ! reason ) {
		return;
	}
	Status.init( event.target );
	function toCall( work ) {
		if( work.length == 0 ) {
			Status.info( 'work done' );
			window.clearInterval( twinklebatchdelete.currentdeletor );
			Wikipedia.removeCheckpoint();
			return;
		} else if( twinklebatchdelete.currentDeleteCounter <= 0 || twinklebatchdelete.currentUnlinkCounter <= 0 ) {
			twinklebatchdelete.currentcounter = TwinkleConfig.batchdeleteChunks;
			twinklebatchdelete.unlinkCache = []; // Clear the cache
			var pages = work.pop();
			for( var i = 0; i < pages.length; ++i ) {
				var page = pages[i];
				var query = {
					'action': 'query',
					'titles': page
				}
				var wikipedia_api = new Wikipedia.api( 'Checking if page ' + page + ' exists', query, twinklebatchdelete.callbacks.main );
				wikipedia_api.params = { page:page, reason:reason, unlink_page:unlink_page, delete_page:delete_page };
				wikipedia_api.post();
			}
		}
	}
	var work = pages.chunk( TwinkleConfig.batchdeleteChunks );
	Wikipedia.addCheckpoint();
	twinklebatchdelete.currentdeletor = window.setInterval( toCall, 1000, work );
}
twinklebatchdelete.callbacks = {
	main: function( self ) {
		var xmlDoc = self.responseXML;
		var normal = xmlDoc.evaluate( '//normalized/n/@to', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
		if( normal ) {
			self.params.page = normal;
		}
		var exists = xmlDoc.evaluate( 'boolean(//pages/page[not(@missing)])', xmlDoc, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;

		if( ! exists ) {
			self.statelem.error( "It seems that the page doesn't exists, perhaps it has already been deleted" );
			return;
		}
		if( self.params.unlink_page ) {
			var query = {
				'action': 'query',
				'list': 'backlinks',
				'blfilterredir': 'nonredirects',
				'blnamespace': [0, 100], // main space and portal space only
				'bltitle': self.params.page,
				'bllimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};
			var wikipedia_api = new Wikipedia.api( 'Grabbing backlinks', query, twinklebatchdelete.callbacks.unlinkBacklinksMain, function( self ) { 
					--twinklebatchdelete.currentUnlinkCounter;
					var link = document.createElement( 'a' );
					link.setAttribute( 'href', wgArticlePath.replace( '$1', self.query['title'] ) );
					link.setAttribute( 'title', self.query['title'] );
					link.appendChild( document.createTextNode( self.query['title'] ) );
					self.statelem.info( [ 'completed (' , link , ')' ] );

				}  );
			wikipedia_api.params = self.params;
			wikipedia_api.post();
		}
		if( self.params.delete_page ) {
			var query = {
				'action': 'query',
				'list': 'backlinks',
				'blfilterredir': 'redirects',
				'bltitle': self.params.page,
				'bllimit': userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};
			var wikipedia_api = new Wikipedia.api( 'Grabbing backlinks', query, twinklebatchdelete.callbacks.deleteRedirectsMain );
			wikipedia_api.params = self.params;
			wikipedia_api.post();

			var query = { 
				'title': self.params.page, 
				'action': 'delete'
			};
			var wikipedia_wiki = new Wikipedia.wiki( 'Deleting page ' + self.params.page, query, twinklebatchdelete.callbacks.deletePage, function( self ) { 
					--twinklebatchdelete.currentDeleteCounter;
					var link = document.createElement( 'a' );
					link.setAttribute( 'href', wgArticlePath.replace( '$1', self.query['title'] ) );
					link.setAttribute( 'title', self.query['title'] );
					link.appendChild( document.createTextNode( self.query['title'] ) );
					self.statelem.info( [ 'completed (' , link , ')' ] );

				} );
			wikipedia_wiki.params = self.params;
			wikipedia_wiki.followRedirect = false;
			wikipedia_wiki.get();		
		}
	},
	deleteRedirectsMain: function( self ) {
		var xmlDoc = self.responseXML;
		var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

		var total = snapshot.snapshotLength * 2;

		if( snapshot.snapshotLength == 0 ) {
			return;
		}

		var statusIndicator = new Status('Deleting redirects', '0%');

		var onsuccess = function( self ) {
			var obj = self.params.obj;
			var total = self.params.total;
			var now = parseInt( 100 * ++(self.params.current)/total ) + '%';
			obj.update( now );
			self.statelem.unlink();
			if( self.params.current >= total ) {
				obj.info( now + ' (completed)' );
				Wikipedia.removeCheckpoint();
			}
		}
		var onloaded = onsuccess;

		var onloading = function( self ) {}


		Wikipedia.addCheckpoint();
		if( snapshot.snapshotLength == 0 ) {
			statusIndicator.info( '100% (completed)' );
			Wikipedia.removeCheckpoint();
			return;
		}

		var params = clone( self.params );
		params.current = 0;
		params.total = total;
		params.obj = statusIndicator;


		for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
			var title = snapshot.snapshotItem(i).value;
			var query = {
				'title': title,
				'action': 'delete'
			}
			var wikipedia_wiki = new Wikipedia.wiki( "Deleting " + title, query, twinklebatchdelete.callbacks.deleteRedirects );
			wikipedia_wiki.params = params;
			wikipedia_wiki.onloading = onloading;
			wikipedia_wiki.onloaded = onloaded;
			wikipedia_wiki.onsuccess = onsuccess;
			wikipedia_wiki.followRedirect = false;
			wikipedia_wiki.get();
		}
	},
	deleteRedirects: function( self ) {
		var form = this.responseXML.getElementById( 'deleteconfirm' );
		if( ! form ) { // Hell, image deletion is b0rked :(
			form = this.responseXML.getElementsByTagName( 'form' )[0];
			var postData = {
				'wpReason': "[[WP:CSD#R1|CSD R1]]: Redirect to nonexistent page",
				'wpEditToken': form.wpEditToken.value
			}
		} else {

			var postData = {
				'wpWatch': form.wpWatch.checked ? '' : undefined,
				'wpReason': "[[WP:CSD#R1|CSD R1]]: Redirect to nonexistent page",
				'wpEditToken': form.wpEditToken.value
			}
		}
		self.post( postData );
	},
	deletePage: function( self ) {
		var form = this.responseXML.getElementById( 'deleteconfirm' );
		var postData = {
			'wpWatch': form.wpWatch.checked ? '' : undefined,
			'wpReason': self.params.reason,
			'wpEditToken': form.wpEditToken.value
		}
		self.post( postData );
	},
	unlinkBacklinksMain: function( self ) {
		var xmlDoc = self.responseXML;
		var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

		if( snapshot.snapshotLength == 0 ) {
			return;
		}

		var statusIndicator = new Status('Unlinking backlinks', '0%');

		var total = snapshot.snapshotLength * 2;

		var onsuccess = function( self ) {
			var obj = self.params.obj;
			var total = self.params.total;
			var now = parseInt( 100 * ++(self.params.current)/total ) + '%';
			obj.update( now );
			self.statelem.unlink();
			if( self.params.current >= total ) {
				obj.info( now + ' (completed)' );
				Wikipedia.removeCheckpoint();
			}
		}
		var onloaded = onsuccess;

		var onloading = function( self ) {}


		Wikipedia.addCheckpoint();
		if( snapshot.snapshotLength == 0 ) {
			statusIndicator.info( '100% (completed)' );
			Wikipedia.removeCheckpoint();
			return;
		}
		self.params.total = total;
		self.params.obj = statusIndicator;
		self.params.current =   0;

		for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
			var title = snapshot.snapshotItem(i).value;
			var query = {
				'title': title,
				'action': 'submit'
			}
			var wikipedia_wiki = new Wikipedia.wiki( "Unlinking on " + title, query, twinklebatchdelete.callbacks.unlinkBacklinks );
			var params = clone( self.params );
			params.title = title;

			wikipedia_wiki.params = params;
			wikipedia_wiki.onloading = onloading;
			wikipedia_wiki.onloaded = onloaded;
			wikipedia_wiki.onsuccess = onsuccess;
			wikipedia_wiki.get();
		}
	},
	unlinkBacklinks: function( self ) {
		var form = self.responseXML.getElementById('editform');
		if( ! form ) {
			// we probably just deleted it, as a recursive backlink
			self.onsuccess( self );
			Wikipedia.actionCompleted( self );
			return;
		}
		var text;

		if( self.params.title in twinklebatchdelete.unlinkCache ) {
			text = twinklebatchdelete.unlinkCache[ self.params.title ];
		} else {
			text = form.wpTextbox1.value;
		}
		var old_text = text;
		var wikiPage = new Mediawiki.Page( text );
		wikiPage.removeLink( self.params.page );

		text = wikiPage.getText();
		twinklebatchdelete.unlinkCache[ self.params.title ] = text;
		if( text == old_text ) {
			// Nothing to do, return
			self.onsuccess( self );
			Wikipedia.actionCompleted( self );
			return;
		}
		var postData = {
			'wpMinoredit': form.wpMinoredit.checked ? '' : undefined,
			'wpWatchthis': undefined,
			'wpStarttime': form.wpStarttime.value,
			'wpEdittime': form.wpEdittime.value,
			'wpAutoSummary': form.wpAutoSummary.value,
			'wpEditToken': form.wpEditToken.value, 'wpSection': '',
			'wpSummary': 'Removing backlinks to page ' + self.params.page,
			'wpTextbox1': text
		};
		self.post( postData );
	}
}