)
.card
label.modal-close(for="modalContact")
- form(@submit.prevent="trySendMessage()" @keyup.enter="trySendMessage()")
- fieldset
- label(for="userEmail") {{ st.tr["Email"] }}
- input#userEmail(type="email")
- fieldset
- label(for="mailSubject") {{ st.tr["Subject"] }}
- input#mailSubject(type="text")
- fieldset
- textarea#mailContent(:placeholder="st.tr['Your message']")
+ fieldset
+ label(for="userEmail") {{ st.tr["Email"] }}
+ input#userEmail(type="email")
+ fieldset
+ label(for="mailSubject") {{ st.tr["Subject"] }}
+ input#mailSubject(type="text")
+ fieldset
+ textarea#mailContent(:placeholder="st.tr['Your message']")
button(@click="trySendMessage()") {{ st.tr["Send"] }}
#dialog.text-center {{ st.tr[infoMsg] }}
</template>
)
.card
label.modal-close(for="modalLang")
- form(@change="setLanguage($event)")
- fieldset
- label(for="langSelect") {{ st.tr["Language"] }}
- select#langSelect
- each language,langCode in langName
- option(value=langCode)
- =language
+ fieldset(@change="setLanguage($event)")
+ label(for="langSelect") {{ st.tr["Language"] }}
+ select#langSelect
+ each language,langCode in langName
+ option(value=langCode)
+ =language
</template>
<script>
.card
label.modal-close(for="modalUser")
h3.section {{ st.tr[stage] }}
- form(@submit.prevent="onSubmit()" @keyup.enter="onSubmit()")
+ div(@keyup.enter="onSubmit()")
div(v-show="stage!='Login'")
fieldset
label(for="username") {{ st.tr["User name"] }}
if (e.code === "Escape") {
let modalBoxes = document.querySelectorAll("[id^='modal']");
modalBoxes.forEach(m => {
- if (m.checked && m.id != "modalWelcome") m.checked = false;
+ if (m.checked && m.id != "modalAccept") m.checked = false;
});
}
});
Abort: "Abort",
About: "About",
"Accept draw?": "Accept draw?",
+ "Accept challenge?": "Accept challenge?",
All: "All",
Analyse: "Analyse",
"Any player": "Any player",
"Processing... Please wait": "Processing... Please wait",
Problems: "Problems",
"participant(s):": "participant(s):",
+ Refuse: "Refuse",
Register: "Register",
"Registration complete! Please check your emails": "Registration complete! Please check your emails",
"Remove game?": "Remove game?",
"White win": "White win",
"Who's there?": "Who's there?",
With: "With",
+ with: "with",
"Write news": "Write news",
"Wrong time control": "Wrong time control",
"Your message": "Your message",
Abort: "Terminar",
About: "Acerca de",
"Accept draw?": "¿Acceptar tablas?",
+ "Accept challenge?": "¿Acceptar el desafío?",
All: "Todos",
Analyse: "Analizar",
"Any player": "Cualquier jugador",
"Processing... Please wait": "Procesando... por favor espere",
Problems: "Problemas",
"participant(s):": "participante(s):",
+ Refuse: "Rechazar",
Register: "Registrarse",
"Registration complete! Please check your emails": "¡Registro completo! Por favor revise sus correos electrónicos",
"Remove game?": "¿Eliminar la partida?",
"White win": "Las blancas gagnan",
"Who's there?": "¿Quién está ahí?",
With: "Con",
+ with: "con",
"Write news": "Escribir una news",
"Wrong time control": "Cadencia errónea",
"Your message": "Tu mensaje",
Abort: "Arrêter",
About: "À propos",
"Accept draw?": "Accepter la nulle ?",
+ "Accept challenge?": "Accepter le défi ?",
All: "Tous",
Analyse: "Analyser",
"Any player": "N'importe qui",
"Processing... Please wait": "Traitement en cours... Attendez SVP",
Problems: "Problèmes",
"participant(s):": "participant(s) :",
+ Refuse: "Refuser",
Register: "S'enregistrer",
"Registration complete! Please check your emails": "Enregistrement terminé ! Allez voir vos emails",
"Remove game?": "Supprimer la partie ?",
"White win": "Les blancs gagnent",
"Who's there?": "Qui est là ?",
With: "Avec",
+ with: "avec",
"Write news": "Écrire une news",
"Wrong time control": "Cadence erronée",
"Your message": "Votre message",
.text-center
input#fen(
v-model="curFen"
- @input="adjustFenSize()"
+ @input="adjustFenSize(); tryGotoFen()"
)
- button(@click="gotoFen()") {{ st.tr["Go"] }}
BaseGame(
:game="game"
:vr="vr"
let fenInput = document.getElementById("fen");
fenInput.style.width = this.curFen.length + "ch";
},
- gotoFen: function() {
- this.gameRef.fen = this.curFen;
- this.loadGame();
+ tryGotoFen: function() {
+ if (V.IsGoodFen(this.curFen))
+ {
+ this.gameRef.fen = this.curFen;
+ this.loadGame();
+ }
}
}
};
}
}
}
+ this.repeat = {}; //reset: scan past moves' FEN:
+ let repIdx = 0;
+ // NOTE: vr_tmp to obtain FEN strings is redundant with BaseGame
+ let vr_tmp = new V(game.fenStart);
+ let movesCount = -1;
+ let curTurn = "n";
+ game.moves.forEach(m => {
+ if (vr_tmp.turn != curTurn)
+ {
+ movesCount++;
+ curTurn = vr_tmp.turn;
+ }
+ vr_tmp.play(m);
+ const fenObj = V.ParseFen(vr_tmp.getFen());
+ repIdx = fenObj.position + "_" + fenObj.turn;
+ if (fenObj.flags) repIdx += "_" + fenObj.flags;
+ this.repeat[repIdx] = this.repeat[repIdx]
+ ? this.repeat[repIdx] + 1
+ : 1;
+ });
+ if (vr_tmp.turn != curTurn)
+ movesCount++;
+ if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
this.game = Object.assign(
{},
game,
// opponent sid not strictly required (or available), but easier
// at least oppsid or oppid is available anyway:
oppsid: myIdx < 0 ? undefined : game.players[1 - myIdx].sid,
- oppid: myIdx < 0 ? undefined : game.players[1 - myIdx].uid
+ oppid: myIdx < 0 ? undefined : game.players[1 - myIdx].uid,
+ movesCount: movesCount,
}
);
this.re_setClocks();
// Did lastate arrive before game was rendered?
if (this.lastate) this.processLastate();
});
- this.repeat = {}; //reset: scan past moves' FEN:
- let repIdx = 0;
- // NOTE: vr_tmp to obtain FEN strings is redundant with BaseGame
- let vr_tmp = new V(game.fenStart);
- game.moves.forEach(m => {
- vr_tmp.play(m);
- const fenObj = V.ParseFen(vr_tmp.getFen());
- repIdx = fenObj.position + "_" + fenObj.turn;
- if (fenObj.flags) repIdx += "_" + fenObj.flags;
- this.repeat[repIdx] = this.repeat[repIdx]
- ? this.repeat[repIdx] + 1
- : 1;
- });
- if (this.repeat[repIdx] >= 3) this.drawOffer = "threerep";
if (callback) callback();
};
if (game) {
}
},
re_setClocks: function() {
- if (this.game.moves.length < 2 || this.game.score != "*") {
+ if (this.game.movesCount < 2 || this.game.score != "*") {
// 1st move not completed yet, or game over: freeze time
this.virtualClocks = this.game.clocks.map(s => ppt(s));
return;
let addTime = 0;
if (move.color == this.game.mycolor) {
if (this.drawOffer == "received")
- //I refuse draw
+ // I refuse draw
this.drawOffer = "";
- if (this.game.moves.length >= 2) {
- //after first move
+ if (this.game.movesCount >= 2) {
const elapsed = Date.now() - this.game.initime[colorIdx];
// elapsed time is measured in milliseconds
addTime = this.game.increment - elapsed / 1000;
move.addTime = addTime;
} else addTime = move.addTime; //supposed transmitted
// Update current game object:
+ if (nextIdx != colorIdx)
+ this.game.movesCount++;
this.game.moves.push(move);
this.game.fen = move.fen;
this.game.clocks[colorIdx] += addTime;
// move.initime is set only when I receive a "lastate" move from opponent
this.game.initime[nextIdx] = move.initime || Date.now();
- //if (colorIdx != nextIdx)
- this.re_setClocks();
+ this.re_setClocks();
// If repetition detected, consider that a draw offer was received:
const fenObj = V.ParseFen(move.fen);
let repIdx = fenObj.position + "_" + fenObj.turn;
.card.text-center
label.modal-close(for="modalInfo")
p(v-html="infoMessage")
+ input#modalAccept.modal(type="checkbox")
+ div#acceptDiv(role="dialog")
+ .card.text-center
+ p
+ span.variantName {{ curChallToAccept.vname }}
+ span {{ curChallToAccept.cadence }}
+ span {{ st.tr["with"] + " " + curChallToAccept.from.name }}
+ .diagram(v-html="tchallDiag")
+ .button-group#buttonsTchall
+ button.acceptBtn(@click="decisionChallenge(true)") {{ st.tr["Accept challenge?"] }}
+ button.refuseBtn(@click="decisionChallenge(false)") {{ st.tr["Refuse"] }}
input#modalNewgame.modal(type="checkbox")
div#newgameDiv(
role="dialog"
)
.card
label#closeNewgame.modal-close(for="modalNewgame")
- form(@submit.prevent="newChallenge()" @keyup.enter="newChallenge()")
+ div(@keyup.enter="newChallenge()")
fieldset
label(for="selectVariant") {{ st.tr["Variant"] }} *
- select#selectVariant(v-model="newchallenge.vid")
+ select#selectVariant(
+ @change="loadNewchallVariant(trySetNewchallDiag)"
+ v-model="newchallenge.vid"
+ )
option(
v-for="v in st.variants"
:value="v.id"
v-model="newchallenge.to"
)
fieldset(v-if="st.user.id > 0 && newchallenge.to.length > 0")
- label(for="inputFen") FEN
input#inputFen(
+ placeholder="FEN"
+ @input="trySetNewchallDiag()"
type="text"
v-model="newchallenge.fen"
)
+ .diagram(v-html="newchallenge.diag")
button(@click="newChallenge()") {{ st.tr["Send challenge"] }}
input#modalPeople.modal(
type="checkbox"
import { ajax } from "@/utils/ajax";
import params from "@/parameters";
import { getRandString, shuffle } from "@/utils/alea";
+import { getDiagram } from "@/utils/printDiagram";
import Chat from "@/components/Chat.vue";
import GameList from "@/components/GameList.vue";
import ChallengeList from "@/components/ChallengeList.vue";
infoMessage: "",
newchallenge: {
fen: "",
- vid: localStorage.getItem("vid") || "",
+ vid: parseInt(localStorage.getItem("vid")) || 0,
to: "", //name of challenged player (if any)
- cadence: localStorage.getItem("cadence") || ""
+ cadence: localStorage.getItem("cadence") || "",
+ // VariantRules object, stored to not interfere with
+ // diagrams of targetted challenges:
+ V: null,
+ vname: "",
+ diag: "" //visualizing FEN
},
+ tchallDiag: "",
+ curChallToAccept: {from: {}},
newChat: "",
conn: null,
connexionString: "",
this.challenges.concat(this.games).forEach(o => {
if (o.vname == "") o.vname = this.getVname(o.vid);
});
+ if (!this.newchallenge.V && this.newchallenge.vid > 0)
+ this.loadNewchallVariant();
}
},
computed: {
}
},
created: function() {
+ if (this.st.variants.length > 0 && this.newchallenge.vid > 0)
+ this.loadNewchallVariant();
const my = this.st.user;
this.$set(this.people, my.sid, { id: my.id, name: my.name, pages: ["/"] });
// Ask server for current corr games (all but mines)
this.conn.addEventListener("close", this.socketCloseListener);
},
// Challenge lifecycle:
+ loadNewchallVariant: async function(cb) {
+ const vname = this.getVname(this.newchallenge.vid);
+ const vModule = await import("@/variants/" + vname + ".js");
+ this.newchallenge.V = vModule.VariantRules;
+ this.newchallenge.vname = vname;
+ if (cb)
+ cb();
+ },
+ trySetNewchallDiag: function() {
+ if (!this.newchallenge.fen) {
+ this.newchallenge.diag = "";
+ return;
+ }
+ // If vid > 0 then the variant is loaded (function above):
+ window.V = this.newchallenge.V;
+ if (
+ this.newchallenge.vid > 0 &&
+ this.newchallenge.fen &&
+ V.IsGoodFen(this.newchallenge.fen)
+ ) {
+ const parsedFen = V.ParseFen(this.newchallenge.fen);
+ this.newchallenge.diag = getDiagram({
+ position: parsedFen.position,
+ orientation: V.GetOppCol(parsedFen.turn)
+ });
+ }
+ },
newChallenge: async function() {
if (this.newchallenge.cadence.match(/^[0-9]+$/))
this.newchallenge.cadence += "+0"; //assume minutes, no increment
alert(error);
return;
}
- const vname = this.getVname(this.newchallenge.vid);
- const vModule = await import("@/variants/" + vname + ".js");
- window.V = vModule.VariantRules;
+ window.V = this.newchallenge.V;
error = checkChallenge(this.newchallenge);
if (error) {
alert(error);
});
// Add new challenge:
chall.from = {
- //decompose to avoid revealing email
+ // Decompose to avoid revealing email
sid: this.st.user.sid,
id: this.st.user.id,
name: this.st.user.name
chall.added = Date.now();
// NOTE: vname and type are redundant (can be deduced from cadence + vid)
chall.type = ctype;
- chall.vname = vname;
+ chall.vname = this.newchallenge.vname;
this.challenges.push(chall);
// Remember cadence + vid for quicker further challenges:
localStorage.setItem("cadence", chall.cadence);
});
}
},
- clickChallenge: function(c) {
+ // Callback function after a diagram was showed to accept
+ // or refuse targetted challenge:
+ decisionChallenge: function(accepted) {
+ this.curChallToAccept.accepted = accepted;
+ this.finishProcessingChallenge(this.curChallToAccept);
+ document.getElementById("modalAccept").checked = false;
+ },
+ finishProcessingChallenge: function(c) {
+ if (c.accepted) {
+ c.seat = {
+ // Again, avoid c.seat = st.user to not reveal email
+ sid: this.st.user.sid,
+ id: this.st.user.id,
+ name: this.st.user.name
+ };
+ this.launchGame(c);
+ } else {
+ this.send("refusechallenge", { data: c.id, target: c.from.sid });
+ }
+ this.send("deletechallenge", { data: c.id });
+ },
+ clickChallenge: async function(c) {
const myChallenge =
c.from.sid == this.st.user.sid || //live
(this.st.user.id > 0 && c.from.id == this.st.user.id); //corr
return;
}
c.accepted = true;
+ const vModule = await import("@/variants/" + c.vname + ".js");
+ window.V = vModule.VariantRules;
if (c.to) {
- //c.to == this.st.user.name (connected)
- // TODO: if special FEN, show diagram after loading variant
- c.accepted = confirm("Accept challenge?");
- }
- if (c.accepted) {
- c.seat = {
- //again, avoid c.seat = st.user to not reveal email
- sid: this.st.user.sid,
- id: this.st.user.id,
- name: this.st.user.name
- };
- this.launchGame(c);
- } else {
- this.send("refusechallenge", { data: c.id, target: c.from.sid });
+ // c.to == this.st.user.name (connected)
+ if (c.fen) {
+ const parsedFen = V.ParseFen(c.fen);
+ this.tchallDiag = getDiagram({
+ position: parsedFen.position,
+ orientation: V.GetOppCol(parsedFen.turn)
+ });
+ this.curChallToAccept = c;
+ document.getElementById("modalAccept").checked = true;
+ }
+ else {
+ if (!confirm(this.st.tr["Accept challenge?"]))
+ c.accepted = false;
+ this.finishProcessingChallenge(c);
+ }
}
- this.send("deletechallenge", { data: c.id });
- } //my challenge
+ else
+ this.finishProcessingChallenge(c);
+ }
else {
+ // My challenge
if (c.type == "corr") {
ajax("/challenges", "DELETE", { id: c.id });
}
ArrayFun.remove(this.challenges, ch => ch.id == c.id);
},
// NOTE: when launching game, the challenge is already being deleted
- launchGame: async function(c) {
- const vModule = await import("@/variants/" + c.vname + ".js");
- window.V = vModule.VariantRules;
+ launchGame: function(c) {
// These game informations will be shared
let gameInfo = {
id: getRandString(),
padding: 15px 0
max-width: 430px
-#newgameDiv > .card
+#newgameDiv > .card, #acceptDiv > .card
max-width: 767px
max-height: 100%
.tabbtn
background-color: #f9faee
+button.acceptBtn
+ background-color: lightgreen
+button.refuseBtn
+ background-color: red
+
+#buttonsTchall
+ margin-top: 10px
+
+.variantName
+ font-weight: bold
+
+.diagram
+ margin: 0 auto
+ max-width: 400px
+
+#inputFen
+ width: 100%
+
#div2, #div3
margin-top: 15px
@media screen and (max-width: 767px)