Experimental board size auto-adjust
[vchess.git] / client / src / components / BaseGame.vue
index dc78b2a..ee3bea1 100644 (file)
@@ -22,6 +22,7 @@ div#baseGame
         :incheck="incheck"
         @play-move="play"
         @click-square="clickSquare"
+        @rendered="adjustSize"
       )
       #turnIndicator(v-if="showTurn") {{ turn }}
       #controls.button-group
@@ -43,6 +44,7 @@ div#baseGame
       p#fenAnalyze(v-show="showFen") {{ (!!vr ? vr.getFen() : "") }}
     #movesList
       MoveList(
+        ref="moveslist"
         :show="showMoves"
         :canAnalyze="canAnalyze"
         :canDownload="allowDownloadPGN"
@@ -51,6 +53,7 @@ div#baseGame
         :firstNum="firstMoveNumber"
         :moves="moves"
         :cursor="cursor"
+        :vname="game.vname"
         @download="download"
         @showrules="showRules"
         @analyze="toggleAnalyze"
@@ -86,10 +89,12 @@ export default {
       endgameMessage: "",
       orientation: "w",
       mode: "",
+      gameMode: "",
       score: "*", //'*' means 'unfinished'
       moves: [],
       cursor: -1, //index of the move just played
       lastMove: null,
+      touchLastClick: "",
       firstMoveNumber: 0, //for printing
       incheck: [], //for Board
       inMultimove: false,
@@ -140,7 +145,7 @@ export default {
     },
     canAnalyze: function() {
       return (
-        !!this.game.mode && this.game.mode != "analyze" &&
+        (!this.game.mode || this.game.mode != "analyze") &&
         !!this.vr && this.vr.canAnalyze
       );
     },
@@ -164,7 +169,8 @@ export default {
       baseGameDiv.tabIndex = 0;
       baseGameDiv.addEventListener("click", this.focusBg);
       baseGameDiv.addEventListener("keydown", this.handleKeys);
-      baseGameDiv.addEventListener("wheel", this.handleScroll);
+      if (this.st.settings.scrollmove)
+        baseGameDiv.addEventListener("wheel", this.handleScroll);
     }
     document.getElementById("eogDiv")
       .addEventListener("click", processModalClick);
@@ -202,6 +208,9 @@ export default {
       if (e.deltaY < 0) this.undo();
       else if (e.deltaY > 0) this.play();
     },
+    adjustSize: function() {
+      this.$refs["moveslist"].adjustBoard("vertical");
+    },
     redrawBoard: function() {
       this.$refs["board"].re_setDrawings();
     },
@@ -228,35 +237,34 @@ export default {
       const firstMoveColor = parsedFen.turn;
       this.firstMoveNumber = Math.floor(parsedFen.movesCount / 2) + 1;
       let L = this.moves.length;
-      if (L == 0) {
-        // Could be started on a random position in analysis mode:
-        this.incheck = this.vr.getCheckSquares();
-        this.score = this.vr.getCurrentScore();
-        if (this.score != '*') {
-          // Show score on screen
-          const message = getScoreMessage(this.score);
-          this.showEndgameMsg(this.score + " . " + this.st.tr[message]);
-        }
-      }
-      else {
-        this.moves.forEach((move,idx) => {
-          // Strategy working also for multi-moves:
-          if (!Array.isArray(move)) move = [move];
-          const Lm = move.length;
-          move.forEach((m,idxM) => {
-            m.notation = this.vr.getNotation(m);
-            m.unambiguous = V.GetUnambiguousNotation(m);
-            this.vr.play(m);
-            const checkSquares = this.vr.getCheckSquares();
-            if (checkSquares.length > 0) m.notation += "+";
-            if (idxM == Lm - 1) m.fen = this.vr.getFen();
-            if (idx == L - 1 && idxM == Lm - 1) {
-              this.incheck = checkSquares;
-              this.score = this.vr.getCurrentScore();
-              if (["1-0", "0-1"].includes(this.score)) m.notation += "#";
-            }
-          });
+      this.moves.forEach((move,idx) => {
+        // Strategy working also for multi-moves:
+        if (!Array.isArray(move)) move = [move];
+        move.forEach(m => {
+          m.notation = this.vr.getNotation(m);
+          m.unambiguous = V.GetUnambiguousNotation(m);
+          this.vr.play(m);
         });
+        const Lm = move.length;
+        move[Lm - 1].fen = this.vr.getFen();
+        if (idx < L - 1 && this.vr.getCheckSquares().length > 0)
+          move[Lm - 1].notation += "+";
+      });
+      this.incheck = this.vr.getCheckSquares();
+      this.score = this.vr.getCurrentScore();
+      if (L >= 1) {
+        const move =
+          !Array.isArray(this.moves[L - 1])
+            ? [this.moves[L - 1]]
+            : this.moves[L - 1];
+        const Lm = move.length;
+        if (["1-0", "0-1"].includes(this.score)) move[Lm - 1].notation += "#";
+        else if (this.incheck.length > 0) move[Lm - 1].notation += "+";
+      }
+      if (this.score != '*') {
+        // Show score on screen
+        const message = getScoreMessage(this.score);
+        this.showEndgameMsg(this.score + " . " + this.st.tr[message]);
       }
       if (firstMoveColor == "b") {
         // 'start' & 'end' is required for Board component
@@ -297,7 +305,7 @@ export default {
       }
       else {
         // Exit analyze mode:
-        this.mode = this.gameMode ;
+        this.mode = this.gameMode;
         this.cursor = this.gameCursor;
         this.moves = this.gameMoves;
         let fen = this.game.fenStart;
@@ -338,6 +346,7 @@ export default {
         pgn += '[Url "' + params.serverUrl + '/game/' + this.game.id + '"]\n';
       if (!!this.game.cadence)
         pgn += '[Cadence "' + this.game.cadence + '"]\n';
+      pgn += '[Options "' + JSON.stringify(this.game.options) + '"]\n';
       pgn += '\n';
       for (let i = 0; i < this.moves.length; i += 2) {
         if (i > 0) pgn += " ";
@@ -431,8 +440,35 @@ export default {
     },
     clickSquare: function(square) {
       // Some variants make use of a single click at specific times:
-      const move = this.vr.doClick(square);
-      if (!!move) this.play(move);
+      const move_s = this.vr.doClick(square);
+      if (!!move_s) {
+        const playMove = () => {
+          if (!Array.isArray(move_s)) this.play(move_s);
+          else this.$refs["board"].choices = move_s;
+        }
+        if ("ontouchstart" in window) {
+          const squareId = "sq-" + square[0] + "-" + square[1];
+          const highlight = function(on, sq) {
+            let elt = document.getElementById(sq);
+            if (!!elt) {
+              if (on) elt.classList.add("touch-hover");
+              else elt.classList.remove("touch-hover");
+            }
+          }
+          // Touch screen (smartphone): require confirmation
+          const squareStr = square[0] + "_" + square[1]
+          if (this.touchLastClick == squareId) {
+            highlight(false, squareId);
+            playMove();
+          }
+          else {
+            highlight(true, squareId);
+            highlight(false, this.touchLastClick);
+          }
+          this.touchLastClick = squareId;
+        }
+        else playMove();
+      }
     },
     // "light": if gotoMove() or gotoEnd()
     play: function(move, received, light, autoplay) {
@@ -463,11 +499,11 @@ export default {
           this.stackToPlay.unshift(move);
           return;
         }
-        this.inPlay = true;
         if (this.mode == "analyze") this.toggleAnalyze();
         if (this.cursor < this.moves.length - 1)
           // To play a received move, cursor must be at the end of the game:
           this.gotoEnd();
+        this.inPlay = true;
       }
       // The board may show some possible moves: (TODO: bad solution)
       this.$refs["board"].resetCurrentAttempt();
@@ -480,9 +516,6 @@ export default {
             this.lastMove = [this.lastMove, smove];
           else this.lastMove.push(smove);
         }
-        // Is opponent (or me) in check?
-        this.incheck = this.vr.getCheckSquares();
-        if (this.incheck.length > 0) smove.notation += "+";
         if (!this.inMultimove) {
           // First sub-move:
           this.lastMove = smove;
@@ -494,7 +527,8 @@ export default {
           }
           this.inMultimove = true; //potentially
           this.cursor++;
-        } else if (!navigate) {
+        }
+        else if (!navigate) {
           // Already in the middle of a multi-move
           const L = this.moves.length;
           if (!Array.isArray(this.moves[L-1]))
@@ -522,7 +556,8 @@ export default {
               if (moveIdx < move.length) setTimeout(executeMove, 500);
               else afterMove(smove, initurn);
             });
-          } else {
+          }
+          else {
             playSubmove(smove);
             if (moveIdx < move.length) executeMove();
             else afterMove(smove, initurn);
@@ -541,7 +576,7 @@ export default {
           }
         }
         if (score != "*" && ["analyze", "versus"].includes(this.mode)) {
-          const message = getScoreMessage(score);
+          const message = getScoreMessage(score, V.ReverseColors);
           // Show score on screen
           this.showEndgameMsg(score + " . " + this.st.tr[message]);
         }
@@ -555,6 +590,8 @@ export default {
             smove.fen = this.vr.getFen();
           this.emitFenIfAnalyze();
           this.inMultimove = false;
+          this.incheck = this.vr.getCheckSquares();
+          if (this.incheck.length > 0) smove.notation += "+";
           this.score = computeScore();
           if (this.autoplay) {
             if (this.cursor < this.moves.length - 1)
@@ -572,7 +609,8 @@ export default {
               const L = this.moves.length;
               // NOTE: always emit the score, even in unfinished
               this.$emit("newmove", this.moves[L-1], { score: this.score });
-            } else {
+            }
+            else {
               this.inPlay = false;
               if (this.stackToPlay.length > 0)
                 // Move(s) arrived in-between
@@ -631,7 +669,8 @@ export default {
         this.incheck = this.vr.getCheckSquares();
         if (this.cursor >= 0) this.lastMove = this.moves[this.cursor];
         else this.lastMove = null;
-      } else {
+      }
+      else {
         if (!move) {
           const minCursor =
             this.moves.length > 0 && this.moves[0].notation == "..."