width: 36px
height: 27px
+@media screen and (max-width: 767px)
+ nav
+ border: none
+
[type="checkbox"].drawer+*
right: -767px
& > p
display: inline-block
margin: 0 0 0 10px
+
+@media screen and (max-width: 767px)
+ footer
+ border: none
</style>
a#download(href="#")
button(@click="download") {{ st.tr["Download PGN"] }}
.col-sm-12.col-md-3
- MoveList(v-if="showMoves"
+ MoveList(v-if="showMoves" :score="game.score" :message="game.scoreMsg"
:moves="moves" :cursor="cursor" @goto-move="gotoMove")
</template>
moves: [],
cursor: -1, //index of the move just played
lastMove: null,
- gameHasEnded: false, //to avoid showing end message twice
};
},
watch: {
"game.moveToPlay": function() {
this.play(this.game.moveToPlay, "receive", this.game.vname=="Dark");
},
- "game.score": function(score) {
- if (!this.gameHasEnded && score != "*")
- {
- // "false" says "don't bubble up": the parent already knows
- this.endGame(score, this.game.scoreMsg, false);
- }
- },
},
computed: {
showMoves: function() {
//return window.innerWidth >= 768;
},
showFen: function() {
- return this.game.vname != "Dark" || this.score != "*";
+ return this.game.vname != "Dark" || this.game.score != "*";
},
analyze: function() {
- return this.game.mode == "analyze" || this.score != "*";
+ return this.game.mode == "analyze" || this.game.score != "*";
},
},
created: function() {
re_setVariables: function() {
this.endgameMessage = "";
this.orientation = this.game.mycolor || "w"; //default orientation for observed games
- this.score = this.game.score || "*"; //mutable (if initially "*")
- this.gameHasEnded = (this.score != "*");
this.moves = JSON.parse(JSON.stringify(this.game.moves || []));
// Post-processing: decorate each move with color + current FEN:
// (to be able to jump to any position quickly)
this.lastMove = (L > 0 ? this.moves[L-1] : null);
},
gotoFenContent: function(event) {
- this.$router.push("/analyze/" + this.game.vname +
- "/?fen=" + event.target.innerText.replace(/ /g, "_"));
+ const newUrl = "#/analyze/" + this.game.vname +
+ "/?fen=" + event.target.innerText.replace(/ /g, "_");
+ window.open(newUrl); //to open in a new tab
},
download: function() {
const content = this.getPgn();
pgn += '[White "' + this.game.players[0].name + '"]\n';
pgn += '[Black "' + this.game.players[1].name + '"]\n';
pgn += '[Fen "' + this.game.fenStart + '"]\n';
- pgn += '[Result "' + this.score + '"]\n\n';
+ pgn += '[Result "' + this.game.score + '"]\n\n';
let counter = 1;
let i = 0;
while (i < this.moves.length)
modalBox.checked = true;
setTimeout(() => { modalBox.checked = false; }, 2000);
},
- endGame: function(score, message, bubbleUp) {
- this.gameHasEnded = true;
- this.score = score;
- if (!message)
- message = this.getScoreMessage(score);
- this.showEndgameMsg(score + " . " + message);
- if (bubbleUp)
- this.$emit("gameover", score);
- },
animateMove: function(move) {
let startSquare = document.getElementById(getSquareId(move.start));
let endSquare = document.getElementById(getSquareId(move.end));
if (!navigate)
{
move.fen = this.vr.getFen();
- if (this.score == "*" || this.analyze)
+ if (this.game.score == "*" || this.analyze)
{
// Stack move on movesList at current cursor
if (this.cursor == this.moves.length)
const score = this.vr.getCurrentScore();
if (score != "*")
{
+ const message = this.getScoreMessage(score);
if (!this.analyze)
- this.endGame(score, undefined, true);
- else
- {
- // Just show score on screen (allow undo)
- const message = this.getScoreMessage(score);
+ this.$emit("gameover", score, message);
+ else //just show score on screen (allow undo)
this.showEndgameMsg(score + " . " + message);
- }
}
},
undo: function(move) {
width: 20%
margin: 0
#boardContainer
- margin-top: 5px
+ //margin-top: 5px
>div
margin-left: auto
margin-right: auto
this.playComputerMove();
}
},
- gameOver: function(score) {
+ gameOver: function(score, scoreMsg) {
this.game.score = score;
+ this.game.scoreMsg = scoreMsg;
this.game.mode = "analyze";
this.$emit("game-over", score); //bubble up to Rules.vue
},
+<template lang="pug">
+div
+ #scoreInfo(v-if="score!='*'")
+ p {{ score }}
+ p {{ message }}
+ table#movesList
+ tbody
+ tr(v-for="moveIdx in evenNumbers")
+ td {{ moveIdx / 2 + 1 }}
+ td(:class="{'highlight-lm': cursor == moveIdx}"
+ data-label="White move" @click="() => gotoMove(moveIdx)")
+ | {{ moves[moveIdx].notation }}
+ td(v-if="moveIdx < moves.length-1"
+ :class="{'highlight-lm': cursor == moveIdx+1}"
+ data-label="Black move" @click="() => gotoMove(moveIdx+1)")
+ | {{ moves[moveIdx+1].notation }}
+ // Else: just add an empty cell
+ td(v-else)
+</template>
+
<script>
// Component for moves list on the right
export default {
name: 'my-move-list',
- props: ["moves","cursor"],
- render(h) {
+ props: ["moves","cursor","score","message"],
+ watch: {
+ cursor: function(newValue) {
+ // $nextTick to wait for table > tr to be rendered
+ this.$nextTick( () => {
+ let rows = document.querySelectorAll('#movesList tr');
+ if (rows.length > 0)
+ {
+ rows[Math.floor(newValue/2)].scrollIntoView({
+ behavior: 'smooth',
+ block: 'center'
+ });
+ }
+ });
+ },
+ },
+ computed: {
+ evenNumbers: function() {
+ return [...Array(this.moves.length).keys()].filter(i => i%2==0);
+ },
+ },
+ methods: {
+ gotoMove: function(index) {
+ this.$emit("goto-move", index);
+ },
+ },
+};
+</script>
+
+<style lang="sass" scoped>
+.moves-list
+ min-width: 250px
+td.highlight-lm
+ background-color: plum
+</style>
+
+<!-- Old render method:
+ render(h) {
if (this.moves.length == 0)
return;
let tableContent = [];
}
tableRow.children = moveCells;
tableContent.push(tableRow);
+ const scoreDiv = h("div",
+ {
+ id: "scoreInfo",
+ style: {
+ display: this.score!="*" ? "block" : "none",
+ },
+ },
+ [
+ h("p", this.score),
+ h("p", this.message),
+ ]
+ );
const movesTable = h(
"div",
{ },
- [h(
- "table",
- {
- "class": {
- "moves-list": true,
+ [
+ scoreDiv,
+ h(
+ "table",
+ {
+ "class": {
+ "moves-list": true,
+ },
},
- },
- tableContent
- )]
+ tableContent
+ )
+ ]
);
return movesTable;
},
- methods: {
- gotoMove: function(index) {
- this.$emit("goto-move", index);
- },
- },
-};
-</script>
-
-<style lang="sass" scoped>
-.moves-list
- min-width: 250px
-td.highlight-lm
- background-color: plum
-</style>
+-->
<template lang="pug">
main
.row
- .col-sm-12.col-md-3
+ #chat.col-sm-12.col-md-4.col-md-offset-4
Chat(:players="game.players")
- .col-sm-12.col-md-9
- BaseGame(:game="game" :vr="vr" ref="basegame"
- @newmove="processMove" @gameover="gameOver")
.row
- .col-sm-12.col-md-9.col-md-offset-3
- .button-group(v-if="game.mode!='analyze' && game.score=='*'")
+ .col-sm-12
+ #actions(v-if="game.mode!='analyze' && game.score=='*'")
button(@click="offerDraw") Draw
button(@click="abortGame") Abort
button(@click="resign") Resign
div(v-if="game.score=='*'") Time: {{ virtualClocks[0] }} - {{ virtualClocks[1] }}
div(v-if="game.type=='corr'") {{ game.corrMsg }}
textarea(v-if="game.score=='*'" v-model="corrMsg")
+ BaseGame(:game="game" :vr="vr" ref="basegame"
+ @newmove="processMove" @gameover="gameOver")
</template>
<script>
{
clearInterval(clockUpdate);
if (countdown < 0)
- this.setScore(this.vr.turn=="w" ? "0-1" : "1-0", "Time");
+ this.gameOver(this.vr.turn=="w" ? "0-1" : "1-0", "Time");
}
else
{
break;
}
case "resign":
- this.setScore(data.side=="b" ? "1-0" : "0-1", "Resign");
+ this.gameOver(data.side=="b" ? "1-0" : "0-1", "Resign");
break;
case "abort":
- this.setScore("?", "Abort");
+ this.gameOver("?", "Abort");
break;
case "draw":
- this.setScore("1/2", "Mutual agreement");
+ this.gameOver("1/2", "Mutual agreement");
break;
case "drawoffer":
this.drawOffer = "received"; //TODO: observers don't know who offered draw
{
// Opponent resigned or aborted game, or accepted draw offer
// (this is not a stalemate or checkmate)
- this.setScore(data.score, "Opponent action");
+ this.gameOver(data.score, "Opponent action");
}
this.game.clocks = data.clocks; //TODO: check this?
if (!!data.lastMove.draw)
this.drawOffer = "received";
}
},
- setScore: function(score, message) {
- this.game.scoreMsg = message;
- this.$set(this.game, "score", score); //TODO: Vue3...
- },
offerDraw: function() {
if (this.drawOffer == "received")
{
if (p.sid != this.st.user.sid)
this.st.conn.send(JSON.stringify({code:"draw", target:p.sid}));
});
- this.setScore("1/2", "Mutual agreement");
+ this.gameOver("1/2", "Mutual agreement");
}
else if (this.drawOffer == "sent")
{
abortGame: function() {
if (!confirm(this.st.tr["Terminate game?"]))
return;
- this.setScore("?", "Abort");
+ this.gameOver("?", "Abort");
this.people.forEach(p => {
if (p.sid != this.st.user.sid)
{
side:this.game.mycolor, target:p.sid}));
}
});
- this.setScore(this.game.mycolor=="w" ? "0-1" : "1-0", "Resign");
+ this.gameOver(this.game.mycolor=="w" ? "0-1" : "1-0", "Resign");
},
// 3 cases for loading a game:
// - from indexedDB (running or completed live game I play)
if (this.repeat[repIdx] >= 3)
this.drawOffer = "received"; //TODO: will print "mutual agreement"...
},
- gameOver: function(score) {
+ gameOver: function(score, scoreMsg) {
this.game.mode = "analyze";
- this.game.score = score; //until Vue3, this property change isn't seen
- //by child (and doesn't need to be)
+ this.game.score = score;
+ this.game.scoreMsg = scoreMsg;
const myIdx = this.game.players.findIndex(p => {
return p.sid == this.st.user.sid || p.uid == this.st.user.id;
});
<style lang="sass">
.connected
background-color: green
-
.disconnected
background-color: red
-.white-turn
- background-color: white
+@media screen and (min-width: 768px)
+ #actions
+ width: 300px
+@media screen and (max-width: 767px)
+ .game
+ width: 100%
-.black-turn
- background-color: black
+#actions
+ margin-top: 10px
+ margin-left: auto
+ margin-right: auto
+ button
+ display: inline-block
+ width: 33%
+ margin: 0
+#chat
+ margin-top: 5px
+ margin-bottom: 5px
+ >.card
+ max-width: 100%
+ margin: 0;
+ border: none;
</style>