-Sur index, introduction menu remplacé par "mes parties", montrant parties (corr) en cours toutes variantes confondues
+tell opponent that I got the move, for him to start timer (and lose...)
+ --> no, not needed and impossible if everybody is offline
+ ==> just store this time locally (cheating possible but...)
+board2, board3, board4
+VariantRules2, 3 et 4 aussi
+fetch challenges and corr games from server at startup (room)
+but forbid anonymous to start corr games or accept challenges
+
Dans variant page, "mes parties" peut toujours contenir corr + importées (deux onglets)
En fin de partie (observée ou non), bouton "import game" en + de "download game" ==> directement dans indexedDB
--> sursis de 7 jours pour les parties par correspondance, qui sont encore chargées depuis le serveur
// TODO: decodeURIComponent() for GET/DELETE parameters
-1) Finish problems tab
2) Integrate computer play into rules tab
3) Allow correspondance play (no need for P2P: online moves through the server (which also store them))
4) Write my-games tab (included current/finished/imported)
Chat button should be more apparent after game ends (color ?)
Reinforce security for problems upload (how ?)
-The mode switch between human/computer/friend (+ problem) is a mess
-(example: finished computer game, ongoing friend game, reload, friend game is unreachable)
-
Later:
-Let choice of time control, allow correspondance play, several games at the same time
+Let choice of time control, allow correspondance play, several corr games at the same time
==> need to use indexedDB instead of localStorage. Maybe with Dexie https://dexie.org/
Each user would have a unique identifier stored in the client DB.
Allow to cancel games (if opponent doesn't connect again)
-Identity would be browser-based: different games on smartphone, home computer, work computer... (why not ?)
-Index might still look the same, and variant page would have another tab "Games"
-==> running, and finished (which can be deleted from local memory)
-(A true analysis mode could be implemented also, to navigate in completed games --> use a button)
+Live games storage would be browser-based: different games on smartphone, home computer, work computer... (why not ?)
+==> (at most 1) running, and finished (which can be deleted from local memory)
Allow challenging a specific player (by his chosen name)
-But keep the random pairings as main playing way + always playing in ZEN mode,
-except when accepting an individual challenge.
+But keep the random pairings as main playing way + always playing in ZEN mode
style menu : surligner onglet courant
Interface :
- newGame: une modalBox à paramètres, timeControl, type d'adversaire ==> "new Game")
- - friend-->renommé en 'analyse' et devenant un vrai mode analyse (on garde ces trois modes ?)
-
-problèmes : récupérer 20 ou 50 depuis le serveur, puis les afficher un par un en les analysant directement,
-comme sur le site de ProgramFOX ==> présentation unifiée échiquier avec instructions dessus et soluce cachée dessous
-
-==> il faut pouvoir faire "new Interface(variables)" pour lancer une analyse de problème sans repasser par le mode jeu...
Importer des parties : nécessite de parser le PGN produit (possible, un peu pénible)
mais permettrait mode analyse (avec bouton "analyse", comme sur ancien site).
fin de la partida au lieu de final de partida ?
Bouton new game ==> human only. Indiquer adversaire (éventuellement), cadence (ou "infini")
-Mode analyse : accessible à tout moment d'une partie (HH, ou computer) terminée.
+Mode analyse : accessible à tout moment d'une partie (HH, ou computer) terminée + bouton "analyze from here" (sur parties observées)
Coordonnées sur échiquier: sur cases, à gauche (verticale) ou en bas (horizontale)
Import game : en local dans indexedDb, affichage dans "Games --> Imported"
-Checkered : si intervention d'un 3eme joueur, initialiser son temps à la moyenne des temps restants des deux autres...
+Checkered : si intervention d'un 3eme joueur, initialiser son temps à la moyenne des temps restants des deux autres... ?
Mode contre ordinateur : seulement accessible depuis onglet "Rules" (son principal intérêt)
}}
}}
+let x=100, y=100, size=40;
+ctx.beginPath();
+ctx.moveTo(x + size * Math.cos(0), y + size * Math.sin(0));
+for (let side=0; side < 7; side++) {
+ ctx.lineTo(x + size * Math.cos(side * 2 * Math.PI / 6), y + size * Math.sin(side * 2 * Math.PI / 6));
+}
+ctx.fillStyle = "#333333";
+ctx.fill();
+
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0, 60, 60);
}
img.src = "public/images/pieces/wb.svg";
-
</script>
</body>
*/
// TODO: callback ?
-exports.create = function(vname, fen, instructions, solution)
-{
- db.serialize(function() {
- const vidQuery =
- "SELECT id " +
- "FROM Variants " +
- "WHERE name = '" + vname + "'";
- db.get(vidQuery, (err,variant) => {
- const insertQuery =
- "INSERT INTO Problems (added, vid, fen, instructions, solution) VALUES " +
- "(" +
- Date.now() + "," +
- variant.id + "," +
- fen + "," +
- instructions + "," +
- solution +
- ")";
- db.run(insertQuery);
- });
- });
-}
-
-exports.getById = function(id, callback)
+exports.create = function(vid, fen, instructions, solution)
{
db.serialize(function() {
const query =
- "SELECT * FROM Problems " +
- "WHERE id ='" + id + "'";
- db.get(query, callback);
+ "INSERT INTO Problems (added, vid, fen, instructions, solution) VALUES " +
+ "(" +
+ Date.now() + "," +
+ vid + "," +
+ fen + "," +
+ instructions + "," +
+ solution +
+ ")";
+ db.run(query);
});
}
-exports.getOne = function(vname, pid, callback)
+exports.getOne = function(id, callback)
{
db.serialize(function() {
const query =
"SELECT * " +
"FROM Problems " +
- "WHERE id = " + pid;
+ "WHERE id = " + id;
db.get(query, callback);
});
}
-exports.fetchN = function(vname, uid, type, directionStr, lastDt, MaxNbProblems, callback)
+exports.fetchN = function(vid, uid, type, directionStr, lastDt, MaxNbProblems, callback)
{
db.serialize(function() {
let typeLine = "";
typeLine = "AND id " + (type=="others" ? "!=" : "=") + " " + uid;
const query =
"SELECT * FROM Problems " +
- "WHERE vid = (SELECT id FROM Variants WHERE name = '" + vname + "') " +
+ "WHERE vid = " + vid +
" AND added " + directionStr + " " + lastDt + " " + typeLine + " " +
"ORDER BY added " + (directionStr=="<" ? "DESC " : "") +
"LIMIT " + MaxNbProblems;
'incheck': showLight && incheckSq[ci][cj],
},
attrs: {
- id: this.getSquareId({x:ci,y:cj}),
+ id: getSquareId({x:ci,y:cj}),
},
},
elems
myReservePiecesArray.push(h('div',
{
'class': {'board':true, ['board'+sizeY]:true},
- attrs: { id: this.getSquareId({x:sizeX+shiftIdx,y:i}) }
+ attrs: { id: getSquareId({x:sizeX+shiftIdx,y:i}) }
},
[
h('img',
oppReservePiecesArray.push(h('div',
{
'class': {'board':true, ['board'+sizeY]:true},
- attrs: { id: this.getSquareId({x:sizeX+(1-shiftIdx),y:i}) }
+ attrs: { id: getSquareId({x:sizeX+(1-shiftIdx),y:i}) }
},
[
h('img',
);
},
methods: {
- // Get the identifier of a HTML square from its numeric coordinates o.x,o.y.
- getSquareId: function(o) {
- // NOTE: a separator is required to allow any size of board
- return "sq-" + o.x + "-" + o.y;
- },
- // Inverse function
- getSquareFromId: function(id) {
- let idParts = id.split('-');
- return [parseInt(idParts[1]), parseInt(idParts[2])];
- },
mousedown: function(e) {
e = e || window.event;
let ingame = false;
this.selectedPiece.style.top = 0;
this.selectedPiece.style.display = "inline-block";
this.selectedPiece.style.zIndex = 3000;
- const startSquare = this.getSquareFromId(e.target.parentNode.id);
+ const startSquare = getSquareFromId(e.target.parentNode.id);
this.possibleMoves = [];
const color = this.mode=="analyze" || this.gameOver
? this.vr.turn
return;
}
// OK: process move attempt
- let endSquare = this.getSquareFromId(landing.id);
+ let endSquare = getSquareFromId(landing.id);
let moves = this.findMatchingMoves(endSquare);
this.possibleMoves = [];
if (moves.length > 1)
Vue.component('my-game', {
// gameId: to find the game in storage (assumption: it exists)
// fen: to start from a FEN without identifiers (analyze mode)
- props: ["conn","gameId","fen","mode","allowChat","allowMovelist"],
+ props: ["conn","gameId","fen","mode","allowChat","allowMovelist","queryHash","settings"],
data: function() {
return {
oppConnected: false, //TODO?
- // sound level: 0 = no sound, 1 = sound only on newgame, 2 = always
- sound: parseInt(localStorage["sound"] || "2"),
// Web worker to play computer moves without freezing interface:
compWorker: new Worker('/javascripts/playCompMove.js'),
timeStart: undefined, //time when computer starts thinking
endgameMessage: "",
orientation: "w",
- // if oppid == "computer" then mode = "computer" (otherwise human)
oppid: "", //opponent ID in case of HH game
score: "*", //'*' means 'unfinished'
- // userColor: given by gameId, or fen (if no game Id)
+ // userColor: given by gameId, or fen in problems mode (if no game Id)...
mycolor: "w",
fenStart: "",
moves: [], //TODO: initialize if gameId is defined...
watch: {
fen: function(newFen) {
this.vr = new VariantRules(newFen);
+ this.moves = [];
+ this.cursor = 0;
+ this.fenStart = newFen;
+ this.score = "*";
+ if (this.mode == "analyze")
+ {
+ this.mycolor = V.ParseFen(newFen).turn;
+ this.orientation = "w"; //convention (TODO?!)
+ }
+ else if (this.mode == "computer") //only other alternative (HH with gameId)
+ {
+ this.mycolor = (Math.random() < 0.5 ? "w" : "b");
+ this.orientation = this.mycolor;
+ this.compWorker.postMessage(["init",newFen]);
+ }
},
gameId: function() {
this.loadGame();
},
+ queryHash: function(newQhash) {
+ // New query hash = "id=42"; get 42 as gameId
+ this.gameId = parseInt(newQhash.substr(2));
+ this.loadGame();
+ },
},
computed: {
showChat: function() {
</div>
<my-chat v-if="showChat">
</my-chat>
- <my-board v-bind:vr="vr" :last-move="lastMove" :mode="mode" :orientation="orientation" :user-color="mycolor" @play-move="play">
+ <my-board v-bind:vr="vr" :last-move="lastMove" :mode="mode"
+ :orientation="orientation" :user-color="mycolor" :settings="settings"
+ @play-move="play">
</my-board>
<div class="button-group">
<button @click="() => play()">Play</button>
this.conn.addEventListener('message', socketMessageListener);
this.conn.addEventListener('close', socketCloseListener);
};
- this.conn.onmessage = socketMessageListener;
- this.conn.onclose = socketCloseListener;
+ if (!!this.conn)
+ {
+ this.conn.onmessage = socketMessageListener;
+ this.conn.onclose = socketCloseListener;
+ }
// Computer moves web worker logic: (TODO: also for observers in HH games)
this.compWorker.postMessage(["scripts",variant.name]);
this.compWorker.postMessage(["askmove"]);
},
animateMove: function(move) {
- let startSquare = document.getElementById(this.getSquareId(move.start));
- let endSquare = document.getElementById(this.getSquareId(move.end));
+ let startSquare = document.getElementById(getSquareId(move.start));
+ let endSquare = document.getElementById(getSquareId(move.end));
let rectStart = startSquare.getBoundingClientRect();
let rectEnd = endSquare.getBoundingClientRect();
let translation = {x:rectEnd.x-rectStart.x, y:rectEnd.y-rectStart.y};
let movingPiece =
- document.querySelector("#" + this.getSquareId(move.start) + " > img.piece");
+ document.querySelector("#" + getSquareId(move.start) + " > img.piece");
// HACK for animation (with positive translate, image slides "under background")
// Possible improvement: just alter squares on the piece's way...
squares = document.getElementsByClassName("board");
for (let i=0; i<squares.length; i++)
{
let square = squares.item(i);
- if (square.id != this.getSquareId(move.start))
+ if (square.id != getSquareId(move.start))
square.style.zIndex = "-1";
}
movingPiece.style.transform = "translate(" + translation.x + "px," +
this.lastMove = move;
if (!move.fen)
move.fen = this.vr.getFen();
- if (this.sound == 2)
+ if (this.settings.sound == 2)
new Audio("/sounds/move.mp3").play().catch(err => {});
if (this.mode == "human")
{
updateStorage(move); //after our moves and opponent moves
- if (this.vr.turn == this.userColor)
+ if (this.vr.turn == this.mycolor)
this.conn.send(JSON.stringify({code:"newmove", move:move, oppid:this.oppid}));
}
else if (this.mode == "computer")
this.showScoreMsg(score);
// TODO: notify end of game (give score)
}
- else if (this.mode == "computer" && this.vr.turn != this.userColor)
+ else if (this.mode == "computer" && this.vr.turn != this.mycolor)
this.playComputerMove();
// https://vuejs.org/v2/guide/list.html#Caveats (also for undo)
if (navigate)
this.lastMove = (this.cursor > 0 ? this.moves[this.cursor-1] : undefined);
if (navigate)
this.$children[0].$forceUpdate(); //TODO!?
- if (this.sound == 2)
+ if (this.settings.sound == 2)
new Audio("/sounds/undo.mp3").play().catch(err => {});
this.incheck = this.vr.getCheckSquares(this.vr.turn);
if (!navigate && this.mode == "analyze")
Vue.component('my-problems', {
+ props: ["queryHash","settings"],
data: function () {
return {
userId: user.id,
{{ curProb.instructions }}
</p>
</div>
- <my-game :fen="curProb.fen" :mode="analyze" :allowMovelist="true">
+ <my-game :fen="curProb.fen" :mode="analyze" :allowMovelist="true" :settings="settings">
</my-board>
<div id="solution-div" class="section-content">
<h3 class="clickable" @click="showSolution = !showSolution">
</div>
</div>
`,
+ watch: {
+ queryHash: function(newQhash) {
+ if (!!newQhash)
+ {
+ // New query hash = "id=42"; get 42 as problem ID
+ const pid = parseInt(newQhash.substr(2));
+ this.showProblem(pid);
+ }
+ else
+ this.curProb = null; //(back to) list display
+ },
+ },
created: function() {
- // TODO: adapt this, #problems:28 ? (for example)
- if (location.hash.length > 0)
- this.showProblem(location.hash.slice(1));
+ if (!!this.queryHash)
+ {
+ const pid = parseInt(this.queryHash.substr(2));
+ this.showProblem(pid);
+ }
else
this.firstFetch();
},
}
}
ajax(
- "/problems/" + variant.name, //TODO: use variant._id ?
+ "/problems/" + variant.id,
"GET",
{
type: type,
},
deleteProblem: function(pid) {
ajax(
- "/problems/" + variant.name + "/" + pid, //TODO: with variant.id ?
+ "/problems/" + variant.id + "/" + pid,
"DELETE",
response => {
// Delete problem from the list on client side
sendProblem: function() {
// Send it to the server and close modal
ajax(
- "/problems/" + variant.name, //TODO: with variant.id ?
+ "/problems/" + variant.id,
(this.modalProb.id > 0 ? "PUT" : "POST"),
this.modalProb,
response => {
// Load rules on variant page
Vue.component('my-rules', {
+ props: ["settings"],
data: function() {
- return { content: "" };
+ return {
+ content: "",
+ display: "rules",
+ mode: "computer",
+ mycolor: "w",
+ allowMovelist: true,
+ fen: "",
+ };
},
+
+ // TODO: third button "see a sample game" (comp VS comp)
+
template: `
<div class="col-sm-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
- <div v-html="content" class="section-content"></div>
+ <div class="button-group">
+ <button @click="display='rules'">
+ Read the rules
+ </button>
+ <button @click="startComputerGame()">
+ Beat the computer!
+ </button>
+ </div>
+ <div v-show="display=='rules'" v-html="content" class="section-content"></div>
+ <my-game v-show="display=='computer'" :mycolor="mycolor" :settings="settings"
+ :allow-movelist="allowMovelist" :mode="mode" :fen="fen">
+ </my-game>
</div>
`,
mounted: function() {
shadow: fenParts[3],
};
},
+ startComputerGame: function() {
+ this.fen = V.GenRandInitFen();
+ this.display = "computer";
+ },
},
})
--- /dev/null
+// Get the identifier of a HTML square from its numeric coordinates o.x,o.y.
+function getSquareId(o)
+{
+ // NOTE: a separator is required to allow any size of board
+ return "sq-" + o.x + "-" + o.y;
+}
+
+// Inverse function
+function getSquareFromId(id) {
+ let idParts = id.split('-');
+ return [parseInt(idParts[1]), parseInt(idParts[2])];
+}
data: {
display: "undefined", //default to main hall; see "created()" function
gameid: undefined, //...yet
-
+ queryHash: "",
conn: null,
+ // Settings initialized with values from localStorage
+ settings: {
+ bcolor: localStorage["bcolor"] || "lichess",
+ sound: parseInt(localStorage["sound"]) || 2,
+ hints: parseInt(localStorage["hints"]) || 1,
+ coords: !!eval(localStorage["coords"]),
+ highlight: !!eval(localStorage["highlight"]),
+ sqSize: parseInt(localStorage["sqSize"]),
+ },
+
// TEMPORARY: DEBUG
mode: "analyze",
orientation: "w",
userColor: "w",
-
allowChat: false,
allowMovelist: true,
fen: V.GenRandInitFen(),
},
created: function() {
- // TODO: navigation becomes a little more complex
- this.setDisplay();
+ if (!!localStorage["variant"])
+ {
+ location.hash = "#game?id=" + localStorage["gameId"];
+ this.display = location.hash.substr(1);
+ }
+ else
+ this.setDisplay();
window.onhashchange = this.setDisplay;
-
this.myid = "abcdefghij";
//console.log(this.myid + " " + variant);
//myid: localStorage.getItem("myid"), //our ID, always set
//this.vr = new VariantRules( V.GenRandInitFen() );
},
methods: {
+ updateSettings: function(event) {
+ const propName =
+ event.target.id.substr(3).replace(/^\w/, c => c.toLowerCase())
+ localStorage[propName] = ["highlight","coords"].includes(propName)
+ ? event.target.checked
+ : event.target.value;
+ },
setDisplay: function() {
-
-//TODO: prevent set display if there is a running game
-
+ // Prevent set display if there is a running game
+ if (!!localStorage["variant"])
+ return;
if (!location.hash)
location.hash = "#room"; //default
- this.display = location.hash.substr(1);
+ const hashParts = location.hash.substr(1).split("?");
+ this.display = hashParts[0];
+ this.queryHash = hashParts[1]; //may be empty, undefined...
// Close menu on small screens:
let menuToggle = document.getElementById("drawer-control");
if (!!menuToggle)
menuToggle.checked = false;
},
-
- // TEMPORARY: DEBUG (duplicate code)
- play: function(move) {
- // Not programmatic, or animation is over
- if (!move.notation)
- move.notation = this.vr.getNotation(move);
- this.vr.play(move);
- if (!move.fen)
- move.fen = this.vr.getFen();
- if (this.sound == 2)
- new Audio("/sounds/move.mp3").play().catch(err => {});
- // Is opponent in check?
- this.incheck = this.vr.getCheckSquares(this.vr.turn);
- const score = this.vr.getCurrentScore();
- },
- undo: function(move) {
- this.vr.undo(move);
- if (this.sound == 2)
- new Audio("/sounds/undo.mp3").play().catch(err => {});
- this.incheck = this.vr.getCheckSquares(this.vr.turn);
- },
},
});
//const continuation = (localStorage.getItem("variant") === variant.name);
// if (continuation) //game VS human has priority
// this.continueGame("human");
-
-// TODO:
-// si quand on arrive il y a une continuation "humaine" : display="game" et retour à la partie !
+++ /dev/null
-tell opponent that I got the move, for him to start timer (and lose...)
- --> no, not needed and impossible if everybody is offline
- ==> just store this time locally (cheating possible but...)
-board2, board3, board4
-VariantRules2, 3 et 4 aussi
-fetch challenges and corr games from server at startup (room)
-but forbid anonymous to start corr games or accept challenges
router.use("/", require("./index"));
router.use("/", require("./users"));
-router.use("/", require("./problems"));
router.use("/", require("./messages"));
-//router.use("/", require("./challenge"));
//router.use("/", require("./playing"));
+//router.use("/", require("./challenge"));
+router.use("/", require("./problems"));
router.use("/", require("./variant"));
module.exports = router;
+// TODO: adapt this (from Mongo to SQLite, and challenge format changed) for corr play
+
var router = require("express").Router();
var ObjectID = require("bson-objectid");
var ChallengeModel = require('../models/Challenge');
var ObjectID = require("bson-objectid");
var access = require("../utils/access");
-// Only AJAX requests here (from variant page and index)
-
// variant page
router.get("/challengesbyvariant", access.logged, access.ajax, (req,res) => {
if (req.query["uid"] != req.user._id)
+// Main index page
+
let router = require("express").Router();
const VariantModel = require("../models/Variant");
const selectLanguage = require("../utils/language.js");
+// Router for contact form sending
+
let router = require("express").Router();
const mailer = require(__dirname.replace("/routes", "/utils/mailer"));
+// TODO: adapt for correspondance play
+
var router = require("express").Router();
var UserModel = require("../models/User");
var GameModel = require('../models/Game');
const sanitizeHtml = require('sanitize-html');
const MaxNbProblems = 20;
-// Get one problem
-router.get("/problems/:vname([a-zA-Z0-9]+)/:pnum([0-9]+)", access.ajax, (req,res) => {
- const vname = req.params["vname"];
- const pnum = req.params["pnum"];
- ProblemModel.getOne(vname, pnum, (err,problem) => {
+function sanitizeUserInput(fen, instructions, solution)
+{
+ if (!fen.match(/^[a-zA-Z0-9, /-]*$/))
+ return "Bad characters in FEN string";
+ instructions = sanitizeHtml(instructions);
+ solution = sanitizeHtml(solution);
+ if (instructions.length == 0)
+ return "Empty instructions";
+ if (solution.length == 0)
+ return "Empty solution";
+ return {
+ fen: fen,
+ instructions: instructions,
+ solution: solution
+ };
+}
+
+// Get one problem (TODO: vid unused, here for URL de-ambiguification)
+router.get("/problems/:vid([0-9]+)/:id([0-9]+)", access.ajax, (req,res) => {
+ const pid = req.params["id"];
+ ProblemModel.getOne(pid, (err,problem) => {
if (!!err)
return res.json(err);
return res.json({problem: problem});
});
// Fetch N previous or next problems
-router.get("/problems/:vname([a-zA-Z0-9]+)", access.ajax, (req,res) => {
- const vname = req.params["vname"];
+router.get("/problems/:vid([0-9]+)", access.ajax, (req,res) => {
+ const vid = req.params["vid"];
const directionStr = (req.query.direction == "forward" ? ">" : "<");
const lastDt = req.query.last_dt;
const type = req.query.type;
return res.json({errmsg: "Bad timestamp"});
if (!["others","mine"].includes(type))
return res.json({errmsg: "Bad type"});
- ProblemModel.fetchN(vname, req.userId, type, directionStr, lastDt, MaxNbProblems,
+ ProblemModel.fetchN(vid, req.userId, type, directionStr, lastDt, MaxNbProblems,
(err,problems) => {
if (!!err)
return res.json(err);
);
});
-function sanitizeUserInput(fen, instructions, solution)
-{
- if (!fen.match(/^[a-zA-Z0-9, /-]*$/))
- return "Bad characters in FEN string";
- instructions = sanitizeHtml(instructions);
- solution = sanitizeHtml(solution);
- if (instructions.length == 0)
- return "Empty instructions";
- if (solution.length == 0)
- return "Empty solution";
- return {
- fen: fen,
- instructions: instructions,
- solution: solution
- };
-}
-
// Upload a problem (sanitize inputs)
-router.post("/problems/:vname([a-zA-Z0-9]+)", access.logged, access.ajax, (req,res) => {
- const vname = req.params["vname"];
+router.post("/problems/:vid([0-9]+)", access.logged, access.ajax, (req,res) => {
+ const vid = req.params["vid"];
const s = sanitizeUserInput(req.body["fen"], req.body["instructions"], req.body["solution"]);
if (typeof s === "string")
return res.json({errmsg: s});
- ProblemModel.create(vname, s.fen, s.instructions, s.solution);
+ ProblemModel.create(vid, s.fen, s.instructions, s.solution);
res.json({});
});
+// AJAX methods to get, create, update or delete a user
+
var router = require("express").Router();
var UserModel = require('../models/User');
var sendEmail = require('../utils/mailer');
});
}
-// AJAX user life cycle...
-
router.post('/register', access.unlogged, access.ajax, (req,res) => {
const name = req.body.name;
const email = req.body.email;
+// (any) variant page (with room, games, problems ...)
+
let router = require("express").Router();
const createError = require('http-errors');
const VariantModel = require("../models/Variant");
const selectLanguage = require("../utils/language.js");
const access = require("../utils/access");
-router.get("/:variant([a-zA-Z0-9]+)", (req,res,next) => {
- const vname = req.params["variant"];
+router.get("/:vname([a-zA-Z0-9]+)", (req,res,next) => {
+ const vname = req.params["vname"];
VariantModel.getByName(vname, (err,variant) => {
if (!!err)
return next(err);
input#modalSettings.modal(type="checkbox")
div(role="dialog" aria-labelledby="settingsTitle")
- .card.smallpad(onChange="blabla(event)")
+ .card.smallpad(@change="updateSettings")
label.modal-close(for="modalSettings")
h3#settingsTitle.section= translations["Preferences"]
- // taille echiquier : TODO
fieldset
- label(for="setHints")= translations["Show hints?"]
- // TODO: this.hints will not work. Idea: query storage in a generic way ?
- // --> getValue("hints") par exemple
- input#setHints(type="checkbox" checked=this.hints)
+ label(for="setSqSize")= translations["Square size (in pixels). 0 for 'adaptative'"]
+ input#setSqSize(type="number" v-model="settings.sqSize")
+ fieldset
+ label(for="selectHints")= translations["Show move hints?"]
+ select#setHints(v-model="settings.hints")
+ option(value="0")= translations["None"]
+ option(value="1")= translations["Moves from a square"]
+ option(value="2")= translations["Pieces which can move"]
+ fieldset
+ label(for="setHighlight")= translations["Highlight squares? (Last move & checks)"]
+ input#setHighlight(type="checkbox" v-model="settings.highlight")
+ fieldset
+ label(for="setCoords")= translations["Show board coordinates?"]
+ input#setCoords(type="checkbox" v-model="settings.coords")
fieldset
label(for="selectColor")= translations["Board colors"]
- select#selectColor
- option(value="lichess" selected="this.color=='lichess'")
+ select#setBcolor(v-model="settings.bcolor")
+ option(value="lichess")
= translations["brown"]
- option(value="chesscom" selected="this.color=='chesscom'")
+ option(value="chesscom")
= translations["green"]
- option(value="chesstempo" selected="this.color=='chesstempo'")
+ option(value="chesstempo")
= translations["blue"]
fieldset
label(for="selectSound")= translations["Play sounds?"]
- select#selectSound
- option(value="0" selected="this.sound==0")= translations["None"]
- option(value="1" selected="this.sound==1")= translations["New game"]
- option(value="2" selected="this.sound==2")= translations["All"]
+ select#setSound(v-model="settings.sound")
+ option(value="0")= translations["None"]
+ option(value="1")= translations["New game"]
+ option(value="2")= translations["All"]
.row
//my-room(v-show="display=='room'")
//my-game-list(v-show="display=='gameList'")
- my-rules(v-show="display=='rules'")
- //my-problems(v-show="display=='problems'")
+ my-rules(v-show="display=='rules'" :settings="settings")
+ //my-problems(v-show="display=='problems'" :query-hash="queryHash")
my-game(v-show="display=='game'" :game-id="gameid" :conn="conn"
:allow-chat="allowChat" :allow-movelist="allowMovelist"
- :mode="mode" :fen="fen")
+ :mode="mode" :fen="fen" :query-hash="queryHash")
//my-board(:vr="vr" :mode="mode" :orientation="orientation"
:user-color="userColor" v-on:play-move="play")
script(src="/javascripts/utils/array.js")
script(src="/javascripts/utils/printDiagram.js")
script(src="/javascripts/utils/datetime.js")
+ script(src="/javascripts/utils/squareId.js")
script(src="/javascripts/socket_url.js")
script(src="/javascripts/base_rules.js")
script(src="/javascripts/settings.js")