Finished. Now some last styling
[vchess.git] / client / src / components / BaseGame.vue
index e04f508..2bf1fbd 100644 (file)
@@ -1,13 +1,14 @@
 <template lang="pug">
-div#baseGame(tabindex=-1 @click="() => focusBg()" @keydown="handleKeys")
+div#baseGame(tabindex=-1 @click="() => focusBg()"
+    @keydown="handleKeys" @wheel="handleScroll")
   input#modalEog.modal(type="checkbox")
-  div(role="dialog" aria-labelledby="eogMessage")
+  div(role="dialog" data-checkbox="modalEog" aria-labelledby="eogMessage")
     .card.smallpad.small-modal.text-center
       label.modal-close(for="modalEog")
       h3#eogMessage.section {{ endgameMessage }}
-  .row
-    #boardContainer.col-sm-12.col-md-9
-      Board(:vr="vr" :last-move="lastMove" :analyze="analyze"
+  #gameContainer
+    #boardContainer
+      Board(:vr="vr" :last-move="lastMove" :analyze="game.mode=='analyze'"
         :user-color="game.mycolor" :orientation="orientation"
         :vname="game.vname" @play-move="play")
       #controls
@@ -16,14 +17,17 @@ div#baseGame(tabindex=-1 @click="() => focusBg()" @keydown="handleKeys")
         button(@click="flip") &#8645;
         button(@click="() => play()") >
         button(@click="gotoEnd") >>
-      #fenDiv(v-if="showFen && !!vr")
-        p(@click="gotoFenContent") {{ vr.getFen() }}
       #pgnDiv
         a#download(href="#")
         button(@click="download") {{ st.tr["Download PGN"] }}
-    .col-sm-12.col-md-3
+        button(v-if="game.mode!='analyze'" @click="analyzePosition")
+          | {{ st.tr["Analyze"] }}
+    #movesList
       MoveList(v-if="showMoves" :score="game.score" :message="game.scoreMsg"
-        :moves="moves" :cursor="cursor" @goto-move="gotoMove")
+        :firstNum="firstMoveNumber" :moves="moves" :cursor="cursor"
+        @goto-move="gotoMove")
+  // TODO: clearer required ?!
+  .clearer
 </template>
 
 <script>
