/****************************************************************************
Chess PGN Reader Program by Ray Wilson
Copyright 2002
Modifications: Tobias Feigel 2005 for kosteniuk.com
Modifications: Antony (PT Demos) 2006 for Indochess.com
*****************************************************************************/
function ResetOption(obj)
{
for (i=15; i<22; i++)
{ if (obj != window.document.forms[1].elements[i]) window.document.forms[1].elements[i].selectedIndex=0; }
}

var globalStopFlagSet = false;
var globalsFiles = "abcdefgh";
var globalsRanks = "12345678";

var browserName = navigator.userAgent.toLowerCase();
var browserVer = parseInt(navigator.appVersion); 

//---------------------------------------------------
// Yes I know that FireFox is not ie4 but it has 
// similar capabilities for this program.
//---------------------------------------------------
var nsp = false;
var ie4 = (browserName.indexOf("msie") > -1 && browserVer>=4) || (browserName.indexOf("firefox") > -1);
if(!ie4) 
{	
	nsp = ( browserName.indexOf("netscape") > -1  );
}


var globalDarkColor     = "#A2BDD9";
var globalLightColor    = "#E7F1FA";
var globalFRColor       = "#0082C3";
var globalCapColor      = "#779977";
var globalBGColor       = "#aabbcc";
var globalHLLightColor  = "#ffff99";
var globalHLDarkColor   = "#99CCFF";
var globalImageSet      = 1;	
var globalGameRunning   = false;
var globaliMoveIndex    = 0;    // Keeps track of current move index.
var globaliMoveNumber   = 0;    // Keeps track of current displayable move number.
var globalNextMoveTimer = null; // Timer used for auto moving
var globalMoveTime      = 3000; // MS of time between moves.
var globalPGN_MaxMvNum  = 0;    // Maximum move number located in the Chess PGN Data
var globalOrgPos        = null; // Origin of each move
var globalDstPos        = null; // Destination of each move.
var globalGameSimulate  = false;

var bglobalBoardOrientation  = true; // true = white on bottom, false = black on bottom.

// Game state arrays
var globalPGN_MoveArray;
var globalChessBoardArray;
var globalCaptureAreaArray;
var globalStr = "";
var globalNum = 0;

//---------------------------------------------------------------
//
// LOAD ALL IMAGES
//
//---------------------------------------------------------------
var FileImages = new Array();
for(var i=0; i<8; ++i)
{
	FileImages[i] = new Image(13,22);
	FileImages[i].src = globalsFiles.charAt(i) + ".gif";
}

var RankImages = new Array();
for(var i=0; i<8; ++i)
{
	RankImages[i] = new Image(13,22);
	RankImages[i].src = "r" + globalsRanks.charAt(i) + ".gif";
}
var PieceImages = new Array();
PieceImages[1] = new Array();
PieceImages[1]["white-p"] = new Image(21,21);
PieceImages[1]["white-R"] = new Image(21,21);
PieceImages[1]["white-N"] = new Image(21,21);
PieceImages[1]["white-B"] = new Image(21,21);
PieceImages[1]["white-Q"] = new Image(21,21);
PieceImages[1]["white-K"] = new Image(21,21);
PieceImages[1]["black-p"] = new Image(21,21);
PieceImages[1]["black-R"] = new Image(21,21);
PieceImages[1]["black-N"] = new Image(21,21);
PieceImages[1]["black-B"] = new Image(21,21);
PieceImages[1]["black-Q"] = new Image(21,21);
PieceImages[1]["black-K"] = new Image(21,21);
PieceImages[1]["white-p"].src = "imagepgn/whitepawn_set2.gif";
PieceImages[1]["white-R"].src = "imagepgn/whiterook_set2.gif";
PieceImages[1]["white-N"].src = "imagepgn/whiteknight_set2.gif";
PieceImages[1]["white-B"].src = "imagepgn/whitebishop_set2.gif";
PieceImages[1]["white-Q"].src = "imagepgn/whitequeen_set2.gif";
PieceImages[1]["white-K"].src = "imagepgn/whiteking_set2.gif";
PieceImages[1]["black-p"].src = "imagepgn/blackpawn_set2.gif";
PieceImages[1]["black-R"].src = "imagepgn/blackrook_set2.gif";
PieceImages[1]["black-N"].src = "imagepgn/blackknight_set2.gif";
PieceImages[1]["black-B"].src = "imagepgn/blackbishop_set2.gif";
PieceImages[1]["black-Q"].src = "imagepgn/blackqueen_set2.gif";
PieceImages[1]["black-K"].src = "imagepgn/blackking_set2.gif";
	
var EMPTY43x43 = new Image(21,21);
EMPTY43x43.src = "imagepgn/empty.gif";

var ACTVGEARS = new Image(58,40);
ACTVGEARS.src = "runninggears.gif";
var GRAYGEARS = new Image(58,40);
GRAYGEARS.src = "graygears.gif";


function setImage(objChessPc)
{
	var sPcColor = (objChessPc.color) ? "white" : "black";
	objChessPc.imgsrc = PieceImages[globalImageSet][sPcColor + "-" + objChessPc.pcType].src;
}

//----------------------------------
// Move Animation Support
//----------------------------------
function getPosition_LEFT( objSelectControl ) 
{ 
	var iLeftPixel = 0; 
	while ( objSelectControl != null ) 
	{ 
		iLeftPixel += objSelectControl.offsetLeft; 
		objSelectControl = objSelectControl.offsetParent; 
	} 
	return iLeftPixel; 
} 

function getPosition_TOP( objSelectControl ) 
{	
	var iTopPixel = 0; 
	while ( objSelectControl != null ) 
	{ 
		iTopPixel += objSelectControl.offsetTop; 
		objSelectControl = objSelectControl.offsetParent; 
	} 
	return iTopPixel; 
}

var globalAnimationQuantum = 5;
var globalAnimationTimer;
var globalMoveCount;
var globalDY;
var globalDX;
var globalCurrentX;
var globalCurrentY;
var globalMovingDIV;
var globalTotalMoves = 10.0;
var globalBPaint;
var globalHoldImageSrc;
var globalMoveText;
var globalImageMoveToX;
var globalImageMoveToY;
var globalCPieceMoving;
var globalKnightMovePhase;
var globalFirstKnightDirection;
var globalMoveInProgressBool = false;

function AnimateMove()
{
	if(globalMoveCount < globalTotalMoves)
	{
		if(globalCPieceMoving.pcType == 'N')
		{
			++globalMoveCount;
			
			if(globalKnightMovePhase == 1)
			{
				if(globalFirstKnightDirection)
					globalMovingDIV.style.left = (globalMoveCount * globalDX) + globalCurrentX;
				else
					globalMovingDIV.style.top  = (globalMoveCount * globalDY) + globalCurrentY;
					
				if(globalMoveCount >= globalTotalMoves)
				{
					globalKnightMovePhase = 2;
					globalMoveCount = 0;
				}
			}
			else
			{
				if(globalFirstKnightDirection)
					globalMovingDIV.style.top  = (globalMoveCount * globalDY) + globalCurrentY;
				else
					globalMovingDIV.style.left = (globalMoveCount * globalDX) + globalCurrentX;
			}
		}
		else
		{
			++globalMoveCount;
			globalMovingDIV.style.left = (globalMoveCount * globalDX) + globalCurrentX;
			globalMovingDIV.style.top  = (globalMoveCount * globalDY) + globalCurrentY;
		}
		
		globalAnimationTimer = window.setTimeout("AnimateMove()",globalAnimationQuantum);
	}
	else
	{
		window.clearTimeout(globalAnimationTimer);
		globalAnimationTimer = null;
		globalMovingDIV.style.left = -100;
		globalMovingDIV.style.top  = -100;
		globalOrgPos.ChessPc.imgsrc = globalHoldImageSrc;
		CompleteMovePiece(globalOrgPos, globalDstPos);
		
		if(globalCPieceMoving)
			if(globalCPieceMoving.pcType == 'p')
				testPawnPromotion(globalDstPos);
		
		PaintBoard();
		
		if(globalCastlingBool)
		{
			CompleteCastle();
		}
		else
		{
			globalMoveInProgressBool = false;
			if(globalStopFlagSet)
			{
				StopGame()
			}
		}
	}
}

