+++ /dev/null
-En dev, faire tourner les deux serveurs 3000 et 8080, server + client
-En prod, client est statique ==> juste serveur, comme d'hab
-
-index.js ne va rien chercher sur serveur, pas même les trucs indispensables comme variant ou variantArray
-==> ils sont demandés au serveur en arrivant sur la page (avec éventuellement "Variant does not exist")
-
-https://alligator.io/vuejs/lazy-loading-vue-cli-3-webpack/
-https://webpack.js.org/guides/code-splitting/#dynamic-imports
-https://vue-loader.vuejs.org/guide/pre-processors.html#pug
-https://cli.vuejs.org/guide/webpack.html#simple-configuration
+++ /dev/null
-// Note: not using Vue, but would be possible
-function trySendMessage()
-{
- let email = document.getElementById("userEmail");
- let subject = document.getElementById("mailSubject");
- let content = document.getElementById("mailContent");
- const error = checkNameEmail({email: email});
- if (!!error)
- return alert(error);
- if (content.value.trim().length == 0)
- return alert("Empty message");
- if (subject.value.trim().length == 0 && !confirm("No subject. Send anyway?"))
- return;
-
- // Message sending:
- ajax(
- "/messages",
- "POST",
- {
- email: email.value,
- subject: subject.value,
- content: content.value,
- },
- () => {
- subject.value = "";
- content.value = "";
- let emailSent = document.getElementById("emailSent");
- emailSent.style.display = "inline-block";
- setTimeout(() => { emailSent.style.display = "none"; }, 2000);
- }
- );
-}
+++ /dev/null
-// Source: https://www.quirksmode.org/js/cookies.html
-function setCookie(name, value)
-{
- var date = new Date();
- date.setTime(date.getTime()+(183*24*60*60*1000)); //6 months
- var expires = "; expires="+date.toGMTString();
- document.cookie = name+"="+value+expires+"; path=/";
-}
-
-function getCookie(name, defaut) {
- var nameEQ = name + "=";
- var ca = document.cookie.split(';');
- for (var i=0;i < ca.length;i++)
- {
- var c = ca[i];
- while (c.charAt(0)==' ')
- c = c.substring(1,c.length);
- if (c.indexOf(nameEQ) == 0)
- return c.substring(nameEQ.length,c.length);
- }
- return defaut; //cookie not found
-}
-
-// Random (enough) string for socket and game IDs
-function getRandString()
-{
- return (Date.now().toString(36) + Math.random().toString(36).substr(2, 7))
- .toUpperCase();
-}
-
-// Used both on index and variant page, to switch language
-function setLanguage(e)
-{
- setCookie("lang", e.target.value);
- location.reload(); //to include the right .pug file
-}
-
-// Shortcut for an often used click (on a modal)
-function doClick(elemId)
-{
- document.getElementById(elemId).click(); //or ".checked = true"
-}
-
-function translate(msg)
-{
- return translations[msg];
-}
this.conn.onclose = socketCloseListener;
},
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;
- },
// Game is over, clear storage and put it in indexedDB
archiveGame: function() {
// TODO: ...
doctype html
html
-
- head
- meta(charset="UTF-8")
- title vchess - club
- meta(name="viewport" content="width=device-width, initial-scale=1")
- meta(name="msapplication-config"
- content="/images/favicon/browserconfig.xml")
- meta(name="theme-color" content="#ffffff")
- link(rel="stylesheet"
- href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css")
- link(rel="stylesheet"
- href="//fonts.googleapis.com/css?family=Open+Sans:400,700")
- link(rel="apple-touch-icon" sizes="180x180"
- href="/images/favicon/apple-touch-icon.png")
- link(rel="icon" type="image/png" sizes="32x32"
- href="/images/favicon/favicon-32x32.png")
- link(rel="icon" type="image/png" sizes="16x16"
- href="/images/favicon/favicon-16x16.png")
- link(rel="manifest" href="/images/favicon/manifest.json")
- link(rel="mask-icon" href="/images/favicon/safari-pinned-tab.svg"
- color="#5bbad5")
- link(rel="shortcut icon" href="/images/favicon/favicon.ico")
- link(rel="stylesheet" href="/stylesheets/app.css")
-
- // TODO: on-demand components, do not load all at startup
- body
- -
- var langName = {
- "en": "English",
- "es": "Español",
- "fr": "Français",
- };
- case lang
- when "en"
- include translations/en
- include welcome/en
- when "es"
- include translations/es
- include welcome/es
- when "fr"
- include translations/fr
- include welcome/fr
- include modals
main#VueElement
my-upsert-user
.container
- // Header (on index only)
.row(v-show="display=='index'")
- .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
- header
- img(src="/images/index/unicorn.svg")
- .info-container
- p vchess.club
- img(src="/images/index/wildebeest.svg")
- // Menu (top of page)
.row
.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
label.drawer-toggle(for="drawerControl")
+++ /dev/null
-input#modalLang.modal(type="checkbox")
-div(role="dialog")
- #language.card
- label.modal-close(for="modalLang")
- form
- fieldset
- label(for="langSelect")= translations["Language"]
- select#langSelect
- each language,langCode in langName
- option(value=langCode selected=(lang==langCode))
- =language
-
-input#modalSettings.modal(type="checkbox")
-div(role="dialog" aria-labelledby="settingsTitle")
- .card.smallpad(@change="updateSettings")
- label.modal-close(for="modalSettings")
- h3#settingsTitle.section= translations["Preferences"]
- fieldset
- 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#setBcolor(v-model="settings.bcolor")
- option(value="lichess")
- = translations["brown"]
- option(value="chesscom")
- = translations["green"]
- option(value="chesstempo")
- = translations["blue"]
- fieldset
- label(for="selectSound")= translations["Play sounds?"]
- select#setSound(v-model="settings.sound")
- option(value="0")= translations["None"]
- option(value="1")= translations["New game"]
- option(value="2")= translations["All"]
-
-input#modalContact.modal(type="checkbox")
-div(role="dialog" aria-labelledby="contactTitle")
- form.card.smallpad
- label.modal-close(for="modalContact")
- h3#contactTitle.section= translations["Contact form"]
- fieldset
- label(for="userEmail")= translations["Email"]
- input#userEmail(type="email")
- fieldset
- label(for="mailSubject")= translations["Subject"]
- input#mailSubject(type="text")
- fieldset
- label(for="mailContent")= translations["Content"]
- br
- textarea#mailContent
- fieldset
- button(type="button" onClick="trySendMessage()") Send
- p#emailSent= translations["Email sent!"]
+++ /dev/null
--
- var translations =
- {
- "Language": "Language",
- "Contact form": "Contact form",
- "Email": "Email",
- "Subject": "Subject",
- "Content": "Content",
- "Email sent!": "Email sent!",
- "Hall": "Hall",
- "My games": "My games",
-
- // Index page:
- "Help": "Help",
- "First visit?": "First visit?",
- ">>> Please read this <<<": ">>> Please read this <<<",
- // Variants boxes:
- "Both sides of the mirror": "Both sides of the mirror",
- "Keep antiking in check": "Keep antiking in check",
- "Explosive captures": "Explosive captures",
- "Shared pieces": "Shared pieces",
- "Standard rules": "Standard rules",
- "Captures reborn": "Captures reborn",
- "Capture all of a kind": "Capture all of a kind",
- "Big board": "Big board",
- "Lose all pieces": "Lose all pieces",
- "Laws of attraction": "Laws of attraction",
- "Exchange pieces positions": "Exchange pieces positions",
- "Exotic captures": "Exotic captures",
- "Balanced sliders & leapers": "Balanced sliders & leapers",
- "Reverse captures": "Reverse captures",
- "Pawns move diagonally": "Pawns move diagonally",
- "In the shadow": "In the shadow",
- "Move twice": "Move twice",
- "Board upside down": "Board upside down",
-
- // Variant page:
- "New game": "New game",
- "Waiting for opponent...": "Waiting for opponent...",
- "Rules": "Rules",
- "Play": "Play",
- "Problems": "Problems",
- "White win": "White win",
- "Black win": "Black win",
- "Draw": "Draw",
- "New live game": "New live game",
- "New game versus computer": "New game versus computer",
- "Analysis mode": "Analysis mode",
- "Start chat": "Start chat",
- "Clear current game": "Clear current game",
- "Settings": "Settings",
- "Resign": "Resign",
- "Undo": "Undo",
- "Flip board": "Flip board",
- "Game state (FEN):": "Game state (FEN):",
- "Ok": "Ok",
- "Random": "Random",
- "Preferences": "Preferences",
- "My name is...": "My name is...",
- "Show hints?": "Show hints?",
- "Board colors": "Board colors",
- "brown": "brown",
- "green": "green",
- "blue": "blue",
- "Play sounds?": "Play sounds?",
- "None": "None",
- "All": "All",
- "Chat with ": "Chat with ",
- "Type here": "Type here",
- "Send": "Send",
- "Download PGN": "Download PGN",
- "Show solution": "Show solution",
- "Load previous problems": "Load previous problems",
- "Load next problems": "Load next problems",
- "New": "New",
- "Add a problem": "Add a problem",
- "Full FEN description": "Full FEN description",
- "Safe HTML tags allowed": "Safe HTML tags allowed",
- "Instructions": "Instructions",
- "Describe the problem goal": "Describe the problem goal",
- "Solution": "Solution",
- "How to solve the problem?": "How to solve the problem?",
- "Preview": "Preview",
- "Cancel": "Cancel",
- "Solve": "Solve",
- "Bad FEN description": "Bad FEN description",
- "Empty instructions": "Empty instructions",
- "Empty solution": "Empty solution",
- "Already playing a game in this variant on another tab!":
- "Already playing a game in this variant on another tab!",
- "Finish your ": "Finish your ",
- " game first!": " game first!",
- ": unfinished computer game will be erased":
- ": unfinished computer game will be erased",
- ": current analysis will be erased":
- ": current analysis will be erased",
- };
+++ /dev/null
--
- var translations =
- {
- "Language": "Idioma",
-
- // Index page:
- "Help": "Ayuda",
- "First visit?": "¿ Primera visita ?",
- ">>> Please read this <<<": ">>> Por favor lee esto <<<",
- // Variants boxes:
- "Both sides of the mirror": "Ambos lados del espejo",
- "Keep antiking in check": "Mantener el antirey en jaque",
- "Explosive captures": "Capturas explosivas",
- "Shared pieces": "Piezas compartidas",
- "Standard rules": "Reglas estandar",
- "Captures reborn": "Las capturas renacen",
- "Capture all of a kind": "Capturar todo del mismo tipo",
- "Big board": "Gran tablero",
- "Lose all pieces": "Perder todas las piezas",
- "Laws of attraction": "Las leyes de las atracciones",
- "Exchange pieces positions": "Intercambiar las posiciones de las piezas",
- "Exotic captures": "Capturas exóticas",
- "Balanced sliders & leapers": "Modos de desplazamiento equilibrados",
- "Reverse captures": "Capturas invertidas",
- "Pawns move diagonally": "Peones se mueven en diagonal",
- "In the shadow": "En la sombra",
- "Move twice": "Mover dos veces",
- "Board upside down": "Tablero al revés",
-
- // Variant page:
- "New game": "Nueva partida",
- "Waiting for opponent...": "Esperando a un oponente...",
- "Rules": "Reglas",
- "Play": "Jugar",
- "Problems": "Problemas",
- "White win": "Las blancas ganan",
- "Black win": "Las negras ganan",
- "Draw": "Empate",
- "New live game": "Nueva partida en vivo",
- "New game versus computer": "Nueva partida contra la computadora",
- "Analysis mode": "Modo de análisis",
- "Start chat": "Iniciar chat",
- "Clear current game": "Borrar la partida actual",
- "Settings": "Ajustes",
- "Resign": "Abandonar",
- "Undo": "Deshacer",
- "Flip board": "Girar el tablero",
- "Game state (FEN):": "Estado del juego (FEN) :",
- "Ok": "Ok",
- "Random": "Aleatorio",
- "Preferences": "Preferencias",
- "My name is...": "Mi nombre es...",
- "Show hints?": "Ayudas visuales ?",
- "Board colors": "Colores del tablero",
- "brown": "marrón",
- "green": "verde",
- "blue": "azul",
- "Play sounds?": "¿ Tocar los sonidos ?",
- "None": "No",
- "All": "Todos",
- "Chat with ": "Hablar con ",
- "Type here": "Escribe aqui",
- "Send": "Enviar",
- "Download PGN": "Descargar el PGN",
- "Show solution": "Mostrar la solucion",
- "Load previous problems": "Cargar los problemas anteriores",
- "Load next problems": "Cargar los siguientes problemas",
- "New": "Nuevo",
- "Add a problem": "Añadir un problema",
- "Full FEN description": "Descripción FEN completa",
- "Safe HTML tags allowed": "HTML 'seguro' autorizado",
- "Instructions": "Instrucciones",
- "Describe the problem goal": "Describe el objetivo del problema",
- "Solution": "Solución",
- "How to solve the problem?": "¿ Como resolver el problema ?",
- "Preview": "Previsualizar",
- "Cancel": "Anular",
- "Solve": "Resolver",
- "Bad FEN string": "Mala descripción FEN",
- "Empty instructions": "Instrucciones vacias",
- "Empty solution": "Solución vacÃa",
- "Already playing a game in this variant on another tab!":
- "¡ Una partida está en progreso en esta variante en otra pestaña !",
- "Finish your ": "¡ Termina tu ",
- " game first!": " partida primero !",
- ": unfinished computer game will be erased":
- " : una partida inconclusa contra la computadora será borrado",
- ": current analysis will be erased":
- " : el análisis actual será borrado",
- };
+++ /dev/null
--
- var translations =
- {
- "Language": "Langue",
-
- // Index page:
- "Help": "Aide",
- "First visit?": "Première visite ?",
- ">>> Please read this <<<": ">>> SVP lisez ceci <<<",
- // Variants boxes:
- "Both sides of the mirror": "Les deux côté du miroir",
- "Keep antiking in check": "Gardez l'antiroi en échec",
- "Explosive captures": "Captures explosives",
- "Shared pieces": "Pièces partagées",
- "Standard rules": "Règles usuelles",
- "Captures reborn": "Les captures renaissent",
- "Capture all of a kind": "Capturez tout d'un même type",
- "Big board": "Grand échiquier",
- "Lose all pieces": "Perdez toutes les pièces",
- "Laws of attraction": "Les lois de l'attraction",
- "Exchange pieces positions": "Échangez les positions des pièces",
- "Exotic captures": "Captures exotiques",
- "Balanced sliders & leapers": "Modes de déplacement équilibrés",
- "Reverse captures": "Captures inversées",
- "Pawns move diagonally": "Les pions vont en diagonale",
- "In the shadow": "Dans l'ombre",
- "Move twice": "Jouer deux coups",
- "Board upside down": "Échiquier à l'envers",
-
- // Variant page:
- "New game": "Nouvelle partie",
- "Waiting for opponent...": "En attente d'un adversaire...",
- "Rules": "Règles",
- "Play": "Jouer",
- "Problems": "Problèmes",
- "White win": "Les blancs gagnent",
- "Black win": "Les noirs gagnent",
- "Draw": "Match nul",
- "New live game": "Nouvelle partie en direct",
- "New game versus computer": "Nouvelle partie contre l'ordinateur",
- "Analysis mode": "Mode analyse",
- "Start chat": "Démarrer le chat",
- "Clear current game": "Effacer la partie courante",
- "Settings": "Réglages",
- "Resign": "Abandonner",
- "Undo": "Annuler",
- "Flip board": "Tourner l'échiquier",
- "Game state (FEN):": "État de la partie (FEN) :",
- "Ok": "Ok",
- "Random": "Aléatoire",
- "Preferences": "Préférences",
- "My name is...": "Je m'appelle...",
- "Show hints?": "Aides visuelles ?",
- "Board colors": "Couleurs de l'échiquier",
- "brown": "marron",
- "green": "vert",
- "blue": "bleu",
- "Play sounds?": "Jouer les sons ?",
- "None": "Aucun",
- "All": "Tous",
- "Chat with ": "Discuter avec ",
- "Type here": "Écrivez ici",
- "Send": "Envoyer",
- "Download PGN": "Télécharger le PGN",
- "Show solution": "Montrer la solution",
- "Load previous problems": "Charger les problèmes précédents",
- "Load next problems": "Charger les problèmes suivants",
- "New": "Nouveau",
- "Add a problem": "Ajouter un problème",
- "Full FEN description": "Description FEN complète",
- "Safe HTML tags allowed": "HTML 'sûr' autorisé",
- "Instructions": "Instructions",
- "Describe the problem goal": "Décrire le but du problème",
- "Solution": "Solution",
- "How to solve the problem?": "Comment résoudre le problème ?",
- "Preview": "Prévisualiser",
- "Cancel": "Annuler",
- "Solve": "Résoudre",
- "Bad FEN string": "Mauvaise description FEN",
- "Empty instructions": "Instructions vides",
- "Empty solution": "Solution vide",
- "Already playing a game in this variant on another tab!":
- "Une partie est en cours sur cette variante dans un autre onglet !",
- "Finish your ": "Terminez votre ",
- " game first!": " partie d'abord !",
- ": unfinished computer game will be erased":
- " : une partie inachevée contre l'ordinateur sera effacée",
- ": current analysis will be erased":
- " : l'analyse en cours sera effacée",
- };
+++ /dev/null
-input#modalWelcome.modal(type="checkbox")
-div(role="dialog")
- #welcome.card.text-center
- label.modal-close(for="modalWelcome")
- h3.blue.section Welcome to v[ariant]chess.club!
- .section
- p A fun place to play chess variants in real time.
- p But wait... what is a chess variant?
- img(src="/images/Hexagonal_chess.svg")
- p.
- As suggested by the picture, a variant setup generally looks
- more or less like a chessboard with regular pieces
- (otherwise it's no longer a variant but a whole new game).
- p.emphasis.purple However...
- p Each variant has its own new rules, which can involve
- table.list-table
- tbody
- tr
- td * different pieces movements
- tr
- td * different chessboard(s)
- tr
- td * new pieces
- tr
- td * moves side effects
- tr
- td ...and so on
- .section
- p.
- Example: imagine that a capture is an atomic explosion, wiping all
- adjacent squares – except the pawns, which as cockroaches can
- resist this kind of event.
- p Also state a goal: make the opponent's king explode.
- p → Congrats, you defined Atomic chess! (Playable here)
- .section
- p.emphasis.purple
- | OK, this all sounds interesting, but why would that be fun?
- p.
- Because all games here start with a random setup: no more boring
- openings memorization, you have to rely on your chess skills only.
- No game is like another one.
- -
- var wikipediaUrl = "https://en.wikipedia.org/wiki/" +
- "List_of_chess_variants#/media/File:Hexagonal_chess.svg";
- p.
- For informations about hundreds (if not thousands) of variants, you
- can visit the excellent
- #[a(href="https://www.chessvariants.com/") chessvariants] website.
- p.smallfont Image credit: #[a(href=wikipediaUrl) Wikipedia]
+++ /dev/null
-input#modalWelcome.modal(type="checkbox")
-div(role="dialog")
- #welcome.card.text-center
- label.modal-close(for="modalWelcome")
- h3.blue.section ¡ Bienvenido a v[ariant]chess.club !
- .section
- p Un sitio donde jugar variantes del juego de ajedrez en vivo.
- p Pero espera... ¿ qué es una "variante" ?
- img(src="/images/Hexagonal_chess.svg")
- p.
- Como lo sugiere la imagen, el punto de inicio de una variante generalmente
- se ve como un tablero de ajedrez con las piezas habituales.
- (sino ya no es una variante, pero un nuevo juego).
- p.emphasis.purple Sin embargo...
- p Cada variante tiene sus propias reglas, que pueden definir
- table.list-table
- tbody
- tr
- td * diferentes desplazamientos de piezas
- tr
- td * differentes(s) tablero(s)
- tr
- td * nuevas piezas
- tr
- td * efectos de borde en los movimientos
- tr
- td ...etc
- .section
- p.
- Ejemplo : Imagina que una captura es una explosión atómica que destruye
- todo en los 8 hexes cercanos, excepto los peones, que como las cucarachas
- se resisten a este tipo de cosas.
- p Define también un objetivo : hacer explotar al rey del adversario.
- p.
- → ¡ Bien hecho, acabas de describir el ajedrez atómico !
- (Se puede jugar aquÃ)
- .section
- p.emphasis.purple
- | OK, parece interesante, pero ¿ por qué serÃa divertido ?!
- p.
- Como todas las partidas comienzan con una posición aleatoria :
- ¡ terminadas las laboriosas memorizaciones de aperturas, puedes expresar
- tus habilidades de ajedrez desde el primer movimiento ! Ninguna partida
- es como otra.
- -
- var wikipediaUrl = "https://en.wikipedia.org/wiki/" +
- "List_of_chess_variants#/media/File:Hexagonal_chess.svg";
- p.
- Pour s'informer sur des centaines de variantes (au moins), je vous invite Ã
- visiter l'excellent site
- #[a(href="https://www.chessvariants.com/") chessvariants].
- p.smallfont Credito de imagen : #[a(href=wikipediaUrl) Wikipedia]
+++ /dev/null
-input#modalWelcome.modal(type="checkbox")
-div(role="dialog")
- #welcome.card.text-center
- label.modal-close(for="modalWelcome")
- h3.blue.section Bienvenue sur v[ariant]chess.club!
- .section
- p Un site où jouer à des variantes du jeu d'échecs en direct.
- p Mais attendez... c'est quoi une "variante" ?
- img(src="/images/Hexagonal_chess.svg")
- p.
- Comme suggéré par l'image, le point de départ d'une variante
- ressemble en général à un échiquier avec les pièces habituelles
- (sinon ce n'est plus une variante, mais un nouveau jeu).
- p.emphasis.purple Cependant...
- p Chaque variante a ses propres règles, qui peuvent définir
- table.list-table
- tbody
- tr
- td * différents déplacements de pièces
- tr
- td * différent(s) échiquier(s)
- tr
- td * de nouvelles pièces
- tr
- td * des effets de bord sur les coups
- tr
- td ...etc
- .section
- p.
- Exemple: imaginez qu'une capture soit une explosion atomique,
- détruisant tout ce qui se trouve sur les 8 cases à proximité –
- sauf les pions, qui tels les cafards résistent à ce genre de truc.
- p Définissez également un but : faire exploser le roi adverse.
- p → Bravo, vous venez de décrire les échecs atomiques ! (Jouable ici)
- .section
- p.emphasis.purple
- | OK ça a l'air intéressant, mais pourquoi ce serait amusant ?!
- p.
- Car toutes les parties démarrent avec une position aléatoire : terminées
- les laborieuses mémorisations d'ouverture, vous pouvez exprimer vos talents
- échiquéens dès le premier coup ! Aucune partie ne ressemble à une autre.
- -
- var wikipediaUrl = "https://en.wikipedia.org/wiki/" +
- "List_of_chess_variants#/media/File:Hexagonal_chess.svg";
- p.
- Pour s'informer sur des centaines de variantes (au moins), je vous invite Ã
- visiter l'excellent site
- #[a(href="https://www.chessvariants.com/") chessvariants].
- p.smallfont Crédit image : #[a(href=wikipediaUrl) Wikipedia]
}
}
},
+ "raw-loader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz",
+ "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "schema-utils": "^1.0.0"
+ },
+ "dependencies": {
+ "ajv-keywords": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
+ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ }
+ }
+ },
"read-pkg": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
"node-sass": "^4.9.0",
"pug": "^2.0.3",
"pug-plain-loader": "^1.0.0",
+ "raw-loader": "^1.0.0",
"sass-loader": "^7.0.1",
"vue-template-compiler": "^2.5.21"
},
-Generated using https://realfavicongenerator.net/ ,
-with an image from here:
+Favicon image from here:
https://www.freefavicon.com/freefavicons/objects/iconinfo/chess-piece-silhouette---red-king--rey-rojo-152-275290.html
<!DOCTYPE html>
-<html lang="en">
+<html>
<head>
<meta charset="utf-8">
<title>vchess - club</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
+ <link rel="stylesheet"
+ href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css">
+ <link rel="stylesheet"
+ href="//fonts.googleapis.com/css?family=Open+Sans:400,700">
</head>
<body>
<div id="app"></div>
<template lang="pug">
- #app
- #nav
- router-link(to="/") Home
- | |
- router-link(to="/about") About
- | |
- router-link(to="/test") Test
- router-view
+#app
+ // modal "welcome" will be filled in the selected language
+ #modalWelcome
+ Language
+ Settings(:settings="settings")
+ ContactForm
+ .container
+ .row(v-show="$route.path == '/'")
+ // Header (on index only)
+ header
+ .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+ header
+ img(src="./assets/images/index/unicorn.svg")
+ .info-container
+ p vchess.club {{ $lang }}
+ img(src="./assets/images/index/wildebeest.svg")
+ .row
+ // Menu (top of page)
+ nav
+ router-link(to="/") Home
+ | |
+ router-link(to="/about") About
+ | |
+ router-link(to="/test") Test
+ router-view
</template>
+<script>
+// See https://stackoverflow.com/a/35417159
+import ContactForm from "@/components/ContactForm.vue";
+import Language from "@/components/Language.vue";
+import Settings from "@/components/Settings.vue";
+export default {
+ data: function() {
+ return {
+ settings: {}, //TODO
+ };
+ },
+ components: {
+ ContactForm,
+ Language,
+ Settings,
+ },
+};
+</script>
+
<style lang="sass">
#app
font-family: "Avenir", Helvetica, Arial, sans-serif
--- /dev/null
+<template lang="pug">
+div
+ input#modalContact.modal(type="checkbox")
+ div(role="dialog" aria-labelledby="contactTitle")
+ form.card.smallpad
+ label.modal-close(for="modalContact")
+ h3#contactTitle.section {{ $tr["Contact form"] }}
+ fieldset
+ label(for="userEmail") {{ $tr["Email"] }}
+ input#userEmail(type="email")
+ fieldset
+ label(for="mailSubject") {{ $tr["Subject"] }}
+ input#mailSubject(type="text")
+ fieldset
+ label(for="mailContent") {{ $tr["Content"] }}
+ br
+ textarea#mailContent
+ fieldset
+ button(type="button" onClick="trySendMessage()") Send
+ p#emailSent {{ $tr["Email sent!"] }}
+</template>
+
+<script>
+import { ajax } from "../utils/ajax";
+export default {
+ name: "ContactForm",
+ methods: {
+ // Note: not using Vue here, but would be possible
+ trySendMessage: function() {
+ let email = document.getElementById("userEmail");
+ let subject = document.getElementById("mailSubject");
+ let content = document.getElementById("mailContent");
+ const error = checkNameEmail({email: email});
+ if (!!error)
+ return alert(error);
+ if (content.value.trim().length == 0)
+ return alert("Empty message");
+ if (subject.value.trim().length == 0 && !confirm("No subject. Send anyway?"))
+ return;
+
+ // Message sending:
+ ajax(
+ "/messages",
+ "POST",
+ {
+ email: email.value,
+ subject: subject.value,
+ content: content.value,
+ },
+ () => {
+ subject.value = "";
+ content.value = "";
+ let emailSent = document.getElementById("emailSent");
+ emailSent.style.display = "inline-block";
+ setTimeout(() => { emailSent.style.display = "none"; }, 2000);
+ }
+ );
+ },
+ },
+};
+</script>
--- /dev/null
+<template lang="pug">
+div
+ -
+ var langName = {
+ "en": "English",
+ "es": "Español",
+ "fr": "Français",
+ };
+ input#modalLang.modal(type="checkbox")
+ div(role="dialog")
+ #language.card
+ label.modal-close(for="modalLang")
+ form
+ fieldset
+ label(for="langSelect") {{ $tr["Language"] }}
+ select#langSelect
+ each language,langCode in langName
+ option(value=langCode selected=(lang==langCode))
+ =language
+</template>
+
+<script>
+export default {
+ name: "Language",
+ methods: {
+ // Used both on index and variant page, to switch language
+ setLanguage: function(e) {
+ localStorage["lang"] = e.target.value;
+ this.$lang = e.target.value;
+ },
+ },
+};
+</script>
--- /dev/null
+<template lang="pug">
+div
+ input#modalSettings.modal(type="checkbox")
+ div(role="dialog" aria-labelledby="settingsTitle")
+ .card.smallpad(@change="updateSettings")
+ label.modal-close(for="modalSettings")
+ h3#settingsTitle.section {{ $tr["Preferences"] }}
+ fieldset
+ label(for="setSqSize") {{ $tr["Square size (in pixels). 0 for 'adaptative'"] }}
+ input#setSqSize(type="number" v-model="settings.sqSize")
+ fieldset
+ label(for="selectHints") {{ $tr["Show move hints?"] }}
+ select#setHints(v-model="settings.hints")
+ option(value="0") {{ $tr["None"] }}
+ option(value="1") {{ $tr["Moves from a square"] }}
+ option(value="2") {{ $tr["Pieces which can move"] }}
+ fieldset
+ label(for="setHighlight") {{ $tr["Highlight squares? (Last move & checks)"] }}
+ input#setHighlight(type="checkbox" v-model="settings.highlight")
+ fieldset
+ label(for="setCoords") {{ $tr["Show board coordinates?"] }}
+ input#setCoords(type="checkbox" v-model="settings.coords")
+ fieldset
+ label(for="selectColor") {{ $tr["Board colors"] }}
+ select#setBcolor(v-model="settings.bcolor")
+ option(value="lichess") {{ $tr["brown"] }}
+ option(value="chesscom") {{ $tr["green"] }}
+ option(value="chesstempo") {{ $tr["blue"] }}
+ fieldset
+ label(for="selectSound") {{ $tr["Play sounds?"] }}
+ select#setSound(v-model="settings.sound")
+ option(value="0") {{ $tr["None"] }}
+ option(value="1") {{ $tr["New game"] }}
+ option(value="2") {{ $tr["All"] }}
+</template>
+
+<script>
+export default {
+ name: "Settings",
+ props: ["settings"],
+ 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;
+ },
+ },
+};
+</script>
import router from "./router";
import params from "./parameters"; //for socket connection
import { ajax } from "./utils/ajax";
+import { util } from "./utils/misc";
Vue.config.productionTip = false;
router,
render: function(h) {
return h(App);
+ },
+// data: {
+// lang: "",
+// },
+ watch: {
+ $lang: async function(newLang) {
+ // Fill modalWelcome, and import translations from "./translations/$lang.js"
+ document.getElementById("modalWelcome").innerHTML =
+ require("raw-loader!pug-plain-loader!./modals/welcome/" + newLang + ".pug");
+ const tModule = await import("./translations/" + newLang + ".js");
+ Vue.prototype.$tr = tModule.translations;
+ //console.log(tModule.translations);
+ },
},
created: function() {
- //alert("test");
+ const supportedLangs = ["en","es","fr"];
+ Vue.prototype.$lang = localStorage["lang"] ||
+ supportedLangs.includes(navigator.language)
+ ? navigator.language
+ : "en";
ajax("/variants", "GET", res => {
Vue.prototype.$variants = res.variantArray;
});
- Vue.prototype.$conn = null; //TODO
+ Vue.prototype.$tr = {}; //to avoid a compiler error
const myid = localStorage["myid"] || util.getRandString();
// NOTE: in this version, we don't say on which page we are, yet
// ==> we'll say "enter/leave" page XY (in fact juste "enter", seemingly)
--- /dev/null
+input#modalWelcome.modal(type="checkbox")
+div(role="dialog")
+ #welcome.card.text-center
+ label.modal-close(for="modalWelcome")
+ h3.blue.section Welcome to v[ariant]chess.club!
+ .section
+ p A fun place to play chess variants in real time.
+ p But wait... what is a chess variant?
+ img(src="/images/Hexagonal_chess.svg")
+ p.
+ As suggested by the picture, a variant setup generally looks more or less
+ like a chessboard with regular pieces (otherwise it's no longer a variant
+ but a whole new game).
+ p.emphasis.purple However...
+ p Each variant has its own new rules, which can involve
+ table.list-table
+ tbody
+ tr
+ td * different pieces movements
+ tr
+ td * different chessboard(s)
+ tr
+ td * new pieces
+ tr
+ td * moves side effects
+ tr
+ td ...and so on
+ .section
+ p.
+ Example: imagine that a capture is an atomic explosion, wiping all
+ adjacent squares – except the pawns, which as cockroaches can
+ resist this kind of event.
+ p Also state a goal: make the opponent's king explode.
+ p → Congrats, you defined Atomic chess! (Playable here)
+ .section
+ p.emphasis.purple
+ | OK, this all sounds interesting, but why would that be fun?
+ p.
+ Because all games here start with a random setup: no more boring
+ openings memorization, you have to rely on your chess skills only.
+ No game is like another one.
+ -
+ var wikipediaUrl = "https://en.wikipedia.org/wiki/" +
+ "List_of_chess_variants#/media/File:Hexagonal_chess.svg";
+ p.
+ For informations about hundreds (if not thousands) of variants, you
+ can visit the excellent
+ #[a(href="https://www.chessvariants.com/") chessvariants] website.
+ p.smallfont Image credit: #[a(href=wikipediaUrl) Wikipedia]
--- /dev/null
+input#modalWelcome.modal(type="checkbox")
+div(role="dialog")
+ #welcome.card.text-center
+ label.modal-close(for="modalWelcome")
+ h3.blue.section ¡ Bienvenido a v[ariant]chess.club !
+ .section
+ p Un sitio donde jugar variantes del juego de ajedrez en vivo.
+ p Pero espera... ¿ qué es una "variante" ?
+ img(src="/images/Hexagonal_chess.svg")
+ p Como lo sugiere la imagen, el punto de inicio de una variante generalmente se ve como un tablero de ajedrez con las piezas habituales (sino ya no es una variante, pero un nuevo juego).
+ p.emphasis.purple Sin embargo...
+ p Cada variante tiene sus propias reglas, que pueden definir
+ table.list-table
+ tbody
+ tr
+ td * diferentes desplazamientos de piezas
+ tr
+ td * differentes(s) tablero(s)
+ tr
+ td * nuevas piezas
+ tr
+ td * efectos de borde en los movimientos
+ tr
+ td ...etc
+ .section
+ p Ejemplo : Imagina que una captura es una explosión atómica que destruye todo en los 8 hexes cercanos, excepto los peones, que como las cucarachas se resisten a este tipo de cosas.
+ p Define también un objetivo : hacer explotar al rey del adversario.
+ p → ¡ Bien hecho, acabas de describir el ajedrez atómico ! (Se puede jugar aquÃ)
+ .section
+ p.emphasis.purple
+ | OK, parece interesante, pero ¿ por qué serÃa divertido ?!
+ p Como todas las partidas comienzan con una posición aleatoria : ¡ terminadas las laboriosas memorizaciones de aperturas, puedes expresar tus habilidades de ajedrez desde el primer movimiento ! Ninguna partida es como otra.
+ p Para conocer cientos de variantes (al menos), te invito a visitar el excelente sitio
+ a(href="https://www.chessvariants.com/" target="_blank" rel="noopener") chessvariants.
+ p.smallfont Credito de imagen : #[a(href="https://en.wikipedia.org/wiki/List_of_chess_variants#/media/File:Hexagonal_chess.svg") Wikipedia]
--- /dev/null
+input#modalWelcome.modal(type="checkbox")
+div(role="dialog")
+ #welcome.card.text-center
+ label.modal-close(for="modalWelcome")
+ h3.blue.section Bienvenue sur v[ariant]chess.club!
+ .section
+ p Un site où jouer à des variantes du jeu d'échecs en direct.
+ p Mais attendez... c'est quoi une "variante" ?
+ img(src="/images/Hexagonal_chess.svg")
+ p Comme suggéré par l'image, le point de départ d'une variante ressemble en général à un échiquier avec les pièces habituelles (sinon ce n'est plus une variante, mais un nouveau jeu).
+ p.emphasis.purple Cependant...
+ p Chaque variante a ses propres règles, qui peuvent définir
+ table.list-table
+ tbody
+ tr
+ td * différents déplacements de pièces
+ tr
+ td * différent(s) échiquier(s)
+ tr
+ td * de nouvelles pièces
+ tr
+ td * des effets de bord sur les coups
+ tr
+ td ...etc
+ .section
+ p Exemple: imaginez qu'une capture soit une explosion atomique, détruisant tout ce qui se trouve sur les 8 cases à proximité – sauf les pions, qui tels les cafards résistent à ce genre de truc.
+ p Définissez également un but : faire exploser le roi adverse.
+ p → Bravo, vous venez de décrire les échecs atomiques ! (Jouable ici)
+ .section
+ p.emphasis.purple
+ | OK ça a l'air intéressant, mais pourquoi ce serait amusant ?!
+ p Car toutes les parties démarrent avec une position aléatoire : terminées les laborieuses mémorisations d'ouverture, vous pouvez exprimer vos talents échiquéens dès le premier coup ! Aucune partie ne ressemble à une autre.
+ -
+ var wikipediaUrl = "https://en.wikipedia.org/wiki/" +
+ "List_of_chess_variants#/media/File:Hexagonal_chess.svg";
+ p.
+ Pour s'informer sur des centaines de variantes (au moins), je vous invite Ã
+ visiter l'excellent site
+ #[a(href="https://www.chessvariants.com/") chessvariants].
+ p.smallfont Crédit image : #[a(href=wikipediaUrl) Wikipedia]
--- /dev/null
+export const translations =
+{
+ "Language": "Language",
+ "Contact form": "Contact form",
+ "Email": "Email",
+ "Subject": "Subject",
+ "Content": "Content",
+ "Email sent!": "Email sent!",
+ "Hall": "Hall",
+ "My games": "My games",
+
+ // Index page:
+ "Help": "Help",
+ "First visit?": "First visit?",
+ ">>> Please read this <<<": ">>> Please read this <<<",
+ // Variants boxes:
+ "Both sides of the mirror": "Both sides of the mirror",
+ "Keep antiking in check": "Keep antiking in check",
+ "Explosive captures": "Explosive captures",
+ "Shared pieces": "Shared pieces",
+ "Standard rules": "Standard rules",
+ "Captures reborn": "Captures reborn",
+ "Capture all of a kind": "Capture all of a kind",
+ "Big board": "Big board",
+ "Lose all pieces": "Lose all pieces",
+ "Laws of attraction": "Laws of attraction",
+ "Exchange pieces positions": "Exchange pieces positions",
+ "Exotic captures": "Exotic captures",
+ "Balanced sliders & leapers": "Balanced sliders & leapers",
+ "Reverse captures": "Reverse captures",
+ "Pawns move diagonally": "Pawns move diagonally",
+ "In the shadow": "In the shadow",
+ "Move twice": "Move twice",
+ "Board upside down": "Board upside down",
+
+ // Variant page:
+ "New game": "New game",
+ "Waiting for opponent...": "Waiting for opponent...",
+ "Rules": "Rules",
+ "Play": "Play",
+ "Problems": "Problems",
+ "White win": "White win",
+ "Black win": "Black win",
+ "Draw": "Draw",
+ "New live game": "New live game",
+ "New game versus computer": "New game versus computer",
+ "Analysis mode": "Analysis mode",
+ "Start chat": "Start chat",
+ "Clear current game": "Clear current game",
+ "Settings": "Settings",
+ "Resign": "Resign",
+ "Undo": "Undo",
+ "Flip board": "Flip board",
+ "Game state (FEN):": "Game state (FEN):",
+ "Ok": "Ok",
+ "Random": "Random",
+ "Preferences": "Preferences",
+ "My name is...": "My name is...",
+ "Show hints?": "Show hints?",
+ "Board colors": "Board colors",
+ "brown": "brown",
+ "green": "green",
+ "blue": "blue",
+ "Play sounds?": "Play sounds?",
+ "None": "None",
+ "All": "All",
+ "Chat with ": "Chat with ",
+ "Type here": "Type here",
+ "Send": "Send",
+ "Download PGN": "Download PGN",
+ "Show solution": "Show solution",
+ "Load previous problems": "Load previous problems",
+ "Load next problems": "Load next problems",
+ "New": "New",
+ "Add a problem": "Add a problem",
+ "Full FEN description": "Full FEN description",
+ "Safe HTML tags allowed": "Safe HTML tags allowed",
+ "Instructions": "Instructions",
+ "Describe the problem goal": "Describe the problem goal",
+ "Solution": "Solution",
+ "How to solve the problem?": "How to solve the problem?",
+ "Preview": "Preview",
+ "Cancel": "Cancel",
+ "Solve": "Solve",
+ "Bad FEN description": "Bad FEN description",
+ "Empty instructions": "Empty instructions",
+ "Empty solution": "Empty solution",
+ "Already playing a game in this variant on another tab!":
+ "Already playing a game in this variant on another tab!",
+ "Finish your ": "Finish your ",
+ " game first!": " game first!",
+ ": unfinished computer game will be erased":
+ ": unfinished computer game will be erased",
+ ": current analysis will be erased":
+ ": current analysis will be erased",
+};
--- /dev/null
+export const translations =
+{
+ "Language": "Idioma",
+
+ // Index page:
+ "Help": "Ayuda",
+ "First visit?": "¿ Primera visita ?",
+ ">>> Please read this <<<": ">>> Por favor lee esto <<<",
+ // Variants boxes:
+ "Both sides of the mirror": "Ambos lados del espejo",
+ "Keep antiking in check": "Mantener el antirey en jaque",
+ "Explosive captures": "Capturas explosivas",
+ "Shared pieces": "Piezas compartidas",
+ "Standard rules": "Reglas estandar",
+ "Captures reborn": "Las capturas renacen",
+ "Capture all of a kind": "Capturar todo del mismo tipo",
+ "Big board": "Gran tablero",
+ "Lose all pieces": "Perder todas las piezas",
+ "Laws of attraction": "Las leyes de las atracciones",
+ "Exchange pieces positions": "Intercambiar las posiciones de las piezas",
+ "Exotic captures": "Capturas exóticas",
+ "Balanced sliders & leapers": "Modos de desplazamiento equilibrados",
+ "Reverse captures": "Capturas invertidas",
+ "Pawns move diagonally": "Peones se mueven en diagonal",
+ "In the shadow": "En la sombra",
+ "Move twice": "Mover dos veces",
+ "Board upside down": "Tablero al revés",
+
+ // Variant page:
+ "New game": "Nueva partida",
+ "Waiting for opponent...": "Esperando a un oponente...",
+ "Rules": "Reglas",
+ "Play": "Jugar",
+ "Problems": "Problemas",
+ "White win": "Las blancas ganan",
+ "Black win": "Las negras ganan",
+ "Draw": "Empate",
+ "New live game": "Nueva partida en vivo",
+ "New game versus computer": "Nueva partida contra la computadora",
+ "Analysis mode": "Modo de análisis",
+ "Start chat": "Iniciar chat",
+ "Clear current game": "Borrar la partida actual",
+ "Settings": "Ajustes",
+ "Resign": "Abandonar",
+ "Undo": "Deshacer",
+ "Flip board": "Girar el tablero",
+ "Game state (FEN):": "Estado del juego (FEN) :",
+ "Ok": "Ok",
+ "Random": "Aleatorio",
+ "Preferences": "Preferencias",
+ "My name is...": "Mi nombre es...",
+ "Show hints?": "Ayudas visuales ?",
+ "Board colors": "Colores del tablero",
+ "brown": "marrón",
+ "green": "verde",
+ "blue": "azul",
+ "Play sounds?": "¿ Tocar los sonidos ?",
+ "None": "No",
+ "All": "Todos",
+ "Chat with ": "Hablar con ",
+ "Type here": "Escribe aqui",
+ "Send": "Enviar",
+ "Download PGN": "Descargar el PGN",
+ "Show solution": "Mostrar la solucion",
+ "Load previous problems": "Cargar los problemas anteriores",
+ "Load next problems": "Cargar los siguientes problemas",
+ "New": "Nuevo",
+ "Add a problem": "Añadir un problema",
+ "Full FEN description": "Descripción FEN completa",
+ "Safe HTML tags allowed": "HTML 'seguro' autorizado",
+ "Instructions": "Instrucciones",
+ "Describe the problem goal": "Describe el objetivo del problema",
+ "Solution": "Solución",
+ "How to solve the problem?": "¿ Como resolver el problema ?",
+ "Preview": "Previsualizar",
+ "Cancel": "Anular",
+ "Solve": "Resolver",
+ "Bad FEN string": "Mala descripción FEN",
+ "Empty instructions": "Instrucciones vacias",
+ "Empty solution": "Solución vacÃa",
+ "Already playing a game in this variant on another tab!":
+ "¡ Una partida está en progreso en esta variante en otra pestaña !",
+ "Finish your ": "¡ Termina tu ",
+ " game first!": " partida primero !",
+ ": unfinished computer game will be erased":
+ " : una partida inconclusa contra la computadora será borrado",
+ ": current analysis will be erased":
+ " : el análisis actual será borrado",
+};
--- /dev/null
+export const translations =
+{
+ "Language": "Langue",
+
+ // Index page:
+ "Help": "Aide",
+ "First visit?": "Première visite ?",
+ ">>> Please read this <<<": ">>> SVP lisez ceci <<<",
+ // Variants boxes:
+ "Both sides of the mirror": "Les deux côté du miroir",
+ "Keep antiking in check": "Gardez l'antiroi en échec",
+ "Explosive captures": "Captures explosives",
+ "Shared pieces": "Pièces partagées",
+ "Standard rules": "Règles usuelles",
+ "Captures reborn": "Les captures renaissent",
+ "Capture all of a kind": "Capturez tout d'un même type",
+ "Big board": "Grand échiquier",
+ "Lose all pieces": "Perdez toutes les pièces",
+ "Laws of attraction": "Les lois de l'attraction",
+ "Exchange pieces positions": "Échangez les positions des pièces",
+ "Exotic captures": "Captures exotiques",
+ "Balanced sliders & leapers": "Modes de déplacement équilibrés",
+ "Reverse captures": "Captures inversées",
+ "Pawns move diagonally": "Les pions vont en diagonale",
+ "In the shadow": "Dans l'ombre",
+ "Move twice": "Jouer deux coups",
+ "Board upside down": "Échiquier à l'envers",
+
+ // Variant page:
+ "New game": "Nouvelle partie",
+ "Waiting for opponent...": "En attente d'un adversaire...",
+ "Rules": "Règles",
+ "Play": "Jouer",
+ "Problems": "Problèmes",
+ "White win": "Les blancs gagnent",
+ "Black win": "Les noirs gagnent",
+ "Draw": "Match nul",
+ "New live game": "Nouvelle partie en direct",
+ "New game versus computer": "Nouvelle partie contre l'ordinateur",
+ "Analysis mode": "Mode analyse",
+ "Start chat": "Démarrer le chat",
+ "Clear current game": "Effacer la partie courante",
+ "Settings": "Réglages",
+ "Resign": "Abandonner",
+ "Undo": "Annuler",
+ "Flip board": "Tourner l'échiquier",
+ "Game state (FEN):": "État de la partie (FEN) :",
+ "Ok": "Ok",
+ "Random": "Aléatoire",
+ "Preferences": "Préférences",
+ "My name is...": "Je m'appelle...",
+ "Show hints?": "Aides visuelles ?",
+ "Board colors": "Couleurs de l'échiquier",
+ "brown": "marron",
+ "green": "vert",
+ "blue": "bleu",
+ "Play sounds?": "Jouer les sons ?",
+ "None": "Aucun",
+ "All": "Tous",
+ "Chat with ": "Discuter avec ",
+ "Type here": "Écrivez ici",
+ "Send": "Envoyer",
+ "Download PGN": "Télécharger le PGN",
+ "Show solution": "Montrer la solution",
+ "Load previous problems": "Charger les problèmes précédents",
+ "Load next problems": "Charger les problèmes suivants",
+ "New": "Nouveau",
+ "Add a problem": "Ajouter un problème",
+ "Full FEN description": "Description FEN complète",
+ "Safe HTML tags allowed": "HTML 'sûr' autorisé",
+ "Instructions": "Instructions",
+ "Describe the problem goal": "Décrire le but du problème",
+ "Solution": "Solution",
+ "How to solve the problem?": "Comment résoudre le problème ?",
+ "Preview": "Prévisualiser",
+ "Cancel": "Annuler",
+ "Solve": "Résoudre",
+ "Bad FEN string": "Mauvaise description FEN",
+ "Empty instructions": "Instructions vides",
+ "Empty solution": "Solution vide",
+ "Already playing a game in this variant on another tab!":
+ "Une partie est en cours sur cette variante dans un autre onglet !",
+ "Finish your ": "Terminez votre ",
+ " game first!": " partie d'abord !",
+ ": unfinished computer game will be erased":
+ " : une partie inachevée contre l'ordinateur sera effacée",
+ ": current analysis will be erased":
+ " : l'analyse en cours sera effacée",
+};
--- /dev/null
+export const util =
+{
+ // Source: https://www.quirksmode.org/js/cookies.html
+ setCookie: function(name, value)
+ {
+ var date = new Date();
+ date.setTime(date.getTime()+(183*24*60*60*1000)); //6 months
+ var expires = "; expires="+date.toGMTString();
+ document.cookie = name+"="+value+expires+"; path=/";
+ },
+
+ getCookie: function(name, defaut) {
+ var nameEQ = name + "=";
+ var ca = document.cookie.split(';');
+ for (var i=0;i < ca.length;i++)
+ {
+ var c = ca[i];
+ while (c.charAt(0)==' ')
+ c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0)
+ return c.substring(nameEQ.length,c.length);
+ }
+ return defaut; //cookie not found
+ },
+
+ // Random (enough) string for socket and game IDs
+ getRandString: function()
+ {
+ return (Date.now().toString(36) + Math.random().toString(36).substr(2, 7))
+ .toUpperCase();
+ },
+
+ // Shortcut for an often used click (on a modal)
+ doClick: function(elemId)
+ {
+ document.getElementById(elemId).click(); //or ".checked = true"
+ },
+
+ translate: function(msg)
+ {
+ return translations[msg];
+ },
+};
export default {
name: "home",
components: {
- HelloWorld
+ HelloWorld,
}
};
</script>
# Various files
/db/vchess.sqlite
/config/parameters.js
-/serve/
+/static/
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
--- /dev/null
+Later: use http2
+https://webapplog.com/http2-node/
+https://www.npmjs.com/package/spdy
+Express 5?
+
+Finish :
+ - models
+ - routes
+ - sockets
var app = express();
-app.use(favicon(path.join(__dirname, "favicon", "favicon.ico")));
+app.use(favicon(path.join(__dirname, "static", "favicon.ico")));
if (app.get('env') === 'development')
{
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
-app.use(express.static(path.join(__dirname, 'serve'))); //client "prod" files
+app.use(express.static(path.join(__dirname, 'static'))); //client "prod" files
// In development stage the client side has its own server
-if (app.get('env') === 'development')
+if (params.cors.enable)
{
app.use(function(req, res, next) {
- res.header("Access-Control-Allow-Origin", "*");
+ res.header("Access-Control-Allow-Origin", params.cors.allowedOrigin);
res.header("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
next();
// To know in which environment the code run
env: process.env.NODE_ENV || 'development',
+
+ // CORS: cross-origin resource sharing options
+ cors: {
+ enable: Parameters.env === "development",
+ allowedOrigin: "*",
+ },
// Lifespan of a (login) cookie
cookieExpire: 183*24*60*60*1000, //6 months in milliseconds
+++ /dev/null
-function checkChallenge(c)
-{
- const vid = parseInt(c.vid);
- if (isNaN(vid) || vid <= 0)
- return "Please select a variant";
-
- const mainTime = parseInt(c.mainTime);
- const increment = parseInt(c.increment);
- if (isNaN(mainTime) || mainTime <= 0)
- return "Main time should be strictly positive";
- if (isNaN(increment) || increment < 0)
- return "Increment must be positive";
-
- // Basic alphanumeric check for players names
- let playerCount = 0;
- for (p of c.players)
- {
- if (p.name.length > 0)
- {
- if (!p.name.match(/^[\w]+$/))
- return "Wrong characters in players names";
- playerCount++;
- }
- }
-
- if (playerCount > 0 && playerCount != c.nbPlayers-1)
- return "None, or all of the opponent names must be filled"
-
- // Just characters check on server:
- if (!c.fen.match(/^[a-zA-Z0-9, /-]*$/))
- return "Bad FEN string";
-}
-
-module.exports = checkChallenge;
+++ /dev/null
-const NbPlayers =
-{
- "Alice": [2,3,4],
- "Antiking": [2,3,4],
- "Atomic": [2,3,4],
- "Baroque": [2,3,4],
- "Berolina": [2,4],
- "Checkered": [2,3,4],
- "Chess960": [2,3,4],
- "Crazyhouse": [2,3,4],
- "Dark": [2,3,4],
- "Extinction": [2,3,4],
- "Grand": [2],
- "Losers": [2,3,4],
- "Magnetic": [2],
- "Marseille": [2],
- "Switching": [2,3,4],
- "Upsidedown": [2],
- "Wildebeest": [2],
- "Zen": [2,3,4],
-};
-
-module.exports = NbPlayers;
+++ /dev/null
-function checkNameEmail(o)
-{
- if (typeof o.name === "string")
- {
- if (o.name.length == 0)
- return "Empty name";
- if (!o.name.match(/^[\w]+$/))
- return "Bad characters in name";
- }
- if (typeof o.email === "string")
- {
- if (o.email.length == 0)
- return "Empty email";
- if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/))
- return "Bad characters in email";
- }
-}
-
-module.exports = checkNameEmail;
+++ /dev/null
-#$# git-fat 9249f7ccb03573152b1210b27c244462ac286aea 12108
+++ /dev/null
-#$# git-fat 510e77fc0cd116eb8ef2e8743718f4c1c22393ec 33049
+++ /dev/null
-#$# git-fat 60b73e04ab8fc7a125745cbdc8f6f5cb298c105f 12621
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<browserconfig>
- <msapplication>
- <tile>
- <square150x150logo src="/images/favicon/mstile-150x150.png"/>
- <TileColor>#da532c</TileColor>
- </tile>
- </msapplication>
-</browserconfig>
+++ /dev/null
-#$# git-fat 10c5fd36c2cb305a3f7e6a6f39eccba03a679087 995
+++ /dev/null
-#$# git-fat 53091595a131897b202391f3f0ffaa61f283c56a 1877
+++ /dev/null
-#$# git-fat c12f062ae7f59219c96acae47fafca862cb035a2 15086
+++ /dev/null
-{
- "name": "",
- "icons": [
- {
- "src": "/images/favicon/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/images/favicon/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}
+++ /dev/null
-#$# git-fat fc323034e5b15647a98909a7cce05bb80eecef93 5784
+++ /dev/null
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
- "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
- width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
- preserveAspectRatio="xMidYMid meet">
-<metadata>
-Created by potrace 1.11, written by Peter Selinger 2001-2013
-</metadata>
-<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
-fill="#000000" stroke="none">
-<path d="M2379 4963 c-1 -10 -2 -85 -3 -168 l-1 -150 -160 0 -160 0 -3 -168
--2 -167 47 -1 c27 0 100 -1 163 -2 l115 -2 1 -173 0 -173 -35 -33 c-120 -110
--227 -244 -237 -296 0 -3 -6 -25 -13 -49 -23 -82 -40 -186 -42 -269 -2 -93 -8
--132 -19 -132 -4 0 -74 36 -156 80 -81 44 -149 80 -150 80 -1 0 -19 8 -40 17
--22 9 -41 17 -44 18 -3 0 -10 3 -16 7 -15 10 -160 46 -219 54 -72 10 -300 12
--365 2 -153 -23 -378 -121 -489 -213 -52 -43 -136 -133 -176 -190 -180 -252
--206 -530 -74 -805 84 -176 177 -292 453 -565 l213 -210 2 -190 c5 -364 17
--639 32 -711 7 -39 19 -60 47 -86 44 -40 123 -86 207 -121 117 -47 100 -41
-140 -52 17 -4 35 -10 40 -14 6 -3 15 -6 20 -7 6 -1 37 -10 70 -19 76 -21 161
--42 200 -49 17 -2 41 -7 55 -10 58 -13 77 -16 120 -21 25 -3 56 -7 70 -10 69
--14 250 -20 585 -20 390 0 553 8 665 31 14 3 39 7 55 9 17 1 55 8 85 15 30 6
-69 14 85 17 17 2 71 16 120 30 50 14 97 27 105 28 8 2 16 4 18 5 1 2 5 3 10 5
-4 1 16 5 27 9 11 4 52 19 90 33 87 32 202 94 242 130 31 28 55 65 60 93 9 54
-14 122 18 240 2 74 7 225 11 335 3 110 7 230 8 267 l2 67 65 62 c35 33 120
-116 187 182 419 413 559 725 470 1047 -7 25 -13 47 -14 50 0 3 -6 19 -13 35
--131 321 -408 538 -766 602 -22 4 -110 7 -195 7 -169 -1 -273 -17 -395 -62
--64 -24 -205 -93 -231 -113 -26 -21 -175 -91 -181 -85 -4 3 -8 43 -9 89 -4
-209 -38 342 -116 455 -16 23 -73 86 -125 139 l-96 96 1 141 c0 78 1 156 1 173
-l1 33 161 -2 c106 -1 163 2 167 9 5 9 8 298 3 323 -1 3 -75 5 -166 5 l-165 -1
--1 30 c0 17 -1 93 -1 169 l0 137 -179 0 c-163 0 -179 -2 -180 -17z m285 -1203
-c98 -44 194 -185 213 -311 10 -63 9 -189 -1 -271 -11 -87 -57 -214 -151 -415
--24 -50 -46 -101 -50 -112 -3 -12 -10 -21 -15 -21 -4 0 -11 -11 -14 -25 -10
--39 -77 -146 -89 -142 -12 4 -107 174 -101 180 3 2 -2 10 -11 17 -8 7 -15 16
--15 21 0 5 -16 44 -36 86 -91 199 -139 329 -149 405 -11 85 -8 279 5 328 35
-129 126 240 228 276 49 17 128 11 186 -16z m-1245 -490 c100 -19 209 -55 257
--87 16 -10 31 -17 34 -16 17 8 196 -123 288 -211 154 -146 325 -411 387 -596
-36 -110 60 -213 70 -305 3 -27 8 -70 11 -95 11 -97 7 -281 -7 -295 -18 -17
--116 -30 -304 -40 -148 -8 -318 -22 -380 -31 -135 -19 -170 -26 -377 -72 -133
--30 -161 -25 -260 44 -140 99 -238 185 -420 367 -258 260 -330 401 -331 647 0
-132 11 176 71 299 41 83 246 281 290 281 6 0 12 4 14 8 6 16 170 87 228 99 14
-2 43 8 65 12 69 14 263 9 364 -9z m2661 7 c76 -18 119 -31 150 -44 270 -115
-439 -297 490 -527 10 -47 14 -191 6 -248 -16 -113 -88 -259 -181 -368 -39 -46
--321 -328 -360 -360 -17 -14 -55 -45 -85 -70 -30 -25 -57 -47 -60 -50 -3 -3
--39 -27 -80 -55 -72 -48 -78 -50 -142 -49 -37 1 -75 5 -85 9 -20 9 -25 10 -68
-19 -16 3 -50 10 -75 15 -170 38 -291 54 -480 66 -155 9 -321 22 -385 30 -11 1
--28 3 -37 4 -42 3 -47 23 -43 190 2 88 6 167 8 178 3 10 8 41 11 68 34 306
-240 683 501 912 193 170 375 257 586 279 35 3 66 8 68 10 6 6 232 -3 261 -9z
-m-909 -2041 c176 -9 246 -19 383 -53 247 -60 370 -151 296 -218 -26 -23 -75
--21 -230 10 -134 27 -284 54 -340 60 -19 2 -53 6 -75 9 -22 3 -69 8 -105 11
--36 4 -83 8 -105 10 -228 22 -667 22 -880 0 -22 -2 -69 -7 -105 -10 -104 -10
--216 -24 -240 -30 -8 -2 -35 -7 -60 -10 -25 -4 -112 -20 -195 -36 -188 -38
--218 -39 -248 -8 -13 13 -22 30 -21 37 1 6 2 16 3 22 2 20 58 63 118 91 65 31
-245 79 353 94 67 9 136 14 265 21 72 3 131 7 133 8 4 5 960 -3 1053 -8z m-381
--371 c125 -7 306 -22 360 -30 14 -2 48 -6 75 -10 28 -3 66 -9 85 -11 74 -10
-248 -46 315 -66 85 -24 204 -80 228 -106 33 -37 28 -89 -11 -118 -14 -10 -97
-3 -197 30 -5 2 -28 7 -50 11 -49 11 -47 10 -65 17 -8 4 -69 18 -135 32 -107
-22 -155 31 -286 50 -92 14 -269 20 -549 21 -361 0 -509 -9 -700 -44 -30 -6
--66 -12 -80 -15 -14 -2 -81 -18 -150 -36 -69 -17 -134 -33 -145 -35 -11 -3
--59 -13 -107 -24 l-87 -19 -20 22 c-11 11 -23 33 -26 48 -19 75 164 162 450
-214 41 7 161 26 195 30 19 2 50 6 69 9 18 3 68 8 110 10 42 3 92 7 111 10 19
-2 87 7 150 11 63 3 116 7 118 9 4 4 223 -2 342 -10z"/>
-<path d="M2525 3631 c-31 -13 -71 -58 -93 -104 -36 -77 -44 -226 -17 -344 3
--13 7 -33 10 -44 17 -78 85 -227 117 -256 19 -17 20 -17 35 2 32 42 94 183
-114 260 20 79 29 235 17 300 -16 82 -60 155 -110 181 -30 16 -44 16 -73 5z"/>
-<path d="M1197 3094 c-1 -1 -31 -4 -67 -7 -97 -9 -223 -49 -282 -89 -14 -10
--29 -18 -32 -18 -34 0 -206 -193 -206 -231 0 -5 -4 -17 -9 -27 -39 -71 -51
--165 -31 -241 13 -52 81 -192 118 -241 64 -87 236 -266 369 -387 48 -43 101
--91 118 -107 58 -54 74 -58 177 -41 51 8 104 17 118 20 14 3 41 8 60 11 19 3
-80 14 135 24 117 21 143 26 310 51 44 6 91 13 105 14 113 14 139 17 169 19 20
-1 37 10 43 20 22 39 -19 225 -92 416 -96 254 -210 452 -319 552 -166 154 -387
-249 -604 260 -42 3 -78 3 -80 2z"/>
-<path d="M3858 3095 c-2 -1 -34 -5 -73 -8 -68 -6 -144 -21 -175 -34 -8 -3 -20
--7 -25 -8 -23 -5 -121 -49 -135 -60 -8 -7 -25 -16 -37 -19 -13 -4 -23 -12 -23
--17 0 -5 -7 -9 -15 -9 -8 0 -15 -4 -15 -10 0 -5 -5 -10 -11 -10 -6 0 -17 -6
--24 -12 -8 -7 -44 -40 -80 -73 -82 -73 -139 -152 -198 -270 -59 -120 -87 -182
--87 -194 0 -6 -4 -11 -10 -11 -5 0 -10 -9 -10 -19 0 -11 -4 -23 -10 -26 -5 -3
--10 -13 -10 -22 0 -8 -6 -29 -14 -46 -37 -82 -85 -266 -86 -327 -1 -58 3 -70
-19 -71 9 -1 27 -2 41 -4 14 -2 52 -6 85 -10 33 -3 71 -8 85 -10 14 -3 43 -7
-65 -10 64 -10 84 -13 190 -30 55 -9 109 -18 120 -19 21 -3 126 -23 319 -62 66
--13 125 -20 132 -16 53 30 475 446 546 539 123 159 158 294 113 438 -42 135
--126 248 -239 320 -73 47 -215 93 -307 101 -41 3 -86 7 -101 9 -15 2 -28 2
--30 0z"/>
-</g>
-</svg>
const ChallengeModel =
{
+ checkChallenge: function(c)
+ {
+ const vid = parseInt(c.vid);
+ if (isNaN(vid) || vid <= 0)
+ return "Please select a variant";
+
+ const mainTime = parseInt(c.mainTime);
+ const increment = parseInt(c.increment);
+ if (isNaN(mainTime) || mainTime <= 0)
+ return "Main time should be strictly positive";
+ if (isNaN(increment) || increment < 0)
+ return "Increment must be positive";
+
+ // Basic alphanumeric check for players names
+ let playerCount = 0;
+ for (p of c.players)
+ {
+ if (p.name.length > 0)
+ {
+ if (!p.name.match(/^[\w]+$/))
+ return "Wrong characters in players names";
+ playerCount++;
+ }
+ }
+
+ if (playerCount > 0 && playerCount != c.nbPlayers-1)
+ return "None, or all of the opponent names must be filled"
+
+ // Just characters check on server:
+ if (!c.fen.match(/^[a-zA-Z0-9, /-]*$/))
+ return "Bad FEN string";
+ },
+
// fen cannot be undefined; TODO: generate fen on server instead
create: function(c, cb)
{
const UserModel =
{
+ checkNameEmail: function(o)
+ {
+ if (typeof o.name === "string")
+ {
+ if (o.name.length == 0)
+ return "Empty name";
+ if (!o.name.match(/^[\w]+$/))
+ return "Bad characters in name";
+ }
+ if (typeof o.email === "string")
+ {
+ if (o.email.length == 0)
+ return "Empty email";
+ if (!o.email.match(/^[\w.+-]+@[\w.+-]+$/))
+ return "Bad characters in email";
+ }
+ },
+
// NOTE: parameters are already cleaned (in controller), thus no sanitization here
create: function(name, email, notify, callback)
{
const VariantModel =
{
+ // This is duplicated in client. TODO: really required here?
+ NbPlayers:
+ {
+ "Alice": [2,3,4],
+ "Antiking": [2,3,4],
+ "Atomic": [2,3,4],
+ "Baroque": [2,3,4],
+ "Berolina": [2,4],
+ "Checkered": [2,3,4],
+ "Chess960": [2,3,4],
+ "Crazyhouse": [2,3,4],
+ "Dark": [2,3,4],
+ "Extinction": [2,3,4],
+ "Grand": [2],
+ "Losers": [2,3,4],
+ "Magnetic": [2],
+ "Marseille": [2],
+ "Switching": [2,3,4],
+ "Upsidedown": [2],
+ "Wildebeest": [2],
+ "Zen": [2,3,4],
+ },
+
getByName: function(name, callback)
{
db.serialize(function() {
let router = require("express").Router();
const access = require("../utils/access");
const ChallengeModel = require("../models/Challenge");
-const checkChallenge = require("../data/challengeCheck.js");
router.post("/challenges/:vid([0-9]+)", access.logged, access.ajax, (req,res) => {
const vid = req.params["vid"];
nbPlayers: req.body["nbPlayers"],
players: req.body["players"],
};
- const error = checkChallenge(chall);
+ const error = ChallengeModel.checkChallenge(chall);
ChallengeModel.create(chall, (err,lastId) => {
res.json(err || {cid: lastId["rowid"]});
});
var genToken = require("../utils/tokenGenerator");
var access = require("../utils/access");
var params = require("../config/parameters");
-var checkNameEmail = require("../data/userCheck")
// to: object user (to who we send an email)
function setAndSendLoginToken(subject, to, res)
const name = req.body.name;
const email = req.body.email;
const notify = !!req.body.notify;
- const error = checkNameEmail({name: name, email: email});
+ const error = UserModel.checkNameEmail({name: name, email: email});
if (!!error)
return res.json({errmsg: error});
UserModel.create(name, email, notify, (err,uid) => {
router.get('/sendtoken', access.unlogged, access.ajax, (req,res) => {
const nameOrEmail = decodeURIComponent(req.query.nameOrEmail);
const type = (nameOrEmail.indexOf('@') >= 0 ? "email" : "name");
- const error = checkNameEmail({[type]: nameOrEmail});
+ const error = UserModel.checkNameEmail({[type]: nameOrEmail});
if (!!error)
return res.json({errmsg: error});
UserModel.getOne(type, nameOrEmail, (err,user) => {
router.put('/update', access.logged, access.ajax, (req,res) => {
const name = req.body.name;
const email = req.body.email;
- const error = checkNameEmail({name: name, email: email});
+ const error = UserModel.checkNameEmail({name: name, email: email});
if (!!error)
return res.json({errmsg: error});
const user = {
const VariantModel = require("./models/Variant");
// Node version in Ubuntu 16.04 does not know about URL class
-function getJsonFromUrl(url) {
- var query = url.substr(2); //starts with "/?"
- var result = {};
- query.split("&").forEach(function(part) {
- var item = part.split("=");
+function getJsonFromUrl(url)
+{
+ const query = url.substr(2); //starts with "/?"
+ let result = {};
+ query.split("&").forEach((part) => {
+ const item = part.split("=");
result[item[0]] = decodeURIComponent(item[1]);
});
return result;