]> git.sesse.net Git - remoteglot/blobdiff - www/js/chessboard-0.3.0.js
Slightly less weird FEN generation.
[remoteglot] / www / js / chessboard-0.3.0.js
index b97961720e66dbd17c34b88be360c10b60883e8d..26c021704f5b45fc5b85b4f376f0ba981b67403a 100644 (file)
@@ -149,6 +149,7 @@ function objToFen(obj) {
   }
 
   var fen = '';
+  let num_empty = 0;
 
   var currentRow = 8;
   for (var i = 0; i < 8; i++) {
@@ -157,32 +158,30 @@ function objToFen(obj) {
 
       // piece exists
       if (obj.hasOwnProperty(square) === true) {
+        if (num_empty > 0) {
+          fen += num_empty;
+          num_empty = 0;
+        }
         fen += pieceCodeToFen(obj[square]);
       }
 
       // empty space
       else {
-        fen += '1';
+        ++num_empty;
       }
     }
 
     if (i !== 7) {
+      if (num_empty > 0) {
+        fen += num_empty;
+        num_empty = 0;
+      }
       fen += '/';
     }
 
     currentRow--;
   }
 
-  // squeeze the numbers together
-  // haha, I love this solution...
-  fen = fen.replace(/11111111/g, '8');
-  fen = fen.replace(/1111111/g, '7');
-  fen = fen.replace(/111111/g, '6');
-  fen = fen.replace(/11111/g, '5');
-  fen = fen.replace(/1111/g, '4');
-  fen = fen.replace(/111/g, '3');
-  fen = fen.replace(/11/g, '2');
-
   return fen;
 }
 
@@ -199,8 +198,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
@@ -216,9 +214,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 = {};
@@ -232,9 +227,7 @@ CSSColor['black'] = 'black-3c85d';
 // DOM elements
 var containerEl,
   boardEl,
-  draggedPieceEl,
-  sparePiecesTopEl,
-  sparePiecesBottomEl;
+  draggedPieceEl;
 
 // constructor return object
 var widget = {};
@@ -251,7 +244,6 @@ var BOARD_BORDER_SIZE = 2,
   DRAGGED_PIECE_LOCATION,
   DRAGGED_PIECE_SOURCE,
   DRAGGING_A_PIECE = false,
-  SPARE_PIECE_ELS_IDS = {},
   SQUARE_ELS_IDS = {},
   SQUARE_ELS_OFFSETS;
 
@@ -259,40 +251,15 @@ var BOARD_BORDER_SIZE = 2,
 // 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
 //------------------------------------------------------------------------------
@@ -406,21 +373,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' &&
@@ -508,15 +460,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();
-  }
 }
 
 //------------------------------------------------------------------------------
@@ -526,18 +469,8 @@ function createElIds() {
 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;
@@ -656,20 +589,6 @@ function buildPiece(piece, hidden, id) {
   return img;
 }
 
-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;
-}
-
 //------------------------------------------------------------------------------
 // Animations
 //------------------------------------------------------------------------------
@@ -683,88 +602,56 @@ function offset(el) {  // From https://youmightnotneedjquery.com/.
   };
 }
 
-function animateSquareToSquare(src, dest, piece, completeFn) {
-  // get information about the source and destination squares
-  var srcSquareEl = document.getElementById(SQUARE_ELS_IDS[src]);
-  var srcSquarePosition = offset(srcSquareEl);
-  var destSquareEl = document.getElementById(SQUARE_ELS_IDS[dest]);
-  var destSquarePosition = offset(destSquareEl);
-
-  // create the animated piece and absolutely position it
-  // over the source square
-  var animatedPieceId = createId();
-  document.body.append(buildPiece(piece, true, animatedPieceId));
-  var animatedPieceEl = document.getElementById(animatedPieceId);
-  animatedPieceEl.style.display = null;
-  animatedPieceEl.style.position = 'absolute';
-  animatedPieceEl.style.top = srcSquarePosition.top + 'px';
-  animatedPieceEl.style.left = srcSquarePosition.left + 'px';
-
-  // remove original piece(s) from source square
-  // TODO: multiple pieces should never really happen, but it will if we are moving
-  // while another animation still isn't done
-  srcSquareEl.querySelectorAll('.' + CSS.piece).forEach((piece) => piece.remove());
-
-  // on complete
-  var complete = function() {
-    // add the "real" piece to the destination square
-    destSquareEl.append(buildPiece(piece));
-
-    // remove the animated piece
-    animatedPieceEl.remove();
-
-    // run complete function
-    if (typeof completeFn === 'function') {
-      completeFn();
-    }
-  };
+function 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();
+      }
+    };
 