function abs(n)
{
	return (n < 0) ? -n : n;
}

function AnimateMovePiece(orgPos, destPos)
{
	globalCPieceMoving = orgPos.ChessPc;
	var pcMovingType = globalCPieceMoving.pcType; 
	globalKnightMovePhase = 1;
	
	var orgPosN  = FileRankToNumber(orgPos.filerank);
	var dstPosN  = FileRankToNumber(destPos.filerank);
	var imgFrom  = document.all("IMG_" + orgPosN);
	var imgDest  = document.all("IMG_" + dstPosN);
		
	globalCurrentX     = getPosition_LEFT(imgFrom);
	globalCurrentY     = getPosition_TOP (imgFrom);
	globalImageMoveToX = getPosition_LEFT(imgDest)+2;
	globalImageMoveToY = getPosition_TOP (imgDest)+2;
	
	var absDX = abs(globalImageMoveToX - globalCurrentX);
	var absDY = abs(globalImageMoveToY - globalCurrentY);
	var maxDir = (absDX > absDY) ? absDX : absDY;
	globalTotalMoves = parseInt(maxDir/4);
	
	globalFirstKnightDirection = false;
	if(pcMovingType == 'N')
	{
		globalFirstKnightDirection = absDX > absDY;
	}
	
	globalMoveCount = 0;
	globalAnimationTimer = null;
	globalDX = (globalImageMoveToX - globalCurrentX)/globalTotalMoves;
	globalDY = (globalImageMoveToY - globalCurrentY)/globalTotalMoves;
	
	globalMovingDIV = document.all("IMGMOVER");
	globalMovingDIV.innerHTML = "<IMG WIDTH='21' HEIGHT='21' SRC='" + orgPos.ChessPc.imgsrc + "'>";
	globalHoldImageSrc = orgPos.ChessPc.imgsrc;
	orgPos.ChessPc.imgsrc = EMPTY43x43.src;
	globalMovingDIV.style.left = globalCurrentX;
	globalMovingDIV.style.top  = globalCurrentY;
	globalMoveInProgressBool = true;
	AnimateMove();
}


//--------------------------------------------
//
// Chessboard state storage
//
//--------------------------------------------
// BoardPosition object definition
// Notice that board positions have a
// reference to a ChessPiece. 
//--------------------------------------------
function BoardPosition(fr)
{
	this.filerank = fr;        // fr = file and rank a1 through h8
	this.file = fr.charAt(0);  // file designator a - h
	this.rank = fr.charAt(1);  // rank designator 1 - 8
	this.ChessPc = null;       // Reference to a ChessPiece
}

//--------------------------------------------
// BoardPosition state storage
//
// The BoardPosition's live throughout the game
// they are assigned pieces as the game progresses.
// A BoardPosition with a null ChessPc is an empty position.
//
// globalChessBoardArray is an associative array where each position's
// key is the file-rank designator for the position (e.g. a1, a2 ... h8).
//--------------------------------------------
function InitializeChessBoard()
{
	var f;
	var r;
	globalChessBoardArray = new Array();
	for(r=0; r<8; ++r)
	{
		for(f=0; f<8; ++f)
		{
			var posIndex = globalsFiles.charAt(f) + globalsRanks.charAt(r);
			globalChessBoardArray[posIndex] = new BoardPosition(posIndex);
		}
	}
//	if (document.getElementById("pbAnalysis")) document.getElementById("pbAnalysis").style.visibility = 'hidden';
}

//--------------------------------------------
// Return a board position by file/rank.
// returns: BoardPosition object
//--------------------------------------------
function getBoardPosition(fr)
{
	return globalChessBoardArray[fr];
}

//--------------------------------------------
//
// Chessmen state storage
//
//--------------------------------------------
//
// ChessPiece object definition
//--------------------------------------------
function ChessPiece(bc, pT, nm, ims, inx)
{
	this.color  = bc;    // true = white, false = black.
	this.pcType = pT;    // B,N,R,K,Q,p
	this.name   = nm;    // Bishop, Knight, Rook, Queen, King, Rook
	this.imgsrc = ims;   // name of image.
	this.moves  = 0;     // number of times moved during game.
	this.index  = inx;   // Index of piece 0 thru 15
	this.moveno = 0;     // Game Move Index at the time this piece was moved.
	                     // used to determine en-passant capture.
}

//--------------------------------------------
// Set the image for any piece on the board
// or in the capture area.
//--------------------------------------------
function SetChessPieceImages() // n = set number.
{
	var posChessPc;
	var capChessPc;
	var f;
	var r;
	var m;
	
	for(r=0; r<8; ++r)
	{
		for(f=0; f<8; ++f)
		{
			posChessPc = getBoardPosition("" + globalsFiles.charAt(f) + globalsRanks.charAt(r)).ChessPc; 
			if(posChessPc != null)
			{
				setImage(posChessPc);
			}
		}
	}
	
	for(m=0; m<16; ++m)
	{
		capChessPc = globalCaptureAreaArray[0][m].ChessPc;
		if(capChessPc != null)
		{
			setImage(capChessPc);
		}
		
		capChessPc = globalCaptureAreaArray[1][m].ChessPc;
		if(capChessPc != null)
		{
			setImage(capChessPc);
		}
	}
}

//--------------------------------------------
// Place ChessPiece objects into their initial
// positions on the Chess Board.
//--------------------------------------------
function BoardToStartState()
{
	var cbp;
	var fr;
	var m;
	var i;
	
	//----------------------------------
	// Set initial board positions	
	//----------------------------------
	
	// BoardPosition.ChessPc = null for Ranks 3 through 6.
	for(m=3; m<7; ++m)
	{
		for(i=0; i<8; ++i)
		{
			getBoardPosition("" + globalsFiles.charAt(i) + m).ChessPc = null;
		}
	}
	
	// BoardPosition.ChessPc = initial piece for Ranks 7 and 8.
	for(i=0; i<8; ++i)
	{
		getBoardPosition("" + globalsFiles.charAt(i) + 7).ChessPc = new ChessPiece(false, "p", "Pawn", "", i); 
	}
	getBoardPosition("a8").ChessPc = new ChessPiece(false, "R", "Rook",   "", 8);
	getBoardPosition("b8").ChessPc = new ChessPiece(false, "N", "Knight", "", 9);
	getBoardPosition("c8").ChessPc = new ChessPiece(false, "B", "Bishop", "", 10);
	getBoardPosition("d8").ChessPc = new ChessPiece(false, "Q", "Queen",  "", 11);
	getBoardPosition("e8").ChessPc = new ChessPiece(false, "K", "King",   "", 12);
	getBoardPosition("f8").ChessPc = new ChessPiece(false, "B", "Bishop", "", 13);
	getBoardPosition("g8").ChessPc = new ChessPiece(false, "N", "Knight", "", 14);
	getBoardPosition("h8").ChessPc = new ChessPiece(false, "R", "Rook",   "", 15);
	
	// BoardPosition.ChessPc = initial piece for Ranks 1 and 2.
	for(i=0; i<8; ++i)
	{
		getBoardPosition("" + globalsFiles.charAt(i) + 2).ChessPc = new ChessPiece(true,  "p", "Pawn", "", i);
	}
	getBoardPosition("a1").ChessPc = new ChessPiece(true, "R", "Rook",   "", 8);
	getBoardPosition("b1").ChessPc = new ChessPiece(true, "N", "Knight", "", 9);
	getBoardPosition("c1").ChessPc = new ChessPiece(true, "B", "Bishop", "", 10);
	getBoardPosition("d1").ChessPc = new ChessPiece(true, "Q", "Queen",  "", 11);
	getBoardPosition("e1").ChessPc = new ChessPiece(true, "K", "King",   "", 12);
	getBoardPosition("f1").ChessPc = new ChessPiece(true, "B", "Bishop", "", 13);
	getBoardPosition("g1").ChessPc = new ChessPiece(true, "N", "Knight", "", 14);
	getBoardPosition("h1").ChessPc = new ChessPiece(true, "R", "Rook",   "", 15);
	
	SetChessPieceImages();
}


