@alepot55/chessboardjs 2.3.6 → 2.3.7

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.
@@ -127,6 +127,7 @@ var Chessboard = (function (exports) {
127
127
  invalid_fadeTime: 'Invalid fadeTime: ',
128
128
  invalid_fadeAnimation: 'Invalid fadeAnimation: ',
129
129
  invalid_ratio: 'Invalid ratio: ',
130
+ invalid_animationStyle: 'Invalid animationStyle: ',
130
131
  animation_failed: 'Animation failed: ',
131
132
 
132
133
  // Event handlers
@@ -378,6 +379,7 @@ var Chessboard = (function (exports) {
378
379
  movableColors: ['w', 'b', 'white', 'black', 'both', 'none'],
379
380
  dropOffBoard: ['snapback', 'trash'],
380
381
  easingTypes: ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'],
382
+ animationStyles: ['sequential', 'simultaneous'],
381
383
  modes: ['normal', 'creative', 'analysis'],
382
384
  promotionPieces: ['q', 'r', 'b', 'n', 'Q', 'R', 'B', 'N']
383
385
  });
@@ -706,6 +708,14 @@ var Chessboard = (function (exports) {
706
708
  return this._validValues.easingTypes.includes(easing);
707
709
  }
708
710
 
711
+ /**
712
+ * Validates animation style
713
+ * @param {string} style - Animation style to validate
714
+ * @returns {boolean} True if valid
715
+ */
716
+ isValidAnimationStyle(style) {
717
+ return this._validValues.animationStyles.includes(style);
718
+ }
709
719
 
710
720
  /**
711
721
  * Validates CSS color format
@@ -902,7 +912,11 @@ var Chessboard = (function (exports) {
902
912
  if (config.dropOffBoard && !this.isValidDropOffBoard(config.dropOffBoard)) {
903
913
  errors.push(ERROR_MESSAGES.invalid_dropOffBoard + config.dropOffBoard);
904
914
  }
905
-
915
+
916
+ if (config.animationStyle && !this.isValidAnimationStyle(config.animationStyle)) {
917
+ errors.push(ERROR_MESSAGES.invalid_animationStyle + config.animationStyle);
918
+ }
919
+
906
920
  // Validate callbacks
907
921
  const callbacks = ['onMove', 'onMoveEnd', 'onChange', 'onDragStart', 'onDragMove', 'onDrop', 'onSnapbackEnd'];
908
922
  for (const callback of callbacks) {
@@ -1080,7 +1094,8 @@ var Chessboard = (function (exports) {
1080
1094
  fadeAnimation: 'ease',
1081
1095
  ratio: 0.9,
1082
1096
  piecesPath: '../assets/themes/default',
1083
- simultaneousAnimationDelay: 100,
1097
+ animationStyle: 'simultaneous',
1098
+ simultaneousAnimationDelay: 0,
1084
1099
  onMove: () => true,
1085
1100
  onMoveEnd: () => true,
1086
1101
  onChange: () => true,
@@ -1114,19 +1129,19 @@ var Chessboard = (function (exports) {
1114
1129
  constructor(settings = {}) {
1115
1130
  // Initialize validation service
1116
1131
  this._validationService = new ValidationService();
1117
-
1132
+
1118
1133
  // Validate input
1119
1134
  this._validateInput(settings);
1120
-
1135
+
1121
1136
  // Merge with defaults
1122
1137
  const config = this._mergeWithDefaults(settings);
1123
-
1138
+
1124
1139
  // Process and validate configuration
1125
1140
  this._processConfiguration(config);
1126
-
1141
+
1127
1142
  // Set CSS properties
1128
1143
  this._setCSSProperties(config);
1129
-
1144
+
1130
1145
  // Configure mode-specific settings
1131
1146
  this._configureModeSettings();
1132
1147
  }
@@ -1141,7 +1156,7 @@ var Chessboard = (function (exports) {
1141
1156
  if (settings !== null && typeof settings !== 'object') {
1142
1157
  throw new ConfigurationError('Settings must be an object', 'settings', settings);
1143
1158
  }
1144
-
1159
+
1145
1160
  // Validate using validation service
1146
1161
  try {
1147
1162
  this._validationService.validateConfig(settings);
@@ -1175,7 +1190,7 @@ var Chessboard = (function (exports) {
1175
1190
  this.size = config.size;
1176
1191
  this.movableColors = config.movableColors;
1177
1192
  this.piecesPath = config.piecesPath;
1178
-
1193
+
1179
1194
  // Event handlers
1180
1195
  this.onMove = this._validateCallback(config.onMove);
1181
1196
  this.onMoveEnd = this._validateCallback(config.onMoveEnd);
@@ -1203,8 +1218,9 @@ var Chessboard = (function (exports) {
1203
1218
  this.snapbackTime = this._setTime(config.snapbackTime);
1204
1219
  this.dropCenterTime = this._setTime(config.dropCenterTime);
1205
1220
  this.fadeTime = this._setTime(config.fadeTime);
1206
-
1221
+
1207
1222
  // Animation style properties
1223
+ this.animationStyle = this._validateAnimationStyle(config.animationStyle);
1208
1224
  this.simultaneousAnimationDelay = this._validateDelay(config.simultaneousAnimationDelay);
1209
1225
  }
1210
1226
 
@@ -1265,6 +1281,19 @@ var Chessboard = (function (exports) {
1265
1281
  return callback;
1266
1282
  }
1267
1283
 
1284
+ /**
1285
+ * Validates animation style
1286
+ * @private
1287
+ * @param {string} style - Animation style
1288
+ * @returns {string} Validated style
1289
+ * @throws {ConfigurationError} If style is invalid
1290
+ */
1291
+ _validateAnimationStyle(style) {
1292
+ if (!this._validationService.isValidAnimationStyle(style)) {
1293
+ throw new ConfigurationError('Invalid animation style', 'animationStyle', style);
1294
+ }
1295
+ return style;
1296
+ }
1268
1297
 
1269
1298
  /**
1270
1299
  * Validates animation delay
@@ -1322,11 +1351,11 @@ var Chessboard = (function (exports) {
1322
1351
  }
1323
1352
  return value;
1324
1353
  }
1325
-
1354
+
1326
1355
  if (typeof value === 'string' && value in ANIMATION_TIMES) {
1327
1356
  return ANIMATION_TIMES[value];
1328
1357
  }
1329
-
1358
+
1330
1359
  throw new ConfigurationError('Invalid time value', 'time', value);
1331
1360
  }
1332
1361
 
@@ -1341,11 +1370,11 @@ var Chessboard = (function (exports) {
1341
1370
  if (typeof value === 'boolean') {
1342
1371
  return value;
1343
1372
  }
1344
-
1373
+
1345
1374
  if (value in BOOLEAN_VALUES) {
1346
1375
  return BOOLEAN_VALUES[value];
1347
1376
  }
1348
-
1377
+
1349
1378
  throw new ConfigurationError('Invalid boolean value', 'boolean', value);
1350
1379
  }
1351
1380
 
@@ -1361,17 +1390,17 @@ var Chessboard = (function (exports) {
1361
1390
  if (typeof value === 'boolean') {
1362
1391
  return value ? TRANSITION_FUNCTIONS.ease : null;
1363
1392
  }
1364
-
1393
+
1365
1394
  // Handle string values
1366
1395
  if (typeof value === 'string' && value in TRANSITION_FUNCTIONS) {
1367
1396
  return TRANSITION_FUNCTIONS[value];
1368
1397
  }
1369
-
1398
+
1370
1399
  // Handle null/undefined
1371
1400
  if (value === null || value === undefined) {
1372
1401
  return null;
1373
1402
  }
1374
-
1403
+
1375
1404
  throw new ConfigurationError('Invalid transition function', 'transitionFunction', value);
1376
1405
  }
1377
1406
 
@@ -1402,6 +1431,7 @@ var Chessboard = (function (exports) {
1402
1431
  fadeTime: this.fadeTime,
1403
1432
  fadeAnimation: this.fadeAnimation,
1404
1433
  piecesPath: this.piecesPath,
1434
+ animationStyle: this.animationStyle,
1405
1435
  simultaneousAnimationDelay: this.simultaneousAnimationDelay,
1406
1436
  onlyLegalMoves: this.onlyLegalMoves
1407
1437
  };
@@ -1423,7 +1453,7 @@ var Chessboard = (function (exports) {
1423
1453
 
1424
1454
  // Apply updates
1425
1455
  const newConfig = Object.assign({}, this.toObject(), updates);
1426
-
1456
+
1427
1457
  // Re-process configuration
1428
1458
  this._processConfiguration(newConfig);
1429
1459
  this._setCSSProperties(newConfig);
@@ -1646,10 +1676,22 @@ var Chessboard = (function (exports) {
1646
1676
 
1647
1677
  setDrag(f) {
1648
1678
  if (!this.element) { console.debug(`[Piece] setDrag: ${this.id} - element is null`); return; }
1679
+ // Remove previous handlers
1680
+ this.element.onmousedown = null;
1681
+ this.element.ontouchstart = null;
1682
+ this.element.ondragstart = null;
1683
+ if (window.PointerEvent) {
1684
+ this.element.onpointerdown = null;
1685
+ }
1686
+ // Set new handlers
1649
1687
  this.element.ondragstart = (e) => { e.preventDefault(); };
1650
1688
  this.element.onmousedown = f;
1651
1689
  this.element.ontouchstart = f; // Drag touch
1652
- console.debug(`[Piece] setDrag: ${this.id}`);
1690
+ if (window.PointerEvent) {
1691
+ this.element.onpointerdown = f;
1692
+ console.debug(`[Piece] setDrag: pointerdown set for ${this.id}`);
1693
+ }
1694
+ console.debug(`[Piece] setDrag: mousedown/ontouchstart set for ${this.id}`);
1653
1695
  }
1654
1696
 
1655
1697
  destroy() {
@@ -7072,7 +7114,7 @@ var Chessboard = (function (exports) {
7072
7114
  * Implements the Facade pattern to provide a unified interface
7073
7115
  * @class
7074
7116
  */
7075
- let Chessboard$1 = class Chessboard {
7117
+ class Chessboard {
7076
7118
  /**
7077
7119
  * Creates a new Chessboard instance
7078
7120
  * @param {Object} config - Configuration object
@@ -7097,8 +7139,6 @@ var Chessboard = (function (exports) {
7097
7139
  } catch (error) {
7098
7140
  this._handleConstructorError(error);
7099
7141
  }
7100
- this._undoneMoves = [];
7101
- this._updateBoardPieces(true, true); // Forza popolamento DOM subito
7102
7142
  }
7103
7143
 
7104
7144
  /**
@@ -7142,31 +7182,6 @@ var Chessboard = (function (exports) {
7142
7182
  }
7143
7183
  }
7144
7184
 
7145
- /**
7146
- * Cleans up any partially initialized resources (safe to call multiple times)
7147
- * @private
7148
- */
7149
- _cleanup() {
7150
- // Remove event listeners if present
7151
- if (this.eventService && typeof this.eventService.removeListeners === 'function') {
7152
- this.eventService.removeListeners();
7153
- }
7154
- // Clear timeouts
7155
- if (this._updateTimeout) {
7156
- clearTimeout(this._updateTimeout);
7157
- this._updateTimeout = null;
7158
- }
7159
- // Null all services
7160
- this.validationService = null;
7161
- this.coordinateService = null;
7162
- this.positionService = null;
7163
- this.boardService = null;
7164
- this.pieceService = null;
7165
- this.animationService = null;
7166
- this.moveService = null;
7167
- this.eventService = null;
7168
- }
7169
-
7170
7185
  /**
7171
7186
  * Initializes all services
7172
7187
  * @private
@@ -7235,23 +7250,8 @@ var Chessboard = (function (exports) {
7235
7250
  /**
7236
7251
  * Builds the board DOM structure
7237
7252
  * @private
7238
- * Best practice: always remove squares (destroy JS/DOM) before clearing the board container.
7239
7253
  */
7240
7254
  _buildBoard() {
7241
- console.log('CHIAMATO: _buildBoard');
7242
- if (this._isUndoRedo) {
7243
- console.log('SKIP _buildBoard per undo/redo');
7244
- return;
7245
- }
7246
- // Forza la pulizia completa del contenitore board (DOM)
7247
- const boardContainer = document.getElementById(this.config.id_div);
7248
- if (boardContainer) boardContainer.innerHTML = '';
7249
- // Force remove all pieces from all squares (no animation, best practice)
7250
- if (this.boardService && this.boardService.squares) {
7251
- Object.values(this.boardService.squares).forEach(sq => sq && sq.forceRemoveAllPieces && sq.forceRemoveAllPieces());
7252
- }
7253
- if (this.boardService && this.boardService.removeSquares) this.boardService.removeSquares();
7254
- if (this.boardService && this.boardService.removeBoard) this.boardService.removeBoard();
7255
7255
  this.boardService.buildBoard();
7256
7256
  }
7257
7257
 
@@ -7260,14 +7260,6 @@ var Chessboard = (function (exports) {
7260
7260
  * @private
7261
7261
  */
7262
7262
  _buildSquares() {
7263
- console.log('CHIAMATO: _buildSquares');
7264
- if (this._isUndoRedo) {
7265
- console.log('SKIP _buildSquares per undo/redo');
7266
- return;
7267
- }
7268
- if (this.boardService && this.boardService.removeSquares) {
7269
- this.boardService.removeSquares();
7270
- }
7271
7263
  this.boardService.buildSquares((row, col) => {
7272
7264
  return this.coordinateService.realCoord(row, col);
7273
7265
  });
@@ -7472,7 +7464,7 @@ var Chessboard = (function (exports) {
7472
7464
  const capturedPiece = move.to.piece;
7473
7465
 
7474
7466
  // For castle moves in simultaneous mode, we need to coordinate both animations
7475
- if (isCastleMove) {
7467
+ if (isCastleMove && this.config.animationStyle === 'simultaneous') {
7476
7468
  // Start king animation
7477
7469
  this.pieceService.translatePiece(
7478
7470
  move,
@@ -7637,7 +7629,6 @@ var Chessboard = (function (exports) {
7637
7629
  * @param {boolean} [isPositionLoad=false] - Whether this is a position load
7638
7630
  */
7639
7631
  _updateBoardPieces(animation = false, isPositionLoad = false) {
7640
- console.log('CHIAMATO: _updateBoardPieces', { animation, isPositionLoad, isUndoRedo: this._isUndoRedo });
7641
7632
  // Check if services are available
7642
7633
  if (!this.positionService || !this.moveService || !this.eventService) {
7643
7634
  console.log('Cannot update board pieces - services not available');
@@ -7697,125 +7688,112 @@ var Chessboard = (function (exports) {
7697
7688
  }
7698
7689
 
7699
7690
  /**
7700
- * Aggiorna i pezzi sulla scacchiera con animazione e delay configurabile (greedy matching)
7691
+ * Performs the actual board update
7701
7692
  * @private
7702
- * @param {boolean} [animation=false] - Se animare
7703
- * @param {boolean} [isPositionLoad=false] - Se è un caricamento posizione (delay 0)
7693
+ * @param {boolean} [animation=false] - Whether to animate
7694
+ * @param {boolean} [isPositionLoad=false] - Whether this is a position load (affects delay)
7704
7695
  */
7705
7696
  _doUpdateBoardPieces(animation = false, isPositionLoad = false) {
7706
- if (this._isDragging) return;
7707
- if (this._isPromoting) return;
7708
- if (!this.positionService || !this.positionService.getGame()) return;
7697
+ // Skip update if we're in the middle of a promotion
7698
+ if (this._isPromoting) {
7699
+ console.log('Skipping board update during promotion');
7700
+ return;
7701
+ }
7702
+
7703
+ // Check if services are available
7704
+ if (!this.positionService || !this.positionService.getGame()) {
7705
+ console.log('Cannot update board pieces - position service not available');
7706
+ return;
7707
+ }
7708
+
7709
7709
  const squares = this.boardService.getAllSquares();
7710
7710
  const gameStateBefore = this.positionService.getGame().fen();
7711
- if (/^8\/8\/8\/8\/8\/8\/8\/8/.test(gameStateBefore)) {
7712
- const boardContainer = document.getElementById(this.config.id_div);
7713
- if (boardContainer) {
7714
- const pieceElements = boardContainer.querySelectorAll('.piece');
7715
- pieceElements.forEach(element => element.remove());
7716
- }
7717
- Object.values(squares).forEach(sq => { if (sq && sq.piece) sq.piece = null; });
7718
- this._clearVisualState();
7719
- this._addListeners();
7720
- if (this.config.onChange) this.config.onChange(gameStateBefore);
7721
- return;
7711
+
7712
+ console.log('_doUpdateBoardPieces - current FEN:', gameStateBefore);
7713
+ console.log('_doUpdateBoardPieces - animation:', animation, 'style:', this.config.animationStyle, 'isPositionLoad:', isPositionLoad);
7714
+
7715
+ // Determine which animation style to use
7716
+ const useSimultaneous = this.config.animationStyle === 'simultaneous';
7717
+ console.log('_doUpdateBoardPieces - useSimultaneous:', useSimultaneous);
7718
+
7719
+ if (useSimultaneous) {
7720
+ console.log('Using simultaneous animation');
7721
+ this._doSimultaneousUpdate(squares, gameStateBefore, isPositionLoad);
7722
+ } else {
7723
+ console.log('Using sequential animation');
7724
+ this._doSequentialUpdate(squares, gameStateBefore, animation);
7722
7725
  }
7726
+ }
7723
7727
 
7724
- // --- Matching greedy tra attuale e atteso ---
7725
- const currentMap = {};
7726
- const expectedMap = {};
7728
+ /**
7729
+ * Performs sequential piece updates (original behavior)
7730
+ * @private
7731
+ * @param {Object} squares - All squares
7732
+ * @param {string} gameStateBefore - Game state before update
7733
+ * @param {boolean} animation - Whether to animate
7734
+ */
7735
+ _doSequentialUpdate(squares, gameStateBefore, animation) {
7736
+ // Update each square sequentially
7727
7737
  Object.values(squares).forEach(square => {
7728
- const currentPiece = square.piece;
7729
7738
  const expectedPieceId = this.positionService.getGamePieceId(square.id);
7730
- if (currentPiece) {
7731
- const key = (currentPiece.color + currentPiece.type).toLowerCase();
7732
- if (!currentMap[key]) currentMap[key] = [];
7733
- currentMap[key].push({ square, id: square.id, piece: currentPiece });
7734
- }
7735
- if (expectedPieceId) {
7736
- const key = expectedPieceId.toLowerCase();
7737
- if (!expectedMap[key]) expectedMap[key] = [];
7738
- expectedMap[key].push({ square, id: square.id });
7739
- }
7740
- });
7741
- const animationDelay = isPositionLoad ? 0 : this.config.simultaneousAnimationDelay || 0;
7742
- let totalAnimations = 0;
7743
- let animationsCompleted = 0;
7739
+ const currentPiece = square.piece;
7740
+ const currentPieceId = currentPiece ? currentPiece.getId() : null;
7744
7741
 
7745
- // 1. Matching greedy: trova i movimenti
7746
- const moves = [];
7747
- const fromMatched = {};
7748
- const toMatched = {};
7749
- const unchanged = [];
7750
- Object.keys(expectedMap).forEach(key => {
7751
- const fromList = (currentMap[key] || []).slice();
7752
- const toList = expectedMap[key].slice();
7753
- const localFromMatched = new Array(fromList.length).fill(false);
7754
- const localToMatched = new Array(toList.length).fill(false);
7755
- // Matrice delle distanze
7756
- const distances = [];
7757
- for (let i = 0; i < fromList.length; i++) {
7758
- distances[i] = [];
7759
- for (let j = 0; j < toList.length; j++) {
7760
- distances[i][j] = Math.abs(fromList[i].square.row - toList[j].square.row) +
7761
- Math.abs(fromList[i].square.col - toList[j].square.col);
7762
- }
7763
- }
7764
- while (true) {
7765
- let minDist = Infinity, minI = -1, minJ = -1;
7766
- for (let i = 0; i < fromList.length; i++) {
7767
- if (localFromMatched[i]) continue;
7768
- for (let j = 0; j < toList.length; j++) {
7769
- if (localToMatched[j]) continue;
7770
- if (distances[i][j] < minDist) {
7771
- minDist = distances[i][j];
7772
- minI = i;
7773
- minJ = j;
7774
- }
7742
+ // Log only for squares that are changing
7743
+ if (currentPieceId !== expectedPieceId) {
7744
+ console.log(`_doSequentialUpdate - ${square.id}: ${currentPieceId} -> ${expectedPieceId}`);
7745
+
7746
+ // Check if we already have the correct piece (from promotion)
7747
+ if (currentPiece && currentPiece.getId() === expectedPieceId) {
7748
+ console.log(`Piece ${expectedPieceId} already correctly placed on ${square.id}`);
7749
+ } else {
7750
+ // Remove current piece if exists
7751
+ if (currentPiece) {
7752
+ this.pieceService.removePieceFromSquare(square, animation);
7753
+ }
7754
+
7755
+ // Add new piece if needed
7756
+ if (expectedPieceId) {
7757
+ const newPiece = this.pieceService.convertPiece(expectedPieceId);
7758
+ this.pieceService.addPieceOnSquare(
7759
+ square,
7760
+ newPiece,
7761
+ animation,
7762
+ this._createDragFunction.bind(this)
7763
+ );
7775
7764
  }
7776
7765
  }
7777
- if (minI === -1 || minJ === -1) break;
7778
- // Se la posizione è la stessa E il Piece è lo stesso oggetto, non fare nulla (pezzo unchanged)
7779
- if (fromList[minI].square === toList[minJ].square && squares[toList[minJ].square.id].piece === fromList[minI].piece) {
7780
- unchanged.push({ square: fromList[minI].square, piece: fromList[minI].piece });
7781
- localFromMatched[minI] = true;
7782
- localToMatched[minJ] = true;
7783
- fromMatched[fromList[minI].square.id] = true;
7784
- toMatched[toList[minJ].square.id] = true;
7785
- continue;
7786
- }
7787
- // Altrimenti, sposta il pezzo
7788
- moves.push({ from: fromList[minI].square, to: toList[minJ].square, piece: fromList[minI].piece });
7789
- localFromMatched[minI] = true;
7790
- localToMatched[minJ] = true;
7791
- fromMatched[fromList[minI].square.id] = true;
7792
- toMatched[toList[minJ].square.id] = true;
7793
7766
  }
7794
7767
  });
7795
7768
 
7796
- // 2. Rimozione: pezzi presenti solo in attuale (non matched)
7797
- const removes = [];
7798
- Object.keys(currentMap).forEach(key => {
7799
- currentMap[key].forEach(({ square, piece }) => {
7800
- if (!fromMatched[square.id]) {
7801
- removes.push({ square, piece });
7802
- }
7803
- });
7804
- });
7769
+ // Re-add listeners after updating pieces to ensure hover events work correctly
7770
+ this._addListeners();
7805
7771
 
7806
- // 3. Aggiunta: pezzi presenti solo in atteso (non matched)
7807
- const adds = [];
7808
- Object.keys(expectedMap).forEach(key => {
7809
- expectedMap[key].forEach(({ square, id }) => {
7810
- if (!toMatched[square.id]) {
7811
- adds.push({ square, pieceId: key });
7812
- }
7813
- });
7814
- });
7772
+ // Trigger change event if position changed
7773
+ const gameStateAfter = this.positionService.getGame().fen();
7774
+ if (gameStateBefore !== gameStateAfter) {
7775
+ this.config.onChange(gameStateAfter);
7776
+ }
7777
+ }
7815
7778
 
7816
- totalAnimations = moves.length + removes.length + adds.length;
7817
- if (totalAnimations === 0) {
7779
+ /**
7780
+ * Performs simultaneous piece updates
7781
+ * @private
7782
+ * @param {Object} squares - All squares
7783
+ * @param {string} gameStateBefore - Game state before update
7784
+ * @param {boolean} [isPositionLoad=false] - Whether this is a position load
7785
+ */
7786
+ _doSimultaneousUpdate(squares, gameStateBefore, isPositionLoad = false) {
7787
+ console.log('_doSimultaneousUpdate - Starting simultaneous update');
7788
+
7789
+ // Analyze what changes need to be made
7790
+ const changeAnalysis = this._analyzePositionChanges(squares);
7791
+
7792
+ if (changeAnalysis.totalChanges === 0) {
7793
+ console.log('_doSimultaneousUpdate - No changes needed, returning');
7818
7794
  this._addListeners();
7795
+
7796
+ // Trigger change event if position changed
7819
7797
  const gameStateAfter = this.positionService.getGame().fen();
7820
7798
  if (gameStateBefore !== gameStateAfter) {
7821
7799
  this.config.onChange(gameStateAfter);
@@ -7823,64 +7801,10 @@ var Chessboard = (function (exports) {
7823
7801
  return;
7824
7802
  }
7825
7803
 
7826
- // Debug: logga i pezzi unchanged
7827
- if (unchanged.length > 0) {
7828
- console.debug('[Chessboard] Unchanged pieces:', unchanged.map(u => u.piece.id + '@' + u.square.id));
7829
- }
7830
-
7831
- const onAnimationComplete = () => {
7832
- animationsCompleted++;
7833
- if (animationsCompleted === totalAnimations) {
7834
- // Pulizia finale robusta: rimuovi tutti i pezzi orfani dal DOM e dal riferimento JS
7835
- Object.values(this.boardService.getAllSquares()).forEach(square => {
7836
- const expectedPieceId = this.positionService.getGamePieceId(square.id);
7837
- if (!expectedPieceId && typeof square.forceRemoveAllPieces === 'function') {
7838
- square.forceRemoveAllPieces();
7839
- }
7840
- });
7841
- this._addListeners();
7842
- const gameStateAfter = this.positionService.getGame().fen();
7843
- if (gameStateBefore !== gameStateAfter) {
7844
- this.config.onChange(gameStateAfter);
7845
- }
7846
- }
7847
- };
7804
+ console.log('_doSimultaneousUpdate - Change analysis:', changeAnalysis);
7848
7805
 
7849
- // 4. Esegui tutte le animazioni con delay
7850
- let idx = 0;
7851
- moves.forEach(move => {
7852
- setTimeout(() => {
7853
- this.pieceService.translatePiece(
7854
- move,
7855
- false,
7856
- animation,
7857
- this._createDragFunction.bind(this),
7858
- onAnimationComplete
7859
- );
7860
- }, idx++ * animationDelay);
7861
- });
7862
- removes.forEach(op => {
7863
- setTimeout(() => {
7864
- if (typeof op.square.forceRemoveAllPieces === 'function') {
7865
- op.square.forceRemoveAllPieces();
7866
- onAnimationComplete();
7867
- } else {
7868
- this.pieceService.removePieceFromSquare(op.square, animation, onAnimationComplete);
7869
- }
7870
- }, idx++ * animationDelay);
7871
- });
7872
- adds.forEach(op => {
7873
- setTimeout(() => {
7874
- const newPiece = this.pieceService.convertPiece(op.pieceId);
7875
- this.pieceService.addPieceOnSquare(
7876
- op.square,
7877
- newPiece,
7878
- animation,
7879
- this._createDragFunction.bind(this),
7880
- onAnimationComplete
7881
- );
7882
- }, idx++ * animationDelay);
7883
- });
7806
+ // Execute all changes simultaneously
7807
+ this._executeSimultaneousChanges(changeAnalysis, gameStateBefore, isPositionLoad);
7884
7808
  }
7885
7809
 
7886
7810
  /**
@@ -8206,380 +8130,99 @@ var Chessboard = (function (exports) {
8206
8130
  }
8207
8131
 
8208
8132
  // -------------------
8209
- // Public API Methods (Refactored)
8133
+ // Public API Methods
8210
8134
  // -------------------
8211
8135
 
8212
- // --- POSITION & STATE ---
8213
- /**
8214
- * Get the current position as FEN
8215
- * @returns {string}
8216
- */
8217
- getPosition() { return this.fen(); }
8218
- /**
8219
- * Set the board position (FEN or object)
8220
- * @param {string|Object} position
8221
- * @param {Object} [opts]
8222
- * @param {boolean} [opts.animate=true]
8223
- * @returns {boolean}
8224
- */
8225
- setPosition(position, opts = {}) {
8226
- const animate = opts.animate !== undefined ? opts.animate : true;
8227
- // Remove highlights and selections
8228
- if (this.boardService && this.boardService.applyToAllSquares) {
8229
- this.boardService.applyToAllSquares('removeHint');
8230
- this.boardService.applyToAllSquares('deselect');
8231
- this.boardService.applyToAllSquares('unmoved');
8232
- }
8233
- if (this.positionService && this.positionService.setGame) {
8234
- this.positionService.setGame(position);
8235
- }
8236
- if (this._updateBoardPieces) {
8237
- this._updateBoardPieces(animate, true);
8238
- }
8239
- // Forza la sincronizzazione dopo setPosition
8240
- this._updateBoardPieces(true, false);
8241
- return true;
8242
- }
8243
- /**
8244
- * Reset the board to the starting position
8245
- * @param {Object} [opts]
8246
- * @param {boolean} [opts.animate=true]
8247
- * @returns {boolean}
8248
- */
8249
- reset(opts = {}) {
8250
- const animate = opts.animate !== undefined ? opts.animate : true;
8251
- // Use the default starting position from config or fallback
8252
- const startPosition = this.config && this.config.position ? this.config.position : 'start';
8253
- this._updateBoardPieces(animate);
8254
- const result = this.setPosition(startPosition, { animate });
8255
- // Forza la sincronizzazione dopo reset
8256
- this._updateBoardPieces(true, false);
8257
- return result;
8258
- }
8259
8136
  /**
8260
- * Clear the board
8261
- * @param {Object} [opts]
8262
- * @param {boolean} [opts.animate=true]
8263
- * @returns {boolean}
8137
+ * Gets the current position as FEN
8138
+ * @returns {string} FEN string
8264
8139
  */
8265
- clear(opts = {}) {
8266
- const animate = opts.animate !== undefined ? opts.animate : true;
8267
- if (!this.positionService || !this.positionService.getGame()) {
8268
- return false;
8269
- }
8270
- if (this._clearVisualState) this._clearVisualState();
8271
- this.positionService.getGame().clear();
8272
- // Forza la rimozione di tutti i pezzi dal DOM
8273
- if (this.boardService && this.boardService.squares) {
8274
- Object.values(this.boardService.squares).forEach(sq => {
8275
- if (sq && sq.piece) sq.piece = null;
8276
- });
8277
- }
8278
- if (this._updateBoardPieces) {
8279
- this._updateBoardPieces(animate, true);
8280
- }
8281
- // Forza la sincronizzazione dopo clear
8282
- this._updateBoardPieces(true, false);
8283
- return true;
8140
+ fen() {
8141
+ return this.positionService.getGame().fen();
8284
8142
  }
8285
8143
 
8286
- // --- MOVE MANAGEMENT ---
8287
- /**
8288
- * Undo last move
8289
- * @param {Object} [opts]
8290
- * @param {boolean} [opts.animate=true]
8291
- * @returns {boolean}
8292
- */
8293
- undoMove(opts = {}) {
8294
- const undone = this.positionService.getGame().undo();
8295
- if (undone) {
8296
- this._undoneMoves.push(undone);
8297
- // Forza refresh completo di tutti i pezzi dopo undo
8298
- this._updateBoardPieces(true, true);
8299
- return undone;
8300
- }
8301
- return null;
8302
- }
8303
8144
  /**
8304
- * Redo last undone move
8305
- * @param {Object} [opts]
8306
- * @param {boolean} [opts.animate=true]
8307
- * @returns {boolean}
8145
+ * Gets current turn
8146
+ * @returns {string} 'w' or 'b'
8308
8147
  */
8309
- redoMove(opts = {}) {
8310
- if (this._undoneMoves && this._undoneMoves.length > 0) {
8311
- const move = this._undoneMoves.pop();
8312
- const moveObj = { from: move.from, to: move.to };
8313
- if (move.promotion) moveObj.promotion = move.promotion;
8314
- const result = this.positionService.getGame().move(moveObj);
8315
- // Forza refresh completo di tutti i pezzi dopo redo
8316
- this._updateBoardPieces(true, true);
8317
- return result;
8318
- }
8319
- return false;
8148
+ turn() {
8149
+ return this.positionService.getGame().turn();
8320
8150
  }
8321
- /**
8322
- * Get legal moves for a square
8323
- * @param {string} square
8324
- * @returns {Array}
8325
- */
8326
- getLegalMoves(square) { return this.legalMoves(square); }
8327
8151
 
8328
- // --- PIECE MANAGEMENT ---
8329
8152
  /**
8330
- * Get the piece at a square
8331
- * @param {string} square
8332
- * @returns {string|null}
8333
- */
8334
- getPiece(square) {
8335
- // Sempre leggi lo stato aggiornato dal boardService
8336
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8337
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[getPiece] Parametro square non valido');
8338
- // Forza sync prima di leggere
8339
- this._updateBoardPieces(false, false);
8340
- const piece = squareObj.piece;
8341
- if (!piece) return null;
8342
- return (piece.color + piece.type).toLowerCase();
8343
- }
8344
- /**
8345
- * Put a piece on a square
8346
- * @param {string|Piece} piece
8347
- * @param {string|Square} square
8348
- * @param {Object} [opts]
8349
- * @param {boolean} [opts.animate=true]
8350
- * @returns {boolean}
8153
+ * Loads a new position
8154
+ * @param {string|Object} position - Position to load
8155
+ * @param {Object} [options={}] - Loading options
8156
+ * @param {boolean} [animation=true] - Whether to animate
8351
8157
  */
8352
- putPiece(piece, square, opts = {}) {
8353
- const animate = opts.animate !== undefined ? opts.animate : true;
8354
- let pieceStr = piece;
8355
- if (typeof piece === 'object' && piece.type && piece.color) {
8356
- pieceStr = (piece.color + piece.type).toLowerCase();
8357
- } else if (typeof piece === 'string' && piece.length === 2) {
8358
- const a = piece[0].toLowerCase();
8359
- const b = piece[1].toLowerCase();
8360
- const types = 'kqrbnp';
8361
- const colors = 'wb';
8362
- if (types.includes(a) && colors.includes(b)) {
8363
- pieceStr = b + a;
8364
- } else if (colors.includes(a) && types.includes(b)) {
8365
- pieceStr = a + b;
8366
- } else {
8367
- throw new Error(`[putPiece] Invalid piece: ${piece}`);
8368
- }
8369
- }
8370
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8371
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[putPiece] Parametro square non valido');
8372
- const pieceObj = this.pieceService.convertPiece(pieceStr);
8373
- if (!pieceObj || typeof pieceObj !== 'object' || !('type' in pieceObj)) throw new Error('[putPiece] Parametro piece non valido');
8374
- // Aggiorna solo il motore chess.js
8375
- const chessJsPiece = { type: pieceObj.type, color: pieceObj.color };
8376
- const game = this.positionService.getGame();
8377
- const result = game.put(chessJsPiece, squareObj.id);
8378
- if (!result) throw new Error(`[putPiece] Game.put failed for ${pieceStr} on ${squareObj.id}`);
8379
- // Non aggiornare direttamente square.piece!
8380
- // Riallinea la board JS allo stato del motore
8381
- this._updateBoardPieces(animate);
8382
- return true;
8383
- }
8384
- /**
8385
- * Remove a piece from a square
8386
- * @param {string|Square} square
8387
- * @param {Object} [opts]
8388
- * @param {boolean} [opts.animate=true]
8389
- * @returns {string|null}
8390
- */
8391
- removePiece(square, opts = {}) {
8392
- const animate = opts.animate !== undefined ? opts.animate : true;
8393
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8394
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[removePiece] Parametro square non valido');
8395
- // Aggiorna solo il motore chess.js
8396
- const game = this.positionService.getGame();
8397
- game.remove(squareObj.id);
8398
- // Non aggiornare direttamente square.piece!
8399
- // Riallinea la board JS allo stato del motore
8400
- this._updateBoardPieces(animate);
8401
- return true;
8402
- }
8158
+ load(position, options = {}, animation = true) {
8159
+ this.boardService.applyToAllSquares('removeHint');
8160
+ this.boardService.applyToAllSquares('deselect');
8161
+ this.boardService.applyToAllSquares('unmoved');
8403
8162
 
8404
- // --- BOARD CONTROL ---
8405
- /**
8406
- * Flip the board orientation
8407
- * @param {Object} [opts]
8408
- * @param {boolean} [opts.animate=true]
8409
- */
8410
- flipBoard(opts = {}) {
8411
- if (this.coordinateService && this.coordinateService.flipOrientation) {
8412
- this.coordinateService.flipOrientation();
8413
- }
8414
- if (this._buildBoard) this._buildBoard();
8415
- if (this._buildSquares) this._buildSquares();
8416
- if (this._addListeners) this._addListeners();
8417
- if (this._updateBoardPieces) this._updateBoardPieces(opts.animate !== false);
8418
- console.log('FEN dopo flip:', this.fen(), 'Orientamento:', this.coordinateService.getOrientation());
8419
- }
8420
- /**
8421
- * Set the board orientation
8422
- * @param {'w'|'b'} color
8423
- * @param {Object} [opts]
8424
- * @param {boolean} [opts.animate=true]
8425
- */
8426
- setOrientation(color, opts = {}) {
8427
- if (this.validationService.isValidOrientation(color)) {
8428
- this.coordinateService.setOrientation(color);
8429
- if (this._buildBoard) this._buildBoard();
8430
- if (this._buildSquares) this._buildSquares();
8431
- if (this._addListeners) this._addListeners();
8432
- if (this._updateBoardPieces) this._updateBoardPieces(opts.animate !== false);
8433
- }
8434
- return this.coordinateService.getOrientation();
8435
- }
8436
- /**
8437
- * Get the current orientation
8438
- * @returns {'w'|'b'}
8439
- */
8440
- getOrientation() { return this.orientation(); }
8441
- /**
8442
- * Resize the board
8443
- * @param {number|string} size
8444
- */
8445
- resizeBoard(size) {
8446
- if (size === 'auto') {
8447
- this.config.size = 'auto';
8448
- document.documentElement.style.setProperty('--dimBoard', 'auto');
8449
- this._updateBoardPieces(false);
8450
- return true;
8451
- }
8452
- if (typeof size !== 'number' || size < 50 || size > 3000) {
8453
- throw new Error(`[resizeBoard] Invalid size: ${size}`);
8454
- }
8455
- this.config.size = size;
8456
- document.documentElement.style.setProperty('--dimBoard', `${size}px`);
8457
- this._updateBoardPieces(false);
8458
- return true;
8163
+ this.positionService.setGame(position, options);
8164
+ this._updateBoardPieces(animation, true); // Position load
8459
8165
  }
8460
8166
 
8461
- // --- HIGHLIGHTING & UI ---
8462
8167
  /**
8463
- * Highlight a square
8464
- * @param {string|Square} square
8465
- * @param {Object} [opts]
8466
- */
8467
- highlight(square, opts = {}) {
8468
- // API: accetta id, converte subito in oggetto
8469
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8470
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[highlight] Parametro square non valido');
8471
- if (this.boardService && this.boardService.highlightSquare) {
8472
- this.boardService.highlightSquare(squareObj, opts);
8473
- } else if (this.eventService && this.eventService.highlightSquare) {
8474
- this.eventService.highlightSquare(squareObj, opts);
8475
- }
8476
- }
8477
- /**
8478
- * Remove highlight from a square
8479
- * @param {string|Square} square
8480
- * @param {Object} [opts]
8168
+ * Destroys the board and cleans up resources
8481
8169
  */
8482
- dehighlight(square, opts = {}) {
8483
- // API: accetta id, converte subito in oggetto
8484
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8485
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[dehighlight] Parametro square non valido');
8486
- if (this.boardService && this.boardService.dehighlightSquare) {
8487
- this.boardService.dehighlightSquare(squareObj, opts);
8488
- } else if (this.eventService && this.eventService.dehighlightSquare) {
8489
- this.eventService.dehighlightSquare(squareObj, opts);
8170
+ destroy() {
8171
+ this.eventService.destroy();
8172
+ this.boardService.destroy();
8173
+ this.positionService.destroy();
8174
+ this.pieceService.destroy();
8175
+ this.moveService.destroy();
8176
+ this.animationService.destroy();
8177
+ this.validationService.destroy();
8178
+
8179
+ if (this._updateTimeout) {
8180
+ clearTimeout(this._updateTimeout);
8181
+ this._updateTimeout = null;
8490
8182
  }
8491
8183
  }
8492
8184
 
8493
- // --- GAME INFO ---
8494
- /**
8495
- * Get FEN string
8496
- * @returns {string}
8497
- */
8498
- fen() {
8499
- // Avoid recursion: call the underlying game object's fen()
8500
- const game = this.positionService.getGame();
8501
- if (!game || typeof game.fen !== 'function') return '';
8502
- return game.fen();
8503
- }
8504
- /**
8505
- * Get current turn
8506
- * @returns {'w'|'b'}
8507
- */
8508
- turn() { return this.positionService.getGame().turn(); }
8509
- /**
8510
- * Is the game over?
8511
- * @returns {boolean}
8512
- */
8513
- isGameOver() {
8514
- // Forza sync prima di interrogare il motore
8515
- this._updateBoardPieces(false, false);
8516
- const game = this.positionService.getGame();
8517
- if (!game) return false;
8518
- if (game.isGameOver) return game.isGameOver();
8519
- // Fallback: checkmate or draw
8520
- if (game.isCheckmate && game.isCheckmate()) return true;
8521
- if (game.isDraw && game.isDraw()) return true;
8522
- return false;
8523
- }
8524
- /**
8525
- * Is it checkmate?
8526
- * @returns {boolean}
8527
- */
8528
- isCheckmate() {
8529
- const game = this.positionService.getGame();
8530
- if (!game) return false;
8531
- return game.isCheckmate ? game.isCheckmate() : false;
8532
- }
8533
- /**
8534
- * Is it draw?
8535
- * @returns {boolean}
8536
- */
8537
- isDraw() {
8538
- const game = this.positionService.getGame();
8539
- if (!game) return false;
8540
- return game.isDraw ? game.isDraw() : false;
8541
- }
8542
8185
  /**
8543
- * Get move history
8544
- * @returns {Array}
8186
+ * Resizes the board
8187
+ * @param {number|string} size - New size
8545
8188
  */
8546
- getHistory() {
8547
- const game = this.positionService.getGame();
8548
- if (!game) return [];
8549
- return game.history ? game.history() : [];
8189
+ resize(size) {
8190
+ this.boardService.resize(size);
8191
+ this._updateBoardPieces();
8550
8192
  }
8551
8193
 
8552
- // --- LIFECYCLE ---
8553
- /**
8554
- * Destroy the board and cleanup
8555
- */
8556
- destroy() { /* TODO: robust destroy logic */ }
8557
8194
  /**
8558
- * Rebuild the board
8195
+ * Flips the board orientation
8559
8196
  */
8560
- rebuild() { this._initialize(); }
8197
+ flip() {
8198
+ this.coordinateService.flipOrientation();
8561
8199
 
8562
- // --- CONFIGURATION ---
8563
- /**
8564
- * Get current config
8565
- * @returns {Object}
8566
- */
8567
- getConfig() { return this.config; }
8568
- /**
8569
- * Set new config
8570
- * @param {Object} newConfig
8571
- */
8572
- setConfig(newConfig) { this.setConfig(newConfig); }
8200
+ // Save current position before destroying
8201
+ let currentPosition = null;
8202
+ try {
8203
+ // Check if there are any pieces on the board
8204
+ const position = this.positionService.getPosition();
8205
+ const hasPieces = Object.keys(position).length > 0;
8573
8206
 
8574
- // --- ALIASES/DEPRECATED ---
8575
- // Note: These methods are now implemented as aliases at the end of the class
8207
+ if (hasPieces) {
8208
+ currentPosition = this.positionService.getGame().fen();
8209
+ }
8210
+ } catch (error) {
8211
+ console.log('No valid position to save during flip');
8212
+ }
8576
8213
 
8577
- /**
8578
- * Alias for flipBoard (for backward compatibility)
8579
- */
8580
- flip(opts = {}) {
8581
- this._updateBoardPieces(opts.animate !== false);
8582
- return this.flipBoard(opts);
8214
+ this.destroy();
8215
+ this._initializeServices(); // Recreate all services
8216
+ this._initParams();
8217
+
8218
+ // Restore position after rebuilding if we had one
8219
+ if (currentPosition) {
8220
+ this._setGame(currentPosition);
8221
+ }
8222
+ this._buildBoard();
8223
+ this._buildSquares();
8224
+ this._addListeners();
8225
+ this._updateBoardPieces(true, true);
8583
8226
  }
8584
8227
 
8585
8228
  /**
@@ -8602,19 +8245,51 @@ var Chessboard = (function (exports) {
8602
8245
  this.load(position, {}, animate); // load() already handles isPositionLoad=true
8603
8246
  }
8604
8247
 
8248
+ /**
8249
+ * Makes a move on the board
8250
+ * @param {string|Object} move - Move to make
8251
+ * @param {boolean} [animate=true] - Whether to animate
8252
+ * @returns {boolean} True if move was successful
8253
+ */
8254
+ move(move, animate = true) {
8255
+ if (typeof move === 'string') {
8256
+ // Parse move string (e.g., 'e2e4')
8257
+ const moveObj = this.moveService.parseMove(move);
8258
+ if (!moveObj) return false;
8259
+
8260
+ const fromSquare = this.boardService.getSquare(moveObj.from);
8261
+ const toSquare = this.boardService.getSquare(moveObj.to);
8262
+
8263
+ if (!fromSquare || !toSquare) return false;
8264
+
8265
+ return this._onMove(fromSquare, toSquare, moveObj.promotion, animate);
8266
+ } else if (move && move.from && move.to) {
8267
+ // Handle move object
8268
+ const fromSquare = this.boardService.getSquare(move.from);
8269
+ const toSquare = this.boardService.getSquare(move.to);
8270
+
8271
+ if (!fromSquare || !toSquare) return false;
8272
+
8273
+ return this._onMove(fromSquare, toSquare, move.promotion, animate);
8274
+ }
8275
+
8276
+ return false;
8277
+ }
8278
+
8605
8279
  /**
8606
8280
  * Undoes the last move
8607
8281
  * @param {boolean} [animate=true] - Whether to animate
8608
8282
  * @returns {boolean} True if undo was successful
8609
8283
  */
8610
8284
  undo(animate = true) {
8611
- const undone = this.positionService.getGame().undo();
8612
- if (undone) {
8613
- this._undoneMoves.push(undone);
8614
- this._updateBoardPieces(animate);
8615
- return undone;
8285
+ if (this.positionService.getGame().undo) {
8286
+ const undoResult = this.positionService.getGame().undo();
8287
+ if (undoResult) {
8288
+ this._updateBoardPieces(animate, true); // Position change
8289
+ return true;
8290
+ }
8616
8291
  }
8617
- return null;
8292
+ return false;
8618
8293
  }
8619
8294
 
8620
8295
  /**
@@ -8623,13 +8298,12 @@ var Chessboard = (function (exports) {
8623
8298
  * @returns {boolean} True if redo was successful
8624
8299
  */
8625
8300
  redo(animate = true) {
8626
- if (this._undoneMoves && this._undoneMoves.length > 0) {
8627
- const move = this._undoneMoves.pop();
8628
- const moveObj = { from: move.from, to: move.to };
8629
- if (move.promotion) moveObj.promotion = move.promotion;
8630
- const result = this.positionService.getGame().move(moveObj);
8631
- this._updateBoardPieces(animate);
8632
- return result;
8301
+ if (this.positionService.getGame().redo) {
8302
+ const redoResult = this.positionService.getGame().redo();
8303
+ if (redoResult) {
8304
+ this._updateBoardPieces(animate, true); // Position change
8305
+ return true;
8306
+ }
8633
8307
  }
8634
8308
  return false;
8635
8309
  }
@@ -8791,6 +8465,112 @@ var Chessboard = (function (exports) {
8791
8465
  }
8792
8466
  }
8793
8467
 
8468
+ /**
8469
+ * Clears the board
8470
+ * @param {boolean} [animate=true] - Whether to animate
8471
+ */
8472
+ clear(animate = true) {
8473
+ // Check if services are available
8474
+ if (!this.positionService || !this.positionService.getGame()) {
8475
+ console.log('Cannot clear - position service not available');
8476
+ return;
8477
+ }
8478
+
8479
+ // Clear visual state first
8480
+ this._clearVisualState();
8481
+
8482
+ // Clear game state
8483
+ this.positionService.getGame().clear();
8484
+
8485
+ // Update board visually
8486
+ this._updateBoardPieces(animate, true); // Position change
8487
+ }
8488
+
8489
+ /**
8490
+ * Resets the board to starting position
8491
+ * @param {boolean} [animate=true] - Whether to animate
8492
+ */
8493
+ reset(animate = true) {
8494
+ // Check if services are available
8495
+ if (!this.positionService || !this.positionService.getGame()) {
8496
+ console.log('Cannot reset - position service not available');
8497
+ return;
8498
+ }
8499
+
8500
+ this.positionService.getGame().reset();
8501
+ this._updateBoardPieces(animate, true); // Position change
8502
+ }
8503
+
8504
+ /**
8505
+ * Puts a piece on a square
8506
+ * @param {string} square - Square to put piece on
8507
+ * @param {string} piece - Piece to put
8508
+ * @param {boolean} [animate=true] - Whether to animate
8509
+ * @returns {boolean} True if successful
8510
+ */
8511
+ put(square, piece, animate = true) {
8512
+ console.log(`put() called with square: ${square}, piece: ${piece}`);
8513
+
8514
+ if (!this.validationService.isValidSquare(square) || !this.validationService.isValidPiece(piece)) {
8515
+ console.log('Validation failed');
8516
+ return false;
8517
+ }
8518
+
8519
+ // Check if services are available
8520
+ if (!this.positionService || !this.positionService.getGame()) {
8521
+ console.log('Cannot put piece - position service not available');
8522
+ return false;
8523
+ }
8524
+
8525
+ const pieceObj = this.pieceService.convertPiece(piece);
8526
+ console.log(`Converted piece:`, pieceObj);
8527
+ console.log(`Piece type: ${pieceObj.type}, color: ${pieceObj.color}`);
8528
+
8529
+ const squareObj = this.boardService.getSquare(square);
8530
+
8531
+ if (!squareObj) {
8532
+ console.log('Square not found');
8533
+ return false;
8534
+ }
8535
+
8536
+ // Update game state - note: chess.js expects (piece, square) order
8537
+ const chessJsPiece = { type: pieceObj.type.toLowerCase(), color: pieceObj.color.toLowerCase() };
8538
+ console.log(`Chess.js piece:`, chessJsPiece);
8539
+
8540
+ const result = this.positionService.getGame().put(chessJsPiece, square);
8541
+ console.log(`Chess.js put result:`, result);
8542
+
8543
+ if (result) {
8544
+ // Update visual representation
8545
+ this._updateBoardPieces(animate, true); // Position change
8546
+ }
8547
+
8548
+ return result;
8549
+ }
8550
+
8551
+ /**
8552
+ * Removes a piece from a square
8553
+ * @param {string} square - Square to remove piece from
8554
+ * @param {boolean} [animate=true] - Whether to animate
8555
+ * @returns {boolean} True if successful
8556
+ */
8557
+ remove(square, animate = true) {
8558
+ if (!this.validationService.isValidSquare(square)) {
8559
+ return false;
8560
+ }
8561
+
8562
+ const squareObj = this.boardService.getSquare(square);
8563
+ if (!squareObj) return false;
8564
+
8565
+ // Update game state
8566
+ this.positionService.getGame().remove(square);
8567
+
8568
+ // Update visual representation
8569
+ this._updateBoardPieces(animate, true); // Position change
8570
+
8571
+ return true;
8572
+ }
8573
+
8794
8574
  /**
8795
8575
  * Gets configuration options
8796
8576
  * @returns {Object} Configuration object
@@ -8816,6 +8596,23 @@ var Chessboard = (function (exports) {
8816
8596
  }
8817
8597
  }
8818
8598
 
8599
+ /**
8600
+ * Gets or sets the animation style
8601
+ * @param {string} [style] - New animation style ('sequential' or 'simultaneous')
8602
+ * @returns {string} Current animation style
8603
+ */
8604
+ animationStyle(style) {
8605
+ if (style === undefined) {
8606
+ return this.config.animationStyle;
8607
+ }
8608
+
8609
+ if (this.validationService.isValidAnimationStyle(style)) {
8610
+ this.config.animationStyle = style;
8611
+ }
8612
+
8613
+ return this.config.animationStyle;
8614
+ }
8615
+
8819
8616
  /**
8820
8617
  * Gets or sets the simultaneous animation delay
8821
8618
  * @param {number} [delay] - New delay in milliseconds
@@ -8835,97 +8632,7 @@ var Chessboard = (function (exports) {
8835
8632
 
8836
8633
  // Additional API methods would be added here following the same pattern
8837
8634
  // This is a good starting point for the refactored architecture
8838
-
8839
- // Additional API methods and aliases for backward compatibility
8840
- insert(square, piece) { return this.putPiece(piece, square); }
8841
- build() { return this._initialize(); }
8842
- ascii() { return this.positionService.getGame().ascii(); }
8843
- board() { return this.positionService.getGame().board(); }
8844
- getCastlingRights(color) { return this.positionService.getGame().getCastlingRights(color); }
8845
- getComment() { return this.positionService.getGame().getComment(); }
8846
- getComments() { return this.positionService.getGame().getComments(); }
8847
- lastMove() { return this.positionService.getGame().lastMove(); }
8848
- moveNumber() { return this.positionService.getGame().moveNumber(); }
8849
- moves(options = {}) { return this.positionService.getGame().moves(options); }
8850
- squareColor(squareId) { return this.boardService.getSquare(squareId).isWhite() ? 'light' : 'dark'; }
8851
- isDrawByFiftyMoves() { return this.positionService.getGame().isDrawByFiftyMoves(); }
8852
- isInsufficientMaterial() { return this.positionService.getGame().isInsufficientMaterial(); }
8853
- removeComment() { return this.positionService.getGame().removeComment(); }
8854
- removeComments() { return this.positionService.getGame().removeComments(); }
8855
- removeHeader(field) { return this.positionService.getGame().removeHeader(field); }
8856
- setCastlingRights(color, rights) { return this.positionService.getGame().setCastlingRights(color, rights); }
8857
- setComment(comment) { return this.positionService.getGame().setComment(comment); }
8858
- setHeader(key, value) { return this.positionService.getGame().setHeader(key, value); }
8859
- validateFen(fen) { return this.positionService.getGame().validateFen(fen); }
8860
-
8861
- // Implementazioni reali per highlight/dehighlight
8862
- highlightSquare(square) {
8863
- return this.boardService.highlight(square);
8864
- }
8865
- dehighlightSquare(square) {
8866
- return this.boardService.dehighlight(square);
8867
- }
8868
- forceSync() { this._updateBoardPieces(true, true); this._updateBoardPieces(true, false); }
8869
-
8870
- // Metodi mancanti che causano fallimenti nei test
8871
- /**
8872
- * Move a piece from one square to another
8873
- * @param {string|Object} move - Move in format 'e2e4' or {from: 'e2', to: 'e4'}
8874
- * @param {Object} [opts] - Options
8875
- * @param {boolean} [opts.animate=true] - Whether to animate
8876
- * @returns {boolean} True if move was successful
8877
- */
8878
- movePiece(move, opts = {}) {
8879
- const animate = opts.animate !== undefined ? opts.animate : true;
8880
-
8881
- // --- API: accetta id/stringhe, ma converte subito in oggetti ---
8882
- let fromSquareObj, toSquareObj, promotion;
8883
- if (typeof move === 'string') {
8884
- if (move.length === 4) {
8885
- fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
8886
- toSquareObj = this.boardService.getSquare(move.substring(2, 4));
8887
- } else if (move.length === 5) {
8888
- fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
8889
- toSquareObj = this.boardService.getSquare(move.substring(2, 4));
8890
- promotion = move.substring(4, 5);
8891
- } else {
8892
- throw new Error(`Invalid move format: ${move}`);
8893
- }
8894
- } else if (typeof move === 'object' && move.from && move.to) {
8895
- // Se sono id, converto in oggetti; se sono già oggetti, li uso direttamente
8896
- fromSquareObj = typeof move.from === 'string' ? this.boardService.getSquare(move.from) : move.from;
8897
- toSquareObj = typeof move.to === 'string' ? this.boardService.getSquare(move.to) : move.to;
8898
- promotion = move.promotion;
8899
- } else {
8900
- throw new Error(`Invalid move: ${move}`);
8901
- }
8902
-
8903
- if (!fromSquareObj || !toSquareObj) {
8904
- throw new Error(`Invalid squares: ${move.from || move.substring(0, 2)} or ${move.to || move.substring(2, 4)}`);
8905
- }
8906
-
8907
- // --- Internamente: lavora solo con oggetti ---
8908
- const result = this._onMove(fromSquareObj, toSquareObj, promotion, animate);
8909
- // Dopo ogni mossa, forza la sincronizzazione della board
8910
- this._updateBoardPieces(true, false);
8911
- return result;
8912
- }
8913
-
8914
- // Aliases for backward compatibility
8915
- move(move, animate = true) {
8916
- // On any new move, clear the redo stack
8917
- this._undoneMoves = [];
8918
- return this.movePiece(move, { animate });
8919
- }
8920
- get(square) { return this.getPiece(square); }
8921
- piece(square) { return this.getPiece(square); }
8922
- put(piece, square, opts = {}) { return this.putPiece(piece, square, opts); }
8923
- remove(square, opts = {}) { return this.removePiece(square, opts); }
8924
- load(position, opts = {}) { return this.setPosition(position, opts); }
8925
- resize(size) { return this.resizeBoard(size); }
8926
- start(opts = {}) { return this.reset(opts); }
8927
- clearBoard(opts = {}) { return this.clear(opts); }
8928
- };
8635
+ }
8929
8636
 
8930
8637
  /**
8931
8638
  * Structured logging system for Chessboard.js
@@ -9383,6 +9090,7 @@ var Chessboard = (function (exports) {
9383
9090
  hints: true,
9384
9091
  clickable: true,
9385
9092
  moveHighlight: true,
9093
+ animationStyle: 'simultaneous'
9386
9094
  });
9387
9095
 
9388
9096
  // Tournament template
@@ -9393,6 +9101,7 @@ var Chessboard = (function (exports) {
9393
9101
  clickable: true,
9394
9102
  moveHighlight: true,
9395
9103
  onlyLegalMoves: true,
9104
+ animationStyle: 'sequential'
9396
9105
  });
9397
9106
 
9398
9107
  // Analysis template
@@ -9403,6 +9112,7 @@ var Chessboard = (function (exports) {
9403
9112
  clickable: true,
9404
9113
  moveHighlight: true,
9405
9114
  mode: 'analysis',
9115
+ animationStyle: 'simultaneous'
9406
9116
  });
9407
9117
 
9408
9118
  // Puzzle template
@@ -9413,6 +9123,7 @@ var Chessboard = (function (exports) {
9413
9123
  clickable: true,
9414
9124
  moveHighlight: true,
9415
9125
  onlyLegalMoves: true,
9126
+ animationStyle: 'sequential'
9416
9127
  });
9417
9128
 
9418
9129
  // Demo template
@@ -9422,6 +9133,7 @@ var Chessboard = (function (exports) {
9422
9133
  hints: false,
9423
9134
  clickable: false,
9424
9135
  moveHighlight: true,
9136
+ animationStyle: 'simultaneous'
9425
9137
  });
9426
9138
  }
9427
9139
 
@@ -9467,7 +9179,7 @@ var Chessboard = (function (exports) {
9467
9179
  this.validationService.validateConfig(finalConfig);
9468
9180
 
9469
9181
  // Create chessboard instance
9470
- const chessboard = new Chessboard$1(finalConfig);
9182
+ const chessboard = new Chessboard(finalConfig);
9471
9183
 
9472
9184
  // Store instance for management
9473
9185
  this.instances.set(containerId, {
@@ -9727,44 +9439,13 @@ var Chessboard = (function (exports) {
9727
9439
  */
9728
9440
 
9729
9441
 
9730
- /**
9731
- * Main Chessboard factory function for backward compatibility
9732
- * Supports both legacy and modern calling conventions
9733
- * @param {string|Object} containerElm - Container element ID or configuration object
9734
- * @param {Object} [config={}] - Configuration options (when first param is string)
9735
- * @returns {ChessboardClass} Chessboard instance
9736
- */
9737
- function Chessboard(containerElm, config = {}) {
9738
- const factoryLogger = logger.child('ChessboardFactory');
9739
-
9740
- try {
9741
- // If first parameter is an object, treat it as config
9742
- if (typeof containerElm === 'object' && containerElm !== null) {
9743
- factoryLogger.debug('Creating chessboard with config object');
9744
- return new Chessboard$1(containerElm);
9745
- }
9746
-
9747
- // Otherwise, treat first parameter as element ID
9748
- if (typeof containerElm === 'string') {
9749
- factoryLogger.debug('Creating chessboard with element ID', { elementId: containerElm });
9750
- const fullConfig = { ...config, id: containerElm };
9751
- return new Chessboard$1(fullConfig);
9752
- }
9753
-
9754
- throw new Error('Invalid parameters: first parameter must be string or object');
9755
- } catch (error) {
9756
- factoryLogger.error('Failed to create chessboard instance', { error });
9757
- throw error;
9758
- }
9759
- }
9760
-
9761
9442
  /**
9762
9443
  * Wrapper class that handles both calling conventions
9763
9444
  * Provides enhanced error handling and logging
9764
9445
  * @class
9765
9446
  * @extends ChessboardClass
9766
9447
  */
9767
- class ChessboardWrapper extends Chessboard$1 {
9448
+ class ChessboardWrapper extends Chessboard {
9768
9449
  /**
9769
9450
  * Creates a new ChessboardWrapper instance
9770
9451
  * @param {string|Object} containerElm - Container element ID or configuration object
@@ -9793,53 +9474,32 @@ var Chessboard = (function (exports) {
9793
9474
  }
9794
9475
  }
9795
9476
 
9796
- /**
9797
- * Refactored Chessboard API - see Chessboard.js for full method docs
9798
- * @typedef {import('./Chessboard.js').Chessboard} Chessboard
9799
- */
9477
+ // Attach classes and utilities to the factory function for direct access
9478
+ Chessboard.Class = ChessboardWrapper;
9479
+ Chessboard.Chessboard = ChessboardWrapper;
9480
+ Chessboard.Config = ChessboardConfig;
9481
+ Chessboard.Factory = ChessboardFactory;
9800
9482
 
9801
- // --- STATIC/FACTORY METHODS ---
9802
- /**
9803
- * Create a new Chessboard instance
9804
- * @param {string|Object} containerElm
9805
- * @param {Object} [config]
9806
- * @returns {Chessboard}
9807
- */
9483
+ // Attach factory methods
9808
9484
  Chessboard.create = createChessboard;
9809
- /**
9810
- * Create a Chessboard from a template
9811
- * @param {string|Object} containerElm
9812
- * @param {string} templateName
9813
- * @param {Object} [config]
9814
- * @returns {Chessboard}
9815
- */
9816
- Chessboard.fromTemplate = createChessboardFromTemplate;
9485
+ Chessboard.createFromTemplate = createChessboardFromTemplate;
9817
9486
  Chessboard.factory = chessboardFactory;
9818
9487
 
9819
- // --- INSTANCE MANAGEMENT ---
9488
+ // Static methods for instance management
9820
9489
  Chessboard.getInstance = (containerId) => chessboardFactory.getInstance(containerId);
9821
9490
  Chessboard.destroyInstance = (containerId) => chessboardFactory.destroy(containerId);
9822
9491
  Chessboard.destroyAll = () => chessboardFactory.destroyAll();
9823
9492
  Chessboard.listInstances = () => chessboardFactory.listInstances();
9824
9493
 
9825
- // --- TEMPLATE MANAGEMENT ---
9494
+ // Template management
9826
9495
  Chessboard.registerTemplate = (name, config) => chessboardFactory.registerTemplate(name, config);
9827
9496
  Chessboard.removeTemplate = (name) => chessboardFactory.removeTemplate(name);
9828
9497
  Chessboard.getTemplate = (name) => chessboardFactory.getTemplate(name);
9829
9498
  Chessboard.listTemplates = () => chessboardFactory.listTemplates();
9830
9499
 
9831
- // --- STATS & DEBUG ---
9500
+ // Statistics and debugging
9832
9501
  Chessboard.getStats = () => chessboardFactory.getStats();
9833
9502
 
9834
- // --- DEPRECATED/LEGACY ALIASES ---
9835
- /**
9836
- * @deprecated Use Chessboard.create instead
9837
- */
9838
- Chessboard.Class = ChessboardWrapper;
9839
- Chessboard.Chessboard = ChessboardWrapper;
9840
- Chessboard.Config = ChessboardConfig;
9841
- Chessboard.Factory = ChessboardFactory;
9842
-
9843
9503
  /**
9844
9504
  * Coordinate utilities for Chessboard.js
9845
9505
  */
@@ -10229,7 +9889,11 @@ var Chessboard = (function (exports) {
10229
9889
  if (config.moveAnimation && !isValidEasing(config.moveAnimation)) {
10230
9890
  errors.push('Invalid moveAnimation. Must be a valid easing function');
10231
9891
  }
10232
-
9892
+
9893
+ if (config.animationStyle && !['sequential', 'simultaneous'].includes(config.animationStyle)) {
9894
+ errors.push('Invalid animationStyle. Must be "sequential" or "simultaneous"');
9895
+ }
9896
+
10233
9897
  return {
10234
9898
  success: errors.length === 0,
10235
9899
  errors