cm-chessboard 3.18.2 → 4.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cm-chessboard/Chessboard.js +28 -20
- package/src/cm-chessboard/ChessboardMoveInput.js +62 -61
- package/src/cm-chessboard/ChessboardPiecesAnimation.js +7 -6
- package/src/cm-chessboard/ChessboardState.js +21 -98
- package/src/cm-chessboard/ChessboardView.js +35 -26
- package/src/cm-chessboard/ChessboardViewAccessible.js +14 -6
- package/src/cm-chessboard/Observed.js +40 -0
- package/src/cm-chessboard/Position.js +115 -0
- package/test/TestChessboard.js +53 -0
- package/test/TestMarkers.js +1 -1
- package/test/TestPiecesAnimation.js +3 -3
- package/test/TestPosition.js +13 -41
- package/test/index.html +3 -3
- package/test/TestState.js +0 -27
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {ChessboardView} from "./ChessboardView.js"
|
|
8
8
|
import {ChessboardState} from "./ChessboardState.js"
|
|
9
9
|
import {ChessboardViewAccessible} from "./ChessboardViewAccessible.js"
|
|
10
10
|
|
|
@@ -81,7 +81,17 @@ export class Chessboard {
|
|
|
81
81
|
if (this.props.style.aspectRatio) {
|
|
82
82
|
this.context.style.height = (this.context.offsetWidth * this.props.style.aspectRatio) + "px"
|
|
83
83
|
}
|
|
84
|
+
|
|
84
85
|
this.state = new ChessboardState()
|
|
86
|
+
const viewType = this.props.accessible ? ChessboardViewAccessible : ChessboardView
|
|
87
|
+
this.view = new viewType(this)
|
|
88
|
+
/*
|
|
89
|
+
this.state.addObserver(() => {
|
|
90
|
+
if(this.view) {
|
|
91
|
+
this.view.drawPieces()
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
*/
|
|
85
95
|
if (this.props.position === "start") {
|
|
86
96
|
this.state.setPosition(FEN_START_POSITION)
|
|
87
97
|
} else if (this.props.position === "empty" || this.props.position === undefined) {
|
|
@@ -90,36 +100,34 @@ export class Chessboard {
|
|
|
90
100
|
this.state.setPosition(this.props.position)
|
|
91
101
|
}
|
|
92
102
|
this.state.orientation = this.props.orientation
|
|
93
|
-
|
|
94
|
-
this.view = new viewType(this)
|
|
103
|
+
this.view.redraw()
|
|
95
104
|
}
|
|
96
|
-
|
|
97
105
|
// API //
|
|
98
106
|
|
|
99
107
|
setPiece(square, piece) {
|
|
100
|
-
this.state.setPiece(
|
|
101
|
-
this.view.drawPieces(this.state.squares)
|
|
108
|
+
this.state.position.setPiece(square, piece)
|
|
109
|
+
this.view.drawPieces(this.state.position.squares)
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
getPiece(square) {
|
|
105
|
-
return this.state.
|
|
113
|
+
return this.state.position.getPiece(square)
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
movePiece(squareFrom, squareTo, animated = true) {
|
|
109
117
|
return new Promise((resolve, reject) => {
|
|
110
|
-
const prevSquares = this.state.
|
|
118
|
+
const prevSquares = this.state.position.clone().squares
|
|
111
119
|
const pieceFrom = this.getPiece(squareFrom)
|
|
112
120
|
if(!pieceFrom) {
|
|
113
121
|
reject("no piece on square " + squareFrom)
|
|
114
122
|
} else {
|
|
115
|
-
this.state.
|
|
116
|
-
this.state.
|
|
123
|
+
this.state.position.setPiece(squareFrom, null)
|
|
124
|
+
this.state.position.setPiece(squareTo, pieceFrom)
|
|
117
125
|
if (animated) {
|
|
118
|
-
this.view.animatePieces(prevSquares, this.state.squares, () => {
|
|
126
|
+
this.view.animatePieces(prevSquares, this.state.position.squares, () => {
|
|
119
127
|
resolve()
|
|
120
128
|
})
|
|
121
129
|
} else {
|
|
122
|
-
this.view.drawPieces(this.state.squares)
|
|
130
|
+
this.view.drawPieces(this.state.position.squares)
|
|
123
131
|
resolve()
|
|
124
132
|
}
|
|
125
133
|
}
|
|
@@ -138,14 +146,14 @@ export class Chessboard {
|
|
|
138
146
|
const fenNormalized = fenParts[0]
|
|
139
147
|
|
|
140
148
|
if (fenNormalized !== currentFen) {
|
|
141
|
-
const prevSquares = this.state.squares.slice(0) // clone
|
|
149
|
+
const prevSquares = this.state.position.squares.slice(0) // clone
|
|
142
150
|
this.state.setPosition(fen)
|
|
143
151
|
if (animated) {
|
|
144
|
-
this.view.animatePieces(prevSquares, this.state.squares.slice(0), () => {
|
|
152
|
+
this.view.animatePieces(prevSquares, this.state.position.squares.slice(0), () => {
|
|
145
153
|
resolve()
|
|
146
154
|
})
|
|
147
155
|
} else {
|
|
148
|
-
this.view.drawPieces(this.state.squares)
|
|
156
|
+
this.view.drawPieces(this.state.position.squares)
|
|
149
157
|
resolve()
|
|
150
158
|
}
|
|
151
159
|
} else {
|
|
@@ -162,18 +170,18 @@ export class Chessboard {
|
|
|
162
170
|
if (!type) {
|
|
163
171
|
console.error("Error addMarker(), type is " + type)
|
|
164
172
|
}
|
|
165
|
-
this.state.addMarker(
|
|
173
|
+
this.state.addMarker(square, type)
|
|
166
174
|
this.view.drawMarkers()
|
|
167
175
|
}
|
|
168
176
|
|
|
169
177
|
getMarkers(square = undefined, type = undefined) {
|
|
170
178
|
const markersFound = []
|
|
171
179
|
this.state.markers.forEach((marker) => {
|
|
172
|
-
const markerSquare =
|
|
180
|
+
const markerSquare = marker.square
|
|
173
181
|
if (!square && (!type || type === marker.type) ||
|
|
174
182
|
!type && square === markerSquare ||
|
|
175
183
|
type === marker.type && square === markerSquare) {
|
|
176
|
-
markersFound.push({square:
|
|
184
|
+
markersFound.push({square: marker.square, type: marker.type})
|
|
177
185
|
}
|
|
178
186
|
})
|
|
179
187
|
return markersFound
|
|
@@ -236,7 +244,7 @@ export class Chessboard {
|
|
|
236
244
|
return
|
|
237
245
|
}
|
|
238
246
|
this.squareSelectListener = function (e) {
|
|
239
|
-
const
|
|
247
|
+
const square = e.target.getAttribute("data-square")
|
|
240
248
|
if (e.type === "contextmenu") {
|
|
241
249
|
// disable context menu
|
|
242
250
|
e.preventDefault()
|
|
@@ -245,7 +253,7 @@ export class Chessboard {
|
|
|
245
253
|
eventHandler({
|
|
246
254
|
chessboard: this,
|
|
247
255
|
type: e.button === 2 ? SQUARE_SELECT_TYPE.secondary : SQUARE_SELECT_TYPE.primary,
|
|
248
|
-
square:
|
|
256
|
+
square: square
|
|
249
257
|
})
|
|
250
258
|
}
|
|
251
259
|
this.context.addEventListener("contextmenu", this.squareSelectListener)
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {Svg} from "./ChessboardView.js"
|
|
8
|
+
// import {Position} from "./Position.js"
|
|
8
9
|
|
|
9
10
|
const STATE = {
|
|
10
11
|
waitForInputStart: 0,
|
|
@@ -61,8 +62,8 @@ export class ChessboardMoveInput {
|
|
|
61
62
|
removeEventListener(this.pointerUpListener.type, this.pointerUpListener)
|
|
62
63
|
this.pointerUpListener = undefined
|
|
63
64
|
}
|
|
64
|
-
this.
|
|
65
|
-
this.
|
|
65
|
+
this.fromSquare = params.square
|
|
66
|
+
this.toSquare = undefined
|
|
66
67
|
this.movedPiece = params.piece
|
|
67
68
|
this.updateStartEndMarkers()
|
|
68
69
|
this.startPoint = params.point
|
|
@@ -101,7 +102,7 @@ export class ChessboardMoveInput {
|
|
|
101
102
|
this.draggablePiece = undefined
|
|
102
103
|
}
|
|
103
104
|
if (prevState === STATE.dragTo) {
|
|
104
|
-
this.view.setPieceVisibility(params.
|
|
105
|
+
this.view.setPieceVisibility(params.square)
|
|
105
106
|
}
|
|
106
107
|
break
|
|
107
108
|
|
|
@@ -117,7 +118,7 @@ export class ChessboardMoveInput {
|
|
|
117
118
|
throw new Error("moveInputState")
|
|
118
119
|
}
|
|
119
120
|
if (this.view.chessboard.state.inputEnabled) {
|
|
120
|
-
this.view.setPieceVisibility(params.
|
|
121
|
+
this.view.setPieceVisibility(params.square, false)
|
|
121
122
|
this.createDraggablePiece(params.piece)
|
|
122
123
|
}
|
|
123
124
|
break
|
|
@@ -127,7 +128,7 @@ export class ChessboardMoveInput {
|
|
|
127
128
|
throw new Error("moveInputState")
|
|
128
129
|
}
|
|
129
130
|
if (this.view.chessboard.state.inputEnabled) {
|
|
130
|
-
this.view.setPieceVisibility(params.
|
|
131
|
+
this.view.setPieceVisibility(params.square, false)
|
|
131
132
|
this.createDraggablePiece(params.piece)
|
|
132
133
|
}
|
|
133
134
|
break
|
|
@@ -136,18 +137,18 @@ export class ChessboardMoveInput {
|
|
|
136
137
|
if ([STATE.dragTo, STATE.clickTo, STATE.clickDragTo].indexOf(prevState) === -1) {
|
|
137
138
|
throw new Error("moveInputState")
|
|
138
139
|
}
|
|
139
|
-
this.
|
|
140
|
-
if (this.
|
|
141
|
-
const prevSquares = this.chessboard.state.
|
|
142
|
-
this.chessboard.state.setPiece(this.
|
|
143
|
-
this.chessboard.state.setPiece(this.
|
|
140
|
+
this.toSquare = params.square
|
|
141
|
+
if (this.toSquare && this.moveDoneCallback(this.fromSquare, this.toSquare)) {
|
|
142
|
+
const prevSquares = this.chessboard.state.position.clone().squares
|
|
143
|
+
this.chessboard.state.position.setPiece(this.fromSquare, undefined)
|
|
144
|
+
this.chessboard.state.position.setPiece(this.toSquare, this.movedPiece)
|
|
144
145
|
if (prevState === STATE.clickTo) {
|
|
145
146
|
this.updateStartEndMarkers()
|
|
146
|
-
this.view.animatePieces(prevSquares, this.chessboard.state.
|
|
147
|
+
this.view.animatePieces(prevSquares, this.chessboard.state.position.clone().squares, () => {
|
|
147
148
|
this.setMoveInputState(STATE.reset)
|
|
148
149
|
})
|
|
149
150
|
} else {
|
|
150
|
-
this.view.drawPieces(this.chessboard.state.squares)
|
|
151
|
+
this.view.drawPieces(this.chessboard.state.position.squares)
|
|
151
152
|
// this.view.animationQueue = [] // todo remove all animations (See PR #59)
|
|
152
153
|
this.setMoveInputState(STATE.reset)
|
|
153
154
|
}
|
|
@@ -158,11 +159,11 @@ export class ChessboardMoveInput {
|
|
|
158
159
|
break
|
|
159
160
|
|
|
160
161
|
case STATE.reset:
|
|
161
|
-
if (this.
|
|
162
|
-
this.chessboard.state.setPiece(this.
|
|
162
|
+
if (this.fromSquare && !this.toSquare && this.movedPiece) {
|
|
163
|
+
this.chessboard.state.position.setPiece(this.fromSquare, this.movedPiece)
|
|
163
164
|
}
|
|
164
|
-
this.
|
|
165
|
-
this.
|
|
165
|
+
this.fromSquare = undefined
|
|
166
|
+
this.toSquare = undefined
|
|
166
167
|
this.movedPiece = undefined
|
|
167
168
|
this.updateStartEndMarkers()
|
|
168
169
|
if (this.draggablePiece) {
|
|
@@ -212,19 +213,19 @@ export class ChessboardMoveInput {
|
|
|
212
213
|
|
|
213
214
|
onPointerDown(e) {
|
|
214
215
|
if (e.type === "mousedown" && e.button === 0 || e.type === "touchstart") {
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
color = pieceName ? pieceName.
|
|
216
|
+
const square = e.target.getAttribute("data-square")
|
|
217
|
+
const pieceName = this.chessboard.getPiece(square)
|
|
218
|
+
console.log("onPointerDown", square, pieceName)
|
|
219
|
+
let color
|
|
220
|
+
if (pieceName) {
|
|
221
|
+
color = pieceName ? pieceName.substring(0, 1) : undefined
|
|
221
222
|
// allow scrolling, if not pointed on draggable piece
|
|
222
223
|
if (color === "w" && this.chessboard.state.inputWhiteEnabled ||
|
|
223
224
|
color === "b" && this.chessboard.state.inputBlackEnabled) {
|
|
224
225
|
e.preventDefault()
|
|
225
226
|
}
|
|
226
227
|
}
|
|
227
|
-
if (
|
|
228
|
+
if (square) { // pointer on square
|
|
228
229
|
if (this.moveInputState !== STATE.waitForInputStart ||
|
|
229
230
|
this.chessboard.state.inputWhiteEnabled && color === "w" ||
|
|
230
231
|
this.chessboard.state.inputBlackEnabled && color === "b") {
|
|
@@ -234,31 +235,31 @@ export class ChessboardMoveInput {
|
|
|
234
235
|
} else if (e.type === "touchstart") {
|
|
235
236
|
point = {x: e.touches[0].clientX, y: e.touches[0].clientY}
|
|
236
237
|
}
|
|
237
|
-
if (this.moveInputState === STATE.waitForInputStart && pieceName && this.moveStartCallback(
|
|
238
|
+
if (this.moveInputState === STATE.waitForInputStart && pieceName && this.moveStartCallback(square)) {
|
|
238
239
|
this.setMoveInputState(STATE.pieceClickedThreshold, {
|
|
239
|
-
|
|
240
|
+
square: square,
|
|
240
241
|
piece: pieceName,
|
|
241
242
|
point: point,
|
|
242
243
|
type: e.type
|
|
243
244
|
})
|
|
244
245
|
} else if (this.moveInputState === STATE.clickTo) {
|
|
245
|
-
if (
|
|
246
|
+
if (square === this.fromSquare) {
|
|
246
247
|
this.setMoveInputState(STATE.secondClickThreshold, {
|
|
247
|
-
|
|
248
|
+
square: square,
|
|
248
249
|
piece: pieceName,
|
|
249
250
|
point: point,
|
|
250
251
|
type: e.type
|
|
251
252
|
})
|
|
252
253
|
} else {
|
|
253
|
-
const pieceName = this.chessboard.getPiece(
|
|
254
|
-
const pieceColor = pieceName ? pieceName.
|
|
255
|
-
const startPieceName = this.chessboard.getPiece(
|
|
256
|
-
const startPieceColor = startPieceName ? startPieceName.
|
|
254
|
+
const pieceName = this.chessboard.getPiece(square)
|
|
255
|
+
const pieceColor = pieceName ? pieceName.substring(0, 1) : undefined
|
|
256
|
+
const startPieceName = this.chessboard.getPiece(this.fromSquare)
|
|
257
|
+
const startPieceColor = startPieceName ? startPieceName.substring(0, 1) : undefined
|
|
257
258
|
if (color && startPieceColor === pieceColor) { // https://github.com/shaack/cm-chessboard/issues/40
|
|
258
|
-
this.moveCanceledCallback(MOVE_CANCELED_REASON.clickedAnotherPiece, this.
|
|
259
|
-
if (this.moveStartCallback(
|
|
259
|
+
this.moveCanceledCallback(MOVE_CANCELED_REASON.clickedAnotherPiece, this.fromSquare, square)
|
|
260
|
+
if (this.moveStartCallback(square)) {
|
|
260
261
|
this.setMoveInputState(STATE.pieceClickedThreshold, {
|
|
261
|
-
|
|
262
|
+
square: square,
|
|
262
263
|
piece: pieceName,
|
|
263
264
|
point: point,
|
|
264
265
|
type: e.type
|
|
@@ -267,7 +268,7 @@ export class ChessboardMoveInput {
|
|
|
267
268
|
this.setMoveInputState(STATE.reset)
|
|
268
269
|
}
|
|
269
270
|
} else {
|
|
270
|
-
this.setMoveInputState(STATE.moveDone, {
|
|
271
|
+
this.setMoveInputState(STATE.moveDone, {square: square})
|
|
271
272
|
}
|
|
272
273
|
}
|
|
273
274
|
}
|
|
@@ -294,9 +295,9 @@ export class ChessboardMoveInput {
|
|
|
294
295
|
if (this.moveInputState === STATE.pieceClickedThreshold || this.moveInputState === STATE.secondClickThreshold) {
|
|
295
296
|
if (Math.abs(this.startPoint.x - clientX) > DRAG_THRESHOLD || Math.abs(this.startPoint.y - clientY) > DRAG_THRESHOLD) {
|
|
296
297
|
if (this.moveInputState === STATE.secondClickThreshold) {
|
|
297
|
-
this.setMoveInputState(STATE.clickDragTo, {
|
|
298
|
+
this.setMoveInputState(STATE.clickDragTo, {square: this.fromSquare, piece: this.movedPiece})
|
|
298
299
|
} else {
|
|
299
|
-
this.setMoveInputState(STATE.dragTo, {
|
|
300
|
+
this.setMoveInputState(STATE.dragTo, {square: this.fromSquare, piece: this.movedPiece})
|
|
300
301
|
}
|
|
301
302
|
if (this.view.chessboard.state.inputEnabled) {
|
|
302
303
|
this.moveDraggablePiece(pageX, pageY)
|
|
@@ -304,17 +305,17 @@ export class ChessboardMoveInput {
|
|
|
304
305
|
}
|
|
305
306
|
} else if (this.moveInputState === STATE.dragTo || this.moveInputState === STATE.clickDragTo || this.moveInputState === STATE.clickTo) {
|
|
306
307
|
if (target && target.getAttribute && target.parentElement === this.view.boardGroup) {
|
|
307
|
-
const
|
|
308
|
-
if (
|
|
309
|
-
this.
|
|
308
|
+
const square = target.getAttribute("data-square")
|
|
309
|
+
if (square !== this.fromSquare && square !== this.toSquare) {
|
|
310
|
+
this.toSquare = square
|
|
310
311
|
this.updateStartEndMarkers()
|
|
311
|
-
} else if (
|
|
312
|
-
this.
|
|
312
|
+
} else if (square === this.fromSquare && this.toSquare !== undefined) {
|
|
313
|
+
this.toSquare = undefined
|
|
313
314
|
this.updateStartEndMarkers()
|
|
314
315
|
}
|
|
315
316
|
} else {
|
|
316
|
-
if (this.
|
|
317
|
-
this.
|
|
317
|
+
if (this.toSquare !== undefined) {
|
|
318
|
+
this.toSquare = undefined
|
|
318
319
|
this.updateStartEndMarkers()
|
|
319
320
|
}
|
|
320
321
|
}
|
|
@@ -332,33 +333,33 @@ export class ChessboardMoveInput {
|
|
|
332
333
|
target = document.elementFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY)
|
|
333
334
|
}
|
|
334
335
|
if (target && target.getAttribute) {
|
|
335
|
-
const
|
|
336
|
+
const square = target.getAttribute("data-square")
|
|
336
337
|
|
|
337
|
-
if (
|
|
338
|
+
if (square) {
|
|
338
339
|
if (this.moveInputState === STATE.dragTo || this.moveInputState === STATE.clickDragTo) {
|
|
339
|
-
if (this.
|
|
340
|
+
if (this.fromSquare === square) {
|
|
340
341
|
if (this.moveInputState === STATE.clickDragTo) {
|
|
341
|
-
this.chessboard.state.setPiece(this.
|
|
342
|
-
this.view.setPieceVisibility(this.
|
|
343
|
-
this.moveCanceledCallback(MOVE_CANCELED_REASON.draggedBack,
|
|
342
|
+
this.chessboard.state.setPiece(this.fromSquare, this.movedPiece)
|
|
343
|
+
this.view.setPieceVisibility(this.fromSquare)
|
|
344
|
+
this.moveCanceledCallback(MOVE_CANCELED_REASON.draggedBack, square, square)
|
|
344
345
|
this.setMoveInputState(STATE.reset)
|
|
345
346
|
} else {
|
|
346
|
-
this.setMoveInputState(STATE.clickTo, {
|
|
347
|
+
this.setMoveInputState(STATE.clickTo, {square: square})
|
|
347
348
|
}
|
|
348
349
|
} else {
|
|
349
|
-
this.setMoveInputState(STATE.moveDone, {
|
|
350
|
+
this.setMoveInputState(STATE.moveDone, {square: square})
|
|
350
351
|
}
|
|
351
352
|
} else if (this.moveInputState === STATE.pieceClickedThreshold) {
|
|
352
|
-
this.setMoveInputState(STATE.clickTo, {
|
|
353
|
+
this.setMoveInputState(STATE.clickTo, {square: square})
|
|
353
354
|
} else if (this.moveInputState === STATE.secondClickThreshold) {
|
|
354
355
|
this.setMoveInputState(STATE.reset)
|
|
355
|
-
this.moveCanceledCallback(MOVE_CANCELED_REASON.secondClick,
|
|
356
|
+
this.moveCanceledCallback(MOVE_CANCELED_REASON.secondClick, square, square)
|
|
356
357
|
}
|
|
357
358
|
} else {
|
|
358
359
|
this.view.drawPieces()
|
|
359
|
-
const
|
|
360
|
+
const moveStartSquare = this.fromSquare
|
|
360
361
|
this.setMoveInputState(STATE.reset)
|
|
361
|
-
this.moveCanceledCallback(MOVE_CANCELED_REASON.movedOutOfBoard,
|
|
362
|
+
this.moveCanceledCallback(MOVE_CANCELED_REASON.movedOutOfBoard, moveStartSquare, undefined)
|
|
362
363
|
}
|
|
363
364
|
} else {
|
|
364
365
|
this.view.drawPieces()
|
|
@@ -374,13 +375,13 @@ export class ChessboardMoveInput {
|
|
|
374
375
|
this.chessboard.state.removeMarkers(undefined, this.chessboard.props.style.moveToMarker)
|
|
375
376
|
}
|
|
376
377
|
if (this.chessboard.props.style.moveFromMarker) {
|
|
377
|
-
if (this.
|
|
378
|
-
this.chessboard.state.addMarker(this.
|
|
378
|
+
if (this.fromSquare) {
|
|
379
|
+
this.chessboard.state.addMarker(this.fromSquare, this.chessboard.props.style.moveFromMarker)
|
|
379
380
|
}
|
|
380
381
|
}
|
|
381
382
|
if (this.chessboard.props.style.moveToMarker) {
|
|
382
|
-
if (this.
|
|
383
|
-
this.chessboard.state.addMarker(this.
|
|
383
|
+
if (this.toSquare) {
|
|
384
|
+
this.chessboard.state.addMarker(this.toSquare, this.chessboard.props.style.moveToMarker)
|
|
384
385
|
}
|
|
385
386
|
}
|
|
386
387
|
this.view.drawMarkers()
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
|
+
import {Position} from "./Position.js"
|
|
6
7
|
|
|
7
8
|
const CHANGE_TYPE = {
|
|
8
9
|
move: 0,
|
|
@@ -75,16 +76,16 @@ export class ChessboardPiecesAnimation {
|
|
|
75
76
|
}
|
|
76
77
|
switch (change.type) {
|
|
77
78
|
case CHANGE_TYPE.move:
|
|
78
|
-
animatedItem.element = this.view.getPiece(change.atIndex)
|
|
79
|
-
animatedItem.atPoint = this.view.
|
|
80
|
-
animatedItem.toPoint = this.view.
|
|
79
|
+
animatedItem.element = this.view.getPiece(Position.indexToSquare(change.atIndex))
|
|
80
|
+
animatedItem.atPoint = this.view.indexToPoint(change.atIndex)
|
|
81
|
+
animatedItem.toPoint = this.view.indexToPoint(change.toIndex)
|
|
81
82
|
break
|
|
82
83
|
case CHANGE_TYPE.appear:
|
|
83
|
-
animatedItem.element = this.view.drawPiece(change.atIndex, change.piece)
|
|
84
|
+
animatedItem.element = this.view.drawPiece(Position.indexToSquare(change.atIndex), change.piece)
|
|
84
85
|
animatedItem.element.style.opacity = 0
|
|
85
86
|
break
|
|
86
87
|
case CHANGE_TYPE.disappear:
|
|
87
|
-
animatedItem.element = this.view.getPiece(change.atIndex)
|
|
88
|
+
animatedItem.element = this.view.getPiece(Position.indexToSquare(change.atIndex))
|
|
88
89
|
break
|
|
89
90
|
}
|
|
90
91
|
animatedElements.push(animatedItem)
|
|
@@ -138,4 +139,4 @@ export class ChessboardPiecesAnimation {
|
|
|
138
139
|
return Math.max(Math.abs(rank2 - rank1), Math.abs(file2 - file1))
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
}
|
|
142
|
+
}
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
|
+
import {Position} from "./Position.js"
|
|
6
7
|
|
|
7
8
|
export class ChessboardState {
|
|
8
9
|
|
|
9
10
|
constructor() {
|
|
10
|
-
this.
|
|
11
|
+
this.position = new Position()
|
|
11
12
|
this.orientation = undefined
|
|
12
13
|
this.markers = []
|
|
13
14
|
this.inputWhiteEnabled = false
|
|
@@ -15,49 +16,41 @@ export class ChessboardState {
|
|
|
15
16
|
this.inputEnabled = false
|
|
16
17
|
this.squareSelectEnabled = false
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
+
/*
|
|
19
20
|
getPieces() {
|
|
20
|
-
|
|
21
|
-
const sortBy = ['k', 'q', 'r', 'b', 'n', 'p']
|
|
22
|
-
const sort = (a, b) => {
|
|
23
|
-
return sortBy.indexOf(a.name) - sortBy.indexOf(b.name)
|
|
24
|
-
}
|
|
25
|
-
for(let i=0; i<64; i++) {
|
|
26
|
-
const piece = this.squares[i]
|
|
27
|
-
if(piece) {
|
|
28
|
-
pieces.push({
|
|
29
|
-
name: piece.charAt(1),
|
|
30
|
-
color: piece.charAt(0),
|
|
31
|
-
position: this.indexToSquare(i)
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
pieces.sort(sort)
|
|
36
|
-
return pieces
|
|
21
|
+
return this.position.getPieces()
|
|
37
22
|
}
|
|
38
23
|
|
|
39
|
-
setPiece(
|
|
40
|
-
this.
|
|
24
|
+
setPiece(square, piece) {
|
|
25
|
+
this.position.setPiece(square, piece)
|
|
26
|
+
}
|
|
27
|
+
*/
|
|
28
|
+
setPosition(fen) {
|
|
29
|
+
this.position.setFen(fen)
|
|
41
30
|
}
|
|
42
31
|
|
|
43
|
-
|
|
44
|
-
this.
|
|
32
|
+
getPosition() {
|
|
33
|
+
return this.position.getFen()
|
|
45
34
|
}
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
addMarker(square, type) {
|
|
37
|
+
this.markers.push({square: square, type: type})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
removeMarkers(square = undefined, type = undefined) {
|
|
41
|
+
if (!square && !type) {
|
|
49
42
|
this.markers = []
|
|
50
43
|
} else {
|
|
51
44
|
this.markers = this.markers.filter((marker) => {
|
|
52
45
|
if (!marker.type) {
|
|
53
|
-
if (
|
|
46
|
+
if (square === marker.square) {
|
|
54
47
|
return false
|
|
55
48
|
}
|
|
56
|
-
} else if (!
|
|
49
|
+
} else if (!square) {
|
|
57
50
|
if (marker.type === type) {
|
|
58
51
|
return false
|
|
59
52
|
}
|
|
60
|
-
} else if (marker.type === type &&
|
|
53
|
+
} else if (marker.type === type && square === marker.square) {
|
|
61
54
|
return false
|
|
62
55
|
}
|
|
63
56
|
return true
|
|
@@ -65,74 +58,4 @@ export class ChessboardState {
|
|
|
65
58
|
}
|
|
66
59
|
}
|
|
67
60
|
|
|
68
|
-
setPosition(fen) {
|
|
69
|
-
if (fen) {
|
|
70
|
-
const parts = fen.replace(/^\s*/, "").replace(/\s*$/, "").split(/\/|\s/)
|
|
71
|
-
for (let part = 0; part < 8; part++) {
|
|
72
|
-
const row = parts[7 - part].replace(/\d/g, (str) => {
|
|
73
|
-
const numSpaces = parseInt(str)
|
|
74
|
-
let ret = ''
|
|
75
|
-
for (let i = 0; i < numSpaces; i++) {
|
|
76
|
-
ret += '-'
|
|
77
|
-
}
|
|
78
|
-
return ret
|
|
79
|
-
})
|
|
80
|
-
for (let c = 0; c < 8; c++) {
|
|
81
|
-
const char = row.substr(c, 1)
|
|
82
|
-
let piece = null
|
|
83
|
-
if (char !== '-') {
|
|
84
|
-
if (char.toUpperCase() === char) {
|
|
85
|
-
piece = `w${char.toLowerCase()}`
|
|
86
|
-
} else {
|
|
87
|
-
piece = `b${char}`
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
this.squares[part * 8 + c] = piece
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
getPosition() {
|
|
97
|
-
let parts = new Array(8).fill("")
|
|
98
|
-
for (let part = 0; part < 8; part++) {
|
|
99
|
-
let spaceCounter = 0
|
|
100
|
-
for (let i = 0; i < 8; i++) {
|
|
101
|
-
const piece = this.squares[part * 8 + i]
|
|
102
|
-
if (!piece) {
|
|
103
|
-
spaceCounter++
|
|
104
|
-
} else {
|
|
105
|
-
if (spaceCounter > 0) {
|
|
106
|
-
parts[7 - part] += spaceCounter
|
|
107
|
-
spaceCounter = 0
|
|
108
|
-
}
|
|
109
|
-
const color = piece.substr(0, 1)
|
|
110
|
-
const name = piece.substr(1, 1)
|
|
111
|
-
if (color === "w") {
|
|
112
|
-
parts[7 - part] += name.toUpperCase()
|
|
113
|
-
} else {
|
|
114
|
-
parts[7 - part] += name
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
if (spaceCounter > 0) {
|
|
119
|
-
parts[7 - part] += spaceCounter
|
|
120
|
-
spaceCounter = 0
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return parts.join("/")
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
squareToIndex(square) {
|
|
127
|
-
const file = square.substr(0, 1).charCodeAt(0) - 97
|
|
128
|
-
const rank = square.substr(1, 1) - 1
|
|
129
|
-
return 8 * rank + file
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
indexToSquare(index) {
|
|
133
|
-
const file = String.fromCharCode(index % 8 + 97)
|
|
134
|
-
const rank = Math.floor(index / 8) + 1
|
|
135
|
-
return file + rank
|
|
136
|
-
}
|
|
137
|
-
|
|
138
61
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import {ChessboardMoveInput} from "./ChessboardMoveInput.js"
|
|
8
8
|
import {COLOR, INPUT_EVENT_TYPE, BORDER_TYPE} from "./Chessboard.js"
|
|
9
9
|
import {ChessboardPiecesAnimation} from "./ChessboardPiecesAnimation.js"
|
|
10
|
+
import {Position} from "./Position.js"
|
|
10
11
|
|
|
11
12
|
export const piecesTranslations = {
|
|
12
13
|
en: {
|
|
@@ -46,7 +47,7 @@ export function renderPieceTitle(lang, name, color = undefined) {
|
|
|
46
47
|
}
|
|
47
48
|
return title
|
|
48
49
|
}
|
|
49
|
-
|
|
50
|
+
/*
|
|
50
51
|
export const SQUARE_COORDINATES = [
|
|
51
52
|
"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
|
|
52
53
|
"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
|
|
@@ -57,7 +58,7 @@ export const SQUARE_COORDINATES = [
|
|
|
57
58
|
"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
|
|
58
59
|
"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8"
|
|
59
60
|
]
|
|
60
|
-
|
|
61
|
+
*/
|
|
61
62
|
export class ChessboardView {
|
|
62
63
|
|
|
63
64
|
constructor(chessboard) {
|
|
@@ -195,7 +196,7 @@ export class ChessboardView {
|
|
|
195
196
|
this.drawCoordinates()
|
|
196
197
|
this.drawMarkers()
|
|
197
198
|
this.visualizeInputState()
|
|
198
|
-
this.drawPieces(this.chessboard.state.squares)
|
|
199
|
+
this.drawPieces(this.chessboard.state.position.squares)
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
// Board //
|
|
@@ -222,12 +223,12 @@ export class ChessboardView {
|
|
|
222
223
|
const index = this.chessboard.state.orientation === COLOR.white ? i : 63 - i
|
|
223
224
|
const squareColor = ((9 * index) & 8) === 0 ? 'black' : 'white'
|
|
224
225
|
const fieldClass = `square ${squareColor}`
|
|
225
|
-
const point = this.
|
|
226
|
+
const point = this.squareToPoint(Position.indexToSquare(index))
|
|
226
227
|
const squareRect = Svg.addElement(this.boardGroup, "rect", {
|
|
227
228
|
x: point.x, y: point.y, width: this.squareWidth, height: this.squareHeight
|
|
228
229
|
})
|
|
229
230
|
squareRect.setAttribute("class", fieldClass)
|
|
230
|
-
squareRect.setAttribute("data-
|
|
231
|
+
squareRect.setAttribute("data-square", Position.indexToSquare(index))
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
234
|
|
|
@@ -289,12 +290,12 @@ export class ChessboardView {
|
|
|
289
290
|
|
|
290
291
|
// Pieces //
|
|
291
292
|
|
|
292
|
-
drawPieces(squares = this.chessboard.state.squares) {
|
|
293
|
+
drawPieces(squares = this.chessboard.state.position.squares) {
|
|
293
294
|
const childNodes = Array.from(this.piecesGroup.childNodes)
|
|
294
295
|
for (let i = 0; i < 64; i++) {
|
|
295
296
|
const pieceName = squares[i]
|
|
296
297
|
if (pieceName) {
|
|
297
|
-
this.drawPiece(i, pieceName)
|
|
298
|
+
this.drawPiece(Position.indexToSquare(i), pieceName)
|
|
298
299
|
}
|
|
299
300
|
}
|
|
300
301
|
for (const childNode of childNodes) {
|
|
@@ -302,11 +303,11 @@ export class ChessboardView {
|
|
|
302
303
|
}
|
|
303
304
|
}
|
|
304
305
|
|
|
305
|
-
drawPiece(
|
|
306
|
+
drawPiece(square, pieceName) {
|
|
306
307
|
const pieceGroup = Svg.addElement(this.piecesGroup, "g")
|
|
307
308
|
pieceGroup.setAttribute("data-piece", pieceName)
|
|
308
|
-
pieceGroup.setAttribute("data-
|
|
309
|
-
const point = this.
|
|
309
|
+
pieceGroup.setAttribute("data-square", square)
|
|
310
|
+
const point = this.squareToPoint(square)
|
|
310
311
|
const transform = (this.svg.createSVGTransform())
|
|
311
312
|
transform.setTranslate(point.x, point.y)
|
|
312
313
|
pieceGroup.transform.baseVal.appendItem(transform)
|
|
@@ -326,18 +327,20 @@ export class ChessboardView {
|
|
|
326
327
|
return pieceGroup
|
|
327
328
|
}
|
|
328
329
|
|
|
329
|
-
setPieceVisibility(
|
|
330
|
-
const piece = this.getPiece(
|
|
330
|
+
setPieceVisibility(square, visible = true) {
|
|
331
|
+
const piece = this.getPiece(square)
|
|
331
332
|
if (visible) {
|
|
332
333
|
piece.setAttribute("visibility", "visible")
|
|
333
334
|
} else {
|
|
334
335
|
piece.setAttribute("visibility", "hidden")
|
|
335
336
|
}
|
|
336
|
-
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
getPiece(
|
|
340
|
-
|
|
339
|
+
getPiece(square) {
|
|
340
|
+
if(square.length < 2) {
|
|
341
|
+
throw new Error("980e03")
|
|
342
|
+
}
|
|
343
|
+
return this.piecesGroup.querySelector(`g[data-square='${square}']`)
|
|
341
344
|
}
|
|
342
345
|
|
|
343
346
|
// Markers //
|
|
@@ -353,9 +356,10 @@ export class ChessboardView {
|
|
|
353
356
|
}
|
|
354
357
|
|
|
355
358
|
drawMarker(marker) {
|
|
359
|
+
// console.log("drawMarker", marker)
|
|
356
360
|
const markerGroup = Svg.addElement(this.markersGroup, "g")
|
|
357
|
-
markerGroup.setAttribute("data-
|
|
358
|
-
const point = this.
|
|
361
|
+
markerGroup.setAttribute("data-square", marker.square)
|
|
362
|
+
const point = this.squareToPoint(marker.square)
|
|
359
363
|
const transform = (this.svg.createSVGTransform())
|
|
360
364
|
transform.setTranslate(point.x, point.y)
|
|
361
365
|
markerGroup.transform.baseVal.appendItem(transform)
|
|
@@ -426,39 +430,39 @@ export class ChessboardView {
|
|
|
426
430
|
|
|
427
431
|
// callbacks //
|
|
428
432
|
|
|
429
|
-
moveStartCallback(
|
|
433
|
+
moveStartCallback(square) {
|
|
430
434
|
if (this.moveInputCallback) {
|
|
431
435
|
return this.moveInputCallback({
|
|
432
436
|
chessboard: this.chessboard,
|
|
433
437
|
type: INPUT_EVENT_TYPE.moveStart,
|
|
434
|
-
square:
|
|
438
|
+
square: square
|
|
435
439
|
})
|
|
436
440
|
} else {
|
|
437
441
|
return true
|
|
438
442
|
}
|
|
439
443
|
}
|
|
440
444
|
|
|
441
|
-
moveDoneCallback(
|
|
445
|
+
moveDoneCallback(squareFrom, squareTo) {
|
|
442
446
|
if (this.moveInputCallback) {
|
|
443
447
|
return this.moveInputCallback({
|
|
444
448
|
chessboard: this.chessboard,
|
|
445
449
|
type: INPUT_EVENT_TYPE.moveDone,
|
|
446
|
-
squareFrom:
|
|
447
|
-
squareTo:
|
|
450
|
+
squareFrom: squareFrom,
|
|
451
|
+
squareTo: squareTo
|
|
448
452
|
})
|
|
449
453
|
} else {
|
|
450
454
|
return true
|
|
451
455
|
}
|
|
452
456
|
}
|
|
453
457
|
|
|
454
|
-
moveCanceledCallback(reason,
|
|
458
|
+
moveCanceledCallback(reason, squareFrom, squareTo) {
|
|
455
459
|
if (this.moveInputCallback) {
|
|
456
460
|
this.moveInputCallback({
|
|
457
461
|
chessboard: this.chessboard,
|
|
458
462
|
type: INPUT_EVENT_TYPE.moveCanceled,
|
|
459
463
|
reason: reason,
|
|
460
|
-
squareFrom:
|
|
461
|
-
squareTo:
|
|
464
|
+
squareFrom: squareFrom,
|
|
465
|
+
squareTo: squareTo
|
|
462
466
|
})
|
|
463
467
|
}
|
|
464
468
|
}
|
|
@@ -475,7 +479,7 @@ export class ChessboardView {
|
|
|
475
479
|
}
|
|
476
480
|
}
|
|
477
481
|
|
|
478
|
-
|
|
482
|
+
indexToPoint(index) {
|
|
479
483
|
let x, y
|
|
480
484
|
if (this.chessboard.state.orientation === COLOR.white) {
|
|
481
485
|
x = this.borderSize + (index % 8) * this.squareWidth
|
|
@@ -487,6 +491,11 @@ export class ChessboardView {
|
|
|
487
491
|
return {x: x, y: y}
|
|
488
492
|
}
|
|
489
493
|
|
|
494
|
+
squareToPoint(square) {
|
|
495
|
+
const index = Position.squareToIndex(square)
|
|
496
|
+
return this.indexToPoint(index)
|
|
497
|
+
}
|
|
498
|
+
|
|
490
499
|
}
|
|
491
500
|
|
|
492
501
|
const SVG_NAMESPACE = "http://www.w3.org/2000/svg"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* License: MIT, see file 'LICENSE'
|
|
5
5
|
*/
|
|
6
6
|
import {ChessboardView, renderPieceTitle} from "./ChessboardView.js"
|
|
7
|
-
import {COLOR} from "./Chessboard.js"
|
|
7
|
+
import {COLOR, INPUT_EVENT_TYPE} from "./Chessboard.js"
|
|
8
8
|
import {piecesTranslations} from "./ChessboardView.js"
|
|
9
9
|
|
|
10
10
|
const hlTranslations = {
|
|
@@ -55,7 +55,15 @@ export class ChessboardViewAccessible extends ChessboardView {
|
|
|
55
55
|
this.inputTo = this.movePieceFormContainer.querySelector(".input-to")
|
|
56
56
|
this.moveButton = this.movePieceFormContainer.querySelector(".button-move")
|
|
57
57
|
this.moveButton.addEventListener("click", () => {
|
|
58
|
-
this.
|
|
58
|
+
if (this.moveInputCallback({
|
|
59
|
+
chessboard: this.chessboard,
|
|
60
|
+
type: INPUT_EVENT_TYPE.moveDone,
|
|
61
|
+
squareFrom: this.inputFrom.value,
|
|
62
|
+
squareTo: this.inputTo.value
|
|
63
|
+
})) {
|
|
64
|
+
this.inputFrom.value = ""
|
|
65
|
+
this.inputTo.value = ""
|
|
66
|
+
}
|
|
59
67
|
})
|
|
60
68
|
this.accessibleContainer.appendChild(this.movePieceFormContainer)
|
|
61
69
|
|
|
@@ -90,7 +98,7 @@ export class ChessboardViewAccessible extends ChessboardView {
|
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
drawPieces(squares = this.chessboard.state.squares) {
|
|
101
|
+
drawPieces(squares = this.chessboard.state.position.squares) {
|
|
94
102
|
super.drawPieces(squares)
|
|
95
103
|
setTimeout(() => {
|
|
96
104
|
this.redrawBoardAsTable()
|
|
@@ -99,7 +107,7 @@ export class ChessboardViewAccessible extends ChessboardView {
|
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
redrawPiecesLists() {
|
|
102
|
-
const pieces = this.chessboard.state.getPieces()
|
|
110
|
+
const pieces = this.chessboard.state.position.getPieces()
|
|
103
111
|
let listW = ""
|
|
104
112
|
let listB = ""
|
|
105
113
|
for (const piece of pieces) {
|
|
@@ -117,7 +125,7 @@ export class ChessboardViewAccessible extends ChessboardView {
|
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
redrawBoardAsTable() {
|
|
120
|
-
const squares = this.chessboard.state.squares.slice()
|
|
128
|
+
const squares = this.chessboard.state.position.squares.slice()
|
|
121
129
|
const ranks = ["a", "b", "c", "d", "e", "f", "g", "h"]
|
|
122
130
|
const files = ["1", "2", "3", "4", "5", "6", "7", "8"]
|
|
123
131
|
if (this.chessboard.state.orientation === COLOR.black) {
|
|
@@ -135,7 +143,7 @@ export class ChessboardViewAccessible extends ChessboardView {
|
|
|
135
143
|
for (let y = 0; y < 8; y++) {
|
|
136
144
|
const pieceCode = squares[y % 8 + x * 8]
|
|
137
145
|
let color, name
|
|
138
|
-
if(pieceCode) {
|
|
146
|
+
if (pieceCode) {
|
|
139
147
|
color = pieceCode.charAt(0)
|
|
140
148
|
name = pieceCode.charAt(1)
|
|
141
149
|
html += `<td>${renderPieceTitle(this.lang, name, color)}</td>`
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author and copyright: Stefan Haack (https://shaack.com)
|
|
3
|
+
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
|
+
* License: MIT, see file 'LICENSE'
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export function Observed(target) {
|
|
8
|
+
const self = this
|
|
9
|
+
this.target = target
|
|
10
|
+
this.observers = []
|
|
11
|
+
this.target.addObserver = (callback, propertyName = undefined) => {
|
|
12
|
+
self.observers.push({callback: callback, property: propertyName})
|
|
13
|
+
// console.log(this.observers)
|
|
14
|
+
}
|
|
15
|
+
this.target.destruct = () => {
|
|
16
|
+
self.observers = []
|
|
17
|
+
}
|
|
18
|
+
this.proxy = new Proxy(this.target, {
|
|
19
|
+
set(target, property, value) {
|
|
20
|
+
const oldValue = target[property]
|
|
21
|
+
target[property] = value
|
|
22
|
+
// console.log("set", "property", property, "value", value)
|
|
23
|
+
for (const observer of self.observers) {
|
|
24
|
+
clearTimeout(observer.callbackDebounce)
|
|
25
|
+
observer.callbackDebounce = setTimeout(() => {
|
|
26
|
+
if (!observer.property || observer.property === property) {
|
|
27
|
+
observer.callback({
|
|
28
|
+
target: target,
|
|
29
|
+
property: property,
|
|
30
|
+
value: value,
|
|
31
|
+
oldValue: oldValue
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
return this.proxy
|
|
40
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author and copyright: Stefan Haack (https://shaack.com)
|
|
3
|
+
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
|
+
* License: MIT, see file 'LICENSE'
|
|
5
|
+
*/
|
|
6
|
+
export const FEN_START_POSITION = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
|
7
|
+
export const FEN_EMPTY_POSITION = "8/8/8/8/8/8/8/8"
|
|
8
|
+
|
|
9
|
+
export class Position {
|
|
10
|
+
|
|
11
|
+
constructor(fen = undefined) {
|
|
12
|
+
this.squares = new Array(64).fill(null)
|
|
13
|
+
this.setFen(fen)
|
|
14
|
+
}
|
|
15
|
+
setFen(fen = FEN_START_POSITION) {
|
|
16
|
+
const parts = fen.replace(/^\s*/, "").replace(/\s*$/, "").split(/\/|\s/)
|
|
17
|
+
for (let part = 0; part < 8; part++) {
|
|
18
|
+
const row = parts[7 - part].replace(/\d/g, (str) => {
|
|
19
|
+
const numSpaces = parseInt(str)
|
|
20
|
+
let ret = ''
|
|
21
|
+
for (let i = 0; i < numSpaces; i++) {
|
|
22
|
+
ret += '-'
|
|
23
|
+
}
|
|
24
|
+
return ret
|
|
25
|
+
})
|
|
26
|
+
for (let c = 0; c < 8; c++) {
|
|
27
|
+
const char = row.substring(c, c + 1)
|
|
28
|
+
let piece = null
|
|
29
|
+
if (char !== '-') {
|
|
30
|
+
if (char.toUpperCase() === char) {
|
|
31
|
+
piece = `w${char.toLowerCase()}`
|
|
32
|
+
} else {
|
|
33
|
+
piece = `b${char}`
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
this.squares[part * 8 + c] = piece
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
getFen() {
|
|
41
|
+
let parts = new Array(8).fill("")
|
|
42
|
+
for (let part = 0; part < 8; part++) {
|
|
43
|
+
let spaceCounter = 0
|
|
44
|
+
for (let i = 0; i < 8; i++) {
|
|
45
|
+
const piece = this.squares[part * 8 + i]
|
|
46
|
+
if (!piece) {
|
|
47
|
+
spaceCounter++
|
|
48
|
+
} else {
|
|
49
|
+
if (spaceCounter > 0) {
|
|
50
|
+
parts[7 - part] += spaceCounter
|
|
51
|
+
spaceCounter = 0
|
|
52
|
+
}
|
|
53
|
+
const color = piece.substring(0, 1)
|
|
54
|
+
const name = piece.substring(1, 2)
|
|
55
|
+
if (color === "w") {
|
|
56
|
+
parts[7 - part] += name.toUpperCase()
|
|
57
|
+
} else {
|
|
58
|
+
parts[7 - part] += name
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (spaceCounter > 0) {
|
|
63
|
+
parts[7 - part] += spaceCounter
|
|
64
|
+
spaceCounter = 0
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return parts.join("/")
|
|
68
|
+
}
|
|
69
|
+
getPieces() {
|
|
70
|
+
const pieces = []
|
|
71
|
+
const sortBy = ['k', 'q', 'r', 'b', 'n', 'p']
|
|
72
|
+
const sort = (a, b) => {
|
|
73
|
+
return sortBy.indexOf(a.name) - sortBy.indexOf(b.name)
|
|
74
|
+
}
|
|
75
|
+
for(let i=0; i<64; i++) {
|
|
76
|
+
const piece = this.squares[i]
|
|
77
|
+
if(piece) {
|
|
78
|
+
pieces.push({
|
|
79
|
+
name: piece.charAt(1),
|
|
80
|
+
color: piece.charAt(0),
|
|
81
|
+
position: Position.indexToSquare(i)
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
pieces.sort(sort)
|
|
86
|
+
return pieces
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setPiece(square, piece) {
|
|
90
|
+
this.squares[Position.squareToIndex(square)] = piece
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getPiece(square) {
|
|
94
|
+
return this.squares[Position.squareToIndex(square)]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static squareToIndex(square) {
|
|
98
|
+
const file = square.substring(0, 1).charCodeAt(0) - 97
|
|
99
|
+
const rank = square.substring(1, 2) - 1
|
|
100
|
+
return 8 * rank + file
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static indexToSquare(index) {
|
|
104
|
+
const file = String.fromCharCode(index % 8 + 97)
|
|
105
|
+
const rank = Math.floor(index / 8) + 1
|
|
106
|
+
return file + rank
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
clone() {
|
|
110
|
+
const cloned = new Position()
|
|
111
|
+
cloned.squares = this.squares.slice(0)
|
|
112
|
+
return cloned
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author and copyright: Stefan Haack (https://shaack.com)
|
|
3
|
+
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
|
+
* License: MIT, see file 'LICENSE'
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {describe, it, assert} from "../node_modules/teevi/src/teevi.js"
|
|
8
|
+
import {PIECE, Chessboard} from "../src/cm-chessboard/Chessboard.js"
|
|
9
|
+
|
|
10
|
+
describe("TestChessboard", () => {
|
|
11
|
+
|
|
12
|
+
it("should create and destroy a chessboard", () => {
|
|
13
|
+
const chessboard = new Chessboard(document.getElementById("TestPosition"), {
|
|
14
|
+
sprite: {url: "../assets/images/chessboard-sprite.svg"},
|
|
15
|
+
position: "start"
|
|
16
|
+
})
|
|
17
|
+
assert.equals(chessboard.getPosition(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR")
|
|
18
|
+
chessboard.destroy()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it("should set and get the position", () => {
|
|
22
|
+
const chessboard = new Chessboard(document.getElementById("TestPosition"),
|
|
23
|
+
{sprite: {url: "../assets/images/chessboard-sprite.svg"},})
|
|
24
|
+
chessboard.setPosition("rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Gkq - 4 11", false).then(() => {
|
|
25
|
+
assert.equals(chessboard.getPosition(), "rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR")
|
|
26
|
+
chessboard.destroy()
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it("should get pieces on squares", () => {
|
|
31
|
+
const chessboard = new Chessboard(document.getElementById("TestPosition"), {
|
|
32
|
+
sprite: {url: "../assets/images/chessboard-sprite.svg"},
|
|
33
|
+
position: "start"
|
|
34
|
+
})
|
|
35
|
+
assert.equals(chessboard.getPiece("d1"), "wq")
|
|
36
|
+
assert.equals(chessboard.getPiece("d8"), "bq")
|
|
37
|
+
assert.equals(chessboard.getPiece("a2"), "wp")
|
|
38
|
+
chessboard.destroy()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it("should set pieces on squares", () => {
|
|
42
|
+
const chessboard = new Chessboard(document.getElementById("TestPosition"), {
|
|
43
|
+
position: "empty",
|
|
44
|
+
sprite: {url: "../assets/images/chessboard-sprite.svg"},
|
|
45
|
+
})
|
|
46
|
+
chessboard.setPiece("a1", PIECE.bk)
|
|
47
|
+
assert.equals(chessboard.getPiece("a1"), "bk")
|
|
48
|
+
chessboard.setPiece("e5", PIECE.wk)
|
|
49
|
+
assert.equals(chessboard.getPiece("e5"), "wk")
|
|
50
|
+
chessboard.destroy()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
})
|
package/test/TestMarkers.js
CHANGED
|
@@ -28,8 +28,8 @@ describe("TestPiecesAnimation", () => {
|
|
|
28
28
|
state1.setPosition("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR")
|
|
29
29
|
const state2 = new ChessboardState()
|
|
30
30
|
state2.setPosition("rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR")
|
|
31
|
-
const previousBoard1 = state1.squares
|
|
32
|
-
const newBoard1 = state2.squares
|
|
31
|
+
const previousBoard1 = state1.position.squares
|
|
32
|
+
const newBoard1 = state2.position.squares
|
|
33
33
|
const changes = cfa.seekChanges(previousBoard1, newBoard1)
|
|
34
34
|
|
|
35
35
|
assert.equals(changes[0].type,0 )
|
|
@@ -52,4 +52,4 @@ describe("TestPiecesAnimation", () => {
|
|
|
52
52
|
assert.equals(changes[13].atIndex, 59)
|
|
53
53
|
})
|
|
54
54
|
|
|
55
|
-
})
|
|
55
|
+
})
|
package/test/TestPosition.js
CHANGED
|
@@ -5,49 +5,21 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {describe, it, assert} from "../node_modules/teevi/src/teevi.js"
|
|
8
|
-
import {
|
|
8
|
+
import {Position} from "../src/cm-chessboard/Position.js"
|
|
9
9
|
|
|
10
10
|
describe("TestPosition", () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
assert.equals(chessboard.getPosition(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR")
|
|
18
|
-
chessboard.destroy()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it("should set and get the position", () => {
|
|
22
|
-
const chessboard = new Chessboard(document.getElementById("TestPosition"),
|
|
23
|
-
{sprite: {url: "../assets/images/chessboard-sprite.svg"},})
|
|
24
|
-
chessboard.setPosition("rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Gkq - 4 11", false).then(() => {
|
|
25
|
-
assert.equals(chessboard.getPosition(), "rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR")
|
|
26
|
-
chessboard.destroy()
|
|
27
|
-
})
|
|
11
|
+
it("should convert square to index", () => {
|
|
12
|
+
assert.equals(Position.squareToIndex("a1"), 0)
|
|
13
|
+
assert.equals(Position.squareToIndex("h1"), 7)
|
|
14
|
+
assert.equals(Position.squareToIndex("a8"), 56)
|
|
15
|
+
assert.equals(Position.squareToIndex("g5"), 38)
|
|
16
|
+
assert.equals(Position.squareToIndex("h8"), 63)
|
|
28
17
|
})
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
assert.equals(chessboard.getPiece("d1"), "wq")
|
|
36
|
-
assert.equals(chessboard.getPiece("d8"), "bq")
|
|
37
|
-
assert.equals(chessboard.getPiece("a2"), "wp")
|
|
38
|
-
chessboard.destroy()
|
|
18
|
+
it("should convert index to square", () => {
|
|
19
|
+
assert.equals(Position.indexToSquare(0), "a1")
|
|
20
|
+
assert.equals(Position.indexToSquare(7), "h1")
|
|
21
|
+
assert.equals(Position.indexToSquare(56), "a8")
|
|
22
|
+
assert.equals(Position.indexToSquare(38), "g5")
|
|
23
|
+
assert.equals(Position.indexToSquare(63), "h8")
|
|
39
24
|
})
|
|
40
|
-
|
|
41
|
-
it("should set pieces on squares", () => {
|
|
42
|
-
const chessboard = new Chessboard(document.getElementById("TestPosition"), {
|
|
43
|
-
position: "empty",
|
|
44
|
-
sprite: {url: "../assets/images/chessboard-sprite.svg"},
|
|
45
|
-
})
|
|
46
|
-
chessboard.setPiece("a1", PIECE.bk)
|
|
47
|
-
assert.equals(chessboard.getPiece("a1"), "bk")
|
|
48
|
-
chessboard.setPiece("e5", PIECE.wk)
|
|
49
|
-
assert.equals(chessboard.getPiece("e5"), "wk")
|
|
50
|
-
chessboard.destroy()
|
|
51
|
-
})
|
|
52
|
-
|
|
53
25
|
})
|
package/test/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<title>Test cm-chessboard</title>
|
|
6
6
|
<link rel="stylesheet" href="../examples/styles/examples.css"/>
|
|
7
7
|
<link rel="stylesheet" href="../assets/styles/cm-chessboard.css"/>
|
|
8
|
-
<style
|
|
8
|
+
<style>
|
|
9
9
|
.board {
|
|
10
10
|
position: absolute;
|
|
11
11
|
right: 20px;
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
<div class="board" id="TestMarkers"></div>
|
|
23
23
|
<script type="module">
|
|
24
24
|
import {teevi} from "../node_modules/teevi/src/teevi.js"
|
|
25
|
-
import "./
|
|
25
|
+
import "./TestChessboard.js"
|
|
26
26
|
import "./TestBoard.js"
|
|
27
27
|
import "./TestPiecesAnimation.js"
|
|
28
28
|
import "./TestMarkers.js"
|
|
29
|
-
import "./
|
|
29
|
+
import "./TestPosition.js"
|
|
30
30
|
teevi.run()
|
|
31
31
|
</script>
|
|
32
32
|
</body>
|
package/test/TestState.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Author and copyright: Stefan Haack (https://shaack.com)
|
|
3
|
-
* Repository: https://github.com/shaack/cm-chessboard
|
|
4
|
-
* License: MIT, see file 'LICENSE'
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {describe, it, assert} from "../node_modules/teevi/src/teevi.js"
|
|
8
|
-
import {ChessboardState} from "../src/cm-chessboard/ChessboardState.js"
|
|
9
|
-
|
|
10
|
-
describe("TestState", () => {
|
|
11
|
-
it("should convert square to index", () => {
|
|
12
|
-
const state = new ChessboardState()
|
|
13
|
-
assert.equals(state.squareToIndex("a1"), 0)
|
|
14
|
-
assert.equals(state.squareToIndex("h1"), 7)
|
|
15
|
-
assert.equals(state.squareToIndex("a8"), 56)
|
|
16
|
-
assert.equals(state.squareToIndex("g5"), 38)
|
|
17
|
-
assert.equals(state.squareToIndex("h8"), 63)
|
|
18
|
-
})
|
|
19
|
-
it("should convert index to square", () => {
|
|
20
|
-
const state = new ChessboardState()
|
|
21
|
-
assert.equals(state.indexToSquare(0), "a1")
|
|
22
|
-
assert.equals(state.indexToSquare(7), "h1")
|
|
23
|
-
assert.equals(state.indexToSquare(56), "a8")
|
|
24
|
-
assert.equals(state.indexToSquare(38), "g5")
|
|
25
|
-
assert.equals(state.indexToSquare(63), "h8")
|
|
26
|
-
})
|
|
27
|
-
})
|