//--------------------------------------------
// CapturePosition object definition
// 
// CapturePosition's live throughout the game
// and hold a ChessPc reference whenever the 
// piece of color and with the same index is
// captured.
//--------------------------------------------
function CapturePosition(inx)
{
	this.index = inx;     // index of position.
	this.ChessPc = null;  // Reference to a ChessPiece
}

function InitializeCaptureArea()
{
	var m;
	globalCaptureAreaArray = new Array();
	globalCaptureAreaArray[0] = new Array(); // For black pieces
	globalCaptureAreaArray[1] = new Array(); // For white pieces
	for(m=0; m<16; ++m)
	{
		globalCaptureAreaArray[0][m] = new CapturePosition(m);
		globalCaptureAreaArray[1][m] = new CapturePosition(m);
	}
}

function CaptureAreaToStartState()
{
	for(m=0; m<16; ++m)
	{
		globalCaptureAreaArray[0][m].ChessPc = null;
		globalCaptureAreaArray[1][m].ChessPc = null;
	}		
}

//--------------------------------------------
// Place objChessPc into its respective 
// position within the capture area.
//--------------------------------------------
function PieceToCaptureArea(objChessPc)
{
	if(objChessPc)
	{
		globalCaptureAreaArray[(objChessPc.color)?1:0][objChessPc.index].ChessPc = objChessPc;
	}
}


//-----------------------------------------------------
// Draw the board with generic positional
// blank images. The PaintBoard function will place 
// the Chess images where they belong.
//
// From left to right and top to bottom
// IMG_0  ... IMG_7
// IMG_8  ... IMG_15
// ...
// IMG_56 ... IMG_63
//-----------------------------------------------------
// IMG_R1 to IMG_R8 for ranks
// IMG_F1 to IMG_F8 for files
//-----------------------------------------------------
function DrawChessBoardCells()
{
	var bgcolor;
	var bColor;
	var imgName;
	var posFileRank;
	var ImgSrc;
	var i;
	var k;
	bColor = false;
	for(i=0; i<64; ++i)
	{
		bColor = !bColor;
		bgcolor = bColor ? globalLightColor : globalDarkColor;
				
		document.write("<TD ID='TD_" + i + "' BGCOLOR='" + bgcolor + "'>");
		imgName = "IMG_" + i;
		document.write("<IMG HEIGHT='21' WIDTH='21' BORDER='0' ID=" + imgName + " NAME=" + imgName + " SRC='" + EMPTY43x43.src + "'>");
		document.write("</TD>");
	
		if((i+1) % 8 == 0)
		{
			document.write("</TR>");
		}
		
		bColor = ((i+1) % 8 == 0) ? !bColor : bColor;
	}
	
	
}

function DrawCapturedCells(posTB)
{
	for(var i=0; i<16; ++i)
	{
		imgName = "CAP_" + posTB + i;
		if(i % 4 == 0)
		{
			document.write("<TR>");
		}

		document.write("<TD ID='TD_" + imgName + "' BGCOLOR='" + globalCapColor + "'>");
		document.write("<IMG HEIGHT='21' WIDTH='21' BORDER='0' ID=" + imgName + " NAME=" + imgName + " SRC='" + EMPTY43x43.src + "'>");
		document.write("</TD>");
	
		if((i+1) % 4 == 0)
		{
			document.write("</TR>");
		}
	}
}


//----------------------------------------------------------
// Paints ChessPiece associated with each board position
//----------------------------------------------------------
function PaintBoard()
{
	//---------------------------
	// Paint Chess Pieces
	//---------------------------
	var bpcp;
	for(var i=0; i<64; ++i)
	{
		bpcp = getBoardPosition(NumberToFileRank(i)).ChessPc;
		document.images["IMG_" + i].src = (bpcp == null) ? EMPTY43x43.src : bpcp.imgsrc;
	}
		
	HighlightMovePositions();
}

function NumberToFileRank(n)
{
	if(bglobalBoardOrientation)
	{
		var f = n % 8;
		var r = ((n/8) < 1) ? 7 : 7-(parseInt(n/8));
		return globalsFiles.charAt(f) + globalsRanks.charAt(r);
	}
	else
	{
		var f = 7 - (n % 8)
		var r = ((n/8) < 1) ? 0 : parseInt(n/8);
		return globalsFiles.charAt(f) + globalsRanks.charAt(r);
	}
}

function FileRankToNumber(fr)
{
	var f = globalsFiles.indexOf(fr.charAt(0));
	var r = globalsRanks.indexOf(fr.charAt(1));

	if(bglobalBoardOrientation)
	{
		return (7-r) * 8 + (f-0);
	}
	else
	{
		return (r * 8) + (7-f);
	}
}

function SwitchSides()
{
	bglobalBoardOrientation = !bglobalBoardOrientation;
	PaintBoard();
}

function HighlightMovePositions()
{
	if(!ie4)
		return;
		
	ResetBoardColors();

	var posn1;
	var posn2;
	if(globalOrgPos)
	{
		posn1 = FileRankToNumber(globalOrgPos.filerank);
		posn2 = FileRankToNumber(globalDstPos.filerank);
		
		document.all("TD_" + posn1).bgColor = (document.all("TD_" + posn1).bgColor == globalLightColor)
		                                    ? globalHLLightColor
		                                    : globalHLDarkColor;
		                                    
		document.all("TD_" + posn2).bgColor = (document.all("TD_" + posn2).bgColor == globalLightColor)
		                                    ? globalHLLightColor
		                                    : globalHLDarkColor;
	}
}

function ResetBoardColors()
{
	if(!ie4)
		return;

	var bColor = false;
	var bgcolor;
	for(var i=0; i<64; ++i)
	{
		bColor = !bColor;
		bgcolor = bColor ? globalLightColor : globalDarkColor;
		document.all("TD_" + i).bgColor = bgcolor;
		bColor = ((i+1) % 8 == 0) ? !bColor : bColor;
	}
}


