/**********************************************************************************************************
Version: 1.0d
28.8.2011: Remove a small bug, that messed up the time count down some times if the time is not cleanly entered.
Version: 1.0c
16.8.2011: Eliminate NaN:NaN:NaN in clock display when clock went below 0
	   Bug:Disable posting.html loading when no posting is present
Version: 1.0b
15.8.2011: Add Id to the Table Cells of each Game, in the form of "BoardCell-<BNr>", this allows to shift entire boards to a different position (thanks to Frank Jaeger).
	Bug: Remove bug in proc ModifyTOMA, where now any carriage return is replaced by a ' ' space rather than an empty character
	ScrollIntoView now obsolete, replaced by a reliable function in RefreshBoard
	Bug: Replace in Procedure: UpdateFile ..imeout("UpdateFile(0)",200).. instea ..imeout("UpdateFile(1)",200), which leads to an endless load-loop when load time exceeds 1 sec.
	
Version: 1.0a 
20.6.2011: Remove small bug in GetFontSize() when multiple .css files are present w/o Font defintion an Error occurs.

Version: 1.0
14.5.2011: Include DGT-TOMA pgn time tag format (thanks for input from Christian Eichner christian@liveschach-schau.de and Michail Prevenios, mprevenios@gmail.com)
15.5.2011: Introduce a AlterateColor Variable, which allows to automatically rotate boards at the beginning by defining a comma separated list named AlternateColors (based on input from Michail Prevenios)
	Remove Bug in IE (all versions), which results in reload does not properly clean up the sleeping tasks, and therefore the count down is not re-initiated (can lead to long time waitng for first update) 
	Improvement on the posting part (IE did not align properly), including some additions to the ichess.css file

Version: 1.0rc3b
15.3.2011: Remove Bug in function getHttpRequest: if (document.getElementById("Postings")) document.getElementById("Postings").innerHTML =.... (error when Posting is switched off)
19.3.2011: Remove Bug in RotateBoard: Time and Names were not properly reversed - corrected (hint by Christian Eichner)
21.3.2011: Bug in ichess(); IE calculates the widht of the chessboards as zero!!!!, corrected (15.5.2011 Ff).

Version: 1.0rc3a
* Remove Bug in AnalyzePGN which prevents proper stop of analysis, and caused an error if there were more boards than games
* Change Display of boards to only max. games (other boards are empty).

Version: 1.0rc3
* Add Preset Games Variable
* Modify default of ScrollIntoView variable to be true for single games and false otherwise (input from Danilo Peruš)
* Add CookiesAllowed variable to switch off cookies, which can cause instable display of games when unstable upload conditions occur.

Version: 1.0rc2a
add "if (Parts[ii].length != 2) continue;" in Line 1318 Safari on iPad gives an error if there is nothing after "]" closing bracket
*************************************************************************************************************/
var iChessVersion	= "1.0d";		// Version
var PgnFileName		= "pgn/ichess.pgn"; 	// PGN FileName to work with;
var PostingFileName	= "pgn/postings.html"	// File containing actual postings
var AddPostsFileName	= "pgn/ichess.php"	// File which writes new postings on the server
var UpdateInterval	= 5; 			// Read frequency of the pgn file in seconds, not recommended to use less than aprox. 5 seconds!
var InitialClock	= "1:30:00";		// initial Clock settings 
var IsDemo		= 0;			// if Demo is set to 1, it seeks simulates a continous game by gradually cutting off some of the moves.
						// in the same directory as given in PgnFileName
var AllowPostings	= 1;			// Defines if Postings are accepted or not
var PieceName 		= "KQRBNP";		// the SAN notation in which the games are delivered (King,Queen,Rook,Bishop,Night,Pawn)
var ShowPieceName 	= "KDTLSB";		// the SAN notation to be displayed
var Scale		= 0.6;			// Scaling of the Board
var Rows		= 1;			// Number of Rows  
var Cols 		= 2;			// Number of Colums
// var PresetGames		= "";		// no preset of games, just follow the .pgn order
var AlternateColors	= '';			// 0 .white at the bottom (std), 1.. black at the bottom, so: var AlternateColors="1,0,1,0" starts with black on the bottom and alteranates the next four games 
var DisplayPostings	= "b";			// values: l=left,r=right,t=top,b=bottom or anything else to disable
var PostingHeight	= 10;			// Number of lines to display for Postings (if enabled)
var Author		=" Rev:"+iChessVersion+" (c) Felix Fuernhammer <a href='http://ichess.de'>iChess.de</a>";
var DisableURI		= false;		// URI input can be disabled, in order to prevent for instance manipulation on UpdateInterval variable 
var ScrollIntoView	= false;		// This defines, if the last move should be displayed when updated, it is a nuisance when the number of boards exceeds the page height.
var TimeCountDown	= true;		// Timer for countdown during moves 
var OneMovePerLine	= false;		// Displays only one move per line in the pgn area, instead of as many as the width of the field and the fontsize allow
var NoFooter		= false;		// Switches off the Footer, only allowed for local broadcasting due to licensing 
var NoButtons		= false;		// Switches off the Move-Buttons, used for local broadcasting
var NoGameSelectForm	= false;		// Switches off the Games Select Form, mainly for local broadcasting
var CookiesAllowed	= false; 		// disable cookies by default, as it can lead to strange effect if the upload is not stable => Need future rework

// the variables below normally dont need to be modified/changed
var FontSize		= 12;			// Nominal FontSize in pt when Field Size ids 65 pixel, is modified by .css entry in #iChess (font-size)
var Size		= 56;			// Size of the Pieces in the original png grapics, use Scale for modification
var TimeOut		= 400;			// in msec for screen refreshes, etc. (this is not the pgn update interval) increase on old machines
var gImagePath		= "png/";		// Path for images
var SkipRefresh		= 0;
var IsLoading		= 0;			// set to 1 if file is loaded
var gImagePathOld 	= "-";
var gPostingName	= "Name";
var gLastGameSelCont 	= 0;
var MaxMove=300, isInit=false, isMoving=false, isCalculating=false, i;
var King=0, Queen=1, Rook=2, Bishop=3, Night=4, Pawn=5, OOO=6, OO=7, NullM=8;
var ShowTimeInPGN	= false;		// true/false to show clock information in pgn section (format in ichess.css #pgn t)
var CorrectPadding	= 0;
var gPics 		= new Array('1x1.png', 'k.png', 'q.png', 'r.png', 'b.png', 'n.png', 'p.png'); 
var isIE		= CheckIE();
PieceCode 		= new Array(6); for (i=0; i<6; i++) PieceCode[i]	= PieceName.charCodeAt(i);


// the  following four arrays contain the pgn data
PgnMoveText		= new Array(); 
// GameText	= new Array(); 
//FenText		= new Array(); 
//SanText		= new Array(); 

var MvCount		= new Array(); 
var LastMove		= new Array();
var MvTyp		= new Array(); 
var IsLive 		= new Array(); 
var Brd			= new Array(); 

var Tags 		= new Array(); 
var Piece 		= new Array();
var PosX		= new Array(); 
var PosY		= new Array();
var PMovs		= new Array(); 
var HTime		= new Array(); 
var HPiece	 	= new Array(); 
var HTyp	 	= new Array(); 
var HPosX	 	= new Array(); 
var HPosY	 	= new Array(); 
var ActiveGames 	= new Array();

var PostingFeedback = "Post sent, please be patient";
var textPosts= new Array("Posting deactivated!", "Post Comment (viewable by everybody)!", "Posts selectively enabled!", "Send Post!", "Currently no postings!");

function sign(nn)		{ if (nn>0) return(1); if (nn<0) return(-1); return(0); }

function EvalUrlString() {
	var entry, llist, ii, jj, wert, url = window.location.search; 
	if ((DisableURI) || (url == "")) return;

	url=url.substring(1, url.length);		// strip the precedign ? off
	url=url.replace(/\|/g,'/');			// allow | instead of / for directory delimiter
	url=url.replace(/%7C/g,'/');			// slash representation
	url=unescape(url);				// the URI is ususally "unescaped" i.e critical letters are hex represented
	llist = url.split("&");
	for (ii=0; ii<llist.length; ii++) { 
		jj=llist[ii];
		if ((jj.length==0) || (jj.search("=")< 0)) continue; 
		entry = jj.split("=");
		if (entry.length != 2) continue;
		wert=entry[1]; name=entry[0];
		eval(name+"="+wert);
	}
}

function DispError(str) { if (document.getElementById('Author')) document.getElementById('Author').innerHTML = str; };

function GetIsRotated(BNr) {			// returns if a board is rotated or not (in value of ID 'isRotated-X' for Board X
	var selObj = document.getElementById('isRotated-'+BNr);
	if ((selObj) && (parseInt(selObj.value) == 1)) return(1); else return(0);  // make sure it only returns 0 or 1;
}

function ReadCookie(BNr) {
	var b = new Array(2);
	if ((! document.cookie) || (! CookiesAllowed)) return -1;
	var b = document.cookie.split("=");
	b = b[1].split(",");
//	if (Tags.length != b.length ) return -1;
 	if (BNr < b.length) ActGame = b[BNr];
	else ActGame = -1;
	if (ActGame >=0 ) return ActGame;
	return -1;
}
function WriteCookie() {
	// first delete old cookie
	if(document.cookie) { 
		var cookie = document.cookie.split(";");
		var a = new Date();
		a.setTime(a.getTime() -(1000*60*60));  // one hour before;
		document.cookie = cookie[0]+'; expires='+a.toGMTString()+';'; 
	}
	cookie = "Selection=";
	for (var BNr = 0; BNr < (Rows*Cols); BNr++) cookie +=GetActiveGame(BNr)+",";
	var a = new Date(); 
	a = new Date(a.getTime() +1000*60*60);  // coockie resides one hour
	var x = cookie;		// +'; expires='+a.toGMTString()+';'; 
	document.cookie = x;
}

function GetActiveGame(BNr) {			// gets active game stored in ID 'fsel-X' of Board X
	var sel = ReadCookie(BNr);
	if ((sel < 0) || (sel >= PgnMoveText.length)) {
		if ((ActiveGames) && (ActiveGames.length>BNr)) sel = ActiveGames[BNr];
		else {
			var x = document.getElementById('GameSel-'+BNr);
			if (x) { var sel = x.selectedIndex; } else { sel = BNr; };
		}
	}
	return(sel%PgnMoveText.length);
}

