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.
/*******************************************************************************
********************************************************************************
           ***      **     ******       ******     ********                     
           ****     **    **    **     **    **    **     **                    
           ** **    **   **      **   **      **   **     **                    
           **  **   **   **      **   **      **   *******                      
           **   **  **   **      **   **      **   **     **                    
           **    ** **   **      **   **      **   **     **                    
           **     ****    **    **     **    **    **     **                    
           **      ***     ******       ******     ********                     
********************************************************************************
*****************It isn't an acronym. Caps lock broke.**************************
********************************************************************************
This gadget was developed by the Grouplens Research lab (http://grouplens.org)
to be used in the English Wikipedia.  See 
http://en.wikipedia.org/wiki/User:EpochFail/NOOB for documentation and 
installation instructions.  Any questions or concerns should be directed to
User:EpochFail.  
*******************************************************************************/
if(!NOOB){
	var NOOB = {}
}

function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

NOOB.DateDiffer = {
	second: 1000,
	minute: 1000 * 60,
	hour: 1000 * 60 * 60,
	day: 1000 * 60 * 60 * 24,
	week: 1000 * 60 * 60 * 24 * 7,
	month: 1000 * 60 * 60 * 24 * 30.44,
	year: 1000 * 60 * 60 * 24 * 365.242,
	approxDiff: function(date1, date2){
		var dateDiffMS = date1-date2
		diff = {}
		diff.years = Math.floor(dateDiffMS/this.year)
		dateDiffMS -= diff.years*this.year
		diff.months = Math.floor(dateDiffMS/this.month)
		dateDiffMS -= diff.months*this.month
		diff.weeks = Math.floor(dateDiffMS/this.week)
		dateDiffMS -= diff.weeks*this.week
		diff.days= Math.floor(dateDiffMS/this.day)
		dateDiffMS -= diff.days*this.day
		diff.hours = Math.floor(dateDiffMS/this.hour)
		dateDiffMS -= diff.hours*this.hour
		diff.minutes = Math.floor(dateDiffMS/this.minute)
		dateDiffMS -= diff.minutes*this.minute
		diff.seconds = Math.floor(dateDiffMS/this.second)
		dateDiffMS -= diff.seconds*this.second
		diff.milliseconds = Math.floor(dateDiffMS)
		return diff
	},
	human: function(diff){
		var timeString = ""
		if(diff.years > 0){
			timeString += diff.years + " year"
			if(diff.years > 1){
				timeString += "s"
			}
			timeString += " and " + diff.months + " month"
			if(diff.months > 1){
				timeString += "s"
			}
		}else if(diff.months > 0){
			timeString += diff.months + "month"
			if(diff.months > 1){
				timeString += "s"
			}
			timeString += " and " + diff.weeks*7+diff.days + " day"
			if(diff.weeks*7+diff.days > 1){
				timeString += "s"
			}
		}else if(diff.weeks > 0){
			timeString += diff.weeks + "week"
			if(diff.weeks > 1){
				timeString += "s"
			}
			timeString += " and " + diff.days + " day"
			if(diff.days > 1){
				timeString += "s"
			}
		}else if(diff.days > 0){
			timeString += diff.days + "day"
			if(diff.days > 1){
				timeString += "s"
			}
			timeString += " and " + diff.hours + " hour"
			if(diff.hours > 1){
				timeString += "s"
			}
		}else if(diff.hours > 0){
			timeString += diff.hours + "hour"
			if(diff.hours > 1){
				timeString += "s"
			}
			timeString += " and " + diff.minutes + " minute"
			if(diff.minutes > 1){
				timeString += "s"
			}
		}else if(diff.minutes > 0){
			timeString += diff.minutes + "minute"
			if(diff.minutes > 1){
				timeString += "s"
			}
			timeString += " and " + diff.seconds + " second"
			if(diff.seconds > 1){
				timeString += "s"
			}
		}else{
			timeString += diff.seconds + "second"
			if(diff.seconds > 1){
				timeString += "s"
			}
			timeString += " and " + diff.milliseconds + " ms"
		}
		return timeString
	}
}

