chesscademy 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/chesscademyA.gemspec +24 -0
- data/lib/chesscademyA.rb +8 -0
- data/lib/chesscademyA/version.rb +5 -0
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/.DS_Store +0 -0
- data/vendor/assets/javascripts/chess.js +1646 -0
- data/vendor/assets/javascripts/chessboard.js +1742 -0
- metadata +100 -0
@@ -0,0 +1,1742 @@
|
|
1
|
+
/*!
|
2
|
+
* chessboard.js v0.3.0
|
3
|
+
*
|
4
|
+
* Copyright 2013 Chris Oakman
|
5
|
+
* Released under the MIT license
|
6
|
+
* http://chessboardjs.com/license
|
7
|
+
*
|
8
|
+
* Date: 10 Aug 2013
|
9
|
+
*/
|
10
|
+
|
11
|
+
// start anonymous scope
|
12
|
+
;(function() {
|
13
|
+
'use strict';
|
14
|
+
|
15
|
+
//------------------------------------------------------------------------------
|
16
|
+
// Chess Util Functions
|
17
|
+
//------------------------------------------------------------------------------
|
18
|
+
var COLUMNS = 'abcdefgh'.split('');
|
19
|
+
|
20
|
+
function validMove(move) {
|
21
|
+
// move should be a string
|
22
|
+
if (typeof move !== 'string') return false;
|
23
|
+
|
24
|
+
// move should be in the form of "e2-e4", "f6-d5"
|
25
|
+
var tmp = move.split('-');
|
26
|
+
if (tmp.length !== 2) return false;
|
27
|
+
|
28
|
+
return (validSquare(tmp[0]) === true && validSquare(tmp[1]) === true);
|
29
|
+
}
|
30
|
+
|
31
|
+
function validSquare(square) {
|
32
|
+
if (typeof square !== 'string') return false;
|
33
|
+
return (square.search(/^[a-h][1-8]$/) !== -1);
|
34
|
+
}
|
35
|
+
|
36
|
+
function validPieceCode(code) {
|
37
|
+
if (typeof code !== 'string') return false;
|
38
|
+
return (code.search(/^[bw][KQRNBP]$/) !== -1);
|
39
|
+
}
|
40
|
+
|
41
|
+
// TODO: this whole function could probably be replaced with a single regex
|
42
|
+
function validFen(fen) {
|
43
|
+
if (typeof fen !== 'string') return false;
|
44
|
+
|
45
|
+
// cut off any move, castling, etc info from the end
|
46
|
+
// we're only interested in position information
|
47
|
+
fen = fen.replace(/ .+$/, '');
|
48
|
+
|
49
|
+
// FEN should be 8 sections separated by slashes
|
50
|
+
var chunks = fen.split('/');
|
51
|
+
if (chunks.length !== 8) return false;
|
52
|
+
|
53
|
+
// check the piece sections
|
54
|
+
for (var i = 0; i < 8; i++) {
|
55
|
+
if (chunks[i] === '' ||
|
56
|
+
chunks[i].length > 8 ||
|
57
|
+
chunks[i].search(/[^kqrbnpKQRNBP1-8]/) !== -1) {
|
58
|
+
return false;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
return true;
|
63
|
+
}
|
64
|
+
|
65
|
+
function validPositionObject(pos) {
|
66
|
+
if (typeof pos !== 'object') return false;
|
67
|
+
|
68
|
+
for (var i in pos) {
|
69
|
+
if (pos.hasOwnProperty(i) !== true) continue;
|
70
|
+
|
71
|
+
if (validSquare(i) !== true || validPieceCode(pos[i]) !== true) {
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
return true;
|
77
|
+
}
|
78
|
+
|
79
|
+
// convert FEN piece code to bP, wK, etc
|
80
|
+
function fenToPieceCode(piece) {
|
81
|
+
// black piece
|
82
|
+
if (piece.toLowerCase() === piece) {
|
83
|
+
return 'b' + piece.toUpperCase();
|
84
|
+
}
|
85
|
+
|
86
|
+
// white piece
|
87
|
+
return 'w' + piece.toUpperCase();
|
88
|
+
}
|
89
|
+
|
90
|
+
// convert bP, wK, etc code to FEN structure
|
91
|
+
function pieceCodeToFen(piece) {
|
92
|
+
var tmp = piece.split('');
|
93
|
+
|
94
|
+
// white piece
|
95
|
+
if (tmp[0] === 'w') {
|
96
|
+
return tmp[1].toUpperCase();
|
97
|
+
}
|
98
|
+
|
99
|
+
// black piece
|
100
|
+
return tmp[1].toLowerCase();
|
101
|
+
}
|
102
|
+
|
103
|
+
// convert FEN string to position object
|
104
|
+
// returns false if the FEN string is invalid
|
105
|
+
function fenToObj(fen) {
|
106
|
+
if (validFen(fen) !== true) {
|
107
|
+
return false;
|
108
|
+
}
|
109
|
+
|
110
|
+
// cut off any move, castling, etc info from the end
|
111
|
+
// we're only interested in position information
|
112
|
+
fen = fen.replace(/ .+$/, '');
|
113
|
+
|
114
|
+
var rows = fen.split('/');
|
115
|
+
var position = {};
|
116
|
+
|
117
|
+
var currentRow = 8;
|
118
|
+
for (var i = 0; i < 8; i++) {
|
119
|
+
var row = rows[i].split('');
|
120
|
+
var colIndex = 0;
|
121
|
+
|
122
|
+
// loop through each character in the FEN section
|
123
|
+
for (var j = 0; j < row.length; j++) {
|
124
|
+
// number / empty squares
|
125
|
+
if (row[j].search(/[1-8]/) !== -1) {
|
126
|
+
var emptySquares = parseInt(row[j], 10);
|
127
|
+
colIndex += emptySquares;
|
128
|
+
}
|
129
|
+
// piece
|
130
|
+
else {
|
131
|
+
var square = COLUMNS[colIndex] + currentRow;
|
132
|
+
position[square] = fenToPieceCode(row[j]);
|
133
|
+
colIndex++;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
currentRow--;
|
138
|
+
}
|
139
|
+
|
140
|
+
return position;
|
141
|
+
}
|
142
|
+
|
143
|
+
// position object to FEN string
|
144
|
+
// returns false if the obj is not a valid position object
|
145
|
+
function objToFen(obj) {
|
146
|
+
if (validPositionObject(obj) !== true) {
|
147
|
+
return false;
|
148
|
+
}
|
149
|
+
|
150
|
+
var fen = '';
|
151
|
+
|
152
|
+
var currentRow = 8;
|
153
|
+
for (var i = 0; i < 8; i++) {
|
154
|
+
for (var j = 0; j < 8; j++) {
|
155
|
+
var square = COLUMNS[j] + currentRow;
|
156
|
+
|
157
|
+
// piece exists
|
158
|
+
if (obj.hasOwnProperty(square) === true) {
|
159
|
+
fen += pieceCodeToFen(obj[square]);
|
160
|
+
}
|
161
|
+
|
162
|
+
// empty space
|
163
|
+
else {
|
164
|
+
fen += '1';
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
if (i !== 7) {
|
169
|
+
fen += '/';
|
170
|
+
}
|
171
|
+
|
172
|
+
currentRow--;
|
173
|
+
}
|
174
|
+
|
175
|
+
// squeeze the numbers together
|
176
|
+
// haha, I love this solution...
|
177
|
+
fen = fen.replace(/11111111/g, '8');
|
178
|
+
fen = fen.replace(/1111111/g, '7');
|
179
|
+
fen = fen.replace(/111111/g, '6');
|
180
|
+
fen = fen.replace(/11111/g, '5');
|
181
|
+
fen = fen.replace(/1111/g, '4');
|
182
|
+
fen = fen.replace(/111/g, '3');
|
183
|
+
fen = fen.replace(/11/g, '2');
|
184
|
+
|
185
|
+
return fen;
|
186
|
+
}
|
187
|
+
|
188
|
+
window['ChessBoard'] = window['ChessBoard'] || function(containerElOrId, cfg) {
|
189
|
+
'use strict';
|
190
|
+
|
191
|
+
cfg = cfg || {};
|
192
|
+
|
193
|
+
//------------------------------------------------------------------------------
|
194
|
+
// Constants
|
195
|
+
//------------------------------------------------------------------------------
|
196
|
+
|
197
|
+
var MINIMUM_JQUERY_VERSION = '1.7.0',
|
198
|
+
START_FEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',
|
199
|
+
START_POSITION = fenToObj(START_FEN);
|
200
|
+
|
201
|
+
// use unique class names to prevent clashing with anything else on the page
|
202
|
+
// and simplify selectors
|
203
|
+
var CSS = {
|
204
|
+
alpha: 'alpha-d2270',
|
205
|
+
black: 'black-3c85d',
|
206
|
+
board: 'board-b72b1',
|
207
|
+
chessboard: 'chessboard-63f37',
|
208
|
+
clearfix: 'clearfix-7da63',
|
209
|
+
highlight1: 'highlight1-32417',
|
210
|
+
highlight2: 'highlight2-9c5d2',
|
211
|
+
notation: 'notation-322f9',
|
212
|
+
numeric: 'numeric-fc462',
|
213
|
+
piece: 'piece-417db',
|
214
|
+
row: 'row-5277c',
|
215
|
+
sparePieces: 'spare-pieces-7492f',
|
216
|
+
sparePiecesBottom: 'spare-pieces-bottom-ae20f',
|
217
|
+
sparePiecesTop: 'spare-pieces-top-4028b',
|
218
|
+
square: 'square-55d63',
|
219
|
+
white: 'white-1e1d7'
|
220
|
+
};
|
221
|
+
|
222
|
+
//------------------------------------------------------------------------------
|
223
|
+
// Module Scope Variables
|
224
|
+
//------------------------------------------------------------------------------
|
225
|
+
|
226
|
+
// DOM elements
|
227
|
+
var containerEl,
|
228
|
+
boardEl,
|
229
|
+
draggedPieceEl,
|
230
|
+
sparePiecesTopEl,
|
231
|
+
sparePiecesBottomEl;
|
232
|
+
|
233
|
+
// constructor return object
|
234
|
+
var widget = {};
|
235
|
+
|
236
|
+
//------------------------------------------------------------------------------
|
237
|
+
// Stateful
|
238
|
+
//------------------------------------------------------------------------------
|
239
|
+
|
240
|
+
var ANIMATION_HAPPENING = false,
|
241
|
+
BOARD_BORDER_SIZE = 2,
|
242
|
+
CURRENT_ORIENTATION = 'white',
|
243
|
+
CURRENT_POSITION = {},
|
244
|
+
SQUARE_SIZE,
|
245
|
+
DRAGGED_PIECE,
|
246
|
+
DRAGGED_PIECE_LOCATION,
|
247
|
+
DRAGGED_PIECE_SOURCE,
|
248
|
+
DRAGGING_A_PIECE = false,
|
249
|
+
SPARE_PIECE_ELS_IDS = {},
|
250
|
+
SQUARE_ELS_IDS = {},
|
251
|
+
SQUARE_ELS_OFFSETS;
|
252
|
+
|
253
|
+
//------------------------------------------------------------------------------
|
254
|
+
// JS Util Functions
|
255
|
+
//------------------------------------------------------------------------------
|
256
|
+
|
257
|
+
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
258
|
+
function createId() {
|
259
|
+
return 'xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx'.replace(/x/g, function(c) {
|
260
|
+
var r = Math.random() * 16 | 0;
|
261
|
+
return r.toString(16);
|
262
|
+
});
|
263
|
+
}
|
264
|
+
|
265
|
+
function deepCopy(thing) {
|
266
|
+
return JSON.parse(JSON.stringify(thing));
|
267
|
+
}
|
268
|
+
|
269
|
+
function parseSemVer(version) {
|
270
|
+
var tmp = version.split('.');
|
271
|
+
return {
|
272
|
+
major: parseInt(tmp[0], 10),
|
273
|
+
minor: parseInt(tmp[1], 10),
|
274
|
+
patch: parseInt(tmp[2], 10)
|
275
|
+
};
|
276
|
+
}
|
277
|
+
|
278
|
+
// returns true if version is >= minimum
|
279
|
+
function compareSemVer(version, minimum) {
|
280
|
+
version = parseSemVer(version);
|
281
|
+
minimum = parseSemVer(minimum);
|
282
|
+
|
283
|
+
var versionNum = (version.major * 10000 * 10000) +
|
284
|
+
(version.minor * 10000) + version.patch;
|
285
|
+
var minimumNum = (minimum.major * 10000 * 10000) +
|
286
|
+
(minimum.minor * 10000) + minimum.patch;
|
287
|
+
|
288
|
+
return (versionNum >= minimumNum);
|
289
|
+
}
|
290
|
+
|
291
|
+
//------------------------------------------------------------------------------
|
292
|
+
// Validation / Errors
|
293
|
+
//------------------------------------------------------------------------------
|
294
|
+
|
295
|
+
function error(code, msg, obj) {
|
296
|
+
// do nothing if showErrors is not set
|
297
|
+
if (cfg.hasOwnProperty('showErrors') !== true ||
|
298
|
+
cfg.showErrors === false) {
|
299
|
+
return;
|
300
|
+
}
|
301
|
+
|
302
|
+
var errorText = 'ChessBoard Error ' + code + ': ' + msg;
|
303
|
+
|
304
|
+
// print to console
|
305
|
+
if (cfg.showErrors === 'console' &&
|
306
|
+
typeof console === 'object' &&
|
307
|
+
typeof console.log === 'function') {
|
308
|
+
console.log(errorText);
|
309
|
+
if (arguments.length >= 2) {
|
310
|
+
console.log(obj);
|
311
|
+
}
|
312
|
+
return;
|
313
|
+
}
|
314
|
+
|
315
|
+
// alert errors
|
316
|
+
if (cfg.showErrors === 'alert') {
|
317
|
+
if (obj) {
|
318
|
+
errorText += '\n\n' + JSON.stringify(obj);
|
319
|
+
}
|
320
|
+
console.log(errorText);
|
321
|
+
return;
|
322
|
+
}
|
323
|
+
|
324
|
+
// custom function
|
325
|
+
if (typeof cfg.showErrors === 'function') {
|
326
|
+
cfg.showErrors(code, msg, obj);
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
// check dependencies
|
331
|
+
function checkDeps() {
|
332
|
+
// if containerId is a string, it must be the ID of a DOM node
|
333
|
+
if (typeof containerElOrId === 'string') {
|
334
|
+
// cannot be empty
|
335
|
+
if (containerElOrId === '') {
|
336
|
+
console.log('ChessBoard Error 1001: ' +
|
337
|
+
'The first argument to ChessBoard() cannot be an empty string.' +
|
338
|
+
'\n\nExiting...');
|
339
|
+
return false;
|
340
|
+
}
|
341
|
+
|
342
|
+
// make sure the container element exists in the DOM
|
343
|
+
var el = document.getElementById(containerElOrId);
|
344
|
+
if (! el) {
|
345
|
+
console.log('ChessBoard Error 1002: Element with id "' +
|
346
|
+
containerElOrId + '" does not exist in the DOM.' +
|
347
|
+
'\n\nExiting...');
|
348
|
+
return false;
|
349
|
+
}
|
350
|
+
|
351
|
+
// set the containerEl
|
352
|
+
containerEl = $(el);
|
353
|
+
}
|
354
|
+
|
355
|
+
// else it must be something that becomes a jQuery collection
|
356
|
+
// with size 1
|
357
|
+
// ie: a single DOM node or jQuery object
|
358
|
+
else {
|
359
|
+
containerEl = $(containerElOrId);
|
360
|
+
|
361
|
+
if (containerEl.length !== 1) {
|
362
|
+
console.log('ChessBoard Error 1003: The first argument to ' +
|
363
|
+
'ChessBoard() must be an ID or a single DOM node.' +
|
364
|
+
'\n\nExiting...');
|
365
|
+
return false;
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
// JSON must exist
|
370
|
+
if (! window.JSON ||
|
371
|
+
typeof JSON.stringify !== 'function' ||
|
372
|
+
typeof JSON.parse !== 'function') {
|
373
|
+
console.log('ChessBoard Error 1004: JSON does not exist. ' +
|
374
|
+
'Please include a JSON polyfill.\n\nExiting...');
|
375
|
+
return false;
|
376
|
+
}
|
377
|
+
|
378
|
+
// check for a compatible version of jQuery
|
379
|
+
if (! (typeof window.$ && $.fn && $.fn.jquery &&
|
380
|
+
compareSemVer($.fn.jquery, MINIMUM_JQUERY_VERSION) === true)) {
|
381
|
+
console.log('ChessBoard Error 1005: Unable to find a valid version ' +
|
382
|
+
'of jQuery. Please include jQuery ' + MINIMUM_JQUERY_VERSION + ' or ' +
|
383
|
+
'higher on the page.\n\nExiting...');
|
384
|
+
return false;
|
385
|
+
}
|
386
|
+
|
387
|
+
return true;
|
388
|
+
}
|
389
|
+
|
390
|
+
function validAnimationSpeed(speed) {
|
391
|
+
if (speed === 'fast' || speed === 'slow') {
|
392
|
+
return true;
|
393
|
+
}
|
394
|
+
|
395
|
+
if ((parseInt(speed, 10) + '') !== (speed + '')) {
|
396
|
+
return false;
|
397
|
+
}
|
398
|
+
|
399
|
+
return (speed >= 0);
|
400
|
+
}
|
401
|
+
|
402
|
+
// validate config / set default options
|
403
|
+
function expandConfig() {
|
404
|
+
if (typeof cfg === 'string' || validPositionObject(cfg) === true) {
|
405
|
+
cfg = {
|
406
|
+
position: cfg
|
407
|
+
};
|
408
|
+
}
|
409
|
+
|
410
|
+
// default for orientation is white
|
411
|
+
if (cfg.orientation !== 'black') {
|
412
|
+
cfg.orientation = 'white';
|
413
|
+
}
|
414
|
+
CURRENT_ORIENTATION = cfg.orientation;
|
415
|
+
|
416
|
+
// default for showNotation is true
|
417
|
+
if (cfg.showNotation !== false) {
|
418
|
+
cfg.showNotation = true;
|
419
|
+
}
|
420
|
+
|
421
|
+
// default for draggable is false
|
422
|
+
if (cfg.draggable !== true) {
|
423
|
+
cfg.draggable = false;
|
424
|
+
}
|
425
|
+
|
426
|
+
// default for dropOffBoard is 'snapback'
|
427
|
+
if (cfg.dropOffBoard !== 'trash') {
|
428
|
+
cfg.dropOffBoard = 'snapback';
|
429
|
+
}
|
430
|
+
|
431
|
+
// default for sparePieces is false
|
432
|
+
if (cfg.sparePieces !== true) {
|
433
|
+
cfg.sparePieces = false;
|
434
|
+
}
|
435
|
+
|
436
|
+
// draggable must be true if sparePieces is enabled
|
437
|
+
if (cfg.sparePieces === true) {
|
438
|
+
cfg.draggable = true;
|
439
|
+
}
|
440
|
+
|
441
|
+
// default piece theme is wikipedia
|
442
|
+
if (cfg.hasOwnProperty('pieceTheme') !== true ||
|
443
|
+
(typeof cfg.pieceTheme !== 'string' &&
|
444
|
+
typeof cfg.pieceTheme !== 'function')) {
|
445
|
+
cfg.pieceTheme = '/img/chesspieces/wikipedia/{piece}.png';
|
446
|
+
}
|
447
|
+
|
448
|
+
// animation speeds
|
449
|
+
if (cfg.hasOwnProperty('appearSpeed') !== true ||
|
450
|
+
validAnimationSpeed(cfg.appearSpeed) !== true) {
|
451
|
+
cfg.appearSpeed = 200;
|
452
|
+
}
|
453
|
+
if (cfg.hasOwnProperty('moveSpeed') !== true ||
|
454
|
+
validAnimationSpeed(cfg.moveSpeed) !== true) {
|
455
|
+
cfg.moveSpeed = 200;
|
456
|
+
}
|
457
|
+
if (cfg.hasOwnProperty('snapbackSpeed') !== true ||
|
458
|
+
validAnimationSpeed(cfg.snapbackSpeed) !== true) {
|
459
|
+
cfg.snapbackSpeed = 50;
|
460
|
+
}
|
461
|
+
if (cfg.hasOwnProperty('snapSpeed') !== true ||
|
462
|
+
validAnimationSpeed(cfg.snapSpeed) !== true) {
|
463
|
+
cfg.snapSpeed = 25;
|
464
|
+
}
|
465
|
+
if (cfg.hasOwnProperty('trashSpeed') !== true ||
|
466
|
+
validAnimationSpeed(cfg.trashSpeed) !== true) {
|
467
|
+
cfg.trashSpeed = 100;
|
468
|
+
}
|
469
|
+
|
470
|
+
// make sure position is valid
|
471
|
+
if (cfg.hasOwnProperty('position') === true) {
|
472
|
+
if (cfg.position === 'start') {
|
473
|
+
CURRENT_POSITION = deepCopy(START_POSITION);
|
474
|
+
}
|
475
|
+
|
476
|
+
else if (validFen(cfg.position) === true) {
|
477
|
+
CURRENT_POSITION = fenToObj(cfg.position);
|
478
|
+
}
|
479
|
+
|
480
|
+
else if (validPositionObject(cfg.position) === true) {
|
481
|
+
CURRENT_POSITION = deepCopy(cfg.position);
|
482
|
+
}
|
483
|
+
|
484
|
+
else {
|
485
|
+
error(7263, 'Invalid value passed to config.position.', cfg.position);
|
486
|
+
}
|
487
|
+
}
|
488
|
+
|
489
|
+
return true;
|
490
|
+
}
|
491
|
+
|
492
|
+
//------------------------------------------------------------------------------
|
493
|
+
// DOM Misc
|
494
|
+
//------------------------------------------------------------------------------
|
495
|
+
|
496
|
+
// calculates square size based on the width of the container
|
497
|
+
// got a little CSS black magic here, so let me explain:
|
498
|
+
// get the width of the container element (could be anything), reduce by 1 for
|
499
|
+
// fudge factor, and then keep reducing until we find an exact mod 8 for
|
500
|
+
// our square size
|
501
|
+
function calculateSquareSize() {
|
502
|
+
var containerWidth = parseInt(containerEl.css('width'), 10);
|
503
|
+
|
504
|
+
// defensive, prevent infinite loop
|
505
|
+
if (! containerWidth || containerWidth <= 0) {
|
506
|
+
return 0;
|
507
|
+
}
|
508
|
+
|
509
|
+
// pad one pixel
|
510
|
+
var boardWidth = containerWidth - 1;
|
511
|
+
|
512
|
+
while (boardWidth % 8 !== 0 && boardWidth > 0) {
|
513
|
+
boardWidth--;
|
514
|
+
}
|
515
|
+
|
516
|
+
return (boardWidth / 8);
|
517
|
+
}
|
518
|
+
|
519
|
+
// create random IDs for elements
|
520
|
+
function createElIds() {
|
521
|
+
// squares on the board
|
522
|
+
for (var i = 0; i < COLUMNS.length; i++) {
|
523
|
+
for (var j = 1; j <= 8; j++) {
|
524
|
+
var square = COLUMNS[i] + j;
|
525
|
+
SQUARE_ELS_IDS[square] = square + '-' + createId();
|
526
|
+
}
|
527
|
+
}
|
528
|
+
|
529
|
+
// spare pieces
|
530
|
+
var pieces = 'KQRBNP'.split('');
|
531
|
+
for (var i = 0; i < pieces.length; i++) {
|
532
|
+
var whitePiece = 'w' + pieces[i];
|
533
|
+
var blackPiece = 'b' + pieces[i];
|
534
|
+
SPARE_PIECE_ELS_IDS[whitePiece] = whitePiece + '-' + createId();
|
535
|
+
SPARE_PIECE_ELS_IDS[blackPiece] = blackPiece + '-' + createId();
|
536
|
+
}
|
537
|
+
}
|
538
|
+
|
539
|
+
//------------------------------------------------------------------------------
|
540
|
+
// Markup Building
|
541
|
+
//------------------------------------------------------------------------------
|
542
|
+
|
543
|
+
function buildBoardContainer() {
|
544
|
+
var html = '<div class="' + CSS.chessboard + '">';
|
545
|
+
|
546
|
+
if (cfg.sparePieces === true) {
|
547
|
+
html += '<div class="' + CSS.sparePieces + ' ' +
|
548
|
+
CSS.sparePiecesTop + '"></div>';
|
549
|
+
}
|
550
|
+
|
551
|
+
html += '<div class="' + CSS.board + '"></div>';
|
552
|
+
|
553
|
+
if (cfg.sparePieces === true) {
|
554
|
+
html += '<div class="' + CSS.sparePieces + ' ' +
|
555
|
+
CSS.sparePiecesBottom + '"></div>';
|
556
|
+
}
|
557
|
+
|
558
|
+
html += '</div>';
|
559
|
+
|
560
|
+
return html;
|
561
|
+
}
|
562
|
+
|
563
|
+
/*
|
564
|
+
var buildSquare = function(color, size, id) {
|
565
|
+
var html = '<div class="' + CSS.square + ' ' + CSS[color] + '" ' +
|
566
|
+
'style="width: ' + size + 'px; height: ' + size + 'px" ' +
|
567
|
+
'id="' + id + '">';
|
568
|
+
|
569
|
+
if (cfg.showNotation === true) {
|
570
|
+
|
571
|
+
}
|
572
|
+
|
573
|
+
html += '</div>';
|
574
|
+
|
575
|
+
return html;
|
576
|
+
};
|
577
|
+
*/
|
578
|
+
|
579
|
+
function buildBoard(orientation) {
|
580
|
+
if (orientation !== 'black') {
|
581
|
+
orientation = 'white';
|
582
|
+
}
|
583
|
+
|
584
|
+
var html = '';
|
585
|
+
|
586
|
+
// algebraic notation / orientation
|
587
|
+
var alpha = deepCopy(COLUMNS);
|
588
|
+
var row = 8;
|
589
|
+
if (orientation === 'black') {
|
590
|
+
alpha.reverse();
|
591
|
+
row = 1;
|
592
|
+
}
|
593
|
+
|
594
|
+
var squareColor = 'white';
|
595
|
+
for (var i = 0; i < 8; i++) {
|
596
|
+
html += '<div class="' + CSS.row + '">';
|
597
|
+
for (var j = 0; j < 8; j++) {
|
598
|
+
var square = alpha[j] + row;
|
599
|
+
|
600
|
+
html += '<div class="' + CSS.square + ' ' + CSS[squareColor] + ' ' +
|
601
|
+
'square-' + square + '" ' +
|
602
|
+
'style="width: ' + SQUARE_SIZE + 'px; height: ' + SQUARE_SIZE + 'px" ' +
|
603
|
+
'id="' + SQUARE_ELS_IDS[square] + '" ' +
|
604
|
+
'data-square="' + square + '">';
|
605
|
+
|
606
|
+
if (cfg.showNotation === true) {
|
607
|
+
// alpha notation
|
608
|
+
if ((orientation === 'white' && row === 1) ||
|
609
|
+
(orientation === 'black' && row === 8)) {
|
610
|
+
html += '<div class="' + CSS.notation + ' ' + CSS.alpha + '">' +
|
611
|
+
alpha[j] + '</div>';
|
612
|
+
}
|
613
|
+
|
614
|
+
// numeric notation
|
615
|
+
if (j === 0) {
|
616
|
+
html += '<div class="' + CSS.notation + ' ' + CSS.numeric + '">' +
|
617
|
+
row + '</div>';
|
618
|
+
}
|
619
|
+
}
|
620
|
+
|
621
|
+
html += '</div>'; // end .square
|
622
|
+
|
623
|
+
squareColor = (squareColor === 'white' ? 'black' : 'white');
|
624
|
+
}
|
625
|
+
html += '<div class="' + CSS.clearfix + '"></div></div>';
|
626
|
+
|
627
|
+
squareColor = (squareColor === 'white' ? 'black' : 'white');
|
628
|
+
|
629
|
+
if (orientation === 'white') {
|
630
|
+
row--;
|
631
|
+
}
|
632
|
+
else {
|
633
|
+
row++;
|
634
|
+
}
|
635
|
+
}
|
636
|
+
|
637
|
+
return html;
|
638
|
+
}
|
639
|
+
|
640
|
+
var imgCache = {}
|
641
|
+
function cacheImages() {
|
642
|
+
var pieces = ['wK', 'wQ', 'wR', 'wB', 'wN', 'wP', 'bK', 'bQ', 'bR', 'bB', 'bN', 'bP'];
|
643
|
+
pieces.forEach(function(piece) {
|
644
|
+
var img = new Image()
|
645
|
+
img.onload = function() {
|
646
|
+
imgCache[piece] = getBase64Image(img)
|
647
|
+
}
|
648
|
+
img.src = buildPieceImgSrc(piece)
|
649
|
+
})
|
650
|
+
|
651
|
+
function getBase64Image(img) {
|
652
|
+
var canvas = document.createElement("canvas");
|
653
|
+
canvas.width = img.width;
|
654
|
+
canvas.height = img.height;
|
655
|
+
var ctx = canvas.getContext("2d");
|
656
|
+
ctx.drawImage(img, 0, 0);
|
657
|
+
var dataURL = canvas.toDataURL("image/png");
|
658
|
+
return dataURL;
|
659
|
+
}
|
660
|
+
}
|
661
|
+
|
662
|
+
function buildPieceImgSrc(piece) {
|
663
|
+
if(imgCache[piece]) return imgCache[piece]
|
664
|
+
else return getUrl(piece)
|
665
|
+
|
666
|
+
function getUrl(piece) {
|
667
|
+
if (typeof cfg.pieceTheme === 'function') {
|
668
|
+
return cfg.pieceTheme(piece);
|
669
|
+
}
|
670
|
+
|
671
|
+
if (typeof cfg.pieceTheme === 'string') {
|
672
|
+
return cfg.pieceTheme.replace(/{piece}/g, piece);
|
673
|
+
}
|
674
|
+
|
675
|
+
// NOTE: this should never happen
|
676
|
+
error(8272, 'Unable to build image source for cfg.pieceTheme.');
|
677
|
+
return '';
|
678
|
+
}
|
679
|
+
}
|
680
|
+
|
681
|
+
function buildPiece(piece, hidden, id) {
|
682
|
+
var html = '<img src="' + buildPieceImgSrc(piece) + '" ';
|
683
|
+
if (id && typeof id === 'string') {
|
684
|
+
html += 'id="' + id + '" ';
|
685
|
+
}
|
686
|
+
html += 'alt="" ' +
|
687
|
+
'class="' + CSS.piece + '" ' +
|
688
|
+
'data-piece="' + piece + '" ' +
|
689
|
+
'style="width: ' + SQUARE_SIZE + 'px;' +
|
690
|
+
'height: ' + SQUARE_SIZE + 'px;';
|
691
|
+
if (hidden === true) {
|
692
|
+
html += 'display:none;';
|
693
|
+
}
|
694
|
+
html += '" />';
|
695
|
+
|
696
|
+
return html;
|
697
|
+
}
|
698
|
+
|
699
|
+
function buildSparePieces(color) {
|
700
|
+
var pieces = ['wK', 'wQ', 'wR', 'wB', 'wN', 'wP'];
|
701
|
+
if (color === 'black') {
|
702
|
+
pieces = ['bK', 'bQ', 'bR', 'bB', 'bN', 'bP'];
|
703
|
+
}
|
704
|
+
|
705
|
+
var html = '';
|
706
|
+
for (var i = 0; i < pieces.length; i++) {
|
707
|
+
html += buildPiece(pieces[i], false, SPARE_PIECE_ELS_IDS[pieces[i]]);
|
708
|
+
}
|
709
|
+
|
710
|
+
return html;
|
711
|
+
}
|
712
|
+
|
713
|
+
//------------------------------------------------------------------------------
|
714
|
+
// Animations
|
715
|
+
//------------------------------------------------------------------------------
|
716
|
+
|
717
|
+
function animateSquareToSquare(src, dest, piece, completeFn) {
|
718
|
+
// get information about the source and destination squares
|
719
|
+
var srcSquareEl = $('#' + SQUARE_ELS_IDS[src]);
|
720
|
+
var srcSquarePosition = srcSquareEl.offset();
|
721
|
+
var destSquareEl = $('#' + SQUARE_ELS_IDS[dest]);
|
722
|
+
var destSquarePosition = destSquareEl.offset();
|
723
|
+
|
724
|
+
// create the animated piece and absolutely position it
|
725
|
+
// over the source square
|
726
|
+
var animatedPieceId = createId();
|
727
|
+
$('body').append(buildPiece(piece, true, animatedPieceId));
|
728
|
+
var animatedPieceEl = $('#' + animatedPieceId);
|
729
|
+
animatedPieceEl.css({
|
730
|
+
display: '',
|
731
|
+
position: 'absolute',
|
732
|
+
top: srcSquarePosition.top,
|
733
|
+
left: srcSquarePosition.left
|
734
|
+
});
|
735
|
+
|
736
|
+
// remove original piece from source square
|
737
|
+
srcSquareEl.find('.' + CSS.piece).remove();
|
738
|
+
|
739
|
+
// on complete
|
740
|
+
var complete = function() {
|
741
|
+
// add the "real" piece to the destination square
|
742
|
+
destSquareEl.append(buildPiece(piece));
|
743
|
+
|
744
|
+
// remove the animated piece
|
745
|
+
animatedPieceEl.remove();
|
746
|
+
|
747
|
+
// run complete function
|
748
|
+
if (typeof completeFn === 'function') {
|
749
|
+
completeFn();
|
750
|
+
}
|
751
|
+
};
|
752
|
+
|
753
|
+
// animate the piece to the destination square
|
754
|
+
var opts = {
|
755
|
+
duration: cfg.moveSpeed,
|
756
|
+
complete: complete
|
757
|
+
};
|
758
|
+
animatedPieceEl.animate(destSquarePosition, opts);
|
759
|
+
}
|
760
|
+
|
761
|
+
function animateSparePieceToSquare(piece, dest, completeFn) {
|
762
|
+
var srcOffset = $('#' + SPARE_PIECE_ELS_IDS[piece]).offset();
|
763
|
+
var destSquareEl = $('#' + SQUARE_ELS_IDS[dest]);
|
764
|
+
var destOffset = destSquareEl.offset();
|
765
|
+
|
766
|
+
// create the animate piece
|
767
|
+
var pieceId = createId();
|
768
|
+
$('body').append(buildPiece(piece, true, pieceId));
|
769
|
+
var animatedPieceEl = $('#' + pieceId);
|
770
|
+
animatedPieceEl.css({
|
771
|
+
display: '',
|
772
|
+
position: 'absolute',
|
773
|
+
left: srcOffset.left,
|
774
|
+
top: srcOffset.top
|
775
|
+
});
|
776
|
+
|
777
|
+
// on complete
|
778
|
+
var complete = function() {
|
779
|
+
// add the "real" piece to the destination square
|
780
|
+
destSquareEl.find('.' + CSS.piece).remove();
|
781
|
+
destSquareEl.append(buildPiece(piece));
|
782
|
+
|
783
|
+
// remove the animated piece
|
784
|
+
animatedPieceEl.remove();
|
785
|
+
|
786
|
+
// run complete function
|
787
|
+
if (typeof completeFn === 'function') {
|
788
|
+
completeFn();
|
789
|
+
}
|
790
|
+
};
|
791
|
+
|
792
|
+
// animate the piece to the destination square
|
793
|
+
var opts = {
|
794
|
+
duration: cfg.moveSpeed,
|
795
|
+
complete: complete
|
796
|
+
};
|
797
|
+
animatedPieceEl.animate(destOffset, opts);
|
798
|
+
}
|
799
|
+
|
800
|
+
// execute an array of animations
|
801
|
+
function doAnimations(a, oldPos, newPos) {
|
802
|
+
ANIMATION_HAPPENING = true;
|
803
|
+
|
804
|
+
var numFinished = 0;
|
805
|
+
function onFinish() {
|
806
|
+
numFinished++;
|
807
|
+
|
808
|
+
// exit if all the animations aren't finished
|
809
|
+
if (numFinished !== a.length) return;
|
810
|
+
|
811
|
+
drawPositionInstant();
|
812
|
+
ANIMATION_HAPPENING = false;
|
813
|
+
|
814
|
+
// run their onMoveEnd function
|
815
|
+
if (cfg.hasOwnProperty('onMoveEnd') === true &&
|
816
|
+
typeof cfg.onMoveEnd === 'function') {
|
817
|
+
cfg.onMoveEnd(deepCopy(oldPos), deepCopy(newPos));
|
818
|
+
}
|
819
|
+
}
|
820
|
+
|
821
|
+
for (var i = 0; i < a.length; i++) {
|
822
|
+
// clear a piece
|
823
|
+
if (a[i].type === 'clear') {
|
824
|
+
$('#' + SQUARE_ELS_IDS[a[i].square] + ' .' + CSS.piece)
|
825
|
+
.fadeOut(cfg.trashSpeed, onFinish);
|
826
|
+
}
|
827
|
+
|
828
|
+
// add a piece (no spare pieces)
|
829
|
+
if (a[i].type === 'add' && cfg.sparePieces !== true) {
|
830
|
+
$('#' + SQUARE_ELS_IDS[a[i].square])
|
831
|
+
.append(buildPiece(a[i].piece, true))
|
832
|
+
.find('.' + CSS.piece)
|
833
|
+
.fadeIn(cfg.appearSpeed, onFinish);
|
834
|
+
}
|
835
|
+
|
836
|
+
// add a piece from a spare piece
|
837
|
+
if (a[i].type === 'add' && cfg.sparePieces === true) {
|
838
|
+
animateSparePieceToSquare(a[i].piece, a[i].square, onFinish);
|
839
|
+
}
|
840
|
+
|
841
|
+
// move a piece
|
842
|
+
if (a[i].type === 'move') {
|
843
|
+
animateSquareToSquare(a[i].source, a[i].destination, a[i].piece,
|
844
|
+
onFinish);
|
845
|
+
}
|
846
|
+
}
|
847
|
+
}
|
848
|
+
|
849
|
+
// returns the distance between two squares
|
850
|
+
function squareDistance(s1, s2) {
|
851
|
+
s1 = s1.split('');
|
852
|
+
var s1x = COLUMNS.indexOf(s1[0]) + 1;
|
853
|
+
var s1y = parseInt(s1[1], 10);
|
854
|
+
|
855
|
+
s2 = s2.split('');
|
856
|
+
var s2x = COLUMNS.indexOf(s2[0]) + 1;
|
857
|
+
var s2y = parseInt(s2[1], 10);
|
858
|
+
|
859
|
+
var xDelta = Math.abs(s1x - s2x);
|
860
|
+
var yDelta = Math.abs(s1y - s2y);
|
861
|
+
|
862
|
+
if (xDelta >= yDelta) return xDelta;
|
863
|
+
return yDelta;
|
864
|
+
}
|
865
|
+
|
866
|
+
// returns an array of closest squares from square
|
867
|
+
function createRadius(square) {
|
868
|
+
var squares = [];
|
869
|
+
|
870
|
+
// calculate distance of all squares
|
871
|
+
for (var i = 0; i < 8; i++) {
|
872
|
+
for (var j = 0; j < 8; j++) {
|
873
|
+
var s = COLUMNS[i] + (j + 1);
|
874
|
+
|
875
|
+
// skip the square we're starting from
|
876
|
+
if (square === s) continue;
|
877
|
+
|
878
|
+
squares.push({
|
879
|
+
square: s,
|
880
|
+
distance: squareDistance(square, s)
|
881
|
+
});
|
882
|
+
}
|
883
|
+
}
|
884
|
+
|
885
|
+
// sort by distance
|
886
|
+
squares.sort(function(a, b) {
|
887
|
+
return a.distance - b.distance;
|
888
|
+
});
|
889
|
+
|
890
|
+
// just return the square code
|
891
|
+
var squares2 = [];
|
892
|
+
for (var i = 0; i < squares.length; i++) {
|
893
|
+
squares2.push(squares[i].square);
|
894
|
+
}
|
895
|
+
|
896
|
+
return squares2;
|
897
|
+
}
|
898
|
+
|
899
|
+
// returns the square of the closest instance of piece
|
900
|
+
// returns false if no instance of piece is found in position
|
901
|
+
function findClosestPiece(position, piece, square) {
|
902
|
+
// create array of closest squares from square
|
903
|
+
var closestSquares = createRadius(square);
|
904
|
+
|
905
|
+
// search through the position in order of distance for the piece
|
906
|
+
for (var i = 0; i < closestSquares.length; i++) {
|
907
|
+
var s = closestSquares[i];
|
908
|
+
|
909
|
+
if (position.hasOwnProperty(s) === true && position[s] === piece) {
|
910
|
+
return s;
|
911
|
+
}
|
912
|
+
}
|
913
|
+
|
914
|
+
return false;
|
915
|
+
}
|
916
|
+
|
917
|
+
// calculate an array of animations that need to happen in order to get
|
918
|
+
// from pos1 to pos2
|
919
|
+
function calculateAnimations(pos1, pos2) {
|
920
|
+
// make copies of both
|
921
|
+
pos1 = deepCopy(pos1);
|
922
|
+
pos2 = deepCopy(pos2);
|
923
|
+
|
924
|
+
var animations = [];
|
925
|
+
var squaresMovedTo = {};
|
926
|
+
|
927
|
+
// remove pieces that are the same in both positions
|
928
|
+
for (var i in pos2) {
|
929
|
+
if (pos2.hasOwnProperty(i) !== true) continue;
|
930
|
+
|
931
|
+
if (pos1.hasOwnProperty(i) === true && pos1[i] === pos2[i]) {
|
932
|
+
delete pos1[i];
|
933
|
+
delete pos2[i];
|
934
|
+
}
|
935
|
+
}
|
936
|
+
|
937
|
+
// find all the "move" animations
|
938
|
+
for (var i in pos2) {
|
939
|
+
if (pos2.hasOwnProperty(i) !== true) continue;
|
940
|
+
|
941
|
+
var closestPiece = findClosestPiece(pos1, pos2[i], i);
|
942
|
+
if (closestPiece !== false) {
|
943
|
+
animations.push({
|
944
|
+
type: 'move',
|
945
|
+
source: closestPiece,
|
946
|
+
destination: i,
|
947
|
+
piece: pos2[i]
|
948
|
+
});
|
949
|
+
|
950
|
+
delete pos1[closestPiece];
|
951
|
+
delete pos2[i];
|
952
|
+
squaresMovedTo[i] = true;
|
953
|
+
}
|
954
|
+
}
|
955
|
+
|
956
|
+
// add pieces to pos2
|
957
|
+
for (var i in pos2) {
|
958
|
+
if (pos2.hasOwnProperty(i) !== true) continue;
|
959
|
+
|
960
|
+
animations.push({
|
961
|
+
type: 'add',
|
962
|
+
square: i,
|
963
|
+
piece: pos2[i]
|
964
|
+
})
|
965
|
+
|
966
|
+
delete pos2[i];
|
967
|
+
}
|
968
|
+
|
969
|
+
// clear pieces from pos1
|
970
|
+
for (var i in pos1) {
|
971
|
+
if (pos1.hasOwnProperty(i) !== true) continue;
|
972
|
+
|
973
|
+
// do not clear a piece if it is on a square that is the result
|
974
|
+
// of a "move", ie: a piece capture
|
975
|
+
if (squaresMovedTo.hasOwnProperty(i) === true) continue;
|
976
|
+
|
977
|
+
animations.push({
|
978
|
+
type: 'clear',
|
979
|
+
square: i,
|
980
|
+
piece: pos1[i]
|
981
|
+
});
|
982
|
+
|
983
|
+
delete pos1[i];
|
984
|
+
}
|
985
|
+
|
986
|
+
return animations;
|
987
|
+
}
|
988
|
+
|
989
|
+
//------------------------------------------------------------------------------
|
990
|
+
// Control Flow
|
991
|
+
//------------------------------------------------------------------------------
|
992
|
+
|
993
|
+
function drawPositionInstant() {
|
994
|
+
// clear the board
|
995
|
+
boardEl.find('.' + CSS.piece).remove();
|
996
|
+
|
997
|
+
// add the pieces
|
998
|
+
for (var i in CURRENT_POSITION) {
|
999
|
+
if (CURRENT_POSITION.hasOwnProperty(i) !== true) continue;
|
1000
|
+
if (DRAGGING_A_PIECE && DRAGGED_PIECE_SOURCE == i) continue;
|
1001
|
+
$('#' + SQUARE_ELS_IDS[i]).append(buildPiece(CURRENT_POSITION[i]));
|
1002
|
+
}
|
1003
|
+
}
|
1004
|
+
|
1005
|
+
function drawBoard() {
|
1006
|
+
boardEl.html(buildBoard(CURRENT_ORIENTATION));
|
1007
|
+
drawPositionInstant();
|
1008
|
+
|
1009
|
+
if (cfg.sparePieces === true) {
|
1010
|
+
if (CURRENT_ORIENTATION === 'white') {
|
1011
|
+
sparePiecesTopEl.html(buildSparePieces('black'));
|
1012
|
+
sparePiecesBottomEl.html(buildSparePieces('white'));
|
1013
|
+
}
|
1014
|
+
else {
|
1015
|
+
sparePiecesTopEl.html(buildSparePieces('white'));
|
1016
|
+
sparePiecesBottomEl.html(buildSparePieces('black'));
|
1017
|
+
}
|
1018
|
+
}
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
// given a position and a set of moves, return a new position
|
1022
|
+
// with the moves executed
|
1023
|
+
function calculatePositionFromMoves(position, moves) {
|
1024
|
+
position = deepCopy(position);
|
1025
|
+
|
1026
|
+
for (var i in moves) {
|
1027
|
+
if (moves.hasOwnProperty(i) !== true) continue;
|
1028
|
+
|
1029
|
+
// skip the move if the position doesn't have a piece on the source square
|
1030
|
+
if (position.hasOwnProperty(i) !== true) continue;
|
1031
|
+
|
1032
|
+
var piece = position[i];
|
1033
|
+
delete position[i];
|
1034
|
+
position[moves[i]] = piece;
|
1035
|
+
}
|
1036
|
+
|
1037
|
+
return position;
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
function setCurrentPosition(position) {
|
1041
|
+
var oldPos = deepCopy(CURRENT_POSITION);
|
1042
|
+
var newPos = deepCopy(position);
|
1043
|
+
var oldFen = objToFen(oldPos);
|
1044
|
+
var newFen = objToFen(newPos);
|
1045
|
+
|
1046
|
+
// do nothing if no change in position
|
1047
|
+
if (oldFen === newFen) return;
|
1048
|
+
|
1049
|
+
// run their onChange function
|
1050
|
+
if (cfg.hasOwnProperty('onChange') === true &&
|
1051
|
+
typeof cfg.onChange === 'function') {
|
1052
|
+
cfg.onChange(oldPos, newPos);
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
// update state
|
1056
|
+
CURRENT_POSITION = position;
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
function isXYOnSquare(x, y) {
|
1060
|
+
for (var i in SQUARE_ELS_OFFSETS) {
|
1061
|
+
if (SQUARE_ELS_OFFSETS.hasOwnProperty(i) !== true) continue;
|
1062
|
+
|
1063
|
+
var s = SQUARE_ELS_OFFSETS[i];
|
1064
|
+
if (x >= s.left && x < s.left + SQUARE_SIZE &&
|
1065
|
+
y >= s.top && y < s.top + SQUARE_SIZE) {
|
1066
|
+
return i;
|
1067
|
+
}
|
1068
|
+
}
|
1069
|
+
|
1070
|
+
return 'offboard';
|
1071
|
+
}
|
1072
|
+
|
1073
|
+
// records the XY coords of every square into memory
|
1074
|
+
function captureSquareOffsets() {
|
1075
|
+
SQUARE_ELS_OFFSETS = {};
|
1076
|
+
|
1077
|
+
for (var i in SQUARE_ELS_IDS) {
|
1078
|
+
if (SQUARE_ELS_IDS.hasOwnProperty(i) !== true) continue;
|
1079
|
+
|
1080
|
+
SQUARE_ELS_OFFSETS[i] = $('#' + SQUARE_ELS_IDS[i]).offset();
|
1081
|
+
}
|
1082
|
+
}
|
1083
|
+
|
1084
|
+
function removeSquareHighlights() {
|
1085
|
+
boardEl.find('.' + CSS.square)
|
1086
|
+
.removeClass(CSS.highlight1 + ' ' + CSS.highlight2);
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
function snapbackDraggedPiece() {
|
1090
|
+
// there is no "snapback" for spare pieces
|
1091
|
+
if (DRAGGED_PIECE_SOURCE === 'spare') {
|
1092
|
+
trashDraggedPiece();
|
1093
|
+
return;
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
removeSquareHighlights();
|
1097
|
+
|
1098
|
+
// animation complete
|
1099
|
+
function complete() {
|
1100
|
+
drawPositionInstant();
|
1101
|
+
draggedPieceEl.css('display', 'none');
|
1102
|
+
|
1103
|
+
// run their onSnapbackEnd function
|
1104
|
+
if (cfg.hasOwnProperty('onSnapbackEnd') === true &&
|
1105
|
+
typeof cfg.onSnapbackEnd === 'function') {
|
1106
|
+
cfg.onSnapbackEnd(DRAGGED_PIECE, DRAGGED_PIECE_SOURCE,
|
1107
|
+
deepCopy(CURRENT_POSITION), CURRENT_ORIENTATION);
|
1108
|
+
}
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
// get source square position
|
1112
|
+
var sourceSquarePosition =
|
1113
|
+
$('#' + SQUARE_ELS_IDS[DRAGGED_PIECE_SOURCE]).offset();
|
1114
|
+
|
1115
|
+
// animate the piece to the target square
|
1116
|
+
var opts = {
|
1117
|
+
duration: cfg.snapbackSpeed,
|
1118
|
+
complete: complete
|
1119
|
+
};
|
1120
|
+
draggedPieceEl.animate(sourceSquarePosition, opts);
|
1121
|
+
|
1122
|
+
// set state
|
1123
|
+
DRAGGING_A_PIECE = false;
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
function trashDraggedPiece() {
|
1127
|
+
removeSquareHighlights();
|
1128
|
+
|
1129
|
+
// remove the source piece
|
1130
|
+
var newPosition = deepCopy(CURRENT_POSITION);
|
1131
|
+
delete newPosition[DRAGGED_PIECE_SOURCE];
|
1132
|
+
setCurrentPosition(newPosition);
|
1133
|
+
|
1134
|
+
// redraw the position
|
1135
|
+
drawPositionInstant();
|
1136
|
+
|
1137
|
+
// hide the dragged piece
|
1138
|
+
draggedPieceEl.fadeOut(cfg.trashSpeed);
|
1139
|
+
|
1140
|
+
// set state
|
1141
|
+
DRAGGING_A_PIECE = false;
|
1142
|
+
}
|
1143
|
+
|
1144
|
+
function dropDraggedPieceOnSquare(square) {
|
1145
|
+
removeSquareHighlights();
|
1146
|
+
|
1147
|
+
// update position
|
1148
|
+
var newPosition = deepCopy(CURRENT_POSITION);
|
1149
|
+
delete newPosition[DRAGGED_PIECE_SOURCE];
|
1150
|
+
newPosition[square] = DRAGGED_PIECE;
|
1151
|
+
setCurrentPosition(newPosition);
|
1152
|
+
|
1153
|
+
// get target square information
|
1154
|
+
var targetSquarePosition = $('#' + SQUARE_ELS_IDS[square]).offset();
|
1155
|
+
|
1156
|
+
// animation complete
|
1157
|
+
var complete = function() {
|
1158
|
+
drawPositionInstant();
|
1159
|
+
draggedPieceEl.css('display', 'none');
|
1160
|
+
|
1161
|
+
// execute their onSnapEnd function
|
1162
|
+
if (cfg.hasOwnProperty('onSnapEnd') === true &&
|
1163
|
+
typeof cfg.onSnapEnd === 'function') {
|
1164
|
+
cfg.onSnapEnd(DRAGGED_PIECE_SOURCE, square, DRAGGED_PIECE);
|
1165
|
+
}
|
1166
|
+
};
|
1167
|
+
|
1168
|
+
// snap the piece to the target square
|
1169
|
+
var opts = {
|
1170
|
+
duration: cfg.snapSpeed,
|
1171
|
+
complete: complete
|
1172
|
+
};
|
1173
|
+
draggedPieceEl.animate(targetSquarePosition, opts);
|
1174
|
+
|
1175
|
+
// set state
|
1176
|
+
DRAGGING_A_PIECE = false;
|
1177
|
+
}
|
1178
|
+
|
1179
|
+
function beginDraggingPiece(source, piece, x, y) {
|
1180
|
+
// run their custom onDragStart function
|
1181
|
+
// their custom onDragStart function can cancel drag start
|
1182
|
+
if (typeof cfg.onDragStart === 'function' &&
|
1183
|
+
cfg.onDragStart(source, piece,
|
1184
|
+
deepCopy(CURRENT_POSITION), CURRENT_ORIENTATION) === false) {
|
1185
|
+
return;
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
// set state
|
1189
|
+
DRAGGING_A_PIECE = true;
|
1190
|
+
DRAGGED_PIECE = piece;
|
1191
|
+
DRAGGED_PIECE_SOURCE = source;
|
1192
|
+
|
1193
|
+
// if the piece came from spare pieces, location is offboard
|
1194
|
+
if (source === 'spare') {
|
1195
|
+
DRAGGED_PIECE_LOCATION = 'offboard';
|
1196
|
+
}
|
1197
|
+
else {
|
1198
|
+
DRAGGED_PIECE_LOCATION = source;
|
1199
|
+
}
|
1200
|
+
|
1201
|
+
// capture the x, y coords of all squares in memory
|
1202
|
+
captureSquareOffsets();
|
1203
|
+
|
1204
|
+
// create the dragged piece
|
1205
|
+
draggedPieceEl.attr('src', buildPieceImgSrc(piece))
|
1206
|
+
.css({
|
1207
|
+
display: '',
|
1208
|
+
position: 'absolute',
|
1209
|
+
left: x - (SQUARE_SIZE / 2),
|
1210
|
+
top: y - (SQUARE_SIZE / 2)
|
1211
|
+
});
|
1212
|
+
|
1213
|
+
if (source !== 'spare') {
|
1214
|
+
// highlight the source square and hide the piece
|
1215
|
+
$('#' + SQUARE_ELS_IDS[source]).addClass(CSS.highlight1)
|
1216
|
+
.find('.' + CSS.piece).css('display', 'none');
|
1217
|
+
}
|
1218
|
+
}
|
1219
|
+
|
1220
|
+
function updateDraggedPiece(x, y) {
|
1221
|
+
// put the dragged piece over the mouse cursor
|
1222
|
+
draggedPieceEl.css({
|
1223
|
+
left: x - (SQUARE_SIZE / 2),
|
1224
|
+
top: y - (SQUARE_SIZE / 2)
|
1225
|
+
});
|
1226
|
+
|
1227
|
+
// get location
|
1228
|
+
var location = isXYOnSquare(x, y);
|
1229
|
+
|
1230
|
+
// do nothing if the location has not changed
|
1231
|
+
if (location === DRAGGED_PIECE_LOCATION) return;
|
1232
|
+
|
1233
|
+
// remove highlight from previous square
|
1234
|
+
if (validSquare(DRAGGED_PIECE_LOCATION) === true) {
|
1235
|
+
$('#' + SQUARE_ELS_IDS[DRAGGED_PIECE_LOCATION])
|
1236
|
+
.removeClass(CSS.highlight2);
|
1237
|
+
}
|
1238
|
+
|
1239
|
+
// add highlight to new square
|
1240
|
+
if (validSquare(location) === true) {
|
1241
|
+
$('#' + SQUARE_ELS_IDS[location]).addClass(CSS.highlight2);
|
1242
|
+
}
|
1243
|
+
|
1244
|
+
// run onDragMove
|
1245
|
+
if (typeof cfg.onDragMove === 'function') {
|
1246
|
+
cfg.onDragMove(location, DRAGGED_PIECE_LOCATION,
|
1247
|
+
DRAGGED_PIECE_SOURCE, DRAGGED_PIECE,
|
1248
|
+
deepCopy(CURRENT_POSITION), CURRENT_ORIENTATION);
|
1249
|
+
}
|
1250
|
+
|
1251
|
+
// update state
|
1252
|
+
DRAGGED_PIECE_LOCATION = location;
|
1253
|
+
}
|
1254
|
+
|
1255
|
+
function stopDraggedPiece(location) {
|
1256
|
+
// determine what the action should be
|
1257
|
+
var action = 'drop';
|
1258
|
+
if (location === 'offboard' && cfg.dropOffBoard === 'snapback') {
|
1259
|
+
action = 'snapback';
|
1260
|
+
}
|
1261
|
+
if (location === 'offboard' && cfg.dropOffBoard === 'trash') {
|
1262
|
+
action = 'trash';
|
1263
|
+
}
|
1264
|
+
|
1265
|
+
// run their onDrop function, which can potentially change the drop action
|
1266
|
+
if (cfg.hasOwnProperty('onDrop') === true &&
|
1267
|
+
typeof cfg.onDrop === 'function') {
|
1268
|
+
var newPosition = deepCopy(CURRENT_POSITION);
|
1269
|
+
|
1270
|
+
// source piece is a spare piece and position is off the board
|
1271
|
+
//if (DRAGGED_PIECE_SOURCE === 'spare' && location === 'offboard') {...}
|
1272
|
+
// position has not changed; do nothing
|
1273
|
+
|
1274
|
+
// source piece is a spare piece and position is on the board
|
1275
|
+
if (DRAGGED_PIECE_SOURCE === 'spare' && validSquare(location) === true) {
|
1276
|
+
// add the piece to the board
|
1277
|
+
newPosition[location] = DRAGGED_PIECE;
|
1278
|
+
}
|
1279
|
+
|
1280
|
+
// source piece was on the board and position is off the board
|
1281
|
+
if (validSquare(DRAGGED_PIECE_SOURCE) === true && location === 'offboard') {
|
1282
|
+
// remove the piece from the board
|
1283
|
+
delete newPosition[DRAGGED_PIECE_SOURCE];
|
1284
|
+
}
|
1285
|
+
|
1286
|
+
// source piece was on the board and position is on the board
|
1287
|
+
if (validSquare(DRAGGED_PIECE_SOURCE) === true &&
|
1288
|
+
validSquare(location) === true) {
|
1289
|
+
// move the piece
|
1290
|
+
delete newPosition[DRAGGED_PIECE_SOURCE];
|
1291
|
+
newPosition[location] = DRAGGED_PIECE;
|
1292
|
+
}
|
1293
|
+
|
1294
|
+
var oldPosition = deepCopy(CURRENT_POSITION);
|
1295
|
+
|
1296
|
+
var result = cfg.onDrop(DRAGGED_PIECE_SOURCE, location, DRAGGED_PIECE,
|
1297
|
+
newPosition, oldPosition, CURRENT_ORIENTATION);
|
1298
|
+
if (result === 'snapback' || result === 'trash') {
|
1299
|
+
action = result;
|
1300
|
+
}
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
// do it!
|
1304
|
+
if (action === 'snapback') {
|
1305
|
+
snapbackDraggedPiece();
|
1306
|
+
}
|
1307
|
+
else if (action === 'trash') {
|
1308
|
+
trashDraggedPiece();
|
1309
|
+
}
|
1310
|
+
else if (action === 'drop') {
|
1311
|
+
dropDraggedPieceOnSquare(location);
|
1312
|
+
}
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
//------------------------------------------------------------------------------
|
1316
|
+
// Public Methods
|
1317
|
+
//------------------------------------------------------------------------------
|
1318
|
+
|
1319
|
+
// clear the board
|
1320
|
+
widget.clear = function(useAnimation) {
|
1321
|
+
widget.position({}, useAnimation);
|
1322
|
+
};
|
1323
|
+
|
1324
|
+
/*
|
1325
|
+
// get or set config properties
|
1326
|
+
// TODO: write this, GitHub Issue #1
|
1327
|
+
widget.config = function(arg1, arg2) {
|
1328
|
+
// get the current config
|
1329
|
+
if (arguments.length === 0) {
|
1330
|
+
return deepCopy(cfg);
|
1331
|
+
}
|
1332
|
+
};
|
1333
|
+
*/
|
1334
|
+
|
1335
|
+
// remove the widget from the page
|
1336
|
+
widget.destroy = function() {
|
1337
|
+
// remove markup
|
1338
|
+
containerEl.html('');
|
1339
|
+
draggedPieceEl.remove();
|
1340
|
+
|
1341
|
+
// remove event handlers
|
1342
|
+
containerEl.unbind();
|
1343
|
+
};
|
1344
|
+
|
1345
|
+
// shorthand method to get the current FEN
|
1346
|
+
widget.fen = function() {
|
1347
|
+
return widget.position('fen');
|
1348
|
+
};
|
1349
|
+
|
1350
|
+
// flip orientation
|
1351
|
+
widget.flip = function() {
|
1352
|
+
widget.orientation('flip');
|
1353
|
+
};
|
1354
|
+
|
1355
|
+
/*
|
1356
|
+
// TODO: write this, GitHub Issue #5
|
1357
|
+
widget.highlight = function() {
|
1358
|
+
|
1359
|
+
};
|
1360
|
+
*/
|
1361
|
+
widget.cache = cacheImages
|
1362
|
+
// move pieces
|
1363
|
+
widget.move = function() {
|
1364
|
+
// no need to throw an error here; just do nothing
|
1365
|
+
if (arguments.length === 0) return;
|
1366
|
+
|
1367
|
+
var useAnimation = true;
|
1368
|
+
|
1369
|
+
// collect the moves into an object
|
1370
|
+
var moves = {};
|
1371
|
+
for (var i = 0; i < arguments.length; i++) {
|
1372
|
+
// any "false" to this function means no animations
|
1373
|
+
if (arguments[i] === false) {
|
1374
|
+
useAnimation = false;
|
1375
|
+
continue;
|
1376
|
+
}
|
1377
|
+
|
1378
|
+
// skip invalid arguments
|
1379
|
+
if (validMove(arguments[i]) !== true) {
|
1380
|
+
error(2826, 'Invalid move passed to the move method.', arguments[i]);
|
1381
|
+
continue;
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
var tmp = arguments[i].split('-');
|
1385
|
+
moves[tmp[0]] = tmp[1];
|
1386
|
+
}
|
1387
|
+
|
1388
|
+
// calculate position from moves
|
1389
|
+
var newPos = calculatePositionFromMoves(CURRENT_POSITION, moves);
|
1390
|
+
|
1391
|
+
// update the board
|
1392
|
+
widget.position(newPos, useAnimation);
|
1393
|
+
|
1394
|
+
// return the new position object
|
1395
|
+
return newPos;
|
1396
|
+
};
|
1397
|
+
|
1398
|
+
widget.orientation = function(arg) {
|
1399
|
+
// no arguments, return the current orientation
|
1400
|
+
if (arguments.length === 0) {
|
1401
|
+
return CURRENT_ORIENTATION;
|
1402
|
+
}
|
1403
|
+
|
1404
|
+
// set to white or black
|
1405
|
+
if (arg === 'white' || arg === 'black') {
|
1406
|
+
CURRENT_ORIENTATION = arg;
|
1407
|
+
drawBoard();
|
1408
|
+
return;
|
1409
|
+
}
|
1410
|
+
|
1411
|
+
// flip orientation
|
1412
|
+
if (arg === 'flip') {
|
1413
|
+
CURRENT_ORIENTATION = (CURRENT_ORIENTATION === 'white') ? 'black' : 'white';
|
1414
|
+
drawBoard();
|
1415
|
+
return;
|
1416
|
+
}
|
1417
|
+
|
1418
|
+
error(5482, 'Invalid value passed to the orientation method.', arg);
|
1419
|
+
};
|
1420
|
+
|
1421
|
+
widget.position = function(position, useAnimation) {
|
1422
|
+
// no arguments, return the current position
|
1423
|
+
if (arguments.length === 0) {
|
1424
|
+
return deepCopy(CURRENT_POSITION);
|
1425
|
+
}
|
1426
|
+
|
1427
|
+
// get position as FEN
|
1428
|
+
if (typeof position === 'string' && position.toLowerCase() === 'fen') {
|
1429
|
+
return objToFen(CURRENT_POSITION);
|
1430
|
+
}
|
1431
|
+
|
1432
|
+
// default for useAnimations is true
|
1433
|
+
if (useAnimation !== false) {
|
1434
|
+
useAnimation = true;
|
1435
|
+
}
|
1436
|
+
|
1437
|
+
// start position
|
1438
|
+
if (typeof position === 'string' && position.toLowerCase() === 'start') {
|
1439
|
+
position = deepCopy(START_POSITION);
|
1440
|
+
}
|
1441
|
+
|
1442
|
+
// convert FEN to position object
|
1443
|
+
if (validFen(position) === true) {
|
1444
|
+
position = fenToObj(position);
|
1445
|
+
}
|
1446
|
+
|
1447
|
+
// validate position object
|
1448
|
+
if (validPositionObject(position) !== true) {
|
1449
|
+
error(6482, 'Invalid value passed to the position method.', position);
|
1450
|
+
return;
|
1451
|
+
}
|
1452
|
+
|
1453
|
+
if (useAnimation === true) {
|
1454
|
+
// start the animations
|
1455
|
+
doAnimations(calculateAnimations(CURRENT_POSITION, position),
|
1456
|
+
CURRENT_POSITION, position);
|
1457
|
+
|
1458
|
+
// set the new position
|
1459
|
+
setCurrentPosition(position);
|
1460
|
+
}
|
1461
|
+
// instant update
|
1462
|
+
else {
|
1463
|
+
setCurrentPosition(position);
|
1464
|
+
drawPositionInstant();
|
1465
|
+
}
|
1466
|
+
};
|
1467
|
+
|
1468
|
+
widget.resize = function() {
|
1469
|
+
// calulate the new square size
|
1470
|
+
SQUARE_SIZE = calculateSquareSize();
|
1471
|
+
|
1472
|
+
// set board width
|
1473
|
+
boardEl.css('width', (SQUARE_SIZE * 8) + 'px');
|
1474
|
+
|
1475
|
+
// set drag piece size
|
1476
|
+
draggedPieceEl.css({
|
1477
|
+
height: SQUARE_SIZE,
|
1478
|
+
width: SQUARE_SIZE
|
1479
|
+
});
|
1480
|
+
|
1481
|
+
// spare pieces
|
1482
|
+
if (cfg.sparePieces === true) {
|
1483
|
+
containerEl.find('.' + CSS.sparePieces)
|
1484
|
+
.css('paddingLeft', (SQUARE_SIZE + BOARD_BORDER_SIZE) + 'px');
|
1485
|
+
}
|
1486
|
+
|
1487
|
+
// redraw the board
|
1488
|
+
drawBoard();
|
1489
|
+
};
|
1490
|
+
|
1491
|
+
// set the starting position
|
1492
|
+
widget.start = function(useAnimation) {
|
1493
|
+
widget.position('start', useAnimation);
|
1494
|
+
};
|
1495
|
+
|
1496
|
+
//------------------------------------------------------------------------------
|
1497
|
+
// Browser Events
|
1498
|
+
//------------------------------------------------------------------------------
|
1499
|
+
|
1500
|
+
function isTouchDevice() {
|
1501
|
+
return ('ontouchstart' in document.documentElement);
|
1502
|
+
}
|
1503
|
+
|
1504
|
+
// reference: http://www.quirksmode.org/js/detect.html
|
1505
|
+
function isMSIE() {
|
1506
|
+
return (navigator && navigator.userAgent &&
|
1507
|
+
navigator.userAgent.search(/MSIE/) !== -1);
|
1508
|
+
}
|
1509
|
+
|
1510
|
+
function stopDefault(e) {
|
1511
|
+
e.preventDefault();
|
1512
|
+
}
|
1513
|
+
|
1514
|
+
function mousedownSquare(e) {
|
1515
|
+
// do nothing if we're not draggable
|
1516
|
+
if (cfg.draggable !== true) return;
|
1517
|
+
|
1518
|
+
var square = $(this).attr('data-square');
|
1519
|
+
|
1520
|
+
// no piece on this square
|
1521
|
+
if (validSquare(square) !== true ||
|
1522
|
+
CURRENT_POSITION.hasOwnProperty(square) !== true) {
|
1523
|
+
return;
|
1524
|
+
}
|
1525
|
+
|
1526
|
+
beginDraggingPiece(square, CURRENT_POSITION[square], e.pageX, e.pageY);
|
1527
|
+
}
|
1528
|
+
|
1529
|
+
function touchstartSquare(e) {
|
1530
|
+
// do nothing if we're not draggable
|
1531
|
+
if (cfg.draggable !== true) return;
|
1532
|
+
|
1533
|
+
var square = $(this).attr('data-square');
|
1534
|
+
|
1535
|
+
// no piece on this square
|
1536
|
+
if (validSquare(square) !== true ||
|
1537
|
+
CURRENT_POSITION.hasOwnProperty(square) !== true) {
|
1538
|
+
return;
|
1539
|
+
}
|
1540
|
+
|
1541
|
+
e = e.originalEvent;
|
1542
|
+
beginDraggingPiece(square, CURRENT_POSITION[square],
|
1543
|
+
e.changedTouches[0].pageX, e.changedTouches[0].pageY);
|
1544
|
+
}
|
1545
|
+
|
1546
|
+
function mousedownSparePiece(e) {
|
1547
|
+
// do nothing if sparePieces is not enabled
|
1548
|
+
if (cfg.sparePieces !== true) return;
|
1549
|
+
|
1550
|
+
var piece = $(this).attr('data-piece');
|
1551
|
+
|
1552
|
+
beginDraggingPiece('spare', piece, e.pageX, e.pageY);
|
1553
|
+
}
|
1554
|
+
|
1555
|
+
function touchstartSparePiece(e) {
|
1556
|
+
// do nothing if sparePieces is not enabled
|
1557
|
+
if (cfg.sparePieces !== true) return;
|
1558
|
+
|
1559
|
+
var piece = $(this).attr('data-piece');
|
1560
|
+
|
1561
|
+
e = e.originalEvent;
|
1562
|
+
beginDraggingPiece('spare', piece,
|
1563
|
+
e.changedTouches[0].pageX, e.changedTouches[0].pageY);
|
1564
|
+
}
|
1565
|
+
|
1566
|
+
function mousemoveWindow(e) {
|
1567
|
+
// do nothing if we are not dragging a piece
|
1568
|
+
if (DRAGGING_A_PIECE !== true) return;
|
1569
|
+
|
1570
|
+
updateDraggedPiece(e.pageX, e.pageY);
|
1571
|
+
}
|
1572
|
+
|
1573
|
+
function touchmoveWindow(e) {
|
1574
|
+
// do nothing if we are not dragging a piece
|
1575
|
+
if (DRAGGING_A_PIECE !== true) return;
|
1576
|
+
|
1577
|
+
// prevent screen from scrolling
|
1578
|
+
e.preventDefault();
|
1579
|
+
|
1580
|
+
updateDraggedPiece(e.originalEvent.changedTouches[0].pageX,
|
1581
|
+
e.originalEvent.changedTouches[0].pageY);
|
1582
|
+
}
|
1583
|
+
|
1584
|
+
function mouseupWindow(e) {
|
1585
|
+
// do nothing if we are not dragging a piece
|
1586
|
+
if (DRAGGING_A_PIECE !== true) return;
|
1587
|
+
|
1588
|
+
// get the location
|
1589
|
+
var location = isXYOnSquare(e.pageX, e.pageY);
|
1590
|
+
|
1591
|
+
stopDraggedPiece(location);
|
1592
|
+
}
|
1593
|
+
|
1594
|
+
function touchendWindow(e) {
|
1595
|
+
// do nothing if we are not dragging a piece
|
1596
|
+
if (DRAGGING_A_PIECE !== true) return;
|
1597
|
+
|
1598
|
+
// get the location
|
1599
|
+
var location = isXYOnSquare(e.originalEvent.changedTouches[0].pageX,
|
1600
|
+
e.originalEvent.changedTouches[0].pageY);
|
1601
|
+
|
1602
|
+
stopDraggedPiece(location);
|
1603
|
+
}
|
1604
|
+
|
1605
|
+
function mouseenterSquare(e) {
|
1606
|
+
// do not fire this event if we are dragging a piece
|
1607
|
+
// NOTE: this should never happen, but it's a safeguard
|
1608
|
+
if (DRAGGING_A_PIECE !== false) return;
|
1609
|
+
|
1610
|
+
if (cfg.hasOwnProperty('onMouseoverSquare') !== true ||
|
1611
|
+
typeof cfg.onMouseoverSquare !== 'function') return;
|
1612
|
+
|
1613
|
+
// get the square
|
1614
|
+
var square = $(e.currentTarget).attr('data-square');
|
1615
|
+
|
1616
|
+
// NOTE: this should never happen; defensive
|
1617
|
+
if (validSquare(square) !== true) return;
|
1618
|
+
|
1619
|
+
// get the piece on this square
|
1620
|
+
var piece = false;
|
1621
|
+
if (CURRENT_POSITION.hasOwnProperty(square) === true) {
|
1622
|
+
piece = CURRENT_POSITION[square];
|
1623
|
+
}
|
1624
|
+
|
1625
|
+
// execute their function
|
1626
|
+
cfg.onMouseoverSquare(square, piece, deepCopy(CURRENT_POSITION),
|
1627
|
+
CURRENT_ORIENTATION);
|
1628
|
+
}
|
1629
|
+
|
1630
|
+
function mouseleaveSquare(e) {
|
1631
|
+
// do not fire this event if we are dragging a piece
|
1632
|
+
// NOTE: this should never happen, but it's a safeguard
|
1633
|
+
if (DRAGGING_A_PIECE !== false) return;
|
1634
|
+
|
1635
|
+
if (cfg.hasOwnProperty('onMouseoutSquare') !== true ||
|
1636
|
+
typeof cfg.onMouseoutSquare !== 'function') return;
|
1637
|
+
|
1638
|
+
// get the square
|
1639
|
+
var square = $(e.currentTarget).attr('data-square');
|
1640
|
+
|
1641
|
+
// NOTE: this should never happen; defensive
|
1642
|
+
if (validSquare(square) !== true) return;
|
1643
|
+
|
1644
|
+
// get the piece on this square
|
1645
|
+
var piece = false;
|
1646
|
+
if (CURRENT_POSITION.hasOwnProperty(square) === true) {
|
1647
|
+
piece = CURRENT_POSITION[square];
|
1648
|
+
}
|
1649
|
+
|
1650
|
+
// execute their function
|
1651
|
+
cfg.onMouseoutSquare(square, piece, deepCopy(CURRENT_POSITION),
|
1652
|
+
CURRENT_ORIENTATION);
|
1653
|
+
}
|
1654
|
+
|
1655
|
+
//------------------------------------------------------------------------------
|
1656
|
+
// Initialization
|
1657
|
+
//------------------------------------------------------------------------------
|
1658
|
+
|
1659
|
+
function addEvents() {
|
1660
|
+
// prevent browser "image drag"
|
1661
|
+
$('body').on('mousedown mousemove', '.' + CSS.piece, stopDefault);
|
1662
|
+
|
1663
|
+
// mouse drag pieces
|
1664
|
+
boardEl.on('mousedown', '.' + CSS.square, mousedownSquare);
|
1665
|
+
containerEl.on('mousedown', '.' + CSS.sparePieces + ' .' + CSS.piece,
|
1666
|
+
mousedownSparePiece);
|
1667
|
+
|
1668
|
+
// mouse enter / leave square
|
1669
|
+
boardEl.on('mouseenter', '.' + CSS.square, mouseenterSquare);
|
1670
|
+
boardEl.on('mouseleave', '.' + CSS.square, mouseleaveSquare);
|
1671
|
+
|
1672
|
+
// IE doesn't like the events on the window object, but other browsers
|
1673
|
+
// perform better that way
|
1674
|
+
if (isMSIE() === true) {
|
1675
|
+
// IE-specific prevent browser "image drag"
|
1676
|
+
document.ondragstart = function() { return false; };
|
1677
|
+
|
1678
|
+
$('body').on('mousemove', mousemoveWindow);
|
1679
|
+
$('body').on('mouseup', mouseupWindow);
|
1680
|
+
}
|
1681
|
+
else {
|
1682
|
+
$(window).on('mousemove', mousemoveWindow);
|
1683
|
+
$(window).on('mouseup', mouseupWindow);
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
// touch drag pieces
|
1687
|
+
if (isTouchDevice() === true) {
|
1688
|
+
boardEl.on('touchstart', '.' + CSS.square, touchstartSquare);
|
1689
|
+
containerEl.on('touchstart', '.' + CSS.sparePieces + ' .' + CSS.piece,
|
1690
|
+
touchstartSparePiece);
|
1691
|
+
$(window).on('touchmove', touchmoveWindow);
|
1692
|
+
$(window).on('touchend', touchendWindow);
|
1693
|
+
}
|
1694
|
+
}
|
1695
|
+
|
1696
|
+
function initDom() {
|
1697
|
+
// build board and save it in memory
|
1698
|
+
cacheImages();
|
1699
|
+
containerEl.html(buildBoardContainer());
|
1700
|
+
boardEl = containerEl.find('.' + CSS.board);
|
1701
|
+
|
1702
|
+
if (cfg.sparePieces === true) {
|
1703
|
+
sparePiecesTopEl = containerEl.find('.' + CSS.sparePiecesTop);
|
1704
|
+
sparePiecesBottomEl = containerEl.find('.' + CSS.sparePiecesBottom);
|
1705
|
+
}
|
1706
|
+
|
1707
|
+
// create the drag piece
|
1708
|
+
var draggedPieceId = createId();
|
1709
|
+
$('body').append(buildPiece('wP', true, draggedPieceId));
|
1710
|
+
draggedPieceEl = $('#' + draggedPieceId);
|
1711
|
+
|
1712
|
+
// get the border size
|
1713
|
+
BOARD_BORDER_SIZE = parseInt(boardEl.css('borderLeftWidth'), 10);
|
1714
|
+
|
1715
|
+
// set the size and draw the board
|
1716
|
+
widget.resize();
|
1717
|
+
}
|
1718
|
+
|
1719
|
+
function init() {
|
1720
|
+
if (checkDeps() !== true ||
|
1721
|
+
expandConfig() !== true) return;
|
1722
|
+
|
1723
|
+
// create unique IDs for all the elements we will create
|
1724
|
+
createElIds();
|
1725
|
+
|
1726
|
+
initDom();
|
1727
|
+
addEvents();
|
1728
|
+
}
|
1729
|
+
|
1730
|
+
// go time
|
1731
|
+
init();
|
1732
|
+
|
1733
|
+
// return the widget object
|
1734
|
+
return widget;
|
1735
|
+
|
1736
|
+
}; // end window.ChessBoard
|
1737
|
+
|
1738
|
+
// expose util functions
|
1739
|
+
window.ChessBoard.fenToObj = fenToObj;
|
1740
|
+
window.ChessBoard.objToFen = objToFen;
|
1741
|
+
|
1742
|
+
})(); // end anonymous wrapper
|