Jump to content

User:EpochFail/No Biting.js

From Wikipedia, the free encyclopedia
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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.
/**
 * XMLHttpRequest
 *
 * A compressed XMLHttpRequest object that works for anything from IE6 to FF to webkit
 */
(function(){var d=window.XMLHttpRequest;var h=!!window.controllers,e=window.document.all&&!window.opera;function c(){this._object=d?new d:new window.ActiveXObject("Microsoft.XMLHTTP");this._listeners=[]}if(h&&d.wrapped){c.wrapped=d.wrapped}c.UNSENT=0;c.OPENED=1;c.HEADERS_RECEIVED=2;c.LOADING=3;c.DONE=4;c.prototype.readyState=c.UNSENT;c.prototype.responseText="";c.prototype.responseXML=null;c.prototype.status=0;c.prototype.statusText="";c.prototype.onreadystatechange=null;c.onreadystatechange=null;c.onopen=null;c.onsend=null;c.onabort=null;c.prototype.open=function(l,o,k,p,j){if(arguments.length<3){k=true}this._async=k;var n=this,m=this.readyState;if(e){var i=function(){if(n._object.readyState!=c.DONE){a(n);n.abort()}};if(k){window.attachEvent("onunload",i)}}this._object.onreadystatechange=function(){if(h&&!k){return}n.readyState=n._object.readyState;g(n);if(n._aborted){n.readyState=c.UNSENT;return}if(n.readyState==c.DONE){a(n);if(e&&k){window.detachEvent("onunload",i)}}if(m!=n.readyState){f(n)}m=n.readyState};if(c.onopen){c.onopen.apply(this,arguments)}if(arguments.length>4){this._object.open(l,o,k,p,j)}else{if(arguments.length>3){this._object.open(l,o,k,p)}else{this._object.open(l,o,k)}}if(!k&&h){this.readyState=c.OPENED;f(this)}};c.prototype.send=function(i){if(c.onsend){c.onsend.apply(this,arguments)}if(i&&i.nodeType){i=window.XMLSerializer?new window.XMLSerializer().serializeToString(i):i.xml;if(!this._headers["Content-Type"]){this._object.setRequestHeader("Content-Type","application/xml")}}this._object.send(i);if(h&&!this._async){this.readyState=c.OPENED;g(this);while(this.readyState<c.DONE){this.readyState++;f(this);if(this._aborted){return}}}};c.prototype.abort=function(){if(c.onabort){c.onabort.apply(this,arguments)}if(this.readyState>c.UNSENT){this._aborted=true}this._object.abort();a(this)};c.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};c.prototype.getResponseHeader=function(i){return this._object.getResponseHeader(i)};c.prototype.setRequestHeader=function(i,j){if(!this._headers){this._headers={}}this._headers[i]=j;return this._object.setRequestHeader(i,j)};c.prototype.addEventListener=function(l,k,j){for(var i=0,m;m=this._listeners[i];i++){if(m[0]==l&&m[1]==k&&m[2]==j){return}}this._listeners.push([l,k,j])};c.prototype.removeEventListener=function(l,k,j){for(var i=0,m;m=this._listeners[i];i++){if(m[0]==l&&m[1]==k&&m[2]==j){break}}if(m){this._listeners.splice(i,1)}};c.prototype.dispatchEvent=function(j){var j={type:j.type,target:this,currentTarget:this,eventPhase:2,bubbles:j.bubbles,cancelable:j.cancelable,timeStamp:j.timeStamp,stopPropagation:function(){},preventDefault:function(){},initEvent:function(){}};if(j.type=="readystatechange"&&this.onreadystatechange){(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[j])}for(var i=0,k;k=this._listeners[i];i++){if(k[0]==j.type&&!k[2]){(k[1].handleEvent||k[1]).apply(this,[j])}}};c.prototype.toString=function(){return"[object XMLHttpRequest]"};c.toString=function(){return"[XMLHttpRequest]"};function f(i){if(c.onreadystatechange){c.onreadystatechange.apply(i)}i.dispatchEvent({type:"readystatechange",bubbles:false,cancelable:false,timeStamp:new Date+0})}function b(j){var i=j.responseXML;if(e&&i&&!i.documentElement&&j.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)){i=new window.ActiveXObject("Microsoft.XMLDOM");i.loadXML(j.responseText)}if(i){if((e&&i.parseError!=0)||(i.documentElement&&i.documentElement.tagName=="parsererror")){return null}}return i}function g(i){try{i.responseText=i._object.responseText}catch(j){}try{i.responseXML=b(i._object)}catch(j){}try{i.status=i._object.status}catch(j){}try{i.statusText=i._object.statusText}catch(j){}}function a(i){i._object.onreadystatechange=new window.Function;delete i._headers}if(!window.Function.prototype.apply){window.Function.prototype.apply=function(i,j){if(!j){j=[]}i.__func=this;i.__func(j[0],j[1],j[2],j[3],j[4]);delete i.__func}}window.HttpRequest=c})();


