diff --git a/lib/chessboard.js b/lib/chessboard.js index 95125fb..f821375 100644 --- a/lib/chessboard.js +++ b/lib/chessboard.js @@ -71,17 +71,6 @@ function throttle(f, interval) { }; } -// function debounce (f, interval, scope) { -// const timeout = 0 -// return function (_args) { -// window.clearTimeout(timeout) -// const args = arguments -// timeout = window.setTimeout(function () { -// f.apply(scope, args) -// }, interval) -// } -// } - function uuid() { return 'xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx'.replace(/x/g, function () { const r = (Math.random() * 16) | 0; @@ -123,38 +112,24 @@ function interpolateTemplate(str, obj) { // Predicates // --------------------------------------------------------------------------- -function isString(s) { - return typeof s === 'string'; -} - -function isFunction(f) { - return typeof f === 'function'; -} - -function isInteger(n) { - return typeof n === 'number' && - isFinite(n) && - Math.floor(n) === n; -} - function isPlainObject(o) { return Object.prototype.toString.call(o) === '[object Object]'; } function validAnimationSpeed(speed) { if (speed === 'fast' || speed === 'slow') return true; - if (!isInteger(speed)) return false; + if (!Number.isInteger(speed)) return false; return speed >= 0; } function validThrottleRate(rate) { - return isInteger(rate) && + return Number.isInteger(rate) && rate >= 1; } function validMove(move) { // move should be a string - if (!isString(move)) return false; + if (typeof move !== 'string') return false; // move should be in the form of "e2-e4", "f6-d5" const squares = move.split('-'); @@ -164,15 +139,20 @@ function validMove(move) { } function validSquare(square) { - return isString(square) && square.search(/^[a-h][1-8]$/) !== -1; + return typeof square === 'string' && square.search(/^[a-h][1-8]$/) !== -1; } function validPieceCode(code) { - return isString(code) && code.search(/^[bw][KQRNBP]$/) !== -1; + return typeof code === 'string' && code.search(/^[bw][KQRNBP]$/) !== -1; } +/** + * + * @param {string} [fen] + * @returns {boolean} + */ function validFen(fen) { - if (!isString(fen)) return false; + if (typeof fen !== 'string') return false; // cut off any move, castling, etc info from the end // we're only interested in position information @@ -242,17 +222,20 @@ function pieceCodeToFen(piece) { return pieceCodeLetters[1].toLowerCase(); } -// convert FEN string to position object -// returns false if the FEN string is invalid +/** + * Convert FEN string to position object. Returns `false` if the FEN string is invalid + * @param {string} [fen] + * @returns {false | Record} + */ function fenToObj(fen) { - if (!validFen(fen)) return false; + if (!fen || !validFen(fen)) return false; // cut off any move, castling, etc info from the end // we're only interested in position information fen = fen.replace(/ .+$/, ''); const rows = fen.split('/'); - const position = {}; + const position = /** @type {Record} */({}); let currentRow = 8; for (let i = 0; i < 8; i++) { @@ -279,8 +262,13 @@ function fenToObj(fen) { return position; } -// position object to FEN string -// returns false if the obj is not a valid position object +// +// +/** + * Converts position object to FEN string. Returns false if the obj is not a valid position object + * @param {object} obj + * @returns {false | string} + */ function objToFen(obj) { if (!validPositionObject(obj)) return false; @@ -313,6 +301,10 @@ function objToFen(obj) { return fen; } +/** + * @param {string} fen + * @returns {string} + */ function squeezeFenEmptySquares(fen) { return fen.replace(/11111111/g, '8') .replace(/1111111/g, '7') @@ -323,6 +315,10 @@ function squeezeFenEmptySquares(fen) { .replace(/11/g, '2'); } +/** + * @param {string} fen + * @returns {string} + */ function expandFenEmptySquares(fen) { return fen.replace(/8/g, '11111111') .replace(/7/g, '1111111') @@ -483,7 +479,7 @@ function expandConfig(config) { // default piece theme is wikipedia if (!Object.prototype.hasOwnProperty.call(config, 'pieceTheme') || - (!isString(config.pieceTheme) && !isFunction(config.pieceTheme))) { + (typeof config.pieceTheme !== 'string' && typeof config.pieceTheme !== 'function')) { config.pieceTheme = 'img/chesspieces/wikipedia/{piece}.png'; } @@ -516,14 +512,13 @@ function checkContainerArg(containerElOrString) { } // convert containerEl to query selector if it is a string - if (isString(containerElOrString) && - containerElOrString.charAt(0) !== '#') { + if (typeof containerElOrString === 'string' && containerElOrString.startsWith('#') === false) { containerElOrString = '#' + containerElOrString; } // containerEl must be something that becomes a NodeList of size 1 - const $container = /** @type {NodeListOf} */(document.querySelectorAll(containerElOrString)); - if ($container.length !== 1) { + const container = /** @type {NodeListOf} */(document.querySelectorAll(containerElOrString)); + if (container.length !== 1) { const errorMsg2 = 'Chessboard Error 1003: ' + 'The first argument to Chessboard() must be the ID of a DOM node, ' + 'an ID query selector, or a single DOM node.' + @@ -533,7 +528,7 @@ function checkContainerArg(containerElOrString) { return false; } - return $container.item(0); + return container.item(0); } // --------------------------------------------------------------------------- @@ -586,6 +581,9 @@ class Chessboard { #sparePiecesElsIds = {}; #squareElsIds = {}; #squareElsOffsets = {}; + /** + * @type {number} + */ #squareSize = 16; constructor(containerElOrString, config) { @@ -734,14 +732,21 @@ class Chessboard { position(position, useAnimation) { if (!position) { return deepCopy(this.#currentPosition); - } else if (isString(position) && position.toLowerCase() === 'fen') { - // get position as FEN - return objToFen(this.#currentPosition); } - if (isString(position) && position.toLowerCase() === 'start') { - // start position - position = deepCopy(START_POSITION); + const positionIsString = typeof position === 'string'; + + if (positionIsString) { + const positionLowerCase = position.toLowerCase(); + + if (positionLowerCase === 'fen') { + // get position as FEN + return objToFen(this.#currentPosition); + } + if (positionLowerCase === 'start') { + // start position + position = deepCopy(START_POSITION); + } } // convert FEN to position object @@ -831,7 +836,7 @@ class Chessboard { this.#draggedPiece.style.setProperty('display', 'none'); // execute their onSnapEnd function - if (isFunction(this.#config.onSnapEnd)) { + if (typeof this.#config.onSnapEnd === 'function') { this.#config.onSnapEnd(this.#draggedPieceSource, square, this.#draggedPiece); } } @@ -876,7 +881,7 @@ class Chessboard { } // custom function - if (isFunction(this.#config.showErrors)) { + if (typeof this.#config.showErrors === 'function') { this.#config.showErrors(code, msg, obj); } } @@ -961,11 +966,11 @@ class Chessboard { } #buildPieceImgSrc(piece) { - if (isFunction(this.#config.pieceTheme)) { + if (typeof this.#config.pieceTheme === 'function') { return this.#config.pieceTheme(piece); } - if (isString(this.#config.pieceTheme)) { + if (typeof this.#config.pieceTheme === 'string') { return interpolateTemplate(this.#config.pieceTheme, { piece }); } @@ -982,7 +987,7 @@ class Chessboard { #buildPieceHTML(piece, hidden, id) { let html = ' 0) { html += 'id="' + id + '" '; } html += 'alt="" ' + @@ -1043,7 +1048,7 @@ class Chessboard { $animatedPiece.remove(); // run complete function - if (isFunction(completeFn)) { + if (typeof completeFn === 'function') { completeFn(); } }; @@ -1075,7 +1080,7 @@ class Chessboard { $animatedPiece.remove(); // run complete function - if (isFunction(completeFn)) { + if (typeof completeFn === 'function') { completeFn(); } }; @@ -1094,7 +1099,7 @@ class Chessboard { this.#drawPositionInstant(); // run their onMoveEnd function - if (isFunction(this.#config.onMoveEnd)) { + if (typeof this.#config.onMoveEnd === 'function') { this.#config.onMoveEnd(deepCopy(oldPos), deepCopy(newPos)); } }; @@ -1246,7 +1251,7 @@ class Chessboard { if (oldFen === newFen) return; // run their onChange function - if (isFunction(this.#config.onChange)) { + if (typeof this.#config.onChange === 'function') { this.#config.onChange(oldPos, newPos); } @@ -1314,7 +1319,7 @@ class Chessboard { this.#draggedPiece.style.setProperty('display', 'none'); // run their onSnapbackEnd function - if (isFunction(this.#config.onSnapbackEnd)) { + if (typeof this.#config.onSnapbackEnd === 'function') { this.#config.onSnapbackEnd( this.#draggedPiece, this.#draggedPieceSource, @@ -1351,7 +1356,7 @@ class Chessboard { #beginDraggingPiece(source, piece, x, y) { // run their custom onDragStart function // their custom onDragStart function can cancel drag start - if (isFunction(this.#config.onDragStart) && + if (typeof this.#config.onDragStart === 'function' && this.#config.onDragStart(source, piece, deepCopy(this.#currentPosition), this.#currentOrientation) === false) { return; } @@ -1411,7 +1416,7 @@ class Chessboard { } // run onDragMove - if (isFunction(this.#config.onDragMove)) { + if (typeof this.#config.onDragMove === 'function') { this.#config.onDragMove( location, this.#draggedPieceLocation, @@ -1437,7 +1442,7 @@ class Chessboard { } // run their onDrop function, which can potentially change the drop action - if (isFunction(this.#config.onDrop)) { + if (typeof this.#config.onDrop === 'function') { const newPosition = deepCopy(this.#currentPosition); // source piece is a spare piece and position is off the board @@ -1598,7 +1603,7 @@ class Chessboard { if (this.#isDragging) return; // exit if they did not provide a onMouseoverSquare function - if (!isFunction(this.#config.onMouseoverSquare)) return; + if (typeof this.#config.onMouseoverSquare !== 'function') return; // get the square const square = /** @type {HTMLElement | null} */(evt.currentTarget)?.getAttribute('data-square'); @@ -1622,7 +1627,7 @@ class Chessboard { if (this.#isDragging) return; // exit if they did not provide an onMouseoutSquare function - if (!isFunction(this.#config.onMouseoutSquare)) return; + if (typeof this.#config.onMouseoutSquare !== 'function') return; // get the square const square = document.getElementById(evt.currentTarget)?.getAttribute('data-square');