//--------------------------------------------
//
// March 02, 2003
// Enhancement
// Change 0-0-0 (zero dash zero dash zero) to O-O-O (Oh dash Oh dash Oh's) which is what they are supposed to be.
// Change 0-0 (zero dash zero's) to O-O (Oh dash Oh's) which is what they are supposed to be.
//
//
// Chess PGN moves are stored in globalPGN_MoveArray.
//
// This...
//
// 1. e4 c5 2. Nf3 d6 3. Bb5+ Bd7 4. Bxd7+ Qxd7 5. O-O e5 6. c3 Nf6 7. Re1 Qe6 8.
// d4 cxd4 9. cxd4 Be7 10. Nc3 O-O 11. Bg5 Nbd7 12. Nd5 Bd8 13. dxe5 dxe5 14. Qb3
// Nc5 15. Nxf6+ Bxf6 16. Qxe6 Nxe6 17. Bxf6 gxf6 18. Rac1 Rac8 19. Red1 Nc5 20.
// Nd2 Nd3 21. Rxc8 Rxc8 22. Nb3 Nxb2 23. Rd7 Nc4 24. f3 Rb8 25. g4 a5 26. Kf2 Kg7
// 27. h4 Kf8 28. Kg3 b6 29. Rc7 Nd6 30. Rc6 Ke7 31. Rc7+ Kd8 32. Rc6 Kd7 33. Rc1
// a4 34. Nd2 Nb5 35. Rc4 Ra8 36. Rb4 Kc6 37. Rc4+ Kb7 38. Nf1 Nd4 39. Ne3 b5 40.
// Rb4 Rc8 41. Nf5 Ne2+ 42. Kf2 Rc2 43. Rxb5+ Ka6 44. Rb8 Rxa2 45. Rf8 Nf4+ 46.
// Kf1 a3 47. Ra8+ Kb6 48. Rb8+ Kc5 49. Rc8+ Kb4 50. Rb8+ Kc3 51. Rc8+ Kd2 52.
// Rd8+ Nd3 53. Ra8 Ra1+ 54. Kg2 a2 55. Ng3 Nb4 56. Nh5 Rc1 57. Nxf6 a1=Q 58. Rxa1
// Rxa1 59. Nxh7 Nd3 60. Ng5 Ne1+ 61. Kh2 f6 62. Nh7 Nxf3+ 63. Kg3 Ke3 64. Ng5
// fxg5 65. h5 Rg1+ 66. Kh3 Kf4 67. h6 Rg3# 0-1
//
// is converted to this...
//
// globalPGN_MoveArray[0]   = 1.e4
// globalPGN_MoveArray[1]   = c5
// globalPGN_MoveArray[2]   = 2.Nf3
// globalPGN_MoveArray[3]   = d6
// globalPGN_MoveArray[4]   = 3.Bb5+
// ...
// globalPGN_MoveArray[n-2] = 67.h6
// globalPGN_MoveArray[n-1] = Rg3#
// globalPGN_MoveArray[n]   = 0-1
//
// Note:
// White moves begin with 'movenumber.' and black moves do not.
// White moves are at indices where (index % 2 == 0)
// Black moves are at indices where (index % 2 != 0)
//--------------------------------------------
function parsePGNmovetext(movetext)
{
	movetext = movetext.replace(/0-0-0/g,"O-O-O");
	movetext = movetext.replace(/0-0/g,"O-O");
	
	if(movetext.length < 1)
		return;
	
	var sNormalized = "";
	var i = 0;
	
	// Replace all tabs, linefeeds, and returns with spaces.
	while(i < movetext.length)
	{
		sNormalized += ("\t\n\r".indexOf(movetext.charAt(i)) > -1) ? " " : movetext.charAt(i);
		++i;
	}
	
	// Normalize successive spaces to single space.
	movetext = sNormalized;
	sNormalized = "";
	i = 0;
	while(i < movetext.length)
	{
		if(movetext.charAt(i) == ' ')
		{
			sNormalized += ' ';
			while(movetext.charAt(i+1) == ' ')
			{
				++i;
			}
		}
		else
		{
			sNormalized += movetext.charAt(i);
		}
		++i;
	}
	
	// Remove spaces after periods, normalize successive periods to single periods.
	movetext = sNormalized;
	sNormalized = "";
	i = 0;
	while(i < movetext.length)
	{
		if(movetext.charAt(i) == '.')
		{
			sNormalized += '.';
			while(movetext.charAt(i+1) == ' ' || movetext.charAt(i+1) == '.')
			{
				++i;
			}
		}
		else
		{
			sNormalized += movetext.charAt(i);
		}
		++i;
	}
	
	
	// Remove spaces BEFORE periods.
	movetext = sNormalized;
	sNormalized = "";
	i = 0;
	while(i < movetext.length)
	{
		if(movetext.charAt(i) == '.')
		{
			if(sNormalized.length > 1)
			{
				// Clip trailing spaces from sNormalized.
				while(sNormalized.charAt(sNormalized.length-1) == ' ')
				{
					sNormalized = sNormalized.substring(0, sNormalized.length-1);
				}
			}
			// Now tack on the period.
			sNormalized += '.';
		}
		else
		{
			sNormalized += movetext.charAt(i);
		}
		++i;
	}

	// Store highest move number.
	globalPGN_MoveArray = sNormalized.split(' ');
	var parts;
	for(i = globalPGN_MoveArray.length-1; i > -1; --i)
	{
		if(globalPGN_MoveArray[i].indexOf('.') > -1)
		{
			parts = globalPGN_MoveArray[i].split('.');
			globalPGN_MaxMvNum = parts[0];
			break;
		}
	}
}

// Clean off non-move decoration (e.g. #,+,x etc)
function cleanMoveText(s)
{	
	var mvtxt = "";
	for(var i=0; i<s.length; ++i)
	{
		if("abcdefgh0123456789-OKQNBR".indexOf(s.charAt(i)) > -1)
			mvtxt += s.charAt(i);
	}
	return mvtxt;
}

// Return just the portion of a movetext token that 
// specifies the move e.g. 'b1', 'cb1', 'c2b1';
function getMoveTextOnly(s)
{
	var mvtxt = "";
	for(var i=0; i<s.length; ++i)
	{
		if("abcdefgh0123456789".indexOf(s.charAt(i)) > -1)
			mvtxt += s.charAt(i);
	}
	return mvtxt;
}

// Return Q,K,N,R,B, or p
function getMovePieceType(s)
{
	var sPieces = "QKNRB";
	var x = sPieces.indexOf(s.charAt(0));
	if(x > -1)
		return sPieces.charAt(x);
	else
		return 'p';
}

//---------------------------------------------
// Return an array of BoardPositions where all
// bColor pType pieces are located anywhere on 
// the board.
//---------------------------------------------
function findBoardPositionsWithPieces(pType, bColor)
{
	var matches = new Array();
	var BoardPos;
	var px = 0;
	
	// Sweep board looking for pieces of the specified 
	// type (B, N, R, K, Q, p) and color: true/false;
	for(m=1; m<9; ++m)
	{	
		for(i=0; i<8; ++i)
		{	
			BoardPos = getBoardPosition("" + globalsFiles.charAt(i) + m);
			if(BoardPos.ChessPc)
			{
				if(BoardPos.ChessPc.pcType == pType && BoardPos.ChessPc.color == bColor)
				{
					matches[px++] = BoardPos;
				}
			}
		}
	}
	return matches;
}

function isNumber(s)
{
	if(s == null)
		return false;
		
	if(s.length == 0)
		return false;
		
	for(var i=0; i<s.length; ++i)
	{
		if("0123456789".indexOf(s.charAt(i)) < 0)
		{
			return false;
		}
	}
	return true;
}

// Execute the chess moves up to the bTurnColor 
// move of the user entered number.
function playToMoveNumber(iMvInx)
{
	globalGameSimulate = true;

	if((iMvInx-0) < 0)
	{
		var mvInx = document.F1.skiptonumber.value;
		if(!isNumber(mvInx))
		{
			alert("You need to enter a move number to go to.");
			return;
		}
		if((mvInx-0) < 1 || (mvInx-0) > (globalPGN_MaxMvNum-0))
		{
			alert("Move number must be between 1 and last move of movetext.");
			return;
		}
	
		while(true)
		{
			if((mvInx-0) <= globaliMoveNumber)
				break;
	
			if(globaliMoveNumber < (mvInx-1))
				makeNextMove(false);
				
			if(globaliMoveNumber == (mvInx-1))
			{
				makeNextMove(false);
				makeNextMove(true);
				break;
			}
			
			if(globaliMoveNumber == (globalPGN_MaxMvNum-0))
				break;
		}
	}
	else
	{
		while(globaliMoveIndex < (iMvInx-1))
			makeNextMove(false);
			
		makeNextMove(true);
	}
	
	globalGameSimulate = false;
}

function makePrevMove()
{
	if(globalGameRunning)
		return;

	var holdMoveIndex = globaliMoveIndex-1;
	gamereset(holdMoveIndex < 1);
	if(holdMoveIndex > 0)
		playToMoveNumber(holdMoveIndex);
	thegoto=document.getElementById("go_to");
	thegoto.value=globaliMoveNumber;
	if (globaliMoveNumber != 0) {
//		document.getElementById("pbAnalysis").style.visibility = 'visible';
	} else {
//		document.getElementById("pbAnalysis").style.visibility = 'hidden';
	}
}

function RunGame()
{
	if (document.F1.moves.value == '') {
		return;
	} 
	setButtonDisabledState(true);
	globalGameRunning = true;
//	document.getElementById("pbAnalysis").style.visibility = 'hidden';
	gameStepper();
}

function gameStepper()
{
	makeNextMove(true);
	thegoto=document.getElementById("go_to");
	thegoto.value=globaliMoveNumber;
	globalNextMoveTimer = setTimeout("gameStepper();",globalMoveTime);
}


function SetMoveTime(msecs)
{
	globalMoveTime = msecs;
}


