';
- if (cfg.showNotation === true) {
+ if (cfg.showNotation) {
// alpha notation
if ((orientation === 'white' && row === 1) ||
(orientation === 'black' && row === 8)) {
- html += '
' +
+ let bottom = 'calc(' + (12.5 * (7-i)) + '% + 1px)';
+ let right = 'calc(' + (12.5 * (7-j)) + '% + 3px)';
+ html += '
' +
alpha[j] + '
';
}
// numeric notation
if (j === 0) {
- html += '
' +
+ let top = 'calc(' + (12.5 * i) + '% + 2px)';
+ let left = 'calc(' + (12.5 * j) + '% + 2px)';
+ html += '
' +
row + '
';
}
}
@@ -623,7 +481,6 @@ function buildBoard(orientation) {
squareColor = (squareColor === 'white' ? 'black' : 'white');
}
- html += '
';
squareColor = (squareColor === 'white' ? 'black' : 'white');
@@ -655,40 +512,16 @@ function buildPieceImgSrc(piece) {
/**
* @param {!string} piece
* @param {boolean=} hidden
- * @param {!string=} id
*/
-function buildPiece(piece, hidden, id) {
- var html = '
';
-
- let elem = document.createElement('template');
- elem.innerHTML = html;
- return elem.content;
-}
-
-function buildSparePieces(color) {
- var pieces = ['wK', 'wQ', 'wR', 'wB', 'wN', 'wP'];
- if (color === 'black') {
- pieces = ['bK', 'bQ', 'bR', 'bB', 'bN', 'bP'];
- }
-
- var html = '';
- for (var i = 0; i < pieces.length; i++) {
- html += buildPiece(pieces[i], false, SPARE_PIECE_ELS_IDS[pieces[i]]);
- }
-
- return html;
+ return img;
}
//------------------------------------------------------------------------------
@@ -704,163 +537,148 @@ function offset(el) { // From https://youmightnotneedjquery.com/.
};
}
-function animateSquareToSquare(src, dest, piece, completeFn) {
- // get information about the source and destination squares
- var srcSquareEl = document.getElementById(SQUARE_ELS_IDS[src]);
- var srcSquarePosition = offset(srcSquareEl);
- var destSquareEl = document.getElementById(SQUARE_ELS_IDS[dest]);
- var destSquarePosition = offset(destSquareEl);
-
- // create the animated piece and absolutely position it
- // over the source square
- var animatedPieceId = createId();
- document.body.append(buildPiece(piece, true, animatedPieceId));
- var animatedPieceEl = document.getElementById(animatedPieceId);
- animatedPieceEl.style.display = null;
- animatedPieceEl.style.position = 'absolute';
- animatedPieceEl.style.top = srcSquarePosition.top + 'px';
- animatedPieceEl.style.left = srcSquarePosition.left + 'px';
-
- // remove original piece(s) from source square
- // TODO: multiple pieces should never really happen, but it will if we are moving
- // while another animation still isn't done
- srcSquareEl.querySelectorAll('.' + CSS.piece).forEach((piece) => 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();
- }
+function findSquarePosition(square) {
+ let s1 = square.split('');
+ var s1x = COLUMNS.indexOf(s1[0]);
+ var s1y = parseInt(s1[1], 10) - 1;
+ if (CURRENT_ORIENTATION === 'white') {
+ s1y = 7 - s1y;
+ }
+ return {
+ top: (s1y * 12.5) + '%',
+ left: (s1x * 12.5) + '%',
};
-
- // animate the piece to the destination square
- animatedPieceEl.addEventListener('transitionend', complete, {once: true});
- requestAnimationFrame(() => {
- animatedPieceEl.style.transitionProperty = 'top left';
- animatedPieceEl.style.transitionDuration = cfg.moveSpeed + 'ms';
- animatedPieceEl.style.top = destSquarePosition.top + 'px';
- animatedPieceEl.style.left = destSquarePosition.left + 'px';
- });
}
-function animateSparePieceToSquare(piece, dest, completeFn) {
- var srcOffset = offset(document.getelementById(SPARE_PIECE_ELS_IDS[piece]));
- var destSquareEl = document.getElementById(SQUARE_ELS_IDS[dest]);
- var destOffset = offset(destSquareEl);
-
- // create the animate piece
- var pieceId = createId();
- document.body.append(buildPiece(piece, true, pieceId));
- var animatedPieceEl = document.getElementById(pieceId);
- animatedPieceEl.style.display = '';
- animatedPieceEl.style.position = 'absolute';
- animatedPieceEl.style.left = srcOffset.left;
- animatedPieceEl.style.top = srcOffset.top;
-
- // on complete
- var complete = function() {
- // add the "real" piece to the destination square
- destSquareEl.querySelector('.' + CSS.piece).remove();
- destSquareEl.append(buildPiece(piece));
-
- // remove the animated piece
- animatedPieceEl.remove();
+// execute an array of animations
+function doAnimations(a, oldPos, newPos) {
+ let fadeout_pieces = [];
+ let fadein_pieces = [];
+ let move_pieces = [];
+ let squares_to_clear = [];
+ let squares_to_fill = {};
+ let removed_pieces = [];
- // run complete function
- if (typeof completeFn === 'function') {
- completeFn();
+ for (var i = 0; i < a.length; i++) {
+ // clear a piece
+ if (a[i].type === 'clear') {
+ let square = a[i].square;
+ let piece = PIECE_ON_SQUARE[square];
+ if (piece) {
+ fadeout_pieces.push(piece);
+ squares_to_clear.push(square);
+ removed_pieces.push(piece);
+ }
}
- };
-
- // animate the piece to the destination square
- // FIXME: support this for non-jquery
- var opts = {
- duration: cfg.moveSpeed,
- complete: complete
- };
- $(animatedPieceEl).animate(destOffset, opts);
-}
-function fadeIn(piece, onFinish) {
- 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;
- });
-}
+ // add a piece
+ if (a[i].type === 'add') {
+ let square = a[i].square;
+ let pos = findSquarePosition(square);
+ let piece = buildPiece(a[i].piece, true);
+ piece.style.left = pos.left;
+ piece.style.top = pos.top;
+ boardEl.append(piece);
+ squares_to_fill[square] = piece;
+ fadein_pieces.push(piece);
+ }
-function fadeOut(piece, onFinish) {
- 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;
- });
-}
+ // move a piece
+ if (a[i].type === 'move') {
+ let piece = PIECE_ON_SQUARE[a[i].source];
+ move_pieces.push([piece, a[i].destination]);
+ squares_to_clear.push(a[i].source);
+ squares_to_fill[a[i].destination] = piece;
+
+ // This is O(n²), but OK.
+ let replaced_piece = PIECE_ON_SQUARE[a[i].destination];
+ if (replaced_piece && !a.some(e => e.type === 'move' && e.source === a[i].destination)) {
+ removed_pieces.push(replaced_piece);
+ }
+ }
+ }
-// execute an array of animations
-function doAnimations(a, oldPos, newPos) {
- ANIMATION_HAPPENING = true;
+ for (const square of squares_to_clear) {
+ delete PIECE_ON_SQUARE[square];
+ }
+ for (const [square, piece] of Object.entries(squares_to_fill)) {
+ PIECE_ON_SQUARE[square] = piece;
+ piece.setAttribute('data-square', square);
+ }
var numFinished = 0;
- function onFinish(e) {
+ function onFinish(e, opt_force) {
if (e && e.target) {
e.target.transitionProperty = null;
+ e.target.transitionDuration = null;
}
- numFinished++;
-
- // exit if all the animations aren't finished
- if (numFinished !== a.length) return;
+ if (opt_force) {
+ // For whatever reason, the transition didn't seem to actually run
+ // (and thus, we didn't get an end event). Finish off now.
+ // (In particular, this seems to happen in Chrome if the tab is
+ // hidden and left alone for a while.)
+ if (numFinished == a.length) return;
+ numFinished = a.length; // Make sure a very delayed later event will be ignored.
+ } else {
+ numFinished++;
+ if (numFinished !== a.length) return;
+ }
- drawPositionInstant();
- ANIMATION_HAPPENING = false;
+ for (let piece of removed_pieces) {
+ piece.remove();
+ }
// run their onMoveEnd function
- if (cfg.hasOwnProperty('onMoveEnd') === true &&
+ if (cfg.hasOwnProperty('onMoveEnd') &&
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') {
- document.getElementById(SQUARE_ELS_IDS[a[i].square]).querySelectorAll('.' + CSS.piece).forEach(
- (piece) => fadeOut(piece, 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(piece, onFinish);
- }
+ if (fadeout_pieces.length == 0 || fadein_pieces.length > 0 || move_pieces.length > 0) {
+ requestAnimationFrame(() => { // Firefox workaround.
+ // Backup in case the transition never runs.
+ setTimeout(() => onFinish(null, true),
+ cfg.appearSpeed + cfg.trashSpeed + cfg.moveSpeed + 100);
- // 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);
- }
+ fadein_pieces.forEach((piece) => {
+ piece.style.opacity = 0;
+ piece.style.display = null;
+ piece.addEventListener('transitionend', onFinish, {once: true});
+ });
+ fadeout_pieces.forEach((piece) => {
+ piece.style.opacity = 1;
+ piece.style.display = null;
+ piece.addEventListener('transitionend', onFinish, {once: true});
+ });
+ for (const [piece, destination] of move_pieces) {
+ // Move it to the end of the stack, which changes the implicit z-index
+ // so that it will go on top of any pieces it's replacing.
+ piece.remove();
+ boardEl.appendChild(piece);
+ piece.addEventListener('transitionend', onFinish, {once: true});
+ }
+ requestAnimationFrame(() => {
+ fadein_pieces.forEach((piece) => {
+ piece.style.transitionProperty = 'opacity';
+ piece.style.transitionDuration = cfg.appearSpeed + 'ms';
+ piece.style.opacity = 1;
+ });
+ fadeout_pieces.forEach((piece) => {
+ piece.style.transitionProperty = 'opacity';
+ piece.style.transitionDuration = cfg.trashSpeed + 'ms';
+ piece.style.opacity = 0;
+ });
+ for (const [piece, destination] of move_pieces) {
+ let destSquarePosition = findSquarePosition(destination);
+ piece.style.transitionProperty = 'top, left';
+ piece.style.transitionDuration = cfg.moveSpeed + 'ms';
+ piece.style.top = destSquarePosition.top;
+ piece.style.left = destSquarePosition.left;
+ }
+ });
+ });
}
}
@@ -881,55 +699,26 @@ function squareDistance(s1, s2) {
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
@@ -944,9 +733,9 @@ function calculateAnimations(pos1, pos2) {
// remove pieces that are the same in both positions
for (var i in pos2) {
- if (pos2.hasOwnProperty(i) !== true) continue;
+ if (!pos2.hasOwnProperty(i)) continue;
- if (pos1.hasOwnProperty(i) === true && pos1[i] === pos2[i]) {
+ if (pos1.hasOwnProperty(i) && pos1[i] === pos2[i]) {
delete pos1[i];
delete pos2[i];
}
@@ -954,7 +743,7 @@ function calculateAnimations(pos1, pos2) {
// find all the "move" animations
for (var i in pos2) {
- if (pos2.hasOwnProperty(i) !== true) continue;
+ if (!pos2.hasOwnProperty(i)) continue;
var closestPiece = findClosestPiece(pos1, pos2[i], i);
if (closestPiece !== false) {
@@ -973,7 +762,7 @@ function calculateAnimations(pos1, pos2) {
// add pieces to pos2
for (var i in pos2) {
- if (pos2.hasOwnProperty(i) !== true) continue;
+ if (!pos2.hasOwnProperty(i)) continue;
animations.push({
type: 'add',
@@ -986,11 +775,11 @@ function calculateAnimations(pos1, pos2) {
// clear pieces from pos1
for (var i in pos1) {
- if (pos1.hasOwnProperty(i) !== true) continue;
+ if (!pos1.hasOwnProperty(i)) 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;
+ if (squaresMovedTo.hasOwnProperty(i)) continue;
animations.push({
type: 'clear',
@@ -1013,27 +802,20 @@ function drawPositionInstant() {
boardEl.querySelectorAll('.' + CSS.piece).forEach((piece) => piece.remove());
// add the pieces
- for (var i in CURRENT_POSITION) {
- if (CURRENT_POSITION.hasOwnProperty(i) !== true) continue;
-
- document.getElementById(SQUARE_ELS_IDS[i]).append(buildPiece(CURRENT_POSITION[i]));
+ for (const [square, piece] of Object.entries(CURRENT_POSITION)) {
+ let pos = findSquarePosition(square);
+ let pieceEl = buildPiece(piece);
+ pieceEl.style.left = pos.left;
+ pieceEl.style.top = pos.top;
+ pieceEl.setAttribute('data-square', square);
+ boardEl.append(pieceEl);
+ PIECE_ON_SQUARE[square] = pieceEl;
}
}
function drawBoard() {
boardEl.innerHTML = buildBoard(CURRENT_ORIENTATION);
drawPositionInstant();
-
- if (cfg.sparePieces === true) {
- if (CURRENT_ORIENTATION === 'white') {
- sparePiecesTopEl.innerHTML = buildSparePieces('black');
- sparePiecesBottomEl.innerHTML = buildSparePieces('white');
- }
- else {
- sparePiecesTopEl.innerHTML = buildSparePieces('white');
- sparePiecesBottomEl.innerHTML = buildSparePieces('black');
- }
- }
}
// given a position and a set of moves, return a new position
@@ -1042,10 +824,10 @@ function calculatePositionFromMoves(position, moves) {
position = deepCopy(position);
for (var i in moves) {
- if (moves.hasOwnProperty(i) !== true) continue;
+ if (!moves.hasOwnProperty(i)) continue;
// skip the move if the position doesn't have a piece on the source square
- if (position.hasOwnProperty(i) !== true) continue;
+ if (!position.hasOwnProperty(i)) continue;
var piece = position[i];
delete position[i];
@@ -1065,7 +847,7 @@ function setCurrentPosition(position) {
if (oldFen === newFen) return;
// run their onChange function
- if (cfg.hasOwnProperty('onChange') === true &&
+ if (cfg.hasOwnProperty('onChange') &&
typeof cfg.onChange === 'function') {
cfg.onChange(oldPos, newPos);
}
@@ -1074,31 +856,6 @@ function setCurrentPosition(position) {
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] = offset(document.getElementById(SQUARE_ELS_IDS[i]));
- }
-}
-
function removeSquareHighlights() {
boardEl.querySelectorAll('.' + CSS.square).forEach((piece) => {
piece.classList.remove(CSS.highlight1);
@@ -1107,21 +864,14 @@ function removeSquareHighlights() {
}
function snapbackDraggedPiece() {
- // there is no "snapback" for spare pieces
- if (DRAGGED_PIECE_SOURCE === 'spare') {
- trashDraggedPiece();
- return;
- }
-
removeSquareHighlights();
// animation complete
function complete() {
drawPositionInstant();
- draggedPieceEl.style.display = 'none';
// run their onSnapbackEnd function
- if (cfg.hasOwnProperty('onSnapbackEnd') === true &&
+ if (cfg.hasOwnProperty('onSnapbackEnd') &&
typeof cfg.onSnapbackEnd === 'function') {
cfg.onSnapbackEnd(DRAGGED_PIECE, DRAGGED_PIECE_SOURCE,
deepCopy(CURRENT_POSITION), CURRENT_ORIENTATION);
@@ -1129,60 +879,52 @@ function snapbackDraggedPiece() {
}
// get source square position
- var sourceSquarePosition =
- offset(document.getElementById(SQUARE_ELS_IDS[DRAGGED_PIECE_SOURCE]));
+ var sourceSquarePosition = findSquarePosition(DRAGGED_PIECE_SOURCE);
// animate the piece to the target square
- draggedPieceEl.addEventListener('transitionend', complete, {once: true});
+ DRAGGED_PIECE.addEventListener('transitionend', complete, {once: true});
requestAnimationFrame(() => {
- draggedPieceEl.style.transitionProperty = 'top left';
- draggedPieceEl.style.transitionDuration = cfg.snapbackSpeed + 'ms';
- draggedPieceEl.style.top = sourceSquarePosition.top + 'px';
- draggedPieceEl.style.left = sourceSquarePosition.left + 'px';
+ DRAGGED_PIECE.style.transitionProperty = 'top, left';
+ DRAGGED_PIECE.style.transitionDuration = cfg.snapbackSpeed + 'ms';
+ DRAGGED_PIECE.style.top = sourceSquarePosition.top;
+ DRAGGED_PIECE.style.left = sourceSquarePosition.left;
});
// set state
DRAGGING_A_PIECE = false;
}
-function trashDraggedPiece() {
+function dropDraggedPieceOnSquare(square) {
removeSquareHighlights();
-
- // remove the source piece
- var newPosition = deepCopy(CURRENT_POSITION);
- delete newPosition[DRAGGED_PIECE_SOURCE];
- setCurrentPosition(newPosition);
-
- // redraw the position
- drawPositionInstant();
-
- // hide the dragged piece
- // FIXME: support this for non-jquery
- $(draggedPieceEl).fadeOut(cfg.trashSpeed);
-
- // set state
DRAGGING_A_PIECE = false;
-}
-function dropDraggedPieceOnSquare(square) {
- removeSquareHighlights();
+ if (DRAGGED_PIECE_SOURCE === square) {
+ // Nothing to do, but call onSnapEnd anyway
+ if (cfg.hasOwnProperty('onSnapEnd') && typeof cfg.onSnapEnd === 'function') {
+ cfg.onSnapEnd(DRAGGED_PIECE_SOURCE, square, DRAGGED_PIECE);
+ }
+ return;
+ }
// update position
var newPosition = deepCopy(CURRENT_POSITION);
+ newPosition[square] = newPosition[DRAGGED_PIECE_SOURCE];
delete newPosition[DRAGGED_PIECE_SOURCE];
- newPosition[square] = DRAGGED_PIECE;
setCurrentPosition(newPosition);
+ delete PIECE_ON_SQUARE[DRAGGED_PIECE_SOURCE];
+ PIECE_ON_SQUARE[square] = DRAGGED_PIECE;
+ DRAGGED_PIECE.setAttribute('data-square', square);
+
// get target square information
- var targetSquarePosition = offset(document.getElementById(SQUARE_ELS_IDS[square]));
+ var targetSquarePosition = findSquarePosition(square);
// animation complete
var complete = function() {
drawPositionInstant();
- draggedPieceEl.style.display = 'none';
// execute their onSnapEnd function
- if (cfg.hasOwnProperty('onSnapEnd') === true &&
+ if (cfg.hasOwnProperty('onSnapEnd') &&
typeof cfg.onSnapEnd === 'function') {
requestAnimationFrame(() => { // HACK: so that we don't add event handlers from the callback...
cfg.onSnapEnd(DRAGGED_PIECE_SOURCE, square, DRAGGED_PIECE);
@@ -1191,16 +933,13 @@ function dropDraggedPieceOnSquare(square) {
};
// snap the piece to the target square
- draggedPieceEl.addEventListener('transitionend', complete, {once: true});
+ DRAGGED_PIECE.addEventListener('transitionend', complete, {once: true});
requestAnimationFrame(() => {
- draggedPieceEl.style.transitionProperty = 'top left';
- draggedPieceEl.style.transitionDuration = cfg.snapSpeed + 'ms';
- draggedPieceEl.style.top = targetSquarePosition.top + 'px';
- draggedPieceEl.style.left = targetSquarePosition.left + 'px';
+ DRAGGED_PIECE.style.transitionProperty = 'top, left';
+ DRAGGED_PIECE.style.transitionDuration = cfg.snapSpeed + 'ms';
+ DRAGGED_PIECE.style.top = targetSquarePosition.top;
+ DRAGGED_PIECE.style.left = targetSquarePosition.left;
});
-
- // set state
- DRAGGING_A_PIECE = false;
}
function beginDraggingPiece(source, piece, x, y) {
@@ -1214,55 +953,62 @@ function beginDraggingPiece(source, piece, x, y) {
// set state
DRAGGING_A_PIECE = true;
- DRAGGED_PIECE = piece;
+ DRAGGED_PIECE = PIECE_ON_SQUARE[source];
DRAGGED_PIECE_SOURCE = source;
+ DRAGGED_PIECE_LOCATION = source;
+ DRAGGED_PIECE.style.transitionProperty = null;
+ DRAGGED_PIECE.style.transitionDuration = null;
+
+ // Move it to the end of the stack, which changes the implicit z-index
+ // so that it will go on top of any pieces it's replacing.
+ DRAGGED_PIECE.remove();
+ boardEl.appendChild(DRAGGED_PIECE);
+
+ // highlight the source square
+ let square = document.querySelector('.' + CSS.square + '[data-square="' + source + '"]');
+ square.classList.add(CSS.highlight1);
+}
- // if the piece came from spare pieces, location is offboard
- if (source === 'spare') {
- DRAGGED_PIECE_LOCATION = 'offboard';
- }
- else {
- DRAGGED_PIECE_LOCATION = source;
- }
-
- // capture the x, y coords of all squares in memory
- captureSquareOffsets();
-
- // create the dragged piece
- draggedPieceEl.setAttribute('src', buildPieceImgSrc(piece));
- draggedPieceEl.style.display = null;
- draggedPieceEl.style.position = 'absolute';
- draggedPieceEl.style.left = (x - (SQUARE_SIZE / 2)) + 'px';
- draggedPieceEl.style.top = (y - (SQUARE_SIZE / 2)) + 'px';
+function findSquareFromEvent(pageX, pageY) {
+ let o = offset(boardEl);
+ let x = pageX - o.left;
+ let y = pageY - o.top;
- if (source !== 'spare') {
- // highlight the source square and hide the piece
- let square = document.getElementById(SQUARE_ELS_IDS[source]);
- square.classList.add(CSS.highlight1);
- square.querySelector('.' + CSS.piece).style.display = 'none';
+ let position = {
+ x: x,
+ y: y,
+ left: Math.floor(x * 8 / boardEl.getBoundingClientRect().width),
+ top: Math.floor(y * 8 / boardEl.getBoundingClientRect().width)
+ };
+ if (CURRENT_ORIENTATION === 'white') {
+ position.top = 7 - position.top;
}
+ if (position.left >= 0 && position.left < 8 && position.top >= 0 && position.top < 8) {
+ position.square = COLUMNS[position.left] + (position.top + 1);
+ } else {
+ position.square = 'offboard';
+ }
+ return position;
}
-function updateDraggedPiece(x, y) {
+function updateDraggedPiece(position) {
// put the dragged piece over the mouse cursor
- draggedPieceEl.style.left = (x - (SQUARE_SIZE / 2)) + 'px';
- draggedPieceEl.style.top = (y - (SQUARE_SIZE / 2)) + 'px';
-
- // get location
- var location = isXYOnSquare(x, y);
+ DRAGGED_PIECE.style.left = 'calc(' + position.x + 'px - 6.25%)';
+ DRAGGED_PIECE.style.top = 'calc(' + position.y + 'px - 6.25%)';
// do nothing if the location has not changed
- if (location === DRAGGED_PIECE_LOCATION) return;
+ if (position === DRAGGED_PIECE_LOCATION) return;
// remove highlight from previous square
- if (validSquare(DRAGGED_PIECE_LOCATION) === true) {
- document.getElementById(SQUARE_ELS_IDS[DRAGGED_PIECE_LOCATION])
+ if (validSquare(DRAGGED_PIECE_LOCATION)) {
+ document.querySelector('.' + CSS.square + '[data-square="' + DRAGGED_PIECE_LOCATION + '"]')
.classList.remove(CSS.highlight2);
}
// add highlight to new square
- if (validSquare(location) === true) {
- document.getElementById(SQUARE_ELS_IDS[location]).classList.add(CSS.highlight2);
+ if (validSquare(position.square)) {
+ document.querySelector('.' + CSS.square + '[data-square="' + position.square + '"]')
+ .classList.add(CSS.highlight2);
}
// run onDragMove
@@ -1273,53 +1019,39 @@ function updateDraggedPiece(x, y) {
}
// update state
- DRAGGED_PIECE_LOCATION = location;
+ DRAGGED_PIECE_LOCATION = position.square;
}
function stopDraggedPiece(location) {
// determine what the action should be
var action = 'drop';
- if (location === 'offboard' && cfg.dropOffBoard === 'snapback') {
+ if (location.square === 'offboard' && cfg.dropOffBoard === 'snapback') {
action = 'snapback';
}
- if (location === 'offboard' && cfg.dropOffBoard === 'trash') {
- action = 'trash';
- }
// run their onDrop function, which can potentially change the drop action
- if (cfg.hasOwnProperty('onDrop') === true &&
+ if (cfg.hasOwnProperty('onDrop') &&
typeof cfg.onDrop === 'function') {
var newPosition = deepCopy(CURRENT_POSITION);
- // source piece is a spare piece and position is off the board
- //if (DRAGGED_PIECE_SOURCE === 'spare' && location === 'offboard') {...}
- // position has not changed; do nothing
-
- // source piece is a spare piece and position is on the board
- if (DRAGGED_PIECE_SOURCE === 'spare' && validSquare(location) === true) {
- // add the piece to the board
- newPosition[location] = DRAGGED_PIECE;
- }
-
- // source piece was on the board and position is off the board
- if (validSquare(DRAGGED_PIECE_SOURCE) === true && location === 'offboard') {
- // remove the piece from the board
- delete newPosition[DRAGGED_PIECE_SOURCE];
- }
-
// source piece was on the board and position is on the board
- if (validSquare(DRAGGED_PIECE_SOURCE) === true &&
- validSquare(location) === true) {
+ if (validSquare(DRAGGED_PIECE_SOURCE) &&
+ validSquare(location.square)) {
// move the piece
delete newPosition[DRAGGED_PIECE_SOURCE];
- newPosition[location] = DRAGGED_PIECE;
+ newPosition[location.square] = DRAGGED_PIECE;
+ if (location.square !== DRAGGED_PIECE_SOURCE) {
+ PIECE_ON_SQUARE[location.square] = PIECE_ON_SQUARE[DRAGGED_PIECE_SOURCE];
+ DRAGGED_PIECE.setAttribute('data-square', location.square);
+ delete PIECE_ON_SQUARE[DRAGGED_PIECE_SOURCE];
+ }
}
var oldPosition = deepCopy(CURRENT_POSITION);
- var result = cfg.onDrop(DRAGGED_PIECE_SOURCE, location, DRAGGED_PIECE,
+ var result = cfg.onDrop(DRAGGED_PIECE_SOURCE, location.square, DRAGGED_PIECE,
newPosition, oldPosition, CURRENT_ORIENTATION);
- if (result === 'snapback' || result === 'trash') {
+ if (result === 'snapback') {
action = result;
}
}
@@ -1328,11 +1060,8 @@ function stopDraggedPiece(location) {
if (action === 'snapback') {
snapbackDraggedPiece();
}
- else if (action === 'trash') {
- trashDraggedPiece();
- }
else if (action === 'drop') {
- dropDraggedPieceOnSquare(location);
+ dropDraggedPieceOnSquare(location.square);
}
}
@@ -1397,7 +1126,7 @@ widget.move = function() {
}
// skip invalid arguments
- if (validMove(arguments[i]) !== true) {
+ if (!validMove(arguments[i])) {
error(2826, 'Invalid move passed to the move method.', arguments[i]);
continue;
}
@@ -1465,17 +1194,17 @@ widget.position = function(position, useAnimation) {
}
// convert FEN to position object
- if (validFen(position) === true) {
+ if (validFen(position)) {
position = fenToObj(position);
}
// validate position object
- if (validPositionObject(position) !== true) {
+ if (!validPositionObject(position)) {
error(6482, 'Invalid value passed to the position method.', position);
return;
}
- if (useAnimation === true) {
+ if (useAnimation) {
// start the animations
doAnimations(calculateAnimations(CURRENT_POSITION, position),
CURRENT_POSITION, position);
@@ -1491,24 +1220,6 @@ widget.position = function(position, useAnimation) {
};
widget.resize = function() {
- // calulate the new square size
- SQUARE_SIZE = calculateSquareSize();
-
- // set board width
- boardEl.style.width = (SQUARE_SIZE * 8) + 'px';
-
- // set drag piece size
- if (draggedPieceEl !== null) {
- draggedPieceEl.style.height = SQUARE_SIZE + 'px';
- draggedPieceEl.style.width = SQUARE_SIZE + 'px';
- }
-
- // spare pieces
- if (cfg.sparePieces === true) {
- containerEl.querySelector('.' + CSS.sparePieces)
- .style.paddingLeft = (SQUARE_SIZE + BOARD_BORDER_SIZE) + 'px';
- }
-
// redraw the board
drawBoard();
};
@@ -1527,22 +1238,17 @@ function isTouchDevice() {
}
function mousedownSquare(e) {
- let target = e.target.closest('.' + CSS.square);
- if (!target) {
- return;
- }
-
- // do nothing if we're not draggable
- if (cfg.draggable !== true) return;
-
- var square = target.getAttribute('data-square');
+ let square = e.target.getAttribute('data-square');
// no piece on this square
- if (validSquare(square) !== true ||
- CURRENT_POSITION.hasOwnProperty(square) !== true) {
+ if (!validSquare(square) ||
+ !CURRENT_POSITION.hasOwnProperty(square)) {
return;
}
+ // do nothing if we're not draggable
+ if (!cfg.draggable) return;
+
beginDraggingPiece(square, CURRENT_POSITION[square], e.pageX, e.pageY);
}
@@ -1553,86 +1259,51 @@ function touchstartSquare(e) {
}
// do nothing if we're not draggable
- if (cfg.draggable !== true) return;
+ if (!cfg.draggable) return;
var square = target.getAttribute('data-square');
// no piece on this square
- if (validSquare(square) !== true ||
- CURRENT_POSITION.hasOwnProperty(square) !== true) {
+ if (!validSquare(square) ||
+ !CURRENT_POSITION.hasOwnProperty(square)) {
return;
}
- e = e.originalEvent;
beginDraggingPiece(square, CURRENT_POSITION[square],
e.changedTouches[0].pageX, e.changedTouches[0].pageY);
}
-function mousedownSparePiece(e) {
- if (!e.target.matches('.' + CSS.sparePieces + ' .' + CSS.piece)) {
- return;
- }
-
- // do nothing if sparePieces is not enabled
- if (cfg.sparePieces !== true) return;
-
- var piece = e.target.getAttribute('data-piece');
-
- beginDraggingPiece('spare', piece, e.pageX, e.pageY);
-}
-
-function touchstartSparePiece(e) {
- if (!e.target.matches('.' + CSS.sparePieces + ' .' + CSS.piece)) {
- return;
- }
-
- // do nothing if sparePieces is not enabled
- if (cfg.sparePieces !== true) return;
-
- var piece = e.target.getAttribute('data-piece');
-
- e = e.originalEvent;
- beginDraggingPiece('spare', piece,
- e.changedTouches[0].pageX, e.changedTouches[0].pageY);
-}
-
function mousemoveWindow(e) {
// do nothing if we are not dragging a piece
- if (DRAGGING_A_PIECE !== true) return;
+ if (!DRAGGING_A_PIECE) return;
- updateDraggedPiece(e.pageX, e.pageY);
+ updateDraggedPiece(findSquareFromEvent(e.pageX, e.pageY));
}
function touchmoveWindow(e) {
// do nothing if we are not dragging a piece
- if (DRAGGING_A_PIECE !== true) return;
+ if (!DRAGGING_A_PIECE) return;
// prevent screen from scrolling
e.preventDefault();
- updateDraggedPiece(e.originalEvent.changedTouches[0].pageX,
- e.originalEvent.changedTouches[0].pageY);
+ updateDraggedPiece(findSquareFromEvent(e.changedTouches[0].pageX,
+ e.changedTouches[0].pageY));
}
function mouseupWindow(e) {
// do nothing if we are not dragging a piece
- if (DRAGGING_A_PIECE !== true) return;
+ if (!DRAGGING_A_PIECE) return;
- // get the location
- var location = isXYOnSquare(e.pageX, e.pageY);
-
- stopDraggedPiece(location);
+ stopDraggedPiece(findSquareFromEvent(e.pageX, e.pageY));
}
function touchendWindow(e) {
// do nothing if we are not dragging a piece
- if (DRAGGING_A_PIECE !== true) return;
-
- // get the location
- var location = isXYOnSquare(e.originalEvent.changedTouches[0].pageX,
- e.originalEvent.changedTouches[0].pageY);
+ if (!DRAGGING_A_PIECE) return;
- stopDraggedPiece(location);
+ stopDraggedPiece(findSquareFromEvent(e.changedTouches[0].pageX,
+ e.changedTouches[0].pageY));
}
function mouseenterSquare(e) {
@@ -1645,18 +1316,18 @@ function mouseenterSquare(e) {
// NOTE: this should never happen, but it's a safeguard
if (DRAGGING_A_PIECE !== false) return;
- if (cfg.hasOwnProperty('onMouseoverSquare') !== true ||
+ if (!cfg.hasOwnProperty('onMouseoverSquare') ||
typeof cfg.onMouseoverSquare !== 'function') return;
// get the square
var square = target.getAttribute('data-square');
// NOTE: this should never happen; defensive
- if (validSquare(square) !== true) return;
+ if (!validSquare(square)) return;
// get the piece on this square
var piece = false;
- if (CURRENT_POSITION.hasOwnProperty(square) === true) {
+ if (CURRENT_POSITION.hasOwnProperty(square)) {
piece = CURRENT_POSITION[square];
}
@@ -1675,18 +1346,18 @@ function mouseleaveSquare(e) {
// NOTE: this should never happen, but it's a safeguard
if (DRAGGING_A_PIECE !== false) return;
- if (cfg.hasOwnProperty('onMouseoutSquare') !== true ||
+ if (!cfg.hasOwnProperty('onMouseoutSquare') ||
typeof cfg.onMouseoutSquare !== 'function') return;
// get the square
var square = target.getAttribute('data-square');
// NOTE: this should never happen; defensive
- if (validSquare(square) !== true) return;
+ if (!validSquare(square)) return;
// get the piece on this square
var piece = false;
- if (CURRENT_POSITION.hasOwnProperty(square) === true) {
+ if (CURRENT_POSITION.hasOwnProperty(square)) {
piece = CURRENT_POSITION[square];
}
@@ -1711,7 +1382,6 @@ function addEvents() {
// mouse drag pieces
boardEl.addEventListener('mousedown', mousedownSquare);
- containerEl.addEventListener('mousedown', mousedownSparePiece);
// mouse enter / leave square
boardEl.addEventListener('mouseenter', mouseenterSquare);
@@ -1721,9 +1391,8 @@ function addEvents() {
window.addEventListener('mouseup', mouseupWindow);
// touch drag pieces
- if (isTouchDevice() === true) {
+ if (isTouchDevice()) {
boardEl.addEventListener('touchstart', touchstartSquare);
- containerEl.addEventListener('touchstart', touchstartSparePiece);
window.addEventListener('touchmove', touchmoveWindow);
window.addEventListener('touchend', touchendWindow);
}
@@ -1734,29 +1403,12 @@ function initDom() {
containerEl.innerHTML = buildBoardContainer();
boardEl = containerEl.querySelector('.' + CSS.board);
- if (cfg.sparePieces === true) {
- sparePiecesTopEl = containerEl.querySelector('.' + CSS.sparePiecesTop);
- sparePiecesBottomEl = containerEl.querySelector('.' + CSS.sparePiecesBottom);
- }
-
- // create the drag piece
- var draggedPieceId = createId();
- document.body.append(buildPiece('wP', true, draggedPieceId));
- draggedPieceEl = document.getElementById(draggedPieceId);
-
- // get the border size
- BOARD_BORDER_SIZE = parseInt(boardEl.style.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();
+ if (!checkDeps() || !expandConfig()) return;
initDom();
addEvents();