var KING = 0x01;
var QUEEN = 0x02;
var ROOK = 0x03;
var BISHOP = 0x04;
var KNIGHT = 0x05;
var PAWN = 0x06;
var NOPIECE = 0x07;

var W_KING = KING;
var W_QUEEN = QUEEN;
var W_ROOK = ROOK;
var W_BISHOP = BISHOP;
var W_KNIGHT = KNIGHT;
var W_PAWN = PAWN;

var B_KING = KING | 0x08;
var B_QUEEN = QUEEN | 0x08;
var B_ROOK = ROOK | 0x08;
var B_BISHOP = BISHOP | 0x08;
var B_KNIGHT = KNIGHT | 0x08;
var B_PAWN = PAWN | 0x08;

var NULL_SQUARE = 65;
var WHITE = 0x00;
var BLACK = 0x01;
var NOCOLOR = 0x02;

function square_Fyle(sq) { return (sq & 0x7); }
function square_Rank(sq) { return ((sq >> 3) & 0x7); }

function rank_FromChar(c) { 
	if (c < '1'  ||  c > '8') { 
		return NULL_SQUARE; 
	} else return (eval(c) - 1);  
}

function fyle_FromChar(c) {
	if (c < 'a'  ||  c > 'h') {
		return NULL_SQUARE; 
	} else return (c.charCodeAt(0) - "a".charCodeAt(0));  
}

function square_Make(f, r) { return ((r << 3) | f); }
function square_LeftDiag(sq) { return square_Rank(sq) + square_Fyle(sq); }
function square_RightDiag(sq) { return (7 + square_Rank(sq) - square_Fyle(sq)); }
function square_Color(sq) { return 1 - (square_LeftDiag(sq) & 1); }
function color_Flip(c) {return (1 - c); }


/**
 * Цвет ячейки - w или b
 **/
function sqcol(pos) {
	row = Math.floor((pos + 1) / 8) - 1;
	col = pos % 8;
	return ((row + col) % 2 - 1 ? 'b' : 'w');
}

/**
 * Create a repeat-string-N-times method for all String objects
 */
function string_repeat(n) {
   var s = "", t = this.toString();
   while (--n >= 0) s += t;
   return s;
} String.prototype.repeat = string_repeat; 

var Piece = {};
Piece.names = ["xx", "wk", "wq", "wr", "wb", "wn", "wp", "xx", "xx", "bk", "bq", "br", "bb", "bn", "bp"];
/**
 * Тип фигуры
 */
Piece.getType = function(p) { 	
	return (p & 0x07); 
}

/**
 * Цвет фигуры
 */
Piece.getColor = function(p) { 
	return (p == NOPIECE) ? NOCOLOR : ((p & 0x08) >> 3); 
}

Piece.Make = function(c, p) {
	return (p == NOPIECE) ? NOPIECE : ((c << 3) | (p & 0x07));
}

Piece.FromChar = function(pc) {
	switch (pc) {
    	case 'k': return KING;
    	case 'q': return QUEEN;
    	case 'r': return ROOK;
    	case 'n': return KNIGHT;
    	case 'b': return BISHOP;
    	default:  return NOPIECE;
	}
}

var FENReader = Class.create();
FENReader.prototype = {
	initialize : function() {
		this.f2p = new Hash({"1": NOPIECE, 
			"P": W_PAWN, "K": W_KING, "Q": W_QUEEN, "R": W_ROOK, "N": W_KNIGHT, "B": W_BISHOP,
			"p": B_PAWN, "k": B_KING, "q": B_QUEEN, "r": B_ROOK, "n": B_KNIGHT, "b": B_BISHOP });
		this._fens = [];
		this._pos = [];
	},
	
	cached : function(fen) {
		for (var i = 0; i < this._fens.length; i++) {
			if (this._fens[i] == fen) {
				return this._pos[i];
			}
		}
		return null;
	},
	
	read : function(fen) {
		while (fen.indexOf('  ') >= 0) 
			fen = fen.replace('  ', ' ');
		var pos = this.cached(fen);
		if (!pos) {
			try 
			{
				pos = new SimplePosition();				
				pos.fen_str = fen; 
			
				var i = 0, sq = 0;
				var tok = fen.split(/\s+/);
				var board_text = String(tok[0]);
			
				// replace NOPIECE square with repeated "1" string
				var s_one = String("1");
				for (i = 2; i <= 8; i++) {
					var re = new RegExp(String(i), "g");			  
					board_text = board_text.replace(re, "1".repeat(i));
				}
			
				// remove slashes
				board_text = board_text.replace(/\//g, "");
				if (board_text.length != 64) return null;	
			
				var fs2rs = Array(64);
				// fenSqToRealSquare[] converts a fen square (0 to 63) to its real
				// square. E.g: [0] -> A8, [1] -> B8, .... [63] -> H1.
				for (sq = 0; sq < 64; sq++)
					fs2rs[sq] = ((7 - Math.floor(sq / 8)) * 8 + (sq % 8));
        	
				for (sq = 0; sq < 64; sq++)
					pos.board[fs2rs[sq]] = this.f2p.get(board_text.charAt(sq));
				
				// Now the side to move:
				pos.whoMove = (tok[1] == 'w') ? WHITE : BLACK;
			
				// Now the castling flags:
				pos.castling = 0; 
				for (i = 0; i < tok[2].length; i++) { 
					if (tok[2].charAt(i) == 'K') pos.castling |= 1; 
					if (tok[2].charAt(i) == 'Q') pos.castling |= 2;
					if (tok[2].charAt(i) == 'k') pos.castling |= 4; 
					if (tok[2].charAt(i) == 'q') pos.castling |= 8;
				}
			
				// Now the EP target:
				if (tok.length > 3) {
	    	    	if (tok[3].charAt(0) == '-') {
						pos.epTarget = NULL_SQUARE;
					} else {
						var fylec = tok[3].charAt(0);
						if (fylec < 'a'  ||  fylec > 'h') {
							return null;
						}
						var rankc = tok[3].charAt(1);
						if (rankc != '3'  &&  rankc != '6') {
							return null;
						}
						pos.epTarget = square_Make(fyle_FromChar(fylec), rank_FromChar(rankc));
					}
				}
			
				pos.halfmovecount = (tok.length > 4) ? eval(tok[4]) : 0;
			
				/* Не обрабатываем число ходов
				if (tok.length > 5) {
					i = eval(tok[5]);
					if (i >= 1) { 
						this.plyCount = (i - 1) * 2;
						if (this.whoMove == BLACK) this.plyCount++; 
					}
				}
				*/
				pos.lastmove = 0; 
				pos.lastmovetext = null;
				
				this._fens.push(fen);
				this._pos.push(pos);
			}
			catch (e) {
				pos = null;
			}
		}
		return pos;
	}
}

var SimplePosition = Class.create();
SimplePosition.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function() {
 		this.board = new Array(64);
		this.captures = new Array(2);
		this.clear();
 	},
 	
 	clear : function() {
		this.fen_str = "";
		for (var i = 0; i < 64; i++)
			this.board[i] = NOPIECE;
		this.whoMove = WHITE;	
		this.captures[WHITE] = [];
		this.captures[BLACK] = [];
		this.epTarget = NULL_SQUARE;
		this.castling = 0;
		this.halfmovecount = 0;
		this.lastmove = 0; 
		this.lastmovetext = null;
	},
 	
 	clone : function() {
 		var result = new SimplePosition();
 		result.board = this.board.clone();
 		result.captures = this.captures.clone();
 		result.whoMove = this.whoMove; 		
 		result.epTarget = this.epTarget;
 		result.castling = this.castling;
 		result.halfmovecount = this.halfmovecount;
		result.lastmove = this.lastmove; 
		result.lastmovetext = this.lastmovetext;
		return result;
 	}
}

/**
 * Position - Игровая позиция
 */
