X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=www%2Fjs%2Fchessboard-0.3.0.js;h=dc0e427b7fae69679c032147280fcdff7876fa17;hb=97a23fa0e0d7b63a51b6ea777ea30c5b2c39d699;hp=3e335b12bd3232e239b0aebca6dc7db410298977;hpb=4c5d301bac0c1cb70229c58a3c45bcafb306e95e;p=remoteglot diff --git a/www/js/chessboard-0.3.0.js b/www/js/chessboard-0.3.0.js index 3e335b1..dc0e427 100644 --- a/www/js/chessboard-0.3.0.js +++ b/www/js/chessboard-0.3.0.js @@ -1,7 +1,8 @@ /*! - * 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 * @@ -185,7 +186,11 @@ function objToFen(obj) { return fen; } -window.ChessBoard = window.ChessBoard || function(containerElOrId, cfg) { +/** @struct */ +var cfg; + +/** @constructor */ +window.ChessBoard = function(containerElOrId, cfg) { 'use strict'; cfg = cfg || {}; @@ -194,8 +199,7 @@ 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 @@ -211,9 +215,6 @@ var CSS = { 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 = {}; @@ -227,9 +228,7 @@ CSSColor['black'] = 'black-3c85d'; // DOM elements var containerEl, boardEl, - draggedPieceEl, - sparePiecesTopEl, - sparePiecesBottomEl; + draggedPieceEl; // constructor return object var widget = {}; @@ -238,8 +237,7 @@ var widget = {}; // Stateful //------------------------------------------------------------------------------ -var ANIMATION_HAPPENING = false, - BOARD_BORDER_SIZE = 2, +var BOARD_BORDER_SIZE = 2, CURRENT_ORIENTATION = 'white', CURRENT_POSITION = {}, SQUARE_SIZE, @@ -247,7 +245,6 @@ var ANIMATION_HAPPENING = false, DRAGGED_PIECE_LOCATION, DRAGGED_PIECE_SOURCE, DRAGGING_A_PIECE = false, - SPARE_PIECE_ELS_IDS = {}, SQUARE_ELS_IDS = {}, SQUARE_ELS_OFFSETS; @@ -255,44 +252,24 @@ var ANIMATION_HAPPENING = false, // 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 || @@ -350,39 +327,12 @@ function checkDeps() { } // 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; @@ -424,21 +374,6 @@ function expandConfig() { 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' && @@ -500,7 +435,7 @@ function expandConfig() { // 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) { @@ -526,15 +461,6 @@ function createElIds() { 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(); - } } //------------------------------------------------------------------------------ @@ -544,18 +470,8 @@ function createElIds() { function buildBoardContainer() { var html = '
'; - if (cfg.sparePieces === true) { - html += '
'; - } - html += '
'; - if (cfg.sparePieces === true) { - html += '
'; - } - html += '
'; return html; @@ -652,138 +568,137 @@ function buildPieceImgSrc(piece) { return ''; } +/** + * @param {!string} piece + * @param {boolean=} hidden + * @param {!string=} id + */ function buildPiece(piece, hidden, id) { - var 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 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)); +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 + }; +} - // remove the animated piece - animatedPieceEl.remove(); +function animateSquareToSquare(moved_pieces, completeFn) { + let pieces = []; + for (const {source, destination, piece} of moved_pieces) { + // get information about the source and destination squares + let srcSquareEl = document.getElementById(SQUARE_ELS_IDS[source]); + let srcSquarePosition = offset(srcSquareEl); + let destSquareEl = document.getElementById(SQUARE_ELS_IDS[destination]); + let destSquarePosition = offset(destSquareEl); + + // create the animated piece and absolutely position it + // over the source square + let animatedPieceId = createId(); + document.body.append(buildPiece(piece, true, animatedPieceId)); + let 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 + let 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(); + } + }; - // run complete function - if (typeof completeFn === 'function') { - completeFn(); + // animate the piece to the destination square + animatedPieceEl.addEventListener('transitionend', complete, {once: true}); + pieces.push([animatedPieceEl, destSquarePosition]); + } + requestAnimationFrame(() => { + for (let [animatedPieceEl, destSquarePosition] of pieces) { + animatedPieceEl.style.transitionProperty = 'top, left'; + animatedPieceEl.style.transitionDuration = cfg.moveSpeed + 'ms'; + animatedPieceEl.style.top = destSquarePosition.top + 'px'; + animatedPieceEl.style.left = destSquarePosition.left + 'px'; } - }; - - // animate the piece to the destination square - var opts = { - duration: cfg.moveSpeed, - complete: complete - }; - animatedPieceEl.animate(destSquarePosition, opts); + }); } -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 && @@ -792,32 +707,45 @@ function doAnimations(a, oldPos, 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); - } + requestAnimationFrame(() => { // Firefox workaround. + let fadeout_pieces = []; + let fadein_pieces = []; + let moved_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') { + moved_pieces.push(a[i]); + } } - // 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); + } + if (moved_pieces.length > 0) { + animateSquareToSquare(moved_pieces, onFinish); + } + }); } // returns the distance between two squares @@ -837,55 +765,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 @@ -966,30 +865,19 @@ function calculateAnimations(pos1, pos2) { 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 @@ -1051,28 +939,24 @@ function captureSquareOffsets() { 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 && @@ -1084,32 +968,16 @@ function snapbackDraggedPiece() { // 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; @@ -1125,26 +993,30 @@ function dropDraggedPieceOnSquare(square) { 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; @@ -1163,40 +1035,28 @@ function beginDraggingPiece(source, piece, x, y) { 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); @@ -1206,13 +1066,13 @@ function updateDraggedPiece(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 @@ -1232,31 +1092,12 @@ function stopDraggedPiece(location) { 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 - 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) { @@ -1269,7 +1110,7 @@ function stopDraggedPiece(location) { var result = cfg.onDrop(DRAGGED_PIECE_SOURCE, location, DRAGGED_PIECE, newPosition, oldPosition, CURRENT_ORIENTATION); - if (result === 'snapback' || result === 'trash') { + if (result === 'snapback') { action = result; } } @@ -1278,9 +1119,6 @@ function stopDraggedPiece(location) { if (action === 'snapback') { snapbackDraggedPiece(); } - else if (action === 'trash') { - trashDraggedPiece(); - } else if (action === 'drop') { dropDraggedPieceOnSquare(location); } @@ -1309,11 +1147,8 @@ widget.config = function(arg1, arg2) { // 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 @@ -1392,6 +1227,10 @@ widget.orientation = function(arg) { 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) { @@ -1444,18 +1283,12 @@ widget.resize = function() { 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 @@ -1475,21 +1308,16 @@ function isTouchDevice() { 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 || @@ -1501,10 +1329,15 @@ function mousedownSquare(e) { } 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 || @@ -1512,31 +1345,10 @@ function touchstartSquare(e) { 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; @@ -1551,8 +1363,8 @@ function touchmoveWindow(e) { // 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) { @@ -1570,13 +1382,18 @@ function touchendWindow(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; @@ -1585,7 +1402,7 @@ function mouseenterSquare(e) { 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; @@ -1602,6 +1419,11 @@ function mouseenterSquare(e) { } 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; @@ -1610,7 +1432,7 @@ function mouseleaveSquare(e) { 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; @@ -1632,58 +1454,44 @@ function mouseleaveSquare(e) { 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();