]> git.sesse.net Git - remoteglot/commitdiff
Rewrite how the chessboard works.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 29 Dec 2022 12:49:07 +0000 (13:49 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 29 Dec 2022 12:49:07 +0000 (13:49 +0100)
Lots of changes; the board is now completely responsive, with everything
being positioned in terms of 12.5% increments relative to the board
(and the grid being an actual CSS grid). Animated pieces are now simply
being moved, instead of making a separate piece for the animation and
deleting it later.

It's not much smaller and I'm not sure if it's actually faster,
but it should make the problems of phantom pieces somewhat smaller.
It should certainly fix the off-grid pieces showing up.

www/css/chessboard-0.3.0.css
www/css/remoteglot.css
www/js/chessboard-0.3.0.js
www/js/remoteglot.js

index bf1a53ad103fdb24f3f0d9ee762a1066a036c75b..557f28349c3e407fd3dfb72fbbe0e8ec5257e193 100644 (file)
@@ -9,23 +9,19 @@
  * Date: 10 Aug 2013
  */
 
-/* clearfix */
-.clearfix-7da63 {
-  clear: both;
-}
-
 /* board */
 .board-b72b1 {
-  border: 2px solid #404040;
-  -moz-box-sizing: content-box;
-  box-sizing: content-box;
+  outline: 2px solid #404040;
+  display: grid;
+  grid-template-rows: repeat(8, 12.5%);
+  grid-template-columns: repeat(8, 12.5%);
+  grid-gap: 0px;
+  width: 100%;
+  aspect-ratio: 1;
 }
 
 /* square */
 .square-55d63 {
-  float: left;
-  position: relative;
-
   /* disable any native browser highlighting */
   user-select: none;
 }
@@ -48,7 +44,7 @@
 }
 
 /* notation */