function setButtonDisabledState(b)
{
	if(ie4)
	{
		document.all("STOPGAME").disabled = !b;
		document.all("RUNGAME").disabled = b;
		document.all("PREVMOVE").disabled = b;
		document.all("NEXTMOVE").disabled = b;
		document.all("PLAYTOMOVENUMBER").disabled = b;
		document.all("RESETGAME").disabled = b;
		document.all("CLEARGAME").disabled = b;
		document.all("SWITCHSIDES").disabled = b;
	}
}


function StopGame()
{
	if (document.getElementById("go_to").value != '') {
//		document.getElementById("pbAnalysis").style.visibility = 'visible';
	}
	if(globalMoveInProgressBool)
	{
		globalStopFlagSet = true;
		return;
	}
	globalStopFlagSet = false;

	setButtonDisabledState(false);
	globalGameRunning = false;
	if(ie4)
	{
		if(globalAnimationTimer)
			window.clearTimeout(globalAnimationTimer);
		globalAnimationTimer = null;
		globalMovingDIV = document.all("IMGMOVER");
		globalMovingDIV.style.left = -100;
		globalMovingDIV.style.top  = -100;
		globalMoveInProgressBool = false;
	}
	clearTimeout(globalNextMoveTimer);
}


//--------------------------------------------------------
// Return the next file rank in the direction indicated
// Return "" if the next position is off of the board.
// fr = current file/rank (a1 - h8) 
// dir = 'N','NE','E','SE','S','SW','W','NW'
//--------------------------------------------------------
function getNextFileRank(dir, fr)
{
	var fx = globalsFiles.indexOf(fr.charAt(0));
	var rx = globalsRanks.indexOf(fr.charAt(1));
	
	if(dir == 'NW' || dir == 'N' || dir == 'NE')
		++rx;
	else if(dir == 'SW' || dir == 'S' || dir == 'SE')
		--rx;
		
	if(dir == 'NW' || dir == 'W' || dir == 'SW')
		--fx;
	else if(dir == 'NE' || dir == 'E' || dir == 'SE')
		++fx;
	
	return (rx < 0 || fx < 0 || rx > 7 || fx > 7) ? "" : globalsFiles.charAt(fx) + globalsRanks.charAt(rx);
}

function getNextBoardPosition(dir, fr)
{
	var fr = getNextFileRank(dir, fr);
	if(fr.length == 0)
		return null;
	else
		return getBoardPosition(fr);
}

//-------------------------------------
// Is the king in check from a directional
// piece (R,B,Q) with the current board state.
//
// Start at the king and look in every
// direction one space at a time for an
// enemy piece with capability to check
// the king. If found return true.
//
// If a friendly piece is found stop and
// test the next direction until all
// directions have been examined.
//-------------------------------------
function isKingInCheck(bColor)
{
	var posArray = findBoardPositionsWithPieces('K', bColor);
	if(posArray.length == 0)
		return false; // Something is very wrong, there is no King.
		
	var KPos = posArray[0];
	var KingPosition  = KPos.filerank;
	var tmpKfr;
	var dirs = "N,NE,E,SE,S,SW,W,NW".split(",");
	var dir;
	var bFriend = false;
	var bInCheck = false;
	var testPiece;
	var testPos;
	var testType
	
	for(var i=0; i<dirs.length; ++i)
	{
		tmpKfr = KingPosition;
		dir = dirs[i];
		
		while(!bFriend && !bInCheck)
		{
			testPos = getNextBoardPosition(dir, tmpKfr);
			
			if(testPos == null)
				break;
			else
			{
				testPiece = testPos.ChessPc;
				if(testPiece)
				{
					bFriend = (testPiece.color == bColor);
					if(!bFriend)
					{
						testType = testPiece.pcType;
						
						if(dir == 'NE' || dir == 'SE' || dir == 'SW' || dir == 'NW')
							bInCheck = (testType == 'B' || testType == 'Q');
						else // dir = N,S,E, or W
							bInCheck = (testType == 'R' || testType == 'Q');
					}
				}
				
				tmpKfr = testPos.filerank;
			}
		}
		
		if(bInCheck)
			break;
	}
	
	return bInCheck;
}


function manualMakeNextMove(bPaint)
{
	if(globalMoveInProgressBool)
		return;
		
	if(globalGameRunning)
		return;
		
	makeNextMove(bPaint);
	thegoto=document.getElementById("go_to");
	thegoto.value=globaliMoveNumber;
	if (globaliMoveNumber != 0) {
//		document.getElementById("pbAnalysis").style.visibility = 'visible';
	} else {
//		document.getElementById("pbAnalysis").style.visibility = 'hidden';
	}
}


//----------------------------------------------------------------
//
// March 02, 2003
// Enhancement
// Added code to handle various game endings.
//
//
// Main piece moving code, calls all sub-move code.
// If bPaint is true then Paint the board and Capture areas
// after the move otherwise just compute the move and store
// the state.
// Detect end of game condition.
//----------------------------------------------------------------
function makeNextMove(bPaint)
{
	if(globalMoveInProgressBool)
		return;

	// Hack for sliding pieces.
	globalBPaint = bPaint;

	var moveText = globalPGN_MoveArray[globaliMoveIndex];
	var fullMvTxt = moveText;
	
	// Hack for sliding pieces.
	globalMoveText = moveText;
	
	if(moveText.indexOf("*") > -1)
	{
		// Conclusion unknown.
		StopGame();
		document.F1.movenumber.value   = globaliMoveNumber;
		document.F1.piececolor.value   = "  -----";
		document.F1.destination.value  = "";
		document.F1.piecename.value    = "Unfinished";
		document.F1.skiptonumber.value = "";
		document.F1.checkstatus.value  = "Unfinished";
		return;
	}
	else if(moveText.indexOf("1/2") > -1 || moveText.indexOf("draw") > -1)
	{
		// Game ended in a draw.
		StopGame();
		document.F1.movenumber.value   = globaliMoveNumber;
		document.F1.piececolor.value   = "  -----";
		document.F1.destination.value  = "";
		document.F1.piecename.value    = "Draw";
		document.F1.skiptonumber.value = "";
		document.F1.checkstatus.value  = "Draw";
		return;
	}
	else if(moveText.indexOf("0-1") > -1 || moveText.indexOf("1-0") > -1)
	{
		// Game over.
		StopGame();
		document.F1.movenumber.value   = globaliMoveNumber;
		document.F1.piececolor.value   = (moveText.indexOf("1-0") > -1) ? "White" : "Black";
		document.F1.destination.value  = "";
		document.F1.piecename.value    = "Wins";
		document.F1.skiptonumber.value = "";
		document.F1.checkstatus.value  = "Checkmate!";
		return;
	}
	
	var chkstatus = (moveText.indexOf('+') > -1) ? "Check!" : (moveText.indexOf('#') > -1) ? "Checkmate!" : "";
	
	var pcColor = (moveText.indexOf('.') > -1); // true = white, false = black
	if(pcColor)
	{
		var parts = moveText.split('.');
		globaliMoveNumber = parts[0];
		moveText = parts[1];
	}
	
	var fullMoveText = moveText;
	var pcName = "";
	moveText = cleanMoveText(moveText);
	
	// Handle castling
	if(moveText.indexOf("O-O-O") > -1)
	{
		QueenSideCastle(pcColor);
		pcName = "QS-Castle"; 
	}
	else if(moveText.indexOf("O-O") > -1)
	{
		KingSideCastle(pcColor);
		pcName = "KS-Castle";
	}
	else
	{		switch(getMovePieceType(moveText))
		{
		case 'Q':
			pcName = "Queen";
			MoveDirectionalPiece(moveText, pcColor, 'Q', "N,E,S,W,NW,NE,SE,SW");
			break;
		case 'K':
			pcName = "King";
			MoveKing(moveText, pcColor);
			break;
		case 'N':
			pcName = "Knight";
			MoveKnight(moveText, pcColor);
			break;
		case 'R':
			pcName = "Rook";
			MoveDirectionalPiece(moveText, pcColor, 'R', "N,E,S,W");
			break;
		case 'B':
			pcName = "Bishop";
			MoveDirectionalPiece(moveText, pcColor, 'B', "NW,NE,SE,SW");
			break;
		case 'p':
			pcName = "pawn";
			MovePawn(moveText, pcColor, fullMvTxt);
			break;
		}
	}
	++globaliMoveIndex;
	
	if(bPaint)
	{
		PaintBoard();
		document.F1.skiptonumber.value = "";
		document.F1.checkstatus.value = chkstatus;
		document.F1.movenumber.value  = "#" + globaliMoveNumber;
		document.F1.piececolor.value  = (pcColor) ? "White" : "Black";
		var mvTxtOnly = getMoveTextOnly(moveText);
		document.F1.destination.value = mvTxtOnly.substring(mvTxtOnly.length-2,mvTxtOnly.length);
		document.F1.piecename.value   = pcName;
	}
	globalStr = globalStr + " " + globalPGN_MoveArray[globalNum++];
	if (document.getElementById("detail4")) document.getElementById("detail4").value = globalStr;
}

