1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183 |
- /*!
- * chessboard.js v0.3.0
- *
- * Copyright 2013 Chris Oakman
- * Released under the MIT license
- * http://chessboardjs.com/license
- *
- * Date: 10 Aug 2013
- */
- // start anonymous scope
- ;(function() {
- 'use strict';
- //------------------------------------------------------------------------------
- // Chess Util Functions
- //------------------------------------------------------------------------------
- var COLUMNS = 'abcdefg'.split('');
- function validMove(move) {
- // move should be a string
- if (typeof move !== 'string') return false;
- // move should be in the form of "e2-e4", "f6-d5"
- var tmp = move.split('-');
- if (tmp.length !== 2) return false;
- return (validSquare(tmp[0]) === true && validSquare(tmp[1]) === true);
- }
- function validSquare(square) {
- if (typeof square !== 'string') return false;
- return (square.search(/^[a-h][1-7]$/) !== -1);
- }
- function validPieceCode(code) {
- if (typeof code !== 'string') return false;
- return (code.search(/^[bw][N]$/) !== -1);
- }
- // TODO: this whole function could probably be replaced with a single regex
- function validFen(fen) {
- if (typeof fen !== 'string') return false;
- // cut off any move, castling, etc info from the end
- // we're only interested in position information
- fen = fen.replace(/ .+$/, '');
- // FEN should be 8 sections separated by slashes
- var chunks = fen.split('/');
- if (chunks.length !== 7) return false;
- // check the piece sections
- for (var i = 0; i < 7; i++) {
- if (chunks[i] === '' ||
- chunks[i].length > 7 ||
- chunks[i].search(/[^nN1-7]/) !== -1) {
- return false;
- }
- }
- return true;
- }
- function validPositionObject(pos) {
- if (typeof pos !== 'object') return false;
- for (var i in pos) {
- if (pos.hasOwnProperty(i) !== true) continue;
- if (validSquare(i) !== true || validPieceCode(pos[i]) !== true) {
- return false;
- }
- }
- return true;
- }
- // convert FEN piece code to bP, wK, etc
- function fenToPieceCode(piece) {
- // black piece
- if (piece.toLowerCase() === piece) {
- return 'b' + piece.toUpperCase();
- }
- // white piece
- return 'w' + piece.toUpperCase();
- }
- // convert bP, wK, etc code to FEN structure
- function pieceCodeToFen(piece) {
- var tmp = piece.split('');
- // white piece
- if (tmp[0] === 'w') {
- return tmp[1].toUpperCase();
- }
- // black piece
- return tmp[1].toLowerCase();
- }
- // convert FEN string to position object
- // returns false if the FEN string is invalid
- function fenToObj(fen) {
- if (validFen(fen) !== true) {
- return false;
- }
- // cut off any move, castling, etc info from the end
- // we're only interested in position information
- fen = fen.replace(/ .+$/, '');
- var rows = fen.split('/');
- var position = {};
- var currentRow = 7;
- for (var i = 0; i < 7; i++) {
- var row = rows[i].split('');
- var colIndex = 0;
- // loop through each character in the FEN section
- for (var j = 0; j < row.length; j++) {
- // number / empty squares
- if (row[j].search(/[1-7]/) !== -1) {
- var emptySquares = parseInt(row[j], 10);
- colIndex += emptySquares;
- }
- // piece
- else {
- var square = COLUMNS[colIndex] + currentRow;
- position[square] = fenToPieceCode(row[j]);
- colIndex++;
- }
- }
- currentRow--;
- }
- return position;
- }
- // position object to FEN string
- // returns false if the obj is not a valid position object
- function objToFen(obj) {
- if (validPositionObject(obj) !== true) {
- return false;
- }
- var fen = '';
- var currentRow = 7;
- for (var i = 0; i < 7; i++) {
- for (var j = 0; j < 7; j++) {
- var square = COLUMNS[j] + currentRow;
- // piece exists
- if (obj.hasOwnProperty(square) === true) {
- fen += pieceCodeToFen(obj[square]);
- }
- // empty space
- else {
- fen += '1';
- }
- }
- if (i !== 6) {
- fen += '/';
- }
- currentRow--;
- }
- // squeeze the numbers together
- // haha, I love this solution...
- fen = fen.replace(/1111111/g, '7');
- fen = fen.replace(/111111/g, '6');
- fen = fen.replace(/11111/g, '5');
- fen = fen.replace(/1111/g, '4');
- fen = fen.replace(/111/g, '3');
- fen = fen.replace(/11/g, '2');
- return fen;
- }
- window['ChessBoard'] = window['ChessBoard'] || function(containerElOrId, cfg) {
- 'use strict';
- cfg = cfg || {};
- //------------------------------------------------------------------------------
- // Constants
- //------------------------------------------------------------------------------
- var MINIMUM_JQUERY_VERSION = '1.7.0',
- START_FEN = '7/7/7/7/7/7/7/7',
- START_POSITION = fenToObj(START_FEN);
- // use unique class names to prevent clashing with anything else on the page
- // and simplify selectors
- var CSS = {
- alpha: 'alpha-d2270',
- black: 'black-3c85d',
- board: 'board-b72b1',
- chessboard: 'chessboard-63f37',
- clearfix: 'clearfix-7da63',
- highlight1: 'highlight1-32417',
- highlight2: 'highlight2-9c5d2',
- notation: 'notation-322f9',
- numeric: 'numeric-fc462',
- piece: 'piece-417db',
- row: 'row-5277c',
- sparePieces: 'spare-pieces-7492f',
- sparePiecesBottom: 'spare-pieces-bottom-ae20f',
- sparePiecesTop: 'spare-pieces-top-4028b',
- square: 'square-55d63',
- white: 'white-1e1d7'
- };
- //------------------------------------------------------------------------------
- // Module Scope Variables
- //------------------------------------------------------------------------------
- // DOM elements
- var containerEl,
- boardEl,
- draggedPieceEl,
- sparePiecesTopEl,
- sparePiecesBottomEl;
- // constructor return object
- var widget = {};
- //------------------------------------------------------------------------------
- // Stateful
- //------------------------------------------------------------------------------
- var ANIMATION_HAPPENING = false,
- BOARD_BORDER_SIZE = 2,
- CURRENT_ORIENTATION = 'white',
- CURRENT_POSITION = {},
- SQUARE_SIZE,
- DRAGGED_PIECE,
- DRAGGED_PIECE_LOCATION,
- DRAGGED_PIECE_SOURCE,
- DRAGGING_A_PIECE = false,
- SPARE_PIECE_ELS_IDS = {},
- SQUARE_ELS_IDS = {},
- SQUARE_ELS_OFFSETS;
- //------------------------------------------------------------------------------
- // JS Util Functions
- //------------------------------------------------------------------------------
- // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
- function createId() {
- return 'xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx'.replace(/x/g, function(c) {
- var r = Math.random() * 16 | 0;
- return r.toString(16);
- });
- }
- function deepCopy(thing) {
- return JSON.parse(JSON.stringify(thing));
- }
- function parseSemVer(version) {
- var tmp = version.split('.');
- return {
- major: parseInt(tmp[0], 10),
- minor: parseInt(tmp[1], 10),
- patch: parseInt(tmp[2], 10)
- };
- }
- // returns true if version is >= minimum
- function compareSemVer(version, minimum) {
- version = parseSemVer(version);
- minimum = parseSemVer(minimum);
- var versionNum = (version.major * 10000 * 10000) +
- (version.minor * 10000) + version.patch;
- var minimumNum = (minimum.major * 10000 * 10000) +
- (minimum.minor * 10000) + minimum.patch;
- return (versionNum >= minimumNum);
- }
- //------------------------------------------------------------------------------
- // Validation / Errors
- //------------------------------------------------------------------------------
- function error(code, msg, obj) {
- // do nothing if showErrors is not set
- if (cfg.hasOwnProperty('showErrors') !== true ||
- cfg.showErrors === false) {
- return;
- }
- var errorText = 'ChessBoard Error ' + code + ': ' + msg;
- // print to console
- if (cfg.showErrors === 'console' &&
- typeof console === 'object' &&
- typeof console.log === 'function') {
- console.log(errorText);
- if (arguments.length >= 2) {
- console.log(obj);
- }
- return;
- }
- // alert errors
- if (cfg.showErrors === 'alert') {
- if (obj) {
- errorText += '\n\n' + JSON.stringify(obj);
- }
- window.alert(errorText);
- return;
- }
- // custom function
- if (typeof cfg.showErrors === 'function') {
- cfg.showErrors(code, msg, obj);
- }
- }
- // check dependencies
- function checkDeps() {
- // if containerId is a string, it must be the ID of a DOM node
- if (typeof containerElOrId === 'string') {
- // cannot be empty
- if (containerElOrId === '') {
- window.alert('ChessBoard Error 1001: ' +
- 'The first argument to ChessBoard() cannot be an empty string.' +
- '\n\nExiting...');
- return false;
- }
- // make sure the container element exists in the DOM
- var el = document.getElementById(containerElOrId);
- if (! el) {
- window.alert('ChessBoard Error 1002: Element with id "' +
- containerElOrId + '" does not exist in the DOM.' +
- '\n\nExiting...');
- return false;
- }
- // set the containerEl
- containerEl = $(el);
- }
- // else it must be something that becomes a jQuery collection
- // with size 1
- // ie: a single DOM node or jQuery object
- else {
- containerEl = $(containerElOrId);
- if (containerEl.length !== 1) {
- window.alert('ChessBoard Error 1003: The first argument to ' +
- 'ChessBoard() must be an ID or a single DOM node.' +
- '\n\nExiting...');
- return false;
- }
- }
- // JSON must exist
- if (! window.JSON ||
- typeof JSON.stringify !== 'function' ||
- typeof JSON.parse !== 'function') {
- window.alert('ChessBoard Error 1004: JSON does not exist. ' +
- 'Please include a JSON polyfill.\n\nExiting...');
- return false;
- }
- // check for a compatible version of jQuery
- if (! (typeof window.$ && $.fn && $.fn.jquery &&
- compareSemVer($.fn.jquery, MINIMUM_JQUERY_VERSION) === true)) {
- window.alert('ChessBoard Error 1005: Unable to find a valid version ' +
- 'of jQuery. Please include jQuery ' + MINIMUM_JQUERY_VERSION + ' or ' +
- 'higher on the page.\n\nExiting...');
- return false;
- }
- return true;
- }
- function validAnimationSpeed(speed) {
- if (speed === 'fast' || speed === 'slow') {
- return true;
- }
- if ((parseInt(speed, 10) + '') !== (speed + '')) {
- return false;
- }
- return (speed >= 0);
- }
- // validate config / set default options
- function expandConfig() {
- if (typeof cfg === 'string' || validPositionObject(cfg) === true) {
- cfg = {
- position: cfg
- };
- }
- // default for orientation is white
- if (cfg.orientation !== 'black') {
- cfg.orientation = 'white';
- }
- CURRENT_ORIENTATION = cfg.orientation;
- // default for showNotation is true
- if (cfg.showNotation !== false) {
- cfg.showNotation = true;
- }
- // default for draggable is false
- if (cfg.draggable !== true) {
- cfg.draggable = false;
- }
- // default for dropOffBoard is 'snapback'
- if (cfg.dropOffBoard !== 'trash') {
- cfg.dropOffBoard = 'snapback';
- }
- // default for sparePieces is false
- if (cfg.sparePieces !== true) {
- cfg.sparePieces = false;
- }
- // draggable must be true if sparePieces is enabled
- if (cfg.sparePieces === true) {
- cfg.draggable = true;
- }
- // default piece theme is wikipedia
- if (cfg.hasOwnProperty('pieceTheme') !== true ||
- (typeof cfg.pieceTheme !== 'string' &&
- typeof cfg.pieceTheme !== 'function')) {
- cfg.pieceTheme = 'img/chesspieces/wikipedia/{piece}.png';
- }
- // animation speeds
- if (cfg.hasOwnProperty('appearSpeed') !== true ||
- validAnimationSpeed(cfg.appearSpeed) !== true) {
- cfg.appearSpeed = 200;
- }
- if (cfg.hasOwnProperty('moveSpeed') !== true ||
- validAnimationSpeed(cfg.moveSpeed) !== true) {
- cfg.moveSpeed = 200;
- }
- if (cfg.hasOwnProperty('snapbackSpeed') !== true ||
- validAnimationSpeed(cfg.snapbackSpeed) !== true) {
- cfg.snapbackSpeed = 50;
- }
- if (cfg.hasOwnProperty('snapSpeed') !== true ||
- validAnimationSpeed(cfg.snapSpeed) !== true) {
- cfg.snapSpeed = 25;
- }
- if (cfg.hasOwnProperty('trashSpeed') !== true ||
- validAnimationSpeed(cfg.trashSpeed) !== true) {
- cfg.trashSpeed = 100;
- }
- // make sure position is valid
- if (cfg.hasOwnProperty('position') === true) {
- if (cfg.position === 'start') {
- CURRENT_POSITION = deepCopy(START_POSITION);
- }
- else if (validFen(cfg.position) === true) {
- CURRENT_POSITION = fenToObj(cfg.position);
- }
- else if (validPositionObject(cfg.position) === true) {
- CURRENT_POSITION = deepCopy(cfg.position);
- }
- else {
- error(7263, 'Invalid value passed to config.position.', cfg.position);
- }
- }
- return true;
- }
- //------------------------------------------------------------------------------
- // DOM Misc
- //------------------------------------------------------------------------------
- // calculates square size based on the width of the container
- // got a little CSS black magic here, so let me explain:
- // get the width of the container element (could be anything), reduce by 1 for
- // fudge factor, and then keep reducing until we find an exact mod 8 for
- // our square size
- function calculateSquareSize() {
- var containerWidth = parseInt(containerEl.css('width'), 10);
- // defensive, prevent infinite loop
- if (! containerWidth || containerWidth <= 0) {
- return 0;
- }
- // pad one pixel
- var boardWidth = containerWidth - 1;
- while (boardWidth % 7 !== 0 && boardWidth > 0) {
- boardWidth--;
- }
- return (boardWidth / 7);
- }
- // create random IDs for elements
- function createElIds() {
- // squares on the board
- for (var i = 0; i < COLUMNS.length; i++) {
- for (var j = 1; j <= 7; j++) {
- var square = COLUMNS[i] + j;
- SQUARE_ELS_IDS[square] = square + '-' + createId();
- }
- }
- }
- //------------------------------------------------------------------------------
- // Markup Building
- //------------------------------------------------------------------------------
- function buildBoardContainer() {
- var html = '<div class="' + CSS.chessboard + '">';
- html += '<div class="' + CSS.board + '"></div>';
- html += '</div>';
- return html;
- }
- /*
- var buildSquare = function(color, size, id) {
- var html = '<div class="' + CSS.square + ' ' + CSS[color] + '" ' +
- 'style="width: ' + size + 'px; height: ' + size + 'px" ' +
- 'id="' + id + '">';
- if (cfg.showNotation === true) {
- }
- html += '</div>';
- return html;
- };
- */
- function buildBoard(orientation) {
- if (orientation !== 'black') {
- orientation = 'white';
- }
- var html = '';
- // algebraic notation / orientation
- var alpha = deepCopy(COLUMNS);
- var row = 7;
- if (orientation === 'black') {
- alpha.reverse();
- row = 1;
- }
- var squareColor = 'white';
- for (var i = 0; i < 7; i++) {
- html += '<div class="' + CSS.row + '">';
- for (var j = 0; j < 7; j++) {
- var square = alpha[j] + row;
- html += '<div class="' + CSS.square + ' ' + CSS[squareColor] + ' ' +
- 'square-' + square + '" ' +
- 'style="width: ' + SQUARE_SIZE + 'px; height: ' + SQUARE_SIZE + 'px" ' +
- 'id="' + SQUARE_ELS_IDS[square] + '" ' +
- 'data-square="' + square + '">';
- if (cfg.showNotation === true) {
- // alpha notation
- if ((orientation === 'white' && row === 1) ||
- (orientation === 'black' && row === 7)) {
- html += '<div class="' + CSS.notation + ' ' + CSS.alpha + '">' +
- '</div>';
- }
- // numeric notation
- if (j === 0) {
- html += '<div class="' + CSS.notation + ' ' + CSS.numeric + '">' +
- '</div>';
- }
- }
- html += '</div>'; // end .square
- squareColor = (squareColor === 'white' ? 'black' : 'white');
- }
- html += '<div class="' + CSS.clearfix + '"></div></div>';
- squareColor = (squareColor === 'white' ? 'white' : 'black');
- if (orientation === 'white') {
- row--;
- }
- else {
- row++;
- }
- }
- return html;
- }
- function buildPieceImgSrc(piece) {
- if (typeof cfg.pieceTheme === 'function') {
- return cfg.pieceTheme(piece);
- }
- if (typeof cfg.pieceTheme === 'string') {
- return cfg.pieceTheme.replace(/{piece}/g, piece);
- }
- // NOTE: this should never happen
- error(8272, 'Unable to build image source for cfg.pieceTheme.');
- return '';
- }
- function buildPiece(piece, hidden, id) {
- var html = '<img src="' + buildPieceImgSrc(piece) + '" ';
- if (id && typeof id === 'string') {
- html += 'id="' + id + '" ';
- }
- html += 'alt="" ' +
- 'class="' + CSS.piece + '" ' +
- 'data-piece="' + piece + '" ' +
- 'style="width: ' + SQUARE_SIZE + 'px;' +
- 'height: ' + SQUARE_SIZE + 'px;';
- if (hidden === true) {
- html += 'display:none;';
- }
- html += '" />';
- return html;
- }
- //------------------------------------------------------------------------------
- // Animations
- //------------------------------------------------------------------------------
- function animateSquareToSquare(src, dest, piece, completeFn) {
- // get information about the source and destination squares
- var srcSquareEl = $('#' + SQUARE_ELS_IDS[src]);
- var srcSquarePosition = srcSquareEl.offset();
- var destSquareEl = $('#' + SQUARE_ELS_IDS[dest]);
- var destSquarePosition = destSquareEl.offset();
- // create the animated piece and absolutely position it
- // over the source square
- var animatedPieceId = createId();
- $('body').append(buildPiece(piece, true, animatedPieceId));
- var animatedPieceEl = $('#' + animatedPieceId);
- animatedPieceEl.css({
- display: '',
- position: 'absolute',
- top: srcSquarePosition.top,
- left: srcSquarePosition.left
- });
- // remove original piece from source square
- srcSquareEl.find('.' + CSS.piece).remove();
- // on complete
- var complete = function() {
- // add the "real" piece to the destination square
- destSquareEl.append(buildPiece(piece));
- // remove the animated piece
- animatedPieceEl.remove();
- // run complete function
- if (typeof completeFn === 'function') {
- completeFn();
- }
- };
- // animate the piece to the destination square
- var opts = {
- duration: cfg.moveSpeed,
- complete: complete
- };
- animatedPieceEl.animate(destSquarePosition, opts);
- }
- // execute an array of animations
- function doAnimations(a, oldPos, newPos) {
- ANIMATION_HAPPENING = true;
- var numFinished = 0;
- function onFinish() {
- numFinished++;
- // exit if all the animations aren't finished
- if (numFinished !== a.length) return;
- drawPositionInstant();
- ANIMATION_HAPPENING = false;
- // run their onMoveEnd function
- if (cfg.hasOwnProperty('onMoveEnd') === true &&
- typeof cfg.onMoveEnd === 'function') {
- cfg.onMoveEnd(deepCopy(oldPos), deepCopy(newPos));
- }
- }
- for (var i = 0; i < a.length; i++) {
- // clear a piece
- if (a[i].type === 'clear') {
- $('#' + SQUARE_ELS_IDS[a[i].square] + ' .' + CSS.piece)
- .fadeOut(cfg.trashSpeed, onFinish);
- }
- // add a piece (no spare pieces)
- if (a[i].type === 'add' && cfg.sparePieces !== true) {
- $('#' + SQUARE_ELS_IDS[a[i].square])
- .append(buildPiece(a[i].piece, true))
- .find('.' + CSS.piece)
- .fadeIn(cfg.appearSpeed, onFinish);
- }
- // add a piece from a spare piece
- if (a[i].type === 'add' && cfg.sparePieces === true) {
- animateSparePieceToSquare(a[i].piece, a[i].square, onFinish);
- }
- // move a piece
- if (a[i].type === 'move') {
- animateSquareToSquare(a[i].source, a[i].destination, a[i].piece,
- onFinish);
- }
- }
- }
- // returns the distance between two squares
- function squareDistance(s1, s2) {
- s1 = s1.split('');
- var s1x = COLUMNS.indexOf(s1[0]) + 1;
- var s1y = parseInt(s1[1], 10);
- s2 = s2.split('');
- var s2x = COLUMNS.indexOf(s2[0]) + 1;
- var s2y = parseInt(s2[1], 10);
- var xDelta = Math.abs(s1x - s2x);
- var yDelta = Math.abs(s1y - s2y);
- if (xDelta >= yDelta) return xDelta;
- return yDelta;
- }
- // returns an array of closest squares from square
- function createRadius(square) {
- var squares = [];
- // calculate distance of all squares
- for (var i = 0; i < 7; i++) {
- for (var j = 0; j < 7; j++) {
- var s = COLUMNS[i] + (j + 1);
- // skip the square we're starting from
- if (square === s) continue;
- squares.push({
- square: s,
- distance: squareDistance(square, s)
- });
- }
- }
- // sort by distance
- squares.sort(function(a, b) {
- return a.distance - b.distance;
- });
- // just return the square code
- var squares2 = [];
- for (var i = 0; i < squares.length; i++) {
- squares2.push(squares[i].square);
- }
- return squares2;
- }
- // returns the square of the closest instance of piece
- // returns false if no instance of piece is found in position
- function findClosestPiece(position, piece, square) {
- // create array of closest squares from square
- var closestSquares = createRadius(square);
- // search through the position in order of distance for the piece
- for (var i = 0; i < closestSquares.length; i++) {
- var s = closestSquares[i];
- if (position.hasOwnProperty(s) === true && position[s] === piece) {
- return s;
- }
- }
- return false;
- }
- // calculate an array of animations that need to happen in order to get
- // from pos1 to pos2
- function calculateAnimations(pos1, pos2) {
- // make copies of both
- pos1 = deepCopy(pos1);
- pos2 = deepCopy(pos2);
- var animations = [];
- var squaresMovedTo = {};
- // remove pieces that are the same in both positions
- for (var i in pos2) {
- if (pos2.hasOwnProperty(i) !== true) continue;
- if (pos1.hasOwnProperty(i) === true && pos1[i] === pos2[i]) {
- delete pos1[i];
- delete pos2[i];
- }
- }
- // find all the "move" animations
- for (var i in pos2) {
- if (pos2.hasOwnProperty(i) !== true) continue;
- var closestPiece = findClosestPiece(pos1, pos2[i], i);
- if (closestPiece !== false) {
- animations.push({
- type: 'move',
- source: closestPiece,
- destination: i,
- piece: pos2[i]
- });
- delete pos1[closestPiece];
- delete pos2[i];
- squaresMovedTo[i] = true;
- }
- }
- // add pieces to pos2
- for (var i in pos2) {
- if (pos2.hasOwnProperty(i) !== true) continue;
- animations.push({
- type: 'add',
- square: i,
- piece: pos2[i]
- })
- delete pos2[i];
- }
- // clear pieces from pos1
- for (var i in pos1) {
- if (pos1.hasOwnProperty(i) !== true) continue;
- // do not clear a piece if it is on a square that is the result
- // of a "move", ie: a piece capture
- if (squaresMovedTo.hasOwnProperty(i) === true) continue;
- animations.push({
- type: 'clear',
- square: i,
- piece: pos1[i]
- });
- delete pos1[i];
- }
- return animations;
- }
- //------------------------------------------------------------------------------
- // Control Flow
- //------------------------------------------------------------------------------
- function drawPositionInstant() {
- // clear the board
- boardEl.find('.' + CSS.piece).remove();
- // add the pieces
- for (var i in CURRENT_POSITION) {
- if (CURRENT_POSITION.hasOwnProperty(i) !== true) continue;
- $('#' + SQUARE_ELS_IDS[i]).append(buildPiece(CURRENT_POSITION[i]));
- }
- }
- function drawBoard() {
- boardEl.html(buildBoard(CURRENT_ORIENTATION));
- drawPositionInstant();
- }
- // given a position and a set of moves, return a new position
- // with the moves executed
- function calculatePositionFromMoves(position, moves) {
- position = deepCopy(position);
- for (var i in moves) {
- if (moves.hasOwnProperty(i) !== true) continue;
- // skip the move if the position doesn't have a piece on the source square
- if (position.hasOwnProperty(i) !== true) continue;
- var piece = position[i];
- delete position[i];
- position[moves[i]] = piece;
- }
- return position;
- }
- function setCurrentPosition(position) {
- var oldPos = deepCopy(CURRENT_POSITION);
- var newPos = deepCopy(position);
- var oldFen = objToFen(oldPos);
- var newFen = objToFen(newPos);
- // do nothing if no change in position
- if (oldFen === newFen) return;
- // run their onChange function
- if (cfg.hasOwnProperty('onChange') === true &&
- typeof cfg.onChange === 'function') {
- cfg.onChange(oldPos, newPos);
- }
- // update state
- CURRENT_POSITION = position;
- }
- function isXYOnSquare(x, y) {
- for (var i in SQUARE_ELS_OFFSETS) {
- if (SQUARE_ELS_OFFSETS.hasOwnProperty(i) !== true) continue;
- var s = SQUARE_ELS_OFFSETS[i];
- if (x >= s.left && x < s.left + SQUARE_SIZE &&
- y >= s.top && y < s.top + SQUARE_SIZE) {
- return i;
- }
- }
- return 'offboard';
- }
- // records the XY coords of every square into memory
- function captureSquareOffsets() {
- SQUARE_ELS_OFFSETS = {};
- for (var i in SQUARE_ELS_IDS) {
- if (SQUARE_ELS_IDS.hasOwnProperty(i) !== true) continue;
- SQUARE_ELS_OFFSETS[i] = $('#' + SQUARE_ELS_IDS[i]).offset();
- }
- }
- function removeSquareHighlights() {
- boardEl.find('.' + CSS.square)
- .removeClass(CSS.highlight1 + ' ' + CSS.highlight2);
- }
- //------------------------------------------------------------------------------
- // Public Methods
- //------------------------------------------------------------------------------
- // clear the board
- widget.clear = function(useAnimation) {
- widget.position({}, useAnimation);
- };
- /*
- // get or set config properties
- // TODO: write this, GitHub Issue #1
- widget.config = function(arg1, arg2) {
- // get the current config
- if (arguments.length === 0) {
- return deepCopy(cfg);
- }
- };
- */
- // remove the widget from the page
- widget.destroy = function() {
- // remove markup
- containerEl.html('');
- draggedPieceEl.remove();
- // remove event handlers
- containerEl.unbind();
- };
- // flip orientation
- widget.flip = function() {
- widget.orientation('flip');
- };
- /*
- // TODO: write this, GitHub Issue #5
- widget.highlight = function() {
- };
- */
- // move pieces
- widget.move = function() {
- // no need to throw an error here; just do nothing
- if (arguments.length === 0) return;
- var useAnimation = true;
- // collect the moves into an object
- var moves = {};
- for (var i = 0; i < arguments.length; i++) {
- // any "false" to this function means no animations
- if (arguments[i] === false) {
- useAnimation = false;
- continue;
- }
- // skip invalid arguments
- if (validMove(arguments[i]) !== true) {
- error(2826, 'Invalid move passed to the move method.', arguments[i]);
- continue;
- }
- var tmp = arguments[i].split('-');
- moves[tmp[0]] = tmp[1];
- }
- // calculate position from moves
- var newPos = calculatePositionFromMoves(CURRENT_POSITION, moves);
- // update the board
- widget.position(newPos, useAnimation);
- // block the square
- for (var m in moves) {
- var el = document.getElementById(SQUARE_ELS_IDS[m]);
- el.classList.add("blocked");
- }
-
- // return the new position object
- return newPos;
- };
- widget.position = function(position, useAnimation) {
- // no arguments, return the current position
- if (arguments.length === 0) {
- return deepCopy(CURRENT_POSITION);
- }
- // get position as FEN
- if (typeof position === 'string' && position.toLowerCase() === 'fen') {
- return objToFen(CURRENT_POSITION);
- }
- // default for useAnimations is true
- if (useAnimation !== false) {
- useAnimation = true;
- }
- // start position
- if (typeof position === 'string' && position.toLowerCase() === 'start') {
- position = deepCopy(START_POSITION);
- }
- // convert FEN to position object
- if (validFen(position) === true) {
- position = fenToObj(position);
- }
- // validate position object
- if (validPositionObject(position) !== true) {
- error(6482, 'Invalid value passed to the position method.', position);
- return;
- }
- if (useAnimation === true) {
- // start the animations
- doAnimations(calculateAnimations(CURRENT_POSITION, position),
- CURRENT_POSITION, position);
- // set the new position
- setCurrentPosition(position);
- }
- // instant update
- else {
- setCurrentPosition(position);
- drawPositionInstant();
- }
- };
- widget.resize = function() {
- // calulate the new square size
- SQUARE_SIZE = calculateSquareSize();
- // set board width
- boardEl.css('width', (SQUARE_SIZE * 7) + 'px');
- // redraw the board
- drawBoard();
- };
- widget.finalize = function(winCell, lossCell) {
- var el;
-
- el = document.getElementById(SQUARE_ELS_IDS[winCell]);
- el.classList.add("win");
- el = document.getElementById(SQUARE_ELS_IDS[lossCell]);
- el.classList.add("lose");
- };
- // set the starting position
- widget.start = function(useAnimation) {
- widget.position('start', useAnimation);
- };
- //------------------------------------------------------------------------------
- // Browser Events
- //------------------------------------------------------------------------------
- function stopDefault(e) {
- e.preventDefault();
- }
- //------------------------------------------------------------------------------
- // Initialization
- //------------------------------------------------------------------------------
- function initDom() {
- // build board and save it in memory
- containerEl.html(buildBoardContainer());
- boardEl = containerEl.find('.' + CSS.board);
- // get the border size
- BOARD_BORDER_SIZE = parseInt(boardEl.css('borderLeftWidth'), 10);
- // set the size and draw the board
- widget.resize();
- }
- function init() {
- if (checkDeps() !== true ||
- expandConfig() !== true) return;
- // create unique IDs for all the elements we will create
- createElIds();
- initDom();
- }
- // go time
- init();
- // return the widget object
- return widget;
- }; // end window.ChessBoard
- // expose util functions
- window.ChessBoard.fenToObj = fenToObj;
- window.ChessBoard.objToFen = objToFen;
- })(); // end anonymous wrapper
|