if(!NOOB){
	var NOOB = {}
}
NOOB.Editor = function(username){
	this.username = username
	this.links = []
	this.signatures = []
	
}
	
	NOOB.Editor.prototype.addLink = function(link){
		this.links.push(link)
	}
	
	NOOB.Editor.prototype.addSignature = function(signature){
		this.signatures.push(signature)
	}
	
	NOOB.Editor.prototype.updateEditCount = function(user){
		if(user.registration){
			var dateParts = user.registration.match(/([0-9]{4})-([0-1][0-9])-([0-3][0-9])T([0-9]{2}):([0-9]{2}):([0-9]{2})Z/)
			var regDate = new Date(dateParts[1], dateParts[2], dateParts[3], dateParts[4], dateParts[5], dateParts[6])
			var nowDate = new Date()
			var diff = NOOB.DateDiffer.approxDiff(nowDate, regDate)
			var diffString = NOOB.DateDiffer.human(diff)
		}else{
			var diffString = " an unknown amount of time"
		}
		
		var className = ""
		if(user.editcount < 10){
			className = "ten_1"
		}else if(user.editcount < 100){
			className = "ten_2"
		}else if(user.editcount < 1000){
			className = "ten_3"
		}else if(user.editcount < 10000){
			className = "ten_4"
		}else{
			className = "ten_5"
		}
		
		for(var i=0;i<this.links.length;i++){
			var link = this.links[i]
			
			link.update(className, user.editcount, diffString)
		}
		for(var i=0;i<this.signatures.length;i++){
			var signature = this.signatures[i]
			
			signature.update(className, user.editcount, diffString)
		}
		
	}
	
	NOOB.Editor.prototype.setUnknown = function(){
		for(var i=0;i<this.links.length;i++){
			var link = this.links[i]
			
			link.unknown()
		}
		for(var i=0;i<this.signatures.length;i++){
			var signature = this.signatures[i]
			
			signature.unknown()
		}
	}
	