-.notation-322f9 {
+.alpha-d2270, .numeric-fc462 {
   cursor: default;
   font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
   font-size: 14px;
@@ -65,4 +61,7 @@
 
 .piece-417db {
   transform: translate(0,0);  /* Force a new stacking context for SVG pieces. */
+  position: absolute;
+  width: 12.5%;
+  height: 12.5%;
 }
index 2b113afea6abe3cbf4826eb9819c232d6bed881f..670f9cee037b1d7b795088f4176022c2bf28831e 100644 (file)
@@ -105,7 +105,7 @@ p {
 }
 #bottompanel {
        display: block;
-       width: 100%;
+       width: calc(100% + 4px);
        font-size: smaller;
        margin-top: 0.5em;
        margin-bottom: 0;
index 26c021704f5b45fc5b85b4f376f0ba981b67403a..5d257a0dc3b222b9facbbc31bb74bffdaeb43d7c 100644 (file)
@@ -207,13 +207,11 @@ var CSS = {
   alpha: 'alpha-d2270',
   board: 'board-b72b1',
   chessboard: 'chessboard-63f37',
-  clearfix: 'clearfix-7da63',
   highlight1: 'highlight1-32417',
   highlight2: 'highlight2-9c5d2',
   notation: 'notation-322f9',
   numeric: 'numeric-fc462',
   piece: 'piece-417db',
-  row: 'row-5277c',
   square: 'square-55d63'
 };
 var CSSColor = {};
@@ -236,16 +234,13 @@ var widget = {};
 // Stateful
 //------------------------------------------------------------------------------
 
-var BOARD_BORDER_SIZE = 2,
-  CURRENT_ORIENTATION = 'white',
+var CURRENT_ORIENTATION = 'white',
   CURRENT_POSITION = {},
-  SQUARE_SIZE,
   DRAGGED_PIECE,
   DRAGGED_PIECE_LOCATION,
   DRAGGED_PIECE_SOURCE,
   DRAGGING_A_PIECE = false,
-  SQUARE_ELS_IDS = {},
-  SQUARE_ELS_OFFSETS;
+  PIECE_ON_SQUARE = {};
 
 //------------------------------------------------------------------------------
 // JS Util Functions
@@ -424,44 +419,6 @@ function expandConfig() {
   return true;
 }
 
-//------------------------------------------------------------------------------
-// DOM Misc
-//------------------------------------------------------------------------------
-
-// calculates square size based on the width of the container
-// got a little CSS black magic here, so let me explain:
-// get the width of the container element (could be anything), reduce by 1 for
-// fudge factor, and then keep reducing until we find an exact mod 8 for
-// our square size
-function calculateSquareSize() {
-  var containerWidth = parseInt(getComputedStyle(containerEl).width, 10);
-
-  // defensive, prevent infinite loop
-  if (! containerWidth || containerWidth <= 0) {
-    return 0;
-  }
-
-  // pad one pixel
-  var boardWidth = containerWidth - 1;
-
-  while (boardWidth % 8 !== 0 && boardWidth > 0) {
-    boardWidth--;
-  }
-
-  return (boardWidth / 8);
-}
-
-// create random IDs for elements
-function createElIds() {
-  // squares on the board
-  for (var i = 0; i < COLUMNS.length; i++) {
-    for (var j = 1; j <= 8; j++) {
-      var square = COLUMNS[i] + j;
-      SQUARE_ELS_IDS[square] = square + '-' + createId();
-    }
-  }
-}
-
 //------------------------------------------------------------------------------
 // Markup Building
 //------------------------------------------------------------------------------
@@ -476,22 +433,6 @@ function buildBoardContainer() {
   return html;
 }
 
-/*
-var buildSquare = function(color, size, id) {
-  var html = '<div class="' + CSS.square + ' ' + CSSColor[color] + '" ' +
-  'style="width: ' + size + 'px; height: ' + size + 'px" ' +
-  'id="' + id + '">';
-
-  if (cfg.showNotation === true) {
-
-  }
-
-  html += '</div>';
-
-  return html;
-};
-*/
-
 function buildBoard(orientation) {
   if (orientation !== 'black') {
     orientation = 'white';
@@ -509,27 +450,29 @@ function buildBoard(orientation) {
 
   var squareColor = 'white';
   for (var i = 0; i < 8; i++) {
-    html += '<div class="' + CSS.row + '">';
     for (var j = 0; j < 8; j++) {
       var square = alpha[j] + row;
 
       html += '<div class="' + CSS.square + ' ' + CSSColor[squareColor] + ' ' +
         'square-' + square + '" ' +
-        'style="width: ' + SQUARE_SIZE + 'px; height: ' + SQUARE_SIZE + 'px" ' +
-        'id="' + SQUARE_ELS_IDS[square] + '" ' +
+        'style="grid-row: ' + (i+1) + '; grid-column: ' + (j+1) + ';" ' +
         'data-square="' + square + '">';
 
       if (cfg.showNotation === true) {
         // alpha notation
         if ((orientation === 'white' && row === 1) ||
             (orientation === 'black' && row === 8)) {
-          html += '<div class="' + CSS.notation + ' ' + CSS.alpha + '">' +
+          let bottom = 'calc(' + (12.5 * (7-i)) + '% + 1px)';
+          let right = 'calc(' + (12.5 * (7-j)) + '% + 3px)';
+          html += '<div class="' + CSS.alpha + '" style="right: ' + right + '; bottom: ' + bottom + ';">' +
             alpha[j] + '</div>';
         }
 
         // numeric notation
         if (j === 0) {
-          html += '<div class="' + CSS.notation + ' ' + CSS.numeric + '">' +
+          let top = 'calc(' + (12.5 * i) + '% + 2px)';
+          let left = 'calc(' + (12.5 * j) + '% + 2px)';
+          html += '<div class="' + CSS.numeric + '" style="top: ' + top + '; left: ' + left + ';">' +
             row + '</div>';
         }
       }
@@ -538,7 +481,6 @@ function buildBoard(orientation) {
 
       squareColor = (squareColor === 'white' ? 'black' : 'white');
     }
-    html += '<div class="' + CSS.clearfix + '"></div></div>';
 
     squareColor = (squareColor === 'white' ? 'black' : 'white');
 
@@ -570,19 +512,12 @@ function buildPieceImgSrc(piece) {
 /**
  * @param {!string} piece
  * @param {boolean=} hidden
- * @param {!string=} id
  */
-function buildPiece(piece, hidden, id) {
+function buildPiece(piece, hidden) {
   let img = document.createElement('img');
   img.src = buildPieceImgSrc(piece);
-  if (id && typeof id === 'string') {
-    img.setAttribute('id', id);
-  }
   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) {
     img.style.display = 'none';
   }
@@ -602,55 +537,37 @@ function offset(el) {  // From https://youmightnotneedjquery.com/.
   };
 }
 
-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();
-      }
-    };
+function findSquarePosition(square) {
+  let s1 = square.split('');
+  var s1x = COLUMNS.indexOf(s1[0]);
+  var s1y = parseInt(s1[1], 10) - 1;
+  if (CURRENT_ORIENTATION === 'white') {
+    s1y = 7 - s1y;
+  }
+  return {
+    top: (s1y * 12.5) + '%',
+    left: (s1x * 12.5) + '%',
+  };
+}
+
+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
-    animatedPieceEl.addEventListener('transitionend', complete, {once: true});
-    pieces.push([animatedPieceEl, destSquarePosition]);
+    piece.addEventListener('transitionend', complete, {once: true});
   }
   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';
-    }
+    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;
+   }
   });
 }
 
