User:Rich Smith/iglooMain.js

// /* ======================================================== *\ \* ======================================================== */ /* fixes: - fixed a bug that prevented igloo from intercepting and blocking some key presses in Google Chome. - fixed a bug that caused text size issues in the vector skin - igloo will now more aggressively defend its CSS. - fixed a bug where igloo would occasionally wait much longer than was necessary after loading data from iglooNet before proceeding. - fixed a bug where page titles containing more than one apostrophe could not be clicked on in the recent changes feed. - fixed an initial filter error where only one condition would be checked. - fixed a bug where igloo did not display total sessions correctly.
 * igloo frontend manager - main

changes: - implemented the filter system. - added settings for controlling filters. - created the default filters for general use. - make several improvements to the profanity highlighter.

behind the scenes: - altered iglooNet to be able to stream filters. - introduced the ID system.



function iglooMain { this.internalCounter 	= 0; this.iglooBase			= new wa_document ; this.launch = function ( details ) { switch ( this.internalCounter ) { default: case 0: this.startload = new Date ; this.canDebug = ( typeof console !== 'undefined' ); wa_window.prototype = new wa_document; wa_element.prototype = new wa_document; // step 1 of launching: build the loading interface. var t = 'igloo - ' + iglooSettings.versionString; var t2 = ''; for ( var i = 0; i < wgUserGroups.length; i ++ ) { if ( wgUserGroups[i] == 'steward' ) { t2 += 'steward|'; } else if ( wgUserGroups[i] == 'suppress' ) { t2 += 'suppresser|'; } else if ( wgUserGroups[i] == 'checkuser' ) { t2 += 'checkuser|'; } else if ( wgUserGroups[i] == 'bureaucrat' ) { t2 += 'bureaucrat|'; } else if ( wgUserGroups[i] == 'sysop' ) { t2 += 'administrator|'; iglooSettings.mesysop = true; } else if ( wgUserGroups[i] == 'rollbacker' ) { t2 += 'rollback|'; } }					if ( t2 !== '' ) t += ' [wiki:' + t2.substr ( 0, t2.length - 1 ) + ']'; //t += ' [igloo:' + iglooSettings.iglooFlags + ']'; document.title = t;					iglooSettings.userGroup = t2.substr ( 0, t2.length - 1 ); this.iglooBase.wk_base.innerHTML= ''; // just destroy the mediawiki page content this.iglooInterface 			= new wa_window ( this.iglooBase.wk_base ); this.iglooInterface.win_bg 		= '#ededff'; this.iglooInterface.win_maintfill	= false; this.iglooInterface.win_fill	= true; this.iglooInterface.applyAll ; // step 2 of launching: add text. this.iglooLoading			= new wa_window ( this.iglooBase.wk_base ); this.iglooLoading.win_width = 210; this.iglooLoading.win_height= 17; this.iglooLoading.win_bd 	= '#bbbbff'; this.iglooLoading.win_bd_wd = 1; this.iglooLoading.win_bg 	= '#fdfdff'; this.iglooLoading.win_content = ' Finalising load. Please wait... ';					this.iglooLoading.applyAll ; this.iglooLoading.center ( 'both', true, new Array ( 0, -100 ) ); // step 3 of launching: verify session. this.iglooSession = new iglooSecurity ; if ( typeof this.iglooSession.shutdown != 'undefined' ) return false; this.iglooSession.verifySession ( 'initial' ); // increment counter this.internalCounter ++; break; case 1: // step 4 of launching: the session is verified, tell the user. this.iglooLoading.win_width = 330; this.iglooLoading.win_content = ' Verified session. Requesting from iglooNet... ';					this.iglooLoading.applyAll ; this.iglooLoading.center ( 'both', true, new Array ( 0, -100 ) ); // increment counter this.internalCounter ++; // run this.iglooNet = new iglooNet ; this.iglooNet.retrieve ( true ); break; case 2: // step 5 of lauching: load the user settings this.iglooLoading.win_width = 180; this.iglooLoading.win_content = ' Loading user settings... ';					this.iglooLoading.applyAll; this.iglooLoading.center ( 'both', true, new Array ( 0, -100 ) ); // increment counter this.internalCounter ++; // run this.iglooManageSettings = new iglooManageSettings ; this.iglooManageSettings.retrieve ; break; case 3: // adjust title now that the settings have loaded and we know the iglooNet permissions document.title = document.title + ' [igloo:' + iglooSettings.iglooFlags + ']'; // display first run if relevant // increment counter this.internalCounter ++; if ( iglooSettings.firstRun == true ) { var url = iglooSettings.remoteHost + 'main.php?action=settings&do=set&session='+igloo.iglooSession.session+'&me='+encodeURIComponent(wgUserName)+'&setting=firstRun&value=false'; iglooImport( url, true, 'iglooFirstRun' ); wa ( ':api' ).get ( 'ig_firstrun', iglooSettings.localBase + 'config', 1 ).wait ( function { 							var firstruntext = 	wa ( ':api' ).results ['ig_firstrun']['query']['pages']['page']['revisions']['rev']['#text'], regTest = /firstrun:(.+?);;/i, o;							firstruntext = regTest.exec ( firstruntext );							firstruntext = firstruntext[1].replace ( '%CURRENTUSER%', wgUserName );							igloo.iglooLoading.win_width = 900;								igloo.iglooLoading.win_height = 400;								igloo.iglooLoading.win_content = firstruntext;								igloo.iglooLoading.applyAll ;							igloo.iglooLoading.center ( 'both', true );						 } ).run ; } else { this.launch ; } break; case 4: // finalising launch: the session is verified, tell the user. this.iglooLoading.win_width = 210; this.iglooLoading.win_height= 17; this.iglooLoading.win_content = ' Success. Building interface... ';					this.iglooLoading.applyAll ; this.iglooLoading.center ( 'both', true, new Array ( 0, -100 ) ); // preload images if ( iglooSettings.preloadInterface === true ) { var images = new Array ( 'logo', 'back', 'back-grey', 'forward', 'forward-grey', 'hist', 'revert', 'settings', 'go' ), images2 = new Array ; for ( var i = 0, l = images.length; i < l; i ++ ) { images2 [i] 	= new Image ; images2 [i].src = iglooSettings.remoteHost + 'images/igloo-' + images [i] + '.png'; }					}					// increment counter this.internalCounter ++; // run setTimeout( "igloo.launch ;", 750 ); break; case 5: this.endload = new Date ; // start the required program elements. log if possible. this.iglooDiff = new iglooDiff ; this.iglooDiff.start ; if ( this.canDebug === true ) console.log ( 'igloo: started diff component' ); this.iglooStatus = new iglooStatus ; this.iglooStatus.start ; if ( this.canDebug === true ) console.log ( 'igloo: started status component' ); this.iglooControls = new iglooControls ; this.iglooControls.start ; if ( this.canDebug === true ) console.log ( 'igloo: started control component' ); this.iglooChanges = new iglooChanges ; this.iglooChanges.start ; if ( this.canDebug === true ) console.log ( 'igloo: started changes component' ); this.iglooPopup = new iglooPopup ; this.iglooPopup.start ; if ( this.canDebug === true ) console.log ( 'igloo: started popup component' ); this.iglooActions = new iglooActions ; if ( this.canDebug === true ) console.log ( 'igloo: started actions component' ); this.iglooManageSettings.start ; if ( this.canDebug === true ) console.log ( 'igloo: started settings component' ); // hide the loading element this.iglooLoading.hide ; if ( this.canDebug === true ) console.log ( 'igloo: completed load!' ); break; }		}		this.shutdown = function ( reason, retry ) { if (reason == null) { reason = ''; } else { reason = ' '+reason; } if (retry == 'retry') { reason += ' retry | help '; } if (retry == 'tryagain') { reason += ' new session '; } if ( typeof this.iglooChanges != 'undefined' ) 	this.iglooChanges.destroy ; if ( typeof this.iglooDiff != 'undefined' ) 	this.iglooDiff.destroy ; if ( typeof this.iglooControls != 'undefined' ) this.iglooControls.destroy ; if ( typeof this.iglooStatus != 'undefined' ) 	this.iglooStatus.destroy ; if ( typeof this.iglooManageSettings != 'undefined' ) 	this.iglooManageSettings.hidedisplay ; if ( typeof this.iglooSession != 'undefined' ) 	clearTimeout ( this.iglooSession.verificationTimer ); if ( typeof this.iglooNet != 'undefined' ) 		clearTimeout ( this.iglooNet.retrievalTimer ); this.iglooLoading.show ; this.iglooLoading.win_height = 0; this.iglooLoading.win_width = 350; this.iglooLoading.win_content = ' igloo has closed'+reason+' '; this.iglooLoading.applyAll ; this.iglooLoading.center ( 'both' ); return true; }	}	function iglooNet { // the iglooNet module retrieves wiki data from the igloo server to help fight vandalism this.initial = true; this.lastUpdate = 0; this.iglooScores = []; this.iglooCount = 0; this.pingFails = 0; this.iglooCommit = 0; this.retrieve = function ( cacheBypass ) { if ( typeof igloo.iglooStatus != 'undefined' ) igloo.iglooStatus.addStatus ( 'Requesting data from iglooNet...' ); this.internalCounter = 0; if ( cacheBypass == true ) { var cache = '&cachebypass=true'; } else { var cache = ''; } iglooImport ( iglooSettings.remoteHost + 'main.php?action=retrieve&pingfails=' + this.pingFails + '' + cache + '&me=' + encodeURIComponent ( wgUserName ) + '&session=' + igloo.iglooSession.session, true, 'iglooRetrieve' ); this.retrieveFailed = setTimeout ( function { igloo.shutdown ( 'userlist retrieval failed - lost connection to iglooNet', 'retry' ); return false; }, iglooSettings.serverTimeout * 3 * 1000 ); //this.retrieveMain ; }		this.retrieveMain = function ( status ) { if ( status === 'ok' ) { clearTimeout ( this.retrieveFailed ); igloo.iglooNet.mergeUpdates ( iglooNetScores ); return true; } else { igloo.shutdown ( 'session invalid or expired', 'tryagain' ); return false; }			/*thisUpdate 		= false; if ( typeof iglooScoresDone != 'undefined' ) if ( iglooScoresDone != 'unknown' ) if ( iglooScoresDone == 'ok' ) { iglooScoresDone = 'unknown'; this.pingFails = 0; igloo.iglooNet.mergeUpdates ( iglooNetScores ); return true; } else { igloo.shutdown ( 'session invalid or expired', 'tryagain' ); return false; }			if ( this.internalCounter >= ( iglooSettings.serverTimeout * 3 ) ) { this.pingFails ++; if ( typeof igloo.iglooStatus !== 'undefined' ) igloo.iglooStatus.addStatus ( ' Server connect failed (action: retrieve; pingfails ' + this.pingFails + ') ' ); if ( this.pingFails >= iglooSettings.permitPingfails ) { igloo.shutdown ( 'userlist retrieval failed - lost connection to iglooNet', 'retry' ); return false; }				if ( igloo.canDebug === true ) console.log ( 'igloo: pingfail on retrieve (number '+this.pingFails+')' ); this.internalCounter = 0; this.retrieveMain ; return false; } else { this.internalCounter ++; thisUpdate 		= this; setTimeout ( "if ( thisUpdate ) { thisUpdate.retrieveMain ; }", 500 ); }*/		}		this.mergeUpdates = function ( newUpdates ) { // the merge updates function takes the data that the iglooNet server has just sent and merges it into the central iglooScores array that is used // to list the diffs to the user. var j = 0; for ( var i in newUpdates ) { // newUpdates[i][0] = u [or] p				// newUpdates[i][1] = score [or] flag // i 				= username [or] pagename if ( ! this.iglooScores[i] ) { // if we don't already have data on this item this.iglooScores[i] = []; this.iglooScores[i] = newUpdates[i]; } else { // if we DO already have data on this item, update it					this.iglooScores[i].length = 0; this.iglooScores[i] = []; this.iglooScores[i] = newUpdates[i]; }				j ++; this.iglooCount ++; }			if ( this.initial == true ) { igloo.launch ; this.initial = false; }			this.retrievalTimer = setTimeout ("igloo.iglooNet.retrieve;", 30 * 1000); }		this.scoreObject = function ( object ) { // generally, administrators/crats will be sent as a risk of 0 and rollbackers will be given 0.2. // trusted users, who have made over 250 edits will be assigned a score of 0.4 - mysteriously meaning // vandalism is likely to appear first, but there will always be an 'untrustworthy' edit to check. // by default, other users and IPs will have 0.5 - the server assigns higher scores based on user // actions var score 	= []; score.length= 0; score[0] 	= false; score[1]	= ''; if ( (igloo.iglooNet.iglooScores[object]) && (igloo.iglooNet.iglooScores[object][0] == 'u') ) { score[0] = igloo.iglooNet.iglooScores[object][1]; }			// if we have special data on this page, such as a whitelist of priority warning, append it here. // pages that are whitelisted will be sent with a priority of 0, regardless of the user score // pages that are flagged will gain a score of 0.7 // pages that are blacklisted will gain a score of 0.9 if ( ( igloo.iglooNet.iglooScores[object] ) && ( igloo.iglooNet.iglooScores[object][0] == 'p' ) ) { switch ( igloo.iglooNet.iglooScores[object][1] ) { case 'w': score[0] = 0; score[1] = 'w'; break; case 'f': score[0] = 0.6; score[1] = 'f'; break; case 'b': score[0] = 0.9; score[1] = 'b'; break; }			}			return score; }		this.colourScore = function ( score, defaultCol ) { defaultCol = typeof ( defaultCol ) != 'undefined' ? defaultCol : '#ffffff'; if ( score === false ) return defaultCol; if (iglooSettings.enableFeedColour == false) { var col = defaultCol; } else if ( score >= 0.8 ) { var col = iglooSettings.flagColours[0]; } else if ( score >= 0.6 ) { var col = iglooSettings.flagColours[1]; } else if ( score >= 0.4 ) { var col = iglooSettings.flagColours[2]; } else if ( score >= 0.2 ) { var col = iglooSettings.flagColours[3]; } else if ( score >= 0 ) { var col = iglooSettings.flagColours[4]; } else { var col = defaultCol; }			return col; }		this.genCode = function ( len ) { var a = new Array ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ), s = '', x = a.length; for ( var i = 0; i < len; i ++ ) { var r = Math.floor ( Math.random * x ); s += a[r]; }			return s;		} this.investigate = function ( user ) { if ( ( typeof user !== 'string' ) || ( iglooSettings.iglooFlags.indexOf ( 't' ) === -1 ) || ( iglooSettings.commitToIgNet === false ) ) return false; var score = this.scoreObject ( user ); if ( ( user.match ( /^[0-9]+\.[0-9]+\.[0-9]+\.?[0-9]*$/i ) === null ) && ( this.iglooCommit >= iglooSettings.commitWhen ) && ( ( score[0] === false ) || ( iglooSettings.recheck === true ) ) ) { this.iglooCommit = 0; // reset counter var url = iglooSettings.remoteHost + 'main.php?action=investigate&session=' + igloo.iglooSession.session + '&me=' + encodeURIComponent ( wgUserName ) +'&user=' + user + ''; iglooImport ( url, true, 'iglooInvestigate' ); } else { this.iglooCommit ++; } }	}	function iglooFilter ( filter ) { // generate filter variables this.conditions = []; this.events = []; this.activeChange = false; this.blockFilters = false; // whether to block further filter execution after this one this.parseFilter = function ( code ) { var t = t2 = t3 = t4 = []; // split into statements t = code.split ( "\n" ); if ( t.length === 0 ) return false; // if no statements // for each statement, extract the commands for ( var i = 0, l = t.length; i < l; i ++ ) { t2 [i] = []; t2 [i] = t [i].split ( ' ' ); // switch the type of statement (i.e. the first command) switch ( t2 [i] [0] ) { case 'if': case 'ifany': if ( this.parseCondition ( t2 [i] ) === false ) return false; break; case 'set': case 'hide': case 'mark': case'blockfilters': if ( this.parseEvent ( t2 [i] ) === false ) return false; break; case '': case '//': //comments break; default: // if we cannot recognise the command, die. return false; break; }			}		}		this.parseCondition = function ( array ) { if ( array.length < 4 ) return false; // insert this condition var useId = this.conditions.length; this.conditions [useId] = {}; // if vs ifany this.conditions [useId] ['type'] = array [0]; if ( array [0] === 'if' ) { switch ( array [1] ) { default: return false; break; case 'summary': case 'oldsize': case 'newsize': case 'changesize': case 'tags': case 'title': case 'user': case 'score': break; } this.conditions [useId] ['target'] = array [1]; if ( this.conditions [useId] ['negate'] = ( ( array [2] === 'NOT' ) || ( array [2] === '!' ) ) ) { var s = 3; } else { var s = 2; } switch ( array [s] ) { default: return false; break; case 'regexmatch': case 'contains': case '==': case '<': case '>': break; } // op limits if ( ( array [1] === 'tags' ) && ( ( array [s] !== 'regexmatch' ) && ( array [s] !== 'contains' ) && ( array [s] !== '==' ) ) ) return false; if ( ( ( array [s] === '<' ) || ( array [s] === '>' ) ) && ( ( array [1] !== 'oldsize' ) && ( array [1] !== 'newsize' ) && ( array [1] !== 'changesize' ) && ( array [1] !== 'score' ) ) ) return false; // fin this.conditions [useId] ['op'] = array [s]; this.conditions [useId] ['value'] = ''; for ( var i = s+1, l = array.length; i < l; i ++ ) { this.conditions [useId] ['value'] += array [i] + ' '; }				this.conditions [useId] ['value'] = this.conditions [useId] ['value'].substr ( 0, this.conditions [useId] ['value'].length - 1 ); }			return true; }		this.parseEvent = function ( array ) { var t = {}; // the command t ['type'] = array [0]; switch ( array [0] ) { default: return false; break; case 'set': // set [score/id] [absolute/relative] value // checks if ( ( array [1] !== 'score' ) && ( array [1] !== 'id' ) && ( array [1] !== 'comment' ) ) return false; // unrecognised if ( ( array [1] !== 'comment' ) && ( array [2] !== 'absolute' ) && ( array [2] !== 'relative' ) ) return false; // unrecognised if ( array [1] === 'score' ) { if ( isNaN ( array [3] = parseFloat ( array [3] ) ) === true ) return false; // not a number array [3] = Math.round ( array [3] * 10 ) / 10; t ['action'] = array [2]; t ['value'] = array [3]; } else if ( array [1] === 'id' ) { if ( isNaN ( array [3] = parseInt ( array [3], 10 ) ) === true ) return false; // not a number if ( ( array [3] > 99 ) || ( array [3] < 0 ) ) return false; // out of range t ['action'] = array [2]; t ['value'] = array [3]; } else { t ['value'] = ''; for ( var i = 2, l = array.length; i < l; i ++ ) { t ['value'] += array [i] + ' '; }						t ['value'] = t ['value'].substr ( 0, t ['value'].length - 1 ); }					// build t ['target'] = array [1]; // what part of the change data do we want to edit? break; }			// insert this event var useId = this.events.length; this.events [useId] = t;			return true; }		this.applyFilter = function ( change ) { this.activeChange = change; if ( this.checkConditions === true ) { this.executeActions ; return this.activeChange; } else { return change; } }		this.checkConditions = function { for ( var i = 0, l = this.conditions.length; i < l; i ++ ) { var m = this.conditions [i]; switch ( m ['type'] ) { case 'if': // convert targets to id codes var target; switch ( m['target'] ) { case 'summary': target = 9; break; case 'changesize': target = 8; break; case 'oldsize': target = 6; break; case 'newsize': target = 7; break; case 'tags': target = 10; break; case 'title': target = 0; break; case 'user': target = 1; break; case 'score': target = 4; break; default: return false; break; }						var meTrue = m['negate'] ? false : true; var meFalse = ! meTrue, r = false; switch ( m['op'] ) { case 'regexmatch': var extractReg = /^\/(.+?)\/([igm]*)$/ig; var regMatch = extractReg.exec ( this.dataReplace ( m['value'] ) ); var regTest = new RegExp ( regMatch [1], regMatch [2] ); extractReg.lastIndex = 0; if ( this.activeChange [target].match ( regTest ) !== null ) { r = meTrue; } else { r = meFalse; } break; case 'contains': if ( this.activeChange [target].indexOf ( this.dataReplace ( m['value'] ) ) > -1 ) { r = meTrue; } else { r = meFalse; } break; case '==': if ( this.activeChange [target] == m['value'] ) { r = meTrue; } else { r = meFalse; } break; case '<': if ( this.activeChange [target] < parseFloat ( m['value'] ) ) { r = meTrue; } else { r = meFalse; } break; case '>': if ( this.activeChange [target] > parseFloat ( m['value'] ) ) { r = meTrue; } else { r = meFalse; } break; default: r = false; break; }						break; }				if ( r === false ) return false; }			return true; }		this.executeActions = function { for ( var i = 0, l = this.events.length; i < l; i ++ ) { var m = this.events [i]; switch ( m ['type'] ) { case 'set': if ( m['target'] === 'comment' ) { if ( typeof this.activeChange [11] !== 'string' ) this.activeChange [11] = ''; this.activeChange [11] += ' ' + this.dataReplace ( m['value'] ); break; }						var existing = this.activeChange [4]; var scoreStr = existing.toString ( 10 ); if ( scoreStr.indexOf ( '.' ) === -1 ) scoreStr += '.0'; var score = scoreStr.substr ( 0, scoreStr.indexOf ( '.' ) + 2 ); var id = scoreStr.substr ( scoreStr.indexOf ( '.' ) + 2 ); if ( id == '' ) id = '00'; if ( id.length === 1 ) id += '0'; if ( m['target'] === 'score' ) { var newScore = parseFloat ( score ); if ( m['action'] === 'relative' ) { newScore += parseFloat ( m['value'] ); newScore = Math.round ( newScore * 10 ) / 10; newId = id; } else { newScore = parseFloat ( m['value'] ); newScore = Math.round ( newScore * 10 ) / 10; newId = id; }						} else { var newId = parseInt ( id, 10 ); if ( m['action'] === 'relative' ) { newId += parseInt ( m['value'], 10 ); if ( newId > 99 ) newId = 99; if ( newId < 0 ) newId = 0; newId = newId.toString ( 10 ); newScore = Math.round ( parseFloat ( score ) * 10 ) / 10; } else { newId = parseInt ( m['value'], 10 ); newId = newId.toString ( 10 ); newScore = Math.round ( parseFloat ( score ) * 10 ) / 10; }						}						if ( ( parseInt ( newId, 10 ) < 10 ) && ( newId.substr ( 0, 1 ) !== '0' ) ) newId = '0' + newId; var final = parseFloat ( newScore + '' + newId ); this.activeChange [4] = final; break; case 'blockfilters': this.blockFilters = true; break; }			}		}		this.dataReplace = function ( string ) { var regTest = /%DATA([0-9A-Z]+)%/g, c = this.activeChange; regTest.lastIndex = 0; var o = string.replace ( regTest, function ( m, m2 ) { 				if ( m2 === 'ME' ) {					return wgUserName;				} else {					if ( c[m2] === null ) return false;					return c[m2].toString;				}			 } ); return o;		} // on create this.parseSuccess = this.parseFilter ( filter ); // return parse success return true; }	function iglooSecurity { this.userActivity = true; this.pingFails = 0; if ( (window.location.href.indexOf('&sessionkey=') == -1) || (window.location.href.indexOf('::::') == -1) ) { igloo.shutdown('invalid session provided', 'tryagain'); this.shutdown = true; return false; } this.session = window.location.href.substr(window.location.href.indexOf('&sessionkey=') + 12); this.session = this.session.substr(0, this.session.length - 4); this.verifySession = function ( type ) { this.internalCounter = 0; if ( this.userActivity == true ) { var alive = '&keep-alive=true'; } else { var alive = '&keep-alive=false'; } this.userActivity = false; iglooImport( iglooSettings.remoteHost + 'main.php?action=verify&pingfails=' + this.pingFails + '&user=' + encodeURIComponent ( wgUserName ) + '&verify=' + this.session + alive, true, 'iglooVerify' ); this.checkVerification ( type ); }		this.checkVerification = function ( type ) { if ( typeof iglooSessionVerified != 'undefined' ) if ( iglooSessionVerified != 'unknown' ) if ( iglooSessionVerified == 'ok' ) { this.pingFails = 0; iglooSessionVerified = 'unknown'; this.verificationTimer = setTimeout("igloo.iglooSession.verifySession;", 45 * 1000); if ( type == 'initial' ) { igloo.launch; }				return true; } else { igloo.shutdown('session invalid or expired', 'tryagain'); return false; }			if ( this.internalCounter >= iglooSettings.serverTimeout * 2 ) { this.pingFails ++; if ( type != 'initial' ) igloo.iglooStatus.addStatus ( ' Server connect failed (action: verify; pingfails ' + this.pingFails + ') ' ); if ( this.pingFails >= iglooSettings.permitPingfails ) { igloo.shutdown ( 'verification failed - lost connection to iglooNet', 'retry' ); }				return false; } else { this.internalCounter ++; setTimeout("igloo.iglooSession.checkVerification('"+type+"');", 1000); }		}	}	function iglooManageSettings { // the iglooManageSettings module retrieves settings from the igloo server, and replaces the defaults where applicable. this.settingsEnabled = true; this.retrieve = function { this.internalCounter = 0; iglooImport ( iglooSettings.remoteHost + 'main.php?action=settings&me=' + encodeURIComponent(wgUserName) + '&do=get&session=' + igloo.iglooSession.session, true, 'iglooNetSettings' ); this.retrieveMain ; }		this.retrieveMain = function { thisSetting = false; if ( typeof iglooNetSettingsDone != 'undefined' ) if ( iglooNetSettingsDone != 'unknown' ) if ( iglooNetSettingsDone == 'ok' ) { // success this.overwritelocal ; this.managefilters ; // continue launch igloo.launch ; return true; } else { igloo.shutdown( 'session invalid or expired', 'tryagain' ); return false; }			if ( this.internalCounter >= ( iglooSettings.serverTimeout * 2 ) ) { igloo.shutdown( 'settings failed - lost connection to iglooNet', 'retry' ); return false; } else { this.internalCounter ++; thisSetting = this; setTimeout( "if ( thisSetting ) { thisSetting.retrieveMain ; }", 500 ); }		}		this.overwritelocal = function { for ( i in iglooNetSettings ) { if ( iglooSettings [i] !== undefined ) { iglooSettings [i] = iglooNetSettings [i]; }			}			return true; }		this.managefilters = function { for ( var i = 0, l = iglooSettings.filterList.length; i < l; i ++ ) { iglooSettings.filterList[i][4] = new iglooFilter ( iglooSettings.filterList[i][3] ); if ( iglooSettings.filterList[i][4].parseSuccess === false ) { alert ( 'Warning: parse error; igloo filter failed to parse, global filter ID ' + iglooSettings.filterList[i][0] ); } }			return true; }		this.set = function ( setting, value ) { if ( this.settingsEnabled === true ) { iglooImport ( iglooSettings.remoteHost + 'main.php?action=settings&me=' + encodeURIComponent ( wgUserName ) + '&do=set&setting=' + encodeURIComponent ( setting ) + '&value=' + encodeURIComponent ( value ) + '&session=' + igloo.iglooSession.session, true, 'iglooNetDoSet' ); this.settingsEnabled = false; return true; } else { alert ( 'Could not alter setting - previous requests are still being processed. Please wait, then try again.' ); return false; }		}		this.freeSettings = function { this.settingsEnabled = true; }		this.start = function { this.mainContent = ' '; if ( igloo.canDebug === true ) console.log ( 'igloo: prepped settings main' ); }		this.showdisplay = function { // generate interface igloo.iglooPopup.show ( this.mainContent ); // add tabs this.addtab ( 'info', 'user info' ); this.addtab ( 'general', 'general' ); this.addtab ( 'interface', 'interface' ); //this.addtab ( 'actions', 'actions' ); this.addtab ( 'filters', 'filters' ); if ( iglooSettings.mesysop ) this.addtab ( 'admin', 'admin' ); this.addtab ( 'close', 'close' ); // default tab this.switchtab ( 'info' ); // dynamic key blocking iglooSettings.dynamicBlockKeys = 'settings'; return true; }		this.hidedisplay = function { igloo.iglooPopup.hide ; iglooSettings.dynamicBlockKeys = 'default'; return true; }		this.addtab = function ( tabid, tabtext ) { if ( ( ! tabid ) || ( ! tabtext ) ) return false; var tabscont = document.getElementById ( 'igloo-settings-tabs' ); tabscont.innerHTML += ' ' + tabtext + ' '; return tabscont; }		this.genFilterText = function { var filterText = ''; for ( var i = 0, l = iglooSettings.filterList.length; i < l; i ++ ) { var t = iglooSettings.filterList [i], optionsString = 'this filter has a problem'; var disp = t[3].replace ( /\\/g, '\\\\' ); disp = disp.replace ( /\n/g, '\\n' ); disp = disp.replace ( /\'/g, '\\\'' ); //this.disEnString = new Array ; //this.disEnString[i] = ( t[1] === true ) ? 'iglooImport ( \+iglooSettings.remoteHost+'main.php?action=settings&me=' + encodeURIComponent ( wgUserName ) + '&do=set&setting=disablefilter&value='+t[0]+'\', true, \'iglooFilter\' ); iglooSettings.filterList['+i+'][1] = false;' : 'iglooImport ( \+iglooSettings.remoteHost+'main.php?action=settings&me=' + encodeURIComponent ( wgUserName ) + '&do=set&setting=enablefilter&value='+t[0]+'\', true, \'iglooFilter\' ); iglooSettings.filterList['+i+'][1] = true;'; var disEnString = ( t[1] === true ) ? 'Disable' : 'Enable'; //disEnString = disEnString.replace ( /'/g, '\\\'' ); if ( t[2] !== 'default' ) { var saveString = ''; optionsString = ''+i+':'+t[0]+' '+disp+'  \'; igloo.iglooManageSettings.attachfilterbuttons ( '+i+' );" onmouseover="this.style.textDecoration=\'underline\';" onmouseout="this.style.textDecoration=\'none\';">edit '; optionsString += ' | delete '; } else { optionsString = ''+i+':'+t[0]+' '+disp+'  \'; igloo.iglooManageSettings.attachfilterbuttons ( '+i+' );" onmouseover="this.style.textDecoration=\'underline\';" onmouseout="this.style.textDecoration=\'none\';">view filter (cannot edit) '; }				var col = ( t[1] === true ) ? '' : 'background-color: #ffbbbb;'; filterText += ''; filterText += '- filter ' + t[0] + ' (' + optionsString + ')'; filterText += ''; }			if ( i === 0 ) filterText = 'no filters</li>'; return filterText; }		this.togglefilterenabled = function ( to ) { var t = document.getElementById ( 'igloo-toggle-filter-disable' ), id = document.getElementById ( 'igloo-filter-current-id' ).innerHTML.split ( ':' ); id = parseInt ( id[0], 10 ); if ( to === 'enabled' ) { iglooImport ( iglooSettings.remoteHost+'main.php?action=settings&me=' + encodeURIComponent ( wgUserName ) + '&do=set&setting=enablefilter&value=' + iglooSettings.filterList[id][0] + '&session=' + igloo.iglooSession.session, true, 'iglooFilter' ); iglooSettings.filterList[id][1] = true; t.value = 'Disable this filter'; t.onclick = function { igloo.iglooManageSettings.togglefilterenabled ( 'disabled' ); document.getElementById ( 'igloo-filter-list' ).innerHTML = igloo.iglooManageSettings.genFilterText ; } } else { iglooImport ( iglooSettings.remoteHost+'main.php?action=settings&me=' + encodeURIComponent ( wgUserName ) + '&do=set&setting=disablefilter&value=' + iglooSettings.filterList[id][0] + '&session=' + igloo.iglooSession.session, true, 'iglooFilter' ); iglooSettings.filterList[id][1] = false; t.value = 'Enable this filter'; t.onclick = function { igloo.iglooManageSettings.togglefilterenabled ( 'enabled' ); document.getElementById ( 'igloo-filter-list' ).innerHTML = igloo.iglooManageSettings.genFilterText ; } }			return true; }		this.attachfilterbuttons = function ( id ) { if ( id !== 'new' ) { var t = document.getElementById ( 'igloo-toggle-filter-disable' ); if ( iglooSettings.filterList[id][1] === true ) { // enabled, so disable t.onclick = function { igloo.iglooManageSettings.togglefilterenabled ( 'disabled' ); document.getElementById ( 'igloo-filter-list' ).innerHTML = igloo.iglooManageSettings.genFilterText ; } } else { // disabled, so enable t.onclick = function { igloo.iglooManageSettings.togglefilterenabled ( 'enabled' ); document.getElementById ( 'igloo-filter-list' ).innerHTML = igloo.iglooManageSettings.genFilterText ; } }			}			if ( document.getElementById ( 'igloo-filter-text' ) !== null ) { t = document.getElementById ( 'igloo-filter-text' ); t.onkeyup = function { var t2 = new iglooFilter ( document.getElementById ( 'igloo-filter-text' ).value ); if ( t2.parseSuccess === false ) { var t3 = document.getElementById ( 'igloo-save-filter' ); t3.disabled = true; t3.value = 'Parse error: filter is invalid'; } else { var t3 = document.getElementById ( 'igloo-save-filter' ); t3.disabled = false; if ( document.getElementById ( 'igloo-filter-current-id' ).innerHTML.indexOf ( 'new' ) > -1 ) { t3.value = 'Create new filter'; } else { t3.value = 'Save filter changes'; }					}				}			}			if ( document.getElementById ( 'igloo-save-filter' ) !== null ) { t = document.getElementById ( 'igloo-save-filter' ); t.onclick = function { this.disabled = true; var id = document.getElementById ( 'igloo-filter-current-id' ).innerHTML.split ( ':' ); var data = document.getElementById ( 'igloo-filter-text' ).value; // if new if ( id[1] === 'new' ) { var t = [], n = true; t [0] = igloo.iglooNet.genCode ( 5 ); t [1] = true; t [2] = wgUserName; t [3] = data; t [4] = new iglooFilter ( t [3] ); id [1] = t [0]; iglooSettings.filterList.push ( t ); } else { var t = iglooSettings.filterList [ parseInt ( id[0], 10 ) ], n = false; t [3] = data; t [4] = new iglooFilter ( t [3] ); iglooSettings.filterList [ parseInt ( id[0], 10 ) ] = t;					} iglooImport ( iglooSettings.remoteHost+'main.php?action=settings&me=' + encodeURIComponent ( wgUserName ) + '&do=set&setting=editfilter&id=' + id [1] + '&value=' + encodeURIComponent ( data ) + '&session=' + igloo.iglooSession.session, true, 'iglooFilter' ); if ( n === true ) { igloo.iglooManageSettings.switchtab ( 'filters' ); } else { document.getElementById ( 'igloo-filter-list' ).innerHTML = igloo.iglooManageSettings.genFilterText ; } }			}		}		this.switchtab = function ( tabid ) { if ( ! tabid ) { throw 'igloo: unexpected settings call, tab is missing'; return false; } var tabcont = document.getElementById ( 'igloo-settings-content' ), v;			switch ( tabid ) { case 'info': tabcont.innerHTML = ''; // blank tabcont.innerHTML += ' Welcome to the igloo settings panel. From here, you can update your igloo settings - there is no need to save your changes or restart igloo as any alterations will take place immediately. igloo currently has the following data regarding your account:

- username: ' + wgUserName + ' - recognised usergroup: ' + iglooSettings.userGroup + ' - igloo user flags: ' + iglooSettings.iglooFlags + ' - igloo trust status: N/A - total sessions: ' + iglooSettings.totalSessions + ' - last connected from: ' + iglooSettings.lastConnectIp + ' - last connected: ' + iglooSettings.lastConnectTime + ' ';					break; case 'general': tabcont.innerHTML = ''; // blank var cont = ''; cont += ' Change general igloo settings here. ';					tabcont.innerHTML = cont; break; case 'interface': tabcont.innerHTML = ''; // blank var cont = ''; cont += ' Change igloo interface settings here. ';					tabcont.innerHTML = cont; break; case 'admin': tabcont.innerHTML = ''; // blank var cont = ''; cont += ' This is the igloo admin settings panel - here, you can change settings related to performing administrative actions using igloo. ';					tabcont.innerHTML = cont; break; case 'filters': // build filterText var filterText = this.genFilterText ; // build display var cont = ''; // blank cont += ' To create a new filter, click the \'new filter\' button. Be aware that poorly designed filters can have an adverse effect on performance. You cannot edit the default filters. ';					tabcont.innerHTML = cont; break; case 'close': this.hidedisplay ; break; default: throw 'igloo: unexpected settings call, tab content undefined'; break; }		}	}	function iglooChanges { // the iglooChanges object is the object that retrieves, sorts, manages and displays changes from Wikipedia this.recentChanges = [];	// main array, holding the changes this.viewedChanges = [];	// main array, holding diffs that should NOT be displayed (we've seen them already) this.serverDetails = [];	// main array, holding details from the server regarding users and pages this.start = function { // start the changed program this.startInterface ; this.startTicker ; }		this.startTicker = function { // perform the refresh ticks that grab info from the server this.updateContent ; this.refreshTicker = setInterval ( "igloo.iglooChanges.updateContent;", iglooSettings.updateTime * 1000 ); }		this.startInterface = function { // this creates the basic interface into which changes are placed when they have been received. // essentially, it is a div that will contain clickable list elements. this.changeDisplay = new wa_window ( igloo.iglooInterface ); this.changeDisplay.win_width = 190; this.changeDisplay.win_height = parseInt ( igloo.iglooInterface.win_obj.style.height ) - 50; this.changeDisplay.win_bg = '#ffffff'; this.changeDisplay.win_bd_rt = '1px solid #000000'; this.changeDisplay.win_bd_bt = '1px solid #000000'; this.changeDisplay.win_padding = 0; this.changeDisplay.win_content = '<ul style="font-size: 10px; width: 100%; height: 100%; margin: 0px; padding: 0px; overflow-x: hidden; overflow-y: auto;" id="iglooChangesList"></ul>'; this.changeDisplay.applyAll ; this.logoDisplay = new wa_window ( igloo.iglooInterface ); this.logoDisplay.win_top = parseInt ( igloo.iglooInterface.win_obj.style.height ) - 50; this.logoDisplay.win_width = 190; this.logoDisplay.win_height= 50; this.logoDisplay.win_bg = '#ffffff'; this.logoDisplay.win_bd_tp = '1px solid #000000'; this.logoDisplay.win_padding = 0; this.logoDisplay.win_content = '<img style="position: relative; top: -15px; left: 20px;" src="' + iglooSettings.remoteHost + 'images/igloo-logo.png" />'; this.logoDisplay.applyAll ; }		this.addToViewed = function ( revid ) { // mark an oldid as viewed so we do not display it more than once in the display window. var sizeLimit = 30; this.viewedChanges.push ( revid ); if ( this.viewedChanges.length > sizeLimit ) this.viewedChanges.shift ; return true; }		this.markViewed = function ( revid ) { this.addToViewed ( revid ); // next, remove it from the recentChanges array, and redisplay that. for ( var i = 0; i < this.recentChanges.length; i ++ ) { if ( this.recentChanges[i][2] == revid ) { this.recentChanges.splice(i, 1); var change = i;				} }			this.displayChanges ; }		this.updateContent = function { var update = new iglooUpdateChanges ; }		this.displayChanges = function { var visibleChangeOutput = ''; for ( var i = 0; i < this.recentChanges.length; i ++ ) { if ( typeof this.recentChanges[i][4] == 'undefined' ) this.recentChanges[i][4] = 0.5; var backgroundColour = igloo.iglooNet.colourScore ( this.recentChanges[i][4] ); if ( this.recentChanges[i][3] == true ) { var newPage = ' N '; } else { var newPage = ''; } visibleChangeOutput += '<li onclick="igloo.iglooControls.action_loadFromFeed(\+this.recentChanges[i][0].replace (/'/g, '\\\)+'\', \+this.recentChanges[i][1].replace (/'/g, '\\\)+'\', \+this.recentChanges[i][2].replace (/'/g, '\\\)+'\', '+this.recentChanges[i][3]+', '+this.recentChanges[i][4]+', \+this.recentChanges[i][11].replace (/'/g, '\\\)+'\');" onmouseover="this.style.backgroundColor = \'#dddddd\';" onmouseout="this.style.backgroundColor = \''+backgroundColour+'\';" style="cursor: pointer; width: 186px; padding: 2px; border-bottom: 1px solid #000000; list-style-type: none; list-style-image: none; marker-offset: 0px; background-color: '+backgroundColour+';">'+newPage+this.recentChanges[i][0]+'</li>'; }			document.getElementById('iglooChangesList').innerHTML = visibleChangeOutput; }		this.destroy = function { // calling this will destroy the interface and connections of this object. this.changeDisplay.hide ; this.logoDisplay.hide ; if ( typeof this.refreshTicker != 'undefined' ) clearInterval ( this.refreshTicker ); }	}	function iglooUpdateChanges { // the iglooUpdate object handles making a request to the server, and returning an array of data. this.updateCounter 	= 0; this.update = function { switch ( this.updateCounter ) { default: case 0: // first, get some recent changes var aRequest = this; this.ajax 				= new wa_ajaxcall ; this.ajax.requestUrl 	= iglooSettings.rootApi + '?format=xml&action=query&list=recentchanges&rcprop=user|title|ids|sizes|comment|tags&rctype=edit|new&rclimit=' + iglooSettings.updateQuantity; this.ajax.doRequest ( function {												 		aRequest.updateCounter ++;														aRequest.update ;												 }); break; case 1: // recent changes are held such that: // [0] = title // [1] = user // [2] = oldid // [3] = new page? // [4] = user vandal score // [5] = priority note - a priority note is assigned to pages that have priority for some reason. These will be displayed at the top of the list, regardless of the score of the user. // [6] = old length // [7] = new length // [8] = changed length // [9] = comment // [10] = tag string // [11] - igloo change comments (can be set using filters) this.tempChanges = []; var data = this.ajax.response.getElementsByTagName ( 'rc' ); for (var i = 0; i < data.length; i++) { this.tempChanges[i] 	= []; this.tempChanges[i][0]	= data[i].getAttribute ( 'title' ); this.tempChanges[i][1]	= data[i].getAttribute ( 'user' ); this.tempChanges[i][2]	= data[i].getAttribute ( 'revid' ); if ( data[i].getAttribute ( 'type' ) == 'edit' ) { this.tempChanges[i][3]	= false; } else { this.tempChanges[i][3]	= true; }						this.tempChanges[i][4]	= 0; this.tempChanges[i][5]	= 0; this.tempChanges[i][6]	= parseInt ( data[i].getAttribute ( 'oldlen' ) ); this.tempChanges[i][7]	= parseInt ( data[i].getAttribute ( 'newlen' ) ); this.tempChanges[i][8]	= this.tempChanges[i][7] - this.tempChanges[i][6]; this.tempChanges[i][9]	= data[i].getAttribute ( 'comment' ); var t = data[i].childNodes[0].childNodes, s = '::'; for ( var j = 0, l = t.length; j < l; j ++ ) { s += t[j].childNodes[0].wholeText + '::'; }						this.tempChanges[i][10] = s;						this.tempChanges[i][11] = ''; }					this.updateCounter ++; this.update ; break; case 2: // we have the details - build the temporary changes array var score = []; for ( var i = 0; i < this.tempChanges.length; i ++ ) { // if we have special data on this user, such as a priority score, append it here. // check for pages score.length = 0; score = igloo.iglooNet.scoreObject ( this.tempChanges[i][0] ); // check for users if ( score[0] === false ) { score.length = 0; score = igloo.iglooNet.scoreObject ( this.tempChanges[i][1] ); }						// backup if ( score[0] === false ) score[0] = iglooSettings.defaultUserScore; this.tempChanges[i][4] = score[0]; this.tempChanges[i][5] = score[1]; // apply any relevant filters to the changes we've loaded if ( iglooSettings.enableFilters === true ) { for ( var j = 0, l = iglooSettings.filterList.length; j < l; j ++ ) { var t = iglooSettings.filterList [j]; if ( ( t[1] === false ) || ( t[4].parseSuccess === false ) ) continue; this.tempChanges[i] = t[4].applyFilter ( this.tempChanges[i] ); if ( t[4].blockFilters === true ) { t[4].blockFilters = false; break; }							}						}					}					// we want to remove all duplicates from the temp array, by copying them into // another temp array only if they are not already there. var tempArray = []; for ( var i = 0; i < this.tempChanges.length; i ++ ) { var t = false; for ( var j = 0; j < tempArray.length; j ++ ) { if ( this.tempChanges[i][0] == tempArray[j][0] ) { t = true; break; }						}						if ( t === false ) tempArray.push ( this.tempChanges[i] ); }					// now, remove any elements that are on the viewed list that we do not wish to display to the user OR that were made by our user var limit = tempArray.length for (var i = 0; i < limit; i ++) { if ( ( in_array ( tempArray[i][2], igloo.iglooChanges.viewedChanges ) == true ) || ( ( tempArray[i][1] == wgUserName ) && ( iglooSettings.hideOwn == true ) ) ) { tempArray.splice ( i, 1 ); i --; limit --; } else { if ( ( tempArray[i][0] == igloo.iglooDiff.currentDiff[0] ) && ( igloo.iglooActions.reversionEnabled !== 'pause' ) ) { // if this is the same title as the page we're viewing, update the display igloo.iglooChanges.addToViewed ( tempArray[i][2] ); igloo.iglooDiff.display ( tempArray[i][0], tempArray[i][1], tempArray[i][2], tempArray[i][3], tempArray[i][4], tempArray[i][11] ); igloo.iglooDiff.haschanged = true; tempArray.splice ( i, 1 ); i --; limit --; }						}					}					// we now have the full array of scores // go through the main recent changes array, check if there are any pages that match // here and there, delete them from the main array, then merge this one into the main one var i = 0; while ( typeof igloo.iglooChanges.recentChanges[i] != 'undefined' ) { for ( var j = 0; j < tempArray.length; j ++ ) { if ( typeof igloo.iglooChanges.recentChanges[i] == 'undefined' ) break; if ( igloo.iglooChanges.recentChanges[i][0] == tempArray[j][0] ) { igloo.iglooChanges.recentChanges.splice ( i, 1 ); i --; }						}						i ++; }					// recent changes array now holds all the data it did before EXCEPT titles that conflict with the new changes // merge the new changes into the old ones igloo.iglooChanges.recentChanges = tempArray.concat ( igloo.iglooChanges.recentChanges ); // now, sort the array based on the float values :)					igloo.iglooChanges.recentChanges = sort_array_multi ( igloo.iglooChanges.recentChanges, 4, 'descending' );					// check that we aren't over the hard limit for number of changes. If we are, remove those least likely to be vandalism.					if ( igloo.iglooChanges.recentChanges.length > iglooSettings.updateLimit ) {						igloo.iglooChanges.recentChanges = igloo.iglooChanges.recentChanges.splice ( 0, iglooSettings.updateLimit ); }					// now, we have to display the changes to the user! 					igloo.iglooChanges.displayChanges ;					break;			}		}		this.update;	}	function iglooDiff {		// the iglooDiff module is the object that displays and styles Wikiepdia diffs in the main window		this.viewingDiff	= false;		this.previousDiff 	= [];		this.currentDiff 	= [];		this.displayCounter = 0;		this.displayHistory = [];		this.historyPosition = 0; this.canAddToHist = true; this.start = function { // this functions creates the diff window for future use, and displays the latest news in it :)			this.startInterface ;			this.displayWelcome ;			this.warnedParser = false;		}		this.startInterface = function  {			// this creates the basic interface into which changes are placed when they have been received.			// essentially, it is a div that will contain clickable list elements.			this.scoreDisplay = new wa_window ( igloo.iglooInterface );			this.scoreDisplay.win_left = 190;			this.scoreDisplay.win_top = 79;			this.scoreDisplay.win_width = parseInt ( igloo.iglooInterface.win_obj.style.width ) - 200;			this.scoreDisplay.win_height= 11;			this.scoreDisplay.win_bg = '#ffffff';			this.scoreDisplay.win_bd_bt = '1px solid #000000';			this.scoreDisplay.win_padding = 2;			this.scoreDisplay.win_fontsize = 10;			this.scoreDisplay.win_content = 'You are not yet viewing a diff'; this.scoreDisplay.applyAll ; this.diffDisplay = new wa_window ( igloo.iglooInterface ); this.diffDisplay.win_left = 190; this.diffDisplay.win_top = 95; this.diffDisplay.win_width = parseInt ( igloo.iglooInterface.win_obj.style.width ) - 200; this.diffDisplay.win_height= parseInt ( igloo.iglooInterface.win_obj.style.height ) - 255; this.diffDisplay.win_bg = '#ededff'; this.diffDisplay.win_padding = 5; this.diffDisplay.win_content = ' DIFFS INIT '; this.diffDisplay.applyAll ; }		this.displayWelcome = function { // this function specifically displays the welcome message in the diff window var welcomeRequest = new wa_mediawikiApi ; welcomeRequest.onCompleteAction = function ( data ) { var regTest = /welcome:(.+?);;/i, o;																	regResult = regTest.exec( this.data['page']['revisions'][0]['content'] ); o = regResult[1].replace ( '%CURRENTVERSION%', iglooSettings.version ); document.getElementById( 'iglooDiffDisplay' ).innerHTML = o;															}; welcomeRequest.getPage ( iglooSettings.localBase + 'config', 1, 'content' ); }		this.display = function ( title, user, revisionId, newPage, diffScore, iglooNetComment ) { // this function displays the diff of an edit on the screen for the user // the user has displayed activity by initiating a diff display. this.viewingDiff = true; this.haschanged = false; igloo.iglooSession.userActivity = true; // update the holding variables. Some of these may be absent based on the origin of the display call. if ( typeof this.currentDiff[0] != 'undefined' ) { this.previousDiff[0] = this.currentDiff[0]; this.previousDiff[1] = this.currentDiff[1]; this.previousDiff[2] = this.currentDiff[2]; this.previousDiff[3] = this.currentDiff[3]; this.previousDiff[4] = this.currentDiff[4]; this.previousDiff[5] = this.currentDiff[5]; } this.currentDiff[0] = title; this.currentDiff[1] = user; this.currentDiff[2] = revisionId; this.currentDiff[3] = newPage; this.currentDiff[4] = diffScore; this.currentDiff[5] = iglooNetComment; // add to history this.manageHist ; // investigate if ( this.currentDiff [1] ) igloo.iglooNet.investigate ( this.currentDiff [1] ); // decide upon the request to perform - is this a specific request, or a general one? if ( ! revisionId ) { revisionId = false; var url = mw.config.get('wgServer') + mw.config.get('wgScript') + '?title=' + encodeURIComponent ( title ) + '&diff=cur&diffonly=true&redirect=no'; } else { var url = mw.config.get('wgServer') + mw.config.get('wgScript') + '?oldid=' + revisionId + '&diff=prev&diffonly=true&redirect=no'; }			var aDisplay = this; this.ajax = new wa_ajaxcall ; this.ajax.requestUrl = url; this.ajax.doRequest ( function {  										aDisplay.parseScreenScrape ;									}); }		this.manageHist = function { // add the PREVIOUS page to the display history. if ( ( this.currentDiff[0] ) && ( this.currentDiff[1] ) && ( this.currentDiff[2] ) ) if ( this.canAddToHist == true ) { // first, remove any history between the current position and 0. if ( this.historyPosition > 0 ) { var temp = []; temp.length = 0; temp = this.displayHistory.slice(this.historyPosition); this.displayHistory.length = 0; this.displayHistory = temp; this.historyPosition = 0; }				// then add the page var histEntry = []; histEntry.length = 0; histEntry[0] = this.currentDiff[0]; histEntry[1] = this.currentDiff[1]; histEntry[2] = this.currentDiff[2]; this.displayHistory.unshift ( histEntry ); if ( this.displayHistory > iglooSettings.maxHistory ) this.displayHistory.length = iglooSettings.maxHistory; }			this.canAddToHist = true; // handle greying of invalid options var backButton 		= document.getElementById('igloo-buttons-back-b'); var forwardButton 	= document.getElementById('igloo-buttons-forward-b'); var backUrl			= '' + iglooSettings.remoteHost + 'images/igloo-back'; var forwardUrl		= '' + iglooSettings.remoteHost + 'images/igloo-forward'; var grey 			= '-grey'; var filetype		= '.png'; if (this.displayHistory.length <= 1) { backButton.src = backUrl + grey + filetype; forwardButton.src = forwardUrl + grey + filetype; } else if ( (this.displayHistory.length > 1) && (this.historyPosition == 0) ) { backButton.src = backUrl + filetype; forwardButton.src = forwardUrl + grey + filetype; } else if ( (this.displayHistory.length > 1) && (this.historyPosition == (this.displayHistory.length - 1)) ) { backButton.src = backUrl + grey + filetype; forwardButton.src = forwardUrl + filetype; } else { backButton.src = backUrl + filetype; forwardButton.src = forwardUrl + filetype; } }		this.goBack = function ( count ) { if ( this.displayHistory.length <= 0 ) return false; if ( ! count ) count = 1; if ( ( this.historyPosition + count ) > this.displayHistory.length ) count = this.displayHistory.length; this.historyPosition += count; var doView = this.displayHistory [this.historyPosition]; this.canAddToHist = false; this.display ( doView[0], doView[1], doView[2] ); return true; }		this.goForward = function(count) { if (this.historyPosition <= 0) return false; if (!count) count = 1; if ( (this.historyPosition - count) < 0 ) { this.historyPosition = 0; } else { this.historyPosition -= count; } var doView = this.displayHistory[this.historyPosition]; this.canAddToHist = false; this.display(doView[0], doView[1], doView[2]); }		this.parseScreenScrape = function ( html ) { if ( html == null ) { if ( typeof this.ajax.responseXML !== 'undefined' ) { html = this.ajax.responseXML; var tryParse = false; } else { html = this.ajax.responseText; var tryParse = true; } }			if ( ! html ) return false; // CSS details: object widths var markerwidth = 14; var contentwidth = ( this.diffDisplay.win_width / 2 ) - markerwidth; // step 0 - build an XML document from the HTML for IE - cos it's rubbish var ie = false, err = false, t = 'nothing'; if ( ( ! html.getElementById ) && ( tryParse === false ) ) { // we're hacking for IE				ie = true; // get, then clean, the responseText html = this.ajax.pageRequest.responseText; html = html.substr ( html.indexOf ( '<html' ) ); html = html.replace ( / /g, '&#160;' ); var doc = ie_create_document ; var test = doc.loadXML ( html ); if ( doc.parseError.errorCode != 0 ) { alert ( 'Internet Explorer failed to parse the incoming page because igloo sanitised it incorrectly. XML parser returned:'+"\n"+doc.parseError.reason ); return false; }				html = doc; } else { // perform error checking on the retrieved document if ( html.getElementsByTagName ) { var err = html.getElementsByTagName ( 'parsererror' ), attempt; err = err.length; } else { var err = 1, attempt; } if ( err > 0 ) { if ( typeof this.ajax.responseText !== 'undefined' ) { attempt = this.ajax.responseText; // sanitise XML attempt = attempt.replace ( / /g, '&#160;' ); attempt = attempt.replace ( /&amp;/g, '&#38;' ); attempt = attempt.replace ( /&reg;/g, '&#174;' ); attempt = attempt.replace ( /&copy;/g, '&#169;' );

// final replacements attempt = attempt.replace ( /&([^#])/g, '&#38;$1' ); if ( window.DOMParser ) { var parser = new DOMParser ; html = parser.parseFromString ( attempt, 'text/xml' ); if ( html.getElementsByTagName ( 'parsererror' ).length > 0 ) { err = true; t = html.getElementsByTagName ( 'parsererror' )[0].firstChild.nodeValue; } else { if ( this.warnedParser !== true ) igloo.iglooStatus.addStatus ( ' Warning: igloo was forced to sanitise the incoming page because it contained invalid XML. This is most commonly caused by a faulty, recent change to the Wikipedia page layout and may require reporting if you frequently receive this error. ' ); this.warnedParser = true; }						} else { err = true; }					} else { err = true; }				}			}			if ( err === true ) { // clean document.getElementById('iglooDiffDisplay').innerHTML = ''; // the container div var innerDiv = document.createElement ( 'div' ); innerDiv.setAttribute ( 'id', 'iglooInnerDiv' ); innerDiv.style.margin = 'auto'; t = t.replace ( '<', '&lt;' ); t = t.replace ( '>', '&gt;' ); t = t.replace ( "\n", ' ' ); innerDiv.innerHTML = ' igloo error: an unrecoverable XML parsing error occured on the page you attempted to view. This is most commonly caused by a faulty, recent change to the Wikipedia page layout. If you are receiving this error frequently, you should stop using igloo and report the problem to User:Ale_jrb. Details: ' + t;

// display everything in the window document.getElementById('iglooDiffDisplay').appendChild ( innerDiv ); document.getElementById('iglooDiffDisplay').innerHTML += ' '; return false; }			// step 1 - build an array of the data that is displayed by the diff screen // this.diffData[0] = old // this.diffData[1] = new // #[0] = revision as of				// #[1] = username // #[2] = comment/summary this.diffData = []; this.diffData[0] = []; this.diffData[1] = []; for ( var i = 0; i < 3; i ++ ) { if ( html.getElementById ) { // good browsers if ( html.getElementById ( 'mw-diff-otitle' + ( i + 1 ) ) != null ) { this.diffData[0][i] = echo_nodes_recursive ( html.getElementById ( 'mw-diff-otitle' + ( i + 1 ) ) ); } if ( html.getElementById ( 'mw-diff-ntitle' + ( i + 1 ) ) != null ) { this.diffData[1][i] = echo_nodes_recursive ( html.getElementById ( 'mw-diff-ntitle' + ( i + 1 ) ) ); } } else { // the trash that is IE					this.diffData[0][i] = ie_getElementById ( html, 'mw-diff-otitle' + ( i + 1 ) ).text; this.diffData[1][i] = ie_getElementById ( html, 'mw-diff-ntitle' + ( i + 1 ) ).text; }			}			// step 2 - remove that from the visible diff var allTables = html.getElementsByClassName ( 'diff' ); for ( var i = 0; i < allTables.length; i ++ ) { if ( allTables[i].className == 'diff' ) break; }			if ( i >= allTables.length ) i = ( allTables.length - 1 ); if (i < 0 ) { var newPage = true; } else { var newPage = false; } if ( newPage == false ) { var table = document.createElement ( 'table' ); table.className = 'diff'; // in order to add content to the table in IE, we must first add it to a diff. We also handle styling IE output here. if ( ie ) { var tempIEDiv 		= document.createElement ( 'div' ); var tempIETable 	= ie_cloneNode ( allTables[i] ); tempIEDiv.appendChild ( tempIETable ); //tempIEDiv.innerHTML = this.flag(tempIEDiv.innerHTML); tempIEDiv.innerHTML = tempIEDiv.innerHTML.replace(/class='?diff-context'?/ig, 			'style="background-color: #ddddff;"'); tempIEDiv.innerHTML = tempIEDiv.innerHTML.replace(/class='?diff-addedline'?/ig, 		'style="background-color: #ccddcc;"'); tempIEDiv.innerHTML = tempIEDiv.innerHTML.replace(/class='?diff-deletedline'?/ig, 		'style="background-color: #ffffaa;"'); tempIEDiv.innerHTML = tempIEDiv.innerHTML.replace(/class='?diff['>]/ig, 				'style="width: 100%; font-size: 12px; background-color: #ededff;"'); tempIEDiv.innerHTML = tempIEDiv.innerHTML.replace(/(del |ins )class=['"]?.*?diffchange.*?['"]?(>)?/ig,	'$1style="text-decoration: none; font-weight: bold; color: #ff0000;"$2'); tempIEDiv.innerHTML = tempIEDiv.innerHTML.replace(/class='?diff-marker'?/ig, 			'style="width: '+markerwidth+'px; font-size: 14px; font-weight: bold;"'); tempIEDiv.innerHTML = tempIEDiv.innerHTML.replace(/class='?diff-content'?/ig, 			'style="width: '+contentwidth+'px;"'); table.appendChild ( tempIEDiv ); } else { table.innerHTML = allTables[i].innerHTML.toString; var size = iglooSettings.diffFontSize; table.innerHTML = table.innerHTML.replace ( /class="diff-addedline"/ig, 'class="diff-addedline" style="font-size: '+size+'px;"' ); table.innerHTML = table.innerHTML.replace ( /class="diff-context"/ig, 'class="diff-context" style="font-size: '+size+'px;"' ); table.innerHTML = table.innerHTML.replace ( /class="diff-deletedline"/ig, 'class="diff-deletedline" style="font-size: '+size+'px;"' ); table.innerHTML = this.flagProfanity(table.innerHTML); }				// remove the built in rubbish if ( table.getElementsByTagName ( 'tr' )[0] != null ) table.getElementsByTagName ( 'tr' )[0].parentNode.removeChild ( table.getElementsByTagName ( 'tr' )[0] ); // add our own (basically rubbish) stuff // first, users var newTr = document.createElement ( 'tr' ); if ( this.diffData[0][1] ) { var oldEditor = this.diffData[0][1].substr( 0, this.diffData[0][1].indexOf('(talk') );				} else { var oldEditor = '? missing - report ?'; }				if ( this.diffData[1][1] ) {					var newEditor = this.diffData[1][1].substr( 0, this.diffData[1][1].indexOf('(talk') ); } else { var newEditor = '? missing - report ?'; } // if we don't have the user for this diff, we might as well set it, even if something goes wrong; it's better than nothing if ( typeof this.currentDiff[1] == 'undefined' ) this.currentDiff[1] = newEditor; if ( this.currentDiff[1] === true ) /* will be true if a revert fails; we should still try and score it */ this.currentDiff[1] = newEditor; var newTd1 = document.createElement ( 'td' ); newTd1.innerHTML = +oldEditor+; newTd1.setAttribute ( 'colspan', '2' ); newTd1.setAttribute ( 'align', 'center' ); newTd1.setAttribute ( 'width', '50%' ); var newTd2 = document.createElement ( 'td' ); newTd2.innerHTML = +newEditor+; newTd2.setAttribute ( 'colspan', '2' ); newTd2.setAttribute ( 'align', 'center' ); newTd2.setAttribute ( 'width', '50%' ); newTr.appendChild(newTd1); newTr.appendChild(newTd2); table.insertBefore(newTr, table.firstChild); // second, summaries var newTr = document.createElement('tr'); if ( ( typeof this.diffData[0][2] != 'undefined' ) && ( this.diffData[0][2] != false ) ) { var oldSummary = this.diffData[0][2]; } else { var oldSummary = ''; } if ( ( typeof this.diffData[1][2] != 'undefined' ) && ( this.diffData[1][2] != false ) ) { var newSummary = this.diffData[1][2]; } else { var newSummary = ''; } if ( oldSummary.indexOf ( '(del/undel)' ) > -1 ) oldSummary = oldSummary.substr ( 0, oldSummary.indexOf ( '(del/undel)' ) ); if ( newSummary.indexOf ( '(del/undel)' ) > -1 ) newSummary = newSummary.substr ( 0, newSummary.indexOf ( '(del/undel)' ) ); var newTd1 = document.createElement('td'); newTd1.innerHTML = +oldSummary+; newTd1.setAttribute('colspan','2'); newTd1.setAttribute('align','center'); var newTd2 = document.createElement('td'); newTd2.innerHTML = +newSummary+; newTd2.setAttribute('colspan','2'); newTd2.setAttribute('align','center'); newTr.appendChild(newTd1); newTr.appendChild(newTd2); table.insertBefore(newTr, table.childNodes[1]); } else { // step 2 - get the page content var table = document.createElement ( 'div' ); table.setAttribute ( 'style', 'font-size: 12px;' ); table.className = 'diff diff-contentalign-left'; var text = this.ajax.pageRequest.responseText; text = text.substring ( text.indexOf ( ' ' ), text.indexOf ( '<div class="printfooter"' ) ); table.innerHTML = text; }			// step 3 - remove old details if (document.getElementById('iglooPageTitle') != null) document.getElementById('iglooPageTitle').parentNode.removeChild(document.getElementById('iglooPageTitle')); if (document.getElementById('iglooInnerDiv') != null) document.getElementById('iglooInnerDiv').parentNode.removeChild(document.getElementById('iglooInnerDiv')); document.getElementById('iglooDiffDisplay').innerHTML = ''; // step 4 - output the page title to the diff screen var pageTitle = document.createElement('div'); pageTitle.setAttribute('id', 'iglooPageTitle'); pageTitle.style.fontSize = '16px'; pageTitle.style.fontWeight = 'bold'; pageTitle.style.width = '100%'; pageTitle.style.marginBottom = '5px'; pageTitle.style.borderBottom = '1px solid #000'; if ( this.currentDiff[3] ) { var extra = '(NEW PAGE) '; } else { var extra = ''; } var pageTitleContent = document.createTextNode(extra + this.currentDiff[0]); pageTitle.appendChild(pageTitleContent); document.getElementById('iglooDiffDisplay').appendChild(pageTitle); // step 5 - output the table in question to the diff screen // here, we handle styling for NON-IE browsers if ( ! ie ) { var style = document.createElement('style'); style.setAttribute('type', 'text/css'); var cssString = 'span.iglooProfanity { background-color: #ff99ff; font-weight: bold; text-decoration: underline; } table.diff { width: 100%; font-size: 12px; background-color: #ededff; } col.diff-marker { width: '+markerwidth+'px; font-size: 14px; font-weight: bold; } col.diff-content { width: '+contentwidth+'px; } td.diff-lineno { font-weight: bold; } td.diff-addedline { background-color: #ccffcc; } td.diff-deletedline { background-color: #ffffaa; } td.diff-context { background-color: #ddddff; } .diffchange { text-decoration: none; font-weight: bold; color: #ff0000; }'; var cssText = document.createTextNode(cssString); style.appendChild(cssText); document.getElementById('iglooDiffDisplay').appendChild(style); }			// the container div var innerDiv = document.createElement ( 'div' ); innerDiv.setAttribute ( 'id', 'iglooInnerDiv' ); innerDiv.style.margin = 'auto'; // stick the table of stuff into the div innerDiv.appendChild ( table ); // display everything in the window document.getElementById('iglooDiffDisplay').appendChild(innerDiv); document.getElementById('iglooDiffDisplay').innerHTML += ' '; // we can now revert this edit if ( igloo.iglooActions.reversionEnabled == 'pause' ) igloo.iglooActions.reversionEnabled = 'yes'; // handle the diff scoring window this.updateScoreDisplay ( this.currentDiff[4] ); }		this.updateScoreDisplay = function ( score ) { if ( score == null ) { score = igloo.iglooNet.scoreObject ( this.currentDiff[1] ); score = score[0]; if ( score === false ) score = iglooSettings.defaultUserScore; }			if ( typeof this.currentDiff[5] !== 'string' ) this.currentDiff[5] = ''; if ( score != null ) { if ( score >= 0.8 ) { this.scoreDisplay.win_content = 'igloo asserts that this edit is PROBABLE VANDALISM .'; } else if ( score >= 0.6 ) { this.scoreDisplay.win_content = 'igloo asserts that this edit is possible vandalism .'; } else if ( score >= 0.4 ) { this.scoreDisplay.win_content = 'igloo has little or no data on this diff, and cannot make a risk assertion.'; } else if ( score >= 0.2 ) { this.scoreDisplay.win_content = 'igloo asserts that this edit is unlikely to be vandalism .'; } else if ( score >= 0 ) { this.scoreDisplay.win_content = 'igloo asserts that this edit is free from vandalism .'; } else { this.scoreDisplay.win_content = 'iglooNet data for this diff is missing.'; }			} else { score = iglooSettings.defaultUserScore; this.scoreDisplay.win_content = 'iglooNet data for this diff is missing.'; }			this.scoreDisplay.win_content += '' + this.currentDiff[5]; this.scoreDisplay.win_bg = igloo.iglooNet.colourScore ( score, '#ffffff' ); this.scoreDisplay.applyAll; }		this.flagProfanity = function(html) { // this function flags profanity in the diff window if ( iglooSettings.profFilter !== true ) return html; var profanity = new Array(											/\b((?:moth[era]*)?[ -]*f+(?:u|oo)[ck]{2,}(?:ing?|er|hole)?s*|s[e3]+x+[iey]*|r[a4]p(?:e+d*|i+s+t+s*)|su[ck]+(?:s+|ed+|i+n+g+)?|gang[- ]?bang(?:er|ing)?|(?:(?:t+h+|f+)r[e3]{2,}|four)+s[o0]+me+)\b/ig, /* sexual intercourse */											/\b(h[o0]+m[o0]+(?:sexual(?:it(?:y|e)+)?)?|(?:is +)?ga+[iy]+|lesb(?:ian|[o0]*))\b/ig, /* homosexuality */											/\b([ck][o0]+[ck]{2,}(?:head|face|(?:su[ck]{2,}(?:er|ing)?))?|d[o0]+n+g|p[3e]+n[iu]+s+[3e]*s*|p+[3e]{2,}|d[i1]+[ck]{2,}s*(?:su[ck]{2,}(?:er|ing)?)?|manh(?:[o0]{2,}|u+)d+|b[o0]+n+e*r+s*|ball[sz]+(?:a[ck]{2,})?)\b/ig, /* male genitalia */											/\b(cun+(?:t|[iey]+)s*|vag(?:ina)?s*|puss+[yie]+s*|fann+[yie]+s*)\b/ig, /* female genitalia */											/\b(t+i+t+(?:s*|[iey]+[sz]*)|breasts*|b+[o0]{2,}b+[ieys]*)\b/ig, /* breasts */											/\b(anal+|ar*ss+e*|ar*se+s+|(?:bum+|butt+) ?(?:h[o0]+le|cr+a[ck]+|o(?:[ck]+s|x+))?)\b/ig, /* anus */ /\b((?:bull)?(?:s+hite*|ass+)(?:holes?|he[a4]+d+s*)?|cr[a4*]+p+[iy]*|cru+d+|poo+)\b/ig, /* excretion */ /\b(w*h+[o0]+r*e+s*|prostitutes*|s+l+u+t+s*|slags*|cu+m+(?:ing)?|d[il]{2,}d[o0]+[o0e]*s*|(?:b+l+[o0]+w+|h+[a4]+n+d+|t[i1]+t+[iey]*)j+[o0]+b+[sz]*|c[o0]+ndom[sz]*|p[o0]+rn)\b/ig, /* sexual */ /\b(nigg+(?:er|a+)s*|naz+i+[sz]+|ped[o0]+(?:[phf]+ile)?[sz]*)\b/ig, /* racism & libel */ /\b(ret[a4]+r+d+(?:ed|s)?|f+[a4]+g+([o0]+t+)?s*|d[ou]+che?(ba+g)?s*|bast[ea]rds*|bit*ch[iey]*|[you]*r+ ?m+[ou]+m+)\b/ig, /* other insults */ /('{3,}bold text'{3,}|'{2,}italic text'{2,}|\[{2,}link title\]{2,}|\[http:\/\/www\.example\.com link title\]|={2,} *headline text *={2,})/ig, /* wiki test */ /\b(q[qwerty]{5,}|[asdf]{8,}|[ghjkl]{8,}|[uiop]{8,})\b/ig, /* nonsense */ /\b(lol(?:l*ol|cat[sz]*)*|li+e+k)\b/ig, /* lolz */ /\b(ha?i+(?=\/)|he+ll+o+|(?:ha+|hee+|ho+)+|l[ou]+v+|ya|ye+h+)\b/ig, /* laughs/greetz */ /([!?;]{3,}|[.|]{4,}|={6,}|([a-z0-9])\2{6,})/ig /* repeating characters */ );			for ( var i = 0, l = profanity.length; i < l; i ++ ) {				html = html.replace(profanity[i], ' $1 ');			}			return html;		}		this.destroy = function { // calling this will destroy the interface and connections of this object.			this.diffDisplay.hide ;			this.scoreDisplay.hide ;		}	}	function iglooHist  {		// the iglooHist object handles the retrieval and display of the history of a page, in order		// that it can be displayed to the user.		// timer var		this.timer = null;		this.startInterface = function {			if (document.getElementById('igloo-buttons-history') == null) return false;			this.histCatcher = new wa_window ( document.getElementById ( 'igloo-buttons-history' ) );			this.histCatcher.win_top = 71;			this.histCatcher.win_left = -100;			this.histCatcher.win_width = 170;			this.histCatcher.win_height= 80;			this.histCatcher.win_alpha = 0;			this.histCatcher.win_cursor = 'pointer'; this.histCatcher.win_disp = 'none'; this.histCatcher.applyAll ; this.histDisplay = new wa_window ( document.getElementById ( 'igloo-buttons-history' ) ); this.histDisplay.win_top = 77; this.histDisplay.win_left = -100; this.histDisplay.win_width = 170; this.histDisplay.win_bg = '#d0d0d0'; this.histDisplay.win_bd = '#000000'; this.histDisplay.win_bd_wd = 1; this.histDisplay.win_padding = 2; this.histDisplay.win_fontsize = 10; this.histDisplay.win_cursor = 'pointer'; this.histDisplay.win_disp = 'none'; this.histDisplay.win_content = ' loading page history - wait... <ul style="display: none; width: 100%; height: 100%; margin: 0px; padding: 0px; overflow-x: hidden; overflow-y: auto;" id="iglooPageHistory-cont"></ul>'; this.histDisplay.applyAll ; // unfortunately, these pseudo events cannot be detatched. See waLib.js for implementation. wa_attach ( document.getElementById ( 'igloo-buttons-history' ), 'mouseenter', igloo.iglooHist.mouseOver ); wa_attach ( document.getElementById ( 'igloo-buttons-history' ), 'mouseleave', igloo.iglooHist.mouseOut ); }		this.mouseOver = function { if ( igloo.iglooDiff.viewingDiff === true ) { if ( igloo.iglooHist.timer ) { clearTimeout ( igloo.iglooHist.timer ); igloo.iglooHist.timer = false; } else { igloo.iglooHist.histDisplay.show ; igloo.iglooHist.histCatcher.show ; igloo.iglooHist.getHistory ; }			}		}		this.mouseOut = function { igloo.iglooHist.timer = setTimeout(function { igloo.iglooHist.histDisplay.hide; igloo.iglooHist.histCatcher.hide; igloo.iglooHist.timer = false; }, iglooSettings.histWinTimeout * 1000); }		this.getHistory = function ( callback, data ) { // the get history module retrieves a page history and displays it to the user switch ( callback ) { default: case 0: document.getElementById ( 'iglooPageHistory-cont' ).innerHTML = 'loading page history - wait...'; var pageHist = new wa_mediawikiApi ; pageHist.onCompleteAction = function ( data ) { igloo.iglooHist.getHistory ( 1, data ); }; pageHist.getPage ( igloo.iglooDiff.currentDiff[0], 15, 'ids|user' ); break; case 1: document.getElementById('iglooPageHistory-cont').style.display = 'block'; document.getElementById('iglooPageHistory-note').style.display = 'none'; var pageHistory = ''; for (var i = 0; i < data['page']['revisions'].length; i ++ ) { var revision = data['page']['revisions'][i]; var user = revision['user']; var score = igloo.iglooNet.scoreObject(user); var backgroundColour = igloo.iglooNet.colourScore(score[0], '#ffffff'); pageHistory += '<li id="iglooHist_'+revision['id']+'" onclick="igloo.iglooControls.action_loadFromFeed(\+igloo.iglooDiff.currentDiff[0].replace ('\, '\\\)+'\', \+revision['user']+'\', \+revision['id']+'\');" onmouseover="this.style.backgroundColor = \'#dddddd\';" onmouseout="this.style.backgroundColor = \+backgroundColour+'\';" style="cursor: pointer; width: 186px; padding: 2px; border-bottom: 1px solid #000000; list-style-type: none; list-style-image: none; marker-offset: 0px; background-color: '+backgroundColour+';">'+revision['user']+'</li>'; }					pageHistory += '<li style="width: 100%; list-style-type: none; list-style-image: none; text-align: center;" onclick="igloo.iglooActions.sendLinkToParent(wgServer + wgScript + \'?title=\' + igloo.iglooDiff.currentDiff[0] + \'&action=history\');">- full history -</li>'; document.getElementById('iglooPageHistory-cont').innerHTML = pageHistory; break; }		}	}	function iglooControls { // the igloo controls module handles attaching to, and watching, keyboard events, and performing the requested function this.blockActions = false; this.start = function { // this functions creates the diff window for future use, and displays the latest news in it :)			this.startInterface ;			this.bindKeys ;		}		this.startInterface = function  {			this.controlDisplay = new wa_window ( igloo.iglooInterface );			this.controlDisplay.win_left = 190;			this.controlDisplay.win_width = parseInt ( igloo.iglooInterface.win_obj.style.width ) - 192;			this.controlDisplay.win_height= 78;			this.controlDisplay.win_bg = '#fdfdff';			this.controlDisplay.win_bd_tp = '1px solid #000000';			this.controlDisplay.win_bd_bt = '1px solid #000000';			this.controlDisplay.win_padding = 1;			this.controlDisplay.win_content = ' CONTROLS INIT ';			this.controlDisplay.applyAll ;			this.attachButtons ;			igloo.iglooHist = new iglooHist ;			igloo.iglooHist.startInterface ;		}		this.attachButtons = function {			var parent = document.getElementById('iglooControlsDisplay'), buttons = 0; var panelButton = '<div id="igloo-buttons-revertwarn" style="position: relative; float: left; padding-left: -1px; padding-top: -1px; width: 73px; height: 73px; margin-top: 7px; margin-left: 5px; cursor: pointer;"><img src="' + iglooSettings.remoteHost + 'images/igloo-revert.png" onclick="if (typeof igloo.iglooDiff.currentDiff[0] != \'undefined\') { igloo.iglooControls.action_revertDiff; }" /> '; panelButton += '<div id="igloo-buttons-back" style="position: relative; float: left; padding-left: -1px; padding-top: -1px; width: 50px; height: 50px; margin-top: 2px; margin-left: 20px; cursor: pointer;"><img id="igloo-buttons-back-b" src="' + iglooSettings.remoteHost + 'images/igloo-back-grey.png" onclick="igloo.iglooControls.action_historyBack;" /> '; buttons ++; panelButton += '<div id="igloo-buttons-forward" style="position: relative; float: left; padding-left: -1px; padding-top: -1px; width: 50px; height: 50px; margin-top: 2px; margin-left: 10px; cursor: pointer;"><img id="igloo-buttons-forward-b" src="' + iglooSettings.remoteHost + 'images/igloo-forward-grey.png" onclick="igloo.iglooControls.action_historyForward;" /> '; buttons ++; if ( iglooSettings.mesysop === true ) { panelButton += '<div id="igloo-buttons-block" style="position: relative; float: left; padding-left: -1px; padding-top: -1px; width: 50px; height: 50px; margin-top: 2px; margin-left: 10px; cursor: pointer;"><img id="igloo-buttons-block-b" src="' + iglooSettings.remoteHost + 'images/igloo-block.png" onclick="igloo.iglooControls.action_block;" /> '; buttons ++; } var browsePos = ( 62 * buttons ) - 15; panelButton += '<div id="igloo-input-browse" style="position: relative; float: left; top: 53px; left: -' + browsePos + 'px; width: 230px; height: 20px;"><input id="igloo-browse-to" type="text" style="width: 200px; height: 14px; " onfocus="igloo.iglooControls.action_browseFocus;" onblur="igloo.iglooControls.action_browseBlur;" /> <img style="position: relative; top: -3px; cursor: pointer;" src="' + iglooSettings.remoteHost + 'images/igloo-go.png" onclick="igloo.iglooControls.action_browseBarDo;" /> '; panelButton += '<div id="igloo-buttons-settings" style="position: relative; float: right; padding-left: -1px; padding-top: -1px; width: 73px; height: 73px; margin-top: 3px; margin-right: 5px; cursor: pointer;"><img src="' + iglooSettings.remoteHost + 'images/igloo-settings.png" onclick="igloo.iglooManageSettings.showdisplay ;" /> '; panelButton += '<div id="igloo-buttons-history" style="position: relative; float: right; padding-left: -1px; padding-top: -1px; width: 73px; height: 73px; margin-top: 3px; margin-right: 5px; cursor: pointer;"><img src="' + iglooSettings.remoteHost + 'images/igloo-hist.png" /> '; parent.innerHTML = panelButton; }		this.bindKeys = function { wa_attach ( document, 'keydown', igloo.iglooControls.killKeys ); //wa_attach ( document, 'keypress', igloo.iglooControls.killKeys ); wa_attach ( document, 'keyup', igloo.iglooControls.handleKeys ); }		this.handleKeys = function ( e ) { if ( ! e ) e = window.event; if ( e.keyCode ) { var keyPress = e.keyCode; var use = 'key'; } else { var keyPress = e.charCode; var use = 'char'; }			var result = igloo.iglooControls.manageKeys ( keyPress, use, false ); if ( result === true ) return true; if ( e.preventDefault ) { e.preventDefault ; } else { return false; } return true; }		this.killKeys = function ( e ) { // check whether to prevent the default action if ( iglooSettings.blockKeys === false ) return true; if ( ! e ) e = window.event; if ( e.keyCode ) { var keyPress = e.keyCode; var use = 'key'; } else { var keyPress = e.charCode; var use = 'char'; }			var result = igloo.iglooControls.manageKeys ( keyPress, use, true ); if ( result === true ) return true; if ( e.preventDefault ) { e.preventDefault ; } else { return false; } return true; }		this.manageKeys = function ( code, use, killcheck ) { var key = 	{ 'default' : { 'key' : { 'backspace' : 8, 'space' : 32, 'q' : 81, 'f5' : 116 }, 'char' : { 'backspace' : 8, 'space' : 32, 'q' : 113, 'f5' : 0 } }, 'browsebar' : { 'key' : { 'f5' : 116, 'enter' : 13 }, 'char' : { 'f5' : 0, 'enter' : 0 } }, 'settings' : { 'key' : { 'f5' : 116 }, 'char' : { 'f5' : 0 } } };			// the active key set var key = key [iglooSettings.dynamicBlockKeys] [use]; if ( killcheck === true ) { for ( var i in key ) { if ( key [i] == code ) { return false; } // if exists, block }				return true; // otherwise, don't			} switch ( iglooSettings.dynamicBlockKeys ) { case 'default': switch ( code ) { case key ['backspace']: // backspace igloo.iglooControls.action_historyBack ; break; case key ['space']: // spacebar if (typeof igloo.iglooChanges.recentChanges[0] != 'undefined') { igloo.iglooControls.action_loadFromFeed(igloo.iglooChanges.recentChanges[0][0], igloo.iglooChanges.recentChanges[0][1], igloo.iglooChanges.recentChanges[0][2], igloo.iglooChanges.recentChanges[0][3], igloo.iglooChanges.recentChanges[0][4], igloo.iglooChanges.recentChanges[0][11]); }							break; case key ['q']: // q							if ( typeof igloo.iglooDiff.currentDiff[0] != 'undefined' ) { igloo.iglooControls.action_revertDiff; }							break; case key ['f5']: // F5 igloo.iglooControls.getPermission ( 'You just pressed the F5 key. By default, this causes the page to refresh in most browsers. To prevent you losing your work, igloo therefore agressively blocks this key. Do you wish to reload the page?', function { location.reload ; } ); break; default: // if the key is unrecognised, do nothing return true; break; }					break; case 'browsebar': switch ( code ) { case key ['enter']: // enter igloo.iglooControls.action_browseBarDo ; break; case key ['f5']: // F5 igloo.iglooControls.getPermission ( 'You just pressed the F5 key. By default, this causes the page to refresh in most browsers. To prevent you losing your work, igloo therefore agressively blocks this key. Do you wish to reload the page?', function { location.reload ; } ); break; default: // if the key is unrecognised, do nothing return true; break; }					break; case 'settings': switch ( code ) { case key ['f5']: // F5 igloo.iglooControls.getPermission ( 'You just pressed the F5 key. By default, this causes the page to refresh in most browsers. To prevent you losing your work, igloo therefore agressively blocks this key. Do you wish to reload the page?', function { location.reload ; } ); break; default: // if the key is unrecognised, do nothing return true; break; }					break; }			return false; }		this.getPermission = function ( message, escalateFunction, nobuttons, owntitle ) { this.blockActions = true; if ( ! nobuttons ) { var button = '

continue | cancel '; } else { var button = ''; } if ( ! owntitle ) { var title = 'igloo needs your permission to continue...'; } else { var title = owntitle; } if ( ! this.userMessage ) { this.userMessage = new wa_window ; this.userMessage.win_alpha = 0.7; this.userMessage.win_bg = '#000000'; this.userMessage.win_fill = true; this.userMessage.applyAll ; } else { this.userMessage.show ; }			if ( ! this.userMessageContent ) { this.userMessageContent = new wa_window ; this.userMessageContent.win_bg = '#ccccff'; this.userMessageContent.win_width = 500; this.userMessageContent.win_height = 130; this.userMessageContent.win_padding = 5; this.userMessageContent.win_fontsize = 10; this.userMessageContent.win_alpha = 1; this.userMessageContent.win_bd = '2px solid #666688'; this.userMessageContent.win_content = ' ' + title + ' ' + message + ' ' + button + ' '; this.userMessageContent.applyAll ; this.userMessageContent.center ( 'both', true ); } else { this.userMessageContent.show ; this.userMessageContent.win_content = ' ' + title + ' ' + message + ' ' + button + ' '; this.userMessageContent.applyAll ; this.userMessageContent.center ( 'both', true ); }			if ( ! nobuttons ) { document.getElementById ( 'igloo-permission-challenge-cont' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide; igloo.iglooControls.userMessageContent.hide; escalateFunction; } document.getElementById ( 'igloo-permission-challenge-canc' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide; igloo.iglooControls.userMessageContent.hide; }; }		}		this.action_historyBack = function { if ( igloo.iglooControls.blockActions ) return false; igloo.iglooDiff.goBack ( 1 ); }		this.action_historyForward = function { if ( igloo.iglooControls.blockActions ) return false; igloo.iglooDiff.goForward ( 1 ); }		this.action_revertDiff = function { if ( igloo.iglooControls.blockActions ) return false; igloo.iglooActions.revertDiff ; }		this.action_block = function { if ( igloo.iglooControls.blockActions ) return false; iglooSettings.dynamicBlockKeys = 'settings'; var user = ( typeof igloo.iglooDiff.currentDiff [1] !== 'undefined' ) ? igloo.iglooDiff.currentDiff [1] : ''; var tb = new iglooRevert ( 0, user ); tb.blockUser ( 5 ); }		this.action_loadFromFeed = function ( a, b, c, d, e, f ) { if ( igloo.iglooControls.blockActions ) return false; igloo.iglooActions.reversionEnabled = 'pause'; igloo.iglooDiff.display ( a, b, c, d, e, f ); igloo.iglooChanges.markViewed ( c ); }		this.action_browseBarDo = function { if ( igloo.iglooControls.blockActions ) return false; igloo.iglooActions.reversionEnabled = 'pause'; var browseTo = document.getElementById ( 'igloo-browse-to' ).value; document.getElementById ( 'igloo-browse-to' ).value = ''; igloo.iglooDiff.display ( browseTo ); }		this.action_browseFocus = function { iglooSettings.dynamicBlockKeys = 'browsebar'; // stop blocking/overwriting key actions when we activate the text box }		this.action_browseBlur = function { iglooSettings.dynamicBlockKeys = 'default'; // start blocking/overwriting key actions when we activate the text box }		this.destroy = function { // calling this will destroy the interface and connections of this object. this.controlDisplay.hide ; }	}	function iglooActions { // the iglooActions module handles the actual editing of the wiki, such as reversions and warnings this.reversionEnabled 		= 'yes'; this.lastRevertedRevision 	= false; this.revertedPage 			= false; this.warnWho 				= false; this.warningLevel			= false; this.revertDiff = function { this.lastRevertedRevision = igloo.iglooDiff.currentDiff [2]; var page = igloo.iglooDiff.currentDiff [0]; var user = igloo.iglooDiff.currentDiff [1]; var revid = igloo.iglooDiff.currentDiff [2]; var rev = new iglooRevert ( page, user, revid ); }		this.sendLinkToParent = function ( linkText, activateAfter ) { if ( activateAfter == null ) activateAfter = true; if ( ( window.opener != null ) && ( ! window.opener.closed ) ) { window.opener.location.href = linkText; window.opener.focus ; return true; }			return false; }		this.cancelActivity = function { }	}	function iglooRevert ( page, user, revid ) { var thisRevert 		= this; this.revertPage		= page; this.revertUser		= user; this.revertRevid	= revid; this.performRollback = function ( callback, details ) { switch ( callback ) { default: case 0: // check that reversion is switched on					if ( igloo.iglooActions.reversionEnabled == 'no' ) { alert ( 'You cannot revert this edit to ' + this.revertPage + ', because you made it using igloo' ); return false; } if ( igloo.iglooActions.reversionEnabled == 'pause' ) { alert ( 'You cannot revert this edit to ' + this.revertPage + ', because a diff is still loading' ); return false; } // notify user igloo.iglooStatus.addStatus ( 'Attempting to revert the change to ' + thisRevert.revertPage + ' made by ' + thisRevert.revertUser + ' ...' ); // prevent interference with this page while we are reverting it					igloo.iglooActions.reversionEnabled = 'pause'; // let the user know we're working... document.getElementById ( 'iglooPageTitle' ).innerHTML = document.getElementById ( 'iglooPageTitle' ).innerHTML + ' - reverting edit...'; // build the reversion summary var summary = iglooSettings.rollbackSummary; // attempt the actual rollback var thisReversion = new wa_mediawikiApi ; thisReversion.onCompleteAction = function ( success ) { thisRevert.performRollback ( 1, success ); }; thisReversion.rollbackPage ( this.revertPage, this.revertUser, summary ); // mark the edit we're reverting as bad var url = iglooSettings.remoteHost + 'main.php?action=markbad&session=' + igloo.iglooSession.session + '&me=' + encodeURIComponent ( wgUserName ) +'&user=' + this.revertUser + '&revid=' + this.revertRevid; iglooImport ( url, true, 'iglooMark' ); break; case 1: if ( details == false ) { igloo.iglooStatus.addStatus ( 'Will not revert the edit to ' + thisRevert.revertPage + ' by ' + thisRevert.revertUser + ' because another user has already done so.' ); //alert('igloo will not revert the edit to '+this.revertPage+', because another user has already done so'); if ( this.revertPage == igloo.iglooDiff.currentDiff[0] ) { igloo.iglooDiff.display ( this.revertPage, true ); igloo.iglooActions.reversionEnabled = 'no'; }					} else { var thisReversion = new wa_mediawikiApi ; thisReversion.onCompleteAction = function ( data ) { thisRevert.performRollback ( 2, data ); }; thisReversion.getPage ( this.revertPage, 1, 'ids' ); }					break; case 2: // retrieve page ID, so we do not redisplay in the feed igloo.iglooChanges.addToViewed ( details['page']['revisions'][0]['id'] ); if ( this.revertPage == igloo.iglooDiff.currentDiff[0] ) igloo.iglooDiff.display ( this.revertPage, wgUserName ); // notify user igloo.iglooStatus.addStatus ( 'Successfully reverted the change to ' + thisRevert.revertPage + ' made by ' + thisRevert.revertUser + ' !' ); this.warnUser; break; }		}		this.warnUser = function( callback, details ) { switch ( callback ) { default: case 0: // don't warn self if ( thisRevert.revertUser == wgUserName ) break; // notify user igloo.iglooStatus.addStatus( 'Attempting to warn ' + thisRevert.revertUser + ' for vandalism on ' + thisRevert.revertPage + ' ...' ); // get the user talk page var getUserPage = new wa_mediawikiApi ; getUserPage.onCompleteAction = function ( data ) { thisRevert.warnUser ( 1, data ); }; getUserPage.getPage ( 'User_talk:' + this.revertUser, 1, 'content' ); break; case 1: // set up the time management systems var months = new Array ( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ); var currentDate = new Date ; var currentMonth = currentDate.getMonth ; var currentYear = currentDate.getFullYear ; var currentTime = currentDate.getTime ; // check for warnings on the user's talk page var warnings = []; // if the page already exists, we must analyse it for warnings if ( typeof details['page']['revisions'] != 'undefined' ) { var pageData = thisRevert.userTalk = details['page']['revisions'][0]['content']; var regTest = /(?:.+?([0-9]{2}):([0-9]{2}), ([0-9]{1,2}) ([a-z]+?) ([0-9]{4}))?/gi; // get all the warnings on the page var i = 0; while ( true ) { var t = regTest.exec ( pageData ); if ( t == null ) break; warnings[i] = []; warnings[i][0] = t[1]; // template warnings[i][1] = t[2]; // level warnings[i][2] = t[3]; // hour warnings[i][3] = t[4]; // minute warnings[i][4] = t[5]; // day warnings[i][5] = t[6]; // month warnings[i][6] = t[7]; // year //alert(t[1]); i ++; }						// we are only interested in the latest one if ( typeof warnings[0] == 'undefined' ) { warnings[0] = []; warnings[0][0] = false; warnings[0][1] = 0; } var useWarning = warnings.length-1; if ( typeof warnings[useWarning][0] === 'string' ) { var t = warnings[useWarning][0]; if ( t.indexOf ( 'block' ) > -1 ) { useWarning --; warnings[useWarning][1] = 0; } }						// check when this warning was given for ( var compareMonth = 0; compareMonth < months.length; compareMonth ++ ) { if ( months[compareMonth] == warnings[useWarning][5] ) break; }						var compareDate = new Date ; compareDate.setFullYear ( parseInt ( warnings[useWarning][6] ), compareMonth, parseInt ( warnings[useWarning][4] ) ); compareDate.setHours ( parseInt ( warnings[useWarning][2] ) ); compareDate.setMinutes ( parseInt ( warnings[useWarning][3] ) ); var compareTime = compareDate.getTime ; // check if it is old enough to ignore for the purposes of incremental warnings var timeDiff = ( currentTime + ( currentDate.getTimezoneOffset * 60 * 1000 ) ) - compareTime; if ( timeDiff > ( iglooSettings.warningsOldAfter * 24 * 60 * 60 * 1000 ) ) { warnings[useWarning][1] = 0; } // check whether a header already exists for the current month. if not, create one var currentHeader = new RegExp ( '={2,4} *' + months[currentMonth] + ' *' + currentYear + ' *={2,4}', 'gi' ); if ( currentHeader.test ( pageData ) != true ) { var header = '== '+months[currentMonth]+' '+currentYear+' =='; } else { var header = false; } }					// if the page does not exist, we can simply set warnings at the default (lowest) levels else { // set up the warning and date header for addition to the user's page warnings[0] = []; warnings[0][0] = false; warnings[0][1] = 0; var header = '== '+months[currentMonth]+' '+currentYear+' =='; var useWarning = 0; }					// decide upon which warning level to issue switch ( warnings[useWarning][1] ) { default: case 0: // use first level warning this.warningLevel = 1; break; case '1': // use second level warning this.warningLevel = 2; break; case '2': // use third level warning this.warningLevel = 3; break; case '3': // use final warning this.warningLevel = 4; break; case '4': case '4im': // perform report/block logic igloo.iglooStatus.addStatus ( 'Will not warn ' + thisRevert.revertUser + ' because they have already recieved a final warning.' ); this.handleFinalWarning ; this.warningLevel = false; break; }					// add the message to their talk page if (this.warningLevel == false) return false; var userPage = 'User_talk:' + this.revertUser; var message = iglooSettings.warningMessage; message = message.replace ( /%LEVEL%/g, this.warningLevel ); message = message.replace ( /%PAGE%/g, this.revertPage ); message = message.replace ( /%DIFF%/g, mw.config.get('wgServer') + mw.config.get('wgScript') + '?diff=' + this.revertRevid + '' ); message = message.replace ( /%MESSAGE%/g, iglooSettings.vandalTemplate ); var summary = iglooSettings.warningSummary; summary = summary.replace ( /%LEVEL%/g, this.warningLevel ); summary = summary.replace ( /%PAGE%/g, this.revertPage ); if ( header != false ) message = header + '\n' + message; var userWarning = new wa_mediawikiApi ; if ( iglooSettings.notifyWarningDone == true ) userWarning.onCompleteAction = function { igloo.iglooStatus.addStatus( 'Successfully issued a level ' + thisRevert.warningLevel + ' warning to ' + thisRevert.revertUser + ' for vandalism on ' + thisRevert.revertPage + ' !' ); }; userWarning.editPage ( userPage, message, summary, false, 'appendtext' ); break; case 2: igloo.iglooStatus.addStatus ( 'Notifying ' + thisRevert.revertUser + ' of block (duration: ' + thisRevert.useduration + ')...' ); var message = thisRevert.usetemplate; message = message.replace ( /%DURATION%/g, thisRevert.useduration ); message = '\n\n'; var summary = 'Notifying user of block (['+'['+iglooSettings.localBase.substr(0, iglooSettings.localBase.length-1)+'|GLOO]'+'])'; wa ( ':api' ).edit ( 'notifyblock', 'User_talk:' + thisRevert.revertUser, 0, message, summary, 'append' ).wait ( function { 						igloo.iglooStatus.addStatus ( 'Successfully notified ' + thisRevert.revertUser + ' of block!' );					} ).run ; break; }		}		this.reportUser = function( callback, details ) { // handle reporting of the user to AIV //return false; switch ( callback ) { default: case 0: // notify user igloo.iglooStatus.addStatus ( 'Attempting to report ' + thisRevert.revertUser + ' to ' + iglooSettings.aiv + ' for vandalism after final warning...' ); // get the aiv page var getAivPage = new wa_mediawikiApi ; getAivPage.onCompleteAction = function ( data ) { thisRevert.reportUser ( 1, data ); }; getAivPage.getPage ( iglooSettings.aiv.replace ( / /g, '_' ), 1, 'content' ); break; case 1: // check whether they are already reported (by a human) if ( typeof details['page']['revisions'] == 'undefined' ) { igloo.iglooStatus.addStatus ( 'Will not report ' + thisRevert.revertUser + ' because the report page does not appear to exist.' ); return false; // error }					var pageData = details['page']['revisions'][0]['content']; if ( pageData.indexOf ( '|' + thisRevert.revertUser + '}}' ) > -1 ) { igloo.iglooStatus.addStatus ( 'Will not report ' + thisRevert.revertUser + ' because they have already been reported.' ); return false; // error }					// check bots var getAivPage = new wa_mediawikiApi ; getAivPage.onCompleteAction = function ( data ) { thisRevert.reportUser ( 2, data ); }; getAivPage.getPage ( iglooSettings.aiv.replace ( / /g, '_' ), 1, 'content' ); break; case 2: // check whether they are already reported (by a bot) if ( typeof details['page']['revisions'] == 'undefined' ) { igloo.iglooStatus.addStatus( 'Will not report ' + thisRevert.revertUser + ' because the report page does not appear to exist.' ); return false; // error }					var pageData = details['page']['revisions'][0]['content']; if ( pageData.indexOf ( '|' + thisRevert.revertUser + '}}' ) > -1 ) { igloo.iglooStatus.addStatus( 'Will not report ' + thisRevert.revertUser + ' because they have already been reported.' ); return false; // error }					// build page link var aivLink = iglooSettings.aiv.replace ( / /g, '_' ); // build the report var myReport = iglooSettings.aivMessage; if ( thisRevert.isIp === true ) { myReport = myReport.replace ( /%TEMPLATE%/g, iglooSettings.aivIp ); } else { myReport = myReport.replace ( /%TEMPLATE%/g, iglooSettings.aivUser ); } myReport = myReport.replace ( /%USER%/g, thisRevert.revertUser ); // build the summary var mySummary = iglooSettings.aivSummary; mySummary = mySummary.replace ( /%USER%/g, thisRevert.revertUser ); // perform the edit var userReport = new wa_mediawikiApi ; userReport.onCompleteAction = function { igloo.iglooStatus.addStatus ( 'Successfully reported ' + thisRevert.revertUser + ' to AIV!' ); }; userReport.editPage ( aivLink, myReport, mySummary, false, iglooSettings.aivWhere ); break; }		}		this.blockUser = function ( callback, details ) { // handle blocking of users if ( typeof thisRevert.customsettings === 'undefined' ) thisRevert.customsettings = false; if ( iglooSettings.mesysop === false ) return false; switch ( callback ) { default: case 0: // notify user igloo.iglooStatus.addStatus( 'Attempting to block ' + thisRevert.revertUser + ' for vandalism after final warning...' ); // analyse user wa ( ':api' ).command ( 'getblocks', 'format=xml&action=query&list=logevents&letype=block&letitle=User:' + thisRevert.revertUser ).wait ( function ( details ) { thisRevert.blockUser ( 1, details ); } ).run ; break; case 1: // we now have access to the talk page data and the block log - redirect depending on whether we're autoblocking or not. if ( details ) if ( details === 'standard' ) { thisRevert.blockUser ( 5, 'userlock' ); break; } if ( thisRevert.isIp !== true ) { thisRevert.checked = { 'auto': 'checked ','talk': ,'anon': ,'email': '','create': 'checked ' }; thisRevert.useduration = 'indefinite'; thisRevert.usetemplate = 'subst:uw-voablock|time=%DURATION%|sig=yes'; thisRevert.blockReason = 'Vandalism-only account'; thisRevert.blockUser ( 5, 'userlock' ); break; } thisRevert.blockUser ( 2 ); break; case 2: // autoblocking - decide on the best warning // pick the best warning length var lastlength = 'neverblocked'; thisRevert.useduration = iglooSettings.blockIncrement [ iglooSettings.blockDefault ]; if ( wa ( ':api' ).results['getblocks']['query']['logevents'] !== null ) { if ( wa ( ':api' ).results['getblocks']['query']['logevents']['item'].length === undefined ) { // one result //alert('oneresult'); if ( wa ( ':api' ).results['getblocks']['query']['logevents']['item-attr']['action'] == 'block' ) { lastlength = wa ( ':api' ).results['getblocks']['query']['logevents']['item']['block-attr']['duration']; } else { /* use default */ } } else { // many results //alert('many results'); for ( var i = 0, l = wa ( ':api' ).results['getblocks']['query']['logevents']['item-attr'].length; i < l; i ++ ) { if ( wa ( ':api' ).results['getblocks']['query']['logevents']['item-attr'][i]['action'] == 'block' ) break; }							if ( i === l ) { /* failed alert('faaiiilllled');*/ thisRevert.blockUser ( 5, 'error' ); return false; } lastlength = wa ( ':api' ).results['getblocks']['query']['logevents']['item'][i]['block-attr']['duration']; //alert('check done, ll: ' + lastlength); }					} else { /* use default */ } if ( lastlength !== 'neverblocked' ) { //alert('valid last length'); var u = iglooSettings.blockIncrement.getPosition ( lastlength ); if ( u !== false ) { if ( u < iglooSettings.blockDefault ) { thisRevert.useduration = iglooSettings.blockIncrement [ iglooSettings.blockDefault ]; } else { if ( u >= ( iglooSettings.blockIncrement.length - 1 ) ) u = iglooSettings.blockIncrement.length - 2; thisRevert.useduration = iglooSettings.blockIncrement [ u + 1 ]; }						} else { /* failed */ thisRevert.blockUser ( 5, 'error' ); return false; } }					thisRevert.lastlength = lastlength; //alert('last length done'); thisRevert.blockReason = 'Vandalism'; // check for relevant templates on the user page thisRevert.usetemplate = iglooSettings.blockTypes ['default']; for ( var i in iglooSettings.blockTypes ) { if ( typeof i !== 'string' ) continue; var regTest = new RegExp ( '{{ *' + i, 'ig' ); if ( ( thisRevert.userTalk !== undefined ) && ( regTest.test ( thisRevert.userTalk ) === true ) && ( i !== 'default' /* just in case there's a weird default template O_o */ ) ) { // show an error is misconfigured OR if the duration doesn't exist and hasn't caused an error yet if ( ( iglooSettings.blockIncrement.getPosition ( thisRevert.useduration ) === false ) || ( iglooSettings.blockIncrement.getPosition ( iglooSettings.blockSpecTemp ) === false ) ) { thisRevert.blockUser ( 5, 'error' ); return false; } if ( iglooSettings.blockIncrement.getPosition ( thisRevert.useduration ) >= iglooSettings.blockIncrement.getPosition ( iglooSettings.blockSpecTemp ) ) { //alert('using special template!'); thisRevert.usetemplate = iglooSettings.blockTypes [i]; if ( thisRevert.usetemplate.indexOf ( '|' ) > -1 ) { thisRevert.blockReason = '{{' + thisRevert.usetemplate.substr ( 0, thisRevert.usetemplate.indexOf ( '|' ) ) + '}}'; } else { thisRevert.blockReason = '{{' + thisRevert.usetemplate + '}}'; } break; } else { /*alert('NOT using special template, as the block is too short!');*/ break; } }					}					// we now know the template to use, and we know the block length to use. //alert ( "igloo would block this user!!!\n\nduration: " + thisRevert.useduration + "\ntemplate: " + thisRevert.usetemplate ); // manage the prompt var message = 'This user has already received a final warning. igloo intends to block the user with the following settings:

' +									' user: ' + thisRevert.revertUser + ' duration: ' + thisRevert.useduration + ' (last blocked for: ' + lastlength + ') block template: ' + thisRevert.usetemplate + '

perform this block (recommended) | adjust block settings | abort block (report user) ' igloo.iglooControls.getPermission ( message, false, true ); // set the functions document.getElementById( 'igloo-just-do-block' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide ; igloo.iglooControls.userMessageContent.hide; thisRevert.blockUser ( 6 ); } document.getElementById( 'igloo-adjust-block' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide ; igloo.iglooControls.userMessageContent.hide; thisRevert.blockUser ( 5, 'adjusting' ); igloo.iglooStatus.addStatus( 'Adjusting block of ' + thisRevert.revertUser + ' : launching block interface...' ); } document.getElementById( 'igloo-abort-block' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide ; igloo.iglooControls.userMessageContent.hide; thisRevert.reportUser ; igloo.iglooStatus.addStatus( 'Aborted block of ' + thisRevert.revertUser + ' : user aborted! Will now report...' ); } break; case 5: if ( details === 'error' ) { // something went wrong. Abort and report user. igloo.iglooStatus.addStatus ( 'Aborted block of ' + thisRevert.revertUser + ' : an error occurred! Will now report...' ); thisRevert.reportUser ; break; }					// nothing went wrong. Display the adjust block system. // generate the dispaly elements if ( details === 'adjusting' ) { var disabled = 'disabled', lastlength = ' (last blocked for ' + thisRevert.lastlength + ' )'; thisRevert.checked = { 'auto': ,'talk': ,'anon': 'checked','email': '','create': 'checked' }; } else { var disabled = , duration = , lastlength = ''; if ( thisRevert.revertUser == null ) thisRevert.revertUser = ''; if ( thisRevert.useduration == null ) thisRevert.useduration = ''; if ( thisRevert.usetemplate == null ) thisRevert.usetemplate = 'subst:uw-block|time=%DURATION%|sig=yes'; if ( typeof thisRevert.blockReason == 'undefined' ) thisRevert.blockReason = 'Vandalism'; }					if ( details === 'userlock' ) disabled = 'disabled'; var t = ''; // duration select for ( var i = 0; i < iglooSettings.blockIncrement.length; i ++ ) { t += '<option '; if ( iglooSettings.blockIncrement [i] == thisRevert.useduration ) t += 'selected '; t += 'value="' + iglooSettings.blockIncrement [i] + '">' + iglooSettings.blockIncrement [i] + ' '; }					var t2 = ''; if ( typeof thisRevert.checked === 'undefined' ) { thisRevert.checked = { 'auto': ,'talk': ,'anon': ,'email': ,'create': '' }; } // output the display var content = ''; content += ' Block user You are blocking a user - select the block options from below. Remember that you are responsible for all blocks made using your account.'; content += '

';					//content += ' ' + t2 + ' '; content += ' '; content += ' '; content += ' <td colspan="2" height="50px" style="margin-top: 50px; font-weight: bold;"><input id="igloo-finish-block" type="button" value="Block" onclick="igloo.iglooPopup.hide; iglooSettings.dynamicBlockKeys = \'default\';" /> | <input type="button" value="Cancel" onclick="igloo.iglooPopup.hide; iglooSettings.dynamicBlockKeys = \'default\';" /> '; content += ' '; igloo.iglooPopup.show ( content ); wa_attach ( document.getElementById ( 'igloo-finish-block' ), 'click', function { 						// set settings						if ( document.getElementById ( 'iglooBlock-duration-b' ).value ===  ) { thisRevert.useduration = document.getElementById ( 'iglooBlock-duration-a' ).value; } else { thisRevert.useduration = document.getElementById ( 'iglooBlock-duration-b' ).value; }						thisRevert.revertUser = document.getElementById ( 'iglooBlock-username' ).value;						thisRevert.usetemplate = document.getElementById ( 'iglooBlock-template' ).value;						thisRevert.blockReason = document.getElementById ( 'iglooBlock-reason' ).value;						thisRevert.customsettings = ;						if ( document.getElementById ( 'iglooBlock-autoblock' ).checked == true ) thisRevert.customsettings += '&autoblock=';						if ( document.getElementById ( 'iglooBlock-blocktalk' ).checked != true ) thisRevert.customsettings += '&allowusertalk='; if ( document.getElementById ( 'iglooBlock-anononly' ).checked == true ) thisRevert.customsettings += '&anononly='; if ( document.getElementById ( 'iglooBlock-blockemail' ).checked == true ) thisRevert.customsettings += '&noemail='; if ( document.getElementById ( 'iglooBlock-blockcreate' ).checked == true ) thisRevert.customsettings += '&nocreate='; thisRevert.blockUser ( 6 ); return true; // done! } );					break;				case 6:					// DO THE BLOCK! Note that this function can be called even without reverting any page.					if ( ( ! thisRevert.useduration ) || ( ! thisRevert.usetemplate ) || ( ! thisRevert.revertUser ) ) { if ( ! thisRevert.revertUser ) thisRevert.revertUser = 'no user supplied'; igloo.iglooStatus.addStatus( 'Aborted block of ' + thisRevert.revertUser + ' : an error occurred!' ); return false; }					//alert (thisRevert.customsettings);					if ( thisRevert.customsettings === false ) { // the block 'settings' haven't been altered (e.g. autoblock, block talk etc.), so use the default block settings.						thisRevert.customsettings = ( thisRevert.isIp === true ) ? iglooSettings.anonBlockSettings : iglooSettings.userBlockSettings;					}					// do the actual block!					igloo.iglooStatus.addStatus( 'Performing block of ' + thisRevert.revertUser + ' ...' );					wa ( ':api' ).block ( 'ig_doblock', thisRevert.revertUser, 0, thisRevert.useduration, thisRevert.blockReason, thisRevert.customsettings ).wait ( function { var result = wa ( ':api' ).results['ig_doblock']; if ( typeof result['block'] !== 'undefined' ) { // success igloo.iglooStatus.addStatus( 'Successfully blocked ' + thisRevert.revertUser + ' !' ); thisRevert.warnUser ( 2 ); } else { // failure igloo.iglooStatus.addStatus( 'Failed to block ' + thisRevert.revertUser + ' - reason: ' + result['error']['info'] ); return false; }					}).run ;					return true;					break;			}		}		this.handleFinalWarning = function ( callback ) {			// this helper function is called when a user who already has a final warning is reverted. It decides what to do based on the user settings, and			// is able to prompt for input if it is unsure.			switch ( callback ) {				default: case 0:					// If we reach a final warning, remember that no further action is required if the user is already blocked!					wa ( ':api' ).command ( 'currentlyblocked', 'format=xml&action=query&list=blocks&bkusers=' + thisRevert.revertUser ).wait ( function { thisRevert.handleFinalWarning ( 1 ); } ).run ;					break;				case 1:					// If already blocked, tell and exit.					if ( wa ( ':api' ).results['currentlyblocked']['query']['blocks'] !== null ) {						igloo.iglooStatus.addStatus( 'igloo will take no further action because ' + thisRevert.revertUser + ' is currently blocked.' ); return false; }					// If you're not an admin, igloo won't let you choose. :) Also report if that's the preferred setting.					if ( ( iglooSettings.mesysop === false ) || ( iglooSettings.blockAction === 'report' ) ) { thisRevert.reportUser ; return true; }					// handle settings					if ( iglooSettings.blockAction === 'prompt' ) {						iglooSettings.blockAction = 'report'; // temporarily, so we don't see this screen again this session						// manage the prompt						var title = 'igloo needs you to choose your block settings'; 						var message = 'Because you are an administrator, igloo can automatically block users on your behalf when you revert someone who has received a final warning. ' +										'The igloo autoblocker will automatically choose and block with the most appropriate settings based on the user in question (you will still be prompted for permission to continue). ' +										'If you want more control, the standard block setting will show you relevant data about the user and allow you to make the block yourself. Alternatively, igloo can simply report the user to AIV. What do you want to do?