if(!NOOB){
	var NOOB = {}
}
NOOB.Signature = function(userHref, spanNode){
	this.expressions = []
	this.expressions.push(/\/wiki\/(U|u)ser(_talk)?:([^\/#]+)/)
	this.expressions.push(/\/wiki\/(S|s)pecial:(C|c)ontributions\/([^\/#]+)/)
	this.expressions.push(/\/w\/index\.php\?title=User_talk:([^\/#]+)&action=edit&redlink=1/)
	this.expressions.push(/\/wiki\/(S|s)pecial:(E|e)mailUser\/([^\/#]+)/)
	
	if(userHref){
		for(var i=0;i<this.expressions.length && !this.username;i++){
			var expression = this.expressions[i]
			match = userHref.match(expression)
			if(match != null){
				this.username = match[match.length-1]
			}
		}
	}
	if(!this.username){
		this.username = "?unknown?"
	}
	
	this.spanNode = spanNode
}

	NOOB.Signature.prototype.update = function(className, editCount, diffString){
		this.spanNode.removeClass('loading')
		this.spanNode.addClass(className)
		this.spanNode.html('<a href="/wiki/Special:Contributions/' + this.username + '">hist</a>')
		this.spanNode.attr('title', editCount + " revisions in " + diffString)
	}
	
	NOOB.Signature.prototype.unknown = function(){
		this.spanNode.removeClass('loading')
		this.spanNode.addClass('unknown')
		this.spanNode.html('<a href="/wiki/Special:Contributions/' + this.username + '">?</a>')
	}

if(!NOOB){
	var NOOB = {}
}
NOOB.Link = function(editorLink){
	this.username = editorLink.text().replace(/ /g, "_")
	this.link = editorLink
}
	NOOB.Link.prototype.update = function(className, editCount, diffString){
		this.link.attr("title", user.editcount + " revisions in " + diffString)
		$('&nbsp;<span class="contribs ' + className + '" title="' + user.editcount + " revisions in " + diffString + '">&nbsp;</span>&nbsp;').insertBefore(this.link)
	}
	NOOB.Link.prototype.unknown = function(){
		$('&nbsp;<span class="contribs unknown" title="unknown">?</span>&nbsp;').insertBefore(this.link)
	}

if(!NOOB){
	var NOOB = {}
}

NOOB.Editors = function(){
	this.editors = {}
}
	
	NOOB.Editors.prototype.addEditorLink = function(editorLink){
		var link = new NOOB.Link(editorLink)
		if(this.editors[link.username] == undefined){
			this.editors[link.username] = new NOOB.Editor(link.username)
		}
		this.editors[link.username].addLink(link)
	}
	NOOB.Editors.prototype.addEditorSignature = function(href, spanNode){
		var signature = new NOOB.Signature(href, spanNode)
		if(this.editors[signature.username] == undefined){
			this.editors[signature.username] = new NOOB.Editor(signature.username)
		}
		this.editors[signature.username].addSignature(signature)
	}
	
	NOOB.Editors.prototype.loadEditCount = function(){
		var usernames = []
		for(var username in this.editors){
			usernames.push(username)
		}
		
		var params = {
			"action": 'query',
			"list": 'users',
			"ususers": usernames.join("|"),
			"usprop": 'editcount|registration',
			"format": 'json'
		}
		
		NOOB.WPAPIHandler.performGET(
			params, 
			function(res, args){
				
				try{
					var users = res.query.users
				}
				catch(e){
					alert("Invalid result structure")
					return
				}
				
				for(var i=0;i<users.length;i++){
					user = users[i]
					
					if(user.editcount != undefined){
						user.hrefName = user.name.replace(/ /g, "_")
						try{
							args.self.editors[user.hrefName].updateEditCount(user)
						}catch(e){
							alert(user.hrefName)
						}
					}else{
						user.hrefName = user.name.replace(/ /g, "_")
						args.self.editors[user.hrefName].setUnknown()
					}
				}
			}, 
			{self: this},
			function(message){
				alert(message)
			}
		)
		
	}

if(!NOOB){
	var NOOB = {}
}

/**
 * Wikipedia API Handler
 *
 * A simple object for interacting with Wikipedia's API.
 */
NOOB.WPAPIHandler=function(){};NOOB.WPAPIHandler.timeoutSeconds=25;if(mw.config.get('wgServer')&&wgScriptPath){NOOB.WPAPIHandler.url=mw.config.get('wgServer')+mw.config.get('wgScriptPath')+"/api.php"}else{NOOB.WPAPIHandler.url="http://en.wikipedia.org/w/api.php"}NOOB.WPAPIHandler.performGET=function(d,b,a,c){this.performRequest(d,b,a,c,"GET")};NOOB.WPAPIHandler.performPOST=function(d,b,a,c){this.performRequest(d,b,a,c,"POST")};NOOB.WPAPIHandler.performRequest=function(f,b,a,e,d){var c=function(g,h){if(h=="success"){b(g,a)}else{e(h)}};if(d=="POST"){$.post(this.url,f,c,"json")}else{$.get(this.url,f,c,"json")}};NOOB.WPAPIHandler.objectToString=function(c,b){b=b||0;str="";for(thing in c){for(var a=0;a<b;a++){str+="  "}if(typeof(c[thing])=="object"){str+=thing+": \n"+NOOB.WPAPIHandler.objectToString(c[thing],b+1)}else{str+=thing+": `"+c[thing]+"`\n"}}return str};

NOOB.getPrecedingAnchor = function(nodeList, i){
	for(var j=i;j>0;j--){
		node = nodeList[j]
		if(node.constructor){
			if(node.constructor == HTMLAnchorElement){
				return node
			}
			else{
				lastAnchor = $('a:last', node)
				if(lastAnchor.length > 0){
					return lastAnchor
				}
			}
		}
	}
	return null
}
NOOB.getSignatures = function(){
	var signatureNodes = []
	//First process the autosigned comments (because it is easiest)
	var autoSigned = $('.autosigned:not(p > small > .autosigned)')
	for(var i=0;i<autoSigned.length;i++){
		var sigElement = $(autoSigned.get(i))
		
		var lastAnchor = $('a:last', sigElement)
		var signature = {href:lastAnchor.attr('href'), spanNode:$('<span class="signature loading"></span>')}
		signatureNodes.push(signature)
		signature.spanNode.insertAfter(sigElement)
	}
	
	//Next process all first-level comments
	var pTags = $('#contentSub ~ p')
	for(var i=0;i<pTags.length;i++){
		var textBlock = $(pTags.get(i))
		if(textBlock.text().match(/[0-9]{2}:[0-9]{2}, [0-9]{1,2} (January|Febuary|March|April|May|June|July|August|September|October|November|December) (1|2)[0-9]{3} \(UTC\)$/g) != null){
			var lastAnchor = $('a:last', textBlock)
			var signature = {href:lastAnchor.attr('href'), spanNode:$('<span class="signature loading"></span>')}
			signatureNodes.push(signature)
			textBlock.append(signature.spanNode)
		}
	}
	
	//Finally, process all nested comments
	var definitionLists = $('#contentSub ~ dl')
	for(var k=0;k<definitionLists.length;k++){
		var definitions = $('dd', definitionLists.get(k))
		for(var i=0;i<definitions.length;i++){
			var definition = definitions.get(i)
			for(var j=0;j<definition.childNodes.length;j++){
				var node = definition.childNodes[j]
				if(node.constructor && node.constructor == Text){
					if(node.textContent.match(/[0-9]{2}:[0-9]{2}, [0-9]{1,2} (January|Febuary|March|April|May|June|July|August|September|October|November|December) (1|2)[0-9]{3} \(UTC\)(\n)?$/g) != null){
						var precedingAnchor = this.getPrecedingAnchor(definition.childNodes, j)
						if(precedingAnchor){
							precedingAnchor = $(precedingAnchor)
							var signature = {href:precedingAnchor.attr('href'), spanNode:$('<span class="signature loading"></span>')}
							signatureNodes.push(signature)
							if(definition.childNodes.length > j+1){
								definition.insertBefore(signature.spanNode[0], definition.childNodes[j+1])
							}else{
								definition.appendChild(signature.spanNode[0])
							}
							
						}
					}
				}
			}
		}
	}
	
	return signatureNodes
}



if(getParameterByName("action") == "history"){
	importStylesheet("User:EpochFail/NOOB.css")
	NOOB.editors= new NOOB.Editors()
	
	$(document).ready(function(){
		
		var editorLinks = $('.mw-userlink')
		for(var i=0;i<editorLinks.length;i++){
			var editorLink = $(editorLinks[i])
			NOOB.editors.addEditorLink(editorLink)
		}
		NOOB.editors.loadEditCount()
		
	})
}else{
	if(getParameterByName("title") != ''){
		var title = getParameterByName("title")
	}else{
		matches = window.location.href.match(/https?:\/\/[a-z]{2}\.wikipedia\.org\/wiki\/(.+)/)
		if(matches != null){
			var title = matches[1]
		}else{
			var title = ""
		}
	}
	
	var namespace = title.split(":", 1)[0]
	title = title.substring(namespace.length+1)
	if(namespace.match(/(((T|t)alk)|(((U|u)ser)|((W|w)ikipedia)|((I|i)mage)|((M|m)ediaWiki)|((T|t)emplate)|((H|h)elp)|((C|c)ategory)|((P|p)ortal))(_| )talk)/) != null){
		//We know we are on a talk page!  Let's look for signiatures
		importStylesheet("User:EpochFail/NOOB.css")
		//importStylesheetURI('http://wikipedia.grouplens.org/NOOB/css/NOOB.css')
		NOOB.editors = new NOOB.Editors()
		
		$(document).ready(function(){
			var signatureNodes = NOOB.getSignatures()
			for(var i=0;i<signatureNodes.length;i++){
				sigNode = signatureNodes[i]
				NOOB.editors.addEditorSignature(sigNode.href, sigNode.spanNode)
			}
			NOOB.editors.loadEditCount()
		})
	}
	
	
}