@lichess-org/chessground 9.2.3

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.
Files changed (87) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +110 -0
  3. package/assets/chessground.base.css +224 -0
  4. package/assets/chessground.brown.css +62 -0
  5. package/assets/chessground.cburnett.css +37 -0
  6. package/dist/anim.d.ts +17 -0
  7. package/dist/anim.js +99 -0
  8. package/dist/anim.js.map +1 -0
  9. package/dist/api.d.ts +28 -0
  10. package/dist/api.js +98 -0
  11. package/dist/api.js.map +1 -0
  12. package/dist/autoPieces.d.ts +3 -0
  13. package/dist/autoPieces.js +38 -0
  14. package/dist/autoPieces.js.map +1 -0
  15. package/dist/board.d.ts +25 -0
  16. package/dist/board.js +331 -0
  17. package/dist/board.js.map +1 -0
  18. package/dist/chessground.d.ts +7 -0
  19. package/dist/chessground.js +63 -0
  20. package/dist/chessground.js.map +1 -0
  21. package/dist/chessground.min.js +1 -0
  22. package/dist/config.d.ts +87 -0
  23. package/dist/config.js +64 -0
  24. package/dist/config.js.map +1 -0
  25. package/dist/drag.d.ts +20 -0
  26. package/dist/drag.js +208 -0
  27. package/dist/drag.js.map +1 -0
  28. package/dist/draw.d.ts +65 -0
  29. package/dist/draw.js +90 -0
  30. package/dist/draw.js.map +1 -0
  31. package/dist/drop.d.ts +5 -0
  32. package/dist/drop.js +31 -0
  33. package/dist/drop.js.map +1 -0
  34. package/dist/events.d.ts +4 -0
  35. package/dist/events.js +72 -0
  36. package/dist/events.js.map +1 -0
  37. package/dist/explosion.d.ts +3 -0
  38. package/dist/explosion.js +18 -0
  39. package/dist/explosion.js.map +1 -0
  40. package/dist/fen.d.ts +4 -0
  41. package/dist/fen.js +79 -0
  42. package/dist/fen.js.map +1 -0
  43. package/dist/premove.d.ts +6 -0
  44. package/dist/premove.js +57 -0
  45. package/dist/premove.js.map +1 -0
  46. package/dist/render.d.ts +4 -0
  47. package/dist/render.js +235 -0
  48. package/dist/render.js.map +1 -0
  49. package/dist/state.d.ts +100 -0
  50. package/dist/state.js +92 -0
  51. package/dist/state.js.map +1 -0
  52. package/dist/svg.d.ts +8 -0
  53. package/dist/svg.js +348 -0
  54. package/dist/svg.js.map +1 -0
  55. package/dist/sync.d.ts +8 -0
  56. package/dist/sync.js +27 -0
  57. package/dist/sync.js.map +1 -0
  58. package/dist/types.d.ts +94 -0
  59. package/dist/types.js +5 -0
  60. package/dist/types.js.map +1 -0
  61. package/dist/util.d.ts +20 -0
  62. package/dist/util.js +89 -0
  63. package/dist/util.js.map +1 -0
  64. package/dist/wrap.d.ts +3 -0
  65. package/dist/wrap.js +90 -0
  66. package/dist/wrap.js.map +1 -0
  67. package/package.json +58 -0
  68. package/src/anim.ts +139 -0
  69. package/src/api.ts +187 -0
  70. package/src/autoPieces.ts +47 -0
  71. package/src/board.ts +371 -0
  72. package/src/chessground.ts +67 -0
  73. package/src/config.ts +165 -0
  74. package/src/drag.ts +223 -0
  75. package/src/draw.ts +154 -0
  76. package/src/drop.ts +36 -0
  77. package/src/events.ts +86 -0
  78. package/src/explosion.ts +19 -0
  79. package/src/fen.ts +78 -0
  80. package/src/premove.ts +76 -0
  81. package/src/render.ts +262 -0
  82. package/src/state.ts +199 -0
  83. package/src/svg.ts +441 -0
  84. package/src/sync.ts +36 -0
  85. package/src/types.ts +110 -0
  86. package/src/util.ts +105 -0
  87. package/src/wrap.ts +111 -0