function RotateBoard(BNr, Refresh) { 			// Called if Board rotation is clicked (black sq or numbering area)
	with (document) {
		var selObj = getElementById('isRotated-'+BNr),rot=GetIsRotated(BNr),bl, blObj,wh, whObj;
		if (selObj) selObj.value = (1 - rot);
		blObj=getElementById('Black-'+BNr); if (!blObj) return; 
		whObj=getElementById('White-'+BNr); if (!whObj) return;	
		bl=blObj.style.backgroundColor; 
		wh=whObj.style.backgroundColor; 
		with (blObj.style) { backgroundColor=wh; color=bl; }
		with (whObj.style) { backgroundColor=bl; color=wh; }
		with (getElementById('TimeBlack-'+BNr).style) { backgroundColor=wh; color=bl; }
		with (getElementById('TimeWhite-'+BNr).style) { backgroundColor=bl; color=wh; }
	}
	if (Refresh) RefreshBoard(BNr); 
}

function CheckIE() { 				// check if Browser is IE5.5 or 6.0 for png bug correction
	var arVersion = navigator.appVersion.split("MSIE");
	var version = parseFloat(arVersion[1]);
	if (isNaN(version)) return(0); 
	else return(version);
}

function ExtendArrays(BNr) {			// Extends all necessary arrays if an additional board is defined
	var i,j;
	if (Brd.length > BNr) return;
	Brd.push(new Array(8)); i=Brd.length-1; for (j=0; j<8; j++) Brd[i][j] = new Array(8);
	MvCount.push(0);
	LastMove.push(0);
	MvTyp.push(0);
	IsLive.push(1);
	Tags.push(new Array('White','Black','...'));
	Piece.push( new Array(new Array(16), new Array(16))); 
	PosX.push(  new Array(new Array(16), new Array(16))); 
	PosY.push(  new Array(new Array(16), new Array(16))); 
	PMovs.push( new Array(new Array(16), new Array(16))); 
	HTime.push( new Array(MaxMove));
	HPiece.push(new Array(new Array(MaxMove),new Array(MaxMove)));
	HTyp.push(  new Array(new Array(MaxMove),new Array(MaxMove)));
	HPosX.push( new Array(new Array(MaxMove),new Array(MaxMove)));
	HPosY.push( new Array(new Array(MaxMove),new Array(MaxMove)));
}

function GetFontSize()	{			// Checks if there is a font-size definition in any of the .css style sheets in rule #iChess
	var obj,i,j,l, FS=FontSize;
	with (document) {
		for (i=0;i< styleSheets.length;i++) {
			if (isIE > 0) obj= styleSheets[i].rules; else  obj= styleSheets[i].cssRules;
			if (obj) {
				for (j=0; j < obj.length;j++) {  
					if (obj[j].selectorText == "#ichess") { 
						FS = obj[j].style.fontSize; 
						if (FS != "") FontSize = parseInt(FS);
					}
				}
			}
		}
	}
}

function SetFontSize() {			// Sets Fontsize to px getting the pixel per pt rate 
	var div, s, px;
	with (document) { 
		if(!createElement) return;
		div = createElement('div');
		s = div.style;
		s.border = 'none';
		s.padding = 0;
		s.width = FontSize+'pt';
		body.appendChild(div);
		px = div.offsetWidth;
		body.removeChild(div);
		if (! px) px = FontSize * 4 / 3;		// if offsetWidth does not work we assume 96 dpi screen resolution 
		FontSize=Math.round(px/2+(px/2*Scale));	
		var padd = Math.round(FontSize/2) 
		s.padding=padd+'px';
		s.width = FontSize+"px";
		body.appendChild(div);
		var px1 = div.offsetWidth;
		body.removeChild(div);
		if (px1) CorrectPadding = FontSize + 2*padd -px1;
	}
}

function DispPostings(Size) {
	with (document) {
		var w = getElementById("Eitschess").offsetWidth
		write("<table id='PostingTab' style='width:"+w+"px;'><tr>");
			write("<td class='inpPostN' style='border-style:none;'><input type='text' class='inpPostN' name='Name' id='Name' value='"+gPostingName+"' onfocus='this.select();'></td>");
			write("<td class='inpPostC' style='border-style:none;'><input type='text' class='inpPostC' name='Post' id='Post' value='"+textPosts[AllowPostings]+"' onfocus='this.select()'></td>");
			write("<td><input type='button' name='Check' id='SubmittPosting' value='"+textPosts[3]+"' onClick='AddPosting()'></td>");
		write("</tr></table>");
		write("<div id='Postings' style='width:"+w+"px;'>"+textPosts[4]+"</div>");
	}			
}	

function AddPosting() {
	var Name = document.getElementById("Name").value;
	var Post = document.getElementById("Post").value;
	if (Post == "") return;
	if (AllowPostings==0) {
		document.getElementById("Post").value=textPosts[AllowPostings];
		return;
	}
	var FName = PgnFileName.split(".")[0];
	var FName =FName+".php";
	with (document) {
		var Name = getElementById("Name").value;
		var Post = getElementById("Post").value;
		getElementById("Post").value = PostingFeedback;
		getHttpRequest(AddPostsFileName,"?Name="+escape(Name)+"&Post="+escape(Post));  	// Parameter: FileName, String, isPosting
		getHttpRequest(PostingFileName,"?T="+Math.random());

	}
}
function Check4AlternateColors () {
	var curcol;
	// check for color preset in Variable AlterateColors '0,1'); etc.
	var acols = AlternateColors.split(',');
	for (var BNr=0; BNr<(Rows*Cols); BNr++) {
	 	if (acols.length > BNr) {
			curcol = GetIsRotated(BNr);
			if (curcol == (1-acols[BNr])) RotateBoard(BNr,0); 
		}
	}
}

function iChess() {							// main routine - displays the entire chess board, and initiates file updating
	var ii, jj,BNr;
	GetFontSize();								// Adjusts Fontsize from style sheet, this is necessary because there is no page jet 
	EvalUrlString();							// accounts for url strings in form of ?name1=val1&name2=val2..
	SetFontSize();								// Scale the Fontsize to pixel
	if(typeof PresetGames != "undefined") ActiveGames = PresetGames.split(",");
	with (document) {
		write("<div>");
			write("<table id='Eitschess'><tr><td>");
			if (DisplayPostings=='t') {
				DispPostings(PostingHeight+'em');write("</td></tr><tr><td>"); 
				WriteGames(); 
			} else if (DisplayPostings=='l') {
				DispPostings('100%');write("</td><td>");
				WriteGames(); 
			} else if (DisplayPostings=='b') {
				WriteGames(); 
				write("</td></tr><tr><td>");DispPostings(PostingHeight+'ex');
			} else if (DisplayPostings=='r') {
				WriteGames(); 
				write("</td><td>");DispPostings('100%');
			} else 	{
				WriteGames(); 
			}
			write("</td></tr></table>");
			write("<input type='hidden' id='pgnfile' value='No pgn file!'>");		// stores the pgn file  
			write("<input type='hidden' id='UpdateTime' value='0'>");			// stores UpdateTime 
		write("</div>");
	}
	if (AlternateColors) Check4AlternateColors();		// set colors along the variable AlternateColors ("comma separated zeros and ones");
	setTimeout("UpdateFile(1)",TimeOut); 			// set remaining time 0 in order to refresh right after load 
}

function WriteGames() {

	with (document) {
		write("<table id='iChess'>");
		for (var ii=0; ii < Rows; ii++) {
			write("<tr>");
			for (var jj=0; jj < Cols; jj++) { 
				BNr = ii*Cols+jj;
				write("<td id='BoardCell-"+BNr+"'>"); 
				ExtendArrays(BNr);
				WriteGame(BNr); write("</td>"); 

				// check if colors are preset (variable AlternateColors), and rotate board if not matched.

			}
			write("</tr>");
		}
		if (! NoFooter) { write("<tr><th colspan="+Cols+">"); WriteFooter(); write("</th></tr>"); }
		write("</table>");
	}
}

function WriteGame(BNr) {  			// displays the entire Board including buttons and Names

	var BlWhW="width:"+Math.round(5*Size*Scale)+"px; ";
	var BlWhT="width:"+Math.round(2.5*Size*Scale)+"px; ";
 	var padd = "padding:"+Math.round(FontSize/3)+"px; ";
	var H0   = "height:"+Math.round(FontSize*7/3)+"px; "; //CorrectPadding;
	var H1   = "height:"+Math.round(FontSize*2.25)+"px; "; //CorrectPadding;
	var H2   = "height:"+(Math.round(8.5*Size*Scale+4.5*FontSize))+"px; ";
	var W1   = "width:"+(Math.round(4*Size*Scale+FontSize*2/3))+"px; ";
	var FS= "font-size:"+Math.round(1.1*FontSize)+"px; ";
	var FS1="font-size:"+Math.round(FontSize)+"px; ";
	var top="vertical-align:top; ";
	var s = "style='"+(top+W1+H1)+"'";
	with (document) {

		write("<TABLE class='FrameTabs'><tr>");
			write("<th rowspan='2'>"); //write("<td "+styl+">");
				write("<table class='iCGameTab'>"); 
					write("<tr><td><div class='BW Black' id='Black-"+BNr+"'     style='"+FS+BlWhW+padd+"'>"+Tags[BNr]["Black"]+"</div></td>");
					write(    "<td><div class='BW Black Time' id='TimeBlack-"+BNr+"' style='"+FS+BlWhT+padd+"'>"+InitialClock+"</div></td></tr>");  
					write("<tr><th colspan='2'>"); WriteBoard(BNr); write("</th></tr>");
					write("<tr><td><div class='BW White'      id='White-"+BNr+"'     style='"+FS+BlWhW+padd+"'>"+Tags[BNr]["White"]+"</div></td>");
					write(    "<td><div class='BW White Time' id='TimeWhite-"+BNr+"' style='"+FS+BlWhT+padd+"'>"+InitialClock+"</div></td></tr>");  
					write("<tr><th colspan='2' text-align='left'>"); WriteButtons(BNr); write("</th></tr>");
				write("</table>");
			// Game Selection area
			write("<td><div class='GameSelCont' id='IDGameSel-"+ BNr +"' "+s+"></div></td></tr>");
			// PGN area - must be in <div>  container in order to get the overflow right	
			write("<tr><td style='vertical-align:top;'><div class='pgn' id='pgn-"+BNr+"' style='"+FS1+W1+H2+padd+"'></div></td>"); 
		write("</tr></TABLE>");		
	}
}