@@ -686,10 +603,65 @@ function fadeOut(pieces, onFinish) {
 
 // execute an array of animations
 function doAnimations(a, oldPos, newPos) {
+  let fadeout_pieces = [];
+  let fadein_pieces = [];
+  let move_pieces = [];
+  let squares_to_clear = [];
+  let squares_to_fill = {};
+  let removed_pieces = [];
+
+  for (var i = 0; i < a.length; i++) {
+    // clear a piece
+    if (a[i].type === 'clear') {
+      let square = a[i].square;
+      let piece = PIECE_ON_SQUARE[square];
+      if (piece) {
+        fadeout_pieces.push(piece);
+        squares_to_clear.push(square);
+        removed_pieces.push(piece);
+      }
+    }
+
+    // add a piece
+    if (a[i].type === 'add') {
+      let square = a[i].square;
+      let pos = findSquarePosition(square);
+      let piece = buildPiece(a[i].piece, true);
+      piece.style.left = pos.left;
+      piece.style.top = pos.top;
+      boardEl.append(piece);
+      squares_to_fill[square] = piece;
+      fadein_pieces.push(piece);
+    }
+
+    // move a piece
+    if (a[i].type === 'move') {
+      let piece = PIECE_ON_SQUARE[a[i].source];
+      move_pieces.push([piece, a[i].destination]);
+      squares_to_clear.push(a[i].source);
+      squares_to_fill[a[i].destination] = piece;
+
+      // This is O(n²), but OK.
+      let replaced_piece = PIECE_ON_SQUARE[a[i].destination];
+      if (replaced_piece && !a.some(e => e.type === 'move' && e.source === a[i].destination)) {
+        removed_pieces.push(replaced_piece);
+      }
+    }
+  }
+
+  for (const square of squares_to_clear) {
+    delete PIECE_ON_SQUARE[square];
+  }
+  for (const [square, piece] of Object.entries(squares_to_fill)) {
+    PIECE_ON_SQUARE[square] = piece;
+    piece.setAttribute('data-square', square);
+  }
+
   var numFinished = 0;
   function onFinish(e) {
     if (e && e.target) {
       e.target.transitionProperty = null;
+      e.target.transitionDuration = null;
     }
 
     numFinished++;
@@ -697,7 +669,9 @@ function doAnimations(a, oldPos, newPos) {
     // exit if all the animations aren't finished
     if (numFinished !== a.length) return;
 
-    drawPositionInstant();
+    for (let piece of removed_pieces) {
+      piece.remove();
+    }
 
     // run their onMoveEnd function
     if (cfg.hasOwnProperty('onMoveEnd') === true &&
@@ -707,42 +681,14 @@ 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
-      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);
-      }
-
-      // move a piece
-      if (a[i].type === 'move') {
-        moved_pieces.push(a[i]);
-      }
-    }
-
-    // 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);
+    if (move_pieces.length > 0) {
+      animateSquareToSquare(move_pieces, onFinish);
     }
   });
 }
