]> git.sesse.net Git - remoteglot/blobdiff - www/js/chessboard-0.3.0.js
Handle streaming PGNs, like from Lichess (although this might break non-streaming...
[remoteglot] / www / js / chessboard-0.3.0.js
index 5d257a0dc3b222b9facbbc31bb74bffdaeb43d7c..24aa3da618d25cf3a9d27456aa3f09d53e2c825a 100644 (file)
@@ -26,7 +26,7 @@ function validMove(move) {
   var tmp = move.split('-');
   if (tmp.length !== 2) return false;
 
-  return (validSquare(tmp[0]) === true && validSquare(tmp[1]) === true);
+  return validSquare(tmp[0]) && validSquare(tmp[1]);
 }
 
 function validSquare(square) {
@@ -67,9 +67,9 @@ function validPositionObject(pos) {
   if (typeof pos !== 'object') return false;
 
   for (var i in pos) {
-    if (pos.hasOwnProperty(i) !== true) continue;
+    if (!pos.hasOwnProperty(i)) continue;
 
-    if (validSquare(i) !== true || validPieceCode(pos[i]) !== true) {
+    if (!validSquare(i) || !validPieceCode(pos[i])) {
       return false;
     }
   }
@@ -104,7 +104,7 @@ function pieceCodeToFen(piece) {
 // convert FEN string to position object
 // returns false if the FEN string is invalid
 function fenToObj(fen) {
-  if (validFen(fen) !== true) {
+  if (!validFen(fen)) {
     return false;
   }
 
@@ -144,7 +144,7 @@ function fenToObj(fen) {
 // position object to FEN string
 // returns false if the obj is not a valid position object
 function objToFen(obj) {
-  if (validPositionObject(obj) !== true) {
+  if (!validPositionObject(obj)) {
     return false;
   }
 
@@ -157,7 +157,7 @@ function objToFen(obj) {
       var square = COLUMNS[j] + currentRow;
 
       // piece exists
-      if (obj.hasOwnProperty(square) === true) {
+      if (obj.hasOwnProperty(square)) {
         if (num_empty > 0) {
           fen += num_empty;
           num_empty = 0;
@@ -266,7 +266,7 @@ function deepCopy(thing) {
  */
 function error(code, msg, obj) {
   // do nothing if showErrors is not set
-  if (cfg.hasOwnProperty('showErrors') !== true ||
+  if (!cfg.hasOwnProperty('showErrors') ||
       cfg.showErrors === false) {
     return;
   }
@@ -346,7 +346,7 @@ function validAnimationSpeed(speed) {
 
 // validate config / set default options
 function expandConfig() {
-  if (typeof cfg === 'string' || validPositionObject(cfg) === true) {
+  if (typeof cfg === 'string' || validPositionObject(cfg)) {
     cfg = {
       position: cfg
     };
@@ -369,45 +369,45 @@ function expandConfig() {
   }
 
   // default piece theme is wikipedia
-  if (cfg.hasOwnProperty('pieceTheme') !== true ||
+  if (!cfg.hasOwnProperty('pieceTheme') ||
       (typeof cfg.pieceTheme !== 'string' &&
        typeof cfg.pieceTheme !== 'function')) {
     cfg.pieceTheme = 'img/chesspieces/wikipedia/{piece}.png';
   }
 
   // animation speeds
-  if (cfg.hasOwnProperty('appearSpeed') !== true ||
-      validAnimationSpeed(cfg.appearSpeed) !== true) {
+  if (!cfg.hasOwnProperty('appearSpeed') ||
+      !validAnimationSpeed(cfg.appearSpeed)) {
     cfg.appearSpeed = 200;
   }
-  if (cfg.hasOwnProperty('moveSpeed') !== true ||
-      validAnimationSpeed(cfg.moveSpeed) !== true) {
+  if (!cfg.hasOwnProperty('moveSpeed') ||
+      !validAnimationSpeed(cfg.moveSpeed)) {
     cfg.moveSpeed = 200;
   }
-  if (cfg.hasOwnProperty('snapbackSpeed') !== true ||
-      validAnimationSpeed(cfg.snapbackSpeed) !== true) {
+  if (!cfg.hasOwnProperty('snapbackSpeed') ||
+      !validAnimationSpeed(cfg.snapbackSpeed)) {
     cfg.snapbackSpeed = 50;
   }
-  if (cfg.hasOwnProperty('snapSpeed') !== true ||
-      validAnimationSpeed(cfg.snapSpeed) !== true) {
+  if (!cfg.hasOwnProperty('snapSpeed') ||
+      !validAnimationSpeed(cfg.snapSpeed)) {
     cfg.snapSpeed = 25;
   }
-  if (cfg.hasOwnProperty('trashSpeed') !== true ||
-      validAnimationSpeed(cfg.trashSpeed) !== true) {
+  if (!cfg.hasOwnProperty('trashSpeed') ||
+      !validAnimationSpeed(cfg.trashSpeed)) {
     cfg.trashSpeed = 100;
   }
 
   // make sure position is valid
-  if (cfg.hasOwnProperty('position') === true) {
+  if (cfg.hasOwnProperty('position')) {
     if (cfg.position === 'start') {
       CURRENT_POSITION = deepCopy(START_POSITION);
     }
 
-    else if (validFen(cfg.position) === true) {
+    else if (validFen(cfg.position)) {
       CURRENT_POSITION = fenToObj(cfg.position);
     }
 
-    else if (validPositionObject(cfg.position) === true) {
+    else if (validPositionObject(cfg.position)) {
       CURRENT_POSITION = deepCopy(cfg.position);
     }
 
@@ -458,7 +458,7 @@ function buildBoard(orientation) {
         'style="grid-row: ' + (i+1) + '; grid-column: ' + (j+1) + ';" ' +
         'data-square="' + square + '">';
 
-      if (cfg.showNotation === true) {
+      if (cfg.showNotation) {
         // alpha notation
         if ((orientation === 'white' && row === 1) ||
             (orientation === 'black' && row === 8)) {
@@ -550,57 +550,6 @@ function findSquarePosition(square) {
   };
 }
 
-function animateSquareToSquare(move_pieces, complete) {
-  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);
-
-    // animate the piece to the destination square
-    piece.addEventListener('transitionend', complete, {once: true});
-  }
-  requestAnimationFrame(() => {
-    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;
-   }
-  });
-}
-
-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;
-    });
-  });
-}
-
-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) {
   let fadeout_pieces = [];
@@ -658,39 +607,53 @@ function doAnimations(a, oldPos, newPos) {
   }
 
   var numFinished = 0;
-  function onFinish(e) {
-    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;
-
-    for (let piece of removed_pieces) {
-      piece.remove();
-    }
+  function onFinish(e, opt_force) {
+    if (++numFinished === a.length) {
+      for (let piece of removed_pieces) {
+        piece.remove();
+      }
 
-    // run their onMoveEnd function
-    if (cfg.hasOwnProperty('onMoveEnd') === true &&
-      typeof cfg.onMoveEnd === 'function') {
-      cfg.onMoveEnd(deepCopy(oldPos), deepCopy(newPos));
+      // run their onMoveEnd function
+      if (cfg.hasOwnProperty('onMoveEnd') &&
+        typeof cfg.onMoveEnd === 'function') {
+        cfg.onMoveEnd(deepCopy(oldPos), deepCopy(newPos));
+      }
     }
   }
 
-  requestAnimationFrame(() => {  // Firefox workaround.
-    if (fadeout_pieces.length > 0) {
-      fadeOut(fadeout_pieces, onFinish);
-    }
-    if (fadein_pieces.length > 0) {
-      fadeIn(fadein_pieces, onFinish);
-    }
-    if (move_pieces.length > 0) {
-      animateSquareToSquare(move_pieces, onFinish);
-    }
+  fadein_pieces.forEach((piece) => {
+    piece.style.display = null;
+    piece.style.opacity = 1;
+    piece.animate(
+      [ { opacity: 0 }, { opacity: 1 } ],
+      { duration: cfg.appearSpeed }
+    ).addEventListener('finish', onFinish);
+  });
+  fadeout_pieces.forEach((piece) => {
+    piece.style.display = null;
+    piece.style.opacity = 0;
+    piece.animate(
+      [ { opacity: 1 }, { opacity: 0 } ],
+      { duration: cfg.trashSpeed }
+    ).addEventListener('finish', onFinish);
   });
+  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);
+
+    let destSquarePosition = findSquarePosition(destination);
+    piece.animate(
+      [
+        { top: piece.style.top, left: piece.style.left },
+        { top: destSquarePosition.top, left: destSquarePosition.left }
+      ],
+      { duration: cfg.moveSpeed }
+    ).addEventListener('finish', onFinish);
+    piece.style.top = destSquarePosition.top;
+    piece.style.left = destSquarePosition.left;
+  }
 }
 
 // returns the distance between two squares
@@ -744,9 +707,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];
     }
@@ -754,7 +717,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) {
@@ -773,7 +736,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',
@@ -786,11 +749,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',
@@ -835,10 +798,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];
@@ -858,7 +821,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);
   }
@@ -882,7 +845,7 @@ function snapbackDraggedPiece() {
     drawPositionInstant();
 
     // 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);
@@ -893,13 +856,15 @@ function snapbackDraggedPiece() {
   var sourceSquarePosition = findSquarePosition(DRAGGED_PIECE_SOURCE);
 
   // animate the piece to the target square
-  DRAGGED_PIECE.addEventListener('transitionend', complete, {once: true});
-  requestAnimationFrame(() => {
-    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;
-  });
+  DRAGGED_PIECE.animate(
+    [
+      { top: DRAGGED_PIECE.style.top, left: DRAGGED_PIECE.style.left },
+      { top: sourceSquarePosition.top, left: sourceSquarePosition.left }
+    ],
+    { duration: cfg.snapbackSpeed }
+  ).addEventListener('finish', complete);
+  DRAGGED_PIECE.style.top = sourceSquarePosition.top;
+  DRAGGED_PIECE.style.left = sourceSquarePosition.left;
 
   // set state
   DRAGGING_A_PIECE = false;
@@ -911,7 +876,7 @@ function dropDraggedPieceOnSquare(square) {
 
   if (DRAGGED_PIECE_SOURCE === square) {
     // Nothing to do, but call onSnapEnd anyway
-    if (cfg.hasOwnProperty('onSnapEnd') === true && typeof cfg.onSnapEnd === 'function') {
+    if (cfg.hasOwnProperty('onSnapEnd') && typeof cfg.onSnapEnd === 'function') {
       cfg.onSnapEnd(DRAGGED_PIECE_SOURCE, square, DRAGGED_PIECE);
     }
     return;
@@ -935,7 +900,7 @@ function dropDraggedPieceOnSquare(square) {
     drawPositionInstant();
 
     // 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);
@@ -944,13 +909,15 @@ function dropDraggedPieceOnSquare(square) {
   };
 
   // snap the piece to the target square
-  DRAGGED_PIECE.addEventListener('transitionend', complete, {once: true});
-  requestAnimationFrame(() => {
-    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;
-  });
+  DRAGGED_PIECE.animate(
+    [
+      { top: DRAGGED_PIECE.style.top, left: DRAGGED_PIECE.style.left },
+      { top: targetSquarePosition.top, left: targetSquarePosition.left }
+    ],
+    { duration: cfg.snapSpeed }
+  ).addEventListener('finish', complete);
+  DRAGGED_PIECE.style.top = targetSquarePosition.top;
+  DRAGGED_PIECE.style.left = targetSquarePosition.left;
 }
 
 function beginDraggingPiece(source, piece, x, y) {
@@ -967,8 +934,6 @@ function beginDraggingPiece(source, piece, x, y) {
   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.
@@ -1041,13 +1006,13 @@ function stopDraggedPiece(location) {
   }
 
   // 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 was on the board and position is on the board
-    if (validSquare(DRAGGED_PIECE_SOURCE) === true &&
-      validSquare(location.square) === true) {
+    if (validSquare(DRAGGED_PIECE_SOURCE) &&
+      validSquare(location.square)) {
       // move the piece
       delete newPosition[DRAGGED_PIECE_SOURCE];
       newPosition[location.square] = DRAGGED_PIECE;
@@ -1137,7 +1102,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;
     }
@@ -1205,17 +1170,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);
@@ -1253,12 +1218,12 @@ function mousedownSquare(e) {
 
   // no piece on this square
   if (!validSquare(square) ||
-      CURRENT_POSITION.hasOwnProperty(square) !== true) {
+      !CURRENT_POSITION.hasOwnProperty(square)) {
     return;
   }
 
   // do nothing if we're not draggable
-  if (cfg.draggable !== true) return;
+  if (!cfg.draggable) return;
 
   beginDraggingPiece(square, CURRENT_POSITION[square], e.pageX, e.pageY);
 }
@@ -1270,13 +1235,13 @@ 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;
   }
 
@@ -1286,14 +1251,14 @@ function touchstartSquare(e) {
 
 function mousemoveWindow(e) {
   // do nothing if we are not dragging a piece
-  if (DRAGGING_A_PIECE !== true) return;
+  if (!DRAGGING_A_PIECE) return;
 
   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();
@@ -1304,18 +1269,14 @@ function touchmoveWindow(e) {
 
 function mouseupWindow(e) {
   // do nothing if we are not dragging a piece
-  if (DRAGGING_A_PIECE !== true) return;
+  if (!DRAGGING_A_PIECE) return;
 
   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.changedTouches[0].pageX,
-    e.changedTouches[0].pageY);
+  if (!DRAGGING_A_PIECE) return;
 
   stopDraggedPiece(findSquareFromEvent(e.changedTouches[0].pageX,
     e.changedTouches[0].pageY));
@@ -1331,18 +1292,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];
   }
 
@@ -1361,18 +1322,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];
   }
 
@@ -1406,7 +1367,7 @@ function addEvents() {
   window.addEventListener('mouseup', mouseupWindow);
 
   // touch drag pieces
-  if (isTouchDevice() === true) {
+  if (isTouchDevice()) {
     boardEl.addEventListener('touchstart', touchstartSquare);
     window.addEventListener('touchmove', touchmoveWindow);
     window.addEventListener('touchend', touchendWindow);
@@ -1423,8 +1384,7 @@ function initDom() {
 }
 
 function init() {
-  if (checkDeps() !== true ||
-      expandConfig() !== true) return;
+  if (!checkDeps() || !expandConfig()) return;
 
   initDom();
   addEvents();