function MoveKing(mvTxt, bPc)
{
	var posArray = findBoardPositionsWithPieces('K', bPc);
	var KingPos = posArray[0];
	var len = mvTxt.length;
	var fr = mvTxt.substring(len-2,len);
	MovePiece(KingPos, getBoardPosition(fr));
}

function MoveKnight(mvTxt, bPc)
{
	//--------------------------------------------------------
	// If bareMvTxt.length == 4 assume fully disambiguated.
	//--------------------------------------------------------
	var bareMvTxt = getMoveTextOnly(mvTxt);
	if(bareMvTxt.length == 4)
	{
		var orgFR = bareMvTxt.substring(0,2);
		var dstFR = bareMvTxt.substring(3,4);
		MovePiece(getBoardPosition(orgFR), getBoardPosition(dstFR));
		return;
	}

	var posArray = findBoardPositionsWithPieces('N', bPc);
	if(posArray.length == 0)
		return; // No knight to move something is very wrong.
		
	if(posArray.length > 2)
	{
		alert("Move too ambiguous, make move with mouse");
		return;
	}
		
	var len = mvTxt.length;
	var destFR = mvTxt.substring(len-2,len);
	var moves0;
	var moves1;
	
	if(posArray.length == 1)
	{
		MovePiece(posArray[0], getBoardPosition(destFR));
	}
	else
	{
		var p1 = false;
		var p2 = false;
		var i;
		
		// See if destFR is in the possible moves for knight at posArray[0]
		moves0 = getKnightMoves(posArray[0], bPc);
		for(i=0; i<moves0.length; ++i)
		{
			if(destFR == moves0[i])
			{	
				p1 = true;
				break;
			}
		}
		
		// See if destFR is in the possible moves for knight at posArray[1]
		moves1 = getKnightMoves(posArray[1], bPc);
		for(i=0; i<moves1.length; ++i)
		{
			if(destFR == moves1[i])
			{	
				p2 = true;
				break;
			}
		}
		
		if(p1 && !p2)
			MovePiece(posArray[0], getBoardPosition(destFR));
		else if(p2 && !p1)
			MovePiece(posArray[1], getBoardPosition(destFR));
		else if(p1 && p2)
		{
			//-------------------------------------------------
			// Both pieces possible moves contain destFR.
			// Determine is the move has been disambiguated.
			//-------------------------------------------------
			if(bareMvTxt.length == 3)
			{
				var F_R = bareMvTxt.charAt(0);
				if(globalsFiles.indexOf(F_R) > -1) // disambiguated by file
				{
					if(posArray[0].file == F_R)
						MovePiece(posArray[0], getBoardPosition(destFR));
					else if(posArray[1].file == F_R)
						MovePiece(posArray[1], getBoardPosition(destFR));
				}
				else if(globalsRanks.indexOf(F_R) > -1) // disambiguated by rank
				{
					if(posArray[0].rank == F_R)
						MovePiece(posArray[0], getBoardPosition(destFR));
					else if(posArray[1].rank == F_R)
						MovePiece(posArray[1], getBoardPosition(destFR));
				}
			}
			else if(bareMvTxt.length == 4) // fully disambiguated by file and rank
			{
				var orgFR = bareMvTxt.substring(0,2);
				MovePiece(getBoardPosition(orgFR), getBoardPosition(destFR));
			}	
			else // bareMvTxt.length = 2 movetext not disambiguated, find pinned Knight.
			{
				// Temporarily remove posArray[0].ChessPc and call isKingInCheck(bColor)
				var holdPiece = posArray[0].ChessPc;
				posArray[0].ChessPc = null;
				if(isKingInCheck(holdPiece.color))
				{
					//----------------------------------------------
					// Removing posArray[0].ChessPc caused a Check
					// thus we deduce it is pinned. Move the other 
					// piece (posArray[1].ChessPc).
					//----------------------------------------------
					posArray[0].ChessPc = holdPiece;
					MovePiece(posArray[1], getBoardPosition(destFR));
				}
				else
				{
					MovePiece(posArray[0], getBoardPosition(destFR));
				}
			}
		}
	}
}

// Return Array of positions to move to
function getKnightMoves(objPos, bColor)
{
	var moves = "";
	var fr = objPos.filerank;
	var fx = globalsFiles.indexOf(fr.charAt(0));
	var rx = globalsRanks.indexOf(fr.charAt(1));
	
	if(fx + 2 < 8 && rx + 1 < 8)
		moves += (globalsFiles.charAt(fx + 2) + globalsRanks.charAt(rx + 1) + ",");
	if(fx + 2 < 8 && rx - 1 > -1)
		moves += (globalsFiles.charAt(fx + 2) + globalsRanks.charAt(rx - 1) + ",");
	if(fx - 2 > -1 && rx + 1 < 8)
		moves += (globalsFiles.charAt(fx - 2) + globalsRanks.charAt(rx + 1) + ",");
	if(fx - 2 > -1 && rx - 1 > -1)
		moves += (globalsFiles.charAt(fx - 2) + globalsRanks.charAt(rx - 1) + ",");
	if(fx + 1 < 8 && rx + 2 < 8)
		moves += (globalsFiles.charAt(fx + 1) + globalsRanks.charAt(rx + 2) + ",");
	if(fx + 1 < 8 && rx - 2 > -1)
		moves += (globalsFiles.charAt(fx + 1) + globalsRanks.charAt(rx - 2) + ",");
	if(fx - 1 > -1 && rx + 2 < 8)
		moves += (globalsFiles.charAt(fx - 1) + globalsRanks.charAt(rx + 2) + ",");
	if(fx - 1 > -1 && rx - 2 > -1)
		moves += (globalsFiles.charAt(fx - 1) + globalsRanks.charAt(rx - 2) + ",");
		
	var len = moves.length;
	moves = moves.substring(0,len-1);
	var movesArray = moves.split(','); 
	
	// Only return valid moves.
	var finalArray = new Array();
	var ix = 0;
	for(var i=0; i<movesArray.length; ++i)
	{
		if(getBoardPosition(movesArray[i]).ChessPc == null) 
		{
			// No piece on space is valid
			finalArray[ix++] = movesArray[i];
		}
		else
		{
			// Opposite color piece on space is valid.
			if(getBoardPosition(movesArray[i]).ChessPc.color != bColor)
				finalArray[ix++] = movesArray[i];
		}
	}
	return finalArray;
}