var ChessPosition = Class.create();
ChessPosition.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(owner) {
 		this.owner = owner;
 		this.listeners = [];
 		this.fenReader = new FENReader();
 		this.start_pos = new SimplePosition();
 		this.board = new Array(64);
		this.captures = new Array(2);
 		this.whoMove = WHITE;
 		this.moves = [];
		this.legalMoves = [];
		this.base_move_length = 0;
		this.game_result = Chess.RESULT_None; 
		this.clear();
 	},
 	
 	clear : function() {
 		if (this.start_pos) this.start_pos.clear();
		for (var i = 0; i < 64; i++)
			this.board[i] = NOPIECE;
		this.whoMove = WHITE;	
		this.captures[WHITE] = [];
		this.captures[BLACK] = [];
		this.epTarget = NULL_SQUARE;
		this.castling = 0;
		this.halfmovecount = 0;
		this.currentMove = 0;
	}, 
		
	GetPlyCount : function() {
		return (this.moves.length);
	},
	
	/**
 	* Установить из другого объекта
 	*/
	Assign : function(p) {
		this.clear();
		if (p.start_pos) this.start_pos = p.start_pos.clone();
		this.board = p.board.clone();
		this.whoMove = p.whoMove;	
		this.captures = p.captures.clone();
		this.epTarget = p.epTarget;
		this.castling = p.castling;
		this.halfmovecount = p.halfmovecount;
		this.moves = p.moves.clone();
		this.legalMoves = p.legalMoves.clone();
		this.currentMove = p.currentMove; 
	},
	
	/**
 	* Вызывается при изменении позиции
 	*/
	PositionChanged : function() {
		for (var i = 0; i < this.listeners.length; i++)	
			this.listeners[i].PositionChanged();
	},
	
	/**
 	* Добавить обработчик события изменения позиции
 	*/
	AddListener : function(listener) {	
		this.listeners.push(listener);
	},
	
	SetStartPos : function() {
		if (this.start_pos) {
			this.board = this.start_pos.board.clone();
			this.whoMove = this.start_pos.whoMove;
			this.captures = this.start_pos.captures.clone();
			this.epTarget = this.start_pos.epTarget;
			this.castling = this.start_pos.castling;
			this.halfmovecount = this.start_pos.halfmovecount;
		}
	},
	
	/**
 	* Читать позицию из FEN
 	*/
	SetFEN : function(fen) {
		this.clear();
		this.start_pos = this.fenReader.read(fen);
		this.SetStartPos(); 
	},
	
	/**
	 * Стандартная начальная позиция
 	*/
	SetStdStart : function() {
		this.SetFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); 
	},
	
	/**
	 * Получить фигуру в позиции sq
	 */
	piece : function(sq) {
		return this.board[sq];
	},
	
	/**
 	* Перевод числовой нотации ячейки в буквенно-числовую
 	**/
	Pos2Note : function(pos) {
		var hqs = Array("a","b","c","d","e","f","g","h");
		return hqs[pos % 8] + Math.ceil((pos + 1) / 8);
	},
	
	ResetMoves : function() {
		while (this.moves.length > this.base_move_length)
			this.moves.pop();	
	},
	
	/**
 	* Установить сделанные ходы
 	**/
	SetMoves : function(mvs) {
		this.moves = [];
		if (mvs != undefined) {
			this.moves = new Array(mvs.length);
			for (var i = 0; i < mvs.length; i++) {
				this.moves[i] = mvs[i];
			}
			this.base_move_length = this.moves.length;
		} 
	}, 
	
	/**
 	* Раскодировать клетку-источник из списка ходов
 	*/
	MoveFrom : function(ply) {
		if (this.moves[ply])
			return this.moves[ply][0] & 63;
		else
			return NULL_SQUARE;
	},
	
	/**
 	* Раскодировать клетку-приемник из списка ходов
 	*/
	MoveTo : function(ply) {
		if (this.moves[ply])
			return (this.moves[ply][0] >> 6) & 63;
		else
			return NULL_SQUARE;
	},
	
	/**
 	* Раскодировать цвет ходившего из списка ходов
 	*/
	MoveColor : function(ply) {
		if (this.moves.length > ply) {
			return (this.moves[ply][0] >> 12) & 63;
		} else {
			return this.whoMove;
		}
	},
	
	/**
 	* Фигура превращение на указанном ходе
 	*/
	MovePromotion : function(ply) {
		
		return this.moves[ply][3];
	},
	
	/**
 	* Поле взятия на указанном ходе
 	*/
	MoveCapturedSquare : function(ply) {
		return (this.moves[ply][2] >> 6) & 63;;
	},
	
	/**
 	* Фигура взятая на указанном ходе
 	*/
	MoveCapturedPiece : function(ply) {
		return this.moves[ply][2] & 63;
	},
	
	/**
 	* Очистить допустимые ходы
 	*/
	ClearLegalMoves : function() {
		this.legalMoves = new Array();
	},
	
	/**
 	* Добавить ход в список допустимых
 	*/
	AddLegalMove : function(fr, to) {
		if (!this.legalMoves[fr]) { this.legalMoves[fr] = new Array(); }
		this.legalMoves[fr][to] = true;
	},
	
	/**
 	* Добавить список допустимых ходов
 	*/
	AddLegalMoves : function(lm) {
		this.ClearLegalMoves();
		if (lm) {
			for (var i = 0; i < lm.length; i++) {
				var fr = lm[i] & 63;
				var to = (lm[i] >> 6 ) & 63;
				this.AddLegalMove(fr, to); 
			}	
		}
	},
	
	/**
	 * Максимальное количество отображаемых ходов
	 */
	MaxListedMove : function() {
		return (this.owner.IsPlayMode()) ? this.base_move_length : this.GetPlyCount();
	},
	
	/**
	 * Наибольший отображаемый ход
	 */
	DisplayCurrentMove : function() {
		if (this.owner.IsPlayMode()) {
			return (this.currentMove <= this.base_move_length) ? this.currentMove : (this.base_move_length); 
		} else {
			return this.currentMove;	
		}	
	},
	
	/**
 	* Проверка, является-ли ход допустимым?
 	*/
	CanMove : function(fr, to) {
		if (this.currentMove < this.base_move_length) return false;
		if (this.owner.IsPlayMode()) {
			if (this.currentMove != this.GetPlyCount()) return false;
			if (to == NULL_SQUARE) {
				return (this.legalMoves[fr] != null);
			} else {
				return (this.legalMoves[fr][to]);
			}
		} else {
			return this.IsValidMove(fr, to);
		}
	},
	
	IsValidMove : function(fr, to) {
		if (to == NULL_SQUARE) {
			return (Piece.getColor(this.board[fr]) == this.whoMove);
		}
		return true;
	},
	
	MoveFirst : function() {
		this.GotoMove(1);
	},
	
	MoveForward : function() {
		this.GotoMove(this.currentMove + 1);
	},
	
	MoveBackward :function() {
		this.GotoMove(this.currentMove - 1);
	},
	
	MoveLast : function() {
		this.GotoMove(this.GetPlyCount());
	},
	
	GotoMove : function(n) {
		if (n < 0) n = 0;
		if (n > this.GetPlyCount()) n = this.GetPlyCount();
		
		var start = 0;
		if (n > this.currentMove) {
			start = this.currentMove;
		} else {
			this.SetStartPos();
			start = 0;
			this.captures[WHITE] = [];
			this.captures[BLACK] = [];
			captureBlack = 0;
			captureWhite = 0;
		}
		this.currentMove = n;
		for (var i = start; i < this.currentMove; i++) {
			this.whoMove = color_Flip(this.MoveColor(i));
			var from = this.MoveFrom(i);
			var to = this.MoveTo(i);
			var p = this.board[from];
			// move piece
			this.board[from] = NOPIECE;
			this.board[to] = p;
			var pt = Piece.getType(p);
			if (pt == KING) { // Castling
				if ((from - to) == 2) { this.board[from - 1] = this.board[from - 4]; this.board[from - 4] = NOPIECE; }
				if ((from - to) == -2) { this.board[from + 1] = this.board[from + 3]; this.board[from + 3] = NOPIECE; }
			}
		
			this.epTarget = NULL_SQUARE;	
			if (pt == PAWN) {
				if ((to - from) == 16) {
					this.epTarget = from + 8;
				} 
				if ((to - from) == -16) {
					this.epTarget = from - 8;
				}
				if (to != this.MoveCapturedSquare(i)) { // en passant
					this.board[this.MoveCapturedSquare(i)] = NOPIECE;
				}
			}
			if (this.MovePromotion(i) != NOPIECE) { // promotion
				this.board[to] = this.MovePromotion(i);
			}
			if (this.MoveCapturedPiece(i) != NOPIECE) { // captive
				if (this.MoveColor(i) == WHITE) {
					this.captures[BLACK].push(this.MoveCapturedPiece(i));
				} else {
					this.captures[WHITE].push(this.MoveCapturedPiece(i));
				}
			}
		}
		this.PositionChanged();
		return false;
	},

	AddMove : function(fr, to, promo) {
		if ((this.game_result > Chess.RESULT_None)) return;
		if (this.owner && this.owner.IsAnalizeMode()) {
			while (this.currentMove < this.GetPlyCount()) {
				this.moves.pop();
			}
		}
		var cs = to; 
		var p = this.board[fr];
		var pt = Piece.getType(p);
		var pc = Piece.getColor(p);
		
		if (pt == PAWN) {
			if (to == this.epTarget) { // check enpassant
				// check enpassant
				if (pc == BLACK) {
					cs = to + 8;
				} else {
					cs = to - 8;
				}
			}

			if (((pc == WHITE) && (square_Rank(to) == 7)) ||
				((pc == BLACK) && (square_Rank(to) == 0))) {
				if (promo == NOPIECE)
					this.owner.ShowPromotion();
			}
		}
	
		var cp = this.board[cs];
		
		var mv = Array(7);
		mv[0] = fr | (to << 6) | (this.whoMove << 12);
		mv[1] = this.Pos2Note(fr) + "-" + this.Pos2Note(to); // Нотация
		mv[2] = cp | (cs << 6); // Битое поле
		if (promo != NOPIECE) {
			promo = Piece.Make(pc, Piece.FromChar(promo));
		}
		mv[3] = promo; // promote piece
		mv[4] = "";
		
		this.moves.push(mv);
		this.MoveLast();	
	}
}

