<?xml version="1.0" encoding="UTF-8"?>

<!-- Copyright 2009 Google Inc. -->
<!-- All Rights Reserved -->

<Module>
  <ModulePrefs title="Chat Chess" width="229" height="230">
    <Optional feature="content-rewrite">
      <Param name="include-tags"/>
    </Optional>
    <Require feature="google.sharedstate"/>
  </ModulePrefs>

  <Content type="html">
    <![CDATA[
    </script>
    <style type="text/css">
      body {
        font-family: 'lucida grande','tahoma','arial','verdana','sans-serif';
        font-size: 11px;
      }
      td {
        font-size: 11px;
      }
      a {
        text-decoration: none;
        color: #3366CC;
      }
      a:visited {
        color: #3366CC;
      }
      a:hover {
        color: red;
      }
      #canvas {
        width: 223px;
        height: 203px;
      }
      #board {
        background-image: url('http://code.google.com/apis/talk/examples/chess/board.gif');
        background-position: left;
        width: 160px;
        height: 160px;
        position: relative;
      }
      #promotion, #selectColor, #help {
        border: 1px solid black;
        width: 128px;
        height: 40px;
        left: 16px;
        top: 60px;
        position: absolute;
        background-color: white;
      }
      #help {
        width: 160px;
        height: 160px;
        left: 0px;
        top: 0px;
      }
      .hide {
        display: none;
      }
      .piece {
        border: 0;
        width: 20px;
        height: 20px;
        position: absolute;
      }
      .cursor {
        border: 2px solid yellow;
        width: 20px;
        height: 20px;
        position: absolute;
      }
      .expand {
        width: 100%;
        height: 100%;
      }
      .selectable {
        cursor: pointer;
      }
    </style>
    <script type="text/javascript">
      HOST = 'http://code.google.com/apis/talk/examples/chess';
      BOARD_SIZE = 160;
      SQUARE_SIZE = 20;
      CURSOR_BORDER = 2;
      EMPTY_SQUARE = '.';
      COLOR = {
        'white': 0,
        'black': 1
      };
      PIECE = {
        'pawn': 0,
        'knight': 1,
        'bishop': 2,
        'rook': 3,
        'queen': 4,
        'king': 5
      };
      PIECE_MAP = {
        'p': PIECE.pawn,
        'n': PIECE.knight,
        'b': PIECE.bishop,
        'r': PIECE.rook,
        'q': PIECE.queen,
        'k': PIECE.king
      };

      DEFAULT_BOARD = [
        'RNBQKBNR'.split(''),
        'PPPPPPPP'.split(''),
        '........'.split(''),
        '........'.split(''),
        '........'.split(''),
        '........'.split(''),
        'pppppppp'.split(''),
        'rnbqkbnr'.split('')
      ];

      var MOVE_COMPASS = [
        [-1,-1], [0,-1], [1,-1], [1,0],
        [1,1], [0,1], [-1,1], [-1,0]
      ];

      capturedPieces = [];

      board = null;
      playerId = undefined;
      opponentId = undefined;

      gameState = {
        board: null,
        moveHistory: [],
        current: COLOR.white,
        winner: -1,
        white: undefined
      };

      window.$ = function(id) {
        return document.getElementById(id);
      };

      function showElement(name) {
        $(name).className = '';
      }

      function hideElement(name) {
        $(name).className = 'hide';
      }

      function clearChildren(el) {
        while (el.firstChild) {
          el.removeChild(el.firstChild);
        }
      }

      function Piece(color, type) {
        this.color = color;
        this.type = type;
      }

      function Move(piece, from, to) {
        this.piece = piece;
        this.from = from;
        this.to = to;
      }

      function recordMove(piece, from, to) {
        var move = new Move(piece, from, to);
        gameState.moveHistory.push(move);
      }

      function hasKingMoved(color) {
        for (var i = 0; i < gameState.moveHistory.length; ++i) {
          var move = gameState.moveHistory[i];
          if (move.piece.color == color &&
              move.piece.type == PIECE.king) {
            return true;
          }
        }
        return false;
      }

      function capturePiece(code) {
        capturedPieces.push(code);
      }

      function isMyTurn() {
        return !self.gameOver && gameState.current == getMyColor();
      }

      function newPos(x, y) {
        return {'x': x, 'y': y};
      }

      function newCoords(row, col) {
        return {'row' : row, 'col': col};
      }

      function coordsToPos(coords) {
        var x = SQUARE_SIZE * coords.col;
        var y = SQUARE_SIZE * coords.row;
        return newPos(x, y);
      }

      function posToCoords(pos) {
        boardPos = getElementPos(board.root);
        var y = (pos.y - boardPos.y) / SQUARE_SIZE;
        var x = (pos.x - boardPos.x) / SQUARE_SIZE;
        y = Math.floor(y);
        x = Math.floor(x);
        return newCoords(y, x);
      }

      function coordsValid(c) {
        return c.col >= 0 && c.col <= 7 &&
               c.row >= 0 && c.row <= 7;
      }

      function coordsEqual(a, b) {
        if (a && b) {
          return a.row == b.row && a.col == b.col;
        }
        return false;
      }

      function setPos(el, pos) {
        el.style.left = pos.x + 'px';
        el.style.top = pos.y + 'px';
      }

      function applyRotation(coords) {
        if (getMyColor() == COLOR.black) {
          coords.col = 7 - coords.col;
          coords.row = 7 - coords.row;
        }
      }

      function codeToImage(code) {
        var img = document.createElement('img');
        var color = getColor(code);
        var src = (color == COLOR.white ? 'w' : 'b');
        src += code.toLowerCase() + '.gif';
        img.src = HOST + '/' + src;
        img.className = 'piece';
        return img;
      }

      function getColor(ch) {
        if (ch >= 'A' && ch <= 'Z') {
          return COLOR.black;
        } else if (ch >= 'a' && ch <= 'z') {
          return COLOR.white;
        }
        throw 'Error: getColor()';
      }

      function getType(ch) {
        return PIECE_MAP[ch.toLowerCase()];
      }

      function getOtherColor(color) {
        return color == COLOR.white ? COLOR.black : COLOR.white;
      }

      function getElementPos(el) {
        var x = 0;
        var y = 0;
        if (el.offsetParent) {
          do {
            x += el.offsetLeft;
            y += el.offsetTop;
          } while (el = el.offsetParent);
        }
        return newPos(x, y);
      }

      function getMousePos(e) {
      	var x = 0;
      	var y = 0;
      	if (!e) {
      	  e = window.event;
        }
      	if (e.pageX || e.pageY) {
      		x = e.pageX;
      		y = e.pageY;
      	}
      	else if (e.clientX || e.clientY) {
      		x = e.clientX + document.body.scrollLeft
      			  + document.documentElement.scrollLeft;
      		y = e.clientY + document.body.scrollTop
      			  + document.documentElement.scrollTop;
      	}
      	return newPos(x, y)
      }

      function changePointer(type) {
        document.body.style.cursor = type;
      }

      function setInfo(msg) {
        $('info').innerHTML = msg;
      }

      function selectColor(color) {
        if (color == COLOR.white) {
          gameState.white = playerId;
        } else {
          gameState.white = opponentId;
        }
        updateGameState();
      }

      function getMyColor() {
        return gameState.white == playerId ? COLOR.white : COLOR.black;
      }

      function getOpponentColor() {
        return gameState.white == playerId ? COLOR.black : COLOR.white;
      }

      function Board() {
        var self = this;
        this.root = $('board');
        clearChildren(this.root);

        this.setState(DEFAULT_BOARD);
        this.cursor = null;
        this.promoteCoords = null;
        this.gameOver = false;
      }

      Board.onClick = function(e) {
        if (!board) {
          return;
        }
        if (!isMyTurn() || board.isPromoting()) {
          return;
        }
        var pos = getMousePos(e);
        coords = posToCoords(pos);
        applyRotation(coords);
        board.onSelectSquare(coords);
      };

      Board.onMouseMove = function(e) {
        if (!board) {
          return;
        }
        if (!isMyTurn() || board.isPromoting()) {
          return;
        }
        if (!isMyTurn()) {
          return false;
        }
        var pos = getMousePos(e);
        coords = posToCoords(pos);
        if (coordsValid(coords)) {
          applyRotation(coords);
          var piece = board.getPiece(coords);
          var pointer = 'default';
          if (piece && piece.color == gameState.current) {
            pointer = 'pointer';
          }
          changePointer(pointer);
        }
      };

      Board.onMouseOut = function(e) {
        changePointer('default');
      };

      Board.prototype.setState = function(state) {
        this.state = [];
        for (var i = 0; i < 8; ++i) {
          var row = [];
          for (var j = 0; j < 8; ++j) {
            row.push(state[i][j]);
          }
          this.state.push(row);
        }
      }

      Board.prototype.isPromoting = function() {
        return this.promoteCoords != null;
      }

      Board.prototype.getPiece = function(coords) {
        var square = this.state[coords.row][coords.col];
        if (square == EMPTY_SQUARE) {
          return null;
        }
        return new Piece(getColor(square), getType(square));
      };

      Board.prototype.onSelectSquare = function(coords) {
        var prev = this.cursor;
        var piece = this.getPiece(coords);
        // If a square was selected.
        if (prev) {
          if (coordsEqual(prev, coords)) {
            // Deselect.
            this.cursor = null;
          } else {
            var prevPiece = this.getPiece(this.cursor);
            if (piece) {
              if (prevPiece.color == piece.color) {
                // Selecting a new piece.
                this.cursor = coords;
              } else {
                // Attacking another piece.
                this.attemptMove(prev, coords);
              }
            } else {
              // Moving to an empty square.
              this.attemptMove(prev, coords);
            }
          }
        } else {
          if (piece && piece.color == gameState.current) {
            this.cursor = coords;
          }
        }
        this.render();
      };

      Board.prototype.isCheckMove = function(from, to) {
        var piece = this.getPiece(from);
        var square = this.state[from.row][from.col];
        var prev = this.state[to.row][to.col];
        // Simulate move.
        this.state[from.row][from.col] = EMPTY_SQUARE;
        this.state[to.row][to.col] = square;
        var result = this.isInCheck(piece.color);
        // Revert back.
        this.state[from.row][from.col] = square;
        this.state[to.row][to.col] = prev;
        return result;
      };

      Board.prototype.attemptMove = function(from, to) {
        if (this.isValidMove(from, to)) {
          // Does this move put our king into check?
          if (!this.isCheckMove(from, to)) {
            this.executeMove(from, to);
          }
          return true;
        }
        return false;
      };

      Board.prototype.isForwardMove = function(piece, from, to) {
        if (piece.color == COLOR.black) {
          if (from.row == 1) {
            return to.row == 2 || to.row == 3;
          } else {
            return to.row == from.row + 1;
          }
        } else {
          if (from.row == 6) {
            return to.row == 5 || to.row == 4;
          } else {
            return to.row == from.row - 1;
          }
        }
        return false;
      };

      Board.prototype.checkEnPassant = function(from, to) {
        var otherPiece = this.getPiece(to);
        if (otherPiece) {
          return null;
        }
        if (gameState.moveHistory.length == 0) {
          return null;
        }
        var move = gameState.moveHistory[gameState.moveHistory.length - 1];
        if (move.piece.type != PIECE.pawn ||
            move.piece.color == gameState.current) {
          return null;
        }
        if (move.from.col == to.col) {
          if (Math.abs(move.to.row - move.from.row) == 2) {
            if (gameState.current == COLOR.black && to.row == 5) {
              return move.to;
            } else if (gameState.current == COLOR.white && to.row == 2) {
              return move.to;
            }
          }
        }
        return null;
      };

      Board.prototype.isCastle = function(from, to) {
        var piece = this.getPiece(from);
        if (piece.type != PIECE.king) {
          return false;
        }
        if (hasKingMoved(piece.color)) {
          return false;
        }
        var rookFile;
        if (to.col == from.col + 2) {
          rookFile = 7;
        } else if (to.col == from.col - 2) {
          rookFile = 0;
        } else {
          return false;
        }
        var piece = this.getPiece(newCoords(from.row, rookFile));
        if (piece.color != gameState.current && piece.type != PIECE.rook) {
          return false;
        }
        // Now, are there any pieces in the way?
        if (!this.isRankClear(from, rookFile)) {
          return false;
        }
        var hd = to.col > from.col ? 1 : -1;
        var c = newCoords(from.row, from.col);
        for (var i = 0; i < 3; ++i) {
          if (this.isSquareThreatened(c, getOtherColor(piece.color))) {
            return false;
          }
          c.col += hd;
        }
        return true;
      };

      Board.prototype.isPromotion = function(from, to) {
        var piece = this.getPiece(to);
        if (piece && piece.type == PIECE.pawn) {
          if (to.row == 0 || to.row == 7) {
            return true;
          }
        }
        return false;
      };

      Board.prototype.isValidMove = function(from, to) {
        var piece = this.getPiece(from);

        // Do not move onto one of our own.
        var otherPiece = this.getPiece(to);
        if (otherPiece && piece.color == otherPiece.color) {
          return false;
        }

        if (piece.type == PIECE.pawn) {
          // Pawn movement.
          if (this.isForwardMove(piece, from, to)) {
            if (from.col == to.col) {
              // Forward movement.
              if (this.isFileClear(from, to.row) &&
                  !this.getPiece(to)) {
                return true;      
              }
            } else {
              if (Math.abs(to.col - from.col) == 1 &&
                  Math.abs(to.row - from.row) == 1) {
                if (this.getPiece(to)) {
                  return true;
                } else {
                  if (this.checkEnPassant(from, to)) {
                    return true;
                  }
                }
              }
            }
          }
        } else if (piece.type == PIECE.knight) {
          // Knight movement.
          var vd = to.row - from.row;
          var hd = to.col - from.col;
          var legal = false;
          legal = (Math.abs(hd) == 2 && Math.abs(vd) == 1) ||
                  (Math.abs(hd) == 1 && Math.abs(vd) == 2);
          if (legal) {
            return true;
          }          
        } else if (piece.type == PIECE.bishop) {
          // Bishop movement.
          if (this.isDiagonalMove(from, to)) {
            return this.isDiagonalClear(from, to);
          }          
        } else if (piece.type == PIECE.rook) {
          // Rook movement.
          if (from.col == to.col) {
            return this.isFileClear(from, to.row);
          } else if (from.row == to.row) {
            return this.isRankClear(from, to.col);
          }          
        } else if (piece.type == PIECE.queen) {
          // Queen movement.
          if (this.isDiagonalMove(from, to)) {
            return this.isDiagonalClear(from, to);
          } else if (from.col == to.col) {
            return this.isFileClear(from, to.row);
          } else if (from.row == to.row) {
            return this.isRankClear(from, to.col);
          }          
        } else if (piece.type == PIECE.king) {
          // King movement.
          if (Math.abs(to.row - from.row) <= 1 &&
              Math.abs(to.col - from.col) <= 1) {
            return true;
          } else if (from.row == to.row) {
            // Check castling.
            if (this.isCastle(from, to)) {
              return true;
            }
          }
        }
        return false;
      };

      Board.prototype.executeMove = function(from, to) {
        var piece = this.getPiece(from);
        var captured = this.getPiece(to);

        // Was this en passant?
        if (!captured) {
          var coords = this.checkEnPassant(from, to);
          if (coords) {
            capturePiece(this.state[coords.row][coords.col]);
            this.state[coords.row][coords.col] = EMPTY_SQUARE;
          }
        }

        // Was this a castle?
        if (this.isCastle(from, to)) {
          var rookFrom;
          var rookTo;
          if (to.col == 6) {
            rookFrom = newCoords(to.row, 7);
            rookTo = newCoords(to.row, 5);
          } else {
            rookFrom = newCoords(to.row, 0);
            rookTo = newCoords(to.row, 3);
          }
          this.state[rookTo.row][rookTo.col] = 
              this.state[rookFrom.row][rookFrom.col];
          this.state[rookFrom.row][rookFrom.col] = EMPTY_SQUARE;
        }

        if (captured) {
          capturePiece(this.state[to.row][to.col]);
        }
        this.state[to.row][to.col] = this.state[from.row][from.col];
        this.state[from.row][from.col] = EMPTY_SQUARE;
        this.cursor = null;
        recordMove(piece, from, to);

        // If this is a promotion, throw up the dialog but don't end
        // the turn.
        if (this.isPromotion(from, to)) {
          this.promoteCoords = to;
          this.render();
          return;
        }

        this.endTurn();
      };

      Board.prototype.onPromote = function(code) {
        this.state[this.promoteCoords.row][this.promoteCoords.col] = code;
        this.promoteCoords = null;
        this.render();
        this.endTurn();
      };

      Board.prototype.endTurn = function() {
        if (this.isInCheckMate(getOtherColor(gameState.current))) {
          gameState.winner = gameState.current;
        } else {
          gameState.current = getOtherColor(gameState.current);
        }

        updateGameState();
      };

      Board.prototype.isSquareThreatened = function(coords, color) {
        for (var i = 0; i < 8; ++i) {
          for (var j = 0; j < 8; ++j) {
            var c = newCoords(i, j);
            var piece = this.getPiece(c);
            if (piece && piece.color == color) {
              if (this.isValidMove(c, coords)) {
                return true;
              }
            }
          }
        }
        return false;
      };

      Board.prototype.getKingCoords = function(color) {
        var kingCoords = null;
        for (var i = 0; i < 8; ++i) {
          for (var j = 0; j < 8; ++j) {
            var piece = this.getPiece(newCoords(i, j));
            if (piece && piece.color == color && piece.type == PIECE.king) {
              kingCoords = newCoords(i, j);
            }
          }
        }
        if (!kingCoords) {
          throw 'Error: getKingCoords()';
        }
        return kingCoords;
      }

      Board.prototype.isInCheck = function(color) {
        var kingCoords = this.getKingCoords(color);
        return this.isSquareThreatened(kingCoords, getOtherColor(color));
      };

      Board.prototype.isInCheckMate = function(color) {
        // First, are we in check?
        if (!this.isInCheck(color)) {
          return false;
        }

        // Now, can we move safely out of the way?
        var kingCoords = this.getKingCoords(color);
        for (var i = 0; i < 8; ++i) {
          var to = newCoords(kingCoords.row, kingCoords.col);
          to.col += MOVE_COMPASS[i][0];
          to.row += MOVE_COMPASS[i][1];
          if (!coordsValid(to)) {
            continue;
          }

          if (this.isValidMove(kingCoords, to)) {
            // Does this move put our king into check?
            if (!this.isCheckMove(kingCoords, to)) {
              return false;
            }
          }
        }

        // Finally, can we block?
        // This is brute-force, basically check all possible valid moves
        // of our own pieces and if any take us out of check, it is not
        // checkmate.
        var fromList = [];
        var toList = [];
        for (var i = 0; i < 8; ++i) {
          for (var j = 0; j < 8; ++j) {
            var piece = this.getPiece(newCoords(i, j));
            if (piece && piece.color == color) {
              fromList.push(newCoords(i, j));
            } else {
              toList.push(newCoords(i, j));
            }
          }
        }
        // Now try all combinations.
        for (var i = 0; i < fromList.length; ++i) {
          for (var j = 0; j < toList.length; ++j) {
            var from = fromList[i];
            var to = toList[j];
            if (this.isValidMove(from, to)) {
              if (!this.isCheckMove(from, to)) {
                // We can weasle out of this one.
                return false;
              }
            }
          }
        }
        return true;
      };

      Board.prototype.clearBoard = function() {
        $('void').appendChild($('promotion'));
        $('void').appendChild($('selectColor'));
        clearChildren(this.root);
      }

      function createPromoteFunction(code) {
        return function() {
          board.onPromote(code);
        };
      }

      Board.prototype.render = function() {
        this.clearBoard();

        for (var i = 0; i < 8; ++i) {
          for (var j = 0; j < 8; ++j) {
            var ch = this.state[i][j];
            if (ch == EMPTY_SQUARE) {
              continue;
            }
            var img = codeToImage(ch);
            var coords = newCoords(i, j);
            applyRotation(coords);
            setPos(img, coordsToPos(coords));
            this.root.appendChild(img);
          }
        }

        if (this.cursor) {
          var coords = newCoords(this.cursor.row, this.cursor.col);
          applyRotation(coords);
          pos = coordsToPos(coords);
          pos.x -= CURSOR_BORDER;
          pos.y -= CURSOR_BORDER;
          cursor = document.createElement('div');
          cursor.className = 'cursor';
          setPos(cursor, pos);
          this.root.appendChild(cursor);
        }

        var pos = newPos(-20, 0);
        // Render the last 16 captured (in case there's some fishy playing
        // going on with pawn promotions.)
        var start = Math.max(0, capturedPieces.length - 16);
        var count = 0;
        for (var i = start; i < capturedPieces.length; ++i) {
          if (++count == 8) {
            pos.x = BOARD_SIZE;
            pos.y = 0;
          }
          var img = codeToImage(capturedPieces[i]);
          setPos(img, pos);
          this.root.appendChild(img);
          pos.y += 20;
        }

        if (this.isPromoting()) {
          var el = $('promotion');
          var pieces = gameState.current == COLOR.white ? 'qrbn' : 'QRBN';
          for (var i = 0; i < 4; ++i) {
            var code = pieces.charAt(i);
            var img = codeToImage(code);
            img.className += ' selectable';
            var self = img;
            img.addEventListener('click', createPromoteFunction(code), false);
            var slot = $('slot' + i);
            clearChildren(slot);
            slot.appendChild(img);
          }
          this.root.appendChild(el);
        }

        if (!gameState.white) {
          this.root.appendChild($('selectColor'));
          hideElement('resign');
        } else {
          showElement('resign');
        }

        var msg = '';
        if (gameState.winner != -1) {
          msg = gameState.winner == COLOR.white ? 'White' : 'Black';
          msg += ' wins!';
          hideElement('resign');
          showElement('rematch');
        } else {
          if (gameState.current == COLOR.white) {
            msg = 'White to Move';
          } else {
            msg = 'Black to Move';
          }
          if (this.isInCheck(gameState.current)) {
            msg += ' - Check';
          }
          hideElement('rematch');
        }
        setInfo(msg);
      };

      Board.prototype.isDiagonalMove = function(from, to) {
        var hd = to.col - from.col;
        var vd = to.row - from.row;
        return Math.abs(hd) == Math.abs(vd);
      };

      Board.prototype.isDiagonalClear = function(from, to) {
        var hd = from.col < to.col ? 1 : -1;
        var vd = from.row < to.row ? 1 : -1;
        var c = newCoords(from.row, from.col);
        c.col += hd;
        c.row += vd;
        while (!coordsEqual(c, to)) {
          if (this.getPiece(c)) {
            return false;
          }
          c.col += hd;
          c.row += vd;
        }
        return true;
      };

      Board.prototype.isFileClear = function(from, toRank) {
        if (from.row < toRank) {
          for (var i = from.row + 1; i < toRank; ++i) {
            if (this.getPiece(newCoords(i, from.col))) {
              return false;
            }
          }
        } else {
          for (var i = from.row - 1; i > toRank; --i) {
            if (this.getPiece(newCoords(i, from.col))) {
              return false;
            }
          }
        }
        return true;
      };

      Board.prototype.isRankClear = function(from, toFile) {
        if (from.col < toFile) {
          for (var i = from.col + 1; i < toFile; ++i) {
            if (this.getPiece(newCoords(from.row, i))) {
              return false;
            }
          }
        } else {
          for (var i = from.col - 1; i > toFile; --i) {
            if (this.getPiece(newCoords(from.row, i))) {
              return false;
            }
          }
        }
        return true;
      };

      function resign() {
        gameState.winner = getOpponentColor();
        updateGameState();
      }

      function rematch() {
        resetGame();
        updateGameState();
      }

      function resetGame() {
        gameState.current = COLOR.white;
        gameState.moveHistory = [];
        gameState.winner = -1;
        gameState.white = undefined;
        capturedPieces = [];
        board = new Board();
        board.render();
      }

      function startGame(myId, otherIds) {
        playerId = myId;
        opponentId = otherIds[0];
        resetGame();
      }

      function onStateChange(state) {
        if (!state) {
          return;
        }

        gameState = state;
        board.setState(state.board);
        board.render();
      }

      function updateGameState() {
        gameState.board = board.state;
        gadgets.sharedstate.updateState(gameState);
      }

      function init() {
        gadgets.sharedstate.initialize(startGame);
        gadgets.sharedstate.setStateChangeHandler(onStateChange);
        var board = $('board');
        if (board.addEventListener) {
          board.addEventListener('click', Board.onClick, false);
          board.addEventListener('mousemove', Board.onMouseMove,
                                      false);
          board.addEventListener('mouseout', Board.onMouseOut,
                                      false);
        } else {
          board.attachEvent('onclick', Board.onClick);
          board.attachEvent('onmousemove', Board.onMouseMove);
          board.attachEvent('onmouseout', Board.onMouseOut);
        }
      }

      gadgets.util.registerOnLoadHandler(init);
    </script>

    <div id="canvas">
      <table class="expand" border="0" cellpadding="0" cellspacing="0">
        <tr>
          <td align="center" valign="center">
            <table border="0">
              <tr>
                <td align="right" valign="top">
                  <a id="resign" class="hide" href="#" onclick="resign(); return false;">Resign</a>
                  <a id="rematch" class="hide" href="#" onclick="rematch(); return false;">Rematch</a>
                </td>
              </tr>
              <tr>
                <td>
                  <div id="board">
                </td>
              </tr>
            </table>
          </td>
        </tr>
        <tr>
          <tr>
            <td align="center" valign="top">
              <div id="info">White's Move</div>
            </td>
        </tr>
      </table>
      </div>
    </div>
    <div id="void" class="hide">
      <div id="promotion">
        <center>
          Choose promotion:</br>
          <table>
            <tr>
              <td width="20" id="slot0"></td>
              <td width="20" id="slot1"></td>
              <td width="20" id="slot2"></td>
              <td width="20" id="slot3"></td>
            </tr>
          </table>
        </center>
      </div>
      <div id="selectColor">
        <center>
          Choose color:</br>
          <table>
            <tr>
              <td width="20">
                <img class="selectable piece" onclick="selectColor(COLOR.white);" src="http://code.google.com/apis/talk/examples/chess/wk.gif"/>
              </td>
              <td width="20">
                <img class="selectable piece" onclick="selectColor(COLOR.black);" src="http://code.google.com/apis/talk/examples/chess/bk.gif"/>
              </td>
            </tr>
          </table>
        </center>
      </div>
    </div>
    ]]>
  </Content>
</Module>