function MoveDirectionalPiece(mvTxt, bPc, pType, sDirs)
{
	//--------------------------------------------------------
	// If bareMvTxt.length == 4 assume fully disambiguated.
	//--------------------------------------------------------
	var bareMvTxt = getMoveTextOnly(mvTxt);
	if(bareMvTxt.length == 4)
	{
		var orgFR = bareMvTxt.substring(0,2);
		var dstFR = bareMvTxt.substring(3,4);
		MovePiece(getBoardPosition(orgFR), getBoardPosition(dstFR));
		return;
	}
	
	var posArray = findBoardPositionsWithPieces(pType, bPc);
	if(posArray.length == 0)
		return; // No rook to move something is very wrong.
		
	if(posArray.length > 2)
	{
		alert("Move too ambiguous, make move with mouse");
		return;
	}
	
	var len = mvTxt.length;
	var destFR = mvTxt.substring(len-2,len);
	var moves0;
	var moves1;
	
	if(posArray.length == 1)
	{
		MovePiece(posArray[0], getBoardPosition(destFR));
	}
	else
	{
		var p1 = false;
		var p2 = false;
		var i;
		
		// See if destFR is in the possible moves for knight at posArray[0]
		moves0 = getDirectionalMoves(posArray[0], bPc, sDirs);
		for(i=0; i<moves0.length; ++i)
		{
			if(destFR == moves0[i])
			{	
				p1 = true;
				break;
			}
		}
		
		// See if destFR is in the possible moves for knight at posArray[1]
		moves1 = getDirectionalMoves(posArray[1], bPc, sDirs);
		for(i=0; i<moves1.length; ++i)
		{
			if(destFR == moves1[i])
			{	
				p2 = true;
				break;
			}
		}
		
		if(p1 && !p2)
			MovePiece(posArray[0], getBoardPosition(destFR));
		else if(p2 && !p1)
			MovePiece(posArray[1], getBoardPosition(destFR));
		else if(p1 && p2)
		{
			//-------------------------------------------------
			// Both pieces possible moves contain destFR.
			// Determine is the move has been disambiguated.
			//-------------------------------------------------
			if(bareMvTxt.length == 3)
			{
				var F_R = bareMvTxt.charAt(0);
				if(globalsFiles.indexOf(F_R) > -1) // disambiguated by file
				{
					if(posArray[0].file == F_R)
						MovePiece(posArray[0], getBoardPosition(destFR));
					else if(posArray[1].file == F_R)
						MovePiece(posArray[1], getBoardPosition(destFR));
				}
				else if(globalsRanks.indexOf(F_R) > -1) // disambiguated by rank
				{
					if(posArray[0].rank == F_R)
						MovePiece(posArray[0], getBoardPosition(destFR));
					else if(posArray[1].rank == F_R)
						MovePiece(posArray[1], getBoardPosition(destFR));
				}
			}
			else if(bareMvTxt.length == 4) // fully disambiguated by file and rank
			{
				var orgFR = bareMvTxt.substring(0,2);
				MovePiece(getBoardPosition(orgFR), getBoardPosition(destFR));
			}	
			else // bareMvTxt.length = 2 movetext not disambiguated, find pinned Knight.
			{
				// Temporarily remove posArray[0].ChessPc and call isKingInCheck(bColor)
				var holdPiece = posArray[0].ChessPc;
				posArray[0].ChessPc = null;
				if(isKingInCheck(holdPiece.color))
				{
					//----------------------------------------------
					// Removing posArray[0].ChessPc caused a Check
					// thus we deduce it is pinned. Move the other 
					// piece (posArray[1].ChessPc).
					//----------------------------------------------
					posArray[0].ChessPc = holdPiece;
					MovePiece(posArray[1], getBoardPosition(destFR));
				}
				else
				{
					MovePiece(posArray[0], getBoardPosition(destFR));
				}
			}
		}
	}
}

function getDirectionalMoves(objPos, bColor, sDirs)
{
	var moves = "";
	var Rfr = objPos.filerank;
	var tmpRfr;
	var dirs = sDirs.split(",");
	var foundEnd;
	var testPos;
	for(var i=0; i<dirs.length; ++i)
	{
		tmpRfr = Rfr;
		dir = dirs[i];
		foundEnd = false;
		
		while(!foundEnd)
		{
			testPos = getNextBoardPosition(dir, tmpRfr);
			if(testPos == null)
				break;
			else
			{
				if(testPos.ChessPc == null) // Empty position valid
				{
					moves += testPos.filerank + ",";	
				}
				else 
				{
					moves += (testPos.ChessPc.color == bColor) ? "" : testPos.filerank + ",";
					foundEnd = true;	
				}				
				tmpRfr = testPos.filerank;
			}
		}
	}
		
	var len = moves.length;
	moves = moves.substring(0,len-1);
	return moves.split(',');
}

function MovePawn(mvTxt, bPc, FullMoveText)
{
	//--------------------------------------------------
	// A pawn can only get to a space by a very limited 
	// number of moves. 
	// 
	// For white:
	// If an enemy is at the dest position then the pawn
	// came from an adjacent file.
	//
	// If no enemy is at the dest position then
	//   If the destination rank is 6 and the dest-file != src-file
	//      en-passant capture occurs.
	//   Else
	//      moving forward by one.
	// Else
	//   capture move coming from an adjacent file.  
	//
	// If dest position has rank 4 then the pawn either
	// came from rank 2 (if no piece is on rank 3) or 
	// from rank 3
	//
	// For black:
	// Reflect the white algorithm.
	//--------------------------------------------------
	
	var bareMvTxt = getMoveTextOnly(mvTxt); // fr (non-attack move) or ffr (attack move).
	var len       = bareMvTxt.length;
	var destFR    = bareMvTxt.substring(len-2,len);
	var destfile  = destFR.charAt(0);
	var destrank  = destFR.charAt(1);
	var destPos   = getBoardPosition(destFR);
	var testPos;
	
	if(destPos.ChessPc == null) 
	{
		var MoveDone;
		if(bPc) // White moves.
		{
			MoveDone = false;
			//-------------------------------------------------
			// Test for en-passant capture move.
			// Actual capture is handled in CompleteMovePiece
			//-------------------------------------------------
			if((destrank == 6 && FullMoveText.indexOf('x') > -1 && destPos.ChessPc == null) ||
			   (destrank == 6 && FullMoveText.length > 2 && destPos.ChessPc == null)) 
			{
				var OrigFileRank = mvTxt.charAt(0) + '5';
				testPos = getBoardPosition(OrigFileRank);
				if(testPos)
				{
					MovePiece(testPos, destPos);
					MoveDone = true;
				}
			}
			
			if(!MoveDone)
			{
				//--------------------------------------------------------
				// Non-attacking moves Pawn is moving from the same file.
				//--------------------------------------------------------
				if(destrank == 4)
				{
					testPos = getBoardPosition("" + destfile + '3');
					if(testPos.ChessPc == null)
					{
						// moving from rank 2
						testPos = getBoardPosition("" + destfile + '2');
						MovePiece(testPos, destPos);
					}
					else
					{
						// moving from rank 3
						MovePiece(testPos, destPos);
					}
				}
				else
				{
					// moving from rank-1
					var rm1 = destrank-1;
					testPos = getBoardPosition("" + destfile + rm1);
					MovePiece(testPos, destPos);
				}
			}
		}
		else // Black moves.
		{
			MoveDone = false;
			
			//-------------------------------------------------
			// Test for en-passant capture move.
			// Actual capture is handled in CompleteMovePiece
			//-------------------------------------------------
			if((destrank == 3 && FullMoveText.indexOf('x') > -1 && destPos.ChessPc == null) ||
			   (destrank == 3 && FullMoveText.length > 2 && destPos.ChessPc == null)) 
			{
				var OrigFileRank = mvTxt.charAt(0) + '4';
				testPos = getBoardPosition(OrigFileRank);
				if(testPos)
				{
					MovePiece(testPos, destPos);
					MoveDone = true;
				}
			}
			
			if(!MoveDone)
			{
				//--------------------------------------------------------
				// Non-attacking moves Pawn is moving from the same file.
				//--------------------------------------------------------
				if(destrank == 5)
				{
					testPos = getBoardPosition("" + destfile + '6');
					if(testPos.ChessPc == null)
					{
						// moving from rank 7
						testPos = getBoardPosition("" + destfile + '7');
						MovePiece(testPos, destPos);
					}
					else
					{
						// moving from rank 6
						MovePiece(testPos, destPos);
					}
				}
				else
				{
					// moving from rank+1
					var rp1 = destrank-0 + 1;
					testPos = getBoardPosition("" + destfile + rp1);
					MovePiece(testPos, destPos);
				}
			}
		}
	}
	else  // Attack moves
	{
		// Pawn attacks are denoted as fxfn (changed to ffn by getMoveTextOnly()) or ffn
		// The first f is the originating file.
		// the second f is the destination file.
		var origfile  = (bareMvTxt.length == 3) ? bareMvTxt.charAt(0) : "";
		
		if(bPc) // White moves.
		{
			var rm1 = destrank-1;
			testPos = getBoardPosition("" + origfile + rm1);
			MovePiece(testPos, destPos);
		}
		else // Black moves.
		{
			var rp1 = destrank-0 + 1;
			testPos = getBoardPosition("" + origfile + rp1);
			MovePiece(testPos, destPos);
		}
	}
	
	if(!ie4 || globalGameSimulate)
	{
		testPawnPromotion(destPos);
	}
}

