@alepot55/chessboardjs 2.3.5 → 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.
@@ -1096,7 +1096,7 @@ const DEFAULT_CONFIG$1 = Object.freeze({
1096
1096
  ratio: 0.9,
1097
1097
  piecesPath: '../assets/themes/default',
1098
1098
  animationStyle: 'simultaneous',
1099
- simultaneousAnimationDelay: 100,
1099
+ simultaneousAnimationDelay: 0,
1100
1100
  onMove: () => true,
1101
1101
  onMoveEnd: () => true,
1102
1102
  onChange: () => true,
@@ -1130,19 +1130,19 @@ class ChessboardConfig {
1130
1130
  constructor(settings = {}) {
1131
1131
  // Initialize validation service
1132
1132
  this._validationService = new ValidationService();
1133
-
1133
+
1134
1134
  // Validate input
1135
1135
  this._validateInput(settings);
1136
-
1136
+
1137
1137
  // Merge with defaults
1138
1138
  const config = this._mergeWithDefaults(settings);
1139
-
1139
+
1140
1140
  // Process and validate configuration
1141
1141
  this._processConfiguration(config);
1142
-
1142
+
1143
1143
  // Set CSS properties
1144
1144
  this._setCSSProperties(config);
1145
-
1145
+
1146
1146
  // Configure mode-specific settings
1147
1147
  this._configureModeSettings();
1148
1148
  }
@@ -1157,7 +1157,7 @@ class ChessboardConfig {
1157
1157
  if (settings !== null && typeof settings !== 'object') {
1158
1158
  throw new ConfigurationError('Settings must be an object', 'settings', settings);
1159
1159
  }
1160
-
1160
+
1161
1161
  // Validate using validation service
1162
1162
  try {
1163
1163
  this._validationService.validateConfig(settings);
@@ -1191,7 +1191,7 @@ class ChessboardConfig {
1191
1191
  this.size = config.size;
1192
1192
  this.movableColors = config.movableColors;
1193
1193
  this.piecesPath = config.piecesPath;
1194
-
1194
+
1195
1195
  // Event handlers
1196
1196
  this.onMove = this._validateCallback(config.onMove);
1197
1197
  this.onMoveEnd = this._validateCallback(config.onMoveEnd);
@@ -1219,7 +1219,7 @@ class ChessboardConfig {
1219
1219
  this.snapbackTime = this._setTime(config.snapbackTime);
1220
1220
  this.dropCenterTime = this._setTime(config.dropCenterTime);
1221
1221
  this.fadeTime = this._setTime(config.fadeTime);
1222
-
1222
+
1223
1223
  // Animation style properties
1224
1224
  this.animationStyle = this._validateAnimationStyle(config.animationStyle);
1225
1225
  this.simultaneousAnimationDelay = this._validateDelay(config.simultaneousAnimationDelay);
@@ -1352,11 +1352,11 @@ class ChessboardConfig {
1352
1352
  }
1353
1353
  return value;
1354
1354
  }
1355
-
1355
+
1356
1356
  if (typeof value === 'string' && value in ANIMATION_TIMES) {
1357
1357
  return ANIMATION_TIMES[value];
1358
1358
  }
1359
-
1359
+
1360
1360
  throw new ConfigurationError('Invalid time value', 'time', value);
1361
1361
  }
1362
1362
 
@@ -1371,11 +1371,11 @@ class ChessboardConfig {
1371
1371
  if (typeof value === 'boolean') {
1372
1372
  return value;
1373
1373
  }
1374
-
1374
+
1375
1375
  if (value in BOOLEAN_VALUES) {
1376
1376
  return BOOLEAN_VALUES[value];
1377
1377
  }
1378
-
1378
+
1379
1379
  throw new ConfigurationError('Invalid boolean value', 'boolean', value);
1380
1380
  }
1381
1381
 
@@ -1391,17 +1391,17 @@ class ChessboardConfig {
1391
1391
  if (typeof value === 'boolean') {
1392
1392
  return value ? TRANSITION_FUNCTIONS.ease : null;
1393
1393
  }
1394
-
1394
+
1395
1395
  // Handle string values
1396
1396
  if (typeof value === 'string' && value in TRANSITION_FUNCTIONS) {
1397
1397
  return TRANSITION_FUNCTIONS[value];
1398
1398
  }
1399
-
1399
+
1400
1400
  // Handle null/undefined
1401
1401
  if (value === null || value === undefined) {
1402
1402
  return null;
1403
1403
  }
1404
-
1404
+
1405
1405
  throw new ConfigurationError('Invalid transition function', 'transitionFunction', value);
1406
1406
  }
1407
1407
 
@@ -1454,7 +1454,7 @@ class ChessboardConfig {
1454
1454
 
1455
1455
  // Apply updates
1456
1456
  const newConfig = Object.assign({}, this.toObject(), updates);
1457
-
1457
+
1458
1458
  // Re-process configuration
1459
1459
  this._processConfiguration(newConfig);
1460
1460
  this._setCSSProperties(newConfig);
@@ -1677,10 +1677,22 @@ class Piece {
1677
1677
 
1678
1678
  setDrag(f) {
1679
1679
  if (!this.element) { console.debug(`[Piece] setDrag: ${this.id} - element is null`); return; }
1680
+ // Remove previous handlers
1681
+ this.element.onmousedown = null;
1682
+ this.element.ontouchstart = null;
1683
+ this.element.ondragstart = null;
1684
+ if (window.PointerEvent) {
1685
+ this.element.onpointerdown = null;
1686
+ }
1687
+ // Set new handlers
1680
1688
  this.element.ondragstart = (e) => { e.preventDefault(); };
1681
1689
  this.element.onmousedown = f;
1682
1690
  this.element.ontouchstart = f; // Drag touch
1683
- console.debug(`[Piece] setDrag: ${this.id}`);
1691
+ if (window.PointerEvent) {
1692
+ this.element.onpointerdown = f;
1693
+ console.debug(`[Piece] setDrag: pointerdown set for ${this.id}`);
1694
+ }
1695
+ console.debug(`[Piece] setDrag: mousedown/ontouchstart set for ${this.id}`);
1684
1696
  }
1685
1697
 
1686
1698
  destroy() {
@@ -4655,9 +4667,15 @@ class PieceService {
4655
4667
  console.debug(`[PieceService] addPieceOnSquare: ${piece.id} to ${square.id}`);
4656
4668
  square.putPiece(piece);
4657
4669
 
4670
+ // Imposta sempre il drag (touch e mouse)
4658
4671
  if (dragFunction) {
4659
4672
  piece.setDrag(dragFunction(square, piece));
4660
4673
  }
4674
+ // Forza il drag touch se manca (debug/robustezza)
4675
+ if (!piece.element.ontouchstart) {
4676
+ piece.element.ontouchstart = dragFunction ? dragFunction(square, piece) : () => { };
4677
+ console.debug(`[PieceService] Forzato ontouchstart su ${piece.id}`);
4678
+ }
4661
4679
 
4662
4680
  if (fade && this.config.fadeTime > 0) {
4663
4681
  piece.fadeIn(
@@ -4715,8 +4733,8 @@ class PieceService {
4715
4733
  */
4716
4734
  movePiece(piece, targetSquare, duration, callback) {
4717
4735
  console.debug(`[PieceService] movePiece: ${piece.id} to ${targetSquare.id}`);
4718
- if (!piece) {
4719
- console.warn('PieceService.movePiece: piece is null, skipping animation');
4736
+ if (!piece || !piece.element) {
4737
+ console.warn(`[PieceService] movePiece: piece or element is null, skipping animation`);
4720
4738
  if (callback) callback();
4721
4739
  return;
4722
4740
  }
@@ -4772,7 +4790,7 @@ class PieceService {
4772
4790
  };
4773
4791
 
4774
4792
  // Check if piece is currently being dragged
4775
- const isDragging = move.piece.element.classList.contains('dragging');
4793
+ const isDragging = move.piece.element && move.piece.element.classList.contains('dragging');
4776
4794
 
4777
4795
  if (isDragging) {
4778
4796
  // If piece is being dragged, don't animate - just move it immediately
@@ -7097,7 +7115,7 @@ class PositionService {
7097
7115
  * Implements the Facade pattern to provide a unified interface
7098
7116
  * @class
7099
7117
  */
7100
- let Chessboard$1 = class Chessboard {
7118
+ class Chessboard {
7101
7119
  /**
7102
7120
  * Creates a new Chessboard instance
7103
7121
  * @param {Object} config - Configuration object
@@ -7122,8 +7140,6 @@ let Chessboard$1 = class Chessboard {
7122
7140
  } catch (error) {
7123
7141
  this._handleConstructorError(error);
7124
7142
  }
7125
- this._undoneMoves = [];
7126
- this._updateBoardPieces(true, true); // Forza popolamento DOM subito
7127
7143
  }
7128
7144
 
7129
7145
  /**
@@ -7167,31 +7183,6 @@ let Chessboard$1 = class Chessboard {
7167
7183
  }
7168
7184
  }
7169
7185
 
7170
- /**
7171
- * Cleans up any partially initialized resources (safe to call multiple times)
7172
- * @private
7173
- */
7174
- _cleanup() {
7175
- // Remove event listeners if present
7176
- if (this.eventService && typeof this.eventService.removeListeners === 'function') {
7177
- this.eventService.removeListeners();
7178
- }
7179
- // Clear timeouts
7180
- if (this._updateTimeout) {
7181
- clearTimeout(this._updateTimeout);
7182
- this._updateTimeout = null;
7183
- }
7184
- // Null all services
7185
- this.validationService = null;
7186
- this.coordinateService = null;
7187
- this.positionService = null;
7188
- this.boardService = null;
7189
- this.pieceService = null;
7190
- this.animationService = null;
7191
- this.moveService = null;
7192
- this.eventService = null;
7193
- }
7194
-
7195
7186
  /**
7196
7187
  * Initializes all services
7197
7188
  * @private
@@ -7260,23 +7251,8 @@ let Chessboard$1 = class Chessboard {
7260
7251
  /**
7261
7252
  * Builds the board DOM structure
7262
7253
  * @private
7263
- * Best practice: always remove squares (destroy JS/DOM) before clearing the board container.
7264
7254
  */
7265
7255
  _buildBoard() {
7266
- console.log('CHIAMATO: _buildBoard');
7267
- if (this._isUndoRedo) {
7268
- console.log('SKIP _buildBoard per undo/redo');
7269
- return;
7270
- }
7271
- // Forza la pulizia completa del contenitore board (DOM)
7272
- const boardContainer = document.getElementById(this.config.id_div);
7273
- if (boardContainer) boardContainer.innerHTML = '';
7274
- // Force remove all pieces from all squares (no animation, best practice)
7275
- if (this.boardService && this.boardService.squares) {
7276
- Object.values(this.boardService.squares).forEach(sq => sq && sq.forceRemoveAllPieces && sq.forceRemoveAllPieces());
7277
- }
7278
- if (this.boardService && this.boardService.removeSquares) this.boardService.removeSquares();
7279
- if (this.boardService && this.boardService.removeBoard) this.boardService.removeBoard();
7280
7256
  this.boardService.buildBoard();
7281
7257
  }
7282
7258
 
@@ -7285,14 +7261,6 @@ let Chessboard$1 = class Chessboard {
7285
7261
  * @private
7286
7262
  */
7287
7263
  _buildSquares() {
7288
- console.log('CHIAMATO: _buildSquares');
7289
- if (this._isUndoRedo) {
7290
- console.log('SKIP _buildSquares per undo/redo');
7291
- return;
7292
- }
7293
- if (this.boardService && this.boardService.removeSquares) {
7294
- this.boardService.removeSquares();
7295
- }
7296
7264
  this.boardService.buildSquares((row, col) => {
7297
7265
  return this.coordinateService.realCoord(row, col);
7298
7266
  });
@@ -7662,7 +7630,6 @@ let Chessboard$1 = class Chessboard {
7662
7630
  * @param {boolean} [isPositionLoad=false] - Whether this is a position load
7663
7631
  */
7664
7632
  _updateBoardPieces(animation = false, isPositionLoad = false) {
7665
- console.log('CHIAMATO: _updateBoardPieces', { animation, isPositionLoad, isUndoRedo: this._isUndoRedo });
7666
7633
  // Check if services are available
7667
7634
  if (!this.positionService || !this.moveService || !this.eventService) {
7668
7635
  console.log('Cannot update board pieces - services not available');
@@ -7728,48 +7695,35 @@ let Chessboard$1 = class Chessboard {
7728
7695
  * @param {boolean} [isPositionLoad=false] - Whether this is a position load (affects delay)
7729
7696
  */
7730
7697
  _doUpdateBoardPieces(animation = false, isPositionLoad = false) {
7731
- // Blocca update se un drag è in corso
7732
- if (this._isDragging) return;
7733
7698
  // Skip update if we're in the middle of a promotion
7734
7699
  if (this._isPromoting) {
7700
+ console.log('Skipping board update during promotion');
7735
7701
  return;
7736
7702
  }
7703
+
7704
+ // Check if services are available
7737
7705
  if (!this.positionService || !this.positionService.getGame()) {
7706
+ console.log('Cannot update board pieces - position service not available');
7738
7707
  return;
7739
7708
  }
7709
+
7740
7710
  const squares = this.boardService.getAllSquares();
7741
7711
  const gameStateBefore = this.positionService.getGame().fen();
7742
- if (/^8\/8\/8\/8\/8\/8\/8\/8/.test(gameStateBefore)) {
7743
- const boardContainer = document.getElementById(this.config.id_div);
7744
- if (boardContainer) {
7745
- const pieceElements = boardContainer.querySelectorAll('.piece');
7746
- pieceElements.forEach(element => {
7747
- element.remove();
7748
- });
7749
- }
7750
- Object.values(squares).forEach(sq => {
7751
- if (sq && sq.piece) {
7752
- sq.piece = null;
7753
- }
7754
- });
7755
- this._clearVisualState();
7756
- this._addListeners();
7757
- if (this.config.onChange) this.config.onChange(gameStateBefore);
7758
- return;
7759
- }
7712
+
7713
+ console.log('_doUpdateBoardPieces - current FEN:', gameStateBefore);
7714
+ console.log('_doUpdateBoardPieces - animation:', animation, 'style:', this.config.animationStyle, 'isPositionLoad:', isPositionLoad);
7715
+
7716
+ // Determine which animation style to use
7760
7717
  const useSimultaneous = this.config.animationStyle === 'simultaneous';
7718
+ console.log('_doUpdateBoardPieces - useSimultaneous:', useSimultaneous);
7719
+
7761
7720
  if (useSimultaneous) {
7721
+ console.log('Using simultaneous animation');
7762
7722
  this._doSimultaneousUpdate(squares, gameStateBefore, isPositionLoad);
7763
7723
  } else {
7724
+ console.log('Using sequential animation');
7764
7725
  this._doSequentialUpdate(squares, gameStateBefore, animation);
7765
7726
  }
7766
- // Pulizia finale robusta: rimuovi tutti i pezzi orfani dal DOM e dal riferimento JS
7767
- Object.values(this.boardService.getAllSquares()).forEach(square => {
7768
- const expectedPieceId = this.positionService.getGamePieceId(square.id);
7769
- if (!expectedPieceId && typeof square.forceRemoveAllPieces === 'function') {
7770
- square.forceRemoveAllPieces();
7771
- }
7772
- });
7773
7727
  }
7774
7728
 
7775
7729
  /**
@@ -7780,45 +7734,43 @@ let Chessboard$1 = class Chessboard {
7780
7734
  * @param {boolean} animation - Whether to animate
7781
7735
  */
7782
7736
  _doSequentialUpdate(squares, gameStateBefore, animation) {
7783
- // Mappa: squareId -> expectedPieceId
7784
- const expectedMap = {};
7785
- Object.values(squares).forEach(square => {
7786
- expectedMap[square.id] = this.positionService.getGamePieceId(square.id);
7787
- });
7788
-
7737
+ // Update each square sequentially
7789
7738
  Object.values(squares).forEach(square => {
7790
- const expectedPieceId = expectedMap[square.id];
7739
+ const expectedPieceId = this.positionService.getGamePieceId(square.id);
7791
7740
  const currentPiece = square.piece;
7792
7741
  const currentPieceId = currentPiece ? currentPiece.getId() : null;
7793
7742
 
7794
- // Se il pezzo attuale e quello atteso sono identici, non fare nulla
7795
- if (currentPieceId === expectedPieceId) {
7796
- return;
7797
- }
7743
+ // Log only for squares that are changing
7744
+ if (currentPieceId !== expectedPieceId) {
7745
+ console.log(`_doSequentialUpdate - ${square.id}: ${currentPieceId} -> ${expectedPieceId}`);
7798
7746
 
7799
- // Se c'è un pezzo attuale ma non è quello atteso, rimuovilo
7800
- if (currentPiece && currentPieceId !== expectedPieceId) {
7801
- // Rimozione robusta: elimina tutti i pezzi orfani dal DOM e dal riferimento JS
7802
- if (typeof square.forceRemoveAllPieces === 'function') {
7803
- square.forceRemoveAllPieces();
7747
+ // Check if we already have the correct piece (from promotion)
7748
+ if (currentPiece && currentPiece.getId() === expectedPieceId) {
7749
+ console.log(`Piece ${expectedPieceId} already correctly placed on ${square.id}`);
7804
7750
  } else {
7805
- this.pieceService.removePieceFromSquare(square, animation);
7806
- }
7807
- }
7751
+ // Remove current piece if exists
7752
+ if (currentPiece) {
7753
+ this.pieceService.removePieceFromSquare(square, animation);
7754
+ }
7808
7755
 
7809
- // Se c'è un pezzo atteso ma non è quello attuale, aggiungilo
7810
- if (expectedPieceId && currentPieceId !== expectedPieceId) {
7811
- const newPiece = this.pieceService.convertPiece(expectedPieceId);
7812
- this.pieceService.addPieceOnSquare(
7813
- square,
7814
- newPiece,
7815
- animation,
7816
- this._createDragFunction.bind(this)
7817
- );
7756
+ // Add new piece if needed
7757
+ if (expectedPieceId) {
7758
+ const newPiece = this.pieceService.convertPiece(expectedPieceId);
7759
+ this.pieceService.addPieceOnSquare(
7760
+ square,
7761
+ newPiece,
7762
+ animation,
7763
+ this._createDragFunction.bind(this)
7764
+ );
7765
+ }
7766
+ }
7818
7767
  }
7819
7768
  });
7820
7769
 
7770
+ // Re-add listeners after updating pieces to ensure hover events work correctly
7821
7771
  this._addListeners();
7772
+
7773
+ // Trigger change event if position changed
7822
7774
  const gameStateAfter = this.positionService.getGame().fen();
7823
7775
  if (gameStateBefore !== gameStateAfter) {
7824
7776
  this.config.onChange(gameStateAfter);
@@ -7833,38 +7785,16 @@ let Chessboard$1 = class Chessboard {
7833
7785
  * @param {boolean} [isPositionLoad=false] - Whether this is a position load
7834
7786
  */
7835
7787
  _doSimultaneousUpdate(squares, gameStateBefore, isPositionLoad = false) {
7836
- // Matching greedy per distanza minima, robusto
7837
- const currentMap = {};
7838
- const expectedMap = {};
7839
-
7840
- Object.values(squares).forEach(square => {
7841
- const currentPiece = square.piece;
7842
- const expectedPieceId = this.positionService.getGamePieceId(square.id);
7843
- if (currentPiece) {
7844
- // Normalizza la chiave come 'color+type' lowercase
7845
- const key = (currentPiece.color + currentPiece.type).toLowerCase();
7846
- if (!currentMap[key]) currentMap[key] = [];
7847
- currentMap[key].push({ square, id: square.id });
7848
- }
7849
- if (expectedPieceId) {
7850
- // Normalizza la chiave come 'color+type' lowercase
7851
- const key = expectedPieceId.toLowerCase();
7852
- if (!expectedMap[key]) expectedMap[key] = [];
7853
- expectedMap[key].push({ square, id: square.id });
7854
- }
7855
- });
7788
+ console.log('_doSimultaneousUpdate - Starting simultaneous update');
7856
7789
 
7857
- let animationsCompleted = 0;
7858
- let totalAnimations = 0;
7859
- const animationDelay = isPositionLoad ? 0 : this.config.simultaneousAnimationDelay;
7860
- let animationIndex = 0;
7790
+ // Analyze what changes need to be made
7791
+ const changeAnalysis = this._analyzePositionChanges(squares);
7861
7792
 
7862
- Object.keys(expectedMap).forEach(key => {
7863
- totalAnimations += Math.max((currentMap[key] || []).length, expectedMap[key].length);
7864
- });
7865
-
7866
- if (totalAnimations === 0) {
7793
+ if (changeAnalysis.totalChanges === 0) {
7794
+ console.log('_doSimultaneousUpdate - No changes needed, returning');
7867
7795
  this._addListeners();
7796
+
7797
+ // Trigger change event if position changed
7868
7798
  const gameStateAfter = this.positionService.getGame().fen();
7869
7799
  if (gameStateBefore !== gameStateAfter) {
7870
7800
  this.config.onChange(gameStateAfter);
@@ -7872,109 +7802,10 @@ let Chessboard$1 = class Chessboard {
7872
7802
  return;
7873
7803
  }
7874
7804
 
7875
- const onAnimationComplete = () => {
7876
- animationsCompleted++;
7877
- if (animationsCompleted === totalAnimations) {
7878
- this._addListeners();
7879
- const gameStateAfter = this.positionService.getGame().fen();
7880
- if (gameStateBefore !== gameStateAfter) {
7881
- this.config.onChange(gameStateAfter);
7882
- }
7883
- }
7884
- };
7885
-
7886
- Object.keys(expectedMap).forEach(key => {
7887
- const fromList = (currentMap[key] || []).slice();
7888
- const toList = expectedMap[key].slice();
7889
-
7890
- // 1. Costruisci matrice delle distanze
7891
- const distances = [];
7892
- for (let i = 0; i < fromList.length; i++) {
7893
- distances[i] = [];
7894
- for (let j = 0; j < toList.length; j++) {
7895
- distances[i][j] = Math.abs(fromList[i].square.row - toList[j].square.row) +
7896
- Math.abs(fromList[i].square.col - toList[j].square.col);
7897
- }
7898
- }
7899
-
7900
- // 2. Matching greedy: abbina i più vicini
7901
- const fromMatched = new Array(fromList.length).fill(false);
7902
- const toMatched = new Array(toList.length).fill(false);
7903
- const moves = [];
7904
-
7905
- while (true) {
7906
- let minDist = Infinity, minI = -1, minJ = -1;
7907
- for (let i = 0; i < fromList.length; i++) {
7908
- if (fromMatched[i]) continue;
7909
- for (let j = 0; j < toList.length; j++) {
7910
- if (toMatched[j]) continue;
7911
- if (distances[i][j] < minDist) {
7912
- minDist = distances[i][j];
7913
- minI = i;
7914
- minJ = j;
7915
- }
7916
- }
7917
- }
7918
- if (minI === -1 || minJ === -1) break;
7919
- // Se la posizione è la stessa, non fare nulla (pezzo unchanged)
7920
- if (fromList[minI].square === toList[minJ].square) {
7921
- fromMatched[minI] = true;
7922
- toMatched[minJ] = true;
7923
- continue;
7924
- }
7925
- // Altrimenti, sposta il pezzo
7926
- moves.push({ from: fromList[minI].square, to: toList[minJ].square, piece: fromList[minI].square.piece });
7927
- fromMatched[minI] = true;
7928
- toMatched[minJ] = true;
7929
- }
7930
-
7931
- // 3. Rimuovi i pezzi non abbinati (presenti solo in fromList)
7932
- for (let i = 0; i < fromList.length; i++) {
7933
- if (!fromMatched[i]) {
7934
- setTimeout(() => {
7935
- // Rimozione robusta: elimina tutti i pezzi orfani dal DOM e dal riferimento JS
7936
- if (typeof fromList[i].square.forceRemoveAllPieces === 'function') {
7937
- fromList[i].square.forceRemoveAllPieces();
7938
- } else {
7939
- this.pieceService.removePieceFromSquare(fromList[i].square, true, onAnimationComplete);
7940
- }
7941
- onAnimationComplete();
7942
- }, animationIndex * animationDelay);
7943
- animationIndex++;
7944
- }
7945
- }
7946
-
7947
- // 4. Aggiungi i pezzi non abbinati (presenti solo in toList)
7948
- for (let j = 0; j < toList.length; j++) {
7949
- if (!toMatched[j]) {
7950
- setTimeout(() => {
7951
- const newPiece = this.pieceService.convertPiece(key);
7952
- this.pieceService.addPieceOnSquare(
7953
- toList[j].square,
7954
- newPiece,
7955
- true,
7956
- this._createDragFunction.bind(this),
7957
- onAnimationComplete
7958
- );
7959
- }, animationIndex * animationDelay);
7960
- animationIndex++;
7961
- }
7962
- }
7805
+ console.log('_doSimultaneousUpdate - Change analysis:', changeAnalysis);
7963
7806
 
7964
- // 5. Anima i movimenti
7965
- moves.forEach(move => {
7966
- setTimeout(() => {
7967
- this.pieceService.translatePiece(
7968
- move,
7969
- false,
7970
- true,
7971
- this._createDragFunction.bind(this),
7972
- onAnimationComplete
7973
- );
7974
- }, animationIndex * animationDelay);
7975
- animationIndex++;
7976
- });
7977
- });
7807
+ // Execute all changes simultaneously
7808
+ this._executeSimultaneousChanges(changeAnalysis, gameStateBefore, isPositionLoad);
7978
7809
  }
7979
7810
 
7980
7811
  /**
@@ -8300,380 +8131,99 @@ let Chessboard$1 = class Chessboard {
8300
8131
  }
8301
8132
 
8302
8133
  // -------------------
8303
- // Public API Methods (Refactored)
8134
+ // Public API Methods
8304
8135
  // -------------------
8305
8136
 
8306
- // --- POSITION & STATE ---
8307
8137
  /**
8308
- * Get the current position as FEN
8309
- * @returns {string}
8310
- */
8311
- getPosition() { return this.fen(); }
8312
- /**
8313
- * Set the board position (FEN or object)
8314
- * @param {string|Object} position
8315
- * @param {Object} [opts]
8316
- * @param {boolean} [opts.animate=true]
8317
- * @returns {boolean}
8318
- */
8319
- setPosition(position, opts = {}) {
8320
- const animate = opts.animate !== undefined ? opts.animate : true;
8321
- // Remove highlights and selections
8322
- if (this.boardService && this.boardService.applyToAllSquares) {
8323
- this.boardService.applyToAllSquares('removeHint');
8324
- this.boardService.applyToAllSquares('deselect');
8325
- this.boardService.applyToAllSquares('unmoved');
8326
- }
8327
- if (this.positionService && this.positionService.setGame) {
8328
- this.positionService.setGame(position);
8329
- }
8330
- if (this._updateBoardPieces) {
8331
- this._updateBoardPieces(animate, true);
8332
- }
8333
- // Forza la sincronizzazione dopo setPosition
8334
- this._updateBoardPieces(true, false);
8335
- return true;
8336
- }
8337
- /**
8338
- * Reset the board to the starting position
8339
- * @param {Object} [opts]
8340
- * @param {boolean} [opts.animate=true]
8341
- * @returns {boolean}
8342
- */
8343
- reset(opts = {}) {
8344
- const animate = opts.animate !== undefined ? opts.animate : true;
8345
- // Use the default starting position from config or fallback
8346
- const startPosition = this.config && this.config.position ? this.config.position : 'start';
8347
- this._updateBoardPieces(animate);
8348
- const result = this.setPosition(startPosition, { animate });
8349
- // Forza la sincronizzazione dopo reset
8350
- this._updateBoardPieces(true, false);
8351
- return result;
8352
- }
8353
- /**
8354
- * Clear the board
8355
- * @param {Object} [opts]
8356
- * @param {boolean} [opts.animate=true]
8357
- * @returns {boolean}
8138
+ * Gets the current position as FEN
8139
+ * @returns {string} FEN string
8358
8140
  */
8359
- clear(opts = {}) {
8360
- const animate = opts.animate !== undefined ? opts.animate : true;
8361
- if (!this.positionService || !this.positionService.getGame()) {
8362
- return false;
8363
- }
8364
- if (this._clearVisualState) this._clearVisualState();
8365
- this.positionService.getGame().clear();
8366
- // Forza la rimozione di tutti i pezzi dal DOM
8367
- if (this.boardService && this.boardService.squares) {
8368
- Object.values(this.boardService.squares).forEach(sq => {
8369
- if (sq && sq.piece) sq.piece = null;
8370
- });
8371
- }
8372
- if (this._updateBoardPieces) {
8373
- this._updateBoardPieces(animate, true);
8374
- }
8375
- // Forza la sincronizzazione dopo clear
8376
- this._updateBoardPieces(true, false);
8377
- return true;
8141
+ fen() {
8142
+ return this.positionService.getGame().fen();
8378
8143
  }
8379
8144
 
8380
- // --- MOVE MANAGEMENT ---
8381
8145
  /**
8382
- * Undo last move
8383
- * @param {Object} [opts]
8384
- * @param {boolean} [opts.animate=true]
8385
- * @returns {boolean}
8146
+ * Gets current turn
8147
+ * @returns {string} 'w' or 'b'
8386
8148
  */
8387
- undoMove(opts = {}) {
8388
- const undone = this.positionService.getGame().undo();
8389
- if (undone) {
8390
- this._undoneMoves.push(undone);
8391
- // Forza refresh completo di tutti i pezzi dopo undo
8392
- this._updateBoardPieces(true, true);
8393
- return undone;
8394
- }
8395
- return null;
8396
- }
8397
- /**
8398
- * Redo last undone move
8399
- * @param {Object} [opts]
8400
- * @param {boolean} [opts.animate=true]
8401
- * @returns {boolean}
8402
- */
8403
- redoMove(opts = {}) {
8404
- if (this._undoneMoves && this._undoneMoves.length > 0) {
8405
- const move = this._undoneMoves.pop();
8406
- const moveObj = { from: move.from, to: move.to };
8407
- if (move.promotion) moveObj.promotion = move.promotion;
8408
- const result = this.positionService.getGame().move(moveObj);
8409
- // Forza refresh completo di tutti i pezzi dopo redo
8410
- this._updateBoardPieces(true, true);
8411
- return result;
8412
- }
8413
- return false;
8149
+ turn() {
8150
+ return this.positionService.getGame().turn();
8414
8151
  }
8415
- /**
8416
- * Get legal moves for a square
8417
- * @param {string} square
8418
- * @returns {Array}
8419
- */
8420
- getLegalMoves(square) { return this.legalMoves(square); }
8421
8152
 
8422
- // --- PIECE MANAGEMENT ---
8423
8153
  /**
8424
- * Get the piece at a square
8425
- * @param {string} square
8426
- * @returns {string|null}
8427
- */
8428
- getPiece(square) {
8429
- // Sempre leggi lo stato aggiornato dal boardService
8430
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8431
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[getPiece] Parametro square non valido');
8432
- // Forza sync prima di leggere
8433
- this._updateBoardPieces(false, false);
8434
- const piece = squareObj.piece;
8435
- if (!piece) return null;
8436
- return (piece.color + piece.type).toLowerCase();
8437
- }
8438
- /**
8439
- * Put a piece on a square
8440
- * @param {string|Piece} piece
8441
- * @param {string|Square} square
8442
- * @param {Object} [opts]
8443
- * @param {boolean} [opts.animate=true]
8444
- * @returns {boolean}
8154
+ * Loads a new position
8155
+ * @param {string|Object} position - Position to load
8156
+ * @param {Object} [options={}] - Loading options
8157
+ * @param {boolean} [animation=true] - Whether to animate
8445
8158
  */
8446
- putPiece(piece, square, opts = {}) {
8447
- const animate = opts.animate !== undefined ? opts.animate : true;
8448
- let pieceStr = piece;
8449
- if (typeof piece === 'object' && piece.type && piece.color) {
8450
- pieceStr = (piece.color + piece.type).toLowerCase();
8451
- } else if (typeof piece === 'string' && piece.length === 2) {
8452
- const a = piece[0].toLowerCase();
8453
- const b = piece[1].toLowerCase();
8454
- const types = 'kqrbnp';
8455
- const colors = 'wb';
8456
- if (types.includes(a) && colors.includes(b)) {
8457
- pieceStr = b + a;
8458
- } else if (colors.includes(a) && types.includes(b)) {
8459
- pieceStr = a + b;
8460
- } else {
8461
- throw new Error(`[putPiece] Invalid piece: ${piece}`);
8462
- }
8463
- }
8464
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8465
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[putPiece] Parametro square non valido');
8466
- const pieceObj = this.pieceService.convertPiece(pieceStr);
8467
- if (!pieceObj || typeof pieceObj !== 'object' || !('type' in pieceObj)) throw new Error('[putPiece] Parametro piece non valido');
8468
- // Aggiorna solo il motore chess.js
8469
- const chessJsPiece = { type: pieceObj.type, color: pieceObj.color };
8470
- const game = this.positionService.getGame();
8471
- const result = game.put(chessJsPiece, squareObj.id);
8472
- if (!result) throw new Error(`[putPiece] Game.put failed for ${pieceStr} on ${squareObj.id}`);
8473
- // Non aggiornare direttamente square.piece!
8474
- // Riallinea la board JS allo stato del motore
8475
- this._updateBoardPieces(animate);
8476
- return true;
8477
- }
8478
- /**
8479
- * Remove a piece from a square
8480
- * @param {string|Square} square
8481
- * @param {Object} [opts]
8482
- * @param {boolean} [opts.animate=true]
8483
- * @returns {string|null}
8484
- */
8485
- removePiece(square, opts = {}) {
8486
- const animate = opts.animate !== undefined ? opts.animate : true;
8487
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8488
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[removePiece] Parametro square non valido');
8489
- // Aggiorna solo il motore chess.js
8490
- const game = this.positionService.getGame();
8491
- game.remove(squareObj.id);
8492
- // Non aggiornare direttamente square.piece!
8493
- // Riallinea la board JS allo stato del motore
8494
- this._updateBoardPieces(animate);
8495
- return true;
8496
- }
8159
+ load(position, options = {}, animation = true) {
8160
+ this.boardService.applyToAllSquares('removeHint');
8161
+ this.boardService.applyToAllSquares('deselect');
8162
+ this.boardService.applyToAllSquares('unmoved');
8497
8163
 
8498
- // --- BOARD CONTROL ---
8499
- /**
8500
- * Flip the board orientation
8501
- * @param {Object} [opts]
8502
- * @param {boolean} [opts.animate=true]
8503
- */
8504
- flipBoard(opts = {}) {
8505
- if (this.coordinateService && this.coordinateService.flipOrientation) {
8506
- this.coordinateService.flipOrientation();
8507
- }
8508
- if (this._buildBoard) this._buildBoard();
8509
- if (this._buildSquares) this._buildSquares();
8510
- if (this._addListeners) this._addListeners();
8511
- if (this._updateBoardPieces) this._updateBoardPieces(opts.animate !== false);
8512
- console.log('FEN dopo flip:', this.fen(), 'Orientamento:', this.coordinateService.getOrientation());
8513
- }
8514
- /**
8515
- * Set the board orientation
8516
- * @param {'w'|'b'} color
8517
- * @param {Object} [opts]
8518
- * @param {boolean} [opts.animate=true]
8519
- */
8520
- setOrientation(color, opts = {}) {
8521
- if (this.validationService.isValidOrientation(color)) {
8522
- this.coordinateService.setOrientation(color);
8523
- if (this._buildBoard) this._buildBoard();
8524
- if (this._buildSquares) this._buildSquares();
8525
- if (this._addListeners) this._addListeners();
8526
- if (this._updateBoardPieces) this._updateBoardPieces(opts.animate !== false);
8527
- }
8528
- return this.coordinateService.getOrientation();
8529
- }
8530
- /**
8531
- * Get the current orientation
8532
- * @returns {'w'|'b'}
8533
- */
8534
- getOrientation() { return this.orientation(); }
8535
- /**
8536
- * Resize the board
8537
- * @param {number|string} size
8538
- */
8539
- resizeBoard(size) {
8540
- if (size === 'auto') {
8541
- this.config.size = 'auto';
8542
- document.documentElement.style.setProperty('--dimBoard', 'auto');
8543
- this._updateBoardPieces(false);
8544
- return true;
8545
- }
8546
- if (typeof size !== 'number' || size < 50 || size > 3000) {
8547
- throw new Error(`[resizeBoard] Invalid size: ${size}`);
8548
- }
8549
- this.config.size = size;
8550
- document.documentElement.style.setProperty('--dimBoard', `${size}px`);
8551
- this._updateBoardPieces(false);
8552
- return true;
8164
+ this.positionService.setGame(position, options);
8165
+ this._updateBoardPieces(animation, true); // Position load
8553
8166
  }
8554
8167
 
8555
- // --- HIGHLIGHTING & UI ---
8556
8168
  /**
8557
- * Highlight a square
8558
- * @param {string|Square} square
8559
- * @param {Object} [opts]
8169
+ * Destroys the board and cleans up resources
8560
8170
  */
8561
- highlight(square, opts = {}) {
8562
- // API: accetta id, converte subito in oggetto
8563
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8564
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[highlight] Parametro square non valido');
8565
- if (this.boardService && this.boardService.highlightSquare) {
8566
- this.boardService.highlightSquare(squareObj, opts);
8567
- } else if (this.eventService && this.eventService.highlightSquare) {
8568
- this.eventService.highlightSquare(squareObj, opts);
8569
- }
8570
- }
8571
- /**
8572
- * Remove highlight from a square
8573
- * @param {string|Square} square
8574
- * @param {Object} [opts]
8575
- */
8576
- dehighlight(square, opts = {}) {
8577
- // API: accetta id, converte subito in oggetto
8578
- const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8579
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[dehighlight] Parametro square non valido');
8580
- if (this.boardService && this.boardService.dehighlightSquare) {
8581
- this.boardService.dehighlightSquare(squareObj, opts);
8582
- } else if (this.eventService && this.eventService.dehighlightSquare) {
8583
- this.eventService.dehighlightSquare(squareObj, opts);
8171
+ destroy() {
8172
+ this.eventService.destroy();
8173
+ this.boardService.destroy();
8174
+ this.positionService.destroy();
8175
+ this.pieceService.destroy();
8176
+ this.moveService.destroy();
8177
+ this.animationService.destroy();
8178
+ this.validationService.destroy();
8179
+
8180
+ if (this._updateTimeout) {
8181
+ clearTimeout(this._updateTimeout);
8182
+ this._updateTimeout = null;
8584
8183
  }
8585
8184
  }
8586
8185
 
8587
- // --- GAME INFO ---
8588
- /**
8589
- * Get FEN string
8590
- * @returns {string}
8591
- */
8592
- fen() {
8593
- // Avoid recursion: call the underlying game object's fen()
8594
- const game = this.positionService.getGame();
8595
- if (!game || typeof game.fen !== 'function') return '';
8596
- return game.fen();
8597
- }
8598
8186
  /**
8599
- * Get current turn
8600
- * @returns {'w'|'b'}
8187
+ * Resizes the board
8188
+ * @param {number|string} size - New size
8601
8189
  */
8602
- turn() { return this.positionService.getGame().turn(); }
8603
- /**
8604
- * Is the game over?
8605
- * @returns {boolean}
8606
- */
8607
- isGameOver() {
8608
- // Forza sync prima di interrogare il motore
8609
- this._updateBoardPieces(false, false);
8610
- const game = this.positionService.getGame();
8611
- if (!game) return false;
8612
- if (game.isGameOver) return game.isGameOver();
8613
- // Fallback: checkmate or draw
8614
- if (game.isCheckmate && game.isCheckmate()) return true;
8615
- if (game.isDraw && game.isDraw()) return true;
8616
- return false;
8617
- }
8618
- /**
8619
- * Is it checkmate?
8620
- * @returns {boolean}
8621
- */
8622
- isCheckmate() {
8623
- const game = this.positionService.getGame();
8624
- if (!game) return false;
8625
- return game.isCheckmate ? game.isCheckmate() : false;
8626
- }
8627
- /**
8628
- * Is it draw?
8629
- * @returns {boolean}
8630
- */
8631
- isDraw() {
8632
- const game = this.positionService.getGame();
8633
- if (!game) return false;
8634
- return game.isDraw ? game.isDraw() : false;
8635
- }
8636
- /**
8637
- * Get move history
8638
- * @returns {Array}
8639
- */
8640
- getHistory() {
8641
- const game = this.positionService.getGame();
8642
- if (!game) return [];
8643
- return game.history ? game.history() : [];
8190
+ resize(size) {
8191
+ this.boardService.resize(size);
8192
+ this._updateBoardPieces();
8644
8193
  }
8645
8194
 
8646
- // --- LIFECYCLE ---
8647
8195
  /**
8648
- * Destroy the board and cleanup
8649
- */
8650
- destroy() { /* TODO: robust destroy logic */ }
8651
- /**
8652
- * Rebuild the board
8196
+ * Flips the board orientation
8653
8197
  */
8654
- rebuild() { this._initialize(); }
8198
+ flip() {
8199
+ this.coordinateService.flipOrientation();
8655
8200
 
8656
- // --- CONFIGURATION ---
8657
- /**
8658
- * Get current config
8659
- * @returns {Object}
8660
- */
8661
- getConfig() { return this.config; }
8662
- /**
8663
- * Set new config
8664
- * @param {Object} newConfig
8665
- */
8666
- setConfig(newConfig) { this.setConfig(newConfig); }
8201
+ // Save current position before destroying
8202
+ let currentPosition = null;
8203
+ try {
8204
+ // Check if there are any pieces on the board
8205
+ const position = this.positionService.getPosition();
8206
+ const hasPieces = Object.keys(position).length > 0;
8667
8207
 
8668
- // --- ALIASES/DEPRECATED ---
8669
- // Note: These methods are now implemented as aliases at the end of the class
8208
+ if (hasPieces) {
8209
+ currentPosition = this.positionService.getGame().fen();
8210
+ }
8211
+ } catch (error) {
8212
+ console.log('No valid position to save during flip');
8213
+ }
8670
8214
 
8671
- /**
8672
- * Alias for flipBoard (for backward compatibility)
8673
- */
8674
- flip(opts = {}) {
8675
- this._updateBoardPieces(opts.animate !== false);
8676
- return this.flipBoard(opts);
8215
+ this.destroy();
8216
+ this._initializeServices(); // Recreate all services
8217
+ this._initParams();
8218
+
8219
+ // Restore position after rebuilding if we had one
8220
+ if (currentPosition) {
8221
+ this._setGame(currentPosition);
8222
+ }
8223
+ this._buildBoard();
8224
+ this._buildSquares();
8225
+ this._addListeners();
8226
+ this._updateBoardPieces(true, true);
8677
8227
  }
8678
8228
 
8679
8229
  /**
@@ -8696,19 +8246,51 @@ let Chessboard$1 = class Chessboard {
8696
8246
  this.load(position, {}, animate); // load() already handles isPositionLoad=true
8697
8247
  }
8698
8248
 
8249
+ /**
8250
+ * Makes a move on the board
8251
+ * @param {string|Object} move - Move to make
8252
+ * @param {boolean} [animate=true] - Whether to animate
8253
+ * @returns {boolean} True if move was successful
8254
+ */
8255
+ move(move, animate = true) {
8256
+ if (typeof move === 'string') {
8257
+ // Parse move string (e.g., 'e2e4')
8258
+ const moveObj = this.moveService.parseMove(move);
8259
+ if (!moveObj) return false;
8260
+
8261
+ const fromSquare = this.boardService.getSquare(moveObj.from);
8262
+ const toSquare = this.boardService.getSquare(moveObj.to);
8263
+
8264
+ if (!fromSquare || !toSquare) return false;
8265
+
8266
+ return this._onMove(fromSquare, toSquare, moveObj.promotion, animate);
8267
+ } else if (move && move.from && move.to) {
8268
+ // Handle move object
8269
+ const fromSquare = this.boardService.getSquare(move.from);
8270
+ const toSquare = this.boardService.getSquare(move.to);
8271
+
8272
+ if (!fromSquare || !toSquare) return false;
8273
+
8274
+ return this._onMove(fromSquare, toSquare, move.promotion, animate);
8275
+ }
8276
+
8277
+ return false;
8278
+ }
8279
+
8699
8280
  /**
8700
8281
  * Undoes the last move
8701
8282
  * @param {boolean} [animate=true] - Whether to animate
8702
8283
  * @returns {boolean} True if undo was successful
8703
8284
  */
8704
8285
  undo(animate = true) {
8705
- const undone = this.positionService.getGame().undo();
8706
- if (undone) {
8707
- this._undoneMoves.push(undone);
8708
- this._updateBoardPieces(animate);
8709
- return undone;
8286
+ if (this.positionService.getGame().undo) {
8287
+ const undoResult = this.positionService.getGame().undo();
8288
+ if (undoResult) {
8289
+ this._updateBoardPieces(animate, true); // Position change
8290
+ return true;
8291
+ }
8710
8292
  }
8711
- return null;
8293
+ return false;
8712
8294
  }
8713
8295
 
8714
8296
  /**
@@ -8717,13 +8299,12 @@ let Chessboard$1 = class Chessboard {
8717
8299
  * @returns {boolean} True if redo was successful
8718
8300
  */
8719
8301
  redo(animate = true) {
8720
- if (this._undoneMoves && this._undoneMoves.length > 0) {
8721
- const move = this._undoneMoves.pop();
8722
- const moveObj = { from: move.from, to: move.to };
8723
- if (move.promotion) moveObj.promotion = move.promotion;
8724
- const result = this.positionService.getGame().move(moveObj);
8725
- this._updateBoardPieces(animate);
8726
- return result;
8302
+ if (this.positionService.getGame().redo) {
8303
+ const redoResult = this.positionService.getGame().redo();
8304
+ if (redoResult) {
8305
+ this._updateBoardPieces(animate, true); // Position change
8306
+ return true;
8307
+ }
8727
8308
  }
8728
8309
  return false;
8729
8310
  }
@@ -8885,6 +8466,112 @@ let Chessboard$1 = class Chessboard {
8885
8466
  }
8886
8467
  }
8887
8468
 
8469
+ /**
8470
+ * Clears the board
8471
+ * @param {boolean} [animate=true] - Whether to animate
8472
+ */
8473
+ clear(animate = true) {
8474
+ // Check if services are available
8475
+ if (!this.positionService || !this.positionService.getGame()) {
8476
+ console.log('Cannot clear - position service not available');
8477
+ return;
8478
+ }
8479
+
8480
+ // Clear visual state first
8481
+ this._clearVisualState();
8482
+
8483
+ // Clear game state
8484
+ this.positionService.getGame().clear();
8485
+
8486
+ // Update board visually
8487
+ this._updateBoardPieces(animate, true); // Position change
8488
+ }
8489
+
8490
+ /**
8491
+ * Resets the board to starting position
8492
+ * @param {boolean} [animate=true] - Whether to animate
8493
+ */
8494
+ reset(animate = true) {
8495
+ // Check if services are available
8496
+ if (!this.positionService || !this.positionService.getGame()) {
8497
+ console.log('Cannot reset - position service not available');
8498
+ return;
8499
+ }
8500
+
8501
+ this.positionService.getGame().reset();
8502
+ this._updateBoardPieces(animate, true); // Position change
8503
+ }
8504
+
8505
+ /**
8506
+ * Puts a piece on a square
8507
+ * @param {string} square - Square to put piece on
8508
+ * @param {string} piece - Piece to put
8509
+ * @param {boolean} [animate=true] - Whether to animate
8510
+ * @returns {boolean} True if successful
8511
+ */
8512
+ put(square, piece, animate = true) {
8513
+ console.log(`put() called with square: ${square}, piece: ${piece}`);
8514
+
8515
+ if (!this.validationService.isValidSquare(square) || !this.validationService.isValidPiece(piece)) {
8516
+ console.log('Validation failed');
8517
+ return false;
8518
+ }
8519
+
8520
+ // Check if services are available
8521
+ if (!this.positionService || !this.positionService.getGame()) {
8522
+ console.log('Cannot put piece - position service not available');
8523
+ return false;
8524
+ }
8525
+
8526
+ const pieceObj = this.pieceService.convertPiece(piece);
8527
+ console.log(`Converted piece:`, pieceObj);
8528
+ console.log(`Piece type: ${pieceObj.type}, color: ${pieceObj.color}`);
8529
+
8530
+ const squareObj = this.boardService.getSquare(square);
8531
+
8532
+ if (!squareObj) {
8533
+ console.log('Square not found');
8534
+ return false;
8535
+ }
8536
+
8537
+ // Update game state - note: chess.js expects (piece, square) order
8538
+ const chessJsPiece = { type: pieceObj.type.toLowerCase(), color: pieceObj.color.toLowerCase() };
8539
+ console.log(`Chess.js piece:`, chessJsPiece);
8540
+
8541
+ const result = this.positionService.getGame().put(chessJsPiece, square);
8542
+ console.log(`Chess.js put result:`, result);
8543
+
8544
+ if (result) {
8545
+ // Update visual representation
8546
+ this._updateBoardPieces(animate, true); // Position change
8547
+ }
8548
+
8549
+ return result;
8550
+ }
8551
+
8552
+ /**
8553
+ * Removes a piece from a square
8554
+ * @param {string} square - Square to remove piece from
8555
+ * @param {boolean} [animate=true] - Whether to animate
8556
+ * @returns {boolean} True if successful
8557
+ */
8558
+ remove(square, animate = true) {
8559
+ if (!this.validationService.isValidSquare(square)) {
8560
+ return false;
8561
+ }
8562
+
8563
+ const squareObj = this.boardService.getSquare(square);
8564
+ if (!squareObj) return false;
8565
+
8566
+ // Update game state
8567
+ this.positionService.getGame().remove(square);
8568
+
8569
+ // Update visual representation
8570
+ this._updateBoardPieces(animate, true); // Position change
8571
+
8572
+ return true;
8573
+ }
8574
+
8888
8575
  /**
8889
8576
  * Gets configuration options
8890
8577
  * @returns {Object} Configuration object
@@ -8946,97 +8633,7 @@ let Chessboard$1 = class Chessboard {
8946
8633
 
8947
8634
  // Additional API methods would be added here following the same pattern
8948
8635
  // This is a good starting point for the refactored architecture
8949
-
8950
- // Additional API methods and aliases for backward compatibility
8951
- insert(square, piece) { return this.putPiece(piece, square); }
8952
- build() { return this._initialize(); }
8953
- ascii() { return this.positionService.getGame().ascii(); }
8954
- board() { return this.positionService.getGame().board(); }
8955
- getCastlingRights(color) { return this.positionService.getGame().getCastlingRights(color); }
8956
- getComment() { return this.positionService.getGame().getComment(); }
8957
- getComments() { return this.positionService.getGame().getComments(); }
8958
- lastMove() { return this.positionService.getGame().lastMove(); }
8959
- moveNumber() { return this.positionService.getGame().moveNumber(); }
8960
- moves(options = {}) { return this.positionService.getGame().moves(options); }
8961
- squareColor(squareId) { return this.boardService.getSquare(squareId).isWhite() ? 'light' : 'dark'; }
8962
- isDrawByFiftyMoves() { return this.positionService.getGame().isDrawByFiftyMoves(); }
8963
- isInsufficientMaterial() { return this.positionService.getGame().isInsufficientMaterial(); }
8964
- removeComment() { return this.positionService.getGame().removeComment(); }
8965
- removeComments() { return this.positionService.getGame().removeComments(); }
8966
- removeHeader(field) { return this.positionService.getGame().removeHeader(field); }
8967
- setCastlingRights(color, rights) { return this.positionService.getGame().setCastlingRights(color, rights); }
8968
- setComment(comment) { return this.positionService.getGame().setComment(comment); }
8969
- setHeader(key, value) { return this.positionService.getGame().setHeader(key, value); }
8970
- validateFen(fen) { return this.positionService.getGame().validateFen(fen); }
8971
-
8972
- // Implementazioni reali per highlight/dehighlight
8973
- highlightSquare(square) {
8974
- return this.boardService.highlight(square);
8975
- }
8976
- dehighlightSquare(square) {
8977
- return this.boardService.dehighlight(square);
8978
- }
8979
- forceSync() { this._updateBoardPieces(true, true); this._updateBoardPieces(true, false); }
8980
-
8981
- // Metodi mancanti che causano fallimenti nei test
8982
- /**
8983
- * Move a piece from one square to another
8984
- * @param {string|Object} move - Move in format 'e2e4' or {from: 'e2', to: 'e4'}
8985
- * @param {Object} [opts] - Options
8986
- * @param {boolean} [opts.animate=true] - Whether to animate
8987
- * @returns {boolean} True if move was successful
8988
- */
8989
- movePiece(move, opts = {}) {
8990
- const animate = opts.animate !== undefined ? opts.animate : true;
8991
-
8992
- // --- API: accetta id/stringhe, ma converte subito in oggetti ---
8993
- let fromSquareObj, toSquareObj, promotion;
8994
- if (typeof move === 'string') {
8995
- if (move.length === 4) {
8996
- fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
8997
- toSquareObj = this.boardService.getSquare(move.substring(2, 4));
8998
- } else if (move.length === 5) {
8999
- fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
9000
- toSquareObj = this.boardService.getSquare(move.substring(2, 4));
9001
- promotion = move.substring(4, 5);
9002
- } else {
9003
- throw new Error(`Invalid move format: ${move}`);
9004
- }
9005
- } else if (typeof move === 'object' && move.from && move.to) {
9006
- // Se sono id, converto in oggetti; se sono già oggetti, li uso direttamente
9007
- fromSquareObj = typeof move.from === 'string' ? this.boardService.getSquare(move.from) : move.from;
9008
- toSquareObj = typeof move.to === 'string' ? this.boardService.getSquare(move.to) : move.to;
9009
- promotion = move.promotion;
9010
- } else {
9011
- throw new Error(`Invalid move: ${move}`);
9012
- }
9013
-
9014
- if (!fromSquareObj || !toSquareObj) {
9015
- throw new Error(`Invalid squares: ${move.from || move.substring(0, 2)} or ${move.to || move.substring(2, 4)}`);
9016
- }
9017
-
9018
- // --- Internamente: lavora solo con oggetti ---
9019
- const result = this._onMove(fromSquareObj, toSquareObj, promotion, animate);
9020
- // Dopo ogni mossa, forza la sincronizzazione della board
9021
- this._updateBoardPieces(true, false);
9022
- return result;
9023
- }
9024
-
9025
- // Aliases for backward compatibility
9026
- move(move, animate = true) {
9027
- // On any new move, clear the redo stack
9028
- this._undoneMoves = [];
9029
- return this.movePiece(move, { animate });
9030
- }
9031
- get(square) { return this.getPiece(square); }
9032
- piece(square) { return this.getPiece(square); }
9033
- put(piece, square, opts = {}) { return this.putPiece(piece, square, opts); }
9034
- remove(square, opts = {}) { return this.removePiece(square, opts); }
9035
- load(position, opts = {}) { return this.setPosition(position, opts); }
9036
- resize(size) { return this.resizeBoard(size); }
9037
- start(opts = {}) { return this.reset(opts); }
9038
- clearBoard(opts = {}) { return this.clear(opts); }
9039
- };
8636
+ }
9040
8637
 
9041
8638
  /**
9042
8639
  * Structured logging system for Chessboard.js
@@ -9583,7 +9180,7 @@ class ChessboardFactory {
9583
9180
  this.validationService.validateConfig(finalConfig);
9584
9181
 
9585
9182
  // Create chessboard instance
9586
- const chessboard = new Chessboard$1(finalConfig);
9183
+ const chessboard = new Chessboard(finalConfig);
9587
9184
 
9588
9185
  // Store instance for management
9589
9186
  this.instances.set(containerId, {
@@ -9843,44 +9440,13 @@ function createChessboardFromTemplate(containerId, templateName, overrides = {})
9843
9440
  */
9844
9441
 
9845
9442
 
9846
- /**
9847
- * Main Chessboard factory function for backward compatibility
9848
- * Supports both legacy and modern calling conventions
9849
- * @param {string|Object} containerElm - Container element ID or configuration object
9850
- * @param {Object} [config={}] - Configuration options (when first param is string)
9851
- * @returns {ChessboardClass} Chessboard instance
9852
- */
9853
- function Chessboard(containerElm, config = {}) {
9854
- const factoryLogger = logger.child('ChessboardFactory');
9855
-
9856
- try {
9857
- // If first parameter is an object, treat it as config
9858
- if (typeof containerElm === 'object' && containerElm !== null) {
9859
- factoryLogger.debug('Creating chessboard with config object');
9860
- return new Chessboard$1(containerElm);
9861
- }
9862
-
9863
- // Otherwise, treat first parameter as element ID
9864
- if (typeof containerElm === 'string') {
9865
- factoryLogger.debug('Creating chessboard with element ID', { elementId: containerElm });
9866
- const fullConfig = { ...config, id: containerElm };
9867
- return new Chessboard$1(fullConfig);
9868
- }
9869
-
9870
- throw new Error('Invalid parameters: first parameter must be string or object');
9871
- } catch (error) {
9872
- factoryLogger.error('Failed to create chessboard instance', { error });
9873
- throw error;
9874
- }
9875
- }
9876
-
9877
9443
  /**
9878
9444
  * Wrapper class that handles both calling conventions
9879
9445
  * Provides enhanced error handling and logging
9880
9446
  * @class
9881
9447
  * @extends ChessboardClass
9882
9448
  */
9883
- class ChessboardWrapper extends Chessboard$1 {
9449
+ class ChessboardWrapper extends Chessboard {
9884
9450
  /**
9885
9451
  * Creates a new ChessboardWrapper instance
9886
9452
  * @param {string|Object} containerElm - Container element ID or configuration object
@@ -9909,53 +9475,32 @@ class ChessboardWrapper extends Chessboard$1 {
9909
9475
  }
9910
9476
  }
9911
9477
 
9912
- /**
9913
- * Refactored Chessboard API - see Chessboard.js for full method docs
9914
- * @typedef {import('./Chessboard.js').Chessboard} Chessboard
9915
- */
9478
+ // Attach classes and utilities to the factory function for direct access
9479
+ Chessboard.Class = ChessboardWrapper;
9480
+ Chessboard.Chessboard = ChessboardWrapper;
9481
+ Chessboard.Config = ChessboardConfig;
9482
+ Chessboard.Factory = ChessboardFactory;
9916
9483
 
9917
- // --- STATIC/FACTORY METHODS ---
9918
- /**
9919
- * Create a new Chessboard instance
9920
- * @param {string|Object} containerElm
9921
- * @param {Object} [config]
9922
- * @returns {Chessboard}
9923
- */
9484
+ // Attach factory methods
9924
9485
  Chessboard.create = createChessboard;
9925
- /**
9926
- * Create a Chessboard from a template
9927
- * @param {string|Object} containerElm
9928
- * @param {string} templateName
9929
- * @param {Object} [config]
9930
- * @returns {Chessboard}
9931
- */
9932
- Chessboard.fromTemplate = createChessboardFromTemplate;
9486
+ Chessboard.createFromTemplate = createChessboardFromTemplate;
9933
9487
  Chessboard.factory = chessboardFactory;
9934
9488
 
9935
- // --- INSTANCE MANAGEMENT ---
9489
+ // Static methods for instance management
9936
9490
  Chessboard.getInstance = (containerId) => chessboardFactory.getInstance(containerId);
9937
9491
  Chessboard.destroyInstance = (containerId) => chessboardFactory.destroy(containerId);
9938
9492
  Chessboard.destroyAll = () => chessboardFactory.destroyAll();
9939
9493
  Chessboard.listInstances = () => chessboardFactory.listInstances();
9940
9494
 
9941
- // --- TEMPLATE MANAGEMENT ---
9495
+ // Template management
9942
9496
  Chessboard.registerTemplate = (name, config) => chessboardFactory.registerTemplate(name, config);
9943
9497
  Chessboard.removeTemplate = (name) => chessboardFactory.removeTemplate(name);
9944
9498
  Chessboard.getTemplate = (name) => chessboardFactory.getTemplate(name);
9945
9499
  Chessboard.listTemplates = () => chessboardFactory.listTemplates();
9946
9500
 
9947
- // --- STATS & DEBUG ---
9501
+ // Statistics and debugging
9948
9502
  Chessboard.getStats = () => chessboardFactory.getStats();
9949
9503
 
9950
- // --- DEPRECATED/LEGACY ALIASES ---
9951
- /**
9952
- * @deprecated Use Chessboard.create instead
9953
- */
9954
- Chessboard.Class = ChessboardWrapper;
9955
- Chessboard.Chessboard = ChessboardWrapper;
9956
- Chessboard.Config = ChessboardConfig;
9957
- Chessboard.Factory = ChessboardFactory;
9958
-
9959
9504
  /**
9960
9505
  * Coordinate utilities for Chessboard.js
9961
9506
  */