- render(h) {
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const smallScreen = (window.innerWidth <= 420);
- // Precompute hints squares to facilitate rendering
- let hintSquares = doubleArray(sizeX, sizeY, false);
- this.possibleMoves.forEach(m => { hintSquares[m.end.x][m.end.y] = true; });
- // Also precompute in-check squares
- let incheckSq = doubleArray(sizeX, sizeY, false);
- this.incheck.forEach(sq => { incheckSq[sq[0]][sq[1]] = true; });
- let elementArray = [];
- let actionArray = [];
- actionArray.push(
- h('button',
- {
- on: { click: this.clickGameSeek },
- attrs: { "aria-label": 'New online game' },
- 'class': {
- "tooltip": true,
- "bottom": true, //display below
- "seek": this.seek,
- "playing": this.mode == "human",
- "small": smallScreen,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "accessibility")])
- );
- if (["idle","chat","computer"].includes(this.mode))
- {
- actionArray.push(
- h('button',
- {
- on: { click: this.clickComputerGame },
- attrs: { "aria-label": 'New game VS computer' },
- 'class': {
- "tooltip":true,
- "bottom": true,
- "playing": this.mode == "computer",
- "small": smallScreen,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "computer")])
- );
- }
- if (["idle","chat","friend"].includes(this.mode))
- {
- actionArray.push(
- h('button',
- {
- on: { click: this.clickFriendGame },
- attrs: { "aria-label": 'New IRL game' },
- 'class': {
- "tooltip":true,
- "bottom": true,
- "playing": this.mode == "friend",
- "small": smallScreen,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "people")])
- );
- }
- if (!!this.vr)
- {
- const square00 = document.getElementById("sq-0-0");
- const squareWidth = !!square00
- ? parseFloat(window.getComputedStyle(square00).width.slice(0,-2))
- : 0;
- const settingsBtnElt = document.getElementById("settingsBtn");
- const indicWidth = !!settingsBtnElt //-2 for border:
- ? parseFloat(window.getComputedStyle(settingsBtnElt).height.slice(0,-2)) - 2
- : (smallScreen ? 31 : 37);
- if (["chat","human"].includes(this.mode))
- {
- const connectedIndic = h(
- 'div',
- {
- "class": {
- "topindicator": true,
- "indic-left": true,
- "connected": this.oppConnected,
- "disconnected": !this.oppConnected,
- },
- style: {
- "width": indicWidth + "px",
- "height": indicWidth + "px",
- },
- }
- );
- elementArray.push(connectedIndic);
- }
- if (this.mode == "chat")
- {
- const chatButton = h(
- 'button',
- {
- on: { click: this.startChat },
- attrs: {
- "aria-label": 'Start chat',
- "id": "chatBtn",
- },
- 'class': {
- "tooltip": true,
- "topindicator": true,
- "indic-left": true,
- "settings-btn": !smallScreen,
- "settings-btn-small": smallScreen,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "chat")]
- );
- elementArray.push(chatButton);
- }
- else if (this.mode == "computer")
- {
- const clearButton = h(
- 'button',
- {
- on: { click: this.clearComputerGame },
- attrs: {
- "aria-label": 'Clear computer game',
- "id": "clearBtn",
- },
- 'class': {
- "tooltip": true,
- "topindicator": true,
- "indic-left": true,
- "settings-btn": !smallScreen,
- "settings-btn-small": smallScreen,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "clear")]
- );
- elementArray.push(clearButton);
- }
- const turnIndic = h(
- 'div',
- {
- "class": {
- "topindicator": true,
- "indic-right": true,
- "white-turn": this.vr.turn=="w",
- "black-turn": this.vr.turn=="b",
- },
- style: {
- "width": indicWidth + "px",
- "height": indicWidth + "px",
- },
- }
- );
- elementArray.push(turnIndic);
- const settingsBtn = h(
- 'button',
- {
- on: { click: this.showSettings },
- attrs: {
- "aria-label": 'Settings',
- "id": "settingsBtn",
- },
- 'class': {
- "tooltip": true,
- "topindicator": true,
- "indic-right": true,
- "settings-btn": !smallScreen,
- "settings-btn-small": smallScreen,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "settings")]
- );
- elementArray.push(settingsBtn);
- if (this.mode == "problem")
- {
- // Show problem instructions
- elementArray.push(
- h('div',
- {
- attrs: { id: "instructions-div" },
- "class": {
- "clearer": true,
- "section-content": true,
- },
- },
- [
- h('p',
- {
- attrs: { id: "problem-instructions" },
- domProps: { innerHTML: this.problem.instructions }
- }
- )
- ]
- )
- );
- }
- const choices = h('div',
- {
- attrs: { "id": "choices" },
- 'class': { 'row': true },
- style: {
- "display": this.choices.length>0?"block":"none",
- "top": "-" + ((sizeY/2)*squareWidth+squareWidth/2) + "px",
- "width": (this.choices.length * squareWidth) + "px",
- "height": squareWidth + "px",
- },
- },
- this.choices.map( m => { //a "choice" is a move
- return h('div',
- {
- 'class': {
- 'board': true,
- ['board'+sizeY]: true,
- },
- style: {
- 'width': (100/this.choices.length) + "%",
- 'padding-bottom': (100/this.choices.length) + "%",
- },
- },
- [h('img',
- {
- attrs: { "src": '/images/pieces/' +
- VariantRules.getPpath(m.appear[0].c+m.appear[0].p) + '.svg' },
- 'class': { 'choice-piece': true },
- on: {
- "click": e => { this.play(m); this.choices=[]; },
- // NOTE: add 'touchstart' event to fix a problem on smartphones
- "touchstart": e => { this.play(m); this.choices=[]; },
- },
- })
- ]
- );
- })
- );
- // Create board element (+ reserves if needed by variant or mode)
- const lm = this.vr.lastMove;
- const showLight = this.hints &&
- (!["idle","chat"].includes(this.mode) || this.cursor==this.vr.moves.length);
- const gameDiv = h('div',
- {
- 'class': { 'game': true },
- },
- [_.range(sizeX).map(i => {
- let ci = (this.mycolor=='w' ? i : sizeX-i-1);
- return h(
- 'div',
- {
- 'class': {
- 'row': true,
- },
- style: { 'opacity': this.choices.length>0?"0.5":"1" },
- },
- _.range(sizeY).map(j => {
- let cj = (this.mycolor=='w' ? j : sizeY-j-1);
- let elems = [];
- if (this.vr.board[ci][cj] != VariantRules.EMPTY)
- {
- elems.push(
- h(
- 'img',
- {
- 'class': {
- 'piece': true,
- 'ghost': !!this.selectedPiece
- && this.selectedPiece.parentNode.id == "sq-"+ci+"-"+cj,
- },
- attrs: {
- src: "/images/pieces/" +
- VariantRules.getPpath(this.vr.board[ci][cj]) + ".svg",
- },
- }
- )
- );
- }
- if (this.hints && hintSquares[ci][cj])
- {
- elems.push(
- h(
- 'img',
- {
- 'class': {
- 'mark-square': true,
- },
- attrs: {
- src: "/images/mark.svg",
- },
- }
- )
- );
- }
- return h(
- 'div',
- {
- 'class': {
- 'board': true,
- ['board'+sizeY]: true,
- 'light-square': (i+j)%2==0,
- 'dark-square': (i+j)%2==1,
- [this.color]: true,
- 'highlight': showLight && !!lm && _.isMatch(lm.end, {x:ci,y:cj}),
- 'incheck': showLight && incheckSq[ci][cj],
- },
- attrs: {
- id: this.getSquareId({x:ci,y:cj}),
- },
- },
- elems
- );
- })
- );
- }), choices]
- );
- if (!["idle","chat"].includes(this.mode))
- {
- actionArray.push(
- h('button',
- {
- on: { click: this.resign },
- attrs: { "aria-label": 'Resign' },
- 'class': {
- "tooltip":true,
- "bottom": true,
- "small": smallScreen,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "flag")])
- );
- }
- else if (this.vr.moves.length > 0)
- {
- // A game finished, and another is not started yet: allow navigation
- actionArray = actionArray.concat([
- h('button',
- {
- on: { click: e => this.undo() },
- attrs: { "aria-label": 'Undo' },
- "class": {
- "small": smallScreen,
- "spaceleft": true,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "fast_rewind")]),
- h('button',
- {
- on: { click: e => this.play() },
- attrs: { "aria-label": 'Play' },
- "class": { "small": smallScreen },
- },
- [h('i', { 'class': { "material-icons": true } }, "fast_forward")]),
- ]
- );
- }
- if (["friend","problem"].includes(this.mode))
- {
- actionArray = actionArray.concat(
- [
- h('button',
- {
- on: { click: this.undoInGame },
- attrs: { "aria-label": 'Undo' },
- "class": {
- "small": smallScreen,
- "spaceleft": true,
- },
- },
- [h('i', { 'class': { "material-icons": true } }, "undo")]
- ),
- h('button',
- {
- on: { click: () => { this.mycolor = this.vr.getOppCol(this.mycolor) } },
- attrs: { "aria-label": 'Flip' },
- "class": { "small": smallScreen },
- },
- [h('i', { 'class': { "material-icons": true } }, "cached")]
- ),
- ]);
- }
- elementArray.push(gameDiv);
- if (!!this.vr.reserve)
- {
- const shiftIdx = (this.mycolor=="w" ? 0 : 1);
- let myReservePiecesArray = [];
- for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
- {
- myReservePiecesArray.push(h('div',
- {
- 'class': {'board':true, ['board'+sizeY]:true},
- attrs: { id: this.getSquareId({x:sizeX+shiftIdx,y:i}) }
- },
- [
- h('img',
- {
- 'class': {"piece":true},
- attrs: {
- "src": "/images/pieces/" +
- this.vr.getReservePpath(this.mycolor,i) + ".svg",
- }
- }),
- h('sup',
- {"class": { "reserve-count": true } },
- [ this.vr.reserve[this.mycolor][VariantRules.RESERVE_PIECES[i]] ]
- )
- ]));
- }
- let oppReservePiecesArray = [];
- const oppCol = this.vr.getOppCol(this.mycolor);
- for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
- {
- oppReservePiecesArray.push(h('div',
- {
- 'class': {'board':true, ['board'+sizeY]:true},
- attrs: { id: this.getSquareId({x:sizeX+(1-shiftIdx),y:i}) }
- },
- [
- h('img',
- {
- 'class': {"piece":true},
- attrs: {
- "src": "/images/pieces/" +
- this.vr.getReservePpath(oppCol,i) + ".svg",
- }
- }),
- h('sup',
- {"class": { "reserve-count": true } },
- [ this.vr.reserve[oppCol][VariantRules.RESERVE_PIECES[i]] ]
- )
- ]));
- }
- let reserves = h('div',
- {
- 'class':{
- 'game': true,
- "reserve-div": true,
- },
- },
- [
- h('div',
- {
- 'class': {
- 'row': true,
- "reserve-row-1": true,
- },
- },
- myReservePiecesArray
- ),
- h('div',
- { 'class': { 'row': true }},
- oppReservePiecesArray
- )
- ]
- );
- elementArray.push(reserves);
- }
- const modalEog = [
- h('input',
- {
- attrs: { "id": "modal-eog", type: "checkbox" },
- "class": { "modal": true },
- }),
- h('div',
- {
- attrs: { "role": "dialog", "aria-labelledby": "eogMessage" },
- },
- [
- h('div',
- {
- "class": { "card": true, "smallpad": true },
- },
- [
- h('label',
- {
- attrs: { "for": "modal-eog" },
- "class": { "modal-close": true },
- }
- ),
- h('h3',
- {
- attrs: { "id": "eogMessage" },
- "class": { "section": true },
- domProps: { innerHTML: this.endgameMessage },
- }
- )
- ]
- )
- ]
- )
- ];
- elementArray = elementArray.concat(modalEog);
- }
- // NOTE: this modal could be in Pug view (no usage of Vue functions or variables)
- const modalNewgame = [
- h('input',
- {
- attrs: { "id": "modal-newgame", type: "checkbox" },
- "class": { "modal": true },
- }),
- h('div',
- {
- attrs: { "role": "dialog", "aria-labelledby": "newGameTxt" },
- },
- [
- h('div',
- {
- "class": { "card": true, "smallpad": true },
- },
- [
- h('label',
- {
- attrs: { "id": "close-newgame", "for": "modal-newgame" },
- "class": { "modal-close": true },
- }
- ),
- h('h3',
- {
- attrs: { "id": "newGameTxt" },
- "class": { "section": true },
- domProps: { innerHTML: "New game" },
- }
- ),
- h('p',
- {
- "class": { "section": true },
- domProps: { innerHTML: "Waiting for opponent..." },
- }
- )
- ]
- )
- ]
- )
- ];
- elementArray = elementArray.concat(modalNewgame);
- const modalFenEdit = [
- h('input',
- {
- attrs: { "id": "modal-fenedit", type: "checkbox" },
- "class": { "modal": true },
- }),
- h('div',
- {
- attrs: { "role": "dialog", "aria-labelledby": "titleFenedit" },
- },
- [
- h('div',
- {
- "class": { "card": true, "smallpad": true },
- },
- [
- h('label',
- {
- attrs: { "id": "close-fenedit", "for": "modal-fenedit" },
- "class": { "modal-close": true },
- }
- ),
- h('h3',
- {
- attrs: { "id": "titleFenedit" },
- "class": { "section": true },
- domProps: { innerHTML: "Position + flags (FEN):" },
- }
- ),
- h('input',
- {
- attrs: {
- "id": "input-fen",
- type: "text",
- value: VariantRules.GenRandInitFen(),
- },
- }
- ),
- h('button',
- {
- on: { click:
- () => {
- const fen = document.getElementById("input-fen").value;
- document.getElementById("modal-fenedit").checked = false;
- this.newGame("friend", fen);
- }
- },
- domProps: { innerHTML: "Ok" },
- }
- ),
- h('button',
- {
- on: { click:
- () => {
- document.getElementById("input-fen").value =
- VariantRules.GenRandInitFen();
- }
- },
- domProps: { innerHTML: "Random" },
- }
- ),
- ]
- )
- ]
- )
- ];
- elementArray = elementArray.concat(modalFenEdit);
- const modalSettings = [
- h('input',
- {
- attrs: { "id": "modal-settings", type: "checkbox" },
- "class": { "modal": true },
- }),
- h('div',
- {
- attrs: { "role": "dialog", "aria-labelledby": "settingsTitle" },
- },
- [
- h('div',
- {
- "class": { "card": true, "smallpad": true },
- },
- [
- h('label',
- {
- attrs: { "id": "close-settings", "for": "modal-settings" },
- "class": { "modal-close": true },
- }
- ),
- h('h3',
- {
- attrs: { "id": "settingsTitle" },
- "class": { "section": true },
- domProps: { innerHTML: "Preferences" },
- }
- ),
- h('fieldset',
- { },
- [
- h('label',
- {
- attrs: { for: "nameSetter" },
- domProps: { innerHTML: "My name is..." },
- },
- ),
- h('input',
- {
- attrs: {
- "id": "nameSetter",
- type: "text",
- value: this.myname,
- },
- on: { "change": this.setMyname },
- }
- ),
- ]
- ),
- h('fieldset',
- { },
- [
- h('label',
- {
- attrs: { for: "setHints" },
- domProps: { innerHTML: "Show hints?" },
- },
- ),
- h('input',
- {
- attrs: {
- "id": "setHints",
- type: "checkbox",
- checked: this.hints,
- },
- on: { "change": this.toggleHints },
- }
- ),
- ]
- ),
- h('fieldset',
- { },
- [
- h('label',
- {
- attrs: { for: "selectColor" },
- domProps: { innerHTML: "Board colors" },
- },
- ),
- h("select",
- {
- attrs: { "id": "selectColor" },
- on: { "change": this.setColor },
- },
- [
- h("option",
- {
- domProps: {
- "value": "lichess",
- innerHTML: "brown"
- },
- attrs: { "selected": this.color=="lichess" },
- }
- ),
- h("option",
- {
- domProps: {
- "value": "chesscom",
- innerHTML: "green"
- },
- attrs: { "selected": this.color=="chesscom" },
- }
- ),
- h("option",
- {
- domProps: {
- "value": "chesstempo",
- innerHTML: "blue"
- },
- attrs: { "selected": this.color=="chesstempo" },
- }
- ),
- ],
- ),
- ]
- ),
- h('fieldset',
- { },
- [
- h('label',
- {
- attrs: { for: "selectSound" },
- domProps: { innerHTML: "Play sounds?" },
- },
- ),
- h("select",
- {
- attrs: { "id": "selectSound" },
- on: { "change": this.setSound },
- },
- [
- h("option",
- {
- domProps: {
- "value": "0",
- innerHTML: "None"
- },
- attrs: { "selected": this.sound==0 },
- }
- ),
- h("option",
- {
- domProps: {
- "value": "1",
- innerHTML: "Newgame"
- },
- attrs: { "selected": this.sound==1 },
- }
- ),
- h("option",
- {
- domProps: {
- "value": "2",
- innerHTML: "All"
- },
- attrs: { "selected": this.sound==2 },
- }
- ),
- ],
- ),
- ]
- ),
- ]
- )
- ]
- )
- ];
- elementArray = elementArray.concat(modalSettings);
- let chatEltsArray =
- [
- h('label',
- {
- attrs: { "id": "close-chat", "for": "modal-chat" },
- "class": { "modal-close": true },
- }
- ),
- h('h3',
- {
- attrs: { "id": "titleChat" },
- "class": { "section": true },
- domProps: { innerHTML: "Chat with " + this.oppName },
- }
- )
- ];
- for (let chat of this.chats)
- {
- chatEltsArray.push(
- h('p',
- {
- "class": {
- "my-chatmsg": chat.author==this.myid,
- "opp-chatmsg": chat.author==this.oppid,
- },
- domProps: { innerHTML: chat.msg }
- }
- )
- );
- }
- chatEltsArray = chatEltsArray.concat([
- h('input',
- {
- attrs: {
- "id": "input-chat",
- type: "text",
- placeholder: "Type here",
- },
- on: { keyup: this.trySendChat }, //if key is 'enter'
- }
- ),
- h('button',
- {
- on: { click: this.sendChat },
- domProps: { innerHTML: "Send" },
- }
- )
- ]);
- const modalChat = [
- h('input',
- {
- attrs: { "id": "modal-chat", type: "checkbox" },
- "class": { "modal": true },
- }),
- h('div',
- {
- attrs: { "role": "dialog", "aria-labelledby": "titleChat" },
- },
- [
- h('div',
- {
- "class": { "card": true, "smallpad": true },
- },
- chatEltsArray
- )
- ]
- )
- ];
- elementArray = elementArray.concat(modalChat);
- const actions = h('div',
- {
- attrs: { "id": "actions" },
- 'class': { 'text-center': true },
- },
- actionArray
- );
- elementArray.push(actions);
- if (this.score != "*" && this.pgnTxt.length > 0)
- {
- elementArray.push(
- h('div',
- {
- attrs: { id: "pgn-div" },
- "class": { "section-content": true },
- },
- [
- h('a',
- {
- attrs: {
- id: "download",
- href: "#",
- }
- }
- ),
- h('p',
- {
- attrs: { id: "pgn-game" },
- domProps: { innerHTML: this.pgnTxt }
- }
- ),
- h('button',
- {
- attrs: { "id": "downloadBtn" },
- on: { click: this.download },
- domProps: { innerHTML: "Download game" },
- }
- ),
- ]
- )
- );
- }
- else if (this.mode != "idle")
- {
- if (this.mode == "problem")
- {
- // Show problem solution (on click)