function WriteBoard(BNr) { 			// writes chess board 
	var siz = Math.round(FontSize*0.8), siz2= Math.round(FontSize*0.5),ii,bw=0; siz3 = Math.round(Scale*Size)-bw;
	var Styl="onMouseDown='RotateBoard("+BNr+",1)' style='font-size:"+siz+"px; vertical-align:middle; text-align:center; border-width:"+bw+"px; border-style:none; ";

	var Styl1=" style='padding:1px; vertical-align:middle; text-align:center; border-width:0px; border-style:solid; border-color:black;' ";
 	var Styl2=" style='height:"+siz+"px; width:"+siz+"px; border-width:0px;border-style:solid; background-color:black;' "; //' ";

	with (document) { 
		write("<Table class='Board' cellpadding=0 cellspacing=0><TR><TH colspan=8 rowspan=8> "); 
			WriteBoardTab(BNr); write("</TH>");
			// write board numbering 8 - 1 
				for (ii=0; ii<8; ii++) write("<TD "+Styl+"height:"+siz3+"px;padding:2px'><div id='no_col_"+BNr+"-"+ii+"'></div></TD></TR><TR>");
				for (ii=0; ii<8; ii++) write("<TD "+Styl+" width:"+siz3+"px;'><div id='no_row_"+BNr+"-"+ii+"'></div></TD>");
				write("<TD onMouseDown='RotateBoard("+BNr+",1)'>");
			write("<div"+Styl2+"></div>");
		write("</TD></TR></Table>");
	}
}

function WriteBoardTab(BNr) {			// writes inner board table (w/o notation)
	var pic = gImagePath+gPics[0],i,j,k;
	var i,j,k, siz = Math.round(Scale*Size);

	with (document) {
		write("<TABLE class='BoardOutline' cellpadding=0 cellspacing=0>"); 
			for (i=0; i<8; i++) { 
				k=8*i; 
				write("<TR>");
				for (j=0;j<8;j++) { 
					if ((i%2+j%2) ==1 ) write("<TD class='WhiteSq'>"); else write("<TD class='BlackSq'>");
					write("<img src='"+pic+"' class='none' id='i"+BNr+"-"+(k+j)+"' width='"+siz+"px' height='"+siz+"px' alt=''>")
					write("</TD>");
				}
				write("</TR>");
			}
		write("</TABLE>")
	}
}

function WriteButtons(BNr) { 			// writes buttons 
	var ss = Math.round(Size*Scale);
	var styl=" style='width:"+ss+"px; font-size:"+FontSize+"px;' ";
	var bIsLive = "live!";
	if (UpdateInterval == 0 ) bIsLive="&gt;I"; 
	with (document) {
		if (! NoButtons) {
			write("<table style='border-style:solid; border-width:0px; width:100%;padding:0px;'><tr><td>");
				write("<TABLE class='ButtonTable' text-align='left' style='border-style:solid; border-width:0px; padding:0px; '><TR>");
					write("<TD><input type=button value='I&lt;'"+styl+                                                 " id='btnInit"+BNr+"' onClick='ButtonAction("+BNr+",0)'></TD>");			
					write("<TD><input type=button value='&lt;' "+styl+                                                 " id='btnMovBk"+BNr+"'  onClick='ButtonAction("+BNr+",1)'></TD>");
					write("<TD><input type=button value='&gt;' "+styl+                                                 " id='btnMovFw"+BNr+"'  onClick='ButtonAction("+BNr+",2)'></TD>");
					write("<TD><input type=button value='"+bIsLive+"' style='width:"+2*ss+"px; font-size:"+FontSize+"px' id='btnLive"+BNr+"' onClick='ButtonAction("+BNr+",3)'></TD>");
				write("</TR>");
			write("</TABLE>");			
			write("</td>");
				write("<TD class='Result' id='Result-"+BNr+"' style='text-align:right; width:"+(2*Size*Scale+Math.round(FontSize/3))+"px; font-size:"+Math.round(1.7*FontSize)+";'></TD>");
			write("</tr></table>");

		}
		write("<input type='hidden' id='isRotated-"+BNr+"' value='0'>");
		write("<input type='hidden' id='Board-"+BNr+"' value=''>");
		write("<input type='hidden' class='HighlightedMove' id='HlightMv"+BNr+"' value='-1'>");		// only to allow css defintion of the highlighted Move
	}
}

function SetLiveButton(BNr,live) {		// Sets the live button, (turns to yellow if live but analzed)
	IsLive[BNr] = live; 
	var lv = "live!"; if ((UpdateInterval==0)||(live==0)) lv = ">I";
	with (document) {
		var obj = getElementById('btnLive'+BNr);
		if (obj) { 
			if ((live==0) && ( UpdateInterval > 0)) obj.style.background = '#FFCF90'
			else obj.style.background=getElementById('btnInit'+BNr).style.background;
			obj.value=lv;

		}
	}
}

function ButtonAction(BNr,no) { 		// function for Button actions such as MoveForward(1), MoveBack(1), ...
	switch (no) {
		case 0		: MoveBack(BNr,MaxMove); 	SetLiveButton(BNr,0); break;
		case 1		: MoveBack(BNr,1); 		SetLiveButton(BNr,0); break;
		case 2		: MoveForward(BNr,1); 		SetLiveButton(BNr,0); break;
		default  	: MoveForward(BNr,MaxMove); 	SetLiveButton(BNr,1); break;
	}
}

function WriteTime(BNr,rot) {			// writes time according to HistTime array
	var w=InitialClock,b=InitialClock;
	var mv = MvCount[BNr]-1; mv_1 = mv - 1;

	if ( HTime[BNr][mv] )   w = HTime[BNr][mv];   else if (mv > 0) w = "&nbsp;";
	if ( HTime[BNr][mv_1] ) b = HTime[BNr][mv_1]; else if (mv > 0) b = "&nbsp;";

	if ((MvCount[BNr]+rot) % 2 == 1 ) { TimeWh=w; TimeBl=b; } else { TimeBl=w; TimeWh=b; }
	// set actual Time if available
	GameNr = GetActiveGame(BNr);
	if ((Tags[GameNr]['ActTime']) && IsLive[BNr]) {
		var t = Tags[GameNr]['ActTime']
		if (mv % 2) { TimeWh = t } else { TimeBl = t; }
	}
	var ID='TimeWhite-'+BNr;
	with (document) {
		if ( getElementById && (getElementById(ID))) {
			if (rot) getElementById(ID).className 	="BW Black Time";		// set classes for player color
			else getElementById(ID).className 	="BW White Time";
			getElementById(ID).innerHTML = TimeWh;
		}
		ID='TimeBlack-'+BNr;
		if ( getElementById && (getElementById(ID))) {
			if (rot) getElementById(ID).className 	="BW White Time";		// set classes for player color
			else getElementById(ID).className 	="BW Black Time";
			getElementById(ID).innerHTML = TimeBl;
		}
	}
}
	
function WritePlayer(ID, Pl, ToolTip, rot) { 		// generates the table for the player "Pl" with "Color" Black/White for.css 
	with (document) { 
		if (getElementById && (getElementById(ID))) {

			if (rot) getElementById(ID).className ="BW Black";		// set classes for player color
			else getElementById(ID).className ="BW White";
		
			getElementById(ID).innerHTML = '<div title="'+ToolTip+'">'+Pl+'</div>'; // set tool tip and player
		}
	}
}

function WriteBoardNumbers(BNr,rot) {		// set Board numbering 1..8 and a..h
	var i,j;
	with (document) {
		if ((! getElementById) || (! getElementById('no_col_'+BNr+'-0'))) return;
		for (i=0; i<8; i++) {
			j=i*(1-rot)+(7-i)*rot;
			getElementById('no_col_'+BNr+'-'+i).innerHTML=8-j;
			getElementById('no_row_'+BNr+'-'+i).innerHTML='abcdefgh'.charAt(j);
		}
	}
}
function GetToolTips(BNr, Player) {
	if (Tags[BNr][Player+"Elo"]) return "Elo:"+Tags[BNr][Player+"Elo"];
	return "Elo:nicht übertragen!"; 
}
function WriteInfos(BNr, rot) {			// sets Players and board numbering a..h and 1..8
	var sel = GetActiveGame(BNr);
	var Pl = new Array("White","Black");
//	var ToolTips= GetToolTips(sel,"White");

	WritePlayer("White-"+BNr, Tags[sel][Pl[rot]],   GetToolTips(sel,"White"), rot ); 
	WritePlayer("Black-"+BNr, Tags[sel][Pl[1-rot]], GetToolTips(sel,"Black"), 1-rot ); 
	WriteTime(BNr,rot);
	WriteBoardNumbers(BNr,rot);
}

function WriteFooter() {			// displays the footer for Author and elapsed time information
	with (document) {
		write("<table width='100%' class='Footer'><tr>");
			write("<td class='download_pgn'><a rel='nofollow' href="+PgnFileName+"?T="+Math.random()+">&nbsp;&nbsp;&nbsp;.pgn&nbsp;&nbsp;&nbsp;</a></td>");
			write("<td id='Author' style='text-align:left; width:auto;'>"+Author+"</td>"); 
			write("<td style='width:auto;'></td>");
			write("<td text-align='right' id='BroadcastTime'  style='text-align:center; width:60px;'></td>");
			write("<td text-align='right' id='ShowUpdateTime' style='text-align:right; width:60px;'>00 sec</td></tr>");
		write("</table>");		
	}
}

function WriteSelGames() {  			// generates pull down menue for the different games in the pgn file
	// first check if more than one game and if there is a tag existing
	if (! document.getElementById) return;
	var padd=Math.round(FontSize/3);
	var Styl1=" style='font-size:"+FontSize+"px; width:100%; padding:"+padd+"px; vertical-align:middle; ' ";

	var NrOfGames = PgnMoveText.length; 
	if ((NrOfGames == 1) || (NoGameSelectForm)) return;
	
	var GameSelForm = "", ii,BNr, GameSelected;

	for (BNr=0; BNr<(Cols*Rows); BNr++) { 
		if (BNr >= NrOfGames) break;
		GameSelected=GetActiveGame(BNr);
		GameSelForm+="<form action=''><SELECT class='GameSel' id='GameSel-"+BNr+"' "+Styl1+"  onChange='OpenGame("+BNr+", this.options[selectedIndex].value);'  SIZE=1>";  //OpenGame("+BNr+", this.options[selectedIndex].value)
		for (ii=0; ii< NrOfGames; ii++) { 
			if (ii == GameSelected) GameSelForm+="<OPTION VALUE="+ii+" selected='selected'>"; else GameSelForm+="<OPTION VALUE="+ii+">";
			GameSelForm+=Tags[ii]["White"].split(",")[0]+"-"+Tags[ii]["Black"].split(",")[0]+"("+Tags[ii]["Result"]+")";
		}
		GameSelForm+="</SELECT></>";
		var len = GameSelForm.length;
		if (len == gLastGameSelCont) { return; }

		// write GameSelect into the <div name="IDGamesSel"> </div> tagged area
		var ID = document.getElementById('IDGameSel-'+BNr);
//		ID.innerHTML = GameSelForm;
		if (ID) { if (ID.innerHTML != GameSelForm) ID.innerHTML = GameSelForm; 
		} // else {  ID.innerHTML = "noGames!"; }
		GameSelForm="";
	}
	gLastGameSelCont = len;
	var W1   = 4*Size*Scale+FontSize;
	var x=document.getElementsByTagName("GameSel");
	for (var i = 0; i < x.length; i++) { 
		if (x[i].className == "GameSel") { x[i].style.width = (W1)+"px"; x[i].style.padding= Math.round(FontSize/3)+"px"}
	}
}

