X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=public%2Fjavascripts%2Fcomponents%2Fboard.js;fp=public%2Fjavascripts%2Fcomponents%2Fboard.js;h=4cadb0bd9ba75d68e8d2950aa302c3763564e301;hb=81da2786f2f497b4416e0488c34a48fb794c28df;hp=0000000000000000000000000000000000000000;hpb=c5fa5762dc441fb1cb669908fae01d0d128e372d;p=vchess.git diff --git a/public/javascripts/components/board.js b/public/javascripts/components/board.js new file mode 100644 index 00000000..4cadb0bd --- /dev/null +++ b/public/javascripts/components/board.js @@ -0,0 +1,385 @@ + hints: (!localStorage["hints"] ? true : localStorage["hints"] === "1"), + bcolor: localStorage["bcolor"] || "lichess", //lichess, chesscom or chesstempo + possibleMoves: [], //filled after each valid click/dragstart + choices: [], //promotion pieces, or checkered captures... (as moves) + selectedPiece: null, //moving piece (or clicked piece) + incheck: [], + start: {}, //pixels coordinates + id of starting square (click or drag) + vr: null, //object to check moves, store them, FEN.. + orientation: "w", //useful if click on "flip board" + + + + const [sizeX,sizeY] = [V.size.x,V.size.y]; + // 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; }); + 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 && variant!="Dark" && + (this.mode != "idle" || + (this.vr.moves.length > 0 && this.cursor==this.vr.moves.length)); + const gameDiv = h('div', + { + 'class': { + 'game': true, + 'clearer': 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 && (variant!="Dark" + || this.score!="*" || this.vr.enlightened[this.mycolor][ci][cj])) + { + 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.bcolor]: true, + 'in-shadow': variant=="Dark" && this.score=="*" + && !this.vr.enlightened[this.mycolor][ci][cj], + '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 (!!this.vr.reserve) + { + const shiftIdx = (this.mycolor=="w" ? 0 : 1); + let myReservePiecesArray = []; + for (let i=0; i 1) + this.choices = moves; + else if (moves.length==1) + this.play(moves[0]); + // Else: impossible move + this.selectedPiece.parentNode.removeChild(this.selectedPiece); + delete this.selectedPiece; + this.selectedPiece = null; + }, + findMatchingMoves: function(endSquare) { + // Run through moves list and return the matching set (if promotions...) + let moves = []; + this.possibleMoves.forEach(function(m) { + if (endSquare[0] == m.end.x && endSquare[1] == m.end.y) + moves.push(m); + }); + return moves; + }, + animateMove: function(move) { + let startSquare = document.getElementById(this.getSquareId(move.start)); + let endSquare = document.getElementById(this.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"); + // 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 { + for (let i=0; i