function MoveRow(prefix, id) {
	this.element = $("mr" + prefix + id);
	this.visible = true;
}

MoveRow.prototype = {
	element : null,
	visible : true,
	
	show : function() {
		if (!this.visible && this.element) {
			this.element.show();
			this.visible = true;
		}
	},
	
	hide : function() {
		if (this.visible && this.element) {
			this.element.hide();
			this.visible = false;
		}
	}
};


var MoveList = Class.create();
MoveList.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(owner) {
 		this.owner = owner;
 		this.position = this.owner.position;
 		this.position.AddListener(this);
 		
 		// Кэш ходов
 		this.moves = [];
 		this.rows = [];
 		this.oldMove = 0;
 		this.ListElement = $("id-movelist-list");
 		if (this.ListElement) {
 			this.commentBox = $("mv_comment");
 			this.TableElement = $("id-movelist-table");
 			this.TableBodyElement = this.TableElement.firstDescendant();
 			this.TableRows = this.TableBodyElement.immediateDescendants();
 			for (var i = 0; i < this.TableRows.length; i++) {
 				this.rows.push(new MoveRow(this.owner.id, i + 1));
 			}
 			for (var i = 0, len = this.TableRows.length * 2; i < len; i++) {
 				var em = this.move(i);
 			}
			
 			this.buttonFirst = $("id-movelist-first");	this.buttonFirst.owner = this;
 			this.buttonPrev = $("id-movelist-prev");	this.buttonPrev.owner = this;
 			this.buttonNext = $("id-movelist-next");	this.buttonNext.owner = this;
 			this.buttonLast = $("id-movelist-last");	this.buttonLast.owner = this;
 			 			
 			Event.observe(this.buttonFirst, "click", function(e) {
 				this.owner.position.MoveFirst();
 				return false;
 			});
			
			Event.observe(this.buttonPrev, "click", function(e) {
				this.owner.position.MoveBackward();
				return false;
			});
			
			Event.observe(this.buttonNext, "click", function(e) {
				this.owner.position.MoveForward();
				return false;
			});
			Event.observe(this.buttonLast, "click", function(e) {
				this.owner.position.MoveLast();
				return false;
			});
 		}
 	},
	
	/**
	 * Кэширование ходов
	 */
	move : function(mv) {
		var m = this.moves[mv];
		if (!m) {
			m = $("mv" + this.owner.id + mv);
			if (m) {
				m.owner = this;
				m.ply = mv + 1;
				Event.observe(m, "click", function(e) {
 					this.owner.position.GotoMove(this.ply);
 					return false;
 				});
				this.moves[mv] = m;
			}
		}
		return m;
	},
	
	row : function(n) {
		var r = this.rows[n];
		if (!r) {
			var re = $("mr" + this.owner.id + n);
			this.rows[n] = {e: re, v: true};
		}
		return r;
	},

 	DravMoveList : function() {
 		var maxMoves = this.position.MaxListedMove();
		var needCells = maxMoves; 
		if (this.owner.game_result > Chess.RESULT_None) needCells++;
		if (needCells % 2) needCells++;
		var needRows = Math.floor(needCells / 2);
		var tableRows = this.TableRows.length;
			
		if (tableRows < needRows) {
			while (tableRows < needRows) {
				tableRows++;
				var wm = tableRows * 2 - 2, bm = wm + 1;
				var row = "<tr id=\"mr" + tableRows + "\">" +
						  "<td class=\"row1\">" + tableRows + "</td>" +
						  "<td class=\"row1\" id=\"mv" + wm + "\"></td>" + 
						  "<td class=\"row1\" id=\"mv" + bm + "\"></td></tr>";
				this.TableBodyElement.insert(row);
				
				this.rows.push(new MoveRow(this.owner.id, tableRows));
			
				var em = this.move(wm);
				em = this.move(bm);
			}
			this.TableRows = this.TableBodyElement.immediateDescendants();
		}
			
		var i = 0;
		
		for (i = 0; i < this.rows.length; i++) {
			if (i < needRows) {
				this.rows[i].show();
			} else {
				this.rows[i].hide();
			}
		}
		
		var i = 0;
		while (i < maxMoves) {
			var from = this.position.MoveFrom(i);
			var to = this.position.MoveTo(i);
			var p = this.position.board[from];
			var em = this.move(i);
			em.innerHTML = "<a href=\"javascript:void(0);\">" + this.position.moves[i][1] + "</a>";
			i++;
		}
		if (i % 2) {
			var em = this.move(i);
			em.innerHTML = "<a href=\"javascript:void(0);\"></a>";
		}			
		if (this.owner.game_result > Chess.RESULT_None) {
			var em = this.move(i);
			em.innerHTML = "<strong>" + Chess.RESULT_LongStr[this.owner.game_result] + "</strong>";
		}
	},
	
	/**
 	* Вызывается при изменении позиции
 	*/
	PositionChanged : function() {
		this.DravMoveList();
		this.HideCurrentMove();
		this.ShowCurrentMove();		
	},
	
	/**
 	* Отметить текущий ход в списке ходов
 	**/
	ShowCurrentMove : function() {
		this.oldMove = this.position.DisplayCurrentMove()-1;
		var el = this.move(this.oldMove);
		if (el) {
			el.removeClassName("row1");
			el.addClassName("row3");
		}
		
		if (this.commentBox) {
			var cmt = (this.oldMove >= 0) ? this.position.moves[this.oldMove][4] : "";
			this.commentBox.innerHTML = cmt + "&nbsp;";
		}
		
		scroll_ply_count = this.oldMove - 11;
		var max_ply = this.position.MaxListedMove() - 1;
		if (scroll_ply_count < 0) {
      		scroll_ply_count = 0;
    	}
		if (this.oldMove <= 19) {
      		newScrollHeight = 0;
    	} else {
      		newScrollHeight = (scroll_ply_count / max_ply) * this.ListElement.scrollHeight;
    	}
		this.ListElement.scrollTop = newScrollHeight;
	},
	
	/**
 	* Скрыть текущий ход в списке ходов
 	**/
	HideCurrentMove : function() {
		var el = this.move(this.oldMove);
		if (el) {
			el.removeClassName("row3");
			el.addClassName("row2");
		}
	} 	
}

/**
 * Битые фигуры.
 */