package/src/fen.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { pos2key, invRanks } from './util.js';
2
+ import * as cg from './types.js';
3
+
4
+ export const initial: cg.FEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR';
5
+
6
+ const roles: { [letter: string]: cg.Role } = {
7
+ p: 'pawn',
8
+ r: 'rook',
9
+ n: 'knight',
10
+ b: 'bishop',
11
+ q: 'queen',
12
+ k: 'king',
13
+ };
14
+
15
+ const letters = {
16
+ pawn: 'p',
17
+ rook: 'r',
18
+ knight: 'n',
19
+ bishop: 'b',
20
+ queen: 'q',
21
+ king: 'k',
22
+ };
23
+
24
+ export function read(fen: cg.FEN): cg.Pieces {
25
+ if (fen === 'start') fen = initial;
26
+ const pieces: cg.Pieces = new Map();
27
+ let row = 7,
28
+ col = 0;
29
+ for (const c of fen) {
30
+ switch (c) {
31
+ case ' ':
32
+ case '[':
33
+ return pieces;
34
+ case '/':
35
+ --row;
36
+ if (row < 0) return pieces;
37
+ col = 0;
38
+ break;
39
+ case '~': {
40
+ const piece = pieces.get(pos2key([col - 1, row]));
41
+ if (piece) piece.promoted = true;
42
+ break;
43
+ }
44
+ default: {
45
+ const nb = c.charCodeAt(0);
46
+ if (nb < 57) col += nb - 48;
47
+ else {
48
+ const role = c.toLowerCase();
49
+ pieces.set(pos2key([col, row]), {
50
+ role: roles[role],
51
+ color: c === role ? 'black' : 'white',
52
+ });
53
+ ++col;
54
+ }
55
+ }
56
+ }
57
+ }
58
+ return pieces;
59
+ }
60
+
61
+ export function write(pieces: cg.Pieces): cg.FEN {
62
+ return invRanks
63
+ .map(y =>
64
+ cg.files
65
+ .map(x => {
66
+ const piece = pieces.get((x + y) as cg.Key);
67
+ if (piece) {
68
+ let p = letters[piece.role];
69
+ if (piece.color === 'white') p = p.toUpperCase();
70
+ if (piece.promoted) p += '~';
71
+ return p;
72
+ } else return '1';
73
+ })
74
+ .join(''),
75
+ )
76
+ .join('/')
77
+ .replace(/1{2,}/g, s => s.length.toString());
78
+ }
package/src/premove.ts ADDED
@@ -0,0 +1,76 @@
1
+ import * as util from './util.js';
2
+ import * as cg from './types.js';
3
+
4
+ type Mobility = (x1: number, y1: number, x2: number, y2: number) => boolean;
5
+
6
+ const diff = (a: number, b: number): number => Math.abs(a - b);
7
+
8
+ const pawn =
9
+ (color: cg.Color): Mobility =>
10
+ (x1, y1, x2, y2) =>
11
+ diff(x1, x2) < 2 &&
12
+ (color === 'white'
13
+ ? // allow 2 squares from first two ranks, for horde
14
+ y2 === y1 + 1 || (y1 <= 1 && y2 === y1 + 2 && x1 === x2)
15
+ : y2 === y1 - 1 || (y1 >= 6 && y2 === y1 - 2 && x1 === x2));
16
+
17
+ export const knight: Mobility = (x1, y1, x2, y2) => {
18
+ const xd = diff(x1, x2);
19
+ const yd = diff(y1, y2);
20
+ return (xd === 1 && yd === 2) || (xd === 2 && yd === 1);
21
+ };
22
+
23
+ const bishop: Mobility = (x1, y1, x2, y2) => {
24
+ return diff(x1, x2) === diff(y1, y2);
25
+ };
26
+
27
+ const rook: Mobility = (x1, y1, x2, y2) => {
28
+ return x1 === x2 || y1 === y2;
29
+ };
30
+
31
+ export const queen: Mobility = (x1, y1, x2, y2) => {
32
+ return bishop(x1, y1, x2, y2) || rook(x1, y1, x2, y2);
33
+ };
34
+
35
+ const king =
36
+ (color: cg.Color, rookFiles: number[], canCastle: boolean): Mobility =>
37
+ (x1, y1, x2, y2) =>
38
+ (diff(x1, x2) < 2 && diff(y1, y2) < 2) ||
39
+ (canCastle &&
40
+ y1 === y2 &&
41
+ y1 === (color === 'white' ? 0 : 7) &&
42
+ ((x1 === 4 && ((x2 === 2 && rookFiles.includes(0)) || (x2 === 6 && rookFiles.includes(7)))) ||
43
+ rookFiles.includes(x2)));
44
+
45
+ function rookFilesOf(pieces: cg.Pieces, color: cg.Color) {
46
+ const backrank = color === 'white' ? '1' : '8';
47
+ const files = [];
48
+ for (const [key, piece] of pieces) {
49
+ if (key[1] === backrank && piece.color === color && piece.role === 'rook') {
50
+ files.push(util.key2pos(key)[0]);
51
+ }
52
+ }
53
+ return files;
54
+ }
55
+
56
+ export function premove(pieces: cg.Pieces, key: cg.Key, canCastle: boolean): cg.Key[] {
57
+ const piece = pieces.get(key);
58
+ if (!piece) return [];
59
+ const pos = util.key2pos(key),
60
+ r = piece.role,
61
+ mobility: Mobility =
62
+ r === 'pawn'
63
+ ? pawn(piece.color)
64
+ : r === 'knight'
65
+ ? knight
66
+ : r === 'bishop'
67
+ ? bishop
68
+ : r === 'rook'
69
+ ? rook
70
+ : r === 'queen'
71
+ ? queen
72
+ : king(piece.color, rookFilesOf(pieces, piece.color), canCastle);
73
+ return util.allPos
74
+ .filter(pos2 => (pos[0] !== pos2[0] || pos[1] !== pos2[1]) && mobility(pos[0], pos[1], pos2[0], pos2[1]))
75
+ .map(util.pos2key);
76
+ }
package/src/render.ts ADDED
@@ -0,0 +1,262 @@
1
+ import { State } from './state.js';
2
+ import { key2pos, createEl, posToTranslate as posToTranslateFromBounds, translate } from './util.js';
3
+ import { whitePov } from './board.js';
4
+ import { AnimCurrent, AnimVectors, AnimVector, AnimFadings } from './anim.js';
5
+ import { DragCurrent } from './drag.js';
6
+ import * as cg from './types.js';
7
+
8
+ type PieceName = string; // `$color $role`
9
+
10
+ // ported from https://github.com/lichess-org/lichobile/blob/master/src/chessground/render.ts
11
+ // in case of bugs, blame @veloce
12
+ export function render(s: State): void {
13
+ const asWhite: boolean = whitePov(s),
14
+ posToTranslate = posToTranslateFromBounds(s.dom.bounds()),
15
+ boardEl: HTMLElement = s.dom.elements.board,
16
+ pieces: cg.Pieces = s.pieces,
17
+ curAnim: AnimCurrent | undefined = s.animation.current,
18
+ anims: AnimVectors = curAnim ? curAnim.plan.anims : new Map(),
19
+ fadings: AnimFadings = curAnim ? curAnim.plan.fadings : new Map(),
20
+ curDrag: DragCurrent | undefined = s.draggable.current,
21
+ squares: cg.SquareClasses = computeSquareClasses(s),
22
+ samePieces: Set<cg.Key> = new Set(),
23
+ sameSquares: Set<cg.Key> = new Set(),
24
+ movedPieces: Map<PieceName, cg.PieceNode[]> = new Map(),
25
+ movedSquares: Map<string, cg.SquareNode[]> = new Map(); // by class name
26
+ let k: cg.Key,
27
+ el: cg.PieceNode | cg.SquareNode | undefined,
28
+ pieceAtKey: cg.Piece | undefined,
29
+ elPieceName: PieceName,
30
+ anim: AnimVector | undefined,
31
+ fading: cg.Piece | undefined,
32
+ pMvdset: cg.PieceNode[] | undefined,
33
+ pMvd: cg.PieceNode | undefined,
34
+ sMvdset: cg.SquareNode[] | undefined,
35
+ sMvd: cg.SquareNode | undefined;
36
+
37
+ // walk over all board dom elements, apply animations and flag moved pieces
38
+ el = boardEl.firstChild as cg.PieceNode | cg.SquareNode | undefined;
39
+ while (el) {
40
+ k = el.cgKey;
41
+ if (isPieceNode(el)) {
42
+ pieceAtKey = pieces.get(k);
43
+ anim = anims.get(k);
44
+ fading = fadings.get(k);
45
+ elPieceName = el.cgPiece;
46
+ // if piece not being dragged anymore, remove dragging style
47
+ if (el.cgDragging && (!curDrag || curDrag.orig !== k)) {
48
+ el.classList.remove('dragging');
49
+ translate(el, posToTranslate(key2pos(k), asWhite));
50
+ el.cgDragging = false;
51
+ }
52
+ // remove fading class if it still remains
53
+ if (!fading && el.cgFading) {
54
+ el.cgFading = false;
55
+ el.classList.remove('fading');
56
+ }
57
+ // there is now a piece at this dom key
58
+ if (pieceAtKey) {
59
+ // continue animation if already animating and same piece
60
+ // (otherwise it could animate a captured piece)
61
+ if (anim && el.cgAnimating && elPieceName === pieceNameOf(pieceAtKey)) {
62
+ const pos = key2pos(k);
63
+ pos[0] += anim[2];
64
+ pos[1] += anim[3];
65
+ el.classList.add('anim');
66
+ translate(el, posToTranslate(pos, asWhite));
67
+ } else if (el.cgAnimating) {
68
+ el.cgAnimating = false;
69
+ el.classList.remove('anim');
70
+ translate(el, posToTranslate(key2pos(k), asWhite));
71
+ if (s.addPieceZIndex) el.style.zIndex = posZIndex(key2pos(k), asWhite);
72
+ }
73
+ // same piece: flag as same
74
+ if (elPieceName === pieceNameOf(pieceAtKey) && (!fading || !el.cgFading)) {
75
+ samePieces.add(k);
76
+ }
77
+ // different piece: flag as moved unless it is a fading piece
78
+ else {
79
+ if (fading && elPieceName === pieceNameOf(fading)) {
80
+ el.classList.add('fading');
81
+ el.cgFading = true;
82
+ } else {
83
+ appendValue(movedPieces, elPieceName, el);
84
+ }
85
+ }
86
+ }
87
+ // no piece: flag as moved
88
+ else {
89
+ appendValue(movedPieces, elPieceName, el);
90
+ }
91
+ } else if (isSquareNode(el)) {
92
+ const cn = el.className;
93
+ if (squares.get(k) === cn) sameSquares.add(k);
94
+ else appendValue(movedSquares, cn, el);
95
+ }
96
+ el = el.nextSibling as cg.PieceNode | cg.SquareNode | undefined;
97
+ }
98
+
99
+ // walk over all squares in current set, apply dom changes to moved squares
100
+ // or append new squares
101
+ for (const [sk, className] of squares) {
102
+ if (!sameSquares.has(sk)) {
103
+ sMvdset = movedSquares.get(className);
104
+ sMvd = sMvdset && sMvdset.pop();
105
+ const translation = posToTranslate(key2pos(sk), asWhite);
106
+ if (sMvd) {
107
+ sMvd.cgKey = sk;
108
+ translate(sMvd, translation);
109
+ } else {
110
+ const squareNode = createEl('square', className) as cg.SquareNode;
111
+ squareNode.cgKey = sk;
112
+ translate(squareNode, translation);
113
+ boardEl.insertBefore(squareNode, boardEl.firstChild);
114
+ }
115
+ }
116
+ }
117
+
118
+ // walk over all pieces in current set, apply dom changes to moved pieces
119
+ // or append new pieces
120
+ for (const [k, p] of pieces) {
121
+ anim = anims.get(k);
122
+ if (!samePieces.has(k)) {
123
+ pMvdset = movedPieces.get(pieceNameOf(p));
124
+ pMvd = pMvdset && pMvdset.pop();
125
+ // a same piece was moved
126
+ if (pMvd) {
127
+ // apply dom changes
128
+ pMvd.cgKey = k;
129
+ if (pMvd.cgFading) {
130
+ pMvd.classList.remove('fading');
131
+ pMvd.cgFading = false;
132
+ }
133
+ const pos = key2pos(k);
134
+ if (s.addPieceZIndex) pMvd.style.zIndex = posZIndex(pos, asWhite);
135
+ if (anim) {
136
+ pMvd.cgAnimating = true;
137
+ pMvd.classList.add('anim');
138
+ pos[0] += anim[2];
139
+ pos[1] += anim[3];
140
+ }
141
+ translate(pMvd, posToTranslate(pos, asWhite));
142
+ }
143
+ // no piece in moved obj: insert the new piece
144
+ // assumes the new piece is not being dragged
145
+ else {
146
+ const pieceName = pieceNameOf(p),
147
+ pieceNode = createEl('piece', pieceName) as cg.PieceNode,
148
+ pos = key2pos(k);
149
+
150
+ pieceNode.cgPiece = pieceName;
151
+ pieceNode.cgKey = k;
152
+ if (anim) {
153
+ pieceNode.cgAnimating = true;
154
+ pos[0] += anim[2];
155
+ pos[1] += anim[3];
156
+ }
157
+ translate(pieceNode, posToTranslate(pos, asWhite));
158
+
159
+ if (s.addPieceZIndex) pieceNode.style.zIndex = posZIndex(pos, asWhite);
160
+
161
+ boardEl.appendChild(pieceNode);
162
+ }
163
+ }
164
+ }
165
+
166
+ // remove any element that remains in the moved sets
167
+ for (const nodes of movedPieces.values()) removeNodes(s, nodes);
168
+ for (const nodes of movedSquares.values()) removeNodes(s, nodes);
169
+ }
170
+
171
+ export function renderResized(s: State): void {
172
+ const asWhite: boolean = whitePov(s),
173
+ posToTranslate = posToTranslateFromBounds(s.dom.bounds());
174
+ let el = s.dom.elements.board.firstChild as cg.PieceNode | cg.SquareNode | undefined;
175
+ while (el) {
176
+ if ((isPieceNode(el) && !el.cgAnimating) || isSquareNode(el)) {
177
+ translate(el, posToTranslate(key2pos(el.cgKey), asWhite));
178
+ }
179
+ el = el.nextSibling as cg.PieceNode | cg.SquareNode | undefined;
180
+ }
181
+ }
182
+
183
+ export function updateBounds(s: State): void {
184
+ const bounds = s.dom.elements.wrap.getBoundingClientRect();
185
+ const container = s.dom.elements.container;
186
+ const ratio = bounds.height / bounds.width;
187
+ const width = (Math.floor((bounds.width * window.devicePixelRatio) / 8) * 8) / window.devicePixelRatio;
188
+ const height = width * ratio;
189
+ container.style.width = width + 'px';
190
+ container.style.height = height + 'px';
191
+ s.dom.bounds.clear();
192
+
193
+ s.addDimensionsCssVarsTo?.style.setProperty('---cg-width', width + 'px');
194
+ s.addDimensionsCssVarsTo?.style.setProperty('---cg-height', height + 'px');
195
+ }
196
+
197
+ const isPieceNode = (el: cg.PieceNode | cg.SquareNode): el is cg.PieceNode => el.tagName === 'PIECE';
198
+ const isSquareNode = (el: cg.PieceNode | cg.SquareNode): el is cg.SquareNode => el.tagName === 'SQUARE';
199
+
200
+ function removeNodes(s: State, nodes: HTMLElement[]): void {
201
+ for (const node of nodes) s.dom.elements.board.removeChild(node);
202
+ }
203
+
204
+ function posZIndex(pos: cg.Pos, asWhite: boolean): string {
205
+ const minZ = 3;
206
+ const rank = pos[1];
207
+ const z = asWhite ? minZ + 7 - rank : minZ + rank;
208
+
209
+ return `${z}`;
210
+ }
211
+
212
+ const pieceNameOf = (piece: cg.Piece): string => `${piece.color} ${piece.role}`;
213
+
214
+ function computeSquareClasses(s: State): cg.SquareClasses {
215
+ const squares: cg.SquareClasses = new Map();
216
+ if (s.lastMove && s.highlight.lastMove)
217
+ for (const k of s.lastMove) {
218
+ addSquare(squares, k, 'last-move');
219
+ }
220
+ if (s.check && s.highlight.check) addSquare(squares, s.check, 'check');
221
+ if (s.selected) {
222
+ addSquare(squares, s.selected, 'selected');
223
+ if (s.movable.showDests) {
224
+ const dests = s.movable.dests?.get(s.selected);
225
+ if (dests)
226
+ for (const k of dests) {
227
+ addSquare(squares, k, 'move-dest' + (s.pieces.has(k) ? ' oc' : ''));
228
+ }
229
+ const pDests = s.premovable.customDests?.get(s.selected) ?? s.premovable.dests;
230
+ if (pDests)
231
+ for (const k of pDests) {
232
+ addSquare(squares, k, 'premove-dest' + (s.pieces.has(k) ? ' oc' : ''));
233
+ }
234
+ }
235
+ }
236
+ const premove = s.premovable.current;
237
+ if (premove) for (const k of premove) addSquare(squares, k, 'current-premove');
238
+ else if (s.predroppable.current) addSquare(squares, s.predroppable.current.key, 'current-premove');
239
+
240
+ const o = s.exploding;
241
+ if (o) for (const k of o.keys) addSquare(squares, k, 'exploding' + o.stage);
242
+
243
+ if (s.highlight.custom) {
244
+ s.highlight.custom.forEach((v: string, k: cg.Key) => {
245
+ addSquare(squares, k, v);
246
+ });
247
+ }
248
+
249
+ return squares;
250
+ }
251
+
252
+ function addSquare(squares: cg.SquareClasses, key: cg.Key, klass: string): void {
253
+ const classes = squares.get(key);
254
+ if (classes) squares.set(key, `${classes} ${klass}`);
255
+ else squares.set(key, klass);
256
+ }
257
+
258
+ function appendValue<K, V>(map: Map<K, V[]>, key: K, value: V): void {
259
+ const arr = map.get(key);
260
+ if (arr) arr.push(value);
261
+ else map.set(key, [value]);
262
+ }
package/src/state.ts ADDED
@@ -0,0 +1,199 @@
1
+ import * as fen from './fen.js';
2
+ import { AnimCurrent } from './anim.js';
3
+ import { DragCurrent } from './drag.js';
4
+ import { Drawable } from './draw.js';
5
+ import { timer } from './util.js';
6
+ import * as cg from './types.js';
7
+
8
+ export interface HeadlessState {
9
+ pieces: cg.Pieces;
10
+ orientation: cg.Color; // board orientation. white | black
11
+ turnColor: cg.Color; // turn to play. white | black
12
+ check?: cg.Key; // square currently in check "a2"
13
+ lastMove?: cg.Key[]; // squares part of the last move ["c3"; "c4"]
14
+ selected?: cg.Key; // square currently selected "a1"
15
+ coordinates: boolean; // include coords attributes
16
+ coordinatesOnSquares: boolean; // include coords attributes on every square
17
+ ranksPosition: cg.RanksPosition; // position ranks on either side. left | right
18
+ autoCastle: boolean; // immediately complete the castle by moving the rook after king move
19
+ viewOnly: boolean; // don't bind events: the user will never be able to move pieces around
20
+ disableContextMenu: boolean; // because who needs a context menu on a chessboard
21
+ addPieceZIndex: boolean; // adds z-index values to pieces (for 3D)
22
+ addDimensionsCssVarsTo?: HTMLElement; // add ---cg-width and ---cg-height CSS vars containing the board's dimensions to this element
23
+ blockTouchScroll: boolean; // block scrolling via touch dragging on the board, e.g. for coordinate training
24
+ pieceKey: boolean; // add a data-key attribute to piece elements
25
+ trustAllEvents?: boolean; // disable checking for human only input (e.isTrusted)
26
+ highlight: {
27
+ lastMove: boolean; // add last-move class to squares
28
+ check: boolean; // add check class to squares
29
+ custom?: cg.SquareClasses; // add custom classes to custom squares
30
+ };
31
+ animation: {
32
+ enabled: boolean;
33
+ duration: number;
34
+ current?: AnimCurrent;
35
+ };
36
+ movable: {
37
+ free: boolean; // all moves are valid - board editor
38
+ color?: cg.Color | 'both'; // color that can move. white | black | both
39
+ dests?: cg.Dests; // valid moves. {"a2" ["a3" "a4"] "b1" ["a3" "c3"]}
40
+ showDests: boolean; // whether to add the move-dest class on squares
41
+ events: {
42
+ after?: (orig: cg.Key, dest: cg.Key, metadata: cg.MoveMetadata) => void; // called after the move has been played
43
+ afterNewPiece?: (role: cg.Role, key: cg.Key, metadata: cg.MoveMetadata) => void; // called after a new piece is dropped on the board
44
+ };
45
+ rookCastle: boolean; // castle by moving the king to the rook
46
+ };
47
+ premovable: {
48
+ enabled: boolean; // allow premoves for color that can not move
49
+ showDests: boolean; // whether to add the premove-dest class on squares
50
+ castle: boolean; // whether to allow king castle premoves
51
+ dests?: cg.Key[]; // premove destinations for the current selection
52
+ customDests?: cg.Dests; // use custom valid premoves. {"a2" ["a3" "a4"] "b1" ["a3" "c3"]}
53
+ current?: cg.KeyPair; // keys of the current saved premove ["e2" "e4"]
54
+ events: {
55
+ set?: (orig: cg.Key, dest: cg.Key, metadata?: cg.SetPremoveMetadata) => void; // called after the premove has been set
56
+ unset?: () => void; // called after the premove has been unset
57
+ };
58
+ };
59
+ predroppable: {
60
+ enabled: boolean; // allow predrops for color that can not move
61
+ current?: {
62
+ // current saved predrop {role: 'knight'; key: 'e4'}
63
+ role: cg.Role;
64
+ key: cg.Key;
65
+ };
66
+ events: {
67
+ set?: (role: cg.Role, key: cg.Key) => void; // called after the predrop has been set
68
+ unset?: () => void; // called after the predrop has been unset
69
+ };
70
+ };
71
+ draggable: {
72
+ enabled: boolean; // allow moves & premoves to use drag'n drop
73
+ distance: number; // minimum distance to initiate a drag; in pixels
74
+ autoDistance: boolean; // lets chessground set distance to zero when user drags pieces
75
+ showGhost: boolean; // show ghost of piece being dragged
76
+ deleteOnDropOff: boolean; // delete a piece when it is dropped off the board
77
+ current?: DragCurrent;
78
+ };
79
+ dropmode: {
80
+ active: boolean;
81
+ piece?: cg.Piece;
82
+ };
83
+ selectable: {
84
+ // disable to enforce dragging over click-click move
85
+ enabled: boolean;
86
+ };
87
+ stats: {
88
+ // was last piece dragged or clicked?
89
+ // needs default to false for touch
90
+ dragged: boolean;
91
+ ctrlKey?: boolean;
92
+ };
93
+ events: {
94
+ change?: () => void; // called after the situation changes on the board
95
+ // called after a piece has been moved.
96
+ // capturedPiece is undefined or like {color: 'white'; 'role': 'queen'}
97
+ move?: (orig: cg.Key, dest: cg.Key, capturedPiece?: cg.Piece) => void;
98
+ dropNewPiece?: (piece: cg.Piece, key: cg.Key) => void;
99
+ select?: (key: cg.Key) => void; // called when a square is selected
100
+ insert?: (elements: cg.Elements) => void; // when the board DOM has been (re)inserted
101
+ };
102
+ drawable: Drawable;
103
+ exploding?: cg.Exploding;
104
+ hold: cg.Timer;
105
+ }
106
+
107
+ export interface State extends HeadlessState {
108
+ dom: cg.Dom;
109
+ }
110
+
111
+ export function defaults(): HeadlessState {
112
+ return {
113
+ pieces: fen.read(fen.initial),
114
+ orientation: 'white',
115
+ turnColor: 'white',
116
+ coordinates: true,
117
+ coordinatesOnSquares: false,
118
+ ranksPosition: 'right',
119
+ autoCastle: true,
120
+ viewOnly: false,
121
+ disableContextMenu: false,
122
+ addPieceZIndex: false,
123
+ blockTouchScroll: false,
124
+ pieceKey: false,
125
+ trustAllEvents: false,
126
+ highlight: {
127
+ lastMove: true,
128
+ check: true,
129
+ },
130
+ animation: {
131
+ enabled: true,
132
+ duration: 200,
133
+ },
134
+ movable: {
135
+ free: true,
136
+ color: 'both',
137
+ showDests: true,
138
+ events: {},
139
+ rookCastle: true,
140
+ },
141
+ premovable: {
142
+ enabled: true,
143
+ showDests: true,
144
+ castle: true,
145
+ events: {},
146
+ },
147
+ predroppable: {
148
+ enabled: false,
149
+ events: {},
150
+ },
151
+ draggable: {
152
+ enabled: true,
153
+ distance: 3,
154
+ autoDistance: true,
155
+ showGhost: true,
156
+ deleteOnDropOff: false,
157
+ },
158
+ dropmode: {
159
+ active: false,
160
+ },
161
+ selectable: {
162
+ enabled: true,
163
+ },
164
+ stats: {
165
+ // on touchscreen, default to "tap-tap" moves
166
+ // instead of drag
167
+ dragged: !('ontouchstart' in window),
168
+ },
169
+ events: {},
170
+ drawable: {
171
+ enabled: true, // can draw
172
+ visible: true, // can view
173
+ defaultSnapToValidMove: true,
174
+ eraseOnClick: true,
175
+ shapes: [],
176
+ autoShapes: [],
177
+ brushes: {
178
+ green: { key: 'g', color: '#15781B', opacity: 1, lineWidth: 10 },
179
+ red: { key: 'r', color: '#882020', opacity: 1, lineWidth: 10 },
180
+ blue: { key: 'b', color: '#003088', opacity: 1, lineWidth: 10 },
181
+ yellow: { key: 'y', color: '#e68f00', opacity: 1, lineWidth: 10 },
182
+ paleBlue: { key: 'pb', color: '#003088', opacity: 0.4, lineWidth: 15 },
183
+ paleGreen: { key: 'pg', color: '#15781B', opacity: 0.4, lineWidth: 15 },
184
+ paleRed: { key: 'pr', color: '#882020', opacity: 0.4, lineWidth: 15 },
185
+ paleGrey: {
186
+ key: 'pgr',
187
+ color: '#4a4a4a',
188
+ opacity: 0.35,
189
+ lineWidth: 15,
190
+ },
191
+ purple: { key: 'purple', color: '#68217a', opacity: 0.65, lineWidth: 10 },
192
+ pink: { key: 'pink', color: '#ee2080', opacity: 0.5, lineWidth: 10 },
193
+ white: { key: 'white', color: 'white', opacity: 1, lineWidth: 10 },
194
+ },
195
+ prevSvgHash: '',
196
+ },
197
+ hold: timer(),
198
+ };
199
+ }