/*!
- * 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
*
return fen;
}
-window.ChessBoard = window.ChessBoard || function(containerElOrId, cfg) {
+/** @struct */
+var cfg;
+
+/** @constructor */
+window.ChessBoard = function(containerElOrId, cfg) {
'use strict';
cfg = cfg || {};
// Constants
//------------------------------------------------------------------------------
-var MINIMUM_JQUERY_VERSION = '1.7.0',
- START_FEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',
+var START_FEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR',
START_POSITION = fenToObj(START_FEN);
// use unique class names to prevent clashing with anything else on the page
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'
};
var CSSColor = {};
// DOM elements
var containerEl,
boardEl,
- draggedPieceEl,
- sparePiecesTopEl,
- sparePiecesBottomEl;
+ draggedPieceEl;
// constructor return object
var widget = {};
// Stateful
//------------------------------------------------------------------------------
-var ANIMATION_HAPPENING = false,
- BOARD_BORDER_SIZE = 2,
+var BOARD_BORDER_SIZE = 2,
CURRENT_ORIENTATION = 'white',
CURRENT_POSITION = {},
SQUARE_SIZE,
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
+let id_counter = 0;
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);
- });
+ return 'chesspiece-id-' + (id_counter++);
}
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
//------------------------------------------------------------------------------
+/**
+ * @param {!number} code
+ * @param {!string} msg
+ * @param {Object=} obj
+ */
function error(code, msg, obj) {
// do nothing if showErrors is not set
if (cfg.hasOwnProperty('showErrors') !== true ||
}
// set the containerEl
- containerEl = $(el);
+ containerEl = el;
}
- // else it must be something that becomes a jQuery collection
- // with size 1
- // ie: a single DOM node or jQuery object
+ // else it must be a DOM node
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;
+ containerEl = containerElOrId;
}
return 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' &&
// 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);
+ var containerWidth = parseInt(getComputedStyle(containerEl).width, 10);
// defensive, prevent infinite loop
if (! containerWidth || containerWidth <= 0) {
SQUARE_ELS_IDS[square] = square + '-' + createId();
}
}
-
- // spare pieces
- var pieces = 'KQRBNP'.split('');
- for (var i = 0; i < pieces.length; i++) {
- var whitePiece = 'w' + pieces[i];
- var blackPiece = 'b' + pieces[i];
- SPARE_PIECE_ELS_IDS[whitePiece] = whitePiece + '-' + createId();
- SPARE_PIECE_ELS_IDS[blackPiece] = blackPiece + '-' + createId();
- }
}
//------------------------------------------------------------------------------
function buildBoardContainer() {
var html = '<div class="' + CSS.chessboard + '">';
- if (cfg.sparePieces === true) {
- html += '<div class="' + CSS.sparePieces + ' ' +
- CSS.sparePiecesTop + '"></div>';
- }
-
html += '<div class="' + CSS.board + '"></div>';
- if (cfg.sparePieces === true) {
- html += '<div class="' + CSS.sparePieces + ' ' +
- CSS.sparePiecesBottom + '"></div>';
- }
-
html += '</div>';
return html;
return '';
}
+/**
+ * @param {!string} piece
+ * @param {boolean=} hidden
+ * @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 += '" />';
-
- return html;
-}
-
-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;
}
//------------------------------------------------------------------------------
// Animations
//------------------------------------------------------------------------------
+function offset(el) { // From https://youmightnotneedjquery.com/.
+ let box = el.getBoundingClientRect();
+ let docElem = document.documentElement;
+ return {
+ top: box.top + window.pageYOffset - docElem.clientTop,
+ left: box.left + window.pageXOffset - docElem.clientLeft
+ };
+}
+
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();
+ 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();
- $('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();
+ 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() {
};
// animate the piece to the destination square
- var opts = {
- duration: cfg.moveSpeed,
- complete: complete
- };
- animatedPieceEl.animate(destSquarePosition, opts);
+ 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 = $('#' + SPARE_PIECE_ELS_IDS[piece]).offset();
- var destSquareEl = $('#' + SQUARE_ELS_IDS[dest]);
- var destOffset = destSquareEl.offset();
-
- // create the animate piece
- var pieceId = createId();
- $('body').append(buildPiece(piece, true, pieceId));
- var animatedPieceEl = $('#' + pieceId);
- animatedPieceEl.css({
- display: '',
- position: 'absolute',
- left: srcOffset.left,
- top: srcOffset.top
+function fadeIn(pieces, onFinish) {
+ pieces.forEach((piece) => {
+ piece.style.opacity = 0;
+ piece.style.display = null;
+ piece.addEventListener('transitionend', onFinish, {once: true});
});
+ requestAnimationFrame(() => {
+ pieces.forEach((piece) => {
+ piece.style.transitionProperty = 'opacity';
+ piece.style.transitionDuration = cfg.appearSpeed + 'ms';
+ piece.style.opacity = 1;
+ });
+ });
+}
- // on complete
- var complete = function() {
- // add the "real" piece to the destination square
- destSquareEl.find('.' + CSS.piece).remove();
- 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(destOffset, opts);
+function fadeOut(pieces, onFinish) {
+ pieces.forEach((piece) => {
+ piece.style.opacity = 1;
+ piece.style.display = null;
+ piece.addEventListener('transitionend', onFinish, {once: true});
+ });
+ requestAnimationFrame(() => {
+ pieces.forEach((piece) => {
+ piece.style.transitionProperty = 'opacity';
+ piece.style.transitionDuration = cfg.trashSpeed + 'ms';
+ piece.style.opacity = 0;
+ });
+ });
}
// execute an array of animations
function doAnimations(a, oldPos, newPos) {
- ANIMATION_HAPPENING = true;
-
var numFinished = 0;
- function onFinish() {
+ function onFinish(e) {
+ if (e && e.target) {
+ e.target.transitionProperty = null;
+ }
+
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 &&
}
}
- 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);
- }
+ requestAnimationFrame(() => { // Firefox workaround.
+ let fadeout_pieces = [];
+ let fadein_pieces = [];
- // 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);
- }
+ 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
+ if (a[i].type === 'add') {
+ 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
function drawPositionInstant() {
// clear the board
- boardEl.find('.' + CSS.piece).remove();
+ boardEl.querySelectorAll('.' + CSS.piece).forEach((piece) => 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]));
+ document.getElementById(SQUARE_ELS_IDS[i]).append(buildPiece(CURRENT_POSITION[i]));
}
}
function drawBoard() {
- boardEl.html(buildBoard(CURRENT_ORIENTATION));
+ boardEl.innerHTML = buildBoard(CURRENT_ORIENTATION);
drawPositionInstant();
-
- if (cfg.sparePieces === true) {
- if (CURRENT_ORIENTATION === 'white') {
- sparePiecesTopEl.html(buildSparePieces('black'));
- sparePiecesBottomEl.html(buildSparePieces('white'));
- }
- else {
- sparePiecesTopEl.html(buildSparePieces('white'));
- sparePiecesBottomEl.html(buildSparePieces('black'));
- }
- }
}
// given a position and a set of moves, return a new position
for (var i in SQUARE_ELS_IDS) {
if (SQUARE_ELS_IDS.hasOwnProperty(i) !== true) continue;
- SQUARE_ELS_OFFSETS[i] = $('#' + SQUARE_ELS_IDS[i]).offset();
+ SQUARE_ELS_OFFSETS[i] = offset(document.getElementById(SQUARE_ELS_IDS[i]));
}
}
function removeSquareHighlights() {
- boardEl.find('.' + CSS.square)
- .removeClass(CSS.highlight1 + ' ' + CSS.highlight2);
+ boardEl.querySelectorAll('.' + CSS.square).forEach((piece) => {
+ piece.classList.remove(CSS.highlight1);
+ piece.classList.remove(CSS.highlight2);
+ });
}
function snapbackDraggedPiece() {
- // there is no "snapback" for spare pieces
- if (DRAGGED_PIECE_SOURCE === 'spare') {
- trashDraggedPiece();
- return;
- }
-
removeSquareHighlights();
// animation complete
function complete() {
drawPositionInstant();
- draggedPieceEl.css('display', 'none');
+ draggedPieceEl.style.display = 'none';
// run their onSnapbackEnd function
if (cfg.hasOwnProperty('onSnapbackEnd') === true &&
// get source square position
var sourceSquarePosition =
- $('#' + SQUARE_ELS_IDS[DRAGGED_PIECE_SOURCE]).offset();
+ offset(document.getElementById(SQUARE_ELS_IDS[DRAGGED_PIECE_SOURCE]));
// animate the piece to the target square
- var opts = {
- duration: cfg.snapbackSpeed,
- complete: complete
- };
- draggedPieceEl.animate(sourceSquarePosition, opts);
-
- // set state
- DRAGGING_A_PIECE = false;
-}
-
-function trashDraggedPiece() {
- 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
- draggedPieceEl.fadeOut(cfg.trashSpeed);
+ draggedPieceEl.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';
+ });
// set state
DRAGGING_A_PIECE = false;
setCurrentPosition(newPosition);
// get target square information
- var targetSquarePosition = $('#' + SQUARE_ELS_IDS[square]).offset();
+ var targetSquarePosition = offset(document.getElementById(SQUARE_ELS_IDS[square]));
// animation complete
var complete = function() {
drawPositionInstant();
- draggedPieceEl.css('display', 'none');
+ draggedPieceEl.style.display = 'none';
// execute their onSnapEnd function
if (cfg.hasOwnProperty('onSnapEnd') === true &&
typeof cfg.onSnapEnd === 'function') {
- cfg.onSnapEnd(DRAGGED_PIECE_SOURCE, square, DRAGGED_PIECE);
+ requestAnimationFrame(() => { // HACK: so that we don't add event handlers from the callback...
+ cfg.onSnapEnd(DRAGGED_PIECE_SOURCE, square, DRAGGED_PIECE);
+ });
}
};
// snap the piece to the target square
- var opts = {
- duration: cfg.snapSpeed,
- complete: complete
- };
- draggedPieceEl.animate(targetSquarePosition, opts);
+ draggedPieceEl.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';
+ });
// set state
DRAGGING_A_PIECE = false;
DRAGGING_A_PIECE = true;
DRAGGED_PIECE = piece;
DRAGGED_PIECE_SOURCE = source;
-
- // if the piece came from spare pieces, location is offboard
- if (source === 'spare') {
- DRAGGED_PIECE_LOCATION = 'offboard';
- }
- else {
- DRAGGED_PIECE_LOCATION = source;
- }
+ DRAGGED_PIECE_LOCATION = source;
// capture the x, y coords of all squares in memory
captureSquareOffsets();
// create the dragged piece
- draggedPieceEl.attr('src', buildPieceImgSrc(piece))
- .css({
- display: '',
- position: 'absolute',
- left: x - (SQUARE_SIZE / 2),
- top: y - (SQUARE_SIZE / 2)
- });
-
- if (source !== 'spare') {
- // highlight the source square and hide the piece
- $('#' + SQUARE_ELS_IDS[source]).addClass(CSS.highlight1)
- .find('.' + CSS.piece).css('display', 'none');
- }
+ 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';
+
+ // 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';
}
function updateDraggedPiece(x, y) {
// put the dragged piece over the mouse cursor
- draggedPieceEl.css({
- left: x - (SQUARE_SIZE / 2),
- top: y - (SQUARE_SIZE / 2)
- });
+ draggedPieceEl.style.left = (x - (SQUARE_SIZE / 2)) + 'px';
+ draggedPieceEl.style.top = (y - (SQUARE_SIZE / 2)) + 'px';
// get location
var location = isXYOnSquare(x, y);
// remove highlight from previous square
if (validSquare(DRAGGED_PIECE_LOCATION) === true) {
- $('#' + SQUARE_ELS_IDS[DRAGGED_PIECE_LOCATION])
- .removeClass(CSS.highlight2);
+ document.getElementById(SQUARE_ELS_IDS[DRAGGED_PIECE_LOCATION])
+ .classList.remove(CSS.highlight2);
}
// add highlight to new square
if (validSquare(location) === true) {
- $('#' + SQUARE_ELS_IDS[location]).addClass(CSS.highlight2);
+ document.getElementById(SQUARE_ELS_IDS[location]).classList.add(CSS.highlight2);
}
// run onDragMove
if (location === '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 &&
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
var result = cfg.onDrop(DRAGGED_PIECE_SOURCE, location, DRAGGED_PIECE,
newPosition, oldPosition, CURRENT_ORIENTATION);
- if (result === 'snapback' || result === 'trash') {
+ if (result === 'snapback') {
action = result;
}
}
if (action === 'snapback') {
snapbackDraggedPiece();
}
- else if (action === 'trash') {
- trashDraggedPiece();
- }
else if (action === 'drop') {
dropDraggedPieceOnSquare(location);
}
// remove the widget from the page
widget.destroy = function() {
// remove markup
- containerEl.html('');
+ containerEl.innerHTML = '';
draggedPieceEl.remove();
-
- // remove event handlers
- containerEl.unbind();
};
// shorthand method to get the current FEN
error(5482, 'Invalid value passed to the orientation method.', arg);
};
+/**
+ * @param {!string|!Object} position
+ * @param {boolean=} useAnimation
+ */
widget.position = function(position, useAnimation) {
// no arguments, return the current position
if (arguments.length === 0) {
SQUARE_SIZE = calculateSquareSize();
// set board width
- boardEl.css('width', (SQUARE_SIZE * 8) + 'px');
+ boardEl.style.width = (SQUARE_SIZE * 8) + 'px';
// set drag piece size
- draggedPieceEl.css({
- height: SQUARE_SIZE,
- width: SQUARE_SIZE
- });
-
- // spare pieces
- if (cfg.sparePieces === true) {
- containerEl.find('.' + CSS.sparePieces)
- .css('paddingLeft', (SQUARE_SIZE + BOARD_BORDER_SIZE) + 'px');
+ if (draggedPieceEl !== null) {
+ draggedPieceEl.style.height = SQUARE_SIZE + 'px';
+ draggedPieceEl.style.width = SQUARE_SIZE + 'px';
}
// redraw the board
return ('ontouchstart' in document.documentElement);
}
-// reference: http://www.quirksmode.org/js/detect.html
-function isMSIE() {
- return (navigator && navigator.userAgent &&
- navigator.userAgent.search(/MSIE/) !== -1);
-}
-
-function stopDefault(e) {
- e.preventDefault();
-}
-
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 = $(this).attr('data-square');
+ var square = target.getAttribute('data-square');
// no piece on this square
if (validSquare(square) !== true ||
}
function touchstartSquare(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 = $(this).attr('data-square');
+ var square = target.getAttribute('data-square');
// no piece on this square
if (validSquare(square) !== true ||
return;
}
- e = e.originalEvent;
beginDraggingPiece(square, CURRENT_POSITION[square],
e.changedTouches[0].pageX, e.changedTouches[0].pageY);
}
-function mousedownSparePiece(e) {
- // do nothing if sparePieces is not enabled
- if (cfg.sparePieces !== true) return;
-
- var piece = $(this).attr('data-piece');
-
- beginDraggingPiece('spare', piece, e.pageX, e.pageY);
-}
-
-function touchstartSparePiece(e) {
- // do nothing if sparePieces is not enabled
- if (cfg.sparePieces !== true) return;
-
- var piece = $(this).attr('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;
// 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);
}
function mouseenterSquare(e) {
+ let target = e.target.closest('.' + CSS.square);
+ if (!target) {
+ return;
+ }
+
// do not fire this event if we are dragging a piece
// NOTE: this should never happen, but it's a safeguard
if (DRAGGING_A_PIECE !== false) return;
typeof cfg.onMouseoverSquare !== 'function') return;
// get the square
- var square = $(e.currentTarget).attr('data-square');
+ var square = target.getAttribute('data-square');
// NOTE: this should never happen; defensive
if (validSquare(square) !== true) return;
}
function mouseleaveSquare(e) {
+ let target = e.target.closest('.' + CSS.square);
+ if (!target) {
+ return;
+ }
+
// do not fire this event if we are dragging a piece
// NOTE: this should never happen, but it's a safeguard
if (DRAGGING_A_PIECE !== false) return;
typeof cfg.onMouseoutSquare !== 'function') return;
// get the square
- var square = $(e.currentTarget).attr('data-square');
+ var square = target.getAttribute('data-square');
// NOTE: this should never happen; defensive
if (validSquare(square) !== true) return;
function addEvents() {
// prevent browser "image drag"
- $('body').on('mousedown mousemove', '.' + CSS.piece, stopDefault);
+ let stopDefault = (e) => {
+ if (e.target.matches('.' + CSS.piece)) {
+ e.preventDefault();
+ }
+ };
+ document.body.addEventListener('mousedown', stopDefault);
+ document.body.addEventListener('mousemove', stopDefault);
// mouse drag pieces
- boardEl.on('mousedown', '.' + CSS.square, mousedownSquare);
- containerEl.on('mousedown', '.' + CSS.sparePieces + ' .' + CSS.piece,
- mousedownSparePiece);
+ boardEl.addEventListener('mousedown', mousedownSquare);
// mouse enter / leave square
- boardEl.on('mouseenter', '.' + CSS.square, mouseenterSquare);
- boardEl.on('mouseleave', '.' + CSS.square, mouseleaveSquare);
+ boardEl.addEventListener('mouseenter', mouseenterSquare);
+ boardEl.addEventListener('mouseleave', mouseleaveSquare);
- // IE doesn't like the events on the window object, but other browsers
- // perform better that way
- if (isMSIE() === true) {
- // IE-specific prevent browser "image drag"
- document.ondragstart = function() { return false; };
-
- $('body').on('mousemove', mousemoveWindow);
- $('body').on('mouseup', mouseupWindow);
- }
- else {
- $(window).on('mousemove', mousemoveWindow);
- $(window).on('mouseup', mouseupWindow);
- }
+ window.addEventListener('mousemove', mousemoveWindow);
+ window.addEventListener('mouseup', mouseupWindow);
// touch drag pieces
if (isTouchDevice() === true) {
- boardEl.on('touchstart', '.' + CSS.square, touchstartSquare);
- containerEl.on('touchstart', '.' + CSS.sparePieces + ' .' + CSS.piece,
- touchstartSparePiece);
- $(window).on('touchmove', touchmoveWindow);
- $(window).on('touchend', touchendWindow);
+ boardEl.addEventListener('touchstart', touchstartSquare);
+ window.addEventListener('touchmove', touchmoveWindow);
+ window.addEventListener('touchend', touchendWindow);
}
}
function initDom() {
// build board and save it in memory
- containerEl.html(buildBoardContainer());
- boardEl = containerEl.find('.' + CSS.board);
-
- if (cfg.sparePieces === true) {
- sparePiecesTopEl = containerEl.find('.' + CSS.sparePiecesTop);
- sparePiecesBottomEl = containerEl.find('.' + CSS.sparePiecesBottom);
- }
+ containerEl.innerHTML = buildBoardContainer();
+ boardEl = containerEl.querySelector('.' + CSS.board);
// create the drag piece
var draggedPieceId = createId();
- $('body').append(buildPiece('wP', true, draggedPieceId));
- draggedPieceEl = $('#' + draggedPieceId);
+ document.body.append(buildPiece('wP', true, draggedPieceId));
+ draggedPieceEl = document.getElementById(draggedPieceId);
// get the border size
- BOARD_BORDER_SIZE = parseInt(boardEl.css('borderLeftWidth'), 10);
+ BOARD_BORDER_SIZE = parseInt(boardEl.style.borderLeftWidth, 10);
// set the size and draw the board
widget.resize();