function RestorePos(BNr,ii,bw,Mv,ind) {		// Restores a Position in function MoveBack()
	var x=PosX[BNr][bw][ii], y=PosY[BNr][bw][ii]; 
	var hx0=HPosX[BNr][ind][Mv], hy0=HPosY[BNr][ind][Mv];
	Brd[BNr][x][y]=0;
	Brd[BNr][hx0][hy0]=(HTyp[BNr][ind][Mv]+1)*(1-2*bw);
}

function RestorePiece(BNr,ii,bw,Mv,ind) {	// Restores a Piece in function MoveBack()
	var hx0=HPosX[BNr][ind][Mv], hy0=HPosY[BNr][ind][Mv];
	Piece[BNr][bw][ii]=HTyp[BNr][ind][Mv]; 
	PosX[BNr][bw][ii]=hx0;
	PosY[BNr][bw][ii]=hy0;
	PMovs[BNr][bw][ii]--;
}

function MoveBack(BNr,NrMvs) { 			// move NrMv Halfmoves back
	var ii, jj, Mv,bw,x,y,hx1,hy1;
	isMoving=true;
	for (jj=0; ((jj<NrMvs ) && (MvCount[BNr] > 0)); jj++) { 
		MvCount[BNr]--;	Mv=MvCount[BNr]; MvTyp[BNr]=1-MvTyp[BNr]; bw = MvTyp[BNr];		
		ii=HPiece[BNr][1][Mv];
		if ((0<=ii)&&(ii<16)) { RestorePos(BNr,ii,bw,Mv,1); }		// we must do this here because of Chess960 castling
		// Restore regular move
		ii=HPiece[BNr][0][Mv]; 
		RestorePos(BNr,ii,bw,Mv,0);					// restore index 0
		RestorePiece(BNr,ii,bw,Mv,0);					// restore index 0

		ii=HPiece[BNr][1][Mv];
		if ((0<=ii)&&(ii<16)) { RestorePiece(BNr,ii,bw,Mv,1)	}	// restore additional piece (such as rook in 0-0
		ii-=16;
		if (0<=ii) {							// captured opponents piece 
			RestorePiece(BNr,ii,1-bw,Mv,1)
			hx1=HPosX[BNr][1][Mv]; hy1=HPosY[BNr][1][Mv];
			Brd[BNr][hx1][hy1]=(HTyp[BNr][1][Mv]+1)*(2*bw-1);
		}
	}
	if (isCalculating) { isMoving=false; return; }
	RefreshBoard(BNr);
//	HighlightMove(BNr,MvCount[BNr]);
	isMoving=false;
}

function MoveForward(BNr,nn) { 			// moves nn Halve Moves forward, max until end of moves
	var ii, ffst=0, llst, ssearch, ssub, ffull, mmove0="", mmove1="";
	isMoving=true;
	var GameNr = GetActiveGame(BNr);
	ffull=Uncomment(PgnMoveText[GameNr]);
	for (ii=0; (ii<nn)&&(ffst>=0)&&(MvCount[BNr]<MaxMove); ii++) {
		ssearch=Math.floor(MvCount[BNr]/2+2)+"."; llst=ffull.indexOf(ssearch);
		ssearch=Math.floor(MvCount[BNr]/2+1)+"."; ffst=ffull.indexOf(ssearch);
		if (ffst>=0) { 
			ffst+=ssearch.length;
			if (llst<0) ssub=ffull.substring(ffst);
			else ssub=ffull.substring(ffst, llst);
			mmove0=GetMove(ssub,MvTyp[BNr]);
			if (mmove0!="") { 
				if (ParseMove(BNr, mmove0)>0) { 
					mmove1=mmove0;
					MvCount[BNr]++;
					MvTyp[BNr]=1-MvTyp[BNr];
				} else { 
					if (MvTyp[BNr]==1) { 
						ssub=Math.floor(MvCount[BNr]/2+1);
						ssearch=ssub+"....";
						ffst=ffull.indexOf(ssearch);
						if (ffst<0) { ssearch=ssub+". ..."; ffst=ffull.indexOf(ssearch); }
						if (ffst<0) { ssearch=ssub+". .."; ffst=ffull.indexOf(ssearch); }
						if (ffst<0) { ssearch=ssub+" ..."; ffst=ffull.indexOf(ssearch); }
						if (ffst<0) { ssearch=ssub+"..."; ffst=ffull.indexOf(ssearch); }
						if (ffst<0) { ssearch=ssub+" .."; ffst=ffull.indexOf(ssearch); }
						if (ffst>=0) { 
							ffst+=ssearch.length;
							if (llst<0) ssub=ffull.substring(ffst);
							else ssub=ffull.substring(ffst, llst);
							mmove0=GetMove(ssub,0);
							if (mmove0!="") { 
								if (ParseMove(BNr, mmove0)>0) { 
									mmove1=mmove0;
									MvCount[BNr]++;
									MvTyp[BNr]=1-MvTyp[BNr];
								} else ffst=-1;
							}
						}
					} else ffst=-1;
				}
			} else ffst=-1;
		}
	}
	
	if (isCalculating) { isMoving=false; return; }
	RefreshBoard(BNr);
//	HighlightMove(BNr, MvCount[BNr]);
	isMoving=false;
}

function SetImg(BNr, ii,src) { 			// copies images located in 'img' to document.images[ii], takes care of IE5.5/6.0 issues
	var i = 'i'+BNr+'-'+ii;
	with (document) {
		if (getElementById(i).src == src) { 
			if ((isIE > 0) && (isIE < 7.0)) 
				getElementById(i).style.filter = ""; 
				return; 
		}
		if (! getElementById(i)) return;
		if (isIE==0) { getElementById(i).src = src; return };
		// use AlphaImageLoader filter for IE 6.0 and IE5.5,as it cannot handle pgn transparency !
		getElementById(i).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='scale')";
		getElementById(i).src = gImagePath+gPics[0];
	}
}

function RefreshBoard(BNr) { 			// updates graphics from array
	var ii=0, jj=0, kk,ll, mm, isRotated = GetIsRotated(BNr);	
	WriteInfos(BNr, isRotated); 		// write all infos such as player, time and board annotation
	for (ii=0; ii<8; ii++) { 		// Update all fields according to the Brd variable.
		for (jj=0; jj<8; jj++) { 
			if (isRotated) kk= 63-ii-(7-jj)*8; else kk = ii+(7-jj)*8 ;
			ll=Brd[BNr][ii][jj]; 
			if (ll < 0) mm='b'; else { if (ll == 0 ) mm = ''; else mm='w'; };
			SetImg(BNr,kk, gImagePath+mm+gPics[Math.abs(ll)]);
		}
	}
	if (IsLive[BNr]==1) {					// 15.8.11 this replaces old ScrollIntoView
		var sdiv = document.getElementById('pgn-'+BNr);	// 	this replaces old ScrollIntoView
		sdiv.scrollTop = sdiv.scrollHeight;		// 	this replaces old ScrollIntoView
	}
	HighlightMove(BNr,MvCount[BNr]);
}