var Captures = Class.create();
Captures.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(owner) {
 		this.owner = owner;
 		this.position = this.owner.position;
 		this.position.AddListener(this);
 		this.white = {};
 		this.black = {};
 		for (var i = 0; i < 15; i++) {
			this.white[i] = {img: $("icw" + i), p: NOPIECE};
			this.black[i] = {img: $("icb" + i), p: NOPIECE};
		}
 	},

	SetImage : function(img, piece) {
		var src = "";
		if (piece != NOPIECE) {
			img.addClassName("icpt");
			if ((Chess.IMG_EXT == ".png") && Chess.NeedAlphaLoader && img.runtimeStyle) {
  				img.src = "/images/pix.gif";
				img.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"
					+ Chess.IMG_ROOT + Piece.names[piece] + "c" + Chess.IMG_EXT,
					+ "',sizingMethod='scale')";
			} else {
				img.src = Chess.IMG_ROOT + Piece.names[piece] + "c" + Chess.IMG_EXT;
			}
		} else {
			img.src = "/images/pix.gif";
			img.removeClassName("icpt");
		}
 	},
	
	/**
	 * Отобразить битые фигуры
	 */
	DrawCaptures : function() {
		var p = NOPIECE;
		for (var i = 0; i < 15; i++) {
			p = (i < this.position.captures[WHITE].length) ? this.position.captures[WHITE][i] : NOPIECE; 
			if (this.white[i].p != p) {
				this.white[i].p = p;
				this.SetImage(this.white[i].img, p);
			}
			p = (i < this.position.captures[BLACK].length) ? this.position.captures[BLACK][i] : NOPIECE;
			if (this.black[i].p != p) {
				this.black[i].p = p;
				this.SetImage(this.black[i].img, p);
			}
		}
	},
 	
 	/**
 	* Вызывается при изменении позиции
 	*/
	PositionChanged : function() {
		this.DrawCaptures();		
	} 	
}

var MoveForm = Class.create();

MoveForm.HasForm = function() {
	if ($("moveform")) {
		return true;
	}
	return false;
}

MoveForm.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(owner) {
 		this.owner = owner;
 		this.resetListeners = [];
 		this.fromField = null;
 		this.toField = null;
 		this.drawBox = null;
 		this.resetButton = null;
 		this.submitButton = null;
 		this.formElement = $("moveform");
 		if (this.formElement) {
 			this.fromField = $("fr");
 			this.toField = $("to");
 			this.drawBox = $("draw");
 			this.promo = $("promote");
 			this.resetButton = $(this.formElement.brst);
 			if (this.resetButton) {
 				this.resetButton.owner = this;
 				Event.observe(this.formElement.brst, "click", function(e) {
 					this.owner.ResetMoves();
 					return false;
 				});
 			}
 			this.submitButton = $(this.formElement.sbmt);
 			if (this.submitButton) {
 				this.submitButton.owner = this;
 				Event.observe(this.formElement.sbmt, "click", function(e) {
 					this.owner.SubmitForm();
 					return false;
 				});
 			}
 		}
 		
 		this.navButtons = $("id-board-nav-buttons");
 		this.gameMsgPanel = $("id-game-msgs");
 		
 		this.analyzeOnLink = $("id-analyze-show");
 		this.analyzeOffLink = $("id-analyze-hide");
 		this.saveMode = this.owner.script_mode;
 		if (this.saveMode > Chess.BOARD_MODE_ANALYZE) {
			if (this.analyzeOnLink) {
				this.analyzeOnLink.owner = this;
				Event.observe(this.analyzeOnLink, "click", function(e) {
					this.owner.TurnAnalyze(true);
					return false;
				});
			}
			if (this.analyzeOffLink) {
				this.analyzeOffLink.owner = this;
				Event.observe(this.analyzeOffLink, "click", function(e) {
					this.owner.TurnAnalyze(false);
					return false;
				});
			}
		}
 		this.clear();
 	},
 	
 	SubmitForm : function() {
 		Form.Element.disable("sbmt");
 		if (this.owner.IsAjaxMode()) {
 			this.owner.SendMove();
 		} else {
 			if (!Chess.popup) {
 				this.formElement.submit();
 			} else {
 				Form.request(this.formElement, {asynchronous: false});
 			 	window.close();
 			}
 		}
 	},
 	
 	TurnAnalyze : function(mode) {
		if (mode) {
			this.analyzeOnLink.hide();
			this.analyzeOffLink.show();
			this.gameMsgPanel.hide();
			this.navButtons.show();
			this.saveMode = this.owner.script_mode;
			this.owner.SetMode(Chess.BOARD_MODE_ANALYZE);
		} else {
			this.analyzeOffLink.hide();
			this.analyzeOnLink.show();
			this.navButtons.hide();
 			this.gameMsgPanel.show();
			this.owner.SetMode(this.saveMode);
			this.ResetMoves();
		}
	},
 	
 	clear : function() {
 		if (this.formElement) {
 			Form.Element.clear(this.fromField);
			Form.Element.clear(this.toField);
			this.setEnableDraw(); 
			this.drawBox.checked = false;
			Form.Element.disable("sbmt");
 		}
 	},
 	
 	setEnableDraw : function() {
 		if (this.owner.script_mode < Chess.BOARD_MODE_STDAJAX) {
			Form.Element.disable(this.drawBox);
		} else {
			Form.Element.enable(this.drawBox);
		}
 	},
 
 	ResetMove : function() {
		this.clear();
		this.owner.OnResetMove();
	},
 
	ResetMoves : function() {
		this.clear();
		this.owner.OnResetMoves();
	},
	
	SetFrom : function(pos) {
		if (this.owner.IsPlayMode())
			this.fromField.value = pos; 
	},
	
	SetTo : function(pos) {
		if (this.owner.IsPlayMode()) {
			this.toField.value = pos;
			Form.Element.enable("sbmt");
			Form.Element.enable("draw");
		}
	},
	
	SetPromo : function(promo) {
		if (this.owner.IsPlayMode()) {
			this.promo.value = promo;
		}
	}	
}

var PromotePanel = Class.create();
PromotePanel.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(owner) {
 		this.owner = owner;
 		this.element = $("id-promote-panel" + this.owner.id);
 		if (this.element) {
 			this.pieceQueen = $("promote-queen-icon");		this.pieceQueen.owner = this;
 			this.pieceRook = $("promote-rook-icon");		this.pieceRook.owner = this;
 			this.pieceBishop = $("promote-bishop-icon");	this.pieceBishop.owner = this;
 			this.pieceKnight = $("promote-knight-icon");	this.pieceKnight.owner = this;
 			Event.observe(this.pieceQueen, "click", function(e) {
 				this.owner.ApplyPromote("q");
 				return false;
 			});
 			Event.observe(this.pieceRook, "click", function(e) {
 				this.owner.ApplyPromote("r");
 				return false;
 			});
 			Event.observe(this.pieceBishop, "click", function(e) {
 				this.owner.ApplyPromote("b");
 				return false;
 			});
 			Event.observe(this.pieceKnight, "click", function(e) {
 				this.owner.ApplyPromote("n");
 				return false;
 			});
 			this.SetImages();
 		}	
 	},
 	
 	SetImages : function() {
 		if (this.pieceQueen) this.SetImage(this.pieceQueen, QUEEN);
 		if (this.pieceRook) this.SetImage(this.pieceRook, ROOK);
 		if (this.pieceBishop) this.SetImage(this.pieceBishop, BISHOP);
 		if (this.pieceKnight) this.SetImage(this.pieceKnight, KNIGHT);
 	},
 	
 	SetImage : function(img, piece) {
 		if (this.owner.my_color == BLACK) piece = piece | 0x08; 
 		var src = Chess.IMG_ROOT + Piece.names[piece] + "w" + Chess.IMG_EXT;
  				
  		if ((Chess.IMG_EXT == ".png") && Chess.NeedAlphaLoader && img.runtimeStyle) {
  			img.src = "/images/pix.gif";
			img.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"
				+ src
				+ "',sizingMethod='scale')";
		} else {
			img.src = src;	
		}  				
 	},
 	
 	ApplyPromote : function(piece) {
 		this.owner.ApplyPromote(piece);
 	},
 	
 	show : function() {
 		if (this.element)
 			this.element.show();
 	},
 	
 	hide : function() {
 		if (this.element)
 			this.element.hide();
 	}
}

