0399410f5049f39b3b5038200ead5db38e4bd60f
1 Vue
.component('my-board', {
2 // Last move cannot be guessed from here, and is required to highlight squares
3 // vr: object to check moves, print board...
4 // mode: HH, HC or analyze
5 // userColor: for mode HH or HC
6 props: ["vr","lastMove","mode","orientation","userColor"],
9 hints: (!localStorage
["hints"] ? true : localStorage
["hints"] === "1"),
10 bcolor: localStorage
["bcolor"] || "lichess", //lichess, chesscom or chesstempo
11 possibleMoves: [], //filled after each valid click/dragstart
12 choices: [], //promotion pieces, or checkered captures... (as moves)
13 selectedPiece: null, //moving piece (or clicked piece)
15 start: {}, //pixels coordinates + id of starting square (click or drag)
21 const [sizeX
,sizeY
] = [V
.size
.x
,V
.size
.y
];
22 // Precompute hints squares to facilitate rendering
23 let hintSquares
= doubleArray(sizeX
, sizeY
, false);
24 this.possibleMoves
.forEach(m
=> { hintSquares
[m
.end
.x
][m
.end
.y
] = true; });
25 // Also precompute in-check squares
26 let incheckSq
= doubleArray(sizeX
, sizeY
, false);
27 this.incheck
.forEach(sq
=> { incheckSq
[sq
[0]][sq
[1]] = true; });
28 const squareWidth
= 40; //TODO: compute this
32 attrs: { "id": "choices" },
33 'class': { 'row': true },
35 "display": this.choices
.length
>0?"block":"none",
36 "top": "-" + ((sizeY
/2)*squareWidth+squareWidth/2) + "px",
37 "width": (this.choices
.length
* squareWidth
) + "px",
38 "height": squareWidth
+ "px",
41 this.choices
.map(m
=> { //a "choice" is a move
46 ['board'+sizeY
]: true,
49 'width': (100/this.choices
.length
) + "%",
50 'padding-bottom': (100/this.choices
.length
) + "%",
55 attrs: { "src": '/images/pieces/' +
56 V
.getPpath(m
.appear
[0].c
+m
.appear
[0].p
) + '.svg' },
57 'class': { 'choice-piece': true },
59 "click": e
=> { this.play(m
); this.choices
=[]; },
60 // NOTE: add 'touchstart' event to fix a problem on smartphones
61 "touchstart": e
=> { this.play(m
); this.choices
=[]; },
68 // Create board element (+ reserves if needed by variant or mode)
69 const lm
= this.lastMove
;
70 const showLight
= this.hints
&& variant
.name
!= "Dark";
79 [_
.range(sizeX
).map(i
=> {
80 let ci
= (this.orientation
=='w' ? i : sizeX
-i
-1);
87 style: { 'opacity': this.choices
.length
>0?"0.5":"1" },
89 _
.range(sizeY
).map(j
=> {
90 let cj
= (this.orientation
=='w' ? j : sizeY
-j
-1);
92 if (this.vr
.board
[ci
][cj
] != V
.EMPTY
&& (variant
.name
!="Dark"
93 || this.gameOver
|| this.mode
== "analyze"
94 || this.vr
.enlightened
[this.userColor
][ci
][cj
]))
102 'ghost': !!this.selectedPiece
103 && this.selectedPiece
.parentNode
.id
== "sq-"+ci
+"-"+cj
,
106 src: "/images/pieces/" +
107 V
.getPpath(this.vr
.board
[ci
][cj
]) + ".svg",
113 if (this.hints
&& hintSquares
[ci
][cj
])
123 src: "/images/mark.svg",
134 ['board'+sizeY
]: true,
135 'light-square': (i
+j
)%2==0,
136 'dark-square': (i
+j
)%2==1,
138 'in-shadow': variant
.name
=="Dark" && !this.gameOver
139 && this.mode
!= "analyze"
140 && !this.vr
.enlightened
[this.userColor
][ci
][cj
],
141 'highlight': showLight
&& !!lm
&& _
.isMatch(lm
.end
, {x:ci
,y:cj
}),
142 'incheck': showLight
&& incheckSq
[ci
][cj
],
145 id: this.getSquareId({x:ci
,y:cj
}),
154 let elementArray
= [choices
, gameDiv
];
155 if (!!this.vr
.reserve
)
157 const shiftIdx
= (this.userColor
=="w" ? 0 : 1);
158 let myReservePiecesArray
= [];
159 for (let i
=0; i
<V
.RESERVE_PIECES
.length
; i
++)
161 myReservePiecesArray
.push(h('div',
163 'class': {'board':true, ['board'+sizeY
]:true},
164 attrs: { id: this.getSquareId({x:sizeX
+shiftIdx
,y:i
}) }
169 'class': {"piece":true, "reserve":true},
171 "src": "/images/pieces/" +
172 this.vr
.getReservePpath(this.userColor
,i
) + ".svg",
176 {"class": { "reserve-count": true } },
177 [ this.vr
.reserve
[this.userColor
][V
.RESERVE_PIECES
[i
]] ]
181 let oppReservePiecesArray
= [];
182 const oppCol
= V
.GetOppCol(this.userColor
);
183 for (let i
=0; i
<V
.RESERVE_PIECES
.length
; i
++)
185 oppReservePiecesArray
.push(h('div',
187 'class': {'board':true, ['board'+sizeY
]:true},
188 attrs: { id: this.getSquareId({x:sizeX
+(1-shiftIdx
),y:i
}) }
193 'class': {"piece":true, "reserve":true},
195 "src": "/images/pieces/" +
196 this.vr
.getReservePpath(oppCol
,i
) + ".svg",
200 {"class": { "reserve-count": true } },
201 [ this.vr
.reserve
[oppCol
][V
.RESERVE_PIECES
[i
]] ]
205 let reserves
= h('div',
217 "reserve-row-1": true,
223 { 'class': { 'row': true }},
224 oppReservePiecesArray
228 elementArray
.push(reserves
);
236 "col-md-offset-1":true,
238 "col-lg-offset-2":true,
240 // NOTE: click = mousedown + mouseup
242 mousedown: this.mousedown
,
243 mousemove: this.mousemove
,
244 mouseup: this.mouseup
,
245 touchstart: this.mousedown
,
246 touchmove: this.mousemove
,
247 touchend: this.mouseup
,
254 // Get the identifier of a HTML square from its numeric coordinates o.x,o.y.
255 getSquareId: function(o
) {
256 // NOTE: a separator is required to allow any size of board
257 return "sq-" + o
.x
+ "-" + o
.y
;
260 getSquareFromId: function(id
) {
261 let idParts
= id
.split('-');
262 return [parseInt(idParts
[1]), parseInt(idParts
[2])];
264 mousedown: function(e
) {
265 e
= e
|| window
.event
;
268 while (!ingame
&& elem
!== null)
270 if (elem
.classList
.contains("game"))
275 elem
= elem
.parentElement
;
277 if (!ingame
) //let default behavior (click on button...)
279 e
.preventDefault(); //disable native drag & drop
280 if (!this.selectedPiece
&& e
.target
.classList
.contains("piece"))
282 // Next few lines to center the piece on mouse cursor
283 let rect
= e
.target
.parentNode
.getBoundingClientRect();
285 x: rect
.x
+ rect
.width
/2,
286 y: rect
.y
+ rect
.width
/2,
287 id: e
.target
.parentNode
.id
289 this.selectedPiece
= e
.target
.cloneNode();
290 this.selectedPiece
.style
.position
= "absolute";
291 this.selectedPiece
.style
.top
= 0;
292 this.selectedPiece
.style
.display
= "inline-block";
293 this.selectedPiece
.style
.zIndex
= 3000;
294 const startSquare
= this.getSquareFromId(e
.target
.parentNode
.id
);
295 this.possibleMoves
= [];
296 const color
= this.mode
=="analyze" || this.gameOver
299 if (this.vr
.canIplay(color
,startSquare
))
300 this.possibleMoves
= this.vr
.getPossibleMovesFrom(startSquare
);
301 // Next line add moving piece just after current image
302 // (required for Crazyhouse reserve)
303 e
.target
.parentNode
.insertBefore(this.selectedPiece
, e
.target
.nextSibling
);
306 mousemove: function(e
) {
307 if (!this.selectedPiece
)
309 e
= e
|| window
.event
;
310 // If there is an active element, move it around
311 if (!!this.selectedPiece
)
313 const [offsetX
,offsetY
] = !!e
.clientX
314 ? [e
.clientX
,e
.clientY
] //desktop browser
315 : [e
.changedTouches
[0].pageX
, e
.changedTouches
[0].pageY
]; //smartphone
316 this.selectedPiece
.style
.left
= (offsetX
-this.start
.x
) + "px";
317 this.selectedPiece
.style
.top
= (offsetY
-this.start
.y
) + "px";
320 mouseup: function(e
) {
321 if (!this.selectedPiece
)
323 e
= e
|| window
.event
;
324 // Read drop target (or parentElement, parentNode... if type == "img")
325 this.selectedPiece
.style
.zIndex
= -3000; //HACK to find square from final coords
326 const [offsetX
,offsetY
] = !!e
.clientX
327 ? [e
.clientX
,e
.clientY
]
328 : [e
.changedTouches
[0].pageX
, e
.changedTouches
[0].pageY
];
329 let landing
= document
.elementFromPoint(offsetX
, offsetY
);
330 this.selectedPiece
.style
.zIndex
= 3000;
331 // Next condition: classList.contains(piece) fails because of marks
332 while (landing
.tagName
== "IMG")
333 landing
= landing
.parentNode
;
334 if (this.start
.id
== landing
.id
)
336 // A click: selectedPiece and possibleMoves are already filled
339 // OK: process move attempt
340 let endSquare
= this.getSquareFromId(landing
.id
);
341 let moves
= this.findMatchingMoves(endSquare
);
342 this.possibleMoves
= [];
343 if (moves
.length
> 1)
344 this.choices
= moves
;
345 else if (moves
.length
==1)
347 // Else: impossible move
348 this.selectedPiece
.parentNode
.removeChild(this.selectedPiece
);
349 delete this.selectedPiece
;
350 this.selectedPiece
= null;
352 findMatchingMoves: function(endSquare
) {
353 // Run through moves list and return the matching set (if promotions...)
355 this.possibleMoves
.forEach(function(m
) {
356 if (endSquare
[0] == m
.end
.x
&& endSquare
[1] == m
.end
.y
)
361 play: function(move) {
362 this.$emit('play-move', move);