@@ -51,6 +55,7 @@ export default {
       moves: [],
       cursor: -1, //index of the move just played
       lastMove: null,
+      firstMoveNumber: 0, //for printing
     };
   },
   watch: {
@@ -59,26 +64,34 @@ export default {
       this.re_setVariables();
     },
     // Received a new move to play:
-    "game.moveToPlay": function() {
-      this.play(this.game.moveToPlay, "receive", this.game.vname=="Dark");
+    "game.moveToPlay": function(newMove) {
+      if (!!newMove) //if stop + launch new game, get undefined move
+        this.play(newMove, "receive");
     },
   },
   computed: {
     showMoves: function() {
-      return true;
-      //return window.innerWidth >= 768;
-    },
-    showFen: function() {
-      return this.game.vname != "Dark" || this.game.score != "*";
-    },
-    analyze: function() {
-      return this.game.mode == "analyze" || this.game.score != "*";
+      return this.game.vname != "Dark" || this.game.mode=="analyze";
     },
   },
   created: function() {
     if (!!this.game.fenStart)
       this.re_setVariables();
   },
+  mounted: function() {
+    // Take full width on small screens:
+    let boardSize = parseInt(localStorage.getItem("boardSize"));
+    if (!boardSize)
+    {
+      boardSize = (window.innerWidth >= 768
+        ? Math.min(600, 0.5*window.innerWidth) //heuristic...
+        : window.innerWidth);
+    }
+    const movesWidth = (window.innerWidth >= 768 ? 280 : 0);
+    document.getElementById("boardContainer").style.width = boardSize + "px";
+    let gameContainer = document.getElementById("gameContainer");
+    gameContainer.style.width = (boardSize + movesWidth) + "px";
+  },
   methods: {
     focusBg: function() {
       // TODO: small blue border appears...
@@ -106,6 +119,12 @@ export default {
           break;
       }
     },
+    handleScroll: function(e) {
+      if (e.deltaY < 0)
+        this.undo();
+      else if (e.deltaY > 0)
+        this.play();
+    },
     re_setVariables: function() {
       this.endgameMessage = "";
       this.orientation = this.game.mycolor || "w"; //default orientation for observed games
@@ -113,6 +132,8 @@ export default {
       // Post-processing: decorate each move with color + current FEN:
       // (to be able to jump to any position quickly)
       let vr_tmp = new V(this.game.fenStart); //vr is already at end of game
+      this.firstMoveNumber =
+        Math.floor(V.ParseFen(this.game.fenStart).movesCount / 2);
       this.moves.forEach(move => {
         // NOTE: this is doing manually what play() function below achieve,
         // but in a lighter "fast-forward" way
@@ -131,10 +152,11 @@ export default {
       this.cursor = L-1;
       this.lastMove = (L > 0 ? this.moves[L-1]  : null);
     },
-    gotoFenContent: function(event) {
-      const newUrl = "#/analyze/" + this.game.vname +
-        "/?fen=" + event.target.innerText.replace(/ /g, "_");
-      window.open(newUrl); //to open in a new tab
+    analyzePosition: function() {
+      const newUrl = "/analyze/" + this.game.vname +
+        "/?fen=" + this.vr.getFen().replace(/ /g, "_");
+      //window.open("#" + newUrl); //to open in a new tab
+      this.$router.push(newUrl); //better
     },
     download: function() {
       const content = this.getPgn();
@@ -194,7 +216,7 @@ export default {
       modalBox.checked = true;
       setTimeout(() => { modalBox.checked = false; }, 2000);
     },
-    animateMove: function(move) {
+    animateMove: function(move, callback) {
       let startSquare = document.getElementById(getSquareId(move.start));
       let endSquare = document.getElementById(getSquareId(move.end));
       let rectStart = startSquare.getBoundingClientRect();
@@ -219,66 +241,65 @@ export default {
         for (let i=0; i<squares.length; i++)
           squares.item(i).style.zIndex = "auto";
         movingPiece.style = {}; //required e.g. for 0-0 with KR swap
-        this.play(move);
+        callback();
       }, 250);
     },
-    play: function(move, receive, noanimate) {
+    play: function(move, receive) {
+      // NOTE: navigate and receive are mutually exclusive
       const navigate = !move;
-      // Forbid playing outside analyze mode when cursor isn't at moves.length-1
-      // (except if we receive opponent's move, human or computer)
-      if (!navigate && !this.analyze && !receive
+      // Forbid playing outside analyze mode, except if move is received.
+      // Sufficient condition because Board already knows which turn it is.
+      if (!navigate && this.game.mode!="analyze" && !receive
         && this.cursor < this.moves.length-1)
       {
         return;
       }
-      if (navigate)
-      {
-        if (this.cursor == this.moves.length-1)
-          return; //no more moves
-        move = this.moves[this.cursor+1];
-      }
-      if (!!receive && !noanimate) //opponent move, variant != "Dark"
-      {
-        if (this.cursor < this.moves.length-1)
+      const doPlayMove = () => {
+        if (!!receive && this.cursor < this.moves.length-1)
           this.gotoEnd(); //required to play the move
-        return this.animateMove(move);
-      }
-      if (!navigate)
-      {
-        move.color = this.vr.turn;
-        move.notation = this.vr.getNotation(move);
-      }
-      // Not programmatic, or animation is over
-      this.vr.play(move);
-      this.cursor++;
-      this.lastMove = move;
-      if (this.st.settings.sound == 2)
-        new Audio("/sounds/move.mp3").play().catch(err => {});
-      if (!navigate)
-      {
-        move.fen = this.vr.getFen();
-        if (this.game.score == "*" || this.analyze)
+        if (navigate)
+        {
+          if (this.cursor == this.moves.length-1)
+            return; //no more moves
+          move = this.moves[this.cursor+1];
+        }
+        else
+        {
+          move.color = this.vr.turn;
+          move.notation = this.vr.getNotation(move);
+        }
+        this.vr.play(move);
+        this.cursor++;
+        this.lastMove = move;
+        if (this.st.settings.sound == 2)
+          new Audio("/sounds/move.mp3").play().catch(err => {});
+        if (!navigate)
         {
+          move.fen = this.vr.getFen();
           // Stack move on movesList at current cursor
           if (this.cursor == this.moves.length)
             this.moves.push(move);
           else
             this.moves = this.moves.slice(0,this.cursor).concat([move]);
         }
-      }
-      if (!this.analyze)
-        this.$emit("newmove", move); //post-processing (e.g. computer play)
-      // Is opponent in check?
-      this.incheck = this.vr.getCheckSquares(this.vr.turn);
-      const score = this.vr.getCurrentScore();
-      if (score != "*")
-      {
-        const message = this.getScoreMessage(score);
-        if (!this.analyze)
-          this.$emit("gameover", score, message);
-        else //just show score on screen (allow undo)
-          this.showEndgameMsg(score + " . " + message);
-      }
+        if (!navigate && this.game.mode != "analyze")
+          this.$emit("newmove", move); //post-processing (e.g. computer play)
+        // Is opponent in check?
+        this.incheck = this.vr.getCheckSquares(this.vr.turn);
+        const score = this.vr.getCurrentScore();
+        if (score != "*")
+        {
+          const message = this.getScoreMessage(score);
+          if (this.game.mode != "analyze")
+            this.$emit("gameover", score, message);
+          else //just show score on screen (allow undo)
+            this.showEndgameMsg(score + " . " + message);
+        }
+      };
+      if (!!receive && this.game.vname != "Dark")
+        this.animateMove(move, doPlayMove);
+      else
+        doPlayMove();
     },
     undo: function(move) {
       const navigate = !move;
@@ -303,6 +324,8 @@ export default {
       this.lastMove = this.moves[index];
     },
     gotoBegin: function() {
+      if (this.cursor == -1)
+        return;
       this.vr.re_init(this.game.fenStart);
       if (this.moves.length > 0 && this.moves[0].notation == "...")
       {
@@ -316,6 +339,8 @@ export default {
       }
     },
     gotoEnd: function() {
+      if (this.cursor == this.moves.length - 1)
+        return;
       this.gotoMove(this.moves.length-1);
     },
     flip: function() {
@@ -326,27 +351,47 @@ export default {
 </script>
 
 <style lang="sass">
-#modal-eog+div .card
-  overflow: hidden
-#pgnDiv, #fenDiv
-  text-align: center
+#baseGame
+  width: 100%
+
+#gameContainer
   margin-left: auto
   margin-right: auto
+
+#modal-eog+div .card
+  overflow: hidden
 @media screen and (min-width: 768px)
   #controls
     width: 400px
-@media screen and (max-width: 767px)
-  .game
-    width: 100%
+    margin-left: auto
+    margin-right: auto
 #controls
   margin-top: 10px
+  margin-left: auto
+  margin-right: auto
   button
     display: inline-block
     width: 20%
     margin: 0
+#pgnDiv
+  text-align: center
+  margin-left: auto
+  margin-right: auto
 #boardContainer
-  //margin-top: 5px
-  >div
-    margin-left: auto
-    margin-right: auto
+  float: left
+#movesList
+  width: 280px
+  float: left
+@media screen and (max-width: 767px)
+  #movesList
+    width: 100%
+    float: none
+    clear: both
+    table
+      tr
+        display: flex
+        margin: 0
+        padding: 0
+        td
+          text-align: left
 </style>