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