/**
 * The NICE interface modification for (monobook) Wikipedia
 *
 * This file contains the entire javascript used to produce the No Biting 
 * interface modification.
 */


ACCOUNT_NAME = "User:EpochFail"

/**
 * Query String
 * 
 * Maps the querystring to an object
 */
mapQueryString = function(qString) {
	var string = qString
	var params = {}
	
	if(!qString.length){
		return params
	}
	
	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])
		}
		
		params[key] = value
	}
	
	return params
}



var params = mapQueryString(location.search.substring(1, location.search.length))
if(params.undo && !NICE){
	var NICE = new Object()
	
	NICE.setup_complete = false
	NICE.warn = false
	NICE.box  = false

	NICE.updateTimeout = 100
	NICE.noobLimit = 100
	NICE.requestTimeout = 10000
	
	NICE.reverted = new Object()
	NICE.reverted.anon         = null
	NICE.reverted.revisionId   = params.undo
	NICE.reverted.username     = null
	NICE.reverted.userRevs     = null
	NICE.reverted.timestamp    = null
	
	
	mw.loader.load("http://wikipedia.grouplens.org/NICE/gadget/interface_setup.js?username=" + mw.config.get('wgUserName'))
	
	/**
	 * Trim
	 *
	 * Cuts whitespace from both sides of a string.
	 */
	String.prototype.trim = function() {
		return this.replace(/^\s+|\s+$/g,"")
	}
	
	/**
	 * Import CSS
	 *
	 * Adds a link to a new CSS file to the header of the document.
	 */
	function importCSS(page) {
		var url = mw.config.get('wgScript') + '?title=' +
			encodeURIComponent(page.replace(/ /g,'_')).replace('%2F','/').replace('%3A',':') +
			'&action=raw&ctype=text/css'
		
		var cssLink = document.createElement("link")
		cssLink.rel = "stylesheet"
		cssLink.type = "text/css"
		cssLink.href = url
		document.getElementsByTagName("head")[0].appendChild(cssLink)
		return cssLink
	}
	
	
	NICE.addEventListener = function(element, event, func){
		if(element.addEventListener){
			element.addEventListener(event, func, false)
		}
		else if(element.attachEvent){
			element.attachEvent("on" + event, func)
		}
	}
	
	NICE.createCookie = function(name,value,days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	}
	
	NICE.killEvent = function(e){
		e.cancelBubble = true
		e.returnValue = false
		if (e.stopPropagation){
			e.stopPropagation()
			e.preventDefault()
		}
	}
	
	NICE.readCookie = function(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	}
	
	NICE.eraseCookie = function(name) {
		createCookie(name,"",-1);
	}
	
	NICE.notice = new Object()
	NICE.notice.container = document.createElement("div")
	NICE.notice.container.style.border="1px solid #ccc"
	NICE.notice.container.style.padding="7px"
	NICE.notice.container.innerHTML = '' +
		'<a href="/wiki/File:Imbox_notice.png" class="image" title="Notice!" style="float:left;"><img alt="" src="/media/wikipedia/en/3/31/Imbox_notice.png" width="40" height="40" border="0" /></a>' + 
		'You have installed the ' +
		'<a href="http://wikipedia.grouplens.org/NICE">NICE</a> gadget, ' +
		'a tool ' +
		'designed by the <a href="http://grouplens.org">Grouplens</a> research lab.  Your ' +
		'use of this tool is being recorded for research and evaluation ' + 
		'purposes.  For more information, see our ' +
		'<a href="http://wikipedia.grouplens.org/NICE/consent/">consent form</a>.  ' + 
		'If you have any questions/comments/suggestions, please contact ' +
		'<a href="http://en.wikipedia.org/wiki/User:EpochFail">EpochFail</a>.' + 
		'<br style="clear:both;"/>'
	/*NICE.notice.hideLink = document.createElement("div")
	NICE.notice.hideLink.style.textAlign="right"
	NICE.notice.hideLink.style.color="blue"
	NICE.notice.hideLink.style.cursor="pointer"
	NICE.notice.hideLink.style.textDecoration="underline"
	NICE.notice.hideLink.style.clear="both"
	NICE.notice.hideLink.innerHTML = "hide this message"
	NICE.notice.hide = function(e){
		NICE.notice.container.style.display="None"
		NICE.createCookie(mw.config.get('wgUserName') + "_no_notice", "please", 30)
	}
	NICE.addEventListener(NICE.notice.hideLink, "click", NICE.notice.hide)
	NICE.notice.container.appendChild(NICE.notice.hideLink)*/
	
	NICE.showNoticeWhenReady = function(){
		if(document.getElementById("bodyContent")){
			document.getElementById("bodyContent").insertBefore(NICE.notice.container, document.getElementById("contentSub"))
		}
		else{
			setTimeout('NICE.showNoticeWhenReady()', NICE.updateTimeout)
		}
	}
	
	
	/**
	 * Save and Post Message
	 *
	 * This is an event handler called when the user wants to save their revert.
	 */
	NICE.saveAndPostMessage = function(e){
		var e = e || window.event
		
		var targetId = '???'
		if(e.explicitOriginalTarget){
			targetId = e.explicitOriginalTarget.id
		}else if(document.activeElement){
			targetId = document.activeElement.id
		}
		
		if(targetId == "wpPreview" || targetId == "wpDiff"){
			return
		}
		
		//Stop the submitting process
		NICE.killEvent(e)
		
		if(NICE.saveForm.messageBoxDisplayed && document.getElementById("niceExplaination").value.trim().length > 0){
			if(document.getElementById("niceExplaination").value.match("~~" + "~~")){
				var sign = ''
			}else{
				var sign = ' ~~' + '~~'
			}
			NICE.postMessageOnUserTalk(
				NICE.reverted.username,
				document.getElementById("niceHeader").value,
				document.getElementById("niceExplaination").value + sign
			)
			
		}else{
			NICE.logger.revert(
				NICE.warn, 
				NICE.box, 
				NICE.reverted.username, 
				NICE.reverted.userRevs, 
				wgUserName, 
				NICE.reverted.revisionId,
				null,
				null
			)
			
			setTimeout('document.getElementById("wpSummary").value += " (using [[" + ACCOUNT_NAME + "/NICE|NICE]])";document.getElementById("editform").submit()', 1000)
		}
	}
	
	/**
	 * Create Save Form When Ready
	 *
	 * A simple timeout function that creates the SaveForm object as soon as the 
	 * interface is ready.
	 */
	NICE.createSaveFormWhenReady = function(){
		if(document.getElementById("editform") && NICE.setup_complete){
			var editForm = document.getElementById("editform")
			var editOptions = null
			var formList = editForm.getElementsByTagName("div")
			for(i in formList){
				if(formList[i].className == "editOptions"){
					editOptions = formList[i]
				}
			}
			NICE.saveForm = new NICE.SaveForm(editOptions)
			NICE.updateSaveFormWhenReady()
			NICE.showWarningWhenReady()
			NICE.logUndoWhenReady()
		}	
		else{
			setTimeout('NICE.createSaveFormWhenReady()', NICE.updateTimeout)
		}
	}
	
	/**
	 * Update Save Form When Ready
	 *
	 * A simple timeout function that updates the SaveForm with information as soon
	 * as it is ready to be updated.
	 */
	NICE.updateSaveFormWhenReady = function(){
		if(
			NICE.reverted.username != null && 
			NICE.reverted.revisionId != null
		){
			if(NICE.box){
				NICE.addEventListener(document.getElementById('editform'), "submit", NICE.saveAndPostMessage)
				NICE.saveForm.initMessageBox(NICE.reverted.revisionId, NICE.reverted.username)
				var boxPref = NICE.readCookie("NICE_boxPref")
				if(boxPref != "hide"){
					NICE.saveForm.showMessageBox()
				}else{
					NICE.saveForm.hideMessageBox()
				}
			}
		}
		else{
			setTimeout('NICE.updateSaveFormWhenReady()', NICE.updateTimeout)
		}
	}
	
	/**
	 * Show warning when ready
	 *
	 *
	 */
	NICE.showWarningWhenReady = function(){
		if(
			NICE.reverted.userRevs != null
		){
			if(NICE.warn && NICE.reverted.userRevs <= NICE.noobLimit && !NICE.reverted.anon){
				NICE.saveForm.showWarning()
			}
		}
		else{
			setTimeout('NICE.showWarningWhenReady()', NICE.updateTimeout)
		}
	}
	
	/**
	 * Show log undo when ready
	 *
	 *
	 */
	NICE.logUndoWhenReady = function(){
		if(
			NICE.reverted.username != null && 
			NICE.reverted.revisionId != null && 
			(NICE.reverted.userRevs != null || NICE.reverted.anon)
		){
			NICE.logger.undo(
				NICE.warn, 
				NICE.box, 
				NICE.reverted.username, 
				NICE.reverted.userRevs, 
				wgUserName, 
				NICE.reverted.revisionId
			)
		}
		else{
			setTimeout('NICE.logUndoWhenReady()', NICE.updateTimeout)
		}
	}
	
	
	/**
	 * Show Error When Ready
	 *
	 * A simple timeout function that waits to shows an error as soon as the
	 * interface is ready.
	 *
	 */
	NICE.showErrorWhenReady = function(message){
		if(NICE.saveForm){
			NICE.saveForm.showError(message)
		}
		else{
			setTimeout('NICE.showErrorWhenReady("' + message + '")', NICE.updateTimeout)
		}
	}
	
	
	/**
	 * Wikipedia API Handler
	 *
	 * A simple object for interacting with Wikipedia's API.
	 */
	NICE.WPAPIHandler = function(){
	}
	
	NICE.WPAPIHandler.requests = new Array();
	NICE.WPAPIHandler.timeoutSeconds = NICE.requestTimeout;
	NICE.WPAPIHandler.url = mw.config.get('wgServer') + mw.config.get('wgScriptPath') + "/api.php";
		
		/**
		 * Timeout
		 *
		 * Stops a long running request and calls its error message
		 */
		NICE.WPAPIHandler.timeout = function(requestId){
			var request = this.requests[requestId];
			if(request){
				request.errorFun("Request to Wikipedia database timed out.");
				try{
					request.abort();
				}catch(e){
					//then don't abort I guess 
				}
			}
		}
		
		
		/**
		 * Perform Request
		 *
		 * Uses a callback function to perform a request to the WP api.
		 */
		NICE.WPAPIHandler.performGET = function(params, successFun, args, errorFun){
			this.performRequest(params, successFun, args, errorFun, "GET")
		}
		
		/**
		 * Perform Request
		 *
		 * Uses a callback function to perform a request to the WP api.
		 */
		NICE.WPAPIHandler.performPOST = function(params, successFun, args, errorFun){
			this.performRequest(params, successFun, args, errorFun, "POST")
		}
		
		
		NICE.WPAPIHandler.performRequest = function(params, successFun, args, errorFun, type){
			var paramString = "foo=foo"
			for(key in params){
				paramString += "&" + key + "=" + this.escape(params[key])
			}
			
			var request = new HttpRequest
			if(!request){
				errorFun("Your browser does not support the ability to make requests to the Wikipedia database.")
				return
			}
			
			if(type=="POST"){
				request.open("POST", this.url, true)
				request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
				request.setRequestHeader("Content-length", paramString.length)
			}
			else{
				request.open("GET", this.url+"?"+paramString, true)
			}
			request.setRequestHeader("Pragma", "cache=yes")
			request.setRequestHeader("Cache-Control", "no-transform")
			request.setRequestHeader("Connection", "close")
			request.args = args
			request.successFun = successFun
			request.errorFun = errorFun
			request.onreadystatechange = function() {
				if (this.readyState != 4){
					return
				}
				if (this.status != 200){
					errorFun("The Wikipedia server responded with an error: " + this.status + " " + this.statusText + ": " + this.responseText)
					return
				}
				clearTimeout(this.timeoutRef)
				
				successFun(this.responseText, args)
				return
			}
			request.timeoutRef = setTimeout("NICE.WPAPIHandler.timeout(" + currentRequestId + ")", this.timeoutSeconds)
			var currentRequestId = this.requests.length
			this.requests[currentRequestId] = request
			
			if(type=="POST"){
				request.send(paramString)
			}
			else{
				request.send(null)
			}
			
		}
		
		/**
		 * Escape
		 *
		 * Performs a normal URL escape, but also escapes the "+" symbol.  This is 
		 * essential for generating edit tokens.
		 */
		NICE.WPAPIHandler.escape = function(string){
			return escape(string).replace(/\+/, "%2B")
		}
	
	
	
	/**
	 * Save Form
	 * 
	 * This object represents a DOM widget in the HTML document
	 *
	 */
	NICE.SaveForm = function(editOptions){
		this.preview = false
		this.diff = false
		this.messageBoxDisplayed = false
		this.editOptions = editOptions
		
		this.errorMessage = document.createElement("div")
		var errorHeader = document.createElement("h2")
		errorHeader.innerHTML = "The NO BITING interface failed to load."
		this.errorBody = document.createElement("div")
		this.errorMessage.appendChild(errorHeader)
		this.errorMessage.appendChild(this.errorBody)
		this.errorMessage.id = "errorMessage"
			
		this.warnDiv = document.createElement("div")
		this.warnDiv.id = "warning"
		this.warnDiv.innerHTML =  '' + 
		'<div class="h2">\n' + 
		'	<span style="font-weight: bold;">Note:</span> You are reverting a new editor.  <a href="http://en.wikipedia.org/wiki/WP:BITE">Don\'t bite.</a>\n' + 
		'</div class="h2">\n' + 
		'<p class="analysis">\n' + 
		'	Analysis suggests that reverting this editor is likely to \n' + 
		'	discourage them from making future contributions.\n' + 
		'</p>\n';
		
		this.saveOptions = document.createElement("div");
		this.saveOptions.id = "saveOptions";
		
		this.normalSelector = document.createElement("a");
		this.normalSelector.innerHTML = "Normal";
		this.normalSelector.id = "normalSelector";
		this.normalSelector.className="selected";
		this.normalSelector.onclick = function(a){
			NICE.saveForm.hideMessageBox()
			NICE.createCookie("NICE_boxPref", "hide", 30)
		}
		this.saveOptions.appendChild(this.normalSelector);
		
		this.niceSelector = document.createElement("a");
		this.niceSelector.innerHTML = "Be Very Nice";
		this.niceSelector.id = "niceSelector";
		this.niceSelector.className="";
		this.niceSelector.onclick = function(a){
			NICE.saveForm.showMessageBox()
			NICE.createCookie("NICE_boxPref", "show", 30)
		}
		this.saveOptions.appendChild(this.niceSelector);
		
		this.oldForm = document.createElement("div");
		this.oldForm.id="oldForm";
		
		this.messageForm = document.createElement("div");
		this.messageForm.id = "messageForm";
		this.messageForm.style.display="none";
		this.messageForm.style.width="48%";
		
		this.formMessage = document.createElement("p");
		this.formMessage.style.display="none";
		
		this.finalBreak = document.createElement("br");
		this.finalBreak.style.clear="both";
	}
	
		/**
		 * Show Error
		 *
		 * Will show an error to the user in the DOM.  If there is already an
		 * error displayed, this error will over-write it.
		 */
		NICE.SaveForm.prototype.showError = function(message){
			this.errorBody.innerHTML = message;
			if(!this.errorMessage.parent){
				//Load style
				importCSS(ACCOUNT_NAME + '/error_message.css')
				
				if(this.saveOptions.parentNode){
					this.editOptions.parentNode.insertBefore(this.errorMessage, this.saveOptions)
				}
				else{
					this.editOptions.parentNode.insertBefore(this.errorMessage, this.editOptions)
				}
			}
		}
		
		/**
		 * Show Warning
		 *
		 * Calling this method displayes the new user warning to the user.
		 */
		NICE.SaveForm.prototype.showWarning = function(){
			//Load style
			importCSS(ACCOUNT_NAME + '/warn.css')
			
			this.copyWarn = document.getElementById("editpage-copywarn")
			this.copyWarn.style.display = "none"
			
			if(this.errorMessage.parentNode){
				this.errorMessage.parentNode.insertBefore(this.warnDiv, this.errorMessage)
			}
			else if(this.saveOptions.parentNode){
				this.saveOptions.parentNode.insertBefore(this.warnDiv, this.saveOptions)
			}
			else{
				this.editOptions.parentNode.insertBefore(this.warnDiv, this.editOptions)
			}
		}
		
		/**
		 * Init Message Box
		 *
		 * This method modifies the save form for saving changes to a revert
		 * to include the form elements for adding information to a talk page.
		 */
		NICE.SaveForm.prototype.initMessageBox = function(revertedId, revertedUser){
			//Load style
			importCSS(ACCOUNT_NAME + '/message_box.css');
			this.editOptions.parentNode.insertBefore(this.saveOptions, this.editOptions)
			
			this.oldForm.innerHTML = this.editOptions.innerHTML
			this.editOptions.innerHTML = ""
			this.editOptions.appendChild(this.oldForm)
			
			this.formMessage.innerHTML= '' + 
				'The form below will allow you to both save your changes to ' + 
				'<a href="' + mw.config.get('wgServer') + '/wiki/' + mw.config.get('wgPageName') + '">' + mw.config.get('wgPageName') + '</a> and post a brief message to  ' + 
				'<a href="' + mw.config.get('wgServer') + '/wiki/User:' + revertedUser + '">' + revertedUser + '</a>\'s talk page explaining the revert. ' + 
				'This is your opportunity to help a new user learn the ropes.'
			this.editOptions.appendChild(this.formMessage);
			
			this.messageForm.innerHTML = '' + 
			'<span id="niceHeaderLabel">' + 
			'	<label for="niceHeader">' + 
			'		<span style="text-align: left;">' + 
			'			Message header' + 
			'			<small>(This will be posted on <a href="' + mw.config.get('wgServer') + '/wiki/User:' + revertedUser + '">' + revertedUser + '</a>\'s talk page)</small>' + 
			'		</span>' + 
			'	</label>' + 
			'</span>' + 
			'<br />' + 
			'<input name="niceHeader" style="width: 90%;" value="Reverted your revision {{diff2|'+revertedId+'}} to [[' + mw.config.get('wgPageName') + ']]" id="niceHeader" maxlength="200" tabindex="1">' + 
			'<br />' + 
			'<span id="niceExplainationLabel">' + 
			'	<label for="niceExplaination">' + 
			'		<span style="text-align: left;">' + 
			'			Explanation' + 
			'			<small>(Consider explaining the appropriate <a href="' + mw.config.get('wgServer') + '/wiki/Wikipedia_policies">Wikipedia Policy</a>)</small>' + 
			'		</span>' + 
			'	</label>' + 
			'</span>' +  
			'<textarea name="niceExplaination" id="niceExplaination" style="width: 90%;" rows="4"></textarea>'
			
			this.editOptions.appendChild(this.messageForm)
			this.editOptions.appendChild(this.oldForm)
			this.editOptions.appendChild(this.finalBreak)
			this.editOptions.appendChild(document.createTextNode(' '))
			document.getElementById("wpSummary").style.width="100%"
		}
		
		/**
		 * Show Message Box
		 *
		 * Displays the message box form to the user.
		 */
		NICE.SaveForm.prototype.showMessageBox = function(){
			if(this.messageBoxDisplayed){
				return true
			}
			
			this.normalSelector.className   = ""
			this.niceSelector.className     = "selected"
			this.oldForm.style.width        = "48%"
			this.messageForm.style.display  = "block"
			this.formMessage.style.display  = "block"
			this.messageBoxDisplayed        = true
		}
		
		/**
		 * Hide Message Box
		 *
		 * Hides the message box form from the user.
		 */
		NICE.SaveForm.prototype.hideMessageBox = function(){
			if(!this.messageBoxDisplayed){
				//return true;
			}
			
			this.normalSelector.className      = "selected"
			this.niceSelector.className        = ""
			this.oldForm.style.width           = "96%"
			this.messageForm.style.display     = "none"
			this.formMessage.style.display     = "none"
			this.messageBoxDisplayed           = false
		}
	
	/**
	 * Logger
	 * 
	 * This object is intended to be used as a simple interface for logging that
	 * uses a the insertion of a Javascript tag to the header of the document in
	 * order to make arbitrary GET requests to any server.
	 */
	NICE.Logger = function(url){
		this.url = url;
	}
		
		/**
		 * Revert
		 *
		 * Logs the event that an editor successfully reverted a revision.
		 */
		NICE.Logger.prototype.revert = function(
				warn, 
				box, 
				revertedUser, 
				revertedUserRevs, 
				revertingUser, 
				revisionReverted, 
				newTalkRevision, 
				prevTalkRevision
			){
			this.log(
				{
					"action":             "revert",
					"timestamp":          NICE.reverted.timestamp,
					"warn":               warn,
					"box":                box,
					"user_reverted":      revertedUser.substring(0,200),
					"user_reverted_revs": revertedUserRevs,
					"user_reverting":     revertingUser.substring(0,200),
					"revision_reverted":  revisionReverted,
					"new_talk_revision":  newTalkRevision,
					"prev_talk_revision": prevTalkRevision
				}
			)
		}
		
		/**
		 * Undo
		 *
		 * Logs the event that an editor successfully loaded the undo page. 
		 */
		NICE.Logger.prototype.undo = function(
			warn, 
			box, 
			userToBeReverted, 
			userRevs, 
			userReverting, 
			revisionToBeReverted){
		
			this.log(
				{
					"action":                   "undo",
					"warn":                     warn,
					"box":                      box,
					"user_to_be_reverted":      userToBeReverted.substring(0,200),
					"user_to_be_reverted_revs": userRevs,
					"user_reverting":           userReverting.substring(0,200),
					"revision_to_be_reverted":  revisionToBeReverted
				}
			)
		}
		
		/**
		 * Error
		 *
		 * Logs errors that a user encounters while interacting with the interface.
		 */
		NICE.Logger.prototype.error = function(revertingUser, revisionToBeReverted, operation, message){
			this.log(
				{
					"action":                   "error",
					"timestamp":                NICE.reverted.timestamp,
					"user_reverting":           revertingUser.substring(0,200),
					"revision_to_be_reverted":  revisionToBeReverted,
					"operation":                operation,
					"message":                  message.substring(0,200)
				}
			)
		}
		
		/**
		 * Log
		 *
		 * A function which formats an object into GET parameters and adds a 
		 * script tag to the head of the document.
		 */
		NICE.Logger.prototype.log = function(obj){
			if(obj.action){
				var queryString = "?logging=logging"
				for(key in obj){
					queryString += "&" + key + "=" + this.escape(obj[key])
				}
				
				//alert("logging: " + queryString);
				
				this.sendRequest(queryString);
			}
		}
		
		/**
		 * Send Request
		 *
		 * Sends a get request to the provided url the query string by adding a 
		 * script tag to the head of a document.
		 */
		NICE.Logger.prototype.sendRequest = function(queryString){
			var tag = document.createElement("script")
			tag.setAttribute("src", this.url + queryString)
			tag.setAttribute('type','text/javascript')
			document.getElementsByTagName('head')[0].appendChild(tag)
		}
		
		/**
		 * Escape
		 *
		 * Properly escapes URL parameters by changing javascript's "null" to a
		 * more useful "/n" sequence.
		 */
		NICE.Logger.prototype.escape = function(thing){
			if(thing == null){
				return escape("/n")
			}
			else{
				return escape(thing)
			}
		}
	
	NICE.logger = new NICE.Logger("http://www-users.cs.umn.edu/~halfak/wpInterfaceLogger.php")
	
	/**
	 * Post Message On User Talk
	 *
	 * Retrieves an edittoken for the user's talk page and starets the sequence of
	 * calls to add a message to the page. 
	 */
	NICE.postMessageOnUserTalk = function(username, header, message){
		var params = {
			"action": "query",
			"prop": "info",
			"intoken": "edit",
			"titles": 'User_talk:' + username,
			"format": "json"
		}
		
		var args = {
			"username": username,
			"header": header,
			"message": /*'<div style="font-size: .7em">This message was added using the [[' + ACCOUNT_NAME + '/NICE | NICE]] gadget.</div>' +*/ message
		}
		
		/* This function will be called in window scope */
		var finishPostingAndLogging = function(result, args){
			try{
				var res = eval("(" + result + ")")
			}
			catch(e){
				NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Searching for edit token for user talk page", "Unable to process JSON result: " + result)
				if(confirm('Unable to post to ' + NICE.reverted.username + '\'s talk page.  Continue with revert?')){
					NICE.logger.revert(
						NICE.warn, 
						NICE.box, 
						NICE.reverted.username, 
						NICE.reverted.userRevs, 
						wgUserName, 
						NICE.reverted.revisionId,
						null,
						null
					)
					setTimeout('document.getElementById("wpSummary").value += " (using [[" + ACCOUNT_NAME + "/NICE|NICE]])";document.getElementById("editform").submit()', 1000)
				}
				return
			}
			
			try{
				for(thing in res.query.pages){
					var page = res.query.pages[thing]
				}
				var editToken = page.edittoken
			}
			catch(e){
				NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Looking for edit token in result", "Invalid result structure: " + result)
				if(confirm('Unable to post to ' + NICE.reverted.username + '\'s talk page.  Continue with revert?')){
					NICE.logger.revert(
						NICE.warn, 
						NICE.box, 
						NICE.reverted.username, 
						NICE.reverted.userRevs, 
						wgUserName, 
						NICE.reverted.revisionId,
						null,
						null
					)
					setTimeout('document.getElementById("wpSummary").value += " (using [[" + ACCOUNT_NAME + "/NICE|NICE]])";document.getElementById("editform").submit()', 1000)
				}
				return
			}
			
			
			var params = {
				"action":  'edit',
				"title":   'User_talk:' + args.username,
				"summary": args.header + " (using [[User:EpochFail/NICE|NICE]])",
				"appendtext":  "\n\n==" + args.header + "==\n\n" + args.message,
				"token":   editToken,
				"format":  'json'
			}
			
			NICE.WPAPIHandler.performPOST(
				params, 
				function(result, args){
					try{
						var res = eval("(" + result + ")")
					}catch(e){
						NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Posting to user's talk page", "Unable to process JSON result: " + result)
						if(confirm('Unable to post to ' + NICE.reverted.username + '\'s talk page.  Continue with revert?')){
							NICE.logger.revert(
								NICE.warn, 
								NICE.box, 
								NICE.reverted.username, 
								NICE.reverted.userRevs, 
								wgUserName, 
								NICE.reverted.revisionId,
								null,
								null
							)
							setTimeout('document.getElementById("wpSummary").value += " (using [[" + ACCOUNT_NAME + "/NICE|NICE]])";document.getElementById("editform").submit()', 1000)
						}
						return
					}
					try{
						var newRevId = null
						var oldRevId = null
						newRevId = res.edit.newrevid,
						oldRevId = res.edit.oldrevid
					}catch(e){
						NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Logging a successful revert", "Invalid result structure: " + result)
						//do not return here.  If we don't have the newRevId and oldRevId, we can still log the revert.
					}
					
					NICE.logger.revert(
						NICE.warn, 
						NICE.box, 
						NICE.reverted.username, 
						NICE.reverted.userRevs, 
						wgUserName, 
						NICE.reverted.revisionId,
						newRevId,
						oldRevId
					)
					
					setTimeout('document.getElementById("wpSummary").value += " (using [[" + ACCOUNT_NAME + "/NICE|NICE]])";document.getElementById("editform").submit()', 1000)
				}, 
				{},
				function(message){
					NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Posting on user's talk page", message)
					if(confirm('Unable to post to ' + NICE.reverted.username + '\'s talk page.  Continue with revert?')){
						NICE.logger.revert(
							NICE.warn, 
							NICE.box, 
							NICE.reverted.username, 
							NICE.reverted.userRevs, 
							wgUserName, 
							NICE.reverted.revisionId,
							null,
							null
						)
						setTimeout('document.getElementById("wpSummary").value += " (using [[" + ACCOUNT_NAME + "/NICE|NICE]])";document.getElementById("editform").submit()', 1000)
					}
				}
			)
		}
		
		NICE.WPAPIHandler.performGET(
			params, 
			finishPostingAndLogging, 
			args, 
			function(message){
				NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Asking for edit token", message)
				if(confirm('Unable to post to ' + NICE.reverted.username + '\'s talk page.  Continue with revert?')){
					document.getElementById("editform").submit()
				}
			}
		)
	}
	
	
	NICE.loadRevertedUserInformation = function(revisionId){
		//Start AJAX call sequence
		var params = {
			"action": 'query',
			"prop": 'revisions',
			"revids": revisionId,
			"rvprop": 'user',
			"format": 'json'
		}
		
		NICE.WPAPIHandler.performGET(
			params, 
			function(result, args){
				try{
					var res = eval("(" + result + ")")
				}
				catch(e){
					NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Searching for username using revisionId", "Unable to process JSON result" + result)
					NICE.showErrorWhenReady("The Wikipedia server returned an invalid result while asking for user information.")
					return
				}
				
				try{
					for(thing in res.query.pages){
						var page = res.query.pages[thing]
					}
					NICE.reverted.username = page.revisions[0].user
					if(page.revisions[0].anon){
						NICE.reverted.anon = true
					}
					else{
						NICE.reverted.anon = false
					}
				}
				catch(e){
					NICE.reverted.username = ""
					NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Looking for username in result", "Invalid result structure: " + result)
					NICE.showErrorWhenReady("The Wikipedia server returned an invalid result while asking for user information.")
					return
				}
				
				if(NICE.reverted.anon){
					//do nothing
					NICE.reverted.userRevs = 0
					return
				}
				
				var params = {
					"action": 'query',
					"list": 'users',
					"ususers": NICE.reverted.username,
					"usprop": 'editcount',
					"format": 'json'
				}
				
				NICE.WPAPIHandler.performGET(
					params, 
					function(result, args){
						try{
							var res = eval("(" + result + ")")
						}
						catch(e){
							NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Searching for editcount using username", "Unable to process JSON result: " + result)
							NICE.showErrorWhenReady("The Wikipedia server returned an invalid result while asking for user information.")
							return
						}
						
						try{
							NICE.reverted.userRevs = res.query.users[0].editcount
						}
						catch(e){
							NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Looking for editcount in result", "Invalid result structure: " + result)
							NICE.showErrorWhenReady("The Wikipedia server returned an invalid result while asking for user information.")
							return
						}
					}, 
					{},
					function(message){
						NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Requesting edit count of user", message)
						NICE.showErrorWhenReady(message)
					}
				)
			}, 
			{}, 
			function(message){
				NICE.logger.error(mw.config.get('wgUserName'), NICE.reverted.revisionId, "Searching for username using revisionId", message)
				NICE.showErrorWhenReady(message)
			}
		)
	}
	
	/* Show notice when ready */
	NICE.showNoticeWhenReady()
	
	/* Start getting work done */
	NICE.loadRevertedUserInformation(NICE.reverted.revisionId)
	
	//Start ready catcher
	NICE.createSaveFormWhenReady()
}