import React from 'react/addons';
import omit from 'lodash.omit';
import cx from 'classnames';
import {Seq, Repeat, List, Set} from 'immutable';
import GameStore from '../stores/GameStore';
import GameActions from '../actions/GameActions';
import ChessPieces from '../constants/ChessPieces';
import onGameChange from '../mixins/onGameChange';
import maybeReverse from '../mixins/maybeReverse';
const FILES = Seq.Indexed('abcdefgh');
const RANKS = Seq.Indexed('12345678');
const Chessboard = React.createClass({
propTypes: {
io: React.PropTypes.object.isRequired,
token: React.PropTypes.string.isRequired,
maybePlaySound: React.PropTypes.func.isRequired,
color: React.PropTypes.oneOf(['white', 'black']).isRequired,
gameOver: React.PropTypes.bool.isRequired,
isOpponentAvailable: React.PropTypes.bool.isRequired
},
mixins: [React.addons.PureRenderMixin, maybeReverse],
getInitialState() {
const state = GameStore.getChessboardState();
return {
fen: state.fen,
moveFrom: null,
lastMove: state.lastMove,
kingInCheck: false
};
},
componentDidMount() {
const {io, token} = this.props;
GameStore.on('change', this._onGameChange);
GameStore.on('new-move', this._onNewMove);
io.on('move', data => {
GameActions.makeMove(data.from, data.to, data.capture, false);
this.props.maybePlaySound();
if (!data.gameOver) {
this._runClock();
}
if (document.hidden) {
let title = document.getElementsByTagName('title')[0];
title.text = '* ' + title.text;
window.addEventListener('focus', this._removeAsteriskFromTitle);
}
});
io.on('rematch-accepted', () => this.setState({moveFrom: null}));
},
componentWillUnmount() {
GameStore.off('change', this._onGameChange);
GameStore.on('new-move', this._onNewMove);
},
render() {
const {color, isOpponentAvailable, gameOver} = this.props;
const {fen, moveFrom, lastMove, kingInCheck} = this.state;
const fenArray = fen.split(' ');
const placement = fenArray[0];
const isItMyTurn = fenArray[1] === color.charAt(0);
const rows = this._maybeReverse(placement.split('/'));
const ranks = this._maybeReverse(RANKS, 'white');
return (
{rows.map((placement, i) =>
)}
);
},
_onGameChange(cb) {
const state = GameStore.getChessboardState();
this.setState({
fen: state.fen,
lastMove: state.lastMove,
kingInCheck: state.check && (state.fen.split(' ')[1] === 'w' ? 'K' : 'k')
}, cb);
},
_setMoveFrom(square) {
this.setState({
moveFrom: square
});
},
_onNewMove(move) {
const {io, token} = this.props;
io.emit('new-move', {
token: token,
move: move
});
setTimeout(this.props.maybePlaySound, 0);
},
_runClock() {
const {io, token, color} = this.props;
io.emit('clock-run', {
token: token,
color: color
});
},
_removeAsteriskFromTitle() {
let title = document.getElementsByTagName('title')[0];
title.text = title.text.replace('* ', '');
window.removeEventListener('focus', this._removeAsteriskFromTitle);
}
});
const Row = React.createClass({
propTypes: {
rank: React.PropTypes.oneOf(['1','2','3','4','5','6','7','8']).isRequired,
placement: React.PropTypes.string.isRequired,
color: React.PropTypes.oneOf(['white', 'black']).isRequired,
isMoveable: React.PropTypes.bool.isRequired,
moveFrom: React.PropTypes.string,
lastMove: React.PropTypes.object,
setMoveFrom: React.PropTypes.func.isRequired,
kingInCheck: React.PropTypes.oneOf([false, 'K', 'k']).isRequired,
validMoves: React.PropTypes.instanceOf(Set).isRequired
},
mixins: [maybeReverse],
render() {
const {rank, placement, color} = this.props;
const files = this._maybeReverse(FILES);
const pieces = this._maybeReverse(placement.length < 8 ?
Seq(placement).flatMap(piece => (
/^\d$/.test(piece) ? Repeat('-', parseInt(piece, 10)) : piece
)).toArray() :
placement.split('')
);
return (
{pieces.map((piece, i) =>
)}
);
}
});
const Column = React.createClass({
propTypes: {
square: React.PropTypes.string.isRequired,
piece: React.PropTypes.string.isRequired,
color: React.PropTypes.oneOf(['white', 'black']).isRequired,
isMoveable: React.PropTypes.bool.isRequired,
moveFrom: React.PropTypes.string,
lastMove: React.PropTypes.object,
setMoveFrom: React.PropTypes.func.isRequired,
kingInCheck: React.PropTypes.oneOf([false, 'K', 'k']).isRequired,
validMoves: React.PropTypes.instanceOf(Set).isRequired
},
render() {
const {moveFrom, lastMove, square, color,
isMoveable, kingInCheck, validMoves} = this.props;
const piece = ChessPieces[this.props.piece];
const rgx = color === 'white' ? /^[KQRBNP]$/ : /^[kqrbnp]$/;
const isDraggable = rgx.test(this.props.piece);
const isDroppable = moveFrom && validMoves.has(square);
return (
{piece ?
{piece}
:null}
|
);
},
_onClickSquare() {
const {isMoveable, color, moveFrom, square, piece} = this.props;
const rgx = color === 'white' ? /^[KQRBNP]$/ : /^[kqrbnp]$/;
if (!isMoveable || (!moveFrom && !rgx.test(piece)))
return;
else if (moveFrom && moveFrom === square)
this.props.setMoveFrom(null);
else if (rgx.test(piece))
this.props.setMoveFrom(square);
else
GameActions.makeMove(moveFrom, square, ChessPieces[piece], true);
},
_onDragStart(e) {
e.dataTransfer.effectAllowed = 'move';
// setData is required by firefox
e.dataTransfer.setData('text/plain', '');
this.props.setMoveFrom(this.props.square);
},
_onDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
},
_onDrop(e) {
e.preventDefault();
const {moveFrom, square, piece} = this.props;
GameActions.makeMove(moveFrom, square, ChessPieces[piece], true);
}
});
export default Chessboard;