├── .gitignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── css
├── chessboard.css
└── chessboard.min.css
├── js
├── chessboard.js
└── chessboard.min.js
├── package.json
├── test
├── manual
│ ├── bower.json
│ ├── js
│ │ └── app.js
│ └── test.html
├── mocha
│ ├── chess-utils-tests.js
│ └── test.html
└── selenium
│ ├── selenium-tests.js
│ └── test.html
└── yuidoc.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | Thumbs.db
3 |
4 | bower_components
5 | node_modules
6 |
7 | doc
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*jslint node:true*/
2 |
3 | 'use strict';
4 |
5 | module.exports = function (grunt) {
6 |
7 | require('time-grunt')(grunt);
8 |
9 | grunt.initConfig({
10 |
11 | uglify: {
12 | my_target: {
13 | files: {
14 | 'js/chessboard.min.js': 'js/chessboard.js'
15 | }
16 | }
17 | },
18 |
19 | cssmin: {
20 | my_target: {
21 | src: 'css/chessboard.css',
22 | dest: 'css/chessboard.min.css'
23 | }
24 | }
25 |
26 | });
27 |
28 | grunt.loadNpmTasks('grunt-contrib-uglify');
29 | grunt.loadNpmTasks('grunt-contrib-cssmin');
30 |
31 | grunt.registerTask('minify', ['uglify', 'cssmin']);
32 |
33 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Attila Szantner
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #ChessboardJS
2 |
3 | A responsive mobile-first javascript chessboard library.
4 |
5 | ##Quick start
6 |
7 | Clone the repo: `https://github.com/caustique/chessboard-js.git`
8 |
9 | or
10 |
11 | Install with [Bower](http://bower.io): `bower install chessboard-js`.
12 |
13 | ##Developer notes
14 |
15 | To start run `npm install` and `bower install` in the project directory.
16 |
17 | To generate documentation run `yuidoc` (after having installed it with `npm -g install yuidocjs`)
18 |
19 | To regenerate minified versions of the css and js files run `grunt minify`
20 |
21 | The tests are far from complete.
22 |
23 | Using [Brackets](http://brackets.io/) - all js files should be without any JSLint problems.
24 |
25 | ##License
26 |
27 | ChessboardJS is released under the [MIT License](https://github.com/caustique/chessboard-js/blob/master/LICENSE).
28 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chessboard-js",
3 | "description": "A responsive mobile-first javascript chessboard library.",
4 | "main": [
5 | "js/chessboard.js",
6 | "css/chessboard.css"
7 | ],
8 | "license": "MIT",
9 | "ignore": [
10 | "Gruntfile.js",
11 | ".gitignore",
12 | "test/",
13 | "package.json",
14 | "yuidoc.json"
15 | ],
16 | "keywords" : ["chess", "chessboard", "js", "javascript", "responsive", "chess-gui", "chessboard-gui", "mobile"],
17 | "authors": [
18 | { "name": "Attila Szantner" }
19 | ],
20 | "dependencies": {
21 | "jquery": "~2.1.0"
22 | },
23 | "devDependencies": {
24 | "mocha": "~1.18.2",
25 | "chai": "~1.9.1"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/css/chessboard.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | SKIN
4 |
5 | VALUES TO PLAY WITH
6 | These values are safe to set and can be used to customize the appearence of the board.
7 |
8 | */
9 |
10 |
11 | /*
12 | Customizing the chessboard
13 | */
14 | /* The color of the light and dark square on the board. */
15 | div.chess_board div.chess_square_light {
16 | background-color: #ffffff;
17 | }
18 | div.chess_board div.chess_square_dark {
19 | background-color: #e5e1d1;
20 | }
21 | /* The color of the light and dark squares when selected. */
22 | div.chess_board div.chess_square_light.chess_square_selected {
23 | background-color: #eeeeee;
24 | }
25 | div.chess_board div.chess_square_dark.chess_square_selected {
26 | background-color: #c5c1a1;
27 | }
28 |
29 |
30 | /*
31 | Customizing chess pieces
32 | */
33 | div.chess_board div.chess_player_white {
34 | color: #285e8e;
35 | }
36 | div.chess_board div.chess_player_black {
37 | color: #BD4932;
38 | }
39 |
40 | div.chess_board div.chess_square_selected div.chess_player_white {
41 | color: #487eae;
42 | }
43 | div.chess_board div.chess_square_selected div.chess_player_black {
44 | color: #dd6952;
45 | }
46 |
47 |
48 | /*div.chess_board div.chess_square_light.chess_square_valid_move {
49 | background-color: #CDE855;
50 | }
51 | div.chess_board div.chess_square_dark.chess_square_valid_move {
52 | background-color: #A7C520;
53 | }*/
54 |
55 | div.chess_board div.chess_square_light.chess_square_valid_move {
56 | -webkit-box-shadow: inset 0 0 3px 3px #CDE855;
57 | -moz-box-shadow: inset 0 0 3px 3px #CDE855;
58 | box-shadow: inset 0 0 3px 3px #CDE855;
59 | }
60 | div.chess_board div.chess_square_dark.chess_square_valid_move {
61 | -webkit-box-shadow: inset 0 0 3px 3px #A7C520;
62 | -moz-box-shadow: inset 0 0 3px 3px #A7C520;
63 | box-shadow: inset 0 0 3px 3px #A7C520;
64 | }
65 |
66 |
67 | div.chess_board div.chess_square {
68 | -webkit-font-smoothing: antialiased;
69 | }
70 |
71 |
72 | div.chess_board div.chess_square {
73 | cursor: pointer;
74 | }
75 | div.chess_board div.chess_piece_none {
76 | cursor: auto;
77 | }
78 |
79 |
80 |
81 | div.chess_board div.chess_square_light > div.chess_label {
82 | color: #e5e1d1;
83 | }
84 |
85 | div.chess_board div.chess_square_dark > div.chess_label {
86 | color: #ffffff;
87 | }
88 |
89 | div.chess_board div.chess_label {
90 | font-family: "Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;
91 | font-weight: normal;
92 | font-style: normal;
93 | font-variant: small-caps;
94 | }
95 |
96 |
97 | /* Chess pieces with unicode characters */
98 | div.chess_board div.chess_player_black.chess_piece_pawn:after {
99 | content: '\265F';
100 | }
101 | div.chess_board div.chess_player_black.chess_piece_rook:after {
102 | content: '\265C';
103 | }
104 | div.chess_board div.chess_player_black.chess_piece_knight:after {
105 | content: '\265E';
106 | }
107 | div.chess_board div.chess_player_black.chess_piece_bishop:after {
108 | content: '\265D';
109 | }
110 | div.chess_board div.chess_player_black.chess_piece_queen:after {
111 | content: '\265B';
112 | }
113 | div.chess_board div.chess_player_black.chess_piece_king:after {
114 | content: '\265A';
115 | }
116 | div.chess_board div.chess_player_white.chess_piece_pawn:after {
117 | content: '\2659';
118 | }
119 | div.chess_board div.chess_player_white.chess_piece_rook:after {
120 | content: '\2656';
121 | }
122 | div.chess_board div.chess_player_white.chess_piece_knight:after {
123 | content: '\2658';
124 | }
125 | div.chess_board div.chess_player_white.chess_piece_bishop:after {
126 | content: '\2657';
127 | }
128 | div.chess_board div.chess_player_white.chess_piece_queen:after {
129 | content: '\2655';
130 | }
131 | div.chess_board div.chess_player_white.chess_piece_king:after {
132 | content: '\2654';
133 | }
134 |
135 |
136 | /* Chess pieces with images */
137 |
138 | /* Use this section if you prefer to use images to fonts */
139 |
140 | /*
141 | div.chess_board div.chess_player_black.chess_piece_pawn {
142 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/c/c7/Chess_pdt45.svg');
143 | }
144 | div.chess_board div.chess_player_black.chess_piece_rook {
145 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/f/ff/Chess_rdt45.svg');
146 | }
147 | div.chess_board div.chess_player_black.chess_piece_knight {
148 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/e/ef/Chess_ndt45.svg');
149 | }
150 | div.chess_board div.chess_player_black.chess_piece_bishop {
151 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/9/98/Chess_bdt45.svg');
152 | }
153 | div.chess_board div.chess_player_black.chess_piece_queen {
154 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/4/47/Chess_qdt45.svg');
155 | }
156 | div.chess_board div.chess_player_black.chess_piece_king {
157 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/f/f0/Chess_kdt45.svg');
158 | }
159 | div.chess_board div.chess_player_white.chess_piece_pawn {
160 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/4/45/Chess_plt45.svg');
161 | }
162 | div.chess_board div.chess_player_white.chess_piece_rook {
163 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/7/72/Chess_rlt45.svg');
164 | }
165 | div.chess_board div.chess_player_white.chess_piece_knight {
166 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/7/70/Chess_nlt45.svg');
167 | }
168 | div.chess_board div.chess_player_white.chess_piece_bishop {
169 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/b/b1/Chess_blt45.svg');
170 | }
171 | div.chess_board div.chess_player_white.chess_piece_queen {
172 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/1/15/Chess_qlt45.svg');
173 | }
174 | div.chess_board div.chess_player_white.chess_piece_king {
175 | background-image: url('http://upload.wikimedia.org/wikipedia/commons/4/42/Chess_klt45.svg');
176 | }
177 | */
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 | /*
190 |
191 | STRUCTURE
192 |
193 | VALUES TO LET ALONE!
194 | If you set these values, you risk to mess up the chessboard structure
195 | */
196 |
197 | div.chess_board,
198 | div.chess_board div.chess_square,
199 | div.chess_board div.chess_square > div {
200 | -moz-box-sizing: border-box;
201 | -webkit-box-sizing: border-box;
202 | box-sizing: border-box;
203 |
204 | -webkit-user-select: none;
205 | -khtml-user-select: none;
206 | -moz-user-select: none;
207 | -ms-user-select: none;
208 | user-select: none;
209 | }
210 |
211 | div.chess_board div.chess_square {
212 | position: relative;
213 | float: left;
214 | width: 12.5%;
215 | z-index: 0;
216 | }
217 |
218 | div.chess_board div.chess_square_last_column {
219 | float: none;
220 | overflow: hidden;
221 | width: auto !important;
222 | }
223 |
224 | div.chess_board div.chess_piece {
225 | text-align: center;
226 | line-height: 100%;
227 | background-repeat: no-repeat;
228 | background-position: center;
229 | background-size: cover;
230 | height: 100%
231 | }
232 |
233 | div.chess_board div.chess_piece:after {
234 | position: absolute;
235 | top: 0px;
236 | left: 0px;
237 | width: 100%;
238 | }
239 |
240 | div.chess_board div.chess_label {
241 | z-index: 0;
242 | position: absolute;
243 | }
244 |
245 | div.chess_board div.chess_label_row,
246 | div.chess_board div.chess_label_column_reversed {
247 | top: 2px;
248 | left: 2px;
249 | }
250 | div.chess_board div.chess_label_column,
251 | div.chess_board div.chess_label_row_reversed {
252 | bottom: 2px;
253 | right: 2px;
254 | }
255 |
256 |
257 | div.chess_board div.chess_label_hidden {
258 | visibility: hidden;
259 | }
--------------------------------------------------------------------------------
/css/chessboard.min.css:
--------------------------------------------------------------------------------
1 | div.chess_board div.chess_square_light{background-color:#fff}div.chess_board div.chess_square_dark{background-color:#e5e1d1}div.chess_board div.chess_square_light.chess_square_selected{background-color:#eee}div.chess_board div.chess_square_dark.chess_square_selected{background-color:#c5c1a1}div.chess_board div.chess_player_white{color:#285e8e}div.chess_board div.chess_player_black{color:#BD4932}div.chess_board div.chess_square_selected div.chess_player_white{color:#487eae}div.chess_board div.chess_square_selected div.chess_player_black{color:#dd6952}div.chess_board div.chess_square_light.chess_square_valid_move{-webkit-box-shadow:inset 0 0 3px 3px #CDE855;-moz-box-shadow:inset 0 0 3px 3px #CDE855;box-shadow:inset 0 0 3px 3px #CDE855}div.chess_board div.chess_square_dark.chess_square_valid_move{-webkit-box-shadow:inset 0 0 3px 3px #A7C520;-moz-box-shadow:inset 0 0 3px 3px #A7C520;box-shadow:inset 0 0 3px 3px #A7C520}div.chess_board div.chess_square{-webkit-font-smoothing:antialiased;cursor:pointer}div.chess_board div.chess_piece_none{cursor:auto}div.chess_board div.chess_square_light>div.chess_label{color:#e5e1d1}div.chess_board div.chess_square_dark>div.chess_label{color:#fff}div.chess_board div.chess_label{font-family:"Helvetica Neue",Helvetica,Helvetica,Arial,sans-serif;font-weight:400;font-style:normal;font-variant:small-caps}div.chess_board div.chess_player_black.chess_piece_pawn:after{content:'\265F'}div.chess_board div.chess_player_black.chess_piece_rook:after{content:'\265C'}div.chess_board div.chess_player_black.chess_piece_knight:after{content:'\265E'}div.chess_board div.chess_player_black.chess_piece_bishop:after{content:'\265D'}div.chess_board div.chess_player_black.chess_piece_queen:after{content:'\265B'}div.chess_board div.chess_player_black.chess_piece_king:after{content:'\265A'}div.chess_board div.chess_player_white.chess_piece_pawn:after{content:'\2659'}div.chess_board div.chess_player_white.chess_piece_rook:after{content:'\2656'}div.chess_board div.chess_player_white.chess_piece_knight:after{content:'\2658'}div.chess_board div.chess_player_white.chess_piece_bishop:after{content:'\2657'}div.chess_board div.chess_player_white.chess_piece_queen:after{content:'\2655'}div.chess_board div.chess_player_white.chess_piece_king:after{content:'\2654'}div.chess_board,div.chess_board div.chess_square,div.chess_board div.chess_square>div{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.chess_board div.chess_square{position:relative;float:left;width:12.5%;z-index:0}div.chess_board div.chess_square_last_column{float:none;overflow:hidden;width:auto!important}div.chess_board div.chess_piece{text-align:center;line-height:100%;background-repeat:no-repeat;background-position:center;background-size:cover;height:100%}div.chess_board div.chess_piece:after{position:absolute;top:0;left:0;width:100%}div.chess_board div.chess_label{z-index:0;position:absolute}div.chess_board div.chess_label_column_reversed,div.chess_board div.chess_label_row{top:2px;left:2px}div.chess_board div.chess_label_column,div.chess_board div.chess_label_row_reversed{bottom:2px;right:2px}div.chess_board div.chess_label_hidden{visibility:hidden}
--------------------------------------------------------------------------------
/js/chessboard.js:
--------------------------------------------------------------------------------
1 | /*jslint plusplus: true, nomen: true, es5: true, regexp: true, browser: true, devel: true*/
2 | /*global $, ChessUtils, module */
3 |
4 |
5 | /*
6 | ----------------------------------------------------------------------------
7 | ----------------------------------------------------------------------------
8 |
9 | ChessboardJS
10 |
11 | ----------------------------------------------------------------------------
12 | ----------------------------------------------------------------------------
13 | */
14 | /**
15 | Chessboard class to create a responsive chessboard from javascript.
16 | Further info https://github.com/caustique/chessboard-js
17 |
18 | @class Chessboard
19 | @constructor
20 | */
21 |
22 | function Chessboard(containerId, config) {
23 |
24 | 'use strict';
25 |
26 | /*
27 | ----------------------------------------------------------------------------
28 | ----------------------------------------------------------------------------
29 |
30 | CONSTANTS
31 |
32 | ----------------------------------------------------------------------------
33 | ----------------------------------------------------------------------------
34 | */
35 |
36 | Chessboard.ANIMATION = {
37 | fadeInTime: 1000,
38 | fadeOutTime: 1000
39 | };
40 |
41 | Chessboard.CSS_PREFIX = 'chess_';
42 | Chessboard.CSS = {
43 | pathSeparator: '_',
44 | board: {
45 | id: Chessboard.CSS_PREFIX + 'board',
46 | className: Chessboard.CSS_PREFIX + 'board'
47 | },
48 | square: {
49 | className: Chessboard.CSS_PREFIX + 'square',
50 | lastColumn: { className: Chessboard.CSS_PREFIX + 'square_last_column'},
51 | idPrefix: Chessboard.CSS_PREFIX + 'square',
52 | dark: { className: Chessboard.CSS_PREFIX + 'square_dark' },
53 | light: { className: Chessboard.CSS_PREFIX + 'square_light' },
54 | createClassName: function (index) {
55 | return ' ' + (((index + ChessUtils.convertIndexToRow(index)) % 2 === 0) ?
56 | Chessboard.CSS.square.dark.className : Chessboard.CSS.square.light.className);
57 | },
58 | selected: { className: Chessboard.CSS_PREFIX + 'square_selected'},
59 | validMove: { className: Chessboard.CSS_PREFIX + 'square_valid_move' }
60 | },
61 | player: {
62 | black: { className: Chessboard.CSS_PREFIX + 'player_' + ChessUtils.PLAYER.black.className },
63 | white: { className: Chessboard.CSS_PREFIX + 'player_' + ChessUtils.PLAYER.white.className },
64 | createClassName: function (player) {
65 | return (player === 'white') ?
66 | Chessboard.CSS.player.white.className :
67 | Chessboard.CSS.player.black.className;
68 | }
69 | },
70 | piece: {
71 | idPrefix: Chessboard.CSS_PREFIX + 'piece',
72 | className: Chessboard.CSS_PREFIX + 'piece',
73 | createClassName: function (piece) {
74 | return Chessboard.CSS.piece.className + '_' + piece;
75 | },
76 | none: { className: Chessboard.CSS_PREFIX + 'piece_none' }
77 | },
78 | label: {
79 | className: Chessboard.CSS_PREFIX + 'label',
80 | hidden: { className: Chessboard.CSS_PREFIX + 'label_hidden' },
81 | row: {
82 | className: Chessboard.CSS_PREFIX + 'label_row',
83 | reversed: {
84 | className: Chessboard.CSS_PREFIX + 'label_row_reversed'
85 | }
86 | },
87 | column: {
88 | className: Chessboard.CSS_PREFIX + 'label_column',
89 | reversed: { className: Chessboard.CSS_PREFIX + 'label_column_reversed' }
90 | }
91 | },
92 | style: {
93 | id: Chessboard.CSS_PREFIX + 'style'
94 | }
95 |
96 | };
97 |
98 | /*
99 | ----------------------------------------------------------------------------
100 | ----------------------------------------------------------------------------
101 |
102 | VARIABLE AND METHOD DECLARATIONS
103 |
104 | ----------------------------------------------------------------------------
105 | ----------------------------------------------------------------------------
106 | */
107 | // PRIVATE METHODS
108 | var init,
109 | initConfig,
110 | initDOM,
111 | // Event handlers
112 | bindEvents,
113 | unbindEvents,
114 | onSizeChanged,
115 | onSquareClicked,
116 | // Managing pieces on board
117 | getSquareElement,
118 | getSquareIndexFromId,
119 | clearSelection,
120 | setSelectedSquareElement,
121 | isSquareEmpty,
122 | getPieceElement,
123 | drawPiece,
124 | drawPosition,
125 | drawAnimations,
126 | // ----------------
127 | // HELPER FUNCTIONS
128 | // ----------------
129 | // CSS helper functions
130 | cssGetUniquePrefix,
131 | cssGetBoardUniqueId,
132 | cssGetSquareUniqueId,
133 | cssGetPieceUniqueId,
134 | cssGetStyleUniqueId,
135 | // PRIVATE VARIABLES
136 | // All private variables are prefixed with _
137 | _that = this, // For event handlers
138 | _containerSelector, // Element selector that is given by the client to create chessboard in
139 | _userInputEnabled = false, // Shows if user input is enabled like clicks
140 | _position = ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.empty),
141 | // Current position on the table string a8-h1 of pieces and '0' (white is capital letter),
142 | _selectedSquareIndex = null, // Index of the selected square
143 | _validMoves, // Array of valid moves for selected piece
144 | _orientation = ChessUtils.ORIENTATION.white, // Who sits south position
145 | _config = {
146 | useAnimation: true, // Setting default for useAnimation parameters
147 | showBoardLabels: true, // Show columns and row numbers
148 | showNextMove: true // Show the valid moves of the selected piece
149 | },
150 | _eventHandlers = {
151 | /**
152 | * Fired when the user clicks on one of the available moves. Next position after move should be returned. Any format is accepted that setPosition() accepts. Returning null cancels move.
153 | *
154 | * @event onMove
155 | * @param {Object} move in notation format (ie. {from: 'e2', to: 'e4'})
156 | */
157 | onMove: null,
158 | onPieceSelected: null,
159 | onChange: null,
160 | onChanged: null
161 | }, // Function references to event handlers
162 | _preventPositionChange = false, // If true position change is not allowed
163 | _preventCallingEvents = true; // If true the system is not calling events. Used during running of constructor function.
164 |
165 |
166 | /*
167 | ----------------------------------------------------------------------------
168 | ----------------------------------------------------------------------------
169 |
170 | PUBLIC METHODS
171 |
172 | ----------------------------------------------------------------------------
173 | ----------------------------------------------------------------------------
174 | */
175 |
176 | /**
177 | Clears the chessboard of all pieces.
178 | Change chessboard orientation to white.
179 |
180 | @method clear
181 | @public
182 | */
183 | this.clear = function () {
184 | if (_preventPositionChange) { return; }
185 | this.setPosition(ChessUtils.FEN.positions.empty);
186 | this.setOrientation(ChessUtils.ORIENTATION.white);
187 | };
188 | /**
189 | Destroys the DOM structure that was created
190 |
191 | @method destroy
192 | @public
193 | */
194 | this.destroy = function () {
195 | $(_containerSelector).html('');
196 |
197 | unbindEvents();
198 | };
199 | /**
200 | Sets or gets the chessboard position.
201 | The method doesn't change the chessboard orientation, so it has to be manually if needed.
202 | Deprecated method for compatibility reasons, use setPosition and getPosition instead.
203 | (No parameter checking!)
204 |
205 | @method position
206 | @public
207 | @deprecated
208 | @param {Object} [position] If omitted returns the internal position string. If ChessUtils.FEN.id (which is 'fen' for compatibility reasons) which returns a fen string (see http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation). If Chessboard.NOTATION.id then returns a notation object. Otherwise it can be a position string, fen string or notation object to set the current position.
209 | @param {Boolean} [useAnimation=true] Whether to use animation for the process.
210 | @return {Object} The current position of the chessboard in different format.
211 | */
212 | this.position = function (position, useAnimation) {
213 | var format;
214 |
215 | if (((arguments.length === 0) || typeof position === 'undefined') ||
216 | (typeof position === 'string' && position.toLowerCase() === ChessUtils.FEN.id) ||
217 | (typeof position === 'string' && position.toLowerCase() === ChessUtils.NOTATION.id)) {
218 | return this.getPosition(position);
219 | } else {
220 | this.setPosition(position, useAnimation);
221 | }
222 | };
223 | /**
224 | Sets the chessboard position.
225 | The method doesn't change the chessboard orientation, so it has to be manually if needed.
226 | (No parameter checking!)
227 |
228 | @method setPosition
229 | @public
230 | @param {Object} [position] It can be a position string, fen string or notation object to set the current position.
231 | @param {Boolean} [useAnimation=true] Whether to use animation for the process.
232 | */
233 | this.setPosition = function (position, useAnimation) {
234 | var prevUserInputEnabled = _userInputEnabled;
235 |
236 | if (_preventPositionChange) { return; }
237 |
238 | clearSelection();
239 |
240 | useAnimation = (arguments.length === 1 || typeof useAnimation === 'undefined') ? _config.useAnimation : useAnimation;
241 |
242 | // start position
243 | if (typeof position === 'string' && (position.toLowerCase() === ChessUtils.FEN.startId)) {
244 | position = ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.start);
245 | } else if (typeof position === 'string' && (position.toLowerCase() === ChessUtils.FEN.emptyId)) {
246 | position = ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.empty);
247 | } else if (typeof position === 'string') {
248 | if (position.indexOf(ChessUtils.FEN.rowSeparator) !== -1) {
249 | position = ChessUtils.convertFenToPosition(position);
250 | }
251 | } else if (typeof position === 'object') {
252 | position = ChessUtils.convertNotationToPosition(position);
253 | }
254 |
255 |
256 | if (_position === position) { return; }
257 |
258 | // run the onChange function
259 | if (_eventHandlers.hasOwnProperty('onChange') === true &&
260 | typeof _eventHandlers.onChange === 'function' &&
261 | !_preventCallingEvents) {
262 | _preventPositionChange = true;
263 | if (!_eventHandlers.onChange(_position, position)) { return; }
264 | _preventPositionChange = false;
265 | }
266 |
267 | _userInputEnabled = false;
268 | if (useAnimation === true) {
269 | drawAnimations(position);
270 | _position = position;
271 | } else {
272 | _position = position;
273 | drawPosition();
274 | }
275 |
276 | // run the onChanged function
277 | if (_eventHandlers.hasOwnProperty('onChanged') === true &&
278 | typeof _eventHandlers.onChanged === 'function' &&
279 | !_preventCallingEvents) {
280 | _preventPositionChange = true;
281 | _eventHandlers.onChanged(_position);
282 | _preventPositionChange = false;
283 | }
284 |
285 | _userInputEnabled = prevUserInputEnabled;
286 | };
287 | /**
288 | Gets the chessboard position.
289 | (No strict parameter checking!)
290 |
291 | @method getPosition
292 | @public
293 | @param {String} [format] If omitted returns the internal position string. If ChessUtils.FEN.id (which is 'fen' for compatibility reasons) which returns a fen string (see http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation). If Chessboard.NOTATION.id then returns a notation object.
294 | @return {Object} The current position of the chessboard in different format.
295 | */
296 | this.getPosition = function (format) {
297 | // no arguments, return the current position
298 | if ((arguments.length === 0) || !format) {
299 | return _position;
300 | }
301 | // get position as fen
302 | if (format.toLowerCase() === ChessUtils.FEN.id) {
303 | return ChessUtils.convertPositionToFen(_position);
304 | }
305 | // get position as notation object
306 | if (format.toLowerCase() === ChessUtils.NOTATION.id) {
307 | return ChessUtils.convertPositionToNotation(_position);
308 | }
309 | };
310 |
311 | this.move = function (firstMove) {
312 | var movesFrom = [],
313 | movesTo = [],
314 | position = _position,
315 | i,
316 | useAnimation = _config.useAnimation,
317 | count;
318 |
319 | if (_preventPositionChange) { return; }
320 |
321 | // TODO: parameter checking
322 |
323 | if (typeof arguments[arguments.length - 1] === 'boolean') {
324 | useAnimation = arguments[arguments.length - 1];
325 | count = arguments.length - 1;
326 | } else {
327 | count = arguments.length;
328 | }
329 |
330 | if (typeof firstMove === 'string') {
331 | if (firstMove.search('-') !== -1) {
332 | for (i = 0; i < count; i++) {
333 | movesFrom.push(ChessUtils.convertNotationSquareToIndex(arguments[i].split('-')[0]));
334 | movesTo.push(ChessUtils.convertNotationSquareToIndex(arguments[i].split('-')[1]));
335 | }
336 | } else {
337 | for (i = 0; i < count; i += 2) {
338 | movesFrom.push(ChessUtils.convertNotationSquareToIndex(arguments[i]));
339 | movesTo.push(ChessUtils.convertNotationSquareToIndex(arguments[i + 1]));
340 | }
341 | }
342 | } else {
343 | for (i = 0; i < count; i += 2) {
344 | movesFrom.push(arguments[i]);
345 | movesTo.push(arguments[i + 1]);
346 | }
347 | }
348 |
349 | for (i = 0; i < movesFrom.length; i++) {
350 | if (_position[movesFrom[i]] !== ChessUtils.POSITION.empty) {
351 | position = ChessUtils.replaceStringAt(position, movesFrom[i], ChessUtils.POSITION.empty);
352 | position = ChessUtils.replaceStringAt(position, movesTo[i], _position[movesFrom[i]]);
353 | }
354 | }
355 |
356 | this.setPosition(position, useAnimation);
357 |
358 | };
359 | /**
360 | Public method to set or get the chessboard position with a fen string (see http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation).
361 | A shortcut to the position method. Use setPosition and getPosition instead.
362 | The method doesn't change the chessboard orientation, so it has to be manually if needed.
363 | (No parameter checking!)
364 |
365 | @method fen
366 | @public
367 | @deprecated
368 | @param {String} [fen] If omitted returns the fen string of the current postion on the checkboard. If given it is a fen string to set the current position.
369 | @param {Boolean} [useAnimation=true] Whether to use animation for the process.
370 | @return {String or Object} The current position of the chessboard in different format.
371 | */
372 | this.fen = function (fen, useAnimation) {
373 | return this.position(ChessUtils.FEN.id, fen, useAnimation);
374 | };
375 | this.orientation = function (orientation) {
376 | if (arguments.length === 0) {
377 | return this.getOrientation();
378 | } else {
379 | this.setOrientation(orientation);
380 | }
381 | };
382 |
383 | this.setOrientation = function (orientation) {
384 | var position;
385 |
386 | if (orientation === ChessUtils.ORIENTATION.flip || _orientation !== orientation) {
387 |
388 | clearSelection();
389 |
390 | _orientation = (_orientation === ChessUtils.ORIENTATION.white) ?
391 | ChessUtils.ORIENTATION.black : ChessUtils.ORIENTATION.white;
392 |
393 | if (_orientation === ChessUtils.ORIENTATION.white) {
394 | $('.' + Chessboard.CSS.label.row.className).removeClass(Chessboard.CSS.label.hidden.className);
395 | $('.' + Chessboard.CSS.label.column.className).removeClass(Chessboard.CSS.label.hidden.className);
396 | $('.' + Chessboard.CSS.label.row.reversed.className).addClass(Chessboard.CSS.label.hidden.className);
397 | $('.' + Chessboard.CSS.label.column.reversed.className).addClass(Chessboard.CSS.label.hidden.className);
398 | } else {
399 | $('.' + Chessboard.CSS.label.row.className).addClass(Chessboard.CSS.label.hidden.className);
400 | $('.' + Chessboard.CSS.label.column.className).addClass(Chessboard.CSS.label.hidden.className);
401 | $('.' + Chessboard.CSS.label.row.reversed.className).removeClass(Chessboard.CSS.label.hidden.className);
402 | $('.' + Chessboard.CSS.label.column.reversed.className).removeClass(Chessboard.CSS.label.hidden.className);
403 | }
404 |
405 | drawPosition();
406 | }
407 | };
408 | this.getOrientation = function () {
409 | return _orientation;
410 | };
411 | /**
412 | Sets the chessboard size to match the container element size.
413 |
414 | @method resize
415 | @public
416 | @deprecated
417 | */
418 | this.resize = function () {
419 | onSizeChanged();
420 | };
421 | /**
422 | Sets the chessboard position to the classical start position.
423 | The method sets the chessboard orientation to Chessboard.ORIENTATION.white.
424 |
425 | @method start
426 | @public
427 | @param {Boolean} [useAnimation=true] Whether to use animation for the process.
428 | */
429 | this.start = function (useAnimation) {
430 |
431 | if (_preventPositionChange) { return; }
432 |
433 | useAnimation = (arguments.length === 0) ? _config.useAnimation : useAnimation;
434 | this.position(ChessUtils.FEN.positions.start, useAnimation);
435 | _orientation = ChessUtils.ORIENTATION.white;
436 | };
437 |
438 | /**
439 | Sets wether the board reacts to user clicks and other inputs. After initialization it is set to true.
440 | The game controller logic should take care of setting it according to who plays.
441 |
442 | @method enableUserInput
443 | @public
444 | @param {Boolean} [enabled=true] Whether to enable user interaction.
445 | */
446 | this.enableUserInput = function (enabled) {
447 | if (arguments.length === 0) {
448 | enabled = true;
449 | }
450 | _userInputEnabled = enabled;
451 | };
452 | /**
453 | Gets wether the board reacts to user clicks and other inputs.
454 |
455 | @method isUserInputEnabled
456 | @public
457 | @return {Boolean} Returns whether to enable user interaction.
458 | */
459 | this.isUserInputEnabled = function () {
460 | return _userInputEnabled;
461 | };
462 |
463 |
464 |
465 | /*
466 | ----------------------------------------------------------------------------
467 | ----------------------------------------------------------------------------
468 |
469 | PRIVATE METHODS
470 |
471 | ----------------------------------------------------------------------------
472 | ----------------------------------------------------------------------------
473 | */
474 | /*
475 | ----------------------------------------------------------------------------
476 | Init methods
477 | ----------------------------------------------------------------------------
478 | */
479 | /**
480 | Initializes the chessboard.
481 |
482 | @method init
483 | @private
484 | @param {String} containerId The html id of the container div where the chessboard will be created.
485 | @param {Object} config Configuration object which is either a position string or an object.
486 | */
487 | init = function (containerId, config) {
488 | var position;
489 |
490 | position = initConfig(config);
491 | initDOM(containerId);
492 | bindEvents();
493 |
494 | _userInputEnabled = true;
495 |
496 | return position;
497 | };
498 | /**
499 | Processes the config object given at init.
500 |
501 | @method initConfig
502 | @private
503 | @param {Object} config Configuration object which is either a position string or an object with the following attributes: position, useAnimation, orientation, showBoardLabels.
504 | */
505 | initConfig = function (config) {
506 | var position;
507 |
508 | if (typeof config === 'undefined') {
509 | position = ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.start);
510 | return position;
511 | } else if (typeof config === 'string') {
512 | if (config.toLowerCase() === ChessUtils.FEN.startId) {
513 | position = ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.start);
514 | } else if (config.toLowerCase() === ChessUtils.FEN.emptyId) {
515 | position = ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.empty);
516 | } else {
517 | position = config;
518 | }
519 | return position;
520 | } else if (typeof config.position === 'undefined') {
521 | position = ChessUtils.convertNotationToPosition(config);
522 | return position;
523 | } else if (config.position !== null) {
524 | if (ChessUtils.isValidFen(config.position)) {
525 | config.position = ChessUtils.convertFenToPosition(config.position);
526 | }
527 | position = config.position;
528 | }
529 | _orientation = config.orientation === ChessUtils.ORIENTATION.black ?
530 | ChessUtils.ORIENTATION.black : ChessUtils.ORIENTATION.white;
531 |
532 | _config.useAnimation = config.useAnimation === false ? false : true;
533 | _config.showBoardLabels = (config.showNotation === false) || (config.showBoardLabels === false) ? false : true;
534 | _config.showNextMove = config.showNextMove === false ? false : true;
535 |
536 | if (config.eventHandlers) {
537 |
538 | if (config.eventHandlers.onChange &&
539 | typeof config.eventHandlers.onChange === 'function') {
540 | _eventHandlers.onChange = config.eventHandlers.onChange;
541 | }
542 | if (config.eventHandlers.onChanged &&
543 | typeof config.eventHandlers.onChanged === 'function') {
544 | _eventHandlers.onChanged = config.eventHandlers.onChanged;
545 | }
546 | if (config.eventHandlers.onPieceSelected &&
547 | typeof config.eventHandlers.onPieceSelected === 'function') {
548 | _eventHandlers.onPieceSelected = config.eventHandlers.onPieceSelected;
549 | }
550 | if (config.eventHandlers.onMove &&
551 | typeof config.eventHandlers.onMove === 'function') {
552 | _eventHandlers.onMove = config.eventHandlers.onMove;
553 | }
554 |
555 | }
556 |
557 | return position;
558 | // TODO: Deprecated compatibility settings
559 | };
560 | /**
561 | Initialises the DOM tree for the chessboard.
562 |
563 | @method initDOM
564 | @private
565 | @param {String} containerId The html id of the container div where the chessboard will be created.
566 | */
567 | initDOM = function (containerId) {
568 | var i,
569 | html = '',
570 | id,
571 | className;
572 |
573 | _containerSelector = '#' + containerId;
574 |
575 | if (!$(_containerSelector)) { throw new Error("ContainerId provided doesn't point to a DOM element."); }
576 |
577 | // Adding dynamic style for resize events
578 | html += '';
579 |
580 |
581 | // Board div
582 | html += '
';
584 |
585 | for (i = 0; i < 64; i++) {
586 | // Square div
587 | id = cssGetSquareUniqueId(i);
588 | className = Chessboard.CSS.square.className;
589 | className += ' ' + Chessboard.CSS.square.createClassName(i);
590 | if (i % 8 === 7) {
591 | className += ' ' + Chessboard.CSS.square.lastColumn.className;
592 | }
593 | html += '
';
594 |
595 | // Column indicators
596 | if (ChessUtils.convertIndexToRow(i) === 0) {
597 | html += '
' +
599 | ChessUtils.NOTATION.columnConverter[ChessUtils.convertIndexToColumn(i)] + '
';
600 | }
601 | if (ChessUtils.convertIndexToRow(i) === 7) {
602 | html += '
' +
605 | ChessUtils.NOTATION.columnConverter[ChessUtils.convertIndexToColumn(7 - i)] + '
';
606 | }
607 | // Row indicators
608 | if (ChessUtils.convertIndexToColumn(i) === 0) {
609 | html += '
' +
611 | ChessUtils.NOTATION.rowConverter[ChessUtils.convertIndexToRow(i)] + '
';
612 | }
613 | if (ChessUtils.convertIndexToColumn(i) === 7) {
614 | html += '
' +
617 | ChessUtils.NOTATION.rowConverter[7 - ChessUtils.convertIndexToRow(i)] + '
';
618 | }
619 |
620 | // Piece placeholders
621 | className = Chessboard.CSS.piece.className;
622 | className += ' ' + Chessboard.CSS.piece.none.className;
623 | html += '
';
624 |
625 | html += '
';
626 | }
627 |
628 | html += '
';
629 |
630 | $(_containerSelector).html(html);
631 | $(_containerSelector).css('display', 'inline-block');
632 | };
633 |
634 | /*
635 | ----------------------------------------------------------------------------
636 | Event handling related methods
637 | ----------------------------------------------------------------------------
638 | */
639 | /**
640 | Binds chessboard events to elements.
641 |
642 | @method bindEvents
643 | @private
644 | */
645 | bindEvents = function () {
646 | $(window).on('resize.chessEvents', onSizeChanged);
647 |
648 | $('div' + _containerSelector + ' div.' + Chessboard.CSS.square.className).on('click', onSquareClicked);
649 | };
650 | /**
651 | Unbinds chessboard events to elements in case the board is detroyed.
652 |
653 | @method unbindEvents
654 | @private
655 | */
656 | unbindEvents = function () {
657 | $(window).unbind('resize.chessEvents');
658 | $('div' + _containerSelector + ' div.' + Chessboard.CSS.square.className).unbind('click');
659 | };
660 | /**
661 | Resizes elements in case the window is resized.
662 |
663 | @method onSizeChanged
664 | @private
665 | */
666 | onSizeChanged = function () {
667 | var newSquareWidth,
668 | newPieceFontSize,
669 | newLabelFontSize,
670 | html;
671 |
672 | newSquareWidth = Math.floor($(_containerSelector).width() / 8);
673 | newPieceFontSize = newSquareWidth * 0.85;
674 | newLabelFontSize = Math.min(Math.max(newSquareWidth * 0.5, 8), 20);
675 |
676 | html = '\
677 | div' + _containerSelector + ' div.' + Chessboard.CSS.piece.className + ' {\
678 | font-size: ' + newPieceFontSize + 'px;\
679 | height: ' + newSquareWidth + 'px;\
680 | }\
681 | div' + _containerSelector + ' div.' + Chessboard.CSS.label.className + ' {\
682 | font-size: ' + newLabelFontSize + 'px;\
683 | ' + (!_config.showBoardLabels ? 'display: none;' : '') + '\
684 | }';
685 | $('#' + cssGetStyleUniqueId()).html(html);
686 | };
687 | /**
688 | Handles onClick event on squares.
689 |
690 | @method onSquareClicked
691 | @private
692 | */
693 | onSquareClicked = function () {
694 | var index = getSquareIndexFromId($(this).context.id),
695 | i,
696 | nextPosition;
697 |
698 | if (!_userInputEnabled) { return; }
699 |
700 | if (_selectedSquareIndex !== null && _validMoves.indexOf(index) > -1) {
701 | if (_eventHandlers.onMove) {
702 | nextPosition = _eventHandlers.onMove({from: ChessUtils.convertIndexToNotationSquare(_selectedSquareIndex),
703 | to: ChessUtils.convertIndexToNotationSquare(index)});
704 | if (nextPosition !== null) {
705 | _that.setPosition(nextPosition);
706 | clearSelection();
707 | }
708 | }
709 | } else {
710 |
711 | if (_selectedSquareIndex !== index) {
712 | if (_eventHandlers.onPieceSelected) {
713 | _validMoves = _eventHandlers.onPieceSelected(ChessUtils.convertIndexToNotationSquare(index));
714 |
715 | if (_validMoves && _validMoves.length !== 0) {
716 | setSelectedSquareElement(index);
717 |
718 | if (!isSquareEmpty(index)) {
719 | for (i = 0; i < _validMoves.length; i++) {
720 | getSquareElement(_validMoves[i]).addClass(Chessboard.CSS.square.validMove.className);
721 | }
722 | }
723 | } else {
724 | clearSelection();
725 | }
726 | }
727 | }
728 | }
729 |
730 | };
731 | /**
732 | Clears the current selection.
733 |
734 | @method clearSelection
735 | @private
736 | */
737 | clearSelection = function () {
738 | if (_selectedSquareIndex !== null) {
739 | getSquareElement(_selectedSquareIndex).removeClass(Chessboard.CSS.square.selected.className);
740 | $('div' + _containerSelector + ' div.' + Chessboard.CSS.square.validMove.className).
741 | removeClass(Chessboard.CSS.square.validMove.className);
742 | }
743 | _selectedSquareIndex = null;
744 | };
745 | /**
746 | Sets the index-th square to selected if there is a piece on it. It deletes the previous selection.
747 |
748 | @method setSelectedSquareElement
749 | @private
750 | @param {Integer} index The index of the quare to be selected.
751 | */
752 | setSelectedSquareElement = function (index) {
753 | clearSelection();
754 | if (!isSquareEmpty(index)) {
755 | _selectedSquareIndex = index;
756 | getSquareElement(_selectedSquareIndex).addClass(Chessboard.CSS.square.selected.className);
757 | }
758 | };
759 |
760 | /*
761 | ----------------------------------------------------------------------------
762 | Managing board
763 | ----------------------------------------------------------------------------
764 | */
765 | /**
766 | Returns the a JQuery object of the square div selected by the index.
767 |
768 | @method getSquareElement
769 | @private
770 | @param {Integer} index Index of a square (0-63)
771 | @return {Object} The JQuery object of the square div
772 | */
773 | getSquareElement = function (index) {
774 | return $('#' + cssGetSquareUniqueId(index));
775 | };
776 | /**
777 | Returns the index of a square div based on the html id attribute.
778 |
779 | @method getSquareIndexFromId
780 | @private
781 | @param {String} htmlId The html id attribute of the square (The last piece contains the id part)
782 | @return {Integer} The index of the square (0-63)
783 | */
784 | getSquareIndexFromId = function (htmlId) {
785 | var classParts,
786 | originalIndex;
787 |
788 | classParts = htmlId.split(Chessboard.CSS.pathSeparator);
789 | originalIndex = parseInt(classParts[classParts.length - 1], 10);
790 | return _orientation === ChessUtils.ORIENTATION.white ? originalIndex : 63 - originalIndex;
791 | };
792 | /**
793 | Returns the a JQuery object of the piece div selected by the index.
794 |
795 | @method getPieceElement
796 | @private
797 | @param {Integer} index Index of a square (0-63)
798 | @return {Object} The JQuery object of the piece div
799 | */
800 | getPieceElement = function (index) {
801 | return $('#' + cssGetPieceUniqueId(index));
802 | };
803 | /**
804 | Returns if a selected index is empty.
805 |
806 | @method isSquareEmpty
807 | @private
808 | @param {Integer} index Index of a square (0-63)
809 | @return {Boolean}
810 | */
811 | isSquareEmpty = function (index) {
812 | // TODO: Check why not using position string
813 | return $(getPieceElement(index)).hasClass(Chessboard.CSS.piece.none.className);
814 | };
815 | /**
816 | Draws a piece at the selected position.
817 |
818 | @method drawPiece
819 | @private
820 | @param {Integer} index Index of a square (0-63)
821 | @param {String} positionPiece The string representing one piece in the position string.
822 | @param {Boolean} isHidden Whether the piece should be placed there as a hidden piece for aimation purposes.
823 | */
824 | drawPiece = function (index, positionPiece, isHidden) {
825 | var className = '',
826 | player = ChessUtils.getPlayerNameFromPiece(positionPiece),
827 | piece = ChessUtils.PIECE.codeToPieceName[positionPiece.toLowerCase()];
828 |
829 | if (isHidden !== true) { isHidden = false; }
830 |
831 | className = Chessboard.CSS.piece.className;
832 | if (positionPiece !== ChessUtils.POSITION.empty) {
833 | className += ' ' + Chessboard.CSS.piece.createClassName(piece);
834 | className += ' ' + Chessboard.CSS.player.createClassName(player);
835 | } else {
836 | className += ' ' + Chessboard.CSS.piece.none.className;
837 | }
838 | if (getPieceElement(index).attr('class') !== className) {
839 | getPieceElement(index).attr('class', className);
840 | }
841 | getPieceElement(index).css('opacity', isHidden ? 0 : 1);
842 | };
843 | /**
844 | Draws the entire board from the position string using the drawPiece method.
845 |
846 | @method drawPosition
847 | @private
848 | */
849 | drawPosition = function () {
850 | var i;
851 |
852 | for (i = 0; i < 64; i++) {
853 | drawPiece(i, _position[i]);
854 | }
855 | };
856 | /**
857 | Draws animated the entire board from the position string.
858 |
859 | @method drawAnimations
860 | @private
861 | @param {String} position A position string to animate to from the actual position.
862 | */
863 | drawAnimations = function (position) {
864 | var i;
865 |
866 | for (i = 0; i < 64; i++) {
867 | if (_position[i] !== position[i]) {
868 | if ((_position[i] !== '0') && (position[i] !== '0')) {
869 | drawPiece(i, position[i], true);
870 | $(getPieceElement(i)).animate({'opacity': '1'}, Chessboard.ANIMATION.fadeInTime);
871 |
872 | // Replacing characters
873 | } else if (_position[i] === '0') {
874 | // New piece on square
875 | drawPiece(i, position[i], true);
876 | $(getPieceElement(i)).animate({'opacity': '1'}, Chessboard.ANIMATION.fadeInTime);
877 | } else if (position[i] === '0') {
878 | // Removing piece from square
879 | drawPiece(i, position[i], true);
880 | }
881 | }
882 | }
883 | };
884 |
885 |
886 |
887 | /*
888 | ----------------------------------------------------------------------------
889 | CSS helper methods
890 | ----------------------------------------------------------------------------
891 | */
892 | /**
893 | Creates a unique prefix for css classnames based on enclosing div id
894 | in order to be able to handle multiple boards on one page.
895 |
896 | @method cssGetUniquePrefix
897 | @private
898 | @return {String}
899 | */
900 | cssGetUniquePrefix = function () {
901 | return $(_containerSelector).attr('id') + '_';
902 | };
903 | /**
904 | Returns unique css classname for the board div.
905 |
906 | @method cssGetBoardUniqueId
907 | @private
908 | @return {String}
909 | */
910 | cssGetBoardUniqueId = function (index) {
911 | return cssGetUniquePrefix() + Chessboard.CSS.board.id;
912 | };
913 | /**
914 | Returns unique css id for a square div.
915 |
916 | @method cssGetSquareUniqueId
917 | @private
918 | @return {String}
919 | */
920 | cssGetSquareUniqueId = function (index) {
921 | return cssGetUniquePrefix() + Chessboard.CSS.square.idPrefix + '_' +
922 | (_orientation === ChessUtils.ORIENTATION.white ? index : 63 - index);
923 | };
924 | /**
925 | Returns unique css id for a piece div.
926 |
927 | @method cssGetPieceUniqueId
928 | @private
929 | @return {String}
930 | */
931 | cssGetPieceUniqueId = function (index) {
932 | return cssGetUniquePrefix() + Chessboard.CSS.piece.idPrefix + '_' +
933 | (_orientation === ChessUtils.ORIENTATION.white ? index : 63 - index);
934 | };
935 | /**
936 | Returns unique css id for the style div.
937 |
938 | @method cssGetStyleUniqueId
939 | @private
940 | @return {String}
941 | */
942 | cssGetStyleUniqueId = function () {
943 | return cssGetUniquePrefix() + Chessboard.CSS.style.id;
944 | };
945 |
946 |
947 | /*
948 | ----------------------------------------------------------------------------
949 | ----------------------------------------------------------------------------
950 |
951 | CONSTRUCTOR CODE
952 |
953 | ----------------------------------------------------------------------------
954 | ----------------------------------------------------------------------------
955 | */
956 |
957 | this.setPosition(init(containerId, config));
958 | onSizeChanged();
959 |
960 | // It is a bit of a hack.
961 | if (_orientation === ChessUtils.ORIENTATION.black) {
962 | this.setOrientation(ChessUtils.ORIENTATION.flip);
963 | _orientation = ChessUtils.ORIENTATION.black;
964 | }
965 |
966 | _preventCallingEvents = false;
967 |
968 | }
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 | /*
987 | ----------------------------------------------------------------------------
988 | ----------------------------------------------------------------------------
989 |
990 | CHESSUTILS
991 |
992 | ----------------------------------------------------------------------------
993 | ----------------------------------------------------------------------------
994 | */
995 |
996 |
997 | /**
998 | ChessUtils class to contain static utility functions.
999 |
1000 | @class ChessUtils
1001 | */
1002 |
1003 | (function () {
1004 | 'use strict';
1005 |
1006 | /*
1007 | ----------------------------------------------------------------------------
1008 | ----------------------------------------------------------------------------
1009 |
1010 | CONSTANTS
1011 |
1012 | ----------------------------------------------------------------------------
1013 | ----------------------------------------------------------------------------
1014 | */
1015 |
1016 | var ChessUtils = {};
1017 |
1018 | ChessUtils.PLAYER = {
1019 | black: {
1020 | code: 'b',
1021 | notation: 'b',
1022 | className: 'black'
1023 | },
1024 | white: {
1025 | code: 'w',
1026 | notation: 'w',
1027 | className: 'white'
1028 | }
1029 | };
1030 |
1031 | ChessUtils.ORIENTATION = {
1032 | white: 'w',
1033 | black: 'b',
1034 | flip: 'flip'
1035 | };
1036 |
1037 | ChessUtils.PIECE = {
1038 | none: '0',
1039 | pawn: 'p',
1040 | rook: 'r',
1041 | knight: 'n',
1042 | bishop: 'b',
1043 | queen: 'q',
1044 | king: 'k',
1045 | codeToPieceName: {
1046 | p: 'pawn',
1047 | r: 'rook',
1048 | n: 'knight',
1049 | b: 'bishop',
1050 | q: 'queen',
1051 | k: 'king'
1052 | }
1053 | };
1054 |
1055 | ChessUtils.POSITION = {
1056 | empty: '0',
1057 | piece: {
1058 | pawn: 'p',
1059 | rook: 'r',
1060 | knight: 'n',
1061 | bishop: 'b',
1062 | queen: 'q',
1063 | king: 'k'
1064 | },
1065 | validator: /^[kqrbnpKQRNBP0]+$/,
1066 | };
1067 |
1068 |
1069 | ChessUtils.NOTATION = {
1070 | id: 'notation',
1071 | positionValidator: /^[a-h][1-8]$/,
1072 | pieceValidator: /^[bw][KQRNBP]$/,
1073 | columns: String.prototype.split.call('abcdefgh', ''),
1074 | rows: String.prototype.split.call('12345678', ''),
1075 | columnConverter: 'abcdefgh',
1076 | rowConverter: '12345678'
1077 | };
1078 |
1079 | ChessUtils.FEN = {
1080 | // Commands
1081 | id: 'fen',
1082 | startId: 'start',
1083 | emptyId: 'empty',
1084 | // Syntax
1085 | rowValidator: /^[kqrbnpKQRNBP1-8]+$/,
1086 | rowSeparator: '/',
1087 | // Misc
1088 | positions: {
1089 | // Standard empty and start position
1090 | empty: '8/8/8/8/8/8/8/8',
1091 | start: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',
1092 | // Some well known positions
1093 | ruyLopez: 'r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R'
1094 | }
1095 | };
1096 |
1097 |
1098 | /*
1099 | ----------------------------------------------------------------------------
1100 | ----------------------------------------------------------------------------
1101 |
1102 | PUBLIC STATIC METHODS
1103 |
1104 | ----------------------------------------------------------------------------
1105 | ----------------------------------------------------------------------------
1106 | */
1107 |
1108 | /*
1109 | ----------------------------------------------------------------------------
1110 | Piece, player etc.
1111 | ----------------------------------------------------------------------------
1112 | */
1113 | /**
1114 | Checks wether a piece code represents a white player.
1115 |
1116 | @method isPieceWhite
1117 | @public
1118 | @static
1119 | @param {String} piece A piece code (B or k)
1120 | @return {Boolean}
1121 | */
1122 | ChessUtils.isPieceWhite = function (piece) {
1123 | return (piece.toUpperCase() === piece) && (piece !== ChessUtils.PIECE.none);
1124 | };
1125 | /**
1126 | Checks wether a piece code represents a black player.
1127 |
1128 | @method isPieceBlack
1129 | @public
1130 | @static
1131 | @param {String} piece A piece code (B or k)
1132 | @return {Boolean}
1133 | */
1134 | ChessUtils.isPieceBlack = function (piece) {
1135 | return (piece.toUpperCase() !== piece);
1136 | };
1137 | /**
1138 | Puts to piece into the valid format according to player information.
1139 |
1140 | @method getPieceForPlayer
1141 | @public
1142 | @static
1143 | @param {String} piece A piece code (B or k)
1144 | @param {String} piece A player code
1145 | @return {String} The piece code in the proper case
1146 | */
1147 | ChessUtils.getPieceForPlayer = function (piece, playerCode) {
1148 | return playerCode === ChessUtils.PLAYER.white.code ? piece.toUpperCase() : piece.toLowerCase();
1149 | };
1150 | /**
1151 | Gets the player of the piece.
1152 |
1153 | @method convertPieceToPlayerName
1154 | @public
1155 | @static
1156 | @param {String} piece A piece code (B or k)
1157 | @return {String} The player name (white or black)
1158 | */
1159 | ChessUtils.getPlayerNameFromPiece = function (piece) {
1160 | if (ChessUtils.isPieceWhite(piece)) { return ChessUtils.PLAYER.white.className; }
1161 | if (ChessUtils.isPieceBlack(piece)) { return ChessUtils.PLAYER.black.className; }
1162 | };
1163 | /**
1164 | Gets the player of the piece.
1165 |
1166 | @method getPlayerCodeFromPiece
1167 | @public
1168 | @static
1169 | @param {String} piece A piece code (B or k)
1170 | @return {String} The player code (w or b)
1171 | */
1172 | ChessUtils.getPlayerCodeFromPiece = function (piece) {
1173 | if (ChessUtils.isPieceWhite(piece)) { return ChessUtils.PLAYER.white.code; }
1174 | if (ChessUtils.isPieceBlack(piece)) { return ChessUtils.PLAYER.black.code; }
1175 | };
1176 |
1177 | /*
1178 | ----------------------------------------------------------------------------
1179 | Validating methods
1180 | ----------------------------------------------------------------------------
1181 | */
1182 | /**
1183 | Checks wether a position string is valid.
1184 |
1185 | @method isValidPosition
1186 | @public
1187 | @static
1188 | @param {String} position A position string
1189 | @return {Boolean}
1190 | */
1191 | ChessUtils.isValidPosition = function (position) {
1192 | if (typeof position !== 'string') { return false; }
1193 | if ((position.length !== 64) ||
1194 | position.search(ChessUtils.POSITION.validator) !== -1) {
1195 | return false;
1196 | }
1197 | };
1198 | /**
1199 | Checks wether a fen string is valid.
1200 |
1201 | @method isValidFen
1202 | @public
1203 | @static
1204 | @param {String} fen A fen string
1205 | @return {Boolean}
1206 | */
1207 | ChessUtils.isValidFen = function (fen) {
1208 | var fenRows,
1209 | i;
1210 |
1211 | if (typeof fen !== 'string') { return false; }
1212 |
1213 | fen = fen.split(' ')[0];
1214 |
1215 | fenRows = fen.split('/');
1216 | if (fenRows.length !== 8) { return false; }
1217 |
1218 | // check the piece sections
1219 | for (i = 0; i < 8; i++) {
1220 | if (fenRows[i] === '' ||
1221 | fenRows[i].length > 8 ||
1222 | fenRows[i].search(ChessUtils.FEN.rowValidator) !== -1) {
1223 | return false;
1224 | }
1225 | }
1226 |
1227 | return true;
1228 | };
1229 |
1230 | /**
1231 | Checks wether a notation position string is valid.
1232 | Notation position string example: 'a1' which represents the first column and row
1233 |
1234 | @method isValidNotationPosition
1235 | @public
1236 | @static
1237 | @param {String} position A notation position string
1238 | @return {Boolean}
1239 | */
1240 | ChessUtils.isValidNotationPosition = function (position) {
1241 | if (typeof position !== 'string') { return false; }
1242 | return (position.search(ChessUtils.NOTATION.positionValidator) !== -1);
1243 | };
1244 | /**
1245 | Checks wether a notation piece string is valid.
1246 | Notation piece string example: 'bK' which represents black king
1247 |
1248 | @method isValidNotationPiece
1249 | @public
1250 | @static
1251 | @param {String} piece A notation piece string
1252 | @return {Boolean}
1253 | */
1254 | ChessUtils.isValidNotationPiece = function (piece) {
1255 | if (typeof piece !== 'string') { return false; }
1256 | return (piece.search(ChessUtils.NOTATION.pieceValidator) !== -1);
1257 | };
1258 | /**
1259 | Checks wether a notation object is valid.
1260 | Notation object example: {a4: 'bK',c4: 'wK',a7: 'wR'}
1261 |
1262 | @method isValidNotation
1263 | @public
1264 | @static
1265 | @param {Object} notation An object containing valid notations
1266 | @return {Boolean}
1267 | */
1268 | ChessUtils.isValidNotation = function (notation) {
1269 | var i;
1270 |
1271 | if (typeof notation !== 'object') {
1272 | return false;
1273 | }
1274 |
1275 | for (i in notation) {
1276 | if (notation.hasOwnProperty(i)) {
1277 | if (!ChessUtils.isValidNotationPosition(i) || !ChessUtils.isValidNotationPiece(notation[i])) {
1278 | return false;
1279 | }
1280 | }
1281 | }
1282 |
1283 | return true;
1284 | };
1285 |
1286 | /*
1287 | ----------------------------------------------------------------------------
1288 | Conversion of chessboard notation methods
1289 | ----------------------------------------------------------------------------
1290 | */
1291 | /**
1292 | Creates a position string from a fen string. (see http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation)
1293 |
1294 | @method convertFenToPosition
1295 | @public
1296 | @static
1297 | @param {String} fen The fen string. Only the first part is used.
1298 | @return {String} The position string representation of the fen string.
1299 | */
1300 | ChessUtils.convertFenToPosition = function (fen) {
1301 | var i,
1302 | position;
1303 |
1304 | if (ChessUtils.isValidFen(fen)) {
1305 | throw new Error('Invalid fen string "' + fen + '".');
1306 | }
1307 |
1308 | // Keeping the first part of fen
1309 | position = fen.split(' ')[0];
1310 |
1311 | for (i = 1; i <= 8; i++) {
1312 | position = position.replace(new RegExp(i, 'g'), ChessUtils.repeatString('0', i));
1313 | }
1314 | position = position.replace(new RegExp(ChessUtils.FEN.rowSeparator, 'g'), '');
1315 |
1316 | return position;
1317 | };
1318 | /**
1319 | Creates a fen string from a position string. (see http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation)
1320 |
1321 | @method convertPositionToFen
1322 | @public
1323 | @static
1324 | @param {String} position The position string.
1325 | @return {String} The fen string representation of the position string.
1326 | */
1327 | ChessUtils.convertPositionToFen = function (position) {
1328 | var i,
1329 | fen = '';
1330 |
1331 | if (ChessUtils.isValidPosition(position)) {
1332 | throw new Error('Invalid position string "' + position + '".');
1333 | }
1334 |
1335 | fen = position.substr(0, 8);
1336 | for (i = 1; i < 8; i++) {
1337 | fen += ChessUtils.FEN.rowSeparator + position.substr(i * 8, 8);
1338 | }
1339 | for (i = 8; i > 0; i--) {
1340 | fen = fen.replace(new RegExp(ChessUtils.repeatString('0', i), 'g'), i);
1341 | }
1342 |
1343 | return fen;
1344 | };
1345 |
1346 |
1347 | /**
1348 | Returns the notation piece string from a piece code string that is used in fen and position strings (K -> wK or k -> bK).
1349 |
1350 | @method convertPieceToNotationPiece
1351 | @public
1352 | @static
1353 | @param {String} piece The piece code string.
1354 | @return {String} The notation piece string.
1355 | */
1356 | ChessUtils.convertPieceToNotationPiece = function (piece) {
1357 | return (ChessUtils.isPieceWhite(piece) ?
1358 | ChessUtils.PLAYER.white.notation : ChessUtils.PLAYER.black.notation) +
1359 | piece.toUpperCase();
1360 | };
1361 | /**
1362 | Returns the piece code string that is used in fen and position strings from a notation piece string (wK -> K or bK -> k).
1363 |
1364 | @method convertNotationPieceToPiece
1365 | @public
1366 | @static
1367 | @param {String} piece The notation piece string.
1368 | @return {String} The piece code string.
1369 | */
1370 | ChessUtils.convertNotationPieceToPiece = function (notationPiece) {
1371 | return ((notationPiece.split('')[0] === ChessUtils.PLAYER.white.notation) ?
1372 | notationPiece.split('')[1].toUpperCase() :
1373 | notationPiece.split('')[1].toLowerCase());
1374 | };
1375 | /**
1376 | Returns the notation square from an index (0-63) (0 -> a8 or 63 -> h1).
1377 |
1378 | @method convertIndexToNotationSquare
1379 | @public
1380 | @static
1381 | @param {Integer} index The index of the square
1382 | @return {String} The notation form of the square
1383 | */
1384 | ChessUtils.convertIndexToNotationSquare = function (index) {
1385 | return ChessUtils.NOTATION.columns[ChessUtils.convertIndexToColumn(index)] +
1386 | ChessUtils.NOTATION.rows[ChessUtils.convertIndexToRow(index)];
1387 |
1388 | };
1389 | /**
1390 | Returns the index (0-63) from a notation square (a8 -> 0 or h1 -> 63).
1391 |
1392 | @method convertNotationSquareToIndex
1393 | @public
1394 | @static
1395 | @param {String} notationSquare The notation form of the square
1396 | @return {Integer} The index of the square
1397 | */
1398 | ChessUtils.convertNotationSquareToIndex = function (notationSquare) {
1399 | var index,
1400 | i,
1401 | row,
1402 | column;
1403 |
1404 | if (notationSquare[notationSquare.length - 1] === '+') {
1405 | notationSquare = notationSquare.substring(0, notationSquare.length - 1);
1406 | }
1407 | column = notationSquare.split('')[notationSquare.length - 2];
1408 | row = notationSquare.split('')[notationSquare.length - 1];
1409 |
1410 | return ChessUtils.convertRowColumnToIndex(
1411 | ChessUtils.NOTATION.rowConverter.search(row),
1412 | ChessUtils.NOTATION.columnConverter.search(column)
1413 | );
1414 | };
1415 | /**
1416 | Creates a position string from a notation object.
1417 |
1418 | @method convertNotationToPosition
1419 | @public
1420 | @static
1421 | @param {Object} notation The notation object (For example {a1:bK, b1:wQ}).
1422 | @return {String} The position string representation of the notation object.
1423 | */
1424 | ChessUtils.convertNotationToPosition = function (notation) {
1425 | var position = ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.empty),
1426 | square;
1427 |
1428 | if (ChessUtils.isValidNotation(position)) {
1429 | throw new Error('Invalid notation object "' + notation.toString() + '".');
1430 | }
1431 |
1432 | for (square in notation) {
1433 | if (notation.hasOwnProperty(square)) {
1434 | position =
1435 | ChessUtils.replaceStringAt(position,
1436 | ChessUtils.convertNotationSquareToIndex(square),
1437 | ChessUtils.convertNotationPieceToPiece(notation[square]));
1438 | }
1439 | }
1440 |
1441 | return position;
1442 |
1443 | };
1444 | /**
1445 | Creates a notation object from a position string.
1446 |
1447 | @method convertPositionToNotation
1448 | @public
1449 | @static
1450 | @param {String} position The position string.
1451 | @return {Object} The notation object representation of the position string.
1452 | */
1453 | ChessUtils.convertPositionToNotation = function (position) {
1454 | var notation = {},
1455 | i;
1456 |
1457 | if (ChessUtils.isValidPosition(position)) {
1458 | throw new Error('Invalid position string "' + position + '".');
1459 | }
1460 |
1461 | for (i = 0; i < 64; i++) {
1462 | if (position[i] !== ChessUtils.POSITION.empty) {
1463 | notation[ChessUtils.convertIndexToNotationSquare(i)] = ChessUtils.convertPieceToNotationPiece(position[i]);
1464 | }
1465 | }
1466 |
1467 | return notation;
1468 | };
1469 |
1470 | /*
1471 | ----------------------------------------------------------------------------
1472 | Conversion of coordinates and connected methods
1473 | ----------------------------------------------------------------------------
1474 | */
1475 | /**
1476 | Checks wether a row index (0-7) and a column index (0-7) is valid.
1477 |
1478 | @method isOutOfBoard
1479 | @public
1480 | @static
1481 | @param {Integer} row
1482 | @param {Integer} column
1483 | @return {Integer}
1484 | */
1485 | ChessUtils.isOutOfBoard = function (row, column) {
1486 | return (row < 0 || row > 7 || column < 0 || column > 7);
1487 | };
1488 | /**
1489 | Converts an index (0-63) to a column index (0-7). No parameter checking.
1490 |
1491 | @method convertIndexToColumn
1492 | @public
1493 | @static
1494 | @param {Integer} index
1495 | @return {Integer}
1496 | */
1497 | ChessUtils.convertIndexToColumn = function (index) {
1498 | return index % 8;
1499 | };
1500 | /**
1501 | Converts an index (0-63) to a row index (0-7). No parameter checking.
1502 |
1503 | @method convertIndexToRow
1504 | @public
1505 | @static
1506 | @param {Integer} index
1507 | @return {Integer}
1508 | */
1509 | ChessUtils.convertIndexToRow = function (index) {
1510 | return 7 - Math.floor(index / 8);
1511 | };
1512 | /**
1513 | Converts a row index (0-7) and a column index (0-7) to an index (0-63). No parameter checking.
1514 |
1515 | @method convertRowColumnToIndex
1516 | @public
1517 | @static
1518 | @param {Integer} row
1519 | @param {Integer} column
1520 | @return {Integer}
1521 | */
1522 | ChessUtils.convertRowColumnToIndex = function (row, column) {
1523 | return (7 - row) * 8 + column;
1524 | };
1525 |
1526 |
1527 | /*
1528 | ----------------------------------------------------------------------------
1529 | Utility functions
1530 | ----------------------------------------------------------------------------
1531 | */
1532 | /**
1533 | Repeats a given string (or character) x times.
1534 | Example: repeatString('0', 3) returns '000'.
1535 | (No parameter checking!)
1536 |
1537 | @method repeatString
1538 | @public
1539 | @static
1540 | @param {String} what The string to copy
1541 | @param {Integer} times The number of repeats
1542 | @return {String} The 'what' string repeated 'times' times.
1543 | */
1544 | ChessUtils.repeatString = function (what, times) {
1545 | var helper = [];
1546 |
1547 | helper.length = times + 1;
1548 | return helper.join(what);
1549 |
1550 | };
1551 |
1552 | /**
1553 | Replaces a character/string in a given string (or character) x times.
1554 | Example: replaceStringAt('Hong', 0, 'K') returns 'Kong', replaceStringAt('Hong', 3, 'g Kong') returns 'Hong Kong'.
1555 | (No parameter checking!)
1556 |
1557 | @method replaceStringAt
1558 | @public
1559 | @static
1560 | @param {String} inputString The string to insert the character into
1561 | @param {Integer} index The index where the insertion should happen
1562 | @param {Integer} what What to insert
1563 | @return {String} The result after replacement
1564 | */
1565 | ChessUtils.replaceStringAt = function (inputString, index, what) {
1566 |
1567 | var a;
1568 |
1569 | a = inputString.split('');
1570 | a[index] = what;
1571 | return a.join('');
1572 | };
1573 |
1574 |
1575 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
1576 | module.exports.ChessUtils = ChessUtils;
1577 | } else {
1578 | window.ChessUtils = ChessUtils;
1579 | }
1580 |
1581 | }());
1582 |
--------------------------------------------------------------------------------
/js/chessboard.min.js:
--------------------------------------------------------------------------------
1 | function Chessboard(a,b){"use strict";Chessboard.ANIMATION={fadeInTime:1e3,fadeOutTime:1e3},Chessboard.CSS_PREFIX="chess_",Chessboard.CSS={pathSeparator:"_",board:{id:Chessboard.CSS_PREFIX+"board",className:Chessboard.CSS_PREFIX+"board"},square:{className:Chessboard.CSS_PREFIX+"square",lastColumn:{className:Chessboard.CSS_PREFIX+"square_last_column"},idPrefix:Chessboard.CSS_PREFIX+"square",dark:{className:Chessboard.CSS_PREFIX+"square_dark"},light:{className:Chessboard.CSS_PREFIX+"square_light"},createClassName:function(a){return" "+((a+ChessUtils.convertIndexToRow(a))%2===0?Chessboard.CSS.square.dark.className:Chessboard.CSS.square.light.className)},selected:{className:Chessboard.CSS_PREFIX+"square_selected"},validMove:{className:Chessboard.CSS_PREFIX+"square_valid_move"}},player:{black:{className:Chessboard.CSS_PREFIX+"player_"+ChessUtils.PLAYER.black.className},white:{className:Chessboard.CSS_PREFIX+"player_"+ChessUtils.PLAYER.white.className},createClassName:function(a){return"white"===a?Chessboard.CSS.player.white.className:Chessboard.CSS.player.black.className}},piece:{idPrefix:Chessboard.CSS_PREFIX+"piece",className:Chessboard.CSS_PREFIX+"piece",createClassName:function(a){return Chessboard.CSS.piece.className+"_"+a},none:{className:Chessboard.CSS_PREFIX+"piece_none"}},label:{className:Chessboard.CSS_PREFIX+"label",hidden:{className:Chessboard.CSS_PREFIX+"label_hidden"},row:{className:Chessboard.CSS_PREFIX+"label_row",reversed:{className:Chessboard.CSS_PREFIX+"label_row_reversed"}},column:{className:Chessboard.CSS_PREFIX+"label_column",reversed:{className:Chessboard.CSS_PREFIX+"label_column_reversed"}}},style:{id:Chessboard.CSS_PREFIX+"style"}};var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z=this,A=!1,B=ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.empty),C=null,D=ChessUtils.ORIENTATION.white,E={useAnimation:!0,showBoardLabels:!0,showNextMove:!0},F={onMove:null,onPieceSelected:null,onChange:null,onChanged:null},G=!1,H=!0;this.clear=function(){G||(this.setPosition(ChessUtils.FEN.positions.empty),this.setOrientation(ChessUtils.ORIENTATION.white))},this.destroy=function(){$(x).html(""),g()},this.position=function(a,b){return 0===arguments.length||"undefined"==typeof a||"string"==typeof a&&a.toLowerCase()===ChessUtils.FEN.id||"string"==typeof a&&a.toLowerCase()===ChessUtils.NOTATION.id?this.getPosition(a):void this.setPosition(a,b)},this.setPosition=function(a,b){var c=A;if(!G&&(l(),b=1===arguments.length||"undefined"==typeof b?E.useAnimation:b,"string"==typeof a&&a.toLowerCase()===ChessUtils.FEN.startId?a=ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.start):"string"==typeof a&&a.toLowerCase()===ChessUtils.FEN.emptyId?a=ChessUtils.convertFenToPosition(ChessUtils.FEN.positions.empty):"string"==typeof a?-1!==a.indexOf(ChessUtils.FEN.rowSeparator)&&(a=ChessUtils.convertFenToPosition(a)):"object"==typeof a&&(a=ChessUtils.convertNotationToPosition(a)),B!==a)){if(F.hasOwnProperty("onChange")===!0&&"function"==typeof F.onChange&&!H){if(G=!0,!F.onChange(B,a))return;G=!1}A=!1,b===!0?(r(a),B=a):(B=a,q()),F.hasOwnProperty("onChanged")!==!0||"function"!=typeof F.onChanged||H||(G=!0,F.onChanged(B),G=!1),A=c}},this.getPosition=function(a){return 0!==arguments.length&&a?a.toLowerCase()===ChessUtils.FEN.id?ChessUtils.convertPositionToFen(B):a.toLowerCase()===ChessUtils.NOTATION.id?ChessUtils.convertPositionToNotation(B):void 0:B},this.move=function(a){var b,c,d=[],e=[],f=B,g=E.useAnimation;if(!G){if("boolean"==typeof arguments[arguments.length-1]?(g=arguments[arguments.length-1],c=arguments.length-1):c=arguments.length,"string"==typeof a)if(-1!==a.search("-"))for(b=0;c>b;b++)d.push(ChessUtils.convertNotationSquareToIndex(arguments[b].split("-")[0])),e.push(ChessUtils.convertNotationSquareToIndex(arguments[b].split("-")[1]));else for(b=0;c>b;b+=2)d.push(ChessUtils.convertNotationSquareToIndex(arguments[b])),e.push(ChessUtils.convertNotationSquareToIndex(arguments[b+1]));else for(b=0;c>b;b+=2)d.push(arguments[b]),e.push(arguments[b+1]);for(b=0;b',e+='',b=0;64>b;b++)c=u(b),d=Chessboard.CSS.square.className,d+=" "+Chessboard.CSS.square.createClassName(b),b%8===7&&(d+=" "+Chessboard.CSS.square.lastColumn.className),e+='
',0===ChessUtils.convertIndexToRow(b)&&(e+='
'+ChessUtils.NOTATION.columnConverter[ChessUtils.convertIndexToColumn(b)]+"
"),7===ChessUtils.convertIndexToRow(b)&&(e+='
'+ChessUtils.NOTATION.columnConverter[ChessUtils.convertIndexToColumn(7-b)]+"
"),0===ChessUtils.convertIndexToColumn(b)&&(e+='
'+ChessUtils.NOTATION.rowConverter[ChessUtils.convertIndexToRow(b)]+"
"),7===ChessUtils.convertIndexToColumn(b)&&(e+='
'+ChessUtils.NOTATION.rowConverter[7-ChessUtils.convertIndexToRow(b)]+"
"),d=Chessboard.CSS.piece.className,d+=" "+Chessboard.CSS.piece.none.className,e+='
',e+="
";e+="
",$(x).html(e),$(x).css("display","inline-block")},f=function(){$(window).on("resize.chessEvents",h),$("div"+x+" div."+Chessboard.CSS.square.className).on("click",i)},g=function(){$(window).unbind("resize.chessEvents"),$("div"+x+" div."+Chessboard.CSS.square.className).unbind("click")},h=function(){var a,b,c,d;a=Math.floor($(x).width()/8),b=.85*a,c=Math.min(Math.max(.5*a,8),20),d=" div"+x+" div."+Chessboard.CSS.piece.className+" { font-size: "+b+"px; height: "+a+"px; } div"+x+" div."+Chessboard.CSS.label.className+" { font-size: "+c+"px; "+(E.showBoardLabels?"":"display: none;")+" }",$("#"+w()).html(d)},i=function(){var a,b,c=k($(this).context.id);if(A)if(null!==C&&y.indexOf(c)>-1)F.onMove&&(b=F.onMove({from:ChessUtils.convertIndexToNotationSquare(C),to:ChessUtils.convertIndexToNotationSquare(c)}),null!==b&&(z.setPosition(b),l()));else if(C!==c&&F.onPieceSelected)if(y=F.onPieceSelected(ChessUtils.convertIndexToNotationSquare(c)),y&&0!==y.length){if(m(c),!n(c))for(a=0;aa;a++)p(a,B[a])},r=function(a){var b;for(b=0;64>b;b++)B[b]!==a[b]&&("0"!==B[b]&&"0"!==a[b]?(p(b,a[b],!0),$(o(b)).animate({opacity:"1"},Chessboard.ANIMATION.fadeInTime)):"0"===B[b]?(p(b,a[b],!0),$(o(b)).animate({opacity:"1"},Chessboard.ANIMATION.fadeInTime)):"0"===a[b]&&p(b,a[b],!0))},s=function(){return $(x).attr("id")+"_"},t=function(){return s()+Chessboard.CSS.board.id},u=function(a){return s()+Chessboard.CSS.square.idPrefix+"_"+(D===ChessUtils.ORIENTATION.white?a:63-a)},v=function(a){return s()+Chessboard.CSS.piece.idPrefix+"_"+(D===ChessUtils.ORIENTATION.white?a:63-a)},w=function(){return s()+Chessboard.CSS.style.id},this.setPosition(c(a,b)),h(),D===ChessUtils.ORIENTATION.black&&(this.setOrientation(ChessUtils.ORIENTATION.flip),D=ChessUtils.ORIENTATION.black),H=!1}!function(){"use strict";var a={};a.PLAYER={black:{code:"b",notation:"b",className:"black"},white:{code:"w",notation:"w",className:"white"}},a.ORIENTATION={white:"w",black:"b",flip:"flip"},a.PIECE={none:"0",pawn:"p",rook:"r",knight:"n",bishop:"b",queen:"q",king:"k",codeToPieceName:{p:"pawn",r:"rook",n:"knight",b:"bishop",q:"queen",k:"king"}},a.POSITION={empty:"0",piece:{pawn:"p",rook:"r",knight:"n",bishop:"b",queen:"q",king:"k"},validator:/^[kqrbnpKQRNBP0]+$/},a.NOTATION={id:"notation",positionValidator:/^[a-h][1-8]$/,pieceValidator:/^[bw][KQRNBP]$/,columns:String.prototype.split.call("abcdefgh",""),rows:String.prototype.split.call("12345678",""),columnConverter:"abcdefgh",rowConverter:"12345678"},a.FEN={id:"fen",startId:"start",emptyId:"empty",rowValidator:/^[kqrbnpKQRNBP1-8]+$/,rowSeparator:"/",positions:{empty:"8/8/8/8/8/8/8/8",start:"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR",ruyLopez:"r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R"}},a.isPieceWhite=function(b){return b.toUpperCase()===b&&b!==a.PIECE.none},a.isPieceBlack=function(a){return a.toUpperCase()!==a},a.getPieceForPlayer=function(b,c){return c===a.PLAYER.white.code?b.toUpperCase():b.toLowerCase()},a.getPlayerNameFromPiece=function(b){return a.isPieceWhite(b)?a.PLAYER.white.className:a.isPieceBlack(b)?a.PLAYER.black.className:void 0},a.getPlayerCodeFromPiece=function(b){return a.isPieceWhite(b)?a.PLAYER.white.code:a.isPieceBlack(b)?a.PLAYER.black.code:void 0},a.isValidPosition=function(b){return"string"!=typeof b?!1:64!==b.length||-1!==b.search(a.POSITION.validator)?!1:void 0},a.isValidFen=function(b){var c,d;if("string"!=typeof b)return!1;if(b=b.split(" ")[0],c=b.split("/"),8!==c.length)return!1;for(d=0;8>d;d++)if(""===c[d]||c[d].length>8||-1!==c[d].search(a.FEN.rowValidator))return!1;return!0},a.isValidNotationPosition=function(b){return"string"!=typeof b?!1:-1!==b.search(a.NOTATION.positionValidator)},a.isValidNotationPiece=function(b){return"string"!=typeof b?!1:-1!==b.search(a.NOTATION.pieceValidator)},a.isValidNotation=function(b){var c;if("object"!=typeof b)return!1;for(c in b)if(b.hasOwnProperty(c)&&(!a.isValidNotationPosition(c)||!a.isValidNotationPiece(b[c])))return!1;return!0},a.convertFenToPosition=function(b){var c,d;if(a.isValidFen(b))throw new Error('Invalid fen string "'+b+'".');for(d=b.split(" ")[0],c=1;8>=c;c++)d=d.replace(new RegExp(c,"g"),a.repeatString("0",c));return d=d.replace(new RegExp(a.FEN.rowSeparator,"g"),"")},a.convertPositionToFen=function(b){var c,d="";if(a.isValidPosition(b))throw new Error('Invalid position string "'+b+'".');for(d=b.substr(0,8),c=1;8>c;c++)d+=a.FEN.rowSeparator+b.substr(8*c,8);for(c=8;c>0;c--)d=d.replace(new RegExp(a.repeatString("0",c),"g"),c);return d},a.convertPieceToNotationPiece=function(b){return(a.isPieceWhite(b)?a.PLAYER.white.notation:a.PLAYER.black.notation)+b.toUpperCase()},a.convertNotationPieceToPiece=function(b){return b.split("")[0]===a.PLAYER.white.notation?b.split("")[1].toUpperCase():b.split("")[1].toLowerCase()},a.convertIndexToNotationSquare=function(b){return a.NOTATION.columns[a.convertIndexToColumn(b)]+a.NOTATION.rows[a.convertIndexToRow(b)]},a.convertNotationSquareToIndex=function(b){var c,d;return"+"===b[b.length-1]&&(b=b.substring(0,b.length-1)),d=b.split("")[b.length-2],c=b.split("")[b.length-1],a.convertRowColumnToIndex(a.NOTATION.rowConverter.search(c),a.NOTATION.columnConverter.search(d))},a.convertNotationToPosition=function(b){var c,d=a.convertFenToPosition(a.FEN.positions.empty);if(a.isValidNotation(d))throw new Error('Invalid notation object "'+b.toString()+'".');for(c in b)b.hasOwnProperty(c)&&(d=a.replaceStringAt(d,a.convertNotationSquareToIndex(c),a.convertNotationPieceToPiece(b[c])));return d},a.convertPositionToNotation=function(b){var c,d={};if(a.isValidPosition(b))throw new Error('Invalid position string "'+b+'".');for(c=0;64>c;c++)b[c]!==a.POSITION.empty&&(d[a.convertIndexToNotationSquare(c)]=a.convertPieceToNotationPiece(b[c]));return d},a.isOutOfBoard=function(a,b){return 0>a||a>7||0>b||b>7},a.convertIndexToColumn=function(a){return a%8},a.convertIndexToRow=function(a){return 7-Math.floor(a/8)},a.convertRowColumnToIndex=function(a,b){return 8*(7-a)+b},a.repeatString=function(a,b){var c=[];return c.length=b+1,c.join(a)},a.replaceStringAt=function(a,b,c){var d;return d=a.split(""),d[b]=c,d.join("")},"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports.ChessUtils=a:window.ChessUtils=a}();
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chessboard-js",
3 | "dependencies": {},
4 | "devDependencies": {
5 | "grunt": "~0.4.1",
6 | "grunt-contrib-cssmin": "~0.9.0",
7 | "grunt-contrib-uglify": "~0.4.0",
8 | "time-grunt": "~0.3.1",
9 |
10 | "selenium-webdriver": "~2.41.0",
11 |
12 | "mocha": "~1.18.2",
13 | "chai": "~1.9.1"
14 | },
15 | "engines": {
16 | "node": ">=0.10.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/manual/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chessboardjs-manual-testing-page",
3 | "version": "0.1.0",
4 | "dependencies": {
5 | "jquery": "~2.1.0",
6 | "bootstrap": "~3.1.1",
7 | "chess.js": "git://github.com/jhlywa/chess.js"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/test/manual/js/app.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true, plusplus: true, devel: true, browser: true*/
2 | /*globals $, Chess, Chessboard, ChessUtils*/
3 |
4 | 'use strict';
5 |
6 | var chess,
7 | board;
8 |
9 | function updateGameInfo(status) {
10 | $('#info-status').html(status);
11 | $('#info-fen').html(chess.fen());
12 | $('#info-pgn').html(chess.pgn());
13 | }
14 |
15 | function resetGame() {
16 | board.setPosition(ChessUtils.FEN.startId);
17 | chess.reset();
18 |
19 | updateGameInfo('Next player is white.');
20 | }
21 |
22 | function pieceMove(move) {
23 |
24 | var nextPlayer,
25 | status,
26 | chessMove = chess.move({
27 | from: move.from,
28 | to: move.to,
29 | promotion: 'q'
30 | });
31 |
32 |
33 | nextPlayer = 'white';
34 | if (chess.turn() === 'b') {
35 | nextPlayer = 'black';
36 | }
37 |
38 | if (chessMove !== null) {
39 | if (chess.in_checkmate() === true) {
40 | status = 'CHECKMATE! Player ' + nextPlayer + ' lost.';
41 | } else if (chess.in_draw() === true) {
42 | status = 'DRAW!';
43 | } else {
44 | status = 'Next player is ' + nextPlayer + '.';
45 |
46 | if (chess.in_check() === true) {
47 | status = 'CHECK! ' + status;
48 | }
49 | }
50 |
51 | updateGameInfo(status);
52 | }
53 |
54 | return chess.fen();
55 | }
56 |
57 | function pieceSelected(notationSquare) {
58 | var i,
59 | movesNotation,
60 | movesPosition = [];
61 |
62 | movesNotation = chess.moves({square: notationSquare, verbose: true});
63 | for (i = 0; i < movesNotation.length; i++) {
64 | movesPosition.push(ChessUtils.convertNotationSquareToIndex(movesNotation[i].to));
65 | }
66 | return movesPosition;
67 | }
68 |
69 |
70 | function updateUserInputEnabledInfo() {
71 | $('#isUserInputEnabled').html('User input is ' + (board.isUserInputEnabled() ? 'enabled' : 'disabled'));
72 | }
73 |
74 | $(document).ready(function () {
75 | chess = new Chess();
76 | board = new Chessboard('board', {
77 | position: ChessUtils.FEN.startId,
78 | eventHandlers: {
79 | onPieceSelected: pieceSelected,
80 | onMove: pieceMove
81 | }
82 | });
83 |
84 | $('#btn-reset-game').click(function () {
85 | resetGame();
86 | });
87 | resetGame();
88 |
89 | //UserInputEnabled
90 | updateUserInputEnabledInfo();
91 | $('#btnEnableUserInput').click(function () {
92 | board.enableUserInput(true);
93 | updateUserInputEnabledInfo();
94 | });
95 | $('#btnDisableUserInput').click(function () {
96 | board.enableUserInput(false);
97 | updateUserInputEnabledInfo();
98 | });
99 |
100 | });
101 |
102 |
103 |
--------------------------------------------------------------------------------
/test/manual/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ChessboardJS - Manual tesing page
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
Public interface
29 |
this.enableUserInput = function (enabled)
30 |
31 |
34 |
37 |
38 |
this.isUserInputEnabled = function ()
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Game information
51 |
52 |
53 |
54 | - Status:
55 | Restart game
56 |
57 | - Fen:
58 | - Pgn:
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/mocha/chess-utils-tests.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true, plusplus: true*/
2 | /*globals chai, describe,it */
3 | /*globals ChessUtils*/
4 |
5 | 'use strict';
6 |
7 | if (typeof chai === 'undefined') { var chai = require('chai'); }
8 | if (typeof ChessUtils === 'undefined') { var ChessUtils = require('../js/chessboard.js').ChessUtils; }
9 |
10 | var expect = chai.expect;
11 |
12 | describe('ChessUtils', function () {
13 |
14 | describe('#repeatString()', function () {
15 | it('should copy "a" 3 times', function () {
16 | expect(ChessUtils.repeatString('a', 3)).to.equal('aaa');
17 | });
18 | it('should copy "Hong" 2 times', function () {
19 | expect(ChessUtils.repeatString('Hong', 2)).to.equal('HongHong');
20 | });
21 | });
22 |
23 | describe('#replaceStringAt()', function () {
24 | it('should change "Hong" to "Kong"', function () {
25 | expect(ChessUtils.replaceStringAt('Hong', 0, 'K')).to.equal('Kong');
26 | });
27 | it('should change "Hong" to "Hong Kong"', function () {
28 | expect(ChessUtils.replaceStringAt('Hong', 3, 'g Kong')).to.equal('Hong Kong');
29 | });
30 | });
31 |
32 | describe('#convertIndexToColumn()', function () {
33 |
34 | it('should return column=7 to index=63', function () {
35 | expect(ChessUtils.convertIndexToColumn(63)).to.equal(7);
36 | });
37 |
38 | it('should return 0 for all indexes below 8', function () {
39 | var i;
40 |
41 | for (i = 0; i < 8; i++) {
42 | expect(ChessUtils.convertIndexToColumn(i)).to.equal(i);
43 | }
44 | });
45 | });
46 |
47 | });
--------------------------------------------------------------------------------
/test/mocha/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ChessUtils tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/test/selenium/selenium-tests.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true, plusplus: true*/
2 |
3 | 'use strict';
4 |
5 | var chai = require('chai');
6 | var ChessUtils = require('../../js/chessboard.js').ChessUtils;
7 | var expect = chai.expect;
8 | var test = require('selenium-webdriver/testing');
9 | var webdriver = require('selenium-webdriver');
10 | var driver;
11 |
12 | test.describe('ChessBoard', function () {
13 |
14 | test.before(function () {
15 | driver = new webdriver.Builder()
16 | .withCapabilities(webdriver.Capabilities.chrome())
17 | .build();
18 |
19 | webdriver.promise.controlFlow().on('uncaughtException', function (e) {
20 | console.error('Unhandled error: ' + e);
21 | });
22 | });
23 |
24 | test.describe('start', function () {
25 |
26 | test.beforeEach(function () {
27 | driver.get('http://localhost:8080/test/selenium/test.html');
28 | driver.executeScript('board.clear()');
29 |
30 | });
31 |
32 | test.it('should setup board to opening position', function () {
33 | var i;
34 |
35 | driver.executeScript('board.setPosition(ChessUtils.FEN.startId)');
36 | driver.executeScript('return board.getPosition(ChessUtils.FEN.id)')
37 | .then(function (position) {
38 | expect(position).to.be.a('string');
39 | expect(position).to.equal(ChessUtils.FEN.positions.start);
40 | });
41 |
42 | });
43 | });
44 |
45 | test.after(function () {
46 | driver.quit();
47 | });
48 |
49 | });
--------------------------------------------------------------------------------
/test/selenium/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ChessboardJS - A responsive, mobile-first javascript chessboard library.
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/yuidoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chessboard-js",
3 | "description": "A responsive mobile-first javascript chessboard library.",
4 | "version": "0.1.0",
5 | "url": "https://github.com/caustique/chessboard-js",
6 | "options": {
7 | "linkNatives": "true",
8 | "outdir": "./doc",
9 | "paths": ["./js"]
10 | }
11 | }
--------------------------------------------------------------------------------