MediaWiki:RecentChangesMultiple/code.js

if(document.recent_changes_multiple_dev_ajax === undefined) { document.recent_changes_multiple_dev_ajax = "instance created"; (function{	"use strict";	//######################################	// Variables	//######################################	// Constants	var MONTH_DICT = Object.freeze(["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]);	var FAVICON_BASE = "http://www.google.com/s2/favicons?domain=";	var IP_REGEX = /^(([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)\.){3}([1-9]?\d|1\d\d|25[0-5]|2[0-4]\d)$/; // http://stackoverflow.com/a/19498244/1411473	var LOADING_ERROR_RETRY_NUM = 5;	// Storage	var _resultCont = document.querySelector('#rc-content-multiple'), _statusNode, _wikisNode, _resultsNode;	var _hideusers, _chosenWikis, temp;	var _recentChangesEntries, _ajaxCallbacks, _wikisLeftToLoad, _totalWikisToLoad;	// Find rcm container, and exit if not found (needed for everything else)	if(_resultCont == undefined) { console.log("No \"Recent Changes - Multiple\" container found; exiting."); return; } //######################################	// Initialization //######################################	function init { _hideusers = []; if(_resultCont.dataset.hideusers) { _hideusers = _resultCont.dataset.hideusers.replace("_", " ").split(","); } if(_resultCont.dataset.params && _resultCont.dataset.params.indexOf("hidemyself=") > -1 && _resultCont.dataset.params.split("hidemyself=")[1][0] == "1") { if((temp = document.querySelector(".ajaxLogin.global-navigation-link")) != undefined) { _hideusers.push(temp.href.split("User:")[1].replace("_", " ")); } }		_chosenWikis = []; forEach(_resultCont.querySelectorAll("li"), function(node){			var tWikiData = node.textContent.split("&"), tWikiDataSplit;			var tWikiObj = { url:tWikiData[0], subdir:"/wiki/", firstSeperator:"?" };			if(tWikiObj.url.indexOf("/") > -1) {				_resultCont.innerHTML = "'"+tWikiObj.url+"' is an inccorect format. Please do not include 'http://' or anything after or including the first '/'. ";				throw "Incorrect format";			}			for(var i = 1; i < tWikiData.length; i++) {				tWikiDataSplit = tWikiData[i].split("=");				if(tWikiDataSplit.length > 1) {					if(tWikiDataSplit[0] == "hideusers") {						tWikiObj["hideusers"] = tWikiDataSplit[1].replace("_", " ").split(",");					} else if(tWikiDataSplit[0] == "subdir") {						var indexphptitle = "";						if(tWikiDataSplit[1].indexOf("--indexphptitle") > -1) {							indexphptitle = "index.php?title="; tWikiObj.firstSeperator = "&"; tWikiDataSplit[1] = tWikiDataSplit[1].replace("--indexphptitle", ""); }						if(tWikiDataSplit[1] == "root") { tWikiObj["subdir"] = "/"; } else if(tWikiDataSplit[1] == "") { // skip } else { tWikiObj["subdir"] = "/"+tWikiDataSplit[1]+"/"; }						tWikiObj["subdir"] += indexphptitle; } else { tWikiObj[tWikiDataSplit[0]] = tWikiDataSplit[1]; }				}			}			_chosenWikis.push(tWikiObj); });		_resultCont.innerHTML = "";		_statusNode = newElement("div", { id:"rcm-status" }, _resultCont);		_wikisNode = newElement("div", { id:"rcm-wikis" }, _resultCont);		_resultsNode = newElement("div", { id:"rcm-results", className:"rc-conntent" }, _resultCont);		newElement("style", { type:"text/css", innerHTML:"" +"#rc-content-multiple { padding:4px 5px; background:rgba(0,0,0,0.1); }" +"#rcm-status { text-align:center; padding:15px 5px; background:rgba(0,0,0,0.1); } #rcm-status .rcm-error { color:red; }" +"#rcm-wikis { padding:4px 0; border-bottom:2px dotted currentColor; } #rcm-wikis img { vertical-align: middle; }" +"#rcm-results { white-space: pre; }" +"#rcm-content-loading { font-size:80%; font-decoration:underline; }" }, document.head);		_start;	}	function _start {		_statusNode.innerHTML = "";		_wikisNode.innerHTML = "Wikis Loaded: ";		_recentChangesEntries = [];		_ajaxCallbacks = [];		forEach(_chosenWikis, function(wikiInfo){ _loadWiki(wikiInfo); });		_totalWikisToLoad = _chosenWikis.length;		_wikisLeftToLoad = _totalWikisToLoad;		_statusNode.innerHTML = " Loading... ( 0% )";	}	// Seperate method so that it can be reused if the loading failed	function _loadWiki(pWikiInfo, pTries) {		if(typeof pTries === 'undefined') { pTries = 0; }		++pTries;		//console.log("http://"+pWikiInfo.url+pWikiInfo.subdir+"Special:RecentChanges"+pWikiInfo.firstSeperator+"feed=atom&action=purge");		var url = "http://"+pWikiInfo.url+pWikiInfo.subdir+"Special:RecentChanges"+pWikiInfo.firstSeperator+"feed=atom&action=purge";		//console.log('http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from xml where url="'+url+'"') + '&format=json');		$.getJSON('http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent('select * from xml where url="'+url+'"') + '&format=json', function(e){ _onWikiLoaded(e, pWikiInfo, pTries) }); }	/* Called after a wiki is loaded; will add it to queue, and run it fi no other callbacks running. */	function _onWikiLoaded(e, pWikiInfo, pTries) { // Make sure results are valid if(e.query.results == null) { console.log("Error loading "+pWikiInfo.url+" ("+pTries+"/"+LOADING_ERROR_RETRY_NUM+" tries)"); console.log(e); if(pTries < LOADING_ERROR_RETRY_NUM) { _loadWiki(pWikiInfo, pTries); } else { _statusNode.innerHTML = " Error loading "+pWikiInfo.url+" ("+pTries+" tries), please correct syntax (or refresh page/script to try again). "; throw "Refresh"; }			return; }		_ajaxCallbacks.push(function{			_onWikiLoadedAfter(e, pWikiInfo, pTries);		}); if(_ajaxCallbacks.length == 1) { _ajaxCallbacks[0]; } }	/* Check wiki data one at a time, either as it's returned, or after the current data is done being processed. */	function _onWikiLoadedAfter(e, pWikiInfo, pTries) { // Calc stuff that only needs to be calced once for every wiki, that every entry needs to know of. var tFavicon = _getWikiFavicon(pWikiInfo); // Check if wiki doesn't have any recent changes if(e.query.results.feed == undefined || e.query.results.feed.entry == undefined) { if(e.query.results.feed == undefined) console.log(e.query.results); __checkIfDoneLoading; return; }		// Add entry from this wiki to the list in a sorted order. console.log(e.query.results.feed.entry); if(e.query.results.feed.entry instanceof Array == false) e.query.results.feed.entry = [e.query.results.feed.entry]; var tRC, tDate, changeAdded; e.query.results.feed.entry.forEach(function(entry){			if(_hideusers.indexOf(entry.author.name) > -1 || (pWikiInfo.hideusers && pWikiInfo.hideusers.indexOf(entry.author.name) > -1)) { return; }			tDate = new Date(entry.updated);			tRC = new RecentChange( entry, tDate, pWikiInfo, tFavicon );			changeAdded = false;			_recentChangesEntries.every(function(obj, i){ if(tDate >= obj.date) { _recentChangesEntries.splice(i, 0, tRC); changeAdded = true; return false; } else { if(tRC.shouldGroupWith(obj)) { if(obj.class_name == "RecentChangeList") { obj.list.push(tRC); // Should always be older } else { // if(obj.class_name == "RecentChange") // Create new list with the current nodes, replacing the current value's spot on the list _recentChangesEntries[i] = new RecentChangeList(obj, tRC); }						changeAdded = true; return false; }				}				return true; });			if(!changeAdded || _recentChangesEntries.length == 0) { _recentChangesEntries.push(tRC); }		}); __checkIfDoneLoading; function __checkIfDoneLoading { --_wikisLeftToLoad; _wikisNode.innerHTML += formatString("{1}:", pWikiInfo.url+pWikiInfo.subdir, tFavicon, (_resultCont.dataset.params ? pWikiInfo.firstSeperator + _resultCont.dataset.params : "")); document.querySelector("#rcm-load-perc").innerHTML = Math.round((_totalWikisToLoad - _wikisLeftToLoad) / _totalWikisToLoad * 100) + "%";//.toFixed(3) + "%"; if(_wikisLeftToLoad > 0) { _ajaxCallbacks.shift; if(_ajaxCallbacks.length > 0){ _ajaxCallbacks[0]; } } else { _wikisNode.innerHTML = _wikisNode.innerHTML.substring(0, _wikisNode.innerHTML.length-1); _setupRecentChangesMultiple; } }	}	function _setupRecentChangesMultiple { console.log(_recentChangesEntries); temp = new Date; _statusNode.innerHTML = "Recent Changes downloaded at:   - [ 0 / "+_recentChangesEntries.length+" Recent Changes added] "; _resultsNode.innerHTML = ""; _rcmChunk(0, 99, 99); }	function _rcmChunk(pIndex, pLastDay, pLastMonth) { var date = _recentChangesEntries[pIndex].date; if(date.getUTCDate != pLastDay || date.getUTCMonth != pLastMonth) { pLastDay = date.getUTCDate; pLastMonth = date.getUTCMonth; _resultsNode.innerHTML += " "+date.getUTCDate+" "+MONTH_DICT[date.getUTCMonth]+" "+date.getUTCFullYear+" "; }		// Add to page _resultsNode.appendChild(_recentChangesEntries[pIndex].toHTML); if(++pIndex < _recentChangesEntries.length) { document.querySelector("#rcm-content-loading-num").innerHTML = pIndex; setTimeout(function{ _rcmChunk(pIndex, pLastDay, pLastMonth); }); } else { removeElement(document.querySelector("#rcm-content-loading")); } }	//######################################	// Specific Helper Methods //######################################	function _getWikiFavicon(pWikiInfo) { if(pWikiInfo.favicon) { if(pWikiInfo.favicon.indexOf(".") > -1) { return formatString("", pWikiInfo.favicon); } else { return formatString("", pWikiInfo.favicon); }		} else if(pWikiInfo.url.indexOf(".wikia.") > -1 && pWikiInfo.url.split(".wikia.")[0].indexOf(".") <= -1) { return formatString("", pWikiInfo.url.split(".wikia.")[0]); } else { return formatString("", FAVICON_BASE+pWikiInfo.url); }	}	//######################################	// Objects //######################################	// ### RecentChange ### // // Holds all info about a single change var RecentChange = (function{		var SEP = " . . ";		var ARROW_FILLIN = '';		RecentChange.LOG_TYPE = Object.freeze({ NONE:"notALog", BLOCK:"blockedUser", CHAT_BAN:"chatBan", DELETION:"deletedPage", IMPORT:"importPage", MERGE:"mergePage", MOVED:"pageMoved", // PATROL not used PROTECTION:"pageProtectionChanged", UPLOAD:"uploadedFile", USER_AVATAR:"userAvatar", USER_CREATION:"userCreated", USER_RENAME:"userRenamed", USER_RIGHTS:"userRightsChanged", WIKI_FEATURES:"wikiFeatures" });		RecentChange.TYPE = Object.freeze({NORMAL:"normalChange", COMMENT:"commentType", LOG:"logChange"});		function RecentChange(pEntry, pDate, pWikiInfo, pFavicon) {			this.class_name = "RecentChange";			this.entry = pEntry;			this.date = pDate;			this.wikiInfo = pWikiInfo;			this.href = "http://"+this.wikiInfo.url+this.wikiInfo.subdir; // link up to the point of the page name			this.hrefNormal = this.entry.link.href.split(this.wikiInfo.firstSeperator+"diff=")[0]; // Includes page name			this.favicon = pFavicon;			this.summaryDiffHTML = newElement("div", { innerHTML:this.entry.summary.content });			this.summaryText = this.summaryDiffHTML.querySelector("p").innerHTML;			this.sizeDiff = this.calcSizeDiff;			// Check if the entry is part of a log			if(this.summaryText.indexOf(" uploaded \"") > -1 || this.summaryText.indexOf(" uploaded a new version of \"") > -1) {				this.logType = RecentChange.LOG_TYPE.UPLOAD; }			else if(this.summaryText.indexOf(" changed block settings for  -1) { this.logType = RecentChange.LOG_TYPE.BLOCK; }			else if(this.summaryText.indexOf(" deleted page  -1) { this.logType = RecentChange.LOG_TYPE.DELETION; }			else if(this.summaryText.indexOf(" moved page  -1) { this.logType = RecentChange.LOG_TYPE.MOVED; }			else if(this.summaryText.indexOf("</a> changed group membership for  -1) { this.logType = RecentChange.LOG_TYPE.USER_RIGHTS; }			else if(this.summaryText.indexOf(" protected \"") > -1 || this.summaryText.indexOf(" changed protection level for \"") > -1 || this.summaryText.indexOf(" moved protection settings from \"") > -1 || this.summaryText.indexOf(" removed protection from \"") > -1) { this.logType = RecentChange.LOG_TYPE.PROTECTION; }			else if(this.summaryText.indexOf("</a> created a user account") > -1) { this.logType = RecentChange.LOG_TYPE.USER_CREATION; }			else if(this.summaryText.indexOf("</a> User avatar added or updated") > -1) { this.logType = RecentChange.LOG_TYPE.USER_AVATAR; }			else { this.logType = RecentChange.LOG_TYPE.NONE; }			this.pageName = this.entry.title; // Page name including namespace this.uniqueID = this.pageName; // A unique ID is primarily important for "comments", since the atom feed doesn't display specific title. // Find out the type of change this is			if(this.logType != RecentChange.LOG_TYPE.NONE) { this.type = RecentChange.TYPE.LOG; }			else if(this.entry.title.indexOf("/@comment") > -1) { this.type = RecentChange.TYPE.COMMENT; // A "comment" can be a article comment, message wall post, or forum post. this.pageName = this.entry.title.split("/@comment")[0]; this.uniqueID = this.pageName+"/@comment"+this.entry.title.split("/@comment")[1]; // "Comments" can have 2 /@comments, the first one is what we care about. }			else { this.type = RecentChange.TYPE.NORMAL; }			this.isNewPage = this.summaryText.indexOf("Created page with \"") > -1;		}		RecentChange.prototype.toHTML = function {			var html = "";			switch(this.logType) {				case RecentChange.LOG_TYPE.UPLOAD: {					//html += this.favicon;					//html += " ";					html += SEP;					html += this.userDetails;					html += " uploaded \"" html += formatString("{1}</a>", this.hrefNormal, this.pageName); html += "\"";					break;				}				case RecentChange.LOG_TYPE.DELETION: {					//html += this.favicon;					//html += " ";					html += " (Deletion log</a>)";					html += SEP;					html += this.userDetails;					html += " deleted page "					html += this.entry.title;					if(this.summaryText != "") { html += " ("+this.summaryText+")"; }					break;				}				case RecentChange.LOG_TYPE.MOVED: {					//break;				}				case RecentChange.LOG_TYPE.USER_RIGHTS: {					//break;				}				case RecentChange.LOG_TYPE.USER_CREATION: {					//break;				}				case RecentChange.LOG_TYPE.NONE:				default: {					//html += this.favicon;					//html += "   ";					//if(this.isNewPage) html += " N ";					html += this._pageTitleText;					html += SEP;					html += this._sizeDiffText;					html += this.userDetails;					if(this.summaryText != "") { html += " ("+this.summaryText+")"; } break; }			}			var tTable = newElement("table", { className:"mw-enhanced-rc" }); var tRow = newElement("tr", {}, tTable); newElement("td", { className:"mw-enhanced-rc", innerHTML:""				+this.favicon				+ARROW_FILLIN				+(this.isNewPage ? 'N ' : ' ')				+" "				+" "				+" "				+" "				+this.time				+" "			}, tRow); newElement("td", { innerHTML:html }, tRow); return tTable;//newElement("div", { innerHTML:html }); };		RecentChange.prototype.toHTMLRow = function { var html = ""; switch(this.type) { case RecentChange.TYPE.NORMAL: { html += " "+this.time+" " html += this._pageTitleText; html += SEP; html += this._sizeDiffText; html += this.userDetails; if(this.summaryText != "") { html += " ("+this.summaryText+")"; } break; }				case RecentChange.TYPE.COMMENT: { html += " "+this.time+" " html += this._pageTitleText; html += SEP; html += this._sizeDiffText; html += this.userDetails; if(this.summaryText != "") { html += " ("+this.summaryText+")"; } break; }				case RecentChange.TYPE.LOG: { html += " "+this.time+" " html += SEP; html += this.userDetails; switch(this.logType) { case RecentChange.LOG_TYPE.BLOCK: case RecentChange.LOG_TYPE.CHAT_BAN: case RecentChange.LOG_TYPE.DELETION: case RecentChange.LOG_TYPE.IMPORT: case RecentChange.LOG_TYPE.MERGE: case RecentChange.LOG_TYPE.MOVED: case RecentChange.LOG_TYPE.PROTECTION: case RecentChange.LOG_TYPE.UPLOAD: case RecentChange.LOG_TYPE.USER_AVATAR: case RecentChange.LOG_TYPE.USER_CREATION: case RecentChange.LOG_TYPE.USER_RENAME: case RecentChange.LOG_TYPE.USER_RIGHTS: case RecentChange.LOG_TYPE.WIKI_FEATURES: default: { html += this.summaryText.split("</a>")[1]; break; }					}					break; }			}			var tRow = newElement("tr", {}); newElement("td", {}, tRow); newElement("td", { className:"mw-enhanced-rc", innerHTML:""				+(this.isNewPage ? 'N ' : ' ')				+" "				+" "				+" "				+" "			}, tRow); newElement("td", { className:"mw-enhanced-rc-nested", innerHTML:html }, tRow); return tRow;//newElement("div", { innerHTML:html }); }		RecentChange.prototype.time = function { return pad(this.date.getUTCHours,2)+":"+pad(this.date.getUTCMinutes,2); };		RecentChange.prototype.calcSizeDiff = function { var charDiff = 0; if(this.summaryDiffHTML.querySelector("table.diff") != undefined) { //console.log(temp); var charCountOld = 0, charCountNew = 0; var changes = this.summaryDiffHTML.querySelectorAll(".diff-marker"); forEach(changes, function(diffMarker){					if(diffMarker == " ") return; forEach(diffMarker.nextElementSibling.querySelectorAll(".diffchange"), function(diff){ // .textContent used to avoid string length issues with unicodes. if(diffMarker.innerHTML == "−") { charCountOld += diff.textContent.length; } else if(diffMarker.innerHTML == "+") { charCountNew += diff.textContent.length; }					});				});				charDiff = charCountNew - charCountOld; } else { //charDiff = pContentHTML.querySelector("div").innerHTML.length; }			return charDiff; }		RecentChange.prototype._sizeDiffText = function { var html = ""; html += formatString("<strong class='{0}'>({1}{2}) ", greaterLessEqual(this.sizeDiff, 0, "mw-plusminus-pos", "mw-plusminus-neg", "mw-plusminus-null"), greaterLessEqual(this.sizeDiff, 0, "+", "", ""), this.sizeDiff); html += SEP; return html; }		RecentChange.prototype._pageTitleText = function { var titleLink = "{1}</a>"; if(this.type == RecentChange.TYPE.COMMENT && this.isNewPage) { titleLink = "Comment ("+titleLink+")"; } var diffLink = "diff"; if(!this.isNewPage) { diffLink = ""+diffLink+"</a>"; } return formatString(titleLink+" ("+diffLink+" | hist</a>)", this.hrefNormal, this.pageName, this.entry.link.href, this.wikiInfo.firstSeperator); }		RecentChange.prototype.userDetails = function { var username = this.entry.author.name; if(!IP_REGEX.test(username)) { return formatString("{1}</a> (wall</a> | contribs</a> | block</a>)", this.href, username); } else { return formatString("{1}</a> (wall</a> | block</a>)", this.href, username); }		}		RecentChange.prototype.logTypeText = function { var logTemplate = "(<a href='"+this.href+"Special:Log/{0}'>{1} log</a>)"; switch(this.logType) { case RecentChange.LOG_TYPE.UPLOAD: { return formatString(logTemplate, "upload", "Upload"); } case RecentChange.LOG_TYPE.DELETION: { return formatString(logTemplate, "delete", "Deletion"); } case RecentChange.LOG_TYPE.MOVED: { return formatString(logTemplate, "move", "Move"); } case RecentChange.LOG_TYPE.USER_RIGHTS: { return formatString(logTemplate, "rights", "User rights"); } case RecentChange.LOG_TYPE.USER_CREATION: { return formatString(logTemplate, "newusers", "User creation"); } case RecentChange.LOG_TYPE.NONE: default: { // nothing }			}			return ""; }		RecentChange.prototype.shouldGroupWith = function(pRC) { if(this.wikiInfo.url == pRC.wikiInfo.url && this.logType == pRC.logType && this.date.getUTCMonth == pRC.date.getUTCMonth && this.date.getUTCDate == pRC.date.getUTCDate) { if(this.logType == RecentChange.LOG_TYPE.NONE && this.uniqueID != pRC.uniqueID) { return false; }				return true; }			return false; }		return RecentChange; });	// ### RecentChangeList ### //	// Holds grouped changes, be they logs or same-page edits.	var RecentChangeList = (function{ var SEP = " . . "; var ARROW = ' <a title="Show details (requires JavaScript)" href="#"><img width="12" height="12" title="Show details (requires JavaScript)" alt="+" src="http://slot1.images.wikia.nocookie.net/__cb1422546004/common/skins/common/images/Arr_r.png"></a> <a title="Hide details" href="#"><img width="12" height="12" title="Hide details" alt="-" src="http://slot1.images.wikia.nocookie.net/__cb1422546004/common/skins/common/images/Arr_d.png"></a>  '; function RecentChangeList(pRC1, pRC2) { this.class_name = "RecentChangeList"; this.list = [pRC1, pRC2]; Object.defineProperty(this, "date", { get: function { return this.list[0].date; }, enumerable: true }); Object.defineProperty(this, "uniqueID", { get: function { return this.list[0].uniqueID; }, enumerable: true }); Object.defineProperty(this, "wikiInfo", { get: function { return this.list[0].wikiInfo; }, enumerable: true }); Object.defineProperty(this, "logType", { get: function { return this.list[0].logType; }, enumerable: true }); }		RecentChangeList.prototype.toHTML = function { var html = ""; //html += this.list[0].favicon; //html += "  "; if(this.logType == RecentChange.LOG_TYPE.NONE) { html += this.list[0]._pageTitleText; html += SEP html += _sizeDiffText(this.totalDiff); } else { html += this.list[0].logTypeText; }			html += SEP; html += this._contributorsCountText; //var myDiv = newElement("div", { innerHTML:html }); var tTable = newElement("table", { className:"mw-collapsible mw-enhanced-rc" }); var tRow = newElement("tr", {}, tTable); newElement("td", { innerHTML:this.list[0].favicon+ARROW }, tRow); newElement("td", { className:"mw-enhanced-rc", innerHTML:""				+" "				+" "				+" "				+" "				+" "				+this.list[0].time				+" "			}, tRow); newElement("td", { innerHTML:html }, tRow); this.list.forEach(function(rc){				tTable.appendChild(rc.toHTMLRow);			}); return tTable; }		RecentChangeList.prototype._contributorsCountText = function { var contribs = {}, indx; this.list.forEach(function(rc){				if(contribs.hasOwnProperty(rc.entry.author.name)) {					contribs[rc.entry.author.name]++;				} else {					contribs[rc.entry.author.name] = 1;				}			}); var returnText = "[", total = 0, tLength = this.list.length; Object.keys(contribs).forEach(function (key) {				returnText += this._userPageLink(key) + (contribs[key] > 1 ? " ("+contribs[key]+"x)" : "");				total += contribs[key];				if(total < tLength) { returnText += "; "; }			}, this); return returnText + "]"; }		RecentChangeList.prototype._userPageLink = function(pUsername) { if(!IP_REGEX.test(pUsername)) { return formatString("<a href='{0}User:{1}'>{1}</a>", this.href, pUsername); } else { return formatString("<a href='{0}Special:Contributions/{1}'>{1}</a>", this.href, pUsername); }		}		RecentChangeList.prototype.totalDiff = function { var total = 0; this.list.forEach(function(rc){ total += rc.sizeDiff; }); return total; }		function _sizeDiffText(pSize) { var html = ""; html += formatString("<strong class='{0}'>({1}{2}) ", greaterLessEqual(pSize, 0, "mw-plusminus-pos", "mw-plusminus-neg", "mw-plusminus-null"), greaterLessEqual(pSize, 0, "+", "", ""), pSize); html += SEP; return html; }		return RecentChangeList; });	//######################################	// General Helper Methods	//######################################	function forEach(collection, callback) { if(collection != undefined) { Array.prototype.forEach.call(collection, callback); } }	// http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript	function pad(n, width, z) {//Number, max padding (ex:3 = 001), what to pad with (default 0)		z = z || '0';		n = n + '';		return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;	}	function greaterLessEqual(a, b, gr, ls, eq) {		return (a > b ? gr : (a < b ? ls : eq));	}	// http://stackoverflow.com/a/4673436/1411473	function formatString(format) {		var args = Array.prototype.slice.call(arguments, 1);		return format.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] != 'undefined' ? args[number] : match ;		});	}	// Creates a new HTML element (not jQuery) with specific attributes	function newElement(tag, attributes, parent) {		var element = document.createElement(tag);		if(attributes != undefined) {			for(var key in attributes)				element[key] = attributes[key];		}		if(parent != undefined) parent.appendChild(element);		return element	;	}	function removeElement(pNode) {		pNode.parentNode.removeChild(pNode);	}	$(document).ready(init); }); }