' +										'&gt;&gt; igloo autoblock (recommended) | standard block | report ' igloo.iglooControls.getPermission ( message, false, true, title ); // set the functions document.getElementById( 'igloo-set-block-auto' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide ; igloo.iglooControls.userMessageContent.hide; thisRevert.blockUser ; iglooSettings.blockAction = 'auto'; var url = iglooSettings.remoteHost + 'main.php?action=settings&do=set&session=' + igloo.iglooSession.session + '&me=' + encodeURIComponent ( wgUserName ) + '&setting=blockAction&value=auto'; iglooImport( url, true, 'iglooSettings' ); } document.getElementById( 'igloo-set-block-standard' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide ; igloo.iglooControls.userMessageContent.hide; thisRevert.blockUser ( 0, 'standard' ); iglooSettings.blockAction = 'standard'; var url = iglooSettings.remoteHost + 'main.php?action=settings&do=set&session=' + igloo.iglooSession.session + '&me=' + encodeURIComponent ( wgUserName ) + '&setting=blockAction&value=standard'; iglooImport( url, true, 'iglooSettings' ); } document.getElementById( 'igloo-set-block-report' ).onclick = function { igloo.iglooControls.blockActions = false; igloo.iglooControls.userMessage.hide ; igloo.iglooControls.userMessageContent.hide; thisRevert.reportUser ; iglooSettings.blockAction = 'report'; var url = iglooSettings.remoteHost + 'main.php?action=settings&do=set&session=' + igloo.iglooSession.session + '&me=' + encodeURIComponent ( wgUserName ) + '&setting=blockAction&value=report'; iglooImport( url, true, 'iglooSettings' ); } } else if ( iglooSettings.blockAction === 'standard' ) { this.blockUser ( 0, 'standard' ); return true; } else if ( iglooSettings.blockAction === 'auto' ) { this.blockUser ; return true; }					return false; break; }		}		// score object var userScore = igloo.iglooNet.scoreObject ( this.revertUser ); // check whether this user is an IP address, or a registered user if ( this.revertUser.match ( /^[0-9]+\.[0-9]+\.[0-9]+\.?[0-9]*$/i ) !== null ) { this.isIp = true; this.checked = { 'auto': ,'talk': ,'anon': 'checked','email': ,'create': 'checked' }; } else { this.isIp = false; this.checked = { 'auto': 'checked','talk': ,'anon': ,'email': ,'create': 'checked' }; } // if we're using this for blocking, for example, don't perform default actions if ( this.revertPage === 0 ) return true; // checks if ( userScore[0] === false ) userScore[0] = iglooSettings.defaultUserScore; if ( ( this.revertUser == wgUserName ) && ( iglooSettings.promptRevertSelf === true ) ) { igloo.iglooControls.getPermission ( 'You are attempting to revert yourself. Ensure you wish to perform this action. igloo will not warn or report users who are reverting themselves.', function ( thisRevert ) { thisRevert.performRollback ( 0, 'cont' ); } ); } else if ( userScore[0] < 0.4 ) { igloo.iglooControls.getPermission ( 'You are attempting to revert a change that iglooNet believes is free from vandalism. Remember that igloo should only be used to revert blatant vandalism.', function ( page, user ) { thisRevert.performRollback ( page, user ); } ); } else if ( igloo.iglooDiff.haschanged === true ) { igloo.iglooControls.getPermission ( 'The page you are viewing has changed since it was first loaded. Ensure that the change you were reverting has not already been fixed.', function ( page, user ) { thisRevert.performRollback ( page, user ); } ); } else { this.performRollback ( page, this.revertUser ); } }	function iglooPopup { // igloo popup handles the general background interace for internal windows, such as the settings window, block adjustment window, custom warnings window, deletion window etc.		this.start = function { this.popupMenu = new wa_window ; this.popupMenu.win_alpha = 0.7; this.popupMenu.win_bg = '#000000'; this.popupMenu.win_disp = 'none'; this.popupMenu.win_maintfill = false; this.popupMenu.win_fill = true; this.popupMenu.applyAll ; if ( igloo.canDebug === true ) console.log ( 'igloo: created popup background' ); this.popupMenuContent = new wa_window ; this.popupMenuContent.win_bg = '#ccccff'; this.popupMenuContent.win_width = 800; this.popupMenuContent.win_height = 400; this.popupMenuContent.win_padding = 0; this.popupMenuContent.win_disp = 'none'; this.popupMenuContent.win_bd = '#000000'; this.popupMenuContent.win_bd_wd = 2; this.popupMenuContent.win_content = ' POPUP INIT '; this.popupMenuContent.applyAll ; if ( igloo.canDebug === true ) console.log ( 'igloo: created popup main' ); return true; }		this.show = function ( content ) { if ( content !== null ) { // content this.popupMenuContent.win_content = ' ' + content + ' '; this.popupMenuContent.applyAll ; }			// show this.popupMenu.show ; this.popupMenuContent.show ; // center this.popupMenuContent.center ( 'both' ); }		this.hide = function { this.popupMenu.hide ; this.popupMenuContent.hide ; }	}	function iglooStatus { // the iglooStatus module handles the display and updating of the status window where we keep track of the actions we've taken for the user this.idCounter = 1; this.start = function { this.startInterface; }		this.startInterface = function { var iglooCount = igloo.iglooNet.iglooCount; var iglooLoadTime = igloo.endload - igloo.startload; if ( iglooSettings.lastConnectIp !== 0 ) { var iglooLastConnection = 'You last connected from ' + iglooSettings.lastConnectIp + ', ' + iglooSettings.lastConnectTime; } else { var iglooLastConnection = ''; } this.intDisplay = new wa_window(igloo.iglooInterface); this.intDisplay.win_left = 190; this.intDisplay.win_top = parseInt(igloo.iglooInterface.win_obj.style.height) - 150; this.intDisplay.win_width = parseInt(igloo.iglooInterface.win_obj.style.width) - 200; this.intDisplay.win_height= 140; this.intDisplay.win_bg = '#ddddee'; // #'ededff'; this.intDisplay.win_bd_tp = '1px solid #000000'; this.intDisplay.win_bd_lf = '1px solid #000000'; this.intDisplay.win_padding = 5; this.intDisplay.win_content = ' <div id="statusObj_0" class="statusObj">Welcome to igloo! This is your status window, where you see the actions that igloo is taking on your behalf. stats: iglooNet currently has data on ' + iglooCount + ' unique entities. stats: igloo loaded in: ' + ( iglooLoadTime / 1000 ) + ' seconds. stats: ' + iglooLastConnection + ' '; // this.intDisplay.applyAll; }		this.destroy = function { this.intDisplay.hide; }		this.addStatus = function(message) { var curDate = new Date; var sec = curDate.getSeconds; if ( sec < 10 ) sec = '0' + sec; var mins = curDate.getMinutes; if ( mins < 10 ) mins = '0' + mins; var hours = curDate.getHours; if ( hours < 10 ) hours = '0' + hours; var dateString = hours + ':' + mins + ':' + sec; var statusId = this.idCounter; this.idCounter ++; var newStatus = document.createElement('div'); newStatus.id = 'statusObj_' + statusId; newStatus.className = 'statusObj'; newStatus.innerHTML = ' ' + dateString + ' - ' + message + ' '; //var newStatusText = document.createTextNode(dateString + ' - ' + message); //newStatus.appendChild(newStatusText); var statusObj = document.getElementById('iglooStatusDisplay'); statusObj.insertBefore(newStatus, statusObj.firstChild); return statusId; }		this.updateStatus = function(id, message, action) { // action = 'append', 'prepend', 'overwrite' or 'destroy' - default 'append' if (document.getElementById('statusObj_' + id)) { var statusObj = document.getElementById('statusObj_' + id); } else { return false; } switch (action) { default: case 'append': statusObj.innerHTML = statusObj.innerHTML + message; return statusObj.innerHTML; break; case 'prepend': statusObj.innerHTML = message + statusObj.innerHTML ; return statusObj.innerHTML; break; case 'overwrite': statusObj.innerHTML = message; return statusObj.innerHTML; break; case 'destroy': statusObj.parentElement.removeChild(statusObj); return true; break; }		}	}	// launch! var igloo = new iglooMain; igloo.launch; //