// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
// @output_file_name classBlueMax.min.js
// ==/ClosureCompiler==
function classBlueMax(containerName, hex) {
	this.containerName = containerName;
	this.container = $(containerName);
	this.width = 10;
	this.height = 10;
	this.hex = hex;
	this.userID = 0;
	this.userInGame = false;
	this.userFaction = '';
	this.userPlane = [];
	this.user = [];
	this.plane = [];
	this.turn = [];
	this.urls = {
		bluemax:"json/bluemax.php",
		campaignDetail:"index.php?tab=campaign&stab=detail&cid="
	};
	this.showSummary = true;
	this.canPoll = true;
	this.activePolls = 0;
	this.pollerIID = -1;
	this.tailPollerIID = -1;
	this.pickupsTested = false;
	
	this.sidePanelFocus = 'summary';
	
	/*=================================================================
	BOARD SETUP
	This is all the parts of the board that are set up once at the 
	beginning of the session.
	=================================================================*/	
	
	/*=================================================================
	Game set up
	=================================================================*/
	this.init = function () {
		$.ajax( {
			url:this.urls.bluemax,
			data:'action=getgame',
			type:'POST',
			dataType:'json',
			callingObject: this,
			success:function(data, textStatus) {
				this.callingObject.gameData = data;
				this.callingObject.setGame(data.game);
			},
			error:function (XMLHttpRequest, textStatus, errorThrown) {
				if($.trace) { $.trace('classBlueMax init error'); }
				if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
			}
		} );
	};
	
	this.setGame = function (data) {
		this.gameID = data.id;
		this.campaignID = data.cmpg;
		this.gameStatus = data.status;
		if (this.gameStatus == 'over') { this.canPoll = false; }
		this.userID = data.user;
		this.turnLast = data.turn;
		this.turnShow = data.turn;
		this.width = data.width;
		this.height = data.height;
		
		this.panelFoot = $('<div id="bmActionBar" class="bmFootPanel"></div>').appendTo(this.container);
		this.panelSide = $('<div class="bmSidePanel"></div>').appendTo(this.container);
		this.boardFrame = $('<div class="bmFrame"></div>').appendTo(this.container);
		
		/*-----------------------------------------
		Create game board
		-----------------------------------------*/
		this.boardWidth = ( this.hex.width * this.width ) + 1;
		this.boardHeight = ( (this.hex.height - this.hex.overlap) * this.height ) + this.hex.overlap;
		
		this.boardPane = $('<div class="bmPane"></div>')
			.css({'width':this.boardWidth, 'height':this.boardHeight })
			.appendTo(this.boardFrame);
		this.background = $('<div class="bmBackground"></div>')
			.css({'width':this.boardWidth, 'height':this.boardHeight, 'opacity':1 })
			.appendTo(this.boardPane);
		this.hexgrid = $('<div class="bmHexgrid"></div>')
			.css({'width':this.boardWidth, 'height':this.boardHeight, 'opacity':0.5 })
			.appendTo(this.boardPane);
		
		/*-----------------------------------------
		Create action bar
		-----------------------------------------*/
		this.actionBar = this.panelFoot;
		this.actionBar.html('<div class="content"><div class="tabHolder"></div></div>');
		
		/*-----------------------------------------
		Create play back controls
		-----------------------------------------*/
		this.boardControls = $('<div id="bmControls"></div>')
			.html(	'<div class="miniMap rounded6"><div class="hilite rounded4"></div></div>' +
				  	'<div id="bmControlRight" class="rounded6 small"></div>' +
					'<div id="bmControlCenter" class="rounded6"> ' +
					'<div id="controlsTurnNumber" class="rounded4"> '+(this.turnShow+1)+' of '+(this.turnLast+1)+' </div>' +
					'<img src="media/images/control_first.gif" class="control ctrlFirst" alt="First Turn" title="First Turn" border="0" style="float:left;" />' +
					'<img src="media/images/control_previous.gif" class="control ctrlPrevious" alt="Previous Turn" title="Previous Turn" border="0" style="float:left;" />' +
					'<img src="media/images/control_last.gif" class="control ctrlLast" alt="Last Turn" title="Last Turn" border="0" style="float:right;" />' +
					'<img src="media/images/control_next.gif" class="control ctrlNext" alt="Next Turn" title="Next Turn" border="0" style="float:right;" />' +
					'</div>')
			.bind('click', {bm:this}, function(e){
				var $target = $(e.target);
				if ($target.hasClass('ctrlFirst')) {
					e.data.bm.turnShow = 0;
					e.data.bm.getTurn(0);
				} else if ($target.hasClass('ctrlPrevious')) {
					e.data.bm.turnShow--;
					if (e.data.bm.turnShow < 0) { e.data.bm.turnShow = 0; }
					e.data.bm.getTurn(e.data.bm.turnShow);
				} else if ($target.hasClass('ctrlNext')) {
					e.data.bm.turnShow++;
					if (e.data.bm.turnShow > e.data.bm.turnLast) { e.data.bm.turnShow = e.data.bm.turnLast; }
					e.data.bm.getTurn(e.data.bm.turnShow);
				} else if ($target.hasClass('ctrlLast')) {
					e.data.bm.turnShow = e.data.bm.turnLast;
					e.data.bm.getTurn(e.data.bm.turnLast);
				}
			})
			.appendTo(this.panelSide);
			
		if (data.cmpg !== undefined && data.cmpg !== 0) {
			$('<a href="'+this.urls.campaignDetail+data.cmpg+'" class="titlebutton rounded3">Campaign detail</a>').appendTo($('#bmControlRight'));
		}
			
		$('div.hilite', this.boardControls)
			.css('opacity',0.4)
			.draggable({ 
				containment: 'div.miniMap',
				drag:function(e,ui){ 
					$('div.bmFrame')
						.scrollLeft(ui.position.left/56 * $('div.bmPane').width())
						.scrollTop(ui.position.top/56 * $('div.bmPane').height());
				}
			})
			.disableSelection();
		
		// TODO: Fix scroll proportions
		this.boardFrame.scroll( function () {
			$('div.hilite')
				.css('top', Math.floor($(this).scrollTop()/10)+'px')
				.css('left', Math.floor($(this).scrollLeft()/10)+'px');
		});
			
		this.panelSideContent = $('<div class="bmSidePanelContent"></div>').appendTo(this.panelSide);
		this.summaryModule = $('<div id="summaryModule" class="sideModule"></div>').appendTo(this.panelSideContent);
		this.planeDetail = $('<div id="planeDetailContainer"></div>').appendTo(this.panelSideContent);
		this.chatModule = $('<div id="chatModule" class="sideModule"></div>').appendTo(this.panelSideContent);
		
		/*-----------------------------------------
			Creat Game Elements
		-----------------------------------------*/
		this.buildSummary();
		this.setPlanes(this.gameData.planes); // Do before chat to find if user is in the game
		this.getTurn(this.turnLast);
		this.buildChat();
		
		// Fix sizes
		$(window).bind('resize', {bm:this}, function(e){ e.data.bm.resize(); });
		$(window).bind('scroll', {bm:this}, function(e){ e.data.bm.resize(); });
		this.resize();		
		
	};
	
	this.resize = function() {
		this.container.height($('body').height() - $('#hexcellHeader').height());
		this.panelSide.height(this.container.height() - $('div.bmFootPanel', this.container).height());
		this.boardFrame
			.height(this.container.height() - this.panelFoot.height())
			.width(this.container.width() - this.panelSide.width());
		var hW = (this.boardFrame.width()<this.boardWidth)?(this.boardFrame.width()/this.boardWidth*56):56;
		var hH = (this.boardFrame.height()<this.boardHeight)?(this.boardFrame.height()/this.boardHeight*56):56;
		$('div.hilite', this.boardControls).width(hW).height(hH);
		this.focusSidePanel(this.sidePanelFocus, true);
	}
	
	this.focusSidePanel = function(newFocus, force) {
		var allowedHeight;
		if (force === undefined) { force = false; }
		if (!force && newFocus == this.sidePanelFocus) { newFocus = (newFocus == 'planes')?'summary':'planes'; }
		this.panelSideContent.css('height', this.panelSide.height() - $('#bmControls').outerHeight() );
		allowedHeight = this.panelSideContent.height() - 91;
		this.sidePanelFocus = newFocus;
		switch(newFocus) {
			case 'summary':
				$('div.planeSideModule div.smContainer').stop(true,false).animate({'height':0},400, function(){ $(this).hide(); });
				$('#chatModule div.smContainer').stop(true,false).animate({'height':50},400, function(){ $(this).css('overflow-y','hidden'); });
				$('#summaryModule div.smContainer').show().stop(true,false).animate({'height':allowedHeight-50},400).css('overflow-y','scroll');
				$('div.planeSideModule, #chatModule').removeClass('open');
				$('#summaryModule').addClass('open');
				$('div.planeSideModule div.smTitle').removeClass('roundtop6').addClass('rounded6');
				$('#summaryModule div.smTitle, #chatModule div.smTitle').addClass('roundtop6').removeClass('rounded6');
				if (this.chatObject) { this.chatObject.size($('#chatModule div.smContainer').width()-8,46,false); }
				break;
			case 'planes':
				$('#summaryModule div.smContainer').stop(true,false).animate({'height':0},400, function(){ $(this).hide(); });
				$('#chatModule div.smContainer').stop(true,false).animate({'height':50},400, function(){ $(this).css('overflow-y','hidden') });
				$('div.planeSideModule div.smContainer').show().stop(true,false).animate({'height':allowedHeight-50},400, function(){ $(this).css('overflow-y','scroll') });
				$('#summaryModule, #chatModule').removeClass('open');
				$('div.planeSideModule').addClass('open');
				$('#summaryModule div.smTitle').removeClass('roundtop6').addClass('rounded6');
				$('div.planeSideModule div.smTitle, #chatModule div.smTitle').addClass('roundtop6').removeClass('rounded6');
				if (this.chatObject) { this.chatObject.size($('#chatModule div.smContainer').width()-8,46,false); }
				break;
			case 'chat':
				$('#summaryModule div.smContainer, div.planeSideModule div.smContainer').stop(true,false).animate({'height':0},400, function(){ $(this).hide(); });
				$('#chatModule div.smContainer').stop(true,false).animate({'height':allowedHeight},400).css('overflow-y','scroll');
				if (this.chatObject) { this.chatObject.size($('#chatModule div.smContainer').width()-8,allowedHeight-8,this.userInGame); }
				$('#summaryModule, div.planeSideModule').removeClass('open');
				$('#chatModule').addClass('open');
				$('#summaryModule div.smTitle, div.planeSideModule div.smTitle').removeClass('roundtop6').addClass('rounded6');
				$('#chatModule div.smTitle').addClass('roundtop6').removeClass('rounded6');
				break;
		}
		// Fix layout in Firefox
		if (this.panelSideContent.offset().top > $('#summaryModule').offset().top) {
			$('#summaryModule').css('padding-top',4 + this.panelSideContent.offset().top - $('#summaryModule').offset().top);
		}
	}
	
	/*=================================================================
	Plane set up
	=================================================================*/
	this.setPlanes = function (planeData) {
		var i, ilen, tabSelected = false;
		for (i=0, ilen=planeData.length; i<ilen; i++) {
			if(this.userID == planeData[i].userID && $.inArray(planeData[i].index, this.userPlane) == -1) {
				this.userPlane.push(planeData[i].index);
				this.userFaction = planeData[i].faction;
				this.userInGame = true;
			}
			if(planeData[i].userID !== 0 && $.inArray(planeData[i].userID, this.user) == -1) {
				this.user.push(planeData[i].userID);
			}
		}
		for (i=0, ilen=planeData.length; i<ilen; i++) {
			this.addPlane( planeData[i] );
		}
		
		if (this.userPlane.length > 0) {
			$('#tab_'+this.userPlane[0]).addClass('open');
			$('#rollover_'+this.userPlane[0]).show();
		} else {
			$('div.actionbarTab:eq(0)').addClass('open');
			$('div.planeSideModule:eq(0)').show();
		}
		
		// Now resize after all the initial data is in;
		this.resize();
	};
	
	this.addPlane = function (data) {
		this.plane[data.index] = {
			id: data.id,
			index: data.index,
			user: data.user,
			name: data.name,
			faction: data.faction,
			status: 'active',
			lastStatus: 'active',
			fuel: data.fuel,
			sprite: $('<div class="bmPlane"></div>').hide().appendTo(this.boardPane),
			bgimg: data.sprite+'_'+data.spriteNum+'.png',
			location: [0,0,0],
			facing: 0,
			ghostSprite: $('<div class="bmGhost"></div>').hide().appendTo(this.boardPane),
			ghostLocation: [0,0,0],
			ghostFacing: 0,
			pilot: 0
		};

		this.plane[data.index].sprite
			.css({'top':0, 'left':0, 'background-image':'url(media/images/planes/'+this.plane[data.index].bgimg+')'})
			.data('id',data.index)
			.bind('click', function(){
				$('#tab_'+$(this).data('id')).trigger('click');
			});
		this.plane[data.index].ghostSprite
			.css({ 'top':0, 'left':0, 'opacity':0.5, 'background-image':'url(media/images/planes/'+this.plane[data.index].bgimg+')' });
		
		if (data.pilot !== undefined) {
			this.plane[data.index].pilot = data.pilot;
			this.plane[data.index].pilotName = data.pilotName;
			this.plane[data.index].pilotRank = data.pilotRank;
		}
		
		// Add tab and panel
		$('#tab_'+data.index+' div.plane').css('background-image','url(media/images/planes/'+this.plane[data.index].bgimg+')');
		if ( this.userID == data.userID ) {
			$('<div class="commitBtn buttonLarge">Lock in this maneuver</div>')
				.bind('click', {planeID:data.id, board:this}, function(e){
					e.data.board.maneuverCommit(e.data.planeID, e);
				})
				.appendTo($('#planeControls_'+data.index+' div.controls'));
		}
		this.buildRollover(data);
		this.buildManeuvers(data.index, data.maneuvers);
	};
	
	/*=================================================================
	TURN MANAGEMENT
	Aquire and display turn data.
	=================================================================*/
	/*=================================================================
	Turn controls
	=================================================================*/	
	this.getTurn = function(turn, overWrite){
		if (overWrite === undefined) { overWrite = false; }
		if (this.turn[turn] !== undefined && !overWrite) {
			if(this.turnShow == turn) { this.setTurn(); }
			return;
		}
		$.ajax( {
			url:this.urls.bluemax,
			data:'action=getturn&t='+turn,
			type:'POST',
			dataType:'json',
			callingObject: this,
			success:function(data, textStatus) {
				this.callingObject.updateTurnData(data);
			},
			error:function (XMLHttpRequest, textStatus, errorThrown) {
				if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
			}
		} );
	};
	
	this.updateTurnData = function(data) {
		var i, ilen;
		this.turn[data.turn] = {
			turn: data.turn,
			status: data.status,
			planes: [],
			msg: data.msg
		};
		for (i = 0, ilen = data.planes.length; i < ilen; i++) {
			this.turn[data.turn].planes[data.planes[i].index] = data.planes[i];
		}
		if(this.turnShow == data.turn) { 
			this.setTurn(); 
			this.updateRollovers();
		}
		// Maybe we were getting the prior turn to show what happened
		if (this.gameStatus != 'over' && this.turnShow == this.turnLast && data.turn == this.turnShow-1) {
			this.displaySummary(data.turn);
		}
	};
	
	this.updateControls = function() {
		$('#controlsTurnNumber').text(' '+(this.turnShow+1)+' of '+(this.turnLast+1)+' ');
	};
	
	this.setTurn = function(){
		var i, ilen;
		var startLocation, endLocation;
		var startFacing, endFacing;
		var turnData = this.turn[this.turnShow];
		var startPollingForTail = false;
		
		this.gameStatus = turnData.status;
		for (i=0, ilen=turnData.planes.length; i < ilen; i++) {
			startLocation = turnData.planes[i].laf.split(',');
			startFacing = startLocation.pop();
			if (turnData.planes[i].lafend != '') {
				endLocation = turnData.planes[i].lafend.split(',');
				endFacing = endLocation.pop();
			} else {
				endLocation = startLocation;
				endFacing = startFacing;
			}
			this.plane[turnData.planes[i].index].status = turnData.planes[i].planeStatus;
			this.plane[turnData.planes[i].index].lastStatus = turnData.planes[i].lastStatus;
			this.plane[turnData.planes[i].index].location = endLocation;
			this.plane[turnData.planes[i].index].facing = endFacing;
			this.plane[turnData.planes[i].index].ghostLocation = startLocation;
			this.plane[turnData.planes[i].index].ghostFacing = startFacing;
			if ( this.turnShow == this.turnLast && turnData.planes[i].maneuvers !== undefined ) {
				this.setAllowedManeuvers(turnData.planes[i].index, turnData.planes[i].maneuvers);
			}
			//if ( this.turnShow == this.turnLast && turnData.planes[i].canTail !== undefined && turnData.planes[i].canTail.length > 0 ) { // turnData.planes[i].tailing !== 0
			//	startPollingForTail = true;
			//}
		}
		//if (startPollingForTail) { 
			this.pollForTail(); 
		//} else {
		//	this.endPollForTail();
		//}
		this.updateRollovers();
		this.updateControls();
		this.updatePlanes();
		if (this.showSummary) {
			if (this.gameStatus != 'over' && this.turnShow == this.turnLast) {
				if (this.turn[this.turnShow-1] !== undefined) {
					this.displaySummary(this.turnShow-1);
					//this.summary.show();
					$('#summaryTab').addClass('open');
				} else {
					this.getTurn(this.turnShow-1, true);
				}
			} else {
				this.displaySummary(this.turnShow);
			}	
		}
		// Access if a user has become inactive
		if (!this.pickupsTested && this.turnShow == this.turnLast) { 
			this.assessInactiveUsers();	 
		}
	};
	
	this.setAllowedManeuvers = function(planeIndex, maneuverData) {
		var i, ilen, idArray = [];
		$('#planeManeuvers_'+planeIndex+' div.maneuver.p'+planeIndex).addClass('notAllowed');
		for (i=0, ilen=maneuverData.length; i<ilen; i++) {
			idArray.push('#mnvr_'+planeIndex+'_'+maneuverData[i]);
		}
		$(idArray.join(', ')).removeClass('notAllowed');
	};
		
	this.updateLocks = function(statusArray) {
		var i, ilen;
		$('div.rollover div.lock').removeClass('lockOn').removeClass('lockDead');
		for (i=0, ilen = statusArray.length; i < ilen; i++) {
			if (statusArray[i][1] == 'locked') {
				$('#tab_'+statusArray[i][0]+' div.lock').addClass('lockOn');
			}
		}
		for (i=0, ilen = this.turn[this.turnShow].planes.length; i < ilen; i++) {
			if (this.turn[this.turnShow].planes[i].planeStatus != 'active') {
				$('#tab_'+this.turn[this.turnShow].planes[i].index+' div.lock').addClass('lockDead');
			}
		}
	};
	
	this.getNewTurn = function(newTurnNumber) {
		if (newTurnNumber != this.turnLast) {
			this.turnLast = newTurnNumber;
			this.turnShow = newTurnNumber;
			this.chatObject.turn(this.turnLast+1);
			this.getTurn(this.turnLast, true);
			this.getTurn((this.turnLast - 1), true);
			$('div.maneuver').removeClass('selected');
		} else {
			this.getTurn(this.turnLast, true);
			this.pollStart();
		}
	};
	
	/*=================================================================
	Plane render
	=================================================================*/
	this.hidePlane = function(planeID) {
		this.plane[planeID].sprite.hide();
		this.plane[planeID].ghostSprite.hide();
	};
	
	this.hideAllPlanes = function() {
		$('div.bmPlane, div.bmGhost').hide();
	};
	
	this.updatePlanes = function() {
		var i, ilen;
		var j, jlen;
		var spaceOccupied;
		var ghostSpaceOccupied;
		var cartLoc;
		var cartLocG;
		var useBGImg;
		var offSetX = [Math.round(this.hex.width/4), Math.round(this.hex.width/4), 0, (this.hex.width/2), 0, (this.hex.width/2)];
		var offSetY = [0, (this.hex.height/2), Math.round(this.hex.width/8), Math.round(this.hex.width*3/8), Math.round(this.hex.width/8), Math.round(this.hex.width*3/8)];
		for (i = 0, ilen = this.plane.length; i < ilen; i++) {
		spaceOccupied = false;
		ghostSpaceOccupied = false;
		//Test this plane's location against all others
		for (j = 0, jlen = this.plane.length; j < jlen; j++) {
			if (i == j) { continue; }
			if (this.plane[i].status != 'active') { continue; }
			if (this.plane[j].status != 'active') { continue; }
			if (this.plane[i].location.toString() == this.plane[j].location.toString() || 
				this.plane[i].location.toString() == this.plane[j].ghostLocation.toString()) {
					spaceOccupied = true;
			}
			if (this.plane[i].ghostLocation.toString() == this.plane[j].location.toString() || 
				this.plane[i].ghostLocation.toString() == this.plane[j].ghostLocation.toString()) {
					ghostSpaceOccupied = true;
			}
			if (spaceOccupied && ghostSpaceOccupied) { break; }
		}	
		if(	this.plane[i].status == 'active' ||
			(this.plane[i].status == 'destroyed' && this.plane[i].lastStatus == 'active')) {
				cartLoc = this.hex.hexToCart(this.plane[i].location);
				cartLocG = this.hex.hexToCart(this.plane[i].ghostLocation);
				if(	this.plane[i].status == 'active' ) { 
					useBGImg = 'media/images/planes/'+this.plane[i].bgimg;
				} else {
					useBGImg = 'media/images/planes/crashing.png';
				}
				if (!spaceOccupied) {
					this.plane[i].sprite
						.css({
							'left':cartLocG[0],
							'top':cartLocG[1],
							'width':this.hex.width,
							'height':this.hex.height,
							'background-position': (-this.hex.width*this.plane[i].facing)+'px 0px',
							'background-image':'url('+useBGImg+')'
						})
						.animate({ 'left':cartLoc[0], 'top':cartLoc[1] }, 'slow')
						.show();
				} else {
					this.plane[i].sprite
						.css({
							'left':(cartLocG[0] + offSetX[i]),
							'top':(cartLocG[1] + offSetY[i]),
							'width':(this.hex.width/2),
							'height':(this.hex.height/2),
							'background-position': (-this.hex.width*this.plane[i].facing*0.5)+'px -'+this.hex.height+'px',
							'background-image':'url('+useBGImg+')'
						})
						.animate({ 'left':(cartLoc[0] + offSetX[i]), 'top':(cartLoc[1] + offSetY[i])}, 'slow' )
						.show();
				}
				
				if ((this.plane[i].ghostLocation.toString() != this.plane[i].location.toString() 
					|| this.plane[i].ghostFacing != this.plane[i].facing)
					&& this.plane[i].status != 'destroyed' ) {
						cartLoc = this.hex.hexToCart(this.plane[i].ghostLocation);
						if (!ghostSpaceOccupied) {
							this.plane[i].ghostSprite
								.css({
									'left':cartLoc[0],
									'top':cartLoc[1],
									'width':this.hex.width,
									'height':this.hex.height,
									'background-position': (-this.hex.width*this.plane[i].ghostFacing)+'px 0px'
								})
								.show();
						} else {
							this.plane[i].ghostSprite
								.css({
									'left':(cartLoc[0] + offSetX[i]),
									'top':(cartLoc[1] + offSetY[i]),
									'width':(this.hex.width/2),
									'height':(this.hex.height/2),
									'background-position': (-this.hex.width*this.plane[i].ghostFacing*0.5)+'px -'+this.hex.height+'px'
								})
								.show();
						}
				} else {
					this.plane[i].ghostSprite.hide();
				}
			} else {
				// Plane is not active
				this.plane[i].sprite.hide();
				this.plane[i].ghostSprite.hide();
			}
		}
	};
	
	this.locatePlane = function(id,x,y,z,f) {
		this.plane[id].location = [x,y,z];
		this.plane[id].facing = f;
		this.updatePlanes();
	};
	
	this.centerPlane = function(index){
		var pSprite = this.plane[index].sprite;
		var pX = parseInt(pSprite.css('left'));
		var pY = parseInt(pSprite.css('top'));
		var pW = pSprite.width();
		var pH = pSprite.height();
		var fW = this.boardFrame.width();
		var fH = this.boardFrame.height();
		var newX = Math.round(pX - ((fW - pW) / 2));
		var newY = Math.round(pY - ((fH - pH) / 2));
		if (fW > this.boardWidth || newX < 0) {
			newX = 0;
		} else if (newX > this.boardWidth - fW) {
			newX = this.boardWidth - fW;
		}
		if (fH > this.boardHeight || newY < 0) {
			newY = 0;
		} else if (newY > this.boardHeight - fH) {
			newY = this.boardHeight - fH;
		}
		//$('#bmControlRight').html(newX + ', ' + newY);
		this.boardFrame.animate({'scrollLeft':newX,'scrollTop':newY}, 400);
		newX = Math.round(newX / this.boardPane.width() * 56);
		newY = Math.round(newY / this.boardPane.height() * 56);
		$('div.hilight', this.boardControls).animate({'left':newX,'top':newY}, 400);
	};

	
	/*=================================================================
		Rollovers
	=================================================================*/
	this.buildRollover = function (planeData) {
		var i;
		var htmlContent = [];
		var $rollover;
		var $planetab;
		
		$rollovertab = $('<div id="tab_'+planeData.index+'" class="actionbarTab"></div>').appendTo($('#bmActionBar div.tabHolder'));
		$rollovertab
			.html(	'<div class="abtTitle roundtop6">' + 
				  	'<div class="lock rounded4"></div>' +
				  	planeData.user +'</div>'+
				  	'<div class="abtBody roundbot6">' +
					'<div class="plane" style="background-image:url(media/images/planes/'+planeData.sprite+'_'+planeData.spriteNum+'.png)"></div>' +
					'<div class="icons"></div>' +
					'<span class="planeName">'+planeData.name+'</span>' +
					'<span class="scoreChange"></span> ' +
					'<span class="scoreTotal"></span>' +
					'</div>')
			.data('index', planeData.index)
			.bind('click', {bm:this}, function(e){
				$('div.planeSideModule').hide();
				$('div.actionbarTab').removeClass('open');
				$('#rollover_'+$(this).data('index')).show();
				$('#tab_'+$(this).data('index')).addClass('open');
				e.data.bm.centerPlane($(this).data('index'));
				e.data.bm.focusSidePanel('planes', true);
			});
		
		$rollover = $('<div id="rollover_'+planeData.index+'" class="sideModule planeSideModule open"></div>').appendTo(this.planeDetail);
		$rollover
			.html(	'<div class="smTitle roundtop6">' +
					'<div class="plane"></div>' + 
					'<span class="title">' + planeData.user + ' - ' + planeData.name + '</span></div>' +
					'<div class="smContainer"><div class="smContainerScroll">' +
					'<div class="rolloverContent"></div>' +
					'<div class="rolloverTailing rounded4"></div>' +
					'<div class="rolloverUserControl rounded4"></div>' +
					'<div id="planeManeuvers_'+planeData.index+'" class="rolloverManeuvers"></div>' +
					'</div></div>')
			.data('index', planeData.index)
			.hide();
			
		if (planeData.pilot !== undefined) {
			$('span.planeName', $rollovertab).html('<b>'+planeData.pilotName+'</b><br>'+planeData.name);
			$('span.title', $rollover).html(planeData.pilotName + ' (' + planeData.user + ') - ' + planeData.name);
		}
		
		$('div.smTitle', $rollover).bind('click', {bm:this}, function(e){ e.data.bm.focusSidePanel('planes', false); });
		
		htmlContent = [];
		htmlContent.push('<table cellspacing="0" class="detailTable rounded4">' +
						'<colgroup><col><col width="21"><col><col width="74"><col width="74"><col width="74"><col width="75"></colgroup>');
		if (planeData.pilot !== undefined) {
			htmlContent.push('<tr><td rowspan="5" class="flyingBG rounded3"><div class="bmPlane"></div></td>' +
							'<td rowspan="5"><div class="icons"></div></td>' + 
							'<td colspan="5">'+this.getRankName(planeData.pilotRank, planeData.faction)+' '+planeData.pilotName+' - Rank '+planeData.pilotRank+'</td>' + 
							'</tr><tr>');
		} else {
			htmlContent.push('<tr><td rowspan="4" class="flyingBG rounded3"><div class="bmPlane"></div></td>' +
							'<td rowspan="4"><div class="icons"></div></td>');
		}
		htmlContent.push('<td>Stability</td>' +
						'<td><span class="value">'+planeData.fuselage+'</span>Fuselage</td>' +
						'<td><span class="value">'+planeData.wing+'</span>Wings</td>' +
						'<td><span class="value"><span class="fuelSpent"></span>'+planeData.fuel+'</span>Fuel</td>' +
						'<td align="center">Last Turn</td>' +
						'</tr>');
		htmlContent.push('<tr><td align="center">'+planeData.stability+'</td><td>');
		for (i=0; i < planeData.fuselage; i++ ) { htmlContent.push('<div class="check fuselage"></div>'); }
		htmlContent.push('</td><td>');
		for (i=0; i < planeData.wing; i++ ) { htmlContent.push('<div class="check wing"></div>'); }
		htmlContent.push('</td><td rowspan="3">');
		for (i=0; i < planeData.fuel; i++ ) { htmlContent.push('<div class="check fuel"></div>'); }
		htmlContent.push('</td><td rowspan="3" align="center"><span class="lastManeuver"></span></td>');
		htmlContent.push('<tr><td>Guns</td>' +
						'<td><span class="value">'+planeData.engine+'</span>Engine</td>' +
						'<td><span class="value">'+planeData.tail+'</span>Tail</td></tr>');
		htmlContent.push('<tr><td align="center"><span class="guns" rel="'+planeData.guns+'">'+planeData.guns+'</span>');
		if (planeData.rguns) { htmlContent.push('<br>+<br><span class="rguns" rel="'+planeData.rguns+'">'+planeData.rguns+'</span>'); }
		htmlContent.push('</td><td>');
		for (i=0; i < planeData.engine; i++ ) { htmlContent.push('<div class="check engine"></div>'); }
		htmlContent.push('</td><td>');
		for (i=0; i < planeData.tail; i++ ) { htmlContent.push('<div class="check tail"></div>'); }
		htmlContent.push('</td></tr></table>');
		$('div.rolloverContent', $rollover).html(htmlContent.join(''));
		
		$('table.detailTable div.bmPlane', $rollover)
			.css('background-image', 'url(media/images/planes/'+planeData.sprite+'_'+planeData.spriteNum+'.png)');
			
		if ( this.userID == planeData.userID ) {
			
			$('div.rolloverUserControl', $rollover)
				.html('<div class="message"></div><div class="controls"></div>');
			
			$('<div class="commitBtn buttonLarge">Lock in this maneuver</div>')
				.bind('click', {planeID:planeData.id, board:this}, function(e){
					e.data.board.maneuverCommit(e.data.planeID, e);
				})
				.appendTo($('div.controls', $rollover));
		} else {
			$('div.rolloverUserControl', $rollover).remove();
		}
	};
	
	this.buildManeuvers = function(planeIndex, data){
		var i, ilen;
		var scheduleTable = $('<table cellspacing="0" class="maneuverTable rounded4"></table>').prependTo($('#planeManeuvers_'+planeIndex));
		$('<colgroup><col /><col width="115" /><col /></colgroup>').appendTo(scheduleTable);
		var restrictedShown = false;
		var stallShown = false;
		var regularShown = false;
		var cellClass;
		var maneuver;
		
		for (i = 0, ilen = data.length; i < ilen; i++) {
			switch (data[i].direction){
				case 's':
					cellClass = '.mStraight';
					break;
				case 'l':
					cellClass = '.mLeft';
					break;
				case 'r':
					cellClass = '.mRight';
					break;
			}
			cellClass = '.f'+data[i].fuel + '.r'+data[i].restricted + ' ' + cellClass;
			if (data[i].number == 1) { cellClass = '.s1' + cellClass; } else { cellClass = '.s0' + cellClass; }
			if ($(cellClass, scheduleTable).length < 1) {
				if (!stallShown && data[i].number == 1) {
					stallShown = true;
					$('<tr class="subtitle"><td colspan="3">Stall maneuvers</td></tr>').appendTo(scheduleTable);
					$('<tr class="s1 f'+data[i].fuel+' r'+data[i].restricted+'"><td class="mLeft"></td><td class="mStraight"></td><td class="mRight"></td></tr>').appendTo(scheduleTable);
				} else if (!restrictedShown && data[i].restricted == 1) {
					restrictedShown = true;
					$('<tr class="subtitle"><td colspan="3">Restricted maneuvers</td></tr>').appendTo(scheduleTable);
					$('<tr class="s0 f'+data[i].fuel+' r'+data[i].restricted+'"><td class="mLeft"></td><td class="mStraight"></td><td class="mRight"></td></tr>').appendTo(scheduleTable);
				} else {
					if (!regularShown) {
						regularShown = true;
						$('<tr class="subtitle"><td colspan="3">Regular maneuvers</td></tr>').appendTo(scheduleTable);	
					}
					$('<tr class="s0 f'+data[i].fuel+' r'+data[i].restricted+'"><td class="mLeft"></td><td class="mStraight"></td><td class="mRight"></td></tr>').appendTo(scheduleTable);
				}
			}
			
			data[i].displayName = data[i].name + data[i].fuel;
			if (!data[i].repeatable) { data[i].displayName = '['+data[i].displayName+']'; }
			if (data[i].direction == 'l') {
				maneuver = $('<div id="mnvr_'+planeIndex+'_'+data[i].id+'" class="maneuver p'+planeIndex+' f'+data[i].fuel+' r'+data[i].restricted+'"></div>').prependTo($(cellClass, scheduleTable));
			} else {
				maneuver = $('<div id="mnvr_'+planeIndex+'_'+data[i].id+'" class="maneuver p'+planeIndex+' f'+data[i].fuel+' r'+data[i].restricted+'"></div>').appendTo($(cellClass, scheduleTable));
			}
			maneuver.html('<div class="mnvr mnvr'+data[i].name.toLowerCase()+'"></div><br>'+data[i].displayName)
				.data('id', data[i].id)
				.data('move', data[i].move)
				.data('rotate', data[i].rotate)
				.data('fuel', data[i].fuel)
				.bind('click', {board: this, planeIndex:planeIndex}, function(e){
					if (!$(this).hasClass('notAllowed') && $(this).parent().parent().parent().parent().hasClass('active')) {
						$('.maneuver.p'+e.data.planeIndex).removeClass('selected');
						$(this).addClass('selected');
						e.data.board.maneuverSelect(e.data.planeIndex, $(this).data('id'), $(this).data('move'), $(this).data('rotate'), $(this).data('fuel'));
					}
				}).hover(
					function(){ if ($(this).parent().parent().parent().parent().hasClass('active')) { $(this).addClass('hover'); } },
					function(){ $(this).removeClass('hover'); }
				);
		}
	};
	
	this.updateRollovers = function () {
		var i, ilen, j, jlen,
			planeIndex, $planeRollover,
			turnData = this.turn[this.turnShow],
			lockCount = 0,
			htmlArray = [];
		for (i=0, ilen=turnData.planes.length; i < ilen; i++) {
			planeIndex = turnData.planes[i].index;
			$planeRollover = $('#rollover_'+planeIndex);
			$planeRolloverTab = $('#tab_'+planeIndex);
			//$('div.lock', $planeRollover).removeClass('lockOn').removeClass('lockDead');
			$('div.lock', $planeRolloverTab).removeClass('lockOn').removeClass('lockDead');
			if (turnData.planes[i].turnStatus == 'locked') {
				//$('div.lock', $planeRollover).addClass('lockOn');
				$('div.lock', $planeRolloverTab).addClass('lockOn');
			}
			if (turnData.planes[i].planeStatus != 'active') {
				//$('div.lock', $planeRollover).addClass('lockDead');
				$('div.lock', $planeRolloverTab).addClass('lockDead');
			}

			// Re-orientate plane
			var facing = turnData.planes[i].lafend.split(',').pop();
			$('table.detailTable div.bmPlane', $planeRollover).css('background-position', (-this.hex.width*facing)+'px 0px');
			
			// Check damage and fuel spent
			$('div.check', $planeRollover).removeClass('checked').removeClass('checkednow');
			if ($.inArray(planeIndex, this.userPlane) != -1 || this.gameStatus == 'over') { 
				$('div.check.engine:lt('+turnData.planes[i].engine+')', $planeRollover).addClass('checked');
				$('div.check.wing:lt('+turnData.planes[i].wing+')', $planeRollover).addClass('checked');
				$('div.check.fuselage:lt('+turnData.planes[i].fuselage+')', $planeRollover).addClass('checked');
				$('div.check.tail:lt('+turnData.planes[i].tail+')', $planeRollover).addClass('checked');
				$('div.check.fuel:lt('+turnData.planes[i].fuel+')', $planeRollover).addClass('checked');
				$('span.fuelSpent', $planeRollover).text((turnData.planes[i].fuel+turnData.planes[i].tfuel)+'/');
				$('div.check.engine', $planeRollover)
					.slice(turnData.planes[i].engine, (turnData.planes[i].engine+turnData.planes[i].tengine))
					.addClass('checkednow');
				$('div.check.wing', $planeRollover)
					.slice(turnData.planes[i].wing, (turnData.planes[i].wing+turnData.planes[i].twing))
					.addClass('checkednow');
				$('div.check.fuselage', $planeRollover)
					.slice(turnData.planes[i].fuselage, (turnData.planes[i].fuselage+turnData.planes[i].tfuselage))
					.addClass('checkednow');
				$('div.check.tail', $planeRollover)
					.slice(turnData.planes[i].tail, (turnData.planes[i].tail+turnData.planes[i].ttail))
					.addClass('checkednow');
				$('div.check.fuel', $planeRollover)
					.slice(turnData.planes[i].fuel, (turnData.planes[i].fuel+turnData.planes[i].tfuel))
					.addClass('checkednow');
			
			
				// Mark guns destroyed
				if (turnData.planes[i].dgun > 0) {
					$('span.guns', $planeRollover).html('<strike>' + $('span.guns', $planeRollover).attr('rel') + '</strike> <b>' +
														($('span.guns', $planeRollover).attr('rel') - turnData.planes[i].dgun) + '</b>');
				} else {
					$('span.guns', $planeRollover).html($('span.guns', $planeRollover).attr('rel'));
				}
				if (turnData.planes[i].drgun > 0) {
					$('span.rguns', $planeRollover).html('<strike>' + $('span.rguns', $planeRollover).attr('rel') + '</strike> <b>' +
														($('span.rguns', $planeRollover).attr('rel') - turnData.planes[i].drgun) + '</b>');
				} else {
					$('span.rguns', $planeRollover).html($('span.rguns', $planeRollover).attr('rel'));
				}
			}
			
			htmlArray = [];
			if (turnData.planes[i].planeStatus == 'destroyed') {
				htmlArray.push('<div class="icon iconDead"></div>');
			} else if (turnData.planes[i].planeStatus == 'retired') {
				htmlArray.push('<div class="icon iconRetired"></div>');
			} 
			if (turnData.planes[i].fire == 1) {
				htmlArray.push('<div class="icon iconFire"></div>');
			} else if (turnData.planes[i].smoke == 1) {
				htmlArray.push('<div class="icon iconSmoke"></div>');
			}
			$('table.detailTable div.icons', $planeRollover).html(htmlArray.join(''));
			$('div.icons', $planeRolloverTab).html(htmlArray.join(''));
			
			if (turnData.planes[i].tscore > 0) {
				$('span.scoreChange', $planeRolloverTab).text(turnData.planes[i].score + ' + '+turnData.planes[i].tscore);
				$('span.scoreTotal', $planeRolloverTab).text(turnData.planes[i].score + turnData.planes[i].tscore);
			} else if (turnData.planes[i].tscore < 0) {
				$('span.scoreChange', $planeRolloverTab).text(turnData.planes[i].score + ' - '+Math.abs(turnData.planes[i].tscore));
				$('span.scoreTotal', $planeRolloverTab).text(turnData.planes[i].score + turnData.planes[i].tscore);
			} else {
				$('span.scoreChange', $planeRolloverTab).text('');
				$('span.scoreTotal', $planeRolloverTab).text(turnData.planes[i].score);
			}
			
		}
		this.updateRolloverTail();
		this.updateRolloverTailResult();
		this.updateRolloverManeuver();
	};
	
	this.updateRolloverTail = function () {
		var i, ilen, j, jlen,
			planeIndex,
			$planeRollover,
			turnData = this.turn[this.turnShow],
			lockCount = 0,
			tailHtml, tailEnemy, tailFriend, 
			tailEnemyCount = 0;
		
		for (i=0, ilen=turnData.planes.length; i < ilen; i++) {
			planeIndex = turnData.planes[i].index;
			$planeRollover = $('#rollover_'+planeIndex);
			$('div', $planeRollover).unbind('click.tail');
			if ($.inArray(planeIndex, this.userPlane) != -1) { 
				tailHtml = [];
				tailEnemy = [];
				tailFriend = [];
				if (turnData.planes[i].planeStatus != 'active') {
					$('div.rolloverTailing', $planeRollover).html('').hide();
				} else if (turnData.planes[i].canTail !== undefined && turnData.planes[i].canTail.length > 0) {
					if(turnData.planes[i].tailDetail !== '') { tailHtml.push('<p class="small">'+turnData.planes[i].tailDetail+'</p>'); }
					
					for (j = 0, jlen = turnData.planes[i].canTail.length; j < jlen; j++) {
						if (turnData.planes[i].canTail[j][2] == this.userFaction) {
							tailFriend.push('<div class="tailBlock rounded3" rel="'+this.plane[turnData.planes[i].canTail[j][1]].id+'">');
							tailFriend.push('<div class="bmPlaneMini" style="background-image:url(media/images/planes/'+this.plane[turnData.planes[i].canTail[j][1]].bgimg+');" ');
							tailFriend.push('title="'+this.plane[turnData.planes[i].canTail[j][1]].user+' '+this.plane[turnData.planes[i].canTail[j][1]].name+'"></div>');
							if(turnData.planes[turnData.planes[i].canTail[j][1]].ownTailDirection !== undefined && turnData.planes[turnData.planes[i].canTail[j][1]].ownTailDirection !== ''){
								tailFriend.push('<div class="tailDirection">' + turnData.planes[turnData.planes[i].canTail[j][1]].ownTailDirection.toUpperCase() + '</div>'); 
							} else {
								tailFriend.push('<div class="tailDirection"></div>'); 
							}
							tailFriend.push('</div>');
						} else {
							if(turnData.planes[i].turnStatus == 'waiting' && turnData.planes[i].tailing == '0') {
								tailEnemy.push('<div class="tailBlock tailEnemy rounded3" rel="'+this.plane[turnData.planes[i].canTail[j][1]].id+'">');
								tailEnemy.push('<div class="bmPlaneMini" style="background-image:url(media/images/planes/'+this.plane[turnData.planes[i].canTail[j][1]].bgimg+');" ');
								tailEnemy.push('title="'+this.plane[turnData.planes[i].canTail[j][1]].user+' '+this.plane[turnData.planes[i].canTail[j][1]].name+'"></div>');
								tailEnemy.push('</div>');
								tailEnemyCount++;
							} 
						}
					}
					
					if (tailFriend.length > 0) {
						tailHtml.push('<div>Friendly Tails</div>');
						tailHtml.push(tailFriend.join(''));
						tailHtml.push('<div class="clearer"></div>');
					}
					
					
					if (tailEnemy.length > 0 && turnData.planes[i].tailing == '0') {
						tailHtml.push('<div class="tailEnemyGroup"><div>Select Enemy to Tail</div>');
						tailHtml.push('<input type="hidden" class="tailSelect">'); 
						tailHtml.push(tailEnemy.join(''));
						tailHtml.push('<div class="tailBtn buttonSmall">Tail</div>');
						tailHtml.push('<div><div class="clearer"></div>');
					} else if (turnData.planes[i].tailing != '0') {
						tailHtml.push('<div class="tailEnemyGroup"><div>Enemy Tail</div>');
						for (j = 0, jlen = this.plane.length; j < jlen; j++) {
							if (this.plane[j].id == turnData.planes[i].tailing) {
								tailHtml.push('<div class="tailBlock tailEnemy rounded3" rel="'+this.plane[j].id+'">');
								tailHtml.push('<div class="bmPlaneMini" style="background-image:url(media/images/planes/'+this.plane[j].bgimg+');" ');
								tailHtml.push('title="'+this.plane[j].user+' '+this.plane[j].name+'"></div>');
								if(turnData.planes[i].tailDirection !== undefined && turnData.planes[i].tailDirection !== ''){
									tailHtml.push('<div class="tailDirection">' + turnData.planes[i].tailDirection.toUpperCase() + '</div>'); 
								} else {
									tailHtml.push('<div class="tailDirection"></div>'); 
								}
								break;
							}
						}
						tailHtml.push('</div><div class="clearer"></div>');
					}

					$('div.rolloverTailing', $planeRollover).html(tailHtml.join('')).show();
					
					if (tailEnemyCount == 1) { // Only one enemy to tail so select it
						$('#rollover_'+planeIndex+' div.tailEnemy:eq(0)').addClass('selected');
						$('#rollover_'+planeIndex+' input.tailSelect').val( $('#rollover_'+planeIndex+' div.tailEnemy:eq(0)').attr('rel') );
					}
					
					$('div.tailEnemy', $planeRollover).bind('click.tail', {bm:this, index:planeIndex}, function(e){
						$('#rollover_'+e.data.index+' div.tailEnemy').removeClass('selected');
						$(this).addClass('selected');
						$('#rollover_'+e.data.index+' input.tailSelect').val( $(this).attr('rel') );							
					});
					$('div.tailBtn', $planeRollover).bind('click.tail', {bm:this, index:planeIndex}, function(e){
						e.data.bm.setTail(e.data.index, $('#rollover_'+e.data.index+' input.tailSelect').val());							
					});
				} else {
					$('div.rolloverTailing', $planeRollover).html('').hide();
				}
			} else {
				$('div.rolloverTailing', $planeRollover).html('').hide();
			}
		} 
	};
	
	this.updateRolloverTailResult = function () {
		//alert('updateRolloverTailResult');
		var i, ilen, j, jlen, planeIndex, $planeRollover, planeData,
			turnData = this.turn[this.turnShow];
			
		for (i=0, ilen=turnData.planes.length; i < ilen; i++) {
			planeIndex = turnData.planes[i].index;
			$planeRollover = $('#rollover_'+planeIndex);
			planeData = turnData.planes[i];
			
			if ($.inArray(planeIndex, this.userPlane) == -1 || planeData.planeStatus != 'active' || planeData.canTail === undefined || planeData.canTail.length === 0) {
				$('div.rolloverTailing', $planeRollover).html('').hide();
			} else {
				for (j = 0, jlen = planeData.canTail.length; j < jlen; j++) {
					if (planeData.canTail[j][2] == this.userFaction && turnData.planes[planeData.canTail[j][1]].ownTailDirection !== undefined) {
						$('div.tailBlock[rel="'+this.plane[planeData.canTail[j][1]].id+'"] div.tailDirection', $planeRollover).text(turnData.planes[planeData.canTail[j][1]].ownTailDirection.toUpperCase());
					} else if(planeData.turnStatus == 'waiting' && planeData.tailing != '0'){
						if (planeData.tailing === planeData.canTail[j][0]) {
							$('div.tailEnemyGroup', $planeRollover).html('<div>Enemy Tail</div><div class="tailBlock tailEnemy rounded3" rel="'+planeData.tailing+'">' +
								'<div class="bmPlaneMini" style="background-image:url(media/images/planes/'+this.plane[planeData.canTail[j][1]].bgimg+');" ' +
								'title="'+this.plane[planeData.canTail[j][1]].user+' '+this.plane[planeData.canTail[j][1]].name+'"></div>' +
								'<div class="tailDirection">' +
								((planeData.tailDirection !== undefined && planeData.tailDirection !== '')?planeData.tailDirection.toUpperCase():'') +
								'</div></div><div class="clearer"></div>');
						}
					}
				}
			}
		}
	};
	
	this.updateRolloverManeuver = function () {
		var i, ilen, planeIndex, $planeRollover,
			turnData = this.turn[this.turnShow],
			lockCount = 0;
		for (i=0, ilen=turnData.planes.length; i < ilen; i++) {
			planeIndex = turnData.planes[i].index;
			$planeRollover = $('#rollover_'+planeIndex);
			$('#mnvr_'+planeIndex+'_'+turnData.planes[i].lastMnvrID)
				.clone(false)
				.appendTo($('span.lastManeuver', $planeRollover).html(''))
				.removeClass('hover')
				.removeClass('selected');
			$('#mnvr_'+planeIndex+'_'+turnData.planes[i].maneuverID)
				.clone(false)
				.appendTo($('span.thisManeuver', $planeRollover).html(''))
				.removeClass('hover')
				.removeClass('selected');
			/*	
			if (this.turnShow == this.turnLast && this.gameStatus != 'over') {
				$('div.rolloverManeuvers', $planeRollover).show();
			} else {
				$('div.rolloverManeuvers', $planeRollover).hide();
			}
			*/
			
			if ($.inArray(planeIndex, this.userPlane) != -1) { 
				if (turnData.planes[i].planeStatus != 'active') {
					$('#planeManeuvers_'+planeIndex+' table.maneuverTable').removeClass('active');
					$('div.controls', $planeRollover).hide();
				} else if (turnData.planes[i].turnStatus == 'waiting' && turnData.planes[i].maneuverID == '0') {
					$('div.message', $planeRollover).html('Select a maneuver for your plane to do this turn');
					
					if (turnData.planes[i].mandtl !== undefined && turnData.planes[i].mandtl !== '') {
						$('div.message', $planeRollover).append($('<div class="small secret">'+turnData.planes[i].mandtl+'</div>'));
					}
					
					$('#planeManeuvers_'+planeIndex+' table.maneuverTable').addClass('active');
					$('div.controls', $planeRollover).hide();
				} else if (turnData.planes[i].turnStatus == 'waiting') {
					$('div.message', $planeRollover).html('');
					$('#planeManeuvers_'+planeIndex+' table.maneuverTable').addClass('active');
					$('div.controls', $planeRollover).show();
				} else {
					$('div.message', $planeRollover).text("Your plane's turn is locked in");	
					$('#planeManeuvers_'+planeIndex+' table.maneuverTable').removeClass('active');
					$('div.controls', $planeRollover).hide();
					lockCount++;
				}
				if (lockCount >= this.userPlane.length) { this.pollStart(); }
			} else { // Plane is not this user's
				$('div.controls', $planeRollover).hide();
			}
		}
	};
	
	/*=================================================================
	Turn summary
	=================================================================*/
	this.buildSummary = function () {

		this.summaryModule
			.html(	'<div id="summaryTitle" class="smTitle roundtop6">' +
					'<span class="title"></span></div>' +
					'<div id="summaryContainer" class="smContainer">' +
					'<div id="turnMessages" class="smContainerScroll"></div>' +
					'</div></div>');
			
		$('div.smTitle', this.summaryModule).bind('click', {bm:this}, function(e){ e.data.bm.focusSidePanel('summary', false); });
	};
	
	this.displaySummary = function (turn) {
		if (turn === undefined) { turn = this.turnShow; }
		var i, ilen;
		var turnMessageData = this.turn[turn].msg;
		var turnPlaneData = this.turn[turn].planes;
		var htmlArray = [];
		var plane1, location1, facing1;
		var plane2, location2, facing2;
		var relFacing1, relFacing2;
		$('span.title', this.summaryModule).html('Summary <span class="turnNumber">for turn '+(turn+1)+'</span>');
		// Add turn messages to summary container
		for (i = 0, ilen = turnMessageData.length; i < ilen; i++) {
			htmlArray.push('<div class="message" id="msg_'+turnMessageData[i].id+'">');
			switch(turnMessageData[i].m.t) {
				case "pmsg":
					plane1 = this.getPlaneByID(turnMessageData[i].m.p);
					htmlArray.push('<div class="meta plane">');
					htmlArray.push('<div class="miniplane" style="background-image:url(media/images/planes/'+plane1.bgimg+');"></div></div>');
					htmlArray.push('<div class="text planetext"><p>'+plane1.user+' ('+plane1.name+')</p>'+turnMessageData[i].t+'</div>');
					break;
				case "gmsg":
					htmlArray.push('<div class="text gametext">'+turnMessageData[i].t+'</div>');
					break;
				case "patt":
				case "poatt":
					var plane1 = this.getPlaneByID(turnMessageData[i].m.pa);
					var location1 = turnPlaneData[plane1.index].lafend.split(',');
					var facing1 = location1.pop();
					var plane2 = this.getPlaneByID(turnMessageData[i].m.pt);
					var location2 = turnPlaneData[plane2.index].lafend.split(',');
					var facing2 = location2.pop();
					if (turnMessageData[i].m.t == 'patt') {
						relFacing1 = 0;
						relFacing2 = $.hex.facingSimplify( facing2 - facing1 );
					} else {
						relFacing1 = $.hex.facingSimplify( facing1 - Math.round( $.hex.direction(location1, location2) ) );
						relFacing2 = $.hex.facingSimplify( facing2 - facing1 + relFacing1 );
					}
					htmlArray.push('<div class="meta attack">');
					htmlArray.push('<div class="bmPlane" style="background-image:url(media/images/planes/'+plane1.bgimg+');background-position:-'+(this.hex.width*relFacing1)+'px 0;"></div>');
					htmlArray.push('<div class="bmPlane" style="background-image:url(media/images/planes/'+plane2.bgimg+');right:0;background-position:-'+(this.hex.width*relFacing2)+'px 0;"></div>');
					htmlArray.push('<div class="chit chitCV">'+turnMessageData[i].m.cv+'</div>');
					htmlArray.push('<div class="chit chitDie">'+turnMessageData[i].m.die+'</div>');
					htmlArray.push('<div class="chit chitBlue">'+turnMessageData[i].m.bc+'</div>');
					htmlArray.push('<div class="chit chitRed">'+turnMessageData[i].m.rc+'</div>');
					htmlArray.push('</div>');
					htmlArray.push('<div class="text attacktext">'+turnMessageData[i].t+'</div>');
					break;
			}
			htmlArray.push('<div class="clearer"></div></div>');
		}
		// Show summary container
		$('#turnMessages', this.summaryModule).html(htmlArray.join(''));
		$('div.text p:nth-child(1)', this.summaryModule).addClass('title');

	}
	
	/*=================================================================
		Chat Module
	=================================================================*/
	this.buildChat = function () {

		this.chatModule
			.html(	'<div class="smTitle roundtop6">Chat</div>' +
					'<div id="chatContainer" class="smContainer">' +
					'<div id="chatMessages" class="smContainerScroll">' +
					'</div></div></div>');
			
		this.chatObject = new ChatClass ('#chatMessages', {
			game: 'bm',
			gameID: this.gameID,
			gameTurn: this.turnLast+1,
			enabled: this.userInGame,
			height: 200,
			poll: this.userInGame,
			jsonRoot: '../'
		});
			
		$('div.smTitle', this.chatModule).bind('click', {bm:this}, function(e){ e.data.bm.focusSidePanel('chat', false); });
	}
	
	
	/*=================================================================
	USER ACTIONS
	Actions triggered by user input.
	=================================================================*/
	this.setTail = function (planeIndex, tailID) {
		if (tailID === undefined || tailID === '' || tailID === 0) { return; }
		$('#rollover_'+planeIndex+' div.rolloverTailing div.tailBtn').unbind('click.tail');
		$('#rollover_'+planeIndex+' div.rolloverTailing div.tailEnemyGroup').html('');
		$.ajax( {
			url:this.urls.bluemax,
			data:'action=settail&p='+this.plane[planeIndex].id+'&t='+tailID,
			type:'POST',
			dataType:'json',
			callingObject: this,
			planeIndex: planeIndex,
			success:function(data, textStatus) {
				if(data.result == 'success') {
					this.callingObject.turn[this.callingObject.turnLast].planes[this.planeIndex].tailing = data.tailing;
					this.callingObject.pollForTail();
					//this.callingObject.updateRollovers();
					this.callingObject.updateRolloverTailResult();
				}
			},
			error:function (XMLHttpRequest, textStatus, errorThrown) {
				if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
			}
		} );
	};
	
	this.maneuverSelect = function(planeIndex, manID, move, rotate, fuel) {
		rotate = parseInt(rotate, 10);
		//this.turn[this.turnLast].planes[planeIndex].maneuverID = maneuverID;
		this.plane[planeIndex].location = $.hex.sum(this.plane[planeIndex].ghostLocation, $.hex.rotate(move, this.plane[planeIndex].ghostFacing));
		this.plane[planeIndex].facing = $.hex.facingSimplify(parseInt(this.plane[planeIndex].ghostFacing, 10) + rotate);
		this.turn[this.turnLast].planes[planeIndex].maneuverID = manID;
		this.turn[this.turnLast].planes[planeIndex].tfuel = fuel;
		this.updatePlanes();
		this.updateRollovers();
		$.ajax( {
			url:this.urls.bluemax,
			data:'action=setmaneuver&p='+this.plane[planeIndex].id+'&m='+manID,
			type:'POST',
			dataType:'json',
			success:function(data, textStatus) {
				//if($.trace) { $.trace(data); }
			},
			error:function (XMLHttpRequest, textStatus, errorThrown) {
				if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
			}
		} );
	};
	
	this.maneuverCommit = function(planeID, e) {
		var $button = $(e.target);
		if ($button.hasClass('disabled')) { return; }
		// Deactivate commit button
		$button.addClass('disabled').css('opacity',0.5);
		
		$.ajax( {
			url:this.urls.bluemax,
			data:'action=committurn&p='+planeID,
			type:'POST',
			dataType:'json',
			callingObject: this,
			planeID: planeID,
			button: $button,
			success:function(data, textStatus) {
				if (data.result == 'success') {
					this.callingObject.getNewTurn(data.currentTurn);
				}
				this.button.removeClass('disabled').css('opacity',1);
			},
			error:function (XMLHttpRequest, textStatus, errorThrown) {
				if($.trace) { $.trace('maneuverCommit error'); }
				if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
			}
		} );
	};
	/*=================================================================
	POLLER
	Polls server for updates.
	=================================================================*/
	this.pollForUpdate = function(){
		clearInterval(this.pollerIID);
		if(this.canPoll) {
			this.pollManager('add');
			$.ajax( {
				url:this.urls.bluemax,
				data:'action=getlastturn',
				type:'POST',
				dataType:'json',
				callingObject: this,
				success:function(data, textStatus) {
					this.callingObject.pollManager('end');
					if (data.turn != this.callingObject.turnLast) {
						this.callingObject.pollEnd();
						this.callingObject.getNewTurn(data.turn);
					} else if (data.status != this.callingObject.turn[data.turn].status) {
						this.callingObject.pollEnd();
						this.callingObject.getTurn(data.turn, true);
					}
					this.callingObject.updateLocks(data.planes);
				},
				error:function (XMLHttpRequest, textStatus, errorThrown) {
					if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
					this.callingObject.pollManager('end');
				}
			} );
			var bm = this;
			this.pollerIID = setInterval(function(){bm.pollForUpdate();}, 5000);
		}
	};
	
	this.pollStart = function() { this.pollForUpdate(); };
	
	this.pollEnd = function() { clearInterval(this.pollerIID); };
	
	this.pollForTail = function(){
		clearInterval(this.tailPollerIID);
		if(this.canPoll) {
			this.pollManager('add');
			$.ajax( {
				url:this.urls.bluemax,
				data:'action=gettails',
				type:'POST',
				dataType:'json',
				callingObject: this,
				success:function(data, textStatus) {
					this.callingObject.processPollForTail(data);
					this.callingObject.pollManager('end');
				},
				error:function (XMLHttpRequest, textStatus, errorThrown) {
					if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
					this.callingObject.pollManager('end');
				}
			} );
			var bm = this;
			this.tailPollerIID = setInterval(function(){bm.pollForTail();}, 5000);
		}
	};
	
	this.processPollForTail = function(data) {
		var i, ilen, 
			updateRollovers = false,
			recievedCount = 0;
		//if (data.tailed.length === 0) { this.endPollForTail(); }
		for (i = 0, ilen = data.tailed.length; i < ilen; i++) {
			if(data.tailed[i][2] != '') {
				this.turn[this.turnLast].planes[data.tailed[i][0]].tailDirection = data.tailed[i][2];
				updateRollovers = true;
				recievedCount++;
			}
		}
		for (i = 0, ilen = data.friends.length; i < ilen; i++) {
			if(data.friends[i][2] !== '') {
				this.turn[this.turnLast].planes[data.friends[i][0]].ownTailDirection = data.friends[i][2];
				updateRollovers = true;
				recievedCount++;
			}
		}
		if (updateRollovers) { 
			//this.updateRollovers(); 
			this.updateRolloverTailResult(); 
		}
		//if (data.tailed.length == recievedCount) { this.endPollForTail(); }
	};
	
	this.endPollForTail = function() { clearInterval(this.tailPollerIID); };
	
	this.pollManager = function (act) {
		switch (act) {
			case 'add':
				this.activePolls++;
				break;
			case 'end':
				this.activePolls--;
				break;
		}
		if (this.activePolls < 1) {
			this.activePolls = 0;
			$('#pollIndicator').attr('src', '../media/images/icon/blank.gif');
		} else {
			$('#pollIndicator').attr('src', '../media/images/icon/loading.gif');
		}
	};
	
	/*=================================================================
		Helpers
	=================================================================*/	
	this.getPlaneByID = function(id) {
		var i, ilen;
		for (i = 0, ilen = this.plane.length; i < ilen; i++) {
			if (this.plane[i].id == id) {
				return this.plane[i];
				break;
			}
		}
		return false;
	};
	
	this.getRankName = function(rank, faction) {
		var ranks;
		if (faction.toLowerCase() == 'german') {
			ranks = ['Oberleutnant','Hauptmann','Major','Oberstleutnant','Oberst','Generalmajor','Generalleutnant'];
		} else {
			ranks = ['Lieutenant','Captain','Major','Lieutenant-Colonel','Colonel','Brigadier-General','Major-General'];
		}
		if (rank > 6) { rank = 6; }
		return ranks[rank];
	};
	
	/*=================================================================
	Check for inactive users
	=================================================================*/
	this.inactiveUserID = [];
	this.inactiveUserName = [];
	
	this.assessInactiveUsers = function() {
		if (this.pickupsTested) { return false; }
		this.pickupsTested = true;
		if (this.gameStatus == 'over') { return false; }
		var i, ilen;
		var isUserInGame = false;
		for (i=0, ilen=this.gameData.planes.length; i < ilen; i++) {
			if (isUserInGame || $.inArray(this.gameData.planes[i].userID, this.inactiveUserID) != -1) { continue; }
			if (this.userID == this.gameData.planes[i].userID) { isUserInGame = true; }
			if (this.plane[this.gameData.planes[i].index].status == 'active' && this.gameData.planes[i].ustatus == 'inactive') {
				this.inactiveUserID.push(this.gameData.planes[i].userID);
				this.inactiveUserName.push(this.gameData.planes[i].user);
			}
		}
		if (this.inactiveUserID.length) { this.buildPickupInterface(); }
	};
	
	this.buildPickupInterface = function() {
		var i, ilen, j, jlen, htmlArray = [];
		
		if (this.campaignID !== undefined && this.campaignID !== 0) {
			this.pickuptab = $('<a href="'+this.urls.campaignDetail+this.campaignID+'" class="titlebutton rounded3">Pickups Available!</a>')
				.appendTo($('#bmControlRight'));
			return;
		} else {
			this.pickuptab = $('<div id="pickupTab" class="titlebutton rounded3">Pickups Available!</div>')
				.bind('click', function(e){ $('#pickup div.fnTitle').trigger('dblclick'); })
				.appendTo($('#bmControlRight'));
		}
		
		this.pickup = $('<div id="pickup" class="floatNotice rounded8"></div>')
			.appendTo(this.container)
			.html(	'<div id="pickupTitle" class="fnTitle roundtop6">'+
					'<div id="pickupMinimise" class="fnControl fnClose rounded4"></div>'+
					'<div id="pickupMinimise" class="fnControl fnMinimise rounded4"></div>'+
					'<span class="title">Pickups Available!</span></div>'+
					'<div id="pickupContainer" class="fnContainer roundbot6">'+
					'</div>')
			.css({'top':10, 'left':10})
			.draggable({ 
				handle: '#pickupTitle', 
				containment: '#pageWrapper',
				snap: true,
				snapMode: 'outer'
			})
			.disableSelection();
		
		$('div.fnTitle', this.pickup)
			.bind('dblclick', {pup:this.pickup}, function(e){ 
				e.data.pup.toggle();
				if (e.data.pup.is(':hidden')) {
					$('#pickupTab').removeClass('open');
				} else {
					var parent = e.data.pup.parent();
					e.data.pup.detach().appendTo(parent);
					$('#pickupTab').addClass('open');
				}
			});
		
		$('div.fnMinimise, div.fnClose', this.pickup)
			.hover( 
				function(){ $(this).addClass('hover'); }, 
				function(){ $(this).removeClass('hover'); }
			)
			.bind('click', {ro:this.pickup}, function(e){
				if ($(e.target).hasClass('fnMinimise')) {
					if ($('div.fnContainer', e.data.ro).is(':hidden')) {
						$(e.target).removeClass('max');
						$('#pickupTitle', e.data.ro).addClass('roundtop6').removeClass('rounded6');
					} else {
						$(e.target).addClass('max');
						$('#pickupTitle', e.data.ro).removeClass('roundtop6').addClass('rounded6');
					}
					$('div.fnContainer', e.data.ro).slideToggle();
				} else if ($(e.target).hasClass('fnClose')) {
					$('div.fnTitle', e.data.ro).trigger('dblclick');
				}
			});
		
		htmlArray.push('<p>These players are inactive and their planes are available for pickup.</p>');
		htmlArray.push('<table cellspacing="0" class="detailTable"><colgroup><col width="130"><col></colgroup>');
		for (i=0, ilen=this.inactiveUserID.length; i < ilen; i++) {
			htmlArray.push('<tr><td align="center" style="font-size:12px;">'+this.inactiveUserName[i]+'<br><br>');
			htmlArray.push('<div class="small">Click to take over<br>this player'+"'"+'s planes</div>');
			htmlArray.push('<div class="buttonMiddle" uid="'+this.inactiveUserID[i]+'">Pickup<div>');
			htmlArray.push('</td><td>');
			for (j=0, jlen=this.plane.length; j < jlen; j++) {
				if (this.plane[j].user == this.inactiveUserName[i] && this.plane[j].status == 'active') {
				htmlArray.push('<div class="bmPlane" style="position: relative; float: left; background-image: url(media/images/planes/'+this.plane[j].bgimg+');"></div>');
				}
			}
			htmlArray.push('</td></tr>');
		}
		htmlArray.push('</table>');
		$('div.fnContainer', this.pickup).html(htmlArray.join(''));
		$('div.buttonMiddle', this.pickup).bind('click', {bm:this}, function(e){
			e.data.bm.requestPickup($(e.target).attr('uid'));
		}); 
	};
	
	this.requestPickup = function(pickupUserID) {
		$('#pickupContainer', this.pickup).html('<div class="ajaxloader" style="float:left;"></div> &nbsp;Processing your request...');
		$.ajax( {
			url:this.urls.bluemax,
			data:'action=pickup&u='+pickupUserID,
			type:'POST',
			dataType:'json',
			callingObject: this,
			success:function(data, textStatus) {
				if (data.result == 'success') {
					$('#pickupContainer', this.callingObject.pickup)
						.html(	'<p>You have picked up the plane(s) of '+data.user+'.</p>'+
								'<p><a href="javascript:void(0);" onclick="window.location=window.location.href;">Reload the game</a>'+
								' to start playing.</p>')
						.css('text-align','center');
				} else {
					$('#pickupContainer', this.callingObject.pickup)
						.html(	'<p>Sorry. There was a problem processing your request.</p>'+
								'<p>'+data.reason+'</p>'+
								'<p><a href="javascript:void(0);" onclick="window.location=window.location.href;">Reload the game</a>'+
								' and try again.</p>')
						.css('text-align','center');
				}
			},
			error:function (XMLHttpRequest, textStatus, errorThrown) {
			if($.trace) { $.trace("Error:\n" + XMLHttpRequest + "\n" + textStatus + "\n" + errorThrown); }
			}
		} );
	};
	
	/*=================================================================
	Call Initalize function
	=================================================================*/
	this.init();

}