var QuestionPanel = Class.create();
QuestionPanel.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(owner, params) {
 		this.owner = owner;
 		this.params = params;
 		this.element = $("id-question-panel");
 		this.yesButton = $("id-question-button-yes"); this.yesButton.owner = this;
 		this.noButton = $("id-question-button-no"); this.noButton.owner = this;
 		this.closeBtn = $("id-question-hide"); this.closeBtn.owner = this;
 		Event.observe(this.yesButton, "click", function(e) {
 			this.owner.yesButtonClick();
 			return false;
 		});
 		Event.observe(this.noButton, "click", function(e) {
 			this.owner.noButtonClick();
 			return false;
 		});
 		Event.observe(this.closeBtn, "click", function(e) {
 			this.owner.hide();
 			return false;
 		});
 	},
 	
 	yesButtonClick : function() {
 		if (this.params.cmdy && ((this.owner.script_mode == Chess.BOARD_MODE_STDPLAY) || 
 								 (this.params.id == Chess.BOARD_MESSAGE_OTHER)))
			window.location = this.params.cmdy; 
 		this.owner.QuestionResult(true, this.params.id);
 		this.close();
 	},
 	
 	noButtonClick : function() {
 		if (this.params.cmdn && ((this.owner.script_mode == Chess.BOARD_MODE_STDPLAY) || 
 								 (this.params.id == Chess.BOARD_MESSAGE_OTHER)))
			window.location = this.params.cmdn;
 		this.owner.QuestionResult(false, this.params.id);
 		this.close();
 	},
 	
 	show : function() {
 		if ((this.params.txt == undefined) || !this.params.txt) return;
 		$("id-question-header").innerHTML = this.params.head; 
 		$("id-question-text").innerHTML = this.params.txt;
 		$("id-question-button-yes").value = this.params.lby;
 		$("id-question-button-no").value = this.params.lbn;
 		if (this.element)
 			this.element.show();
 	}, 
 	
 	close : function() {
 		this.params = {};
 		this.hide();
 	},
 	
 	hide : function() {
 		if (this.element)
 			this.element.hide();
 		return false;
 	}
}

/**
 * Board Mode Form
 **/
var BoardModeForm = Class.create();
BoardModeForm.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(owner) {
 		this.owner = owner;
 		this.formElement = $("boardmode");
 		if (this.formElement) {
 			this.rb_play_mode_blitz = $("rb_play_mode_blitz");	
 			this.rb_play_mode_ajax = $("rb_play_mode_ajax");
 			this.rb_play_mode_form = $("rb_play_mode_form");
 			this.rb_mode_observe = $("rb_mode_observe");
 			this.rb_mode_analize = $("rb_mode_analize");
 			
 			if (this.rb_play_mode_blitz) {
 				this.rb_play_mode_blitz.owner = this;
 				Event.observe(this.rb_play_mode_blitz, "click", function(e) {
 					this.owner.ApplyMode();
 				});
 			}
 			if (this.rb_play_mode_ajax) {
 				this.rb_play_mode_ajax.owner = this;
 				Event.observe(this.rb_play_mode_ajax, "click", function(e) {
 					this.owner.ApplyMode();
 				});
 			}
 			if (this.rb_play_mode_form) {
 				this.rb_play_mode_form.owner = this;
 				Event.observe(this.rb_play_mode_form, "click", function(e) {
 					this.owner.ApplyMode();
 				});
 			}
 			if (this.rb_mode_observe) {
 				this.rb_mode_observe.owner = this;
 				Event.observe(this.rb_mode_observe, "click", function(e) {
 					this.owner.ApplyMode();
 				});
 			}
 			if (this.rb_mode_analize) {
 				this.rb_mode_analize.owner = this;
 				Event.observe(this.rb_mode_analize, "click", function(e) {
 					this.owner.ApplyMode();
 				});
 			}
 			this.SetModeControls();
 		}	
 	},
 	
 	/**
 	 * Установить переключатели
 	 */
 	SetModeControls : function() {
 		if (this.owner.IsPlayMode()) {
 			if (this.owner.script_mode == Chess.BOARD_MODE_AJAX) {
 				if (this.rb_play_mode_blitz) this.rb_play_mode_blitz.checked = true;
 			} else if (this.owner.script_mode == Chess.BOARD_MODE_STDAJAX) {
 				if (this.rb_play_mode_ajax) this.rb_play_mode_ajax.checked = true;
 			} else {
 				if (this.rb_play_mode_form) this.rb_play_mode_form.checked = true;
 			}   
 		} else if (this.owner.script_mode == Chess.BOARD_MODE_OBSERVE) {
 			if (this.rb_mode_observe) this.rb_mode_observe.checked = true;
 		} else if (this.owner.script_mode == Chess.BOARD_MODE_ANALYZE) {
 			if (this.rb_mode_analize) this.rb_mode_analize.checked = true;
 		} else {
 			//TODO: Что делать с формах при прочих режимах				
 		}
 	},
 	
 	ApplyMode : function() {
 		if (this.rb_play_mode_blitz && this.rb_play_mode_blitz.checked) {
 			this.owner.SetMode(Chess.BOARD_MODE_AJAX);
 		} else if (this.rb_play_mode_ajax && this.rb_play_mode_ajax.checked) {
 			this.owner.SetMode(Chess.BOARD_MODE_STDAJAX);
 		} else if (this.rb_play_mode_form && this.rb_play_mode_form.checked) {
 			this.owner.SetMode(Chess.BOARD_MODE_STDPLAY);
 		} else if (this.rb_mode_observe && this.rb_mode_observe.checked) {
 			this.owner.SetMode(Chess.BOARD_MODE_OBSERVE);
 		} else if (this.rb_mode_analize && this.rb_mode_analize.checked) {
 			this.owner.SetMode(Chess.BOARD_MODE_ANALYZE);
 		} else {
 			this.owner.SetMode(Chess.BOARD_MODE_EXAMINE);
 		}
 	}
}

var GameChat = Class.create(Ajax.Base, {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function($super, options) {
 		$super(options);
		this.onComplete = this.options.onComplete;
		
		this.owner = this.options.owner;
		this.gameid = this.options.gameid;
		this.frequency = (this.options.frequency || 60);
		this.decay = (this.options.decay || 1);
		this.updater = { };
		this.firstCall = true;
		this.container = $("id-board-comments");
		this.lastText = "";
		this.url = "/chess/gamechat.html?gid=" + String(this.gameid);
		
		this.formElement = $("gamechat");
 		if (this.formElement) {
 			this.formElement.onsubmit = this.SubmitForm;
 			this.sendBtn = $("comment_post");
 			this.chat_tab = $("id-board-btn-messages");
 			this.comment_text = $("comment_text");
 			if (this.sendBtn) {
				Event.observe(this.sendBtn, "click", this.Send.bindAsEventListener(this)); 				
 			}
 			this.start();	
 		}
 		GlobalPage.updaters.push(this); 
 	},
 	
 	start: function() {
		this.options.onComplete = this.updateComplete.bind(this);
		this.onTimerEvent();
	},

	stop: function() {
		this.updater.options.onComplete = undefined;
		clearTimeout(this.timer);
		(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
	},
	
	updateComplete: function(response, json) {
		if (this.options.decay) {
    		this.decay = (response.responseText == this.lastText ?
			this.decay * this.options.decay : 1);
		}
		if (this.lastText && (response.responseText != this.lastText)) {
			if (this.chat_tab) {
				if (!this.chat_tab.hasClassName("selected")) {
					this.chat_tab.addClassName("blinked");
				}
			}
		}
		this.lastText = response.responseText;
		if (this.firstCall) {
			this.firstCall = false;
			this.onTimerEvent();
		} else {
			this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
		}
	},

	onTimerEvent: function() {
		var url = this.firstCall ? this.url + "&step=1" : this.url;
		this.updater = new Ajax.Updater(this.container, url, this.options);
	},
 	
 	SubmitForm : function() {
 		if (!$("comment_show")) return false;
 	},
 	
 	Send : function(e) {
 		if (this.formElement) {
 			if (this.comment_text.present()) {	
 				this.formElement.request({asynchronous: false});
 				this.comment_text.clear();
 				this.stop();
 				this.start();
 			}
 		}
 	}
});

var GameNotes = Class.create();
GameNotes.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(options) {
 		if (!options) options = {};
 		
 		this.owner = options.owner;
 		this.gameid = options.gameid;
 		this.formElement = $("gamenotes");
 		this.updater = null;
 		if (this.formElement) {
 			this.sendBtn = $("notes_post");
 			if (this.sendBtn) {
				Event.observe(this.sendBtn, "click", this.Send.bindAsEventListener(this)); 				
 			}
 		} 
 	},
 	
 	Send : function(e) {
 		if (this.formElement) {
 			this.formElement.request();
 		}
 	}
}