function testPawnPromotion(destPos)
{
	
	if(destPos == null)
		return;

	if(destPos.ChessPc == null)
		return;

	var bPc = destPos.ChessPc.color;
	var mvTxt = globalMoveText;

	//----------------------------------------------------------------------------
	// Pawn Promotion: If a pawn advances all the way across the board promote it.
	//----------------------------------------------------------------------------
	if(bPc && destPos.rank == 8 || !bPc && destPos.rank == 1)
	{
		var promotes = "QNBR";
		var promoinx = promotes.indexOf(mvTxt.charAt(mvTxt.length-1));
		promoinx = (promoinx > -1) ? promoinx : 0;
		var pcIndex = destPos.ChessPc.index;
		var pt = promotes.charAt(promoinx);
		
		switch(pt)
		{
		case 'Q':
			destPos.ChessPc = new ChessPiece(bPc, "Q", "Queen", "", pcIndex); 
			break;
		case 'N':
			destPos.ChessPc = new ChessPiece(bPc, "N", "Knight", "", pcIndex);
			break;
		case 'B':
			destPos.ChessPc = new ChessPiece(bPc, "B", "Bishop", "", pcIndex);
			break;
		case 'R':
			destPos.ChessPc = new ChessPiece(bPc, "R", "Rook", "", pcIndex);
			break;
		}
		
		if(destPos)
			if(destPos.ChessPc)
				setImage(destPos.ChessPc);
	}
}

//--------------------------------------
// Castle Animation Support
//--------------------------------------
var globalBPking;
var globalBProok;
var globalCPking;
var globalCProok;
var globalCastlingBool = false;

function QueenSideCastle(bc)
{
	globalBPking = getBoardPosition((bc)?'e1':'e8');
	globalCPking = getBoardPosition((bc)?'c1':'c8');
	globalBProok = getBoardPosition((bc)?'a1':'a8');
	globalCProok = getBoardPosition((bc)?'d1':'d8');
	
	if(!ie4 || globalBPaint == false)
	{
		globalCastlingBool = false;
	
		globalCPking.ChessPc = globalBPking.ChessPc;
		globalCProok.ChessPc = globalBProok.ChessPc;
		globalBPking.ChessPc = null;
		globalBProok.ChessPc = null;
	
		globalOrgPos = globalCPking;
		globalDstPos = globalCProok;
		ResetBoardColors();
		HighlightMovePositions();
	}
	else
	{
		globalCastlingBool = true;
		MovePiece(globalBPking, globalCPking)
	}
}

function KingSideCastle(bc)
{
	globalBPking = getBoardPosition((bc)?'e1':'e8');
	globalCPking = getBoardPosition((bc)?'g1':'g8');
	globalBProok = getBoardPosition((bc)?'h1':'h8');
	globalCProok = getBoardPosition((bc)?'f1':'f8');
	
	if(!ie4 || globalBPaint == false)
	{
		globalCastlingBool = false;
		
		globalCPking.ChessPc = globalBPking.ChessPc;
		globalCProok.ChessPc = globalBProok.ChessPc;
		globalBPking.ChessPc = null;
		globalBProok.ChessPc = null;
	
		globalOrgPos = globalCPking;
		globalDstPos = globalCProok;
		ResetBoardColors();
		HighlightMovePositions();
	}
	else
	{
		globalCastlingBool = true;
		MovePiece(globalBPking, globalCPking)
	}
}

function CompleteCastle()
{
	globalOrgPos = globalBProok;
	globalDstPos = globalCProok;
	HighlightMovePositions();
	var imgnum = FileRankToNumber(globalBProok.filerank);
	document.images["IMG_" + imgnum].src = EMPTY43x43.src;
	globalCastlingBool = false;
	MovePiece(globalBProok, globalCProok);
}


function MovePiece(orgPos, destPos)
{
	globalOrgPos = orgPos;
	globalDstPos = destPos;
	
	if(globalBPaint && ie4)
	{
		AnimateMovePiece(orgPos, destPos);
	}
	else
	{
		CompleteMovePiece(orgPos, destPos);
	}
}

function CompleteMovePiece(orgPos, destPos)
{
	globalOrgPos = orgPos;
	globalDstPos = destPos;

	var bColor = orgPos.ChessPc.color;
	var file   = destPos.file;
	var rank   = destPos.rank;

	if(destPos.ChessPc)
	{
		PieceToCaptureArea(destPos.ChessPc);
	}
	else if(orgPos.ChessPc.pcType == 'p') // Test for en passant captures
	{
		if(bColor && rank == 6) // White
		{
			// If space at destPos.file/destPos.rank-1 contains a black pawn, with moveno = globaliMoveIndex-1.
			// it has been captured en-passant.
			var rm1 = rank-1;
			var testPos = getBoardPosition("" + file + rm1);
			if(testPos.ChessPc)
			{	
				if(!testPos.ChessPc.color && testPos.ChessPc.pcType == 'p')
				{	
					if(testPos.ChessPc.moveno == globaliMoveIndex-1)
					{
						PieceToCaptureArea(testPos.ChessPc);
						testPos.ChessPc = null;
					}
				}
			}
		}
		else if(!bColor && rank == 3)
		{
			// If space at destPos.file/destPos.rank+1 contains a white pawn, with moveno = globaliMoveIndex-1.
			// it has been captured en-passant.
			var rp1 = rank-0+1;
			var testPos = getBoardPosition("" + file + rp1);
			if(testPos.ChessPc)
			{
				if(testPos.ChessPc.color && testPos.ChessPc.pcType == 'p')
				{
					if(testPos.ChessPc.moveno == globaliMoveIndex-1)
					{
						PieceToCaptureArea(testPos.ChessPc);
						testPos.ChessPc = null;
					}
				}
			}
		}
	}
	destPos.ChessPc = orgPos.ChessPc;
	++destPos.ChessPc.moves;
	destPos.ChessPc.moveno = globaliMoveIndex;
	orgPos.ChessPc = null;
}


function applyColors()
{
	SetChessPieceImages();
	ResetBoardColors();
	PaintBoard();
	if(ie4)
	{
		document.all("GAMEAREA").bgColor = globalBGColor;
	}
}

function gameinit()
{
	if (document.F1) {
		InitializeChessBoard();
		InitializeCaptureArea();
		gamereset(true);
		applyColors();
	}
}

function gamereset(bPaint)
{
	if(ie4)
	{
		document.getElementById("STOPGAME").disabled = true;
	}
	
	if(globalAnimationTimer)
		window.clearTimeout(globalAnimationTimer);
	globalAnimationTimer = null;
	BoardToStartState();
	CaptureAreaToStartState();
	globaliMoveIndex = 0;
	globaliMoveNumber = 0;
	parsePGNmovetext(document.F1.moves.value);
	
	if(bPaint)
	{
		document.F1.movenumber.value = "";
		document.F1.piececolor.value = "";
		document.F1.destination.value = "";
		document.F1.piecename.value = "";
		document.F1.skiptonumber.value = "";
		document.F1.checkstatus.value = "";
		globalOrgPos = null;
		globalDstPos = null;
		ResetBoardColors();
		PaintBoard();
	}
	globalNum = 0;
	globalStr = "";
	if (document.getElementById("detail4")) document.getElementById("detail4").value = globalStr;

}

function showhelp()
{
	if(globalGameRunning)
		return;
	var helpwin = window.open("chesspractice3help.htm","helpwindow","width=500,height=300,scrollbars",false);
	helpwin.focus();
}


function showsettings()
{
	if(globalGameRunning)
		return;
	var helpwin = window.open("chess_settings.htm","settingswindow","width=540,height=300",false);
	helpwin.focus();
}
