User:Ale jrb/Scripts/iglooMain.js

//

/* ======================================================== *\

  • igloo frontend manager - main

\* ======================================================== */

/*

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.

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] == 'oversight' ) { t2 += 'oversighter|'; }

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 = 'edit';

optionsString += ' | delete';

} else {

optionsString = '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
  • ';

    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.
    '

    v = iglooSettings.updateTime;

    cont += '

    ';

    v = iglooSettings.updateQuantity;

    cont += '

    ';

    v = iglooSettings.updateLimit;

    cont += '

    ';

    v = iglooSettings.promptRevertSelf ? 'checked' : '';

    cont += '

    ';

    v = iglooSettings.profFilter ? 'checked' : '';

    cont += '

    ';

    cont += '

    Update time
    Update quantity
    Update list limit
    Prompt on self revert
    Enable profanity highlighting
    ';

    tabcont.innerHTML = cont;

    break;

    case 'interface':

    tabcont.innerHTML = ''; // blank

    var cont = '';

    cont += '

    Change igloo interface settings here.
    '

    v = iglooSettings.preloadInterface ? 'checked' : '';

    cont += '

    ';

    v = iglooSettings.diffFontSize;

    cont += '

    ';

    v = iglooSettings.enableFeedColour ? 'checked' : '';

    cont += '

    ';

    v = iglooSettings.histWinTimeout;

    cont += '

    ';

    cont += '

    Preload interface elements
    Diff font size (px)
    Enable feed colouring
    History window timeout
    ';

    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.
    '

    v = iglooSettings.preloadInterface ? 'checked' : '';

    cont += '

    ';

    //v = iglooSettings.enableFeedColour ? 'checked' : '';

    //cont += '

    ';

    //v = iglooSettings.histWinTimeout;

    //cont += '

    ';

    cont += '

    Action after final warning
    Enable feed colouring
    History window timeout
    ';

    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.
    '

    cont += '

    ';

    cont += '

      ' + filterText + '
    ';

    cont += '

    ';

    cont += '

    Select \'edit\' from a filter on the left to begin...
    ';

    cont += '

    ';

    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 = '

      ';

      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 = '';

      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 += '

    • '+newPage+this.recentChanges[i][0]+'
    • ';

      }

      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.replace ( / /g, ' ' );

      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, ' ' );

      attempt = attempt.replace ( /&/g, '&' );

      attempt = attempt.replace ( /®/g, '®' );

      attempt = attempt.replace ( /©/g, '©' );

      // final replacements

      attempt = attempt.replace ( /&([^#])/g, '&$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 ( '<', '<' ); t = t.replace ( '>', '>' ); 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.getElementsByTagName ( 'table' );

      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';

      var text = this.ajax.pageRequest.responseText;

      text = text.substring ( text.indexOf ( '' ), text.indexOf ( '

      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...
      ';

      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 += '

    • '+revision['user']+'
    • ';

      }

      pageHistory += '

    • - full history -
    • ';

      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 = '

      ';

      panelButton += '

      '; buttons ++;

      panelButton += '

      '; buttons ++;

      if ( iglooSettings.mesysop === true ) { panelButton += '

      '; buttons ++; }

      var browsePos = ( 62 * buttons ) - 15;

      panelButton += '

      ';

      panelButton += '

      ';

      panelButton += '

      ';

      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{{' + message + '}}';

      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 += '

      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 += '

      ';

      content += '

      ';

      content += '

      ';

      content += '

      ';

      content += '

      ';

      content += '

      ';

      content += '

      Username:' + lastlength + '
      Duration: (or type)
      Reason:
      Notify with template (igloo will automatically add \'{{\' and \'}}\'):
      Details:
      ';

      content += '

      ';

      content += '

      ';

      content += '

      ';

      content += '

      Autoblock:
      Anon only:
      Block acc create:
      Block talk:
      Block email:
      ';

      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?

      ' +

      '>> 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 = '

      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;

      }

      }

      }

      if (typeof iglooSettings !== 'undefined' && typeof wa_document !== 'undefined') {

      // launch!

      var igloo = new iglooMain();

      igloo.launch();

      }

      //