function InitBoard(BNr) { 			// initializes the board variables
	var cc, ii, jj;

	for (ii=0; ii<2; ii++) { 
		Piece[BNr][ii]	= Array(0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5)
		PosX[BNr][ii] 	= Array(4, 3, 0, 7, 2, 5, 1, 6, 0, 1, 2, 3, 4, 5, 6, 7);
		PMovs[BNr][ii]	= Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
	}
	PosY[BNr][0]		= Array(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
	PosY[BNr][1]		= Array(7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6);
	
	for (ii=0; ii<8; ii++) { for (jj=0; jj<8; jj++)  Brd[BNr][ii][jj]=0; }
	for (ii=0; ii<2; ii++) { for (jj=0; jj<16; jj++) Brd[BNr][PosX[BNr][ii][jj]][PosY[BNr][ii][jj]]=(Piece[BNr][ii][jj]+1)*(1-2*ii); }
	MvCount[BNr]=0;
	MvTyp[BNr]=0;
}

function Init(BNr) {				// initializes board variables, refreshes board and highlights move
	isInit=true;
	InitBoard(BNr);
	RefreshBoard(BNr);
//	while (HTime[BNr].length > 0 ) HTime[BNr].pop();
	isInit=false;

}

function Uncomment(ss) { 			// strips a pgn game string off the comments {} and $
	if (! ss) return("");
	var ii, jj, llist=ss.split("{"), ll=llist.length, uu=llist[0], lla, llalen, kk, level=0;
	// first extract all {} comments
	for (ii=1; ii<ll; ii++) { 
		level++;
		lla=llist[ii].split("}");
		lla_len = lla.length -1;
		level = level-lla_len;
		if ((level == 0) && ( lla_len > 0 )) uu+=lla[lla_len]; 
	}
//	if (level != 0) DispError('Unbalanced brackets \{\}:'+level);
	// then extract all line comments beginning with $
	llist=uu.split("$");
	ll=llist.length;
	uu=llist[0];
	for (ii=1; ii<ll; ii++) { 
		tt=llist[ii];
		kk=tt.length;
		for (jj=0; jj<kk; jj++) { 
			if (isNaN(parseInt(tt.charAt(jj)))) { 
				uu+=tt.substring(jj+1);
				jj=kk;
			}
		}
	}
	return(uu);
}

function HTML2Text(txt) {			// Strips all specific html tags off a text string
	txt=txt.replace(/\<html\>/g,'');
	txt=txt.replace(/\<\/html\>/g,'');
	txt=txt.replace(/\<head\>/g,'');
	txt=txt.replace(/\<\/head\>/g,'');
	txt=txt.replace(/\<body\>/g,'');
	txt=txt.replace(/\<\/body\>/g,'');
	txt=txt.replace(/\<pre\>/g,'');
	txt=txt.replace(/\<\/pre\>/g,'');
	txt=txt.replace(/\<xmp\>/g,'');
	txt=txt.replace(/\<br \/\>/g,'');   // <br />
	txt=txt.replace(/\<br\>/g,'');   // <br />
	txt=txt.replace(/\<\/xmp\>/g,'');
	txt=txt.replace(/&quot;/g,'"');
	txt=txt.replace(/&lt;/g,'<');
	txt=txt.replace(/&gt;/g,'>');
	txt=" "+txt;
	return(txt);
}

function ApplySAN(ss) { 			// applies SAN from possible SAN tags in the pgn file
	if (ss.length != 6) return;
	for (var ii=0; ii<6; ii++) PieceCode[ii]=ss.charCodeAt(ii);
}

function GetMove(tt,nn) { 	 		// Cleans move 
	var ii=0, jj=0, mm="", ll=-1, cc, ss=tt;
	while (ss.indexOf("<br />") > 0) ss=ss.replace("<br />","");
	var len=ss.length;
	while ( ii < len) { 
		cc=ss.charCodeAt(ii);
		if ((cc<=32)) { //||(cc==46)) //||(cc>=127)) 
			if (ll+1!=ii) jj++;
			ll=ii;
		} else { 
			if (jj==nn) { 
				if ((cc==46)&&(!isNaN(mm))) { mm=""; ll=ii; }
				else mm+=ss.charAt(ii);
			}
		}
		ii++;
	}
	if ((nn==1)&&(mm=="")&&(ss.charAt(0)==".")) { 
		ii=0;
		while (ii<len) { 
			cc=ss.charAt(ii);
			if ((cc!=".")&&(cc!=" ")) mm+=cc;
			ii++;
		}
	} 
	if (mm!="") {
		ii=mm.indexOf("<");
		jj=mm.indexOf(">");
		ll=0;
		while ((ii>=0)&&(jj>=0)&&(ii<jj)) { 
			mm=mm.substr(0,ii)+mm.substr(jj+1);
			ii=mm.indexOf("<");
			jj=mm.indexOf(">");
		}
	}
	return(mm);
}

function CanCastleLong(BNr) { 			// check if O-O-O is allowed
	var bw = MvTyp[BNr];
	if (PMovs[BNr][bw][0]>0) return(-1);
	var jj=0;
	while (jj<16) { 
		if ((PosX[BNr][bw][jj]<PosX[BNr][bw][0])&& (PosY[BNr][bw][jj]==bw*7)&& (Piece[BNr][bw][jj]==2)&& (PMovs[BNr][bw][jj]==0)) jj+=100;
		else jj++;
	}
	if (jj==16) return(-1);
	jj-=100;
	Brd[BNr][PosX[BNr][bw][0]][bw*7]=0;
	Brd[BNr][PosX[BNr][bw][jj]][bw*7]=0;
	var ff=PosX[BNr][bw][jj];
	if (ff>2) ff=2;
	while ((ff<PosX[BNr][bw][0])||(ff<=3)) { 
		if (Brd[BNr][ff][bw*7]!=0) { 
			Brd[BNr][PosX[BNr][bw][0]][bw*7]=1-2*bw;
			Brd[BNr][PosX[BNr][bw][jj]][bw*7]=(1-2*bw)*3;
			return(-1);
		}
		ff++;
	}
	Brd[BNr][PosX[BNr][bw][0]][bw*7]=1-2*bw;
	Brd[BNr][PosX[BNr][bw][jj]][bw*7]=(1-2*bw)*3;  
	return(jj);
}

function CanCastleShort(BNr){			// checks is O-O is allowed
	var bw = MvTyp[BNr];
	if (PMovs[BNr][bw][0]>0) return(-1);
	var jj=0;
	while (jj<16) { 
		if ((PosX[BNr][bw][jj]>PosX[BNr][bw][0])&& (PosY[BNr][bw][jj]==bw*7)&& (Piece[BNr][bw][jj]==2)&& (PMovs[BNr][bw][jj]==0)) jj+=100;
		else jj++;
	}
	if (jj==16) return(-1);
	jj-=100;
	Brd[BNr][PosX[BNr][bw][0]][bw*7]=0;
	Brd[BNr][PosX[BNr][bw][jj]][bw*7]=0;
	var ff=PosX[BNr][bw][jj];
	if (ff<6) ff=6;
	while ((ff>PosX[BNr][bw][0])||(ff>=5)) { 
		if (Brd[BNr][ff][bw*7]!=0) {
			Brd[BNr][PosX[BNr][bw][0]][bw*7]=1-2*bw;
			Brd[BNr][PosX[BNr][bw][jj]][bw*7]=(1-2*bw)*3;
			return(-1);
		}
		ff--;
	}
	Brd[BNr][PosX[BNr][bw][0]][bw*7]=1-2*bw;
	Brd[BNr][PosX[BNr][bw][jj]][bw*7]=(1-2*bw)*3;
	return(jj); 
}

function IsInComment(ss, nn) { 			// checks if string is in comment
	var ii=-1, bb=0;
	do { ii=ss.indexOf("{",ii+1); bb++; } while ((ii>=0)&&(ii<nn));
	ii=-1;
	do { ii=ss.indexOf("}",ii+1); bb--; } while ((ii>=0)&&(ii<nn));  
  return(bb);
}

function TransformSAN(ss) {			// translates  shown piece names from input piece names (ie. Rxe3 => Txe3)
	if (ss=="") return("");
	if ((ShowPieceName=="")||(ShowPieceName==PieceName)) return(ss);
	var jj, rr, tt="";
	for (jj=0; jj<ss.length; jj++) { 
		rr=PieceName.indexOf(ss.charAt(jj));
		if (rr>=0) tt+=ShowPieceName.charAt(rr);
		else tt+=ss.charAt(jj);
	}
	return(tt);
}

function SetMove(BNr, mmove) {			// sets moves position to a given move either forward or back
	SetLiveButton(BNr,0);
	if (isNaN(mmove)) return;
	var dd=mmove-MvCount[BNr];	
	if (dd<=0) MoveBack(BNr, -dd); else MoveForward(BNr, dd);
}

function GetTimeInPGN(BNr, ss,Mv)  {		// analyzes comments and extracts time tag if n:nn:nn format 
	if (!ss) return(ss);
	var i,j, Comm;
//	if (! HTime[BNr][Mv] ) HTime[BNr][Mv] = "&nbsp;"; // presume no time tag existing for this move
	j=ss.indexOf("*");  			//consider only comments up to the next move
	if (j > 0) Comm = ss.substring(0,j); else Comm=ss;
	j = Comm.indexOf("."); if (j>0) Comm=Comm.substring(0,j);
	i = Comm.indexOf("{"); if (i < 0) return(ss);
	j = Comm.indexOf("}"); if (j < 0) return(ss);

	Comm = Comm.substring(i+1,j);
	if (Comm.match(/[0-9]:[0-9][0-9]:[0-9][0-9]/) ) { 
			if (Mv == 'ActTime') { 
				Tags[GetActiveGame(BNr)]['ActTime']=Comm;
				return;
			} else if (Mv < 0 ) return;
			HTime[BNr][Mv] = Comm;
			if (ShowTimeInPGN) return(ss.substring(0,i)+"<span id='pgn_t'> "+Comm+" </span>"+ss.substring(j+1));
			else return(ss.substring(0,i)+ss.substring(j+1));
	} else return("<span id='pgn_t'>"+ss+"</span>");
} 

function GetGameResult(BNr,ss,Mv) {			// analyzes the result tag in the pgn file
	if (!ss) return(ss);
	var t = new Array(2);
	t=ss.split("1/2-1/2"); 	if (t.length > 1) { Tags[BNr]["Result"]="1/2-1/2"; return(t[0]+"<div class='pgn_result'> 1/2 - 1/2 </div>"); }
	t=ss.split("1-0"); 	if (t.length > 1) { Tags[BNr]["Result"]="1-0"; return(t[0]+"<div class='pgn_result'> 1 - 0 </div>"); }
	t=ss.split("0-1"); 	if (t.length > 1) { Tags[BNr]["Result"]="0-1"; return(t[0]+"<div class='pgn_result'> 0 - 1 </div>"); }
	t=ss.split("*"); 	if (t.length > 1) { 
		if (t[1].indexOf(":") > 0) { GetTimeInPGN(BNr,t[1],'ActTime'); }
		return(t[0]+"<div id='pgn_a'> ...</div>");
	}
	return("{Parsing Error when trying to determine the game result}"); // ss);
}

function HighlightMove(BNr,Mv) {		// highlights last move 
	var obj, bg, last_mv, anch;;
	if (! document.getElementById) return;
	obj = document.getElementById('HlightMv'+BNr);
	if (obj) bg = obj.style.background; bg = 'silver';  // seems to be buggy !	
	
	if (obj) {
		last_mv=obj.value;
		obj.value=Mv;
	} else last_mv=""; 
	anch='m-'+BNr+'-'+last_mv; 

	with (document) {
		if (( last_mv >= 0) && (getElementById(anch)) ) {
			obj=getElementById(anch); 
			obj.style.background="";
			obj.style.borderWidth='0px';
			obj.style.borderStyle='none';
		}
		anch='m-'+BNr+'-'+Mv;
		if ( getElementById(anch) ) {
			obj=getElementById(anch); 
			obj.style.background=bg;
			obj.style.borderWidth='1px';
			obj.style.borderStyle='none';

		}

	}
}

function GetHTMLMoveText(BNr) {			// extracts moves out of pgn and cnoverts pgn to HTML format including links 
	var MvTxt, GameNr, Mv, jj, uu="", uuu="", cc, vvariant=0; bb=0, bbb=0, ccommenttype=true;
	var ss="", sstart=0, ffst=0,llst,ssearch,ssub,ffull,mmove0="",mmove1="",MvCnt=MvCount[BNr];
	isCalculating=true;
	Init(BNr); 
	GameNr = GetActiveGame(BNr);
	MvTxt=PgnMoveText[GameNr];
	ffull=Uncomment(MvTxt);						// Strip all comments off 
	for (var ii=0; (ii<MaxMove)&&(ffst>=0)&&(MvCount[BNr]<MaxMove); ii++) { 
		ssearch=Math.floor(MvCount[BNr]/2+2)+"."; llst=ffull.indexOf(ssearch);
		ssearch=Math.floor(MvCount[BNr]/2+1)+"."; ffst=ffull.indexOf(ssearch);
		mmove1=""
		if (ffst>=0) { 						// if there is a low move count
			ffst+=ssearch.length;
			if (llst<0) ssub=ffull.substring(ffst);	else ssub=ffull.substring(ffst, llst); // cut the move out 
			mmove0=GetMove(ssub,MvTyp[BNr]);			// get Move Clean and only black or white 
			if ((mmove0!="") && (ParseMove(BNr, mmove0)>0)) { 		// Build HistMove Array
				mmove1=mmove0;
				MvCount[BNr]++;
				MvTyp[BNr]=1-MvTyp[BNr];
			} else ffst=-1;
		}	// end if ffst>=0 
		if (mmove1!="") { 
			sstart=-1;
			do sstart=MvTxt.indexOf(mmove1, sstart+1); while ((sstart>0)&&(IsInComment(MvTxt, sstart)));
			if (sstart>=0) { Mv=MvCount[BNr]
				ss+=GetTimeInPGN(BNr, MvTxt.substr(0,sstart),(Mv-2));
				ss+="<a href='javascript:SetMove("+BNr+","+Mv+")' id='m-"+BNr+"-"+Mv+"'>";
				ss+="<b>"+TransformSAN(mmove1)+"</b></a>";
				if ( (MvTyp[BNr]==0) && (OneMovePerLine)) { ss+="<br>"; };  // insert new line
				MvTxt=MvTxt.substr(sstart+mmove1.length);
			} else ffst=-1;
		} else { 
			MvTxt=GetTimeInPGN(BNr, MvTxt,(MvCount[BNr]-1)); 
			MvTxt=GetGameResult(BNr,MvTxt,(MvCount[BNr]-1)); 
		}
	}
	ss+=MvTxt;
	uu = MarkAllComments(ss);			// set all comments to "grey"
	LastMove[BNr]=MvCount[BNr];			// store maximum move for usage in MoveForward
	isCalculating=false;
	// if islive then go to the end of the game
	if (IsLive[BNr]==1) MoveForward(BNr, MaxMove); else SetMove(BNr, MvCnt);
	return(uu);
}

function MarkAllComments(ss) {			// sets all all comments to <i> ..</i>
	var MvTxt=ss.split("{"), ll=MvTxt.length, uu, ii;
	uu=MvTxt[0];
	for (ii=1; ii<ll; ii++) uu+="<i>"+MvTxt[ii];
	MvTxt=uu.split("}"); ll=MvTxt.length;	uu=MvTxt[0];
	for (ii=1; ii<ll; ii++) uu+="</i>"+MvTxt[ii];
	return(uu);
}

function Check4Castling(BNr, Mv, x1, y1) {	// used in ParseMove to check  if move has been O-O, or O-O-O
	if ((x1>=0)&&(x1<=7)&&(y1>=0)&&(y1<=7)) return(-1);
	// if no valid number check for castling
	if ((Mv.indexOf("O")>=0)||(Mv.indexOf("0")>=0)) {
		if ((Mv.indexOf("O-O-O")>=0)||(Mv.indexOf("0-0-0")>=0)||(Mv.indexOf("O-O-O")>=0)||(Mv.indexOf("0-0-0")>=0)) { 
			if (EvalMove(BNr, -1 , 6, -1, -1, -1, x1, y1, 0, -1)) return(1);
			return(0);
		}
		if ((Mv.indexOf("O-O")>=0)||(Mv.indexOf("0-0")>=0)||(Mv.indexOf("O?O")>=0)||(Mv.indexOf("0?0")>=0)) { 
			if (EvalMove(BNr, -1 , 7, -1, -1, -1, x1, y1, 0, -1)) return(1);
			return(0);
		}
		return(0);
	}
	if ((Mv.indexOf("---")>=0)||(Mv.indexOf("???")>=0)) {	//  Null-Move
		//if (Mv.indexOf("...")>=0) //is buggy
		if (EvalMove(BNr, -1 , 8, -1, -1, -1, x1, y1, 0, -1)) return(1);
		return(0);
	}
	return(0);
}

function ParseMove(BNr, Mv) {			// parses a given move and extracts fields
	var ii, jj, ffrom="", ccapt=0, ll, Fig=Pawn, x0=-1, y0=-1, Fig2=-1, x1=-1, y1=-1;
	var rMv=MvCount[BNr]-1, bw = MvTyp[BNr], CanPass=-1;

	// find last valid field target field such as d4
	ii=Mv.length-1;
	while (ii>0) { if (! isNaN(Mv.charAt(ii))) { x1=Mv.charCodeAt(ii-1)-97; y1=Mv.charAt(ii)-1;  ffrom=Mv.substring(0, ii-1); jj=ii; break; }
		ii--; 
	} // scan from the end until a number occurs
	

	// check for Castling and NullMoves
	ii = Check4Castling(BNr,Mv,x1,y1); if (ii >= 0) return(ii);  
	ll=ffrom.length-1; 

 	// find piece (KQRBNP) at Position 0, Fig is initialized with Pawn
	if (ll>=0) { for (ii=0; ii<5; ii++) { if (ffrom.charCodeAt(0)==PieceCode[ii]) { Fig=ii; ffrom=ffrom.slice(1); ll--; } } }

	// check for capturing, last part for Smith Notation
	if (ll>=0) { if (ffrom.charAt(ll)=="x") { ccapt=1; ll--; } else { if ((ffrom.charAt(ll)=="-")||(ffrom.charAt(ll)=="?")) ll--;} } 

	// get source field y such as R3xe8
	if (ll>=0) { if (! isNaN(ffrom.charAt(ll))) { y0=ffrom.charAt(ll)-1;      if ((y0<0)||(y0>7)) y0=-1; ll--; } }

	// get source field x such as Rexe8
	if (ll>=0) { if (isNaN(ffrom.charAt(ll)))   { x0=ffrom.charCodeAt(ll)-97; if ((x0<0)||(x0>7)) x0=-1; ll--; } }

	// check if pieces are captured, take care for En-Passent
	if (rMv >= 0 ) {
		ii=HPiece[BNr][0][rMv]; 
		if ((HTyp[BNr][0][rMv]==Pawn)&&(Math.abs(HPosY[BNr][0][rMv]-PosY[BNr][1-bw][ii])==2)) CanPass=PosX[BNr][1-bw][ii];
	}
	if (Brd[BNr][x1][y1] != 0) ccapt=1; else { if ((Fig==5)&&(x1==CanPass)&&(y1==5-3*bw)) ccapt=1; }

	// check for promotion such a e8=Q or e8Qc, accept only valid piece codes
	Fig2=Fig; ii=Mv.indexOf("="); if (ii>0) jj=ii; 
	if ((jj>0)&& (jj < Mv.length-1))  { if (Fig==Pawn) { ii=Mv.charCodeAt(jj+1); for (ll=1;ll<5;ll++) { if (ii==PieceCode[ll]) Fig2=ll; } } }

	// Now check Moves for all 16 pieces, if there is a match 
	for (ii=0; ii<16; ii++) { if (Piece[BNr][bw][ii]==Fig) { if (EvalMove(BNr, ii, Fig, x0, y0, Fig2, x1, y1, ccapt, CanPass)) return(1); } }

	return(0);
}

function EvalMove(BNr, ii, Fig, x0, y0, Fig2, x1, y1, ccapt, CanPass) { // evaluates parsed move, checks for validity 
	var dx, dy, xx, yy, jj=-1, Fig_prom=-1;
	var bw = MvTyp[BNr], Xii=PosX[BNr][bw][ii], Yii=PosY[BNr][bw][ii];

	var BRD=Brd[BNr], X=PosX[BNr][bw], Y=PosY[BNr][bw];

	//O-O-O with Chess960 rules
	if (Fig==OOO) { jj=CanCastleLong(BNr);  if (jj<0) return(false); if (StoreMove(BNr, 0, 0, 2, bw*7, jj, 2, 3, bw*7)) return(true); return(false); }
	//O-O with Chess960 rules
	if (Fig==OO)  { jj=CanCastleShort(BNr); if (jj<0) return(false); if (StoreMove(BNr, 0, 0, 6, bw*7, jj, 2, 5, bw*7)) return(true); return(false); }
	// --- NullMove
	if (Fig==NullM) { 						 if (StoreMove(BNr, 0, 0,PosX[BNr][bw][0], PosY[BNr][bw][0], -1, -1, -1, -1)) return(true); return(false); }
	
	if ((Xii==x1)&&(Yii==y1)) return(false);  	// No move at all	

	// Check if captured pieces from opponent's color, and check for EnPassent if not ?
	if ((ccapt>0)&&(sign(BRD[x1][y1])!=(2*bw-1))) { if ((Fig!=Pawn)||(CanPass!=x1)||(y1!=5-3*bw)) return(false); }

	// wrong starting field X or Y
	if ( ((x0>=0)&&(x0!=Xii)) || ((y0>=0)&&(y0!=Yii)) ) return(false);	
	// Valid Move for King ?
	if (Fig==King)   { if ( (Math.abs(Xii-x1)>1) || (Math.abs(Yii-y1)>1) ) return(false); }
	// Valid Move for Queen?
	if (Fig==Queen)  { if ( ((Math.abs(Xii-x1)!= Math.abs(Yii-y1))) && ((Xii-x1)*(Yii-y1)!=0)) return(false); }
	// Valid Move for Rook?
	if (Fig==Rook)   { if ((Xii-x1)*(Yii-y1)!=0) 			return(false); }
	// Valid Move for Bishop?
	if (Fig==Bishop) { if (Math.abs(Xii-x1)!=Math.abs(Yii-y1)) 		return(false); }
	// Valid Move for Bishop?
	if (Fig==Night)  { if (Math.abs(Xii-x1)*Math.abs(Yii-y1)!=2) 	return(false); }

	// There must not be any other piecee between start- and end position 
	if ((Fig==Queen)||(Fig==Rook)||(Fig==Bishop)) { dx=sign(x1-Xii); dy=sign(y1-Yii); xx=Xii+dx; yy=Yii+dy;
		while ((xx!=x1)||(yy!=y1)) { if (BRD[xx][yy]!=0) return(false);  xx+=dx; yy+=dy; }
	}
	// Valid Move for Pawn?
	if (Fig==Pawn) {
		if ( (Math.abs(Xii-x1) != ccapt) || ((y1==7*(1-bw))&&(Fig==Fig2)) ) return(false); // X=0 or +-1 if captured  
		if (ccapt==0) {	if (Yii-y1==4*bw-2) {   			// is it a two step pawn move 
				if (Yii!=1+5*bw) return(false); 		// must come from initial position 
				if (BRD[x1][y1+2*bw-1]!=0) return(false); 	// field in between is not empty
			} else {if (Yii-y1!=2*bw-1) 	return(false); }  	// must move exactly one field
		} else { if (Yii-y1!=2*bw-1) return(false); }			// for captured exactly one step forward
	}
	// Check for valid Promotion
	if (Fig2!=Fig)   { if ((Fig!=Pawn) || (Fig2>=Pawn) || (y1!=7-7*bw))  return(false); } 
	// get opponents captured piece (only index is relevant)
	if ((Fig<=Pawn)&&(ccapt>0)) { 
		jj=15; 
		var wb = 1-bw;
		while (jj>=0) { if ((Piece[BNr][wb][jj]>0)&& (PosX[BNr][wb][jj]==x1)&& (PosY[BNr][wb][jj]==y1)) { Fig_prom=Piece[BNr][wb][jj]; break; }
			else jj--;
		}
		// if no piece then it must be en-passent
		if ((Fig_prom==-1)&&(Fig==Pawn)&&(CanPass>=0)) { 
			jj=15;
			while (jj>=0) { 
				if ((Piece[BNr][wb][jj]==5)&&(PosX[BNr][wb][jj]==x1)&&(PosY[BNr][wb][jj]==y1-1+2*bw)) { Fig_prom=Piece[BNr][wb][jj]; break; }
				else jj--;
			}
		}
		Fig_prom=-1;
	}  

	// now move is correct, so store it 
	if ( StoreMove(BNr, ii, Fig2, x1, y1, jj, Fig_prom, -1, -1) ) return(true);
  	return(false);
}

function Save2Hist(BNr,Mv,bw,ind,ii,Adder) {	// save Piece and Position to MoveHistory, used in StoreMove
       HPiece[BNr][ind][Mv] 	= ii+Adder; 
	 HTyp[BNr][ind][Mv] 	= Piece[BNr][bw][ii]; 
	HPosX[BNr][ind][Mv] 	= PosX[BNr][bw][ii]; 
	HPosY[BNr][ind][Mv] 	= PosY[BNr][bw][ii];
}

function SetField2Val(BNr,bw,ind,val) { 	// sets Piece(val) on field(ind)
	Brd[BNr][PosX[BNr][bw][ind]][PosY[BNr][bw][ind]]=val; 
}

function StoreMove(BNr, ii, Fig, x2, y2, jj, Fig2, x3, y3) { // stor2es move and sets the board position
	var iis_check=0, ll, Mv=MvCount[BNr], dd=0;
	var bw = MvTyp[BNr], wb=1-bw;

	// save Primary history 
	HPiece[BNr][1][Mv] = -1; 	// preset ext. moves to null
	Save2Hist(BNr,Mv,bw,0,ii,0); 
	SetField2Val(BNr,bw,ii,0);
	// increase #of Moves for each piece (can later on check for illegal castling 
	if ((PosX[BNr][bw][ii]!=x2)||(PosY[BNr][bw][ii]!=y2)||(jj>=0)) { PMovs[BNr][bw][ii]++; dd++;  } //not a nullmove

	// set additional history whenever 2 pieces are involved such as 0-0, or exd4 
	if (jj>=0) { 
		if (Fig2 < 0 ) {
			Save2Hist(BNr,Mv,1-bw,1,jj,16);
			SetField2Val(BNr,1-bw,jj,0)
			Piece[BNr][1-bw][jj]=Fig2;  
			PMovs[BNr][1-bw][jj]++;
			SetField2Val(BNr,1-bw,jj,0);
		} else {
			Save2Hist(BNr,Mv,bw,1,jj,0);
			SetField2Val(BNr,bw,jj,0)
			PosX[BNr][bw][jj]=x3; 
			PosY[BNr][bw][jj]=y3; 
			PMovs[BNr][bw][jj]++;
			SetField2Val( BNr, bw, jj, (Piece[BNr][bw][jj]+1)*(1-2*bw) );
		}
	}
	// set new Piece and position
	Piece[BNr][bw][ii]=Fig;
	PosX[BNr][bw][ii]=x2;
	PosY[BNr][bw][ii]=y2;
	SetField2Val(BNr,bw,ii,(Fig+1)*(1-2*bw));

	//O-O-O, O-O 
	if ((Fig==King)&&(Fig2==Rook)) { 
		while (PosX[BNr][bw][King]>x2) { 
			iis_check+=IsCheck(BNr, PosX[BNr][bw][King], bw*7, bw);
			PosX[BNr][bw][King]--;
		}
		while (PosX[BNr][bw][King]<x2)  { 
			iis_check+=IsCheck(BNr, PosX[BNr][bw][King], bw*7, bw);
			PosX[BNr][bw][King]++;
		} 
	}
	iis_check+=IsCheck(BNr, PosX[BNr][bw][King], PosY[BNr][bw][0], bw);
	if (iis_check==0) return(true);

	// reverse all doings if move is not allowed -- need to be reviewed !!!!!!!!!!!!!!!!
	SetField2Val(BNr,bw,ii,0);
	Brd[BNr][HPosX[BNr][0][Mv]][HPosY[BNr][0][Mv]]=(HTyp[BNr][0][Mv]+1)*(1-2*bw);
	Piece[BNr][bw][ii]=HTyp[BNr][0][Mv];
	PosX[BNr][bw][ii]=HPosX[BNr][0][Mv];
	PosY[BNr][bw][ii]=HPosY[BNr][0][Mv];
	PMovs[BNr][bw][ii]-=dd;
	if (jj>=0) { 
		if (Fig2>=0) { 
			Brd[BNr][PosX[BNr][bw][jj]][PosY[BNr][bw][jj]]=0;
			Brd[BNr][HPosX[BNr][0][Mv]][HPosY[BNr][0][Mv]]=(HTyp[BNr][0][Mv]+1)*(1-2*bw);
			Brd[BNr][HPosX[BNr][1][Mv]][HPosY[BNr][1][Mv]]=(HTyp[BNr][1][Mv]+1)*(1-2*bw);
			Piece[BNr][bw][jj]=HTyp[BNr][1][Mv];
			PosX[BNr][bw][jj]=HPosX[BNr][1][Mv];
			PosY[BNr][jj]=HPosY[BNr][1][Mv];
			PMovs[BNr][bw][jj]--;
		} else { 
			Brd[BNr][HPosX[BNr][1][Mv]][HPosY[BNr][1][Mv]]=(HTyp[BNr][1][Mv]+1)*(2*bw-1);
			Piece[BNr][wb][jj]=HTyp[BNr][1][Mv];
			PosX[BNr][wb][jj]=HPosX[BNr][1][Mv];
			PosY[BNr][wb][jj]=HPosY[BNr][1][Mv];
			PMovs[BNr][wb][jj]--;
		}
	}
	if (iis_check==0) return(true);
	return(false);
}

function IsCheck(BNr, xx, yy, bw) { 		// checks if king is in check by any of the opponent's pieces
	var x0=xx, y0=yy, dx, dy, bb;
	// Night check
	for (dx=-2; dx<=2; dx+=4) { for (dy=-1; dy<=1; dy+=2) {  if (IsOnBoard(x0+dx, y0+dy)) { if (Brd[BNr][x0+dx][y0+dy]==10*bw-(Night+1)) return(1); } } }
	for (dx=-1; dx<=1; dx+=2) { for (dy=-2; dy<=2; dy+=4) {  if (IsOnBoard(x0+dx, y0+dy)) { if (Brd[BNr][x0+dx][y0+dy]==10*bw-(Night+1)) return(1); } } }
	// Pawn Check
	dy=1-2*bw; 		    for (dx=-1; dx<=1; dx+=2) {  if (IsOnBoard(x0+dx, y0+dy)) { if (Brd[BNr][x0+dx][y0+dy]==12*bw-(Pawn+1)) return(1);  } }
	// not next to opponents king
	if ((Math.abs(PosX[BNr][1-bw][King]-xx)<2)&&(Math.abs(PosY[BNr][1-bw][King]-yy)<2)) return(1);
	// check for Queen, Rook, Bishop
	for (dx=-1; dx<=1; dx++) { 
		for (dy=-1; dy<=1; dy++) { 
			if ((dx!=0)||(dy!=0)) { x0=xx+dx; y0=yy+dy; bb=0;
				while ((IsOnBoard(x0, y0))&&(bb==0)) { 
					bb=Brd[BNr][x0][y0];
					if (bb==0) { x0+=dx; y0+=dy; }
					else { 	if ( bb==4*bw-(Queen+1)) 			 
							return(1); 
						if ((bb==6*bw-(Rook+1))&&((dx==0)||(dy==0))) 
							return(1); 
						if ((bb==8*bw-(Bishop+1))&&(dx!=0)&&(dy!=0)) 
							return(1); 
					}
				}
			}
		}
	}
	return(0);
}

function IsOnBoard(ii, jj) { 			// checks if field is on board (0-7)
	if ( (ii<0) || (ii>7) || (jj<0) || (jj>7)) return(false);
	return(true);
}

function OpenGames() {				// opens all Games after file update
	for (var BNr = 0; BNr < (Rows*Cols); BNr++) {
		var sel = GetActiveGame(BNr);
		if ( BNr >= PgnMoveText.length) break; Tags[BNr]=Tags[sel]; // assign Tags to the Board;
		OpenGame(BNr,sel);
	}
	WriteCookie();
}

function OpenGame(BNr,GameNr) { 		// Opens active game after Viewer Update
	// check if valid game and already fully loaded
	if (isMoving) { setTimeout('OpenGame('+BNr+','+GameNr+')',TimeOut); return; }
	if (GameNr < 0) return; 

	if (Tags[GameNr]["SAN"]) ApplySAN(Tags[GameNr]["SAN"]);		// change nameing of the pieces if there is such a string in the pgn file

	WriteCookie();

	with (document) { 
		if (getElementById) {
			 getElementById('pgn-'+BNr).innerHTML=GetHTMLMoveText(BNr); //pgn with html links;
			 if ( Tags[GameNr]["Result"] !="*")
				getElementById('Result-'+BNr).innerHTML = Tags[GameNr]["Result"].substr(0,3);
			 else 
				getElementById('Result-'+BNr).innerHTML = "...";
	  	}
	}
	RefreshBoard(BNr)
}

function UpdateViewer() {			// Handles Viewer Update after new pgn file is read - called from AnalyzePgn
	SkipRefresh=1; 				// no refresh during update
	WriteSelGames(); 			// write all games for all boards into the <Select> Form 
	OpenGames();
	SkipRefresh=0;	

}
function ModifyTOMA(pgn) {
	// strip carriage return
	pgn = pgn.replace (/[\n\r]/g, ' ');

	// Modify DGT-time format { [%clk ??:??.??] } to iChess {00:00:00}
	pgn=pgn.replace (/\{ *\[%clk *(\d+):(\d+)/g,'\{$1:$2'); // strip off [%clk
	pgn=pgn.replace (/(\d+) *\] *\}/g,'$1\}'); 		// strip off trailing ]
	pgn=pgn.replace (/\{(\d+):(\d+)\}/g,'\{$1:$2:00\}');	// unify to 00:00:00 when only minutes are given
	pgn=pgn.replace (/\{(\d+):(\d+)\.(\d+)\}/g,'\{$1:$2:$3\}'); // unify second delimiter from '.' to ':'
	
    return(pgn);
}

function AnalyzePgn() { 			// analyzes the imported PGN file

	var pgn = document.getElementById('pgnfile').value;

	if (pgn == "") return;			// no data in pgnFile so skip everything 

	pgn = HTML2Text(pgn);		 	// strip HTML specifics off
	pgn = ModifyTOMA(pgn);			// Modify DGT TOMA input

	var ii, j, NrGames=-1, IsNewGame=1, ll=0, TagName, TagContent, kk, ff, found=0;
	var ppgnText	= new Array();
	var GlobTags	= new Object();
	GlobTags["Event"] = ""; GlobTags["Site"]=""; GlobTags["BroadcastTime"] = ""; GlobTags["UpdateInterval"]=UpdateInterval;
	GlobTags["AllowPostings"]=AllowPostings;

	function TagNames() {};
	TagNames["Black"] = "Schwarz"; TagNames["White"] = "Weiss"; TagNames["Result"] = "*"; TagNames["WhiteElo"] = "";
	TagNames["BlackElo"] = ""; TagNames["ActTime"] = ""; TagNames["FEN"]=""; TagNames["SAN"]=""; TagNames["Round"]="";


	var lTags 	= new Array();
	var Results 	= new Array("*", "1/2-1/2", "1-0", "0-1");

	// now extract the Game Tags such as [Event "Bundesliga"]
	aPgnText = pgn.split("[");
	var Parts=new Array(aPgnText.length-1);
	for (ii=1; ii < aPgnText.length; ii++) Parts[ii-1] = aPgnText[ii].split("]");
	for (ii=0; ii<Parts.length; ii++) { 
		TagName=Parts[ii][0].split(" ")[0];
		TagContent=Parts[ii][0].split("\"")[1];  	// Content is within the two " .." brackets

		if (NrGames == -1) { 				// Check for Global Tags only if there is no game yet
			for (kk in GlobTags) { if (TagName == kk ) { GlobTags[kk] = TagContent; break; 	} }
		}

		for (kk in TagNames)  {
			if (TagName == kk ) { 
				if (IsNewGame) { 
					NrGames++;
					lTags[NrGames]=new TagNames;
					ppgnText[NrGames]="...";
					IsNewGame=0;
				} 				// extend array at the beginning of each game parsing 
				lTags[NrGames][kk]=TagContent;  // now assign tags
				break; 
			} 
		}
		if (Parts[ii].length != 2) continue;		// only consider parts with "something after the closing "]" bracket 
		ff= Parts[ii][1]
		for (j=0; j<4; j++) { 
			if (ff.indexOf(Results[j]) >=0 ) {
				// Now we have a game which is correctly terminated by "*" or any valid result
				ppgnText[NrGames]=ff; // lTags[ii][1].substr(kk,lTags[ii][1].length);
				IsNewGame=1;
				break;
			}
		 }
	}
	// now update Servertime and or modified Updatetime (i.e. Update time can be influenced via tags
	if (document.getElementById('BroadcastTime')) document.getElementById('BroadcastTime').innerHTML= GlobTags["BroadcastTime"];
	UpdateInterval = GlobTags["UpdateInterval"];
	ii = parseInt(GlobTags["AllowPostings"]);
	if ((AllowPostings != 2) && ( AllowPostings != ii)) {		// this enables different "usergroups", to switch on and off for posts 
		AllowPostings=ii;
		document.getElementById("Post").value=textPosts[AllowPostings];
	}

	if (NrGames < 0) return;

	// assign the global variables;
	PgnMoveText	= ppgnText;
	Tags		= lTags;

	// now the pgn Text is clean and structured
//	if (IsDemo) PrepGames4Demo(NrGames);		// "prepare" game if Demo is enabled

	//	UpdateViewer();
	setTimeout("UpdateViewer()",0);
}

function PrepGames4Demo() {
	IsDemo++;
	return(PgnFileName+"?Demo="+IsDemo*UpdateInterval+"&UpdateInterval="+UpdateInterval);
}

function PrepGames4Demo(NoG) {			// This is the function to "cut" off games for demo purposes, called from: AnalyzePgn() if IsDemo Variable is set
	var BNr, NrOfBoards=(Rows*Cols);
	Games = new Array(NoG);  for (i=0; i<=NoG; i++) Games[i]=-1;	 // initialize all games with 0;
	for (BNr=0; BNr<NrOfBoards; BNr++) { i= GetActiveGame(BNr); Games[i]=BNr; } 
	for (var i=0; i<=NoG; i++) { 
		BNr=Games[i];
		if (BNr >= 0 ) { 
			ssearch=Math.floor(LastMove[BNr]/2 + 1 + Math.round( Math.random()*3 ) )+"."; 
			llst=PgnMoveText[i].indexOf(ssearch); 
			if (llst > 0) {
				PgnMoveText[i] = PgnMoveText[i].substring(0,llst)+" *";
				Tags[BNr]["Result"]="*";
			} // else PgnMoveText[i]="";
		} else PgnMoveText[i]=""; 
	}
	gLastFileContent=0;		// reset "old" File Length in order enforce interpretation

}

function getHttpRequest(FileN,URIString) {		// core AJAX function to retrive the pgn file !!

	if (IsLoading) { // wait until file is loaded 
		var x='getHttpRequest("'+FileN+'","'+URIString+'")';
		setTimeout(x,200); 
		return; 
	}  

	var isPosting = 2;
	if (FileN==PgnFileName) isPosting=0;
	else if (FileN == PostingFileName) {
		if (! document.getElementById('Postings')) return; // dont load postings if not allowed.
		isPosting=1;
	}

	var xmlHttp = null;

	if (typeof XMLHttpRequest != 'undefined') {	// Mozilla, Opera, Safari sowie Internet Explorer (ab v7)
		xmlHttp = new XMLHttpRequest();
	} else {
		if (!xmlHttp) {    			// Internet Explorer 6 und älter
			try { xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP");
			} catch(e) {
				try { xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP");
				} catch(e) { xmlHttp  = null; }
			}
		}
	}
	if (!xmlHttp) return; 				// invalid Browser 
	IsLoading=1;

	if (isPosting==0) {
		with (document) {
			if (getElementById('ShowUpdateTime')) getElementById('ShowUpdateTime').innerHTML="lade...";
			getElementById('UpdateTime').value = UpdateInterval;
		}
	}

	xmlHttp.open('GET', FileN+URIString, true);
	if (isPosting==1) {			// define function for Postings
		xmlHttp.onreadystatechange = function() {
				if (xmlHttp.readyState == 4) { 
					if (document.getElementById("Postings")) document.getElementById("Postings").innerHTML = unescape(xmlHttp.responseText);
					IsLoading=0;			// enable new requests
				}
		}

	} else if (isPosting==2) {			// define function for Postings
		xmlHttp.onreadystatechange = function() {
				if (xmlHttp.readyState == 4) { 
					if (document.getElementById("Post") && (document.getElementById("Post").value == PostingFeedback)) document.getElementById("Post").value ="";
					IsLoading=0;			// enable new requests
				}
		}
	} else {				// define Function for .pgn File
		xmlHttp.onreadystatechange = function() {
				if (xmlHttp.readyState == 4) { 
					document.getElementById('pgnfile').value = xmlHttp.responseText;
					if (document.getElementById('ShowUpdateTime')) document.getElementById('ShowUpdateTime').innerHTML=UpdateInterval+' sec';	// successfully loaded,
					AnalyzePgn();  // Update pgn here in order to make sure that the CountDown works properly!
					IsLoading=0;			// enable new requests
				}
		}
	};
	xmlHttp.send(null);	// send out the request finally.


}

function DecrementTime(BNr,tc) {			// writes time according to HistTime array
	if (! IsLive[BNr] || (TimeCountDown == 0) || (Tags[BNr]["Result"] != "*" ) ) { return; };
	var Time = InitialClock;
	blwh = new Array('TimeWhite-'+BNr,'TimeBlack-'+BNr);
	var tPlayer = blwh[ (MvCount[BNr] + GetIsRotated(BNr)) % 2];
	with (document) { if ( getElementById && (getElementById(tPlayer))) Time = getElementById(tPlayer).innerHTML; }
	var t = Time.split(":");
	var sec=0;
	if (!isNaN(t[0])) { sec =  t[0]*3600; }
	if (!isNaN(t[1])) { sec += t[1]*60; }
	if (!isNaN(t[2])) { sec += t[2]*1; } 

	sec--;

	if (sec < 0) sec=0;

	var s = sec %60; if (s < 10) s = "0"+s;
	var m = parseInt(sec/60)%60; if (m < 10) m="0"+m;
	var h = parseInt(sec/3600); 
	tc = tc %2;
	if ((sec == 0) && (tc)) {
		document.getElementById(tPlayer).innerHTML = sec;
	} else {
		document.getElementById(tPlayer).innerHTML = h+":"+m+":"+s;
	}
}

function CountDown(t) {
	var BNr, MaxMv;
        t--;
	with (document) {
		getElementById('UpdateTime').value = t;
		if (getElementById('ShowUpdateTime')) getElementById('ShowUpdateTime').innerHTML= t+" sec";
	}
	for (BNr = 0; BNr < (Rows*Cols); BNr++) {
		if (BNr < PgnMoveText.length) DecrementTime(BNr,t);
	}
}

function UpdateFile(force) { 			// Update files as defined "PgnFileName" after each "UpdateInterval", otherwise it counts down and displays the time to next update
	if (IsLoading > 0) { setTimeout("UpdateFile(0)",200); return; }  // wait until file is loaded 
	if ( document.getElementById('UpdateTime') ) { var t = document.getElementById('UpdateTime').value;
	} else { var t = 0 };

	if (force == 1) { t = 0; }			// avoids a Bug in IE, which does not kill the sleeping tasks at reload !!!!

	if ( (!isNaN(t)) && (t > 1)) { 	CountDown(t); 
	} else {
		getHttpRequest(PgnFileName,"?T="+Math.random());
		if (DisplayPostings) getHttpRequest(PostingFileName,"?T="+Math.random());
	}

	if (UpdateInterval > 0) setTimeout("UpdateFile(0)",1000); 		// check every second only if update ne 0;

}