-  // animate the piece to the destination square
-  animatedPieceEl.addEventListener('transitionend', complete, {once: true});
+    // animate the piece to the destination square
+    animatedPieceEl.addEventListener('transitionend', complete, {once: true});
+    pieces.push([animatedPieceEl, destSquarePosition]);
+  }
   requestAnimationFrame(() => {
-    animatedPieceEl.style.transitionProperty = 'top, left';
-    animatedPieceEl.style.transitionDuration = cfg.moveSpeed + 'ms';
-    animatedPieceEl.style.top = destSquarePosition.top + 'px';
-    animatedPieceEl.style.left = destSquarePosition.left + 'px';
-  });
-}
-
-function animateSparePieceToSquare(piece, dest, completeFn) {
-  var srcOffset = offset(document.getelementById(SPARE_PIECE_ELS_IDS[piece]));
-  var destSquareEl = document.getElementById(SQUARE_ELS_IDS[dest]);
-  var destOffset = offset(destSquareEl);
-
-  // create the animate piece
-  var pieceId = createId();
-  document.body.append(buildPiece(piece, true, pieceId));
-  var animatedPieceEl = document.getElementById(pieceId);
-  animatedPieceEl.style.display = '';
-  animatedPieceEl.style.position = 'absolute';
-  animatedPieceEl.style.left = srcOffset.left;
-  animatedPieceEl.style.top = srcOffset.top;
-
-  // on complete
-  var complete = function() {
-    // add the "real" piece to the destination square
-    destSquareEl.querySelector('.' + CSS.piece).remove();
-    destSquareEl.append(buildPiece(piece));
-
-    // remove the animated piece
-    animatedPieceEl.remove();
-
-    // run complete function
-    if (typeof completeFn === 'function') {
-      completeFn();
+    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
-  // FIXME: support this for non-jquery
-  var opts = {
-    duration: cfg.moveSpeed,
-    complete: complete
-  };
-  //$(animatedPieceEl).animate(destOffset, opts);
+  });
 }
 
 function fadeIn(pieces, onFinish) {
@@ -822,6 +709,7 @@ function doAnimations(a, oldPos, newPos) {
   requestAnimationFrame(() => {  // Firefox workaround.
     let fadeout_pieces = [];
     let fadein_pieces = [];
+    let moved_pieces = [];
 
     for (var i = 0; i < a.length; i++) {
       // clear a piece
@@ -831,23 +719,17 @@ function doAnimations(a, oldPos, newPos) {
         );
       }
 
-      // add a piece (no spare pieces)
-      if (a[i].type === 'add' && cfg.sparePieces !== true) {
+      // 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);
+        moved_pieces.push(a[i]);
       }
     }
 
@@ -859,6 +741,9 @@ function doAnimations(a, oldPos, newPos) {
     if (fadein_pieces.length > 0) {
       fadeIn(fadein_pieces, onFinish);
     }
+    if (moved_pieces.length > 0) {
+      animateSquareToSquare(moved_pieces, onFinish);
+    }
   });
 }
 
@@ -992,17 +877,6 @@ function drawPositionInstant() {
 function drawBoard() {
   boardEl.innerHTML = buildBoard(CURRENT_ORIENTATION);
   drawPositionInstant();
-
-  if (cfg.sparePieces === true) {
-    if (CURRENT_ORIENTATION === 'white') {
-      sparePiecesTopEl.innerHTML = buildSparePieces('black');
-      sparePiecesBottomEl.innerHTML = buildSparePieces('white');
-    }
-    else {
-      sparePiecesTopEl.innerHTML = buildSparePieces('white');
-      sparePiecesBottomEl.innerHTML = buildSparePieces('black');
-    }
-  }
 }
 
 // given a position and a set of moves, return a new position
@@ -1076,12 +950,6 @@ function removeSquareHighlights() {
 }
 
 function snapbackDraggedPiece() {
-  // there is no "snapback" for spare pieces
-  if (DRAGGED_PIECE_SOURCE === 'spare') {
-    trashDraggedPiece();
-    return;
-  }
-
   removeSquareHighlights();
 
   // animation complete
@@ -1114,25 +982,6 @@ function snapbackDraggedPiece() {
   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
-  // FIXME: support this for non-jquery
-  //$(draggedPieceEl).fadeOut(cfg.trashSpeed);
-
-  // set state
-  DRAGGING_A_PIECE = false;
-}
-
 function dropDraggedPieceOnSquare(square) {
   removeSquareHighlights();
 
@@ -1185,14 +1034,7 @@ 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();
@@ -1204,12 +1046,10 @@ function beginDraggingPiece(source, piece, x, y) {
   draggedPieceEl.style.left = (x - (SQUARE_SIZE / 2)) + 'px';
   draggedPieceEl.style.top = (y - (SQUARE_SIZE / 2)) + 'px';
 
-  if (source !== 'spare') {
-    // highlight the source square and hide the piece
-    let square = document.getElementById(SQUARE_ELS_IDS[source]);
-    square.classList.add(CSS.highlight1);
-    square.querySelector('.' + CSS.piece).style.display = 'none';
-  }
+  // 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) {
@@ -1251,31 +1091,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) {
@@ -1288,7 +1109,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;
     }
   }
@@ -1297,9 +1118,6 @@ function stopDraggedPiece(location) {
   if (action === 'snapback') {
     snapbackDraggedPiece();
   }
-  else if (action === 'trash') {
-    trashDraggedPiece();
-  }
   else if (action === 'drop') {
     dropDraggedPieceOnSquare(location);
   }
@@ -1472,12 +1290,6 @@ widget.resize = function() {
     draggedPieceEl.style.width = SQUARE_SIZE + 'px';
   }
 
-  // spare pieces
-  if (cfg.sparePieces === true) {
-    containerEl.querySelector('.' + CSS.sparePieces)
-      .style.paddingLeft = (SQUARE_SIZE + BOARD_BORDER_SIZE) + 'px';
-  }
-
   // redraw the board
   drawBoard();
 };
@@ -1536,33 +1348,6 @@ function touchstartSquare(e) {
     e.changedTouches[0].pageX, e.changedTouches[0].pageY);
 }
 
-function mousedownSparePiece(e) {
-  if (!e.target.matches('.' + CSS.sparePieces + ' .' + CSS.piece)) {
-    return;
-  }
-
-  // do nothing if sparePieces is not enabled
-  if (cfg.sparePieces !== true) return;
-
-  var piece = e.target.getAttribute('data-piece');
-
-  beginDraggingPiece('spare', piece, e.pageX, e.pageY);
-}
-
-function touchstartSparePiece(e) {
-  if (!e.target.matches('.' + CSS.sparePieces + ' .' + CSS.piece)) {
-    return;
-  }
-
-  // do nothing if sparePieces is not enabled
-  if (cfg.sparePieces !== true) return;
-
-  var piece = e.target.getAttribute('data-piece');
-
-  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;
@@ -1678,7 +1463,6 @@ function addEvents() {
 
   // mouse drag pieces
   boardEl.addEventListener('mousedown', mousedownSquare);
-  containerEl.addEventListener('mousedown', mousedownSparePiece);
 
   // mouse enter / leave square
   boardEl.addEventListener('mouseenter', mouseenterSquare);
@@ -1690,7 +1474,6 @@ function addEvents() {
   // touch drag pieces
   if (isTouchDevice() === true) {
     boardEl.addEventListener('touchstart', touchstartSquare);
-    containerEl.addEventListener('touchstart', touchstartSparePiece);
     window.addEventListener('touchmove', touchmoveWindow);
     window.addEventListener('touchend', touchendWindow);
   }
@@ -1701,11 +1484,6 @@ function initDom() {
   containerEl.innerHTML = buildBoardContainer();
   boardEl = containerEl.querySelector('.' + CSS.board);
 
-  if (cfg.sparePieces === true) {
-    sparePiecesTopEl = containerEl.querySelector('.' + CSS.sparePiecesTop);
-    sparePiecesBottomEl = containerEl.querySelector('.' + CSS.sparePiecesBottom);
-  }
-
   // create the drag piece
   var draggedPieceId = createId();
   document.body.append(buildPiece('wP', true, draggedPieceId));