/*!
- * chessboard.js v0.3.0
+ * chessboard.js v0.3.0+asn
*
* Copyright 2013 Chris Oakman
+ * Portions copyright 2022 Steinar H. Gunderson
* Released under the MIT license
* http://chessboardjs.com/license
*
containerEl = containerElOrId;
}
- // 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;
}
* @param {!string=} id
*/
function buildPiece(piece, hidden, id) {
- var html = '<img src="' + buildPieceImgSrc(piece) + '" ';
+ let img = document.createElement('img');
+ img.src = buildPieceImgSrc(piece);
if (id && typeof id === 'string') {
- html += 'id="' + id + '" ';
+ img.setAttribute('id', id);
}
- html += 'alt="" ' +
- 'class="' + CSS.piece + '" ' +
- 'data-piece="' + piece + '" ' +
- 'style="width: ' + SQUARE_SIZE + 'px;' +
- 'height: ' + SQUARE_SIZE + 'px;';
+ img.setAttribute('alt', '');
+ img.classList.add(CSS.piece);
+ img.setAttribute('data-piece', piece);
+ img.style.width = SQUARE_SIZE + 'px';
+ img.style.height = SQUARE_SIZE + 'px';
if (hidden === true) {
- html += 'display:none;';
+ img.style.display = 'none';
}
- html += '" />';
-
- let elem = document.createElement('template');
- elem.innerHTML = html;
- return elem.content;
+ return img;
}
function buildSparePieces(color) {
// animate the piece to the destination square
animatedPieceEl.addEventListener('transitionend', complete, {once: true});
requestAnimationFrame(() => {
- animatedPieceEl.style.transitionProperty = 'top left';
+ animatedPieceEl.style.transitionProperty = 'top, left';
animatedPieceEl.style.transitionDuration = cfg.moveSpeed + 'ms';
animatedPieceEl.style.top = destSquarePosition.top + 'px';
animatedPieceEl.style.left = destSquarePosition.left + 'px';
duration: cfg.moveSpeed,
complete: complete
};
- $(animatedPieceEl).animate(destOffset, opts);
+ //$(animatedPieceEl).animate(destOffset, opts);
}
-function fadeIn(piece, onFinish) {
- piece.style.opacity = 0;
- piece.style.display = null;
- piece.addEventListener('transitionend', onFinish, {once: true});
+function fadeIn(pieces, onFinish) {
+ pieces.forEach((piece) => {
+ piece.style.opacity = 0;
+ piece.style.display = null;
+ piece.addEventListener('transitionend', onFinish, {once: true});
+ });
requestAnimationFrame(() => {
- piece.style.transitionProperty = 'opacity';
- piece.style.transitionDuration = cfg.appearSpeed + 'ms';
- piece.style.opacity = 1;
+ pieces.forEach((piece) => {
+ piece.style.transitionProperty = 'opacity';
+ piece.style.transitionDuration = cfg.appearSpeed + 'ms';
+ piece.style.opacity = 1;
+ });
});
}
-function fadeOut(piece, onFinish) {
- piece.style.opacity = 1;
- piece.style.display = null;
- piece.addEventListener('transitionend', onFinish, {once: true});
+function fadeOut(pieces, onFinish) {
+ pieces.forEach((piece) => {
+ piece.style.opacity = 1;
+ piece.style.display = null;
+ piece.addEventListener('transitionend', onFinish, {once: true});
+ });
requestAnimationFrame(() => {
- piece.style.transitionProperty = 'opacity';
- piece.style.transitionDuration = cfg.trashSpeed + 'ms';
- piece.style.opacity = 0;
+ pieces.forEach((piece) => {
+ piece.style.transitionProperty = 'opacity';
+ piece.style.transitionDuration = cfg.trashSpeed + 'ms';
+ piece.style.opacity = 0;
+ });
});
}
}
}
- for (var i = 0; i < a.length; i++) {
- // clear a piece
- if (a[i].type === 'clear') {
- document.getElementById(SQUARE_ELS_IDS[a[i].square]).querySelectorAll('.' + CSS.piece).forEach(
- (piece) => fadeOut(piece, onFinish)
- );
- }
+ requestAnimationFrame(() => { // Firefox workaround.
+ let fadeout_pieces = [];
+ let fadein_pieces = [];
- // add a piece (no spare pieces)
- if (a[i].type === 'add' && cfg.sparePieces !== true) {
- let square = document.getElementById(SQUARE_ELS_IDS[a[i].square]);
- square.append(buildPiece(a[i].piece, true));
- let piece = square.querySelector('.' + CSS.piece);
- fadeIn(piece, onFinish);
- }
+ for (var i = 0; i < a.length; i++) {
+ // clear a piece
+ if (a[i].type === 'clear') {
+ document.getElementById(SQUARE_ELS_IDS[a[i].square]).querySelectorAll('.' + CSS.piece).forEach(
+ (piece) => fadeout_pieces.push(piece)
+ );
+ }
- // add a piece from a spare piece
- if (a[i].type === 'add' && cfg.sparePieces === true) {
- animateSparePieceToSquare(a[i].piece, a[i].square, onFinish);
+ // add a piece (no spare pieces)
+ if (a[i].type === 'add' && cfg.sparePieces !== true) {
+ let square = document.getElementById(SQUARE_ELS_IDS[a[i].square]);
+ square.append(buildPiece(a[i].piece, true));
+ let piece = square.querySelector('.' + CSS.piece);
+ fadein_pieces.push(piece);
+ }
+
+ // 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);
+ }
}
- // move a piece
- if (a[i].type === 'move') {
- animateSquareToSquare(a[i].source, a[i].destination, a[i].piece,
- onFinish);
+ // TODO: Batch moves as well, not just fade in/out.
+ // (We batch them because requestAnimationFrame seemingly costs real time.)
+ if (fadeout_pieces.length > 0) {
+ fadeOut(fadeout_pieces, onFinish);
}
- }
+ if (fadein_pieces.length > 0) {
+ fadeIn(fadein_pieces, onFinish);
+ }
+ });
}
// returns the distance between two squares
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 < 8; i++) {
- for (var j = 0; j < 8; 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];
+ let best_square = false;
+ let best_dist = 1e9;
+ for (var i = 0; i < COLUMNS.length; i++) {
+ for (var j = 1; j <= 8; j++) {
+ let other_square = COLUMNS[i] + j;
- if (position.hasOwnProperty(s) === true && position[s] === piece) {
- return s;
+ if (position[other_square] === piece && square != other_square) {
+ let dist = squareDistance(square, other_square);
+ if (dist < best_dist) {
+ best_square = other_square;
+ best_dist = dist;
+ }
+ }
}
}
- return false;
+ return best_square;
}
// calculate an array of animations that need to happen in order to get
// animate the piece to the target square
draggedPieceEl.addEventListener('transitionend', complete, {once: true});
requestAnimationFrame(() => {
- draggedPieceEl.style.transitionProperty = 'top left';
+ draggedPieceEl.style.transitionProperty = 'top, left';
draggedPieceEl.style.transitionDuration = cfg.snapbackSpeed + 'ms';
draggedPieceEl.style.top = sourceSquarePosition.top + 'px';
draggedPieceEl.style.left = sourceSquarePosition.left + 'px';
// hide the dragged piece
// FIXME: support this for non-jquery
- $(draggedPieceEl).fadeOut(cfg.trashSpeed);
+ //$(draggedPieceEl).fadeOut(cfg.trashSpeed);
// set state
DRAGGING_A_PIECE = false;
// snap the piece to the target square
draggedPieceEl.addEventListener('transitionend', complete, {once: true});
requestAnimationFrame(() => {
- draggedPieceEl.style.transitionProperty = 'top left';
+ draggedPieceEl.style.transitionProperty = 'top, left';
draggedPieceEl.style.transitionDuration = cfg.snapSpeed + 'ms';
draggedPieceEl.style.top = targetSquarePosition.top + 'px';
draggedPieceEl.style.left = targetSquarePosition.left + 'px';
return;
}
- e = e.originalEvent;
beginDraggingPiece(square, CURRENT_POSITION[square],
e.changedTouches[0].pageX, e.changedTouches[0].pageY);
}
var piece = e.target.getAttribute('data-piece');
- e = e.originalEvent;
beginDraggingPiece('spare', piece,
e.changedTouches[0].pageX, e.changedTouches[0].pageY);
}
// prevent screen from scrolling
e.preventDefault();
- updateDraggedPiece(e.originalEvent.changedTouches[0].pageX,
- e.originalEvent.changedTouches[0].pageY);
+ updateDraggedPiece(e.changedTouches[0].pageX,
+ e.changedTouches[0].pageY);
}
function mouseupWindow(e) {
if (DRAGGING_A_PIECE !== true) return;
// get the location
- var location = isXYOnSquare(e.originalEvent.changedTouches[0].pageX,
- e.originalEvent.changedTouches[0].pageY);
+ var location = isXYOnSquare(e.changedTouches[0].pageX,
+ e.changedTouches[0].pageY);
stopDraggedPiece(location);
}