var BoardAjax = Class.create(Ajax.Base, {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function($super, owner, options) {
 		$super(options);
		
		this.onComplete = this.options.onComplete;
		
		this.owner = owner;
		this.gameid = owner.gameid;
		this.frequency = 1;
		this.updater = { };
		this.commands = [];
		this.url = "/chess/ajax.html";
		if (this.owner.script_mode >= Chess.BOARD_MODE_OBSERVE)
			this.start();
		GlobalPage.updaters.push(this);
 	},
 	
 	start: function() {
		this.options.onComplete = this.processResponse.bind(this);
		this.onTimerEvent();
	},

	stop: function() {
		if (this.updater.options)
			this.updater.options.onComplete = undefined;
		clearTimeout(this.timer);
		this.commands = [];
		(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
	},
	
	SendImmediate : function(request) {
		this.stop();
		this.commands = [request];
		this.start();
	},
	
	redirect : function(url) {
		this.stop();
		window.location = url;
	},
	
	processResponse: function(response, json) {
		json = json ? json : response.responseJSON; 
		if (json && !this.owner.InPromotion && this.owner.IsActiveMode()) {
			if (json.result == -2) {
				if (Chess.popup) {
					window.close();	
				} else {
					this.redirect("/index.html");
				}
				return;
			}
			
			if (json.result != this.owner.game_result) {
				this.redirect(window.location);
				return;
			}
			
			if ((json.cmd == Chess.BOARD_COMMAND_ACCEPT_CHLG) ||
				(json.cmd == Chess.BOARD_COMMAND_REJECT_CHLG)) {
				if ((json.cmd == Chess.BOARD_COMMAND_ACCEPT_CHLG) && (!json.msg)) {
					this.redirect(window.location);
					return;
				}
				if ((json.cmd == Chess.BOARD_COMMAND_REJECT_CHLG) && (!json.msg)) {
					if (Chess.popup) {
						window.close();	
					} else {
						this.redirect("/index.html");
					}
					return;
				}
			}
			
			this.owner.script_mode = json.mode;
			this.owner.my_color = json.color;
			if ((json.moves != undefined) && (json.moves.length > 0)) {
				if ((json.cmd == Chess.BOARD_COMMAND_POLL) && (json.moves.length == 1)) {
					this.owner.position.moves.push(json.moves[0]);
					this.owner.position.base_move_length++;
				} else {
					this.owner.position.SetMoves(json.moves);
				}
				this.owner.position.MoveLast();
			}
			if (json.legal && json.legal.length > 0) {
				this.owner.position.AddLegalMoves(json.legal);			
			}
			
			this.owner.SetMessages(json.msg);
					
			if (json.quest) {
				if (!this.owner.InQuestion()) {
					this.owner.ShowQuestion(json.quest);
				}
			} else {
				this.owner.question = null;
			}
			
			this.owner.SetTime(json.wt, json.bt);
			
			this.owner.SetMode(json.mode);
		}
		this.frequency = this.owner.GetPollFreq();
		this.timer = this.onTimerEvent.bind(this).delay(this.frequency)
	},
	
	onTimerEvent: function() {		
		var request = this.commands.pop();
		if (!request || (request == undefined)) {
			request = { };
			request.gid = this.gameid;
			request.cmd = Chess.BOARD_COMMAND_POLL;
			request.mode = this.owner.script_mode;
			request.ply = this.owner.position.MaxListedMove();
			request.color = this.owner.my_color;			
		}
		this.options.parameters = request; 
		this.updater = new Ajax.Request(this.url, this.options);
	}
});


var BORDER_LAST_MOVE = 0;
var BORDER_THIS_MOVE = 1;

/**
 * Board
 **/
var Board = Class.create();
Board.prototype = {
	/**
	 * Инициализация класса
	 */ 
 	initialize : function(gameid, mode, type, id, pos) {
 		this.id = String(id ? "_" + id + "_" : "");
 		this.gameid = gameid;
 		this.script_mode = mode;
 		this.rating_type = type; 		
 		this.question = null;
 		this.LoadStart();
 		
 		this.boardElement = $("id-chess-board" + this.id);
 		this.boardElement.board = this;
 		
 		this.classes = Array("last", "move");
		this.highlights = new Array(2);
		this.highlights[BORDER_LAST_MOVE] = [];
		this.highlights[BORDER_THIS_MOVE] = [];
		
		this.my_color = NOCOLOR;
		this.game_result = Chess.RESULT_None;
		this.white_timer = $("id-white-timer");
 		this.black_timer = $("id-black-timer")
		
		this.lastmoves = new Array();
		this.listeners = new Array();
		
		this.colors = new Array("w", "b", "x");
		if (pos == null) {
			this.position = new ChessPosition(this);
			this.position.AddListener(this);
		} else {
			this.position = pos;
			this.position.owner = this;
			this.position.AddListener(this);
		}
		
		this.pieces = {};
		this.borders = {};
		this.squares = {};
		this.display = [];
		this.AddSquaresEvents();
		
		this.busy = false;
		this.updater = null;
		this.InPromotion = false;
		this.promotePiece = NOPIECE;
		
		// Список ходов
 		if ($("id-movelist-list" + this.id))
			new MoveList(this);
		// Битые фигуры
		if ($("id-captures-block" + this.id))	
			new Captures(this);
		// Форма хода
		this.moveForm = null;
		if ($("moveform" + this.id))
			this.moveForm = new MoveForm(this);
		// Чат (сообщения)
		if ($("id-board-pg-messages" + this.id))
			new GameChat({ owner: this, gameid: this.gameid, frequency: 15 });
		// Примечания
		if ($("id-board-pg-notes" + this.id))
			new GameNotes({owner: this, gameid: this.gameid});
		// Режимы
		this.modeForm = null;
		if ($("boardmode" + this.id))
			this.modeForm = new BoardModeForm(this);
		// Превращение пешки
		this.promotionForm = null;
		if ($("id-promote-panel" + this.id)) 
			this.promotionForm = new PromotePanel(this);
		
		this.aFrom = NULL_SQUARE;
		this.aTo = NULL_SQUARE;
		this.step = 0;
	},
	
	LoadStart : function() {
		Chess.enabled = false;
		if ($("id-wait-spinner")) {
			$("id-wait-spinner").show();
		}
	},
	
	LoadComplete : function() {
		if ($("id-wait-spinner")) {
			$("id-wait-spinner").hide();
		}
		this.position.MoveLast();
		this.updater = new BoardAjax(this);
		if (this.question) this.question.show();
		Chess.enabled = true;
	},
	
	SetColor : function(color) {
		this.my_color = color;
		if (this.promotionForm) {
			this.promotionForm.SetImages();
		}
	},
	
	SetResult : function(result) {
		this.game_result = result;
		this.position.game_result = result;
	},
	
	IsActiveMode : function() {
		return (this.script_mode >= Chess.BOARD_MODE_OBSERVE);
	},
	
	IsPlayMode : function() {
		return ((this.script_mode == Chess.BOARD_MODE_STDPLAY) || 
	    		(this.script_mode == Chess.BOARD_MODE_STDAJAX) ||
	    		(this.script_mode == Chess.BOARD_MODE_AJAX));
	},
	
	IsAjaxMode : function() {
		return ((this.script_mode == Chess.BOARD_MODE_STDAJAX) ||
	    		(this.script_mode == Chess.BOARD_MODE_AJAX));
	},

	GetPollFreq : function() {
		var frequency = 3600;
		var index = 0;
		var pmode = 0;
		if (this.script_mode >= Chess.BOARD_MODE_OBSERVE) {
			if (this.my_color != NOCOLOR) {
				if (this.my_color != this.position.whoMove) {
					index = 1; //Chess.PollFreq[this.owner.rating_type][1];
				} else {
					index = 0; //Chess.PollFreq[this.owner.rating_type][0];
				}	
				if (this.script_mode == Chess.BOARD_MODE_STDPLAY) {
					pmode = 1;
				} else if (this.script_mode == Chess.BOARD_MODE_AJAX) {
					pmode = 2;
				} else if (this.script_mode == Chess.BOARD_MODE_STDAJAX) {
					pmode = 3;
				}
			} else {
				pmode = this.rating_type;
				index = 1;
			}
			frequency = Chess.PollFreq[index][pmode]; 		
		}
		return frequency;
	},

	IsAnalizeMode : function() {
		return (this.script_mode == Chess.BOARD_MODE_ANALYZE);
	},	
	
	/** 
	 * Добавить вопрос
	 */
	AddQuestion : function(quest) {
		if (this.question == null)  
			this.question = new QuestionPanel(this, quest);
	},
	
	/**
	 * Показать вопрос
	 */
	ShowQuestion : function (quest) {
		this.AddQuestion(quest);
		this.question.show();
	},
	
	/**
	 * Вопрос активен
	 */
	InQuestion : function() {
		return (this.question != null);
	},
	
	/**
	 * Результат вопроса
	 */
	QuestionResult : function(result, id) {
		this.question = null;
		if (this.IsAjaxMode()) {
			if (id == Chess.BOARD_MESSAGE_CHALLENGE_ACCEPT) {
				this.HandleChallenge(result);
			} else if (id == Chess.BOARD_MESSAGE_JOIN_ACCEPT) {
				this.HandleJoin(result);
			} else if (id == Chess.BOARD_MESSAGE_DRAW_OFFER) {
				this.HandleDrawOffer(result);			
			}
		}		
	},
	
	ShowPromotion : function() {
		if (this.promotionForm) {
			this.InPromotion = true;
			this.promotionForm.show();
		}
	},
	
	HidePromotion : function() {
		if (this.promotionForm) {
			this.promotionForm.hide();
			this.InPromotion = false;
		}
	},
	
	piece : function(sq) {
		var p = this.pieces[sq];
		if (!p) {
			p = $("i" + this.id + sq);
			p.board = this;
			p.pos = sq;
			if (!Prototype.Browser.IE) {
				new Draggable(p, { 
					revert: true,
					onStart: function(drag, event) {
						var el = drag.element;
						if (el) {
							if (el.board.step == 1) el.board.ResetMove();
							if (el.board.step == 0) el.board.Click(el.pos);
						}
					}
				});
			}
			this.pieces[sq] = p;
		}
		return p;
	},
	
	square : function(sq) {
		var s = this.squares[sq];
		if (!s) {
			s = $("td" + this.id + sq);
			if (this.script_mode >= Chess.BOARD_MODE_ANALYZE) {
				s.board = this;
				s.pos = sq;
				if (!Prototype.Browser.IE) {
					Droppables.add(s, { 
    					accept: 'ibsq',
    					hoverclass: 'sqhover',
    					onDrop: function(dragPiece, targetSquare, event) {
    						if (targetSquare.board.CanMove(targetSquare.pos)) {
    							if (dragPiece.pos != targetSquare.pos) {
    								dragPiece.hide();
    							}
    							targetSquare.board.Click(targetSquare.pos);	
    						}
    					}
  					});
				}
				
				Event.observe(s, "mouseover", function(e) {
					this.board.MoveOverSquare(this, this.pos);
					return false;
				});
				Event.observe(s, "click", function(e) {
					this.board.ClickSquare(this.pos);
					return false;
				});
			}
			this.squares[sq] = s;
		}
		return s;
	},
	
	border : function(sq) {
		var b = this.borders[sq];
		if (!b) {
			b = $("dd" + this.id + sq);
			this.borders[sq] = b;
		}
		return b;
	},
	
	/**
	 * Назначить события на клетки доски
	 */
	AddSquaresEvents : function() {
		if (this.script_mode < Chess.BOARD_MODE_EXAMINE) return;
		for (var i = 0; i < 64; i++) {
			var im = this.piece(i);
			var border = this.border(i);
			var td = this.square(i);
			if (!td) window.alert("error");
		}
	},
	
	MoveOverSquare : function(el, pos) {
		if (this.CanMove(pos)) {
			el.style.cursor = "pointer";
		} else {
			el.style.cursor = "default";
		}
	},
	
	ClickSquare : function(pos) {
		this.Click(pos);
	},
	
	/**
 	* Добавить обработчик события хода
 	**/
	AddMoveListener : function(listener) {
		this.listeners.push(listener);
	},
	
	/**
 	* Вызывается при выполнении хода
 	*/
	OnMove : function(listener) {
		for (var i = 0; i < this.listeners.length; i++)	
			this.listeners[i].OnMove(this);
		//TODO: Board.OnMove - реализовать?
	},
	
	SetTime : function(wt, bt) {
		if (wt) this.white_timer.innerHTML = wt;
		if (bt) this.black_timer.innerHTML = bt;
	},
	
	/**
	 * Установить стиль таймера
	 */
	SetTimerStyle : function() {
		if (this.position.MoveColor(this.position.MaxListedMove()) == WHITE) {
			this.white_timer.addClassName("who_move");
			this.black_timer.removeClassName("who_move");
		} else {
			this.black_timer.addClassName("who_move");
			this.white_timer.removeClassName("who_move");
		}
	},
	
	SetMode : function(mode) {
		if (this.script_mode != mode) {
			this.script_mode = mode;
			this.modeForm.SetModeControls();
			if (this.updater) {
				this.updater.stop();
				if (this.script_mode >= Chess.BOARD_MODE_OBSERVE) {
					this.updater.start();
				}
			}
		}
	},

	/**
 	* Вызывается при изменении позиции
 	*/
	PositionChanged : function() {
		if (this.position.currentMove != this.position.GetPlyCount()) {
			this.step = this.IsAnalizeMode() ? 0 : -1;
		} else {
			this.step = 0;
		}
		this.DrawBoard();
		this.SetTimerStyle();
	},
	
	/**
 	* Подсветить клетку
 	*/
	HighlightSquare : function(pos, sel_index) {
		if (pos != NULL_SQUARE) {
			var border = this.border(pos);
			var prevClass = "";
			if (border.hasClassName("last")) {
				prevClass = "last"
				border.removeClassName("last");
			}
			this.highlights[sel_index].push({id: pos, cname: prevClass});
			border.addClassName(this.classes[sel_index]);
		}
	},
	
	/**
 	* Снять подсветку с клетки
 	*/
	UnHighlightSquare : function(sel_index) {
		while (this.highlights[sel_index].length > 0) {
			var old = this.highlights[sel_index].pop();
			var border = this.border(old.id);
			border.removeClassName("last");
			border.removeClassName("move");
			if (old.cname)
				border.addClassName(old.cname);
		}
	},
	
	/**
 	* Цвет рамки для ячейки
 	**/
	BorderColor : function(pos) {
		return (((lastmv[0] == pos) || (lastmv[1] == pos)) ? 'blue' : bgcolor[sqcol(pos)]);
	},
	
	OnResetMove : function() {
		this.step = 0;
		this.aFrom = NULL_SQUARE;
		this.aTo = NULL_SQUARE;
		this.UnHighlightSquare(BORDER_THIS_MOVE);
	},
	
	ResetMove : function() {
		if (MoveForm.HasForm()) {
			this.moveForm.ResetMove();
		} else {
			this.OnResetMove();
		}
	},
	
	OnResetMoves : function() {
		this.step = 0;
		this.HidePromotion();
		this.promotePiece = NOPIECE;
		this.aFrom = NULL_SQUARE;
		this.aTo = NULL_SQUARE;
		this.UnHighlightSquare(BORDER_THIS_MOVE);
		this.UnHighlightSquare(BORDER_LAST_MOVE);
		this.position.ResetMoves();
		this.position.MoveLast();
	},
	
	ResetMoves : function() {
		if (MoveForm.HasForm()) {
			this.moveForm.ResetMoves();
		} else {
			this.OnResetMoves();
		}
	},
	
	/**
	 * Возможно ли сделать ход
	 */
	CanMove : function(pos) {
		if (!Chess.enabled) return false;
		if (this.script_mode < Chess.BOARD_MODE_ANALYZE) return;
		if ((this.game_result > Chess.RESULT_None)) return false;
		if (this.InQuestion() && this.IsPlayMode()) return false;
		if (this.IsPlayMode() && (this.my_color != this.position.whoMove)) return false;
		if (this.step == 1) {
			return this.position.CanMove(this.aFrom, pos);
		} else if (this.step == 0) {
			return this.position.CanMove(pos, NULL_SQUARE);
		}	
		return false;
	},
	
	/**
	 * Установить системное сообщение
	 */
	SetMessages : function(msgs) {
		if (!$("id-game-msgs")) return;
		if (!msgs || !msgs.length) {
			msgs = [];	
		}
		amsgs = $A(msgs);		
		mhtml = "";
		for (var i = 0; i < amsgs.length; i++) {
			mhtml = mhtml + "<div>" + amsgs[i] + "</div>";
		}
		$("id-game-msgs").innerHTML = mhtml;
	},
	
	/**
	 * Щелчок по доске
	 */
	Click : function(pos) {
		if (this.InQuestion() && this.IsPlayMode()) {
			this.question.show();
			return;
		}
		if (this.InPromotion) return;
		
		if (this.step == 0) {
			if (!this.CanMove(pos)) return;
			if (this.aFrom != NULL_SQUARE) this.UnHighlightSquare(BORDER_THIS_MOVE);
			
			if (MoveForm.HasForm())
				this.moveForm.SetFrom(this.position.Pos2Note(pos));
			this.step++;
			this.aFrom = pos;
			this.HighlightSquare(this.aFrom, BORDER_THIS_MOVE)
		} else if (this.step == 1) {
			if (pos == this.aFrom) { this.ResetMove(); return;}
			if (!this.CanMove(pos)) { this.ResetMove(); return;}
			this.step++;
			this.aTo = pos;
			this.HighlightSquare(this.aTo, BORDER_THIS_MOVE);
			this.position.AddMove(this.aFrom, this.aTo, NOPIECE);
			
			if (MoveForm.HasForm())
				this.moveForm.SetTo(this.position.Pos2Note(pos));
							
			if (this.script_mode == Chess.BOARD_MODE_AJAX) {
				if (!this.InPromotion) {
					this.position.ClearLegalMoves();
					this.SendMove();
				}				
			}
			if (this.script_mode == Chess.BOARD_MODE_ANALYZE) {
				this.step = 0;
			}
		}
	},
	
	ApplyPromote : function(piece) {
		if (this.InPromotion) {
			this.promotePiece = piece;
			this.HidePromotion();
			if (this.script_mode == Chess.BOARD_MODE_AJAX) {
				this.position.ClearLegalMoves();
				this.SendMove();
			} else if (this.IsPlayMode()) {
				if (MoveForm.HasForm())
					this.moveForm.SetPromo(this.promotePiece);
				this.position.MoveBackward();
				this.position.AddMove(this.aFrom, this.aTo, this.promotePiece);
			} else if (this.IsAnalizeMode()) {
				this.position.MoveBackward();
				this.position.AddMove(this.aFrom, this.aTo, this.promotePiece);
			}
		}
	},
	
	SendMove : function() {
		var request = { };
		request.gid = this.gameid;
		request.cmd = Chess.BOARD_COMMAND_MOVE;
		request.mode = this.script_mode;
		request.ply = this.position.GetPlyCount();
		request.color = this.my_color;
		request.fr = this.position.Pos2Note(this.aFrom);
		request.to = this.position.Pos2Note(this.aTo);
		request.promote = this.promotePiece;
		request.draw = 0;
		if (this.moveForm) {
			request.draw = this.moveForm.drawBox.checked ? 1 : 0;
		}	
		this.updater.SendImmediate(request);
		
		if (this.moveForm)
			this.moveForm.clear();
	},
	
	/**
	 * Обработчик приема вызова
	 */
	HandleChallenge : function(result) {
		var request = { };
		request.gid = this.gameid;
		request.cmd = result ? Chess.BOARD_COMMAND_ACCEPT_CHLG : Chess.BOARD_COMMAND_REJECT_CHLG;
		request.mode = this.script_mode;
		request.ply = this.position.GetPlyCount();
		request.color = this.my_color;
		this.updater.SendImmediate(request);
	},
	
	/**
	 * Обработчик приема вызова
	 */
	HandleJoin : function(result) {
		var request = { };
		request.gid = this.gameid;
		request.cmd = result ? Chess.BOARD_COMMAND_ACCEPT_JOIN : Chess.BOARD_COMMAND_REJECT_JOIN;
		request.mode = this.script_mode;
		request.ply = this.position.GetPlyCount();
		request.color = this.my_color;
		this.updater.SendImmediate(request);
	},
	
	/**
	 * Обработчик приема ничьей
	 */
	HandleDrawOffer : function(result) {
		var request = { };
		request.gid = this.gameid;
		request.cmd = result ? Chess.BOARD_COMMAND_ACCEPT_DRAW : Chess.BOARD_COMMAND_REJECT_DRAW;
		request.mode = this.script_mode;
		request.ply = this.position.GetPlyCount();
		request.color = this.my_color;
		this.updater.SendImmediate(request);
	},
	
	DrawSquare : function(sq) {
		var p = this.position.piece(sq);		
		if (p != this.display[sq]) {
			this.display[sq] = p;		
  			var img = this.piece(sq);  		
  			if (img) {
  				if (p != NOPIECE) {
  					var sqcol = this.colors[square_Color(sq)];
	  				var src = Chess.IMG_ROOT + Piece.names[p] + sqcol + Chess.IMG_EXT;
  				
  					if ((Chess.IMG_EXT == ".png") && Chess.NeedAlphaLoader && img.runtimeStyle) {
  						img.src = "/images/pix.gif";
  						img.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"
 							+ src
 							+ "',sizingMethod='scale')";
	  				} else {
  						img.src = src;	
	  				}  						
  					img.style.display = "block";
  				} else {
  					img.style.display = "none";
	  			}
  			}  			
  		}
	},
	
	DrawBoard : function() {
		this.UnHighlightSquare(BORDER_THIS_MOVE);
		this.UnHighlightSquare(BORDER_LAST_MOVE);
		
		for(var i = 0; i < NULL_SQUARE - 1; i++)
			this.DrawSquare(i);
			
		if (this.position.GetPlyCount() > 0) {
			var dm = this.position.DisplayCurrentMove() - 1;
			var cm = this.position.currentMove - 1;
			if (this.IsPlayMode()) {
				if (dm >= 0) {
					this.HighlightSquare(this.position.MoveFrom(dm), BORDER_LAST_MOVE);
					this.HighlightSquare(this.position.MoveTo(dm), BORDER_LAST_MOVE);
				}
				if ((cm != dm) && (cm >= 0)) {
					this.HighlightSquare(this.position.MoveFrom(cm), BORDER_THIS_MOVE);
					this.HighlightSquare(this.position.MoveTo(cm), BORDER_THIS_MOVE);	
				}			
			} else {
				this.HighlightSquare(this.position.MoveFrom(cm), BORDER_LAST_MOVE);
				this.HighlightSquare(this.position.MoveTo(cm), BORDER_LAST_MOVE);
			}
		}
	}	
}

Board.__GetElement = function(id) {
	return $("id-chess-board" + id);
}

Board.Prepare = function(gameid, mode, type, id) {
	id = id ? id : "";
	return new Board(gameid, mode, type, id, null);
}

function dummy() {
	// dummy
}

function reset_move() {
	if (aboard)
		aboard.ResetMoves();
} 

/**
* :))))))))))))
*/
function WinGame() {
	if (window.confirm("Вы действительно хотите выиграть эту партию просто так?")) {
		if (window.confirm("Подумайте еще раз! Ваш соперник наверное будет очень расстроен таким исходом.\nПродолжить присуждение Вам выигрыша?")) {
			window.alert("Конечно же НЕТ! Здесь ЧЕСТНАЯ игра!\nОтчет о вашем неспортивном поведении отправлен на рассмотрение Арбитража сайта!");
			window.alert("А вообще, все это шутка!\nСмех продлевает жизнь\nС уважением, Администрация портала :)");
		}
	}
}