@@ -867,10 +813,14 @@ function drawPositionInstant() {
   boardEl.querySelectorAll('.' + CSS.piece).forEach((piece) => piece.remove());
 
   // add the pieces
-  for (var i in CURRENT_POSITION) {
-    if (CURRENT_POSITION.hasOwnProperty(i) !== true) continue;
-
-    document.getElementById(SQUARE_ELS_IDS[i]).append(buildPiece(CURRENT_POSITION[i]));
+  for (const [square, piece] of Object.entries(CURRENT_POSITION)) {
+    let pos = findSquarePosition(square);
+    let pieceEl = buildPiece(piece);
+    pieceEl.style.left = pos.left;
+    pieceEl.style.top = pos.top;
+    pieceEl.setAttribute('data-square', square);
+    boardEl.append(pieceEl);
+    PIECE_ON_SQUARE[square] = pieceEl;
   }
 }
 
@@ -917,31 +867,6 @@ function setCurrentPosition(position) {
   CURRENT_POSITION = position;
 }
 
-function isXYOnSquare(x, y) {
-  for (var i in SQUARE_ELS_OFFSETS) {
-    if (SQUARE_ELS_OFFSETS.hasOwnProperty(i) !== true) continue;
-
-    var s = SQUARE_ELS_OFFSETS[i];
-    if (x >= s.left && x < s.left + SQUARE_SIZE &&
-        y >= s.top && y < s.top + SQUARE_SIZE) {
-      return i;
-    }
-  }
-
-  return 'offboard';
-}
-
-// records the XY coords of every square into memory
-function captureSquareOffsets() {
-  SQUARE_ELS_OFFSETS = {};
-
-  for (var i in SQUARE_ELS_IDS) {
-    if (SQUARE_ELS_IDS.hasOwnProperty(i) !== true) continue;
-
-    SQUARE_ELS_OFFSETS[i] = offset(document.getElementById(SQUARE_ELS_IDS[i]));
-  }
-}
-
 function removeSquareHighlights() {
   boardEl.querySelectorAll('.' + CSS.square).forEach((piece) => {
     piece.classList.remove(CSS.highlight1);
@@ -955,7 +880,6 @@ function snapbackDraggedPiece() {
   // animation complete
   function complete() {
     drawPositionInstant();
-    draggedPieceEl.style.display = 'none';
 
     // run their onSnapbackEnd function
     if (cfg.hasOwnProperty('onSnapbackEnd') === true &&
@@ -966,16 +890,15 @@ function snapbackDraggedPiece() {
   }
 
   // get source square position
-  var sourceSquarePosition =
-    offset(document.getElementById(SQUARE_ELS_IDS[DRAGGED_PIECE_SOURCE]));
+  var sourceSquarePosition = findSquarePosition(DRAGGED_PIECE_SOURCE);
 
   // animate the piece to the target square
-  draggedPieceEl.addEventListener('transitionend', complete, {once: true});
+  DRAGGED_PIECE.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';
+    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;
   });
 
   // set state
@@ -984,20 +907,32 @@ function snapbackDraggedPiece() {
 
 function dropDraggedPieceOnSquare(square) {
   removeSquareHighlights();
+  DRAGGING_A_PIECE = false;
+
+  if (DRAGGED_PIECE_SOURCE === square) {
+    // Nothing to do, but call onSnapEnd anyway
+    if (cfg.hasOwnProperty('onSnapEnd') === true && typeof cfg.onSnapEnd === 'function') {
+      cfg.onSnapEnd(DRAGGED_PIECE_SOURCE, square, DRAGGED_PIECE);
+    }
+    return;
+  }
 
   // update position
   var newPosition = deepCopy(CURRENT_POSITION);
+  newPosition[square] = newPosition[DRAGGED_PIECE_SOURCE];
   delete newPosition[DRAGGED_PIECE_SOURCE];
-  newPosition[square] = DRAGGED_PIECE;
   setCurrentPosition(newPosition);
 
+  delete PIECE_ON_SQUARE[DRAGGED_PIECE_SOURCE];
+  PIECE_ON_SQUARE[square] = DRAGGED_PIECE;
+  DRAGGED_PIECE.setAttribute('data-square', square);
+
   // get target square information
-  var targetSquarePosition = offset(document.getElementById(SQUARE_ELS_IDS[square]));
+  var targetSquarePosition = findSquarePosition(square);
 
   // animation complete
   var complete = function() {
     drawPositionInstant();
-    draggedPieceEl.style.display = 'none';
 
     // execute their onSnapEnd function
     if (cfg.hasOwnProperty('onSnapEnd') === true &&
@@ -1009,16 +944,13 @@ function dropDraggedPieceOnSquare(square) {
   };
 
   // snap the piece to the target square
-  draggedPieceEl.addEventListener('transitionend', complete, {once: true});
+  DRAGGED_PIECE.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';
+    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;
   });
-
-  // set state
-  DRAGGING_A_PIECE = false;
 }
 
 function beginDraggingPiece(source, piece, x, y) {
@@ -1032,46 +964,62 @@ function beginDraggingPiece(source, piece, x, y) {
 
   // set state
   DRAGGING_A_PIECE = true;
-  DRAGGED_PIECE = piece;
+  DRAGGED_PIECE = PIECE_ON_SQUARE[source];
   DRAGGED_PIECE_SOURCE = source;
   DRAGGED_PIECE_LOCATION = source;
+  DRAGGED_PIECE.style.transitionProperty = null;
+  DRAGGED_PIECE.style.transitionDuration = null;
 
-  // capture the x, y coords of all squares in memory
-  captureSquareOffsets();
+  // 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.
+  DRAGGED_PIECE.remove();
+  boardEl.appendChild(DRAGGED_PIECE);
 
-  // create the dragged piece
-  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]);
+  // highlight the source square
+  let square = document.querySelector('.' + CSS.square + '[data-square="' + 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.style.left = (x - (SQUARE_SIZE / 2)) + 'px';
-  draggedPieceEl.style.top = (y - (SQUARE_SIZE / 2)) + 'px';
+function findSquareFromEvent(pageX, pageY) {
+  let o = offset(boardEl);
+  let x = pageX - o.left;
+  let y = pageY - o.top;
+
+  let position = {
+    x: x,
+    y: y,
+    left: Math.floor(x * 8 / boardEl.getBoundingClientRect().width),
+    top: Math.floor(y * 8 / boardEl.getBoundingClientRect().width)
+  };
+  if (CURRENT_ORIENTATION === 'white') {
+    position.top = 7 - position.top;
+  }
+  if (position.left >= 0 && position.left < 8 && position.top >= 0 && position.top < 8) {
+    position.square = COLUMNS[position.left] + (position.top + 1);
+  } else {
+    position.square = 'offboard';
+  }
+  return position;
+}
 
-  // get location
-  var location = isXYOnSquare(x, y);
+function updateDraggedPiece(position) {
+  // put the dragged piece over the mouse cursor
+  DRAGGED_PIECE.style.left = 'calc(' + position.x + 'px - 6.25%)';
+  DRAGGED_PIECE.style.top = 'calc(' + position.y + 'px - 6.25%)';
 
   // do nothing if the location has not changed
-  if (location === DRAGGED_PIECE_LOCATION) return;
+  if (position === DRAGGED_PIECE_LOCATION) return;
 
   // remove highlight from previous square
-  if (validSquare(DRAGGED_PIECE_LOCATION) === true) {
-    document.getElementById(SQUARE_ELS_IDS[DRAGGED_PIECE_LOCATION])
+  if (validSquare(DRAGGED_PIECE_LOCATION)) {
+    document.querySelector('.' + CSS.square + '[data-square="' + DRAGGED_PIECE_LOCATION + '"]')
       .classList.remove(CSS.highlight2);
   }
 
   // add highlight to new square
-  if (validSquare(location) === true) {
-    document.getElementById(SQUARE_ELS_IDS[location]).classList.add(CSS.highlight2);
+  if (validSquare(position.square)) {
+    document.querySelector('.' + CSS.square + '[data-square="' + position.square + '"]')
+      .classList.add(CSS.highlight2);
   }
 
   // run onDragMove
@@ -1082,13 +1030,13 @@ function updateDraggedPiece(x, y) {
   }
 
   // update state
-  DRAGGED_PIECE_LOCATION = location;
+  DRAGGED_PIECE_LOCATION = position.square;
 }
 
 function stopDraggedPiece(location) {
   // determine what the action should be
   var action = 'drop';
-  if (location === 'offboard' && cfg.dropOffBoard === 'snapback') {
+  if (location.square === 'offboard' && cfg.dropOffBoard === 'snapback') {
     action = 'snapback';
   }
 
@@ -1099,15 +1047,20 @@ function stopDraggedPiece(location) {
 
     // source piece was on the board and position is on the board
     if (validSquare(DRAGGED_PIECE_SOURCE) === true &&
-      validSquare(location) === true) {
+      validSquare(location.square) === true) {
       // move the piece
       delete newPosition[DRAGGED_PIECE_SOURCE];
-      newPosition[location] = DRAGGED_PIECE;
+      newPosition[location.square] = DRAGGED_PIECE;
+      if (location.square !== DRAGGED_PIECE_SOURCE) {
+        PIECE_ON_SQUARE[location.square] = PIECE_ON_SQUARE[DRAGGED_PIECE_SOURCE];
+        DRAGGED_PIECE.setAttribute('data-square', location.square);
+        delete PIECE_ON_SQUARE[DRAGGED_PIECE_SOURCE];
+      }
     }
 
     var oldPosition = deepCopy(CURRENT_POSITION);
 
-    var result = cfg.onDrop(DRAGGED_PIECE_SOURCE, location, DRAGGED_PIECE,
+    var result = cfg.onDrop(DRAGGED_PIECE_SOURCE, location.square, DRAGGED_PIECE,
       newPosition, oldPosition, CURRENT_ORIENTATION);
     if (result === 'snapback') {
       action = result;
@@ -1119,7 +1072,7 @@ function stopDraggedPiece(location) {
     snapbackDraggedPiece();
   }
   else if (action === 'drop') {
-    dropDraggedPieceOnSquare(location);
+    dropDraggedPieceOnSquare(location.square);
   }
 }
 
@@ -1278,18 +1231,6 @@ widget.position = function(position, useAnimation) {
 };
 
 widget.resize = function() {
-  // calulate the new square size
-  SQUARE_SIZE = calculateSquareSize();
-
-  // set board width
-  boardEl.style.width = (SQUARE_SIZE * 8) + 'px';
-
-  // set drag piece size
-  if (draggedPieceEl !== null) {
-    draggedPieceEl.style.height = SQUARE_SIZE + 'px';
-    draggedPieceEl.style.width = SQUARE_SIZE + 'px';
-  }
-
   // redraw the board
   drawBoard();
 };
@@ -1308,22 +1249,17 @@ function isTouchDevice() {
 }
 
 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 = target.getAttribute('data-square');
+  let square = e.target.getAttribute('data-square');
 
   // no piece on this square
-  if (validSquare(square) !== true ||
+  if (!validSquare(square) ||
       CURRENT_POSITION.hasOwnProperty(square) !== true) {
     return;
   }
 
+  // do nothing if we're not draggable
+  if (cfg.draggable !== true) return;
+
   beginDraggingPiece(square, CURRENT_POSITION[square], e.pageX, e.pageY);
 }
 
@@ -1352,7 +1288,7 @@ function mousemoveWindow(e) {
   // do nothing if we are not dragging a piece
   if (DRAGGING_A_PIECE !== true) return;
 
-  updateDraggedPiece(e.pageX, e.pageY);
+  updateDraggedPiece(findSquareFromEvent(e.pageX, e.pageY));
 }
 
 function touchmoveWindow(e) {
@@ -1362,18 +1298,15 @@ function touchmoveWindow(e) {
   // prevent screen from scrolling
   e.preventDefault();
 
-  updateDraggedPiece(e.changedTouches[0].pageX,
-    e.changedTouches[0].pageY);
+  updateDraggedPiece(findSquareFromEvent(e.changedTouches[0].pageX,
+    e.changedTouches[0].pageY));
 }
 
 function mouseupWindow(e) {
   // do nothing if we are not dragging a piece
   if (DRAGGING_A_PIECE !== true) return;
 
-  // get the location
-  var location = isXYOnSquare(e.pageX, e.pageY);
-
-  stopDraggedPiece(location);
+  stopDraggedPiece(findSquareFromEvent(e.pageX, e.pageY));
 }
 
 function touchendWindow(e) {
@@ -1384,7 +1317,8 @@ function touchendWindow(e) {
   var location = isXYOnSquare(e.changedTouches[0].pageX,
     e.changedTouches[0].pageY);
 
-  stopDraggedPiece(location);
+  stopDraggedPiece(findSquareFromEvent(e.changedTouches[0].pageX,
+    e.changedTouches[0].pageY));
 }
 
 function mouseenterSquare(e) {
@@ -1484,14 +1418,6 @@ function initDom() {
   containerEl.innerHTML = buildBoardContainer();
   boardEl = containerEl.querySelector('.' + CSS.board);
 
-  // create the drag piece
-  var draggedPieceId = createId();
-  document.body.append(buildPiece('wP', true, draggedPieceId));
-  draggedPieceEl = document.getElementById(draggedPieceId);
-
-  // get the border size
-  BOARD_BORDER_SIZE = parseInt(boardEl.style.borderLeftWidth, 10);
-
   // set the size and draw the board
   widget.resize();
 }
@@ -1500,9 +1426,6 @@ function init() {
   if (checkDeps() !== true ||
       expandConfig() !== true) return;
 
-  // create unique IDs for all the elements we will create
-  createElIds();
-
   initDom();
   addEvents();
 }
index c2bf8ff482b7f69cb07bc09c4b1accd0659850e9..f65dd98ed038240e06abd04f6ee40ebc6288d54a 100644 (file)
@@ -504,12 +504,11 @@ function position_arrow(arrow) {
                return;
        }
 
-       let board_width = parseInt(document.querySelector(".board-b72b1").style.width, 10);
-       let zoom_factor = board_width / 400.0;
-       let line_width = arrow.line_width * zoom_factor;
-       let arrow_size = arrow.arrow_size * zoom_factor;
+       // We always draw as if the board is 400x400, the viewBox will adjust that for us
+       let line_width = arrow.line_width;
+       let arrow_size = arrow.arrow_size;
 
-       let square_width = board_width / 8;
+       let square_width = 400 / 8;
        let from_y, to_y, from_x, to_x;
        if (board.orientation() === 'black') {
                from_y = (arrow.from_row + 0.5)*square_width;
@@ -526,10 +525,9 @@ function position_arrow(arrow) {
        let SVG_NS = "http://www.w3.org/2000/svg";
        let XHTML_NS = "http://www.w3.org/1999/xhtml";
        let svg = document.createElementNS(SVG_NS, "svg");
-       svg.setAttribute("width", board_width);
-       svg.setAttribute("height", board_width);
-       svg.setAttribute("style", "position: absolute");
-       svg.setAttribute("position", "absolute");
+       svg.setAttribute("width", "100%");
+       svg.setAttribute("height", "100%");
+       svg.setAttribute("viewBox", "0 0 400 400");
        svg.setAttribute("version", "1.1");
        svg.setAttribute("class", "c1");
        svg.setAttribute("xmlns", XHTML_NS);
@@ -570,8 +568,9 @@ function position_arrow(arrow) {
        head.setAttribute("fill", arrow.fg_color);
        svg.appendChild(head);
 
-       svg.style.top = '2px';  /* Border for .board-b72b1. */
-       svg.style.left = '2px';
+       svg.style.position = 'absolute';
+       svg.style.top = '0px';  /* Border for .board-b72b1. */
+       svg.style.left = '0px';
        svg.style.pointerEvents = 'none';
        document.getElementById('board').appendChild(svg);
        arrow.svg = svg;
@@ -2077,12 +2076,12 @@ function onDragStart(source, piece, position, orientation) {
 }
 
 function mousedownSquare(e) {
-       if (!e.target || !e.target.closest('.square-55d63')) {
+       if (!e.target || !e.target.getAttribute('data-square')) {
                return;
        }
 
        reverse_dragging_from = null;
-       let square = e.target.closest('.square-55d63').getAttribute('data-square');
+       let square = e.target.getAttribute('data-square');
 
        let pseudogame = new Chess(display_fen);
        if (pseudogame.game_over() === true) {
@@ -2107,13 +2106,13 @@ function mousedownSquare(e) {
 }
 
 function mouseupSquare(e) {
-       if (!e.target || !e.target.closest('.square-55d63')) {
+       if (!e.target || !e.target.getAttribute('data-square')) {
                return;
        }
        if (reverse_dragging_from === null) {
                return;
        }
-       let source = e.target.closest('.square-55d63').getAttribute('data-square');
+       let source = e.target.getAttribute('data-square');
        let target = reverse_dragging_from;
        reverse_dragging_from = null;
        if (onDrop(source, target) !== 'snapback') {