@alepot55/chessboardjs 2.3.6 → 2.3.8

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.
@@ -348,7 +348,7 @@ var Chessboard = (function (exports) {
348
348
  }
349
349
 
350
350
  /**
351
- * Service for input validation and data sanitization
351
+ * ValidationService - Handles input validation and data sanitization
352
352
  * @module services/ValidationService
353
353
  * @since 2.0.0
354
354
  */
@@ -397,45 +397,39 @@ var Chessboard = (function (exports) {
397
397
  /**
398
398
  * Service responsible for validating inputs and data
399
399
  * Implements caching for performance optimization
400
- * @class
400
+ * @class ValidationService
401
401
  */
402
402
  class ValidationService {
403
403
  /**
404
- * Creates a new ValidationService instance
404
+ * Create a new ValidationService instance
405
405
  */
406
406
  constructor() {
407
- // Cache for validation results to improve performance
408
407
  this._validationCache = new Map();
409
408
  this._cacheMaxSize = 1000;
410
-
411
- // Compile patterns for reuse
412
409
  this._patterns = VALIDATION_PATTERNS;
413
410
  this._validValues = VALID_VALUES;
414
411
  this._constraints = SIZE_CONSTRAINTS;
415
412
  }
416
413
 
417
414
  /**
418
- * Validates a square identifier with caching
415
+ * Validate a square identifier with caching
419
416
  * @param {string} square - Square to validate (e.g., 'e4')
420
417
  * @returns {boolean} True if valid
421
418
  */
422
419
  isValidSquare(square) {
423
420
  const cacheKey = `square:${square}`;
424
-
425
421
  if (this._validationCache.has(cacheKey)) {
426
422
  return this._validationCache.get(cacheKey);
427
423
  }
428
-
429
- const isValid = typeof square === 'string' &&
430
- square.length === 2 &&
431
- this._patterns.square.test(square);
432
-
424
+ const isValid = typeof square === 'string' &&
425
+ square.length === 2 &&
426
+ this._patterns.square.test(square);
433
427
  this._cacheValidationResult(cacheKey, isValid);
434
428
  return isValid;
435
429
  }
436
430
 
437
431
  /**
438
- * Validates a piece identifier with enhanced format support
432
+ * Validate a piece identifier with enhanced format support
439
433
  * @param {string} piece - Piece to validate (e.g., 'wK', 'bp')
440
434
  * @returns {boolean} True if valid
441
435
  */
@@ -443,28 +437,22 @@ var Chessboard = (function (exports) {
443
437
  if (typeof piece !== 'string' || piece.length !== 2) {
444
438
  return false;
445
439
  }
446
-
447
440
  const cacheKey = `piece:${piece}`;
448
-
449
441
  if (this._validationCache.has(cacheKey)) {
450
442
  return this._validationCache.get(cacheKey);
451
443
  }
452
-
453
444
  const [first, second] = piece.split('');
454
-
455
- // Check both formats: [type][color] and [color][type]
456
- const format1 = PIECE_TYPES.includes(first.toLowerCase()) &&
457
- PIECE_COLORS.includes(second);
458
- const format2 = PIECE_COLORS.includes(first) &&
459
- PIECE_TYPES.includes(second.toLowerCase());
460
-
445
+ const format1 = PIECE_TYPES.includes(first.toLowerCase()) &&
446
+ PIECE_COLORS.includes(second);
447
+ const format2 = PIECE_COLORS.includes(first) &&
448
+ PIECE_TYPES.includes(second.toLowerCase());
461
449
  const isValid = format1 || format2;
462
450
  this._cacheValidationResult(cacheKey, isValid);
463
451
  return isValid;
464
452
  }
465
453
 
466
454
  /**
467
- * Validates a FEN string with comprehensive checks
455
+ * Validate a FEN string with comprehensive checks
468
456
  * @param {string} fen - FEN string to validate
469
457
  * @returns {boolean} True if valid
470
458
  */
@@ -472,86 +460,64 @@ var Chessboard = (function (exports) {
472
460
  if (typeof fen !== 'string') {
473
461
  return false;
474
462
  }
475
-
476
463
  const cacheKey = `fen:${fen}`;
477
-
478
464
  if (this._validationCache.has(cacheKey)) {
479
465
  return this._validationCache.get(cacheKey);
480
466
  }
481
-
482
- // Basic pattern check
483
467
  if (!this._patterns.fen.test(fen)) {
484
468
  this._cacheValidationResult(cacheKey, false);
485
469
  return false;
486
470
  }
487
-
488
- // Additional FEN validation
489
471
  const isValid = this._validateFenStructure(fen);
490
472
  this._cacheValidationResult(cacheKey, isValid);
491
473
  return isValid;
492
474
  }
493
475
 
494
476
  /**
495
- * Validates FEN structure in detail
477
+ * Validate FEN structure in detail
496
478
  * @private
497
479
  * @param {string} fen - FEN string to validate
498
480
  * @returns {boolean} True if valid
499
481
  */
500
482
  _validateFenStructure(fen) {
501
483
  const parts = fen.split(' ');
502
-
503
484
  if (parts.length !== 6) {
504
485
  return false;
505
486
  }
506
-
507
- // Validate piece placement
508
487
  const ranks = parts[0].split('/');
509
488
  if (ranks.length !== 8) {
510
489
  return false;
511
490
  }
512
-
513
- // Validate each rank
514
491
  for (const rank of ranks) {
515
492
  if (!this._validateRank(rank)) {
516
493
  return false;
517
494
  }
518
495
  }
519
-
520
- // Validate active color
521
496
  if (!['w', 'b'].includes(parts[1])) {
522
497
  return false;
523
498
  }
524
-
525
- // Validate castling rights
526
499
  if (!/^[KQkq-]*$/.test(parts[2])) {
527
500
  return false;
528
501
  }
529
-
530
- // Validate en passant target
531
502
  if (parts[3] !== '-' && !this.isValidSquare(parts[3])) {
532
503
  return false;
533
504
  }
534
-
535
- // Validate halfmove clock and fullmove number
536
505
  const halfmove = parseInt(parts[4], 10);
537
506
  const fullmove = parseInt(parts[5], 10);
538
-
539
- return !isNaN(halfmove) && !isNaN(fullmove) &&
540
- halfmove >= 0 && fullmove >= 1;
507
+ return !isNaN(halfmove) && !isNaN(fullmove) &&
508
+ halfmove >= 0 && fullmove >= 1;
541
509
  }
542
510
 
543
511
  /**
544
- * Validates a single rank in FEN notation
512
+ * Validate a single rank in FEN notation
545
513
  * @private
546
514
  * @param {string} rank - Rank to validate
547
515
  * @returns {boolean} True if valid
548
516
  */
549
517
  _validateRank(rank) {
550
518
  let squares = 0;
551
-
552
519
  for (let i = 0; i < rank.length; i++) {
553
520
  const char = rank[i];
554
-
555
521
  if (/[1-8]/.test(char)) {
556
522
  squares += parseInt(char, 10);
557
523
  } else if (/[prnbqkPRNBQK]/.test(char)) {
@@ -560,12 +526,11 @@ var Chessboard = (function (exports) {
560
526
  return false;
561
527
  }
562
528
  }
563
-
564
529
  return squares === 8;
565
530
  }
566
531
 
567
532
  /**
568
- * Validates a move string with comprehensive format support
533
+ * Validate a move string with comprehensive format support
569
534
  * @param {string} move - Move string to validate (e.g., 'e2e4', 'e7e8q')
570
535
  * @returns {boolean} True if valid
571
536
  */
@@ -573,20 +538,17 @@ var Chessboard = (function (exports) {
573
538
  if (typeof move !== 'string') {
574
539
  return false;
575
540
  }
576
-
577
541
  const cacheKey = `move:${move}`;
578
-
579
542
  if (this._validationCache.has(cacheKey)) {
580
543
  return this._validationCache.get(cacheKey);
581
544
  }
582
-
583
545
  const isValid = this._validateMoveFormat(move);
584
546
  this._cacheValidationResult(cacheKey, isValid);
585
547
  return isValid;
586
548
  }
587
549
 
588
550
  /**
589
- * Validates move format in detail
551
+ * Validate move format in detail
590
552
  * @private
591
553
  * @param {string} move - Move string to validate
592
554
  * @returns {boolean} True if valid
@@ -595,21 +557,15 @@ var Chessboard = (function (exports) {
595
557
  if (move.length < 4 || move.length > 5) {
596
558
  return false;
597
559
  }
598
-
599
- const from = move.slice(0, 2);
600
- const to = move.slice(2, 4);
601
- const promotion = move.slice(4, 5);
602
-
560
+ const from = move.substring(0, 2);
561
+ const to = move.substring(2, 4);
603
562
  if (!this.isValidSquare(from) || !this.isValidSquare(to)) {
604
563
  return false;
605
564
  }
606
-
607
- if (promotion && !this._validValues.promotionPieces.includes(promotion)) {
565
+ if (move.length === 5 && !/[qrnb]/i.test(move[4])) {
608
566
  return false;
609
567
  }
610
-
611
- // Additional move validation (e.g., source and target different)
612
- return from !== to;
568
+ return true;
613
569
  }
614
570
 
615
571
  /**
@@ -639,11 +595,9 @@ var Chessboard = (function (exports) {
639
595
  if (size === 'auto') {
640
596
  return true;
641
597
  }
642
-
643
598
  if (typeof size === 'number') {
644
599
  return size >= this._constraints.min && size <= this._constraints.max;
645
600
  }
646
-
647
601
  return false;
648
602
  }
649
603
 
@@ -653,9 +607,9 @@ var Chessboard = (function (exports) {
653
607
  * @returns {boolean} True if valid
654
608
  */
655
609
  isValidTime(time) {
656
- return typeof time === 'number' &&
657
- time >= 0 &&
658
- time <= this._constraints.maxTime;
610
+ return typeof time === 'number' &&
611
+ time >= 0 &&
612
+ time <= this._constraints.maxTime;
659
613
  }
660
614
 
661
615
  /**
@@ -673,10 +627,10 @@ var Chessboard = (function (exports) {
673
627
  * @returns {boolean} True if valid
674
628
  */
675
629
  isValidElementId(id) {
676
- return typeof id === 'string' &&
677
- id.length > 0 &&
678
- id.length <= 100 && // Reasonable length limit
679
- /^[a-zA-Z][\w:-]*$/.test(id); // Valid HTML ID format
630
+ return typeof id === 'string' &&
631
+ id.length > 0 &&
632
+ id.length <= 100 && // Reasonable length limit
633
+ /^[a-zA-Z][\w:-]*$/.test(id); // Valid HTML ID format
680
634
  }
681
635
 
682
636
  /**
@@ -716,24 +670,20 @@ var Chessboard = (function (exports) {
716
670
  if (typeof color !== 'string') {
717
671
  return false;
718
672
  }
719
-
720
673
  // Check for hex colors
721
674
  if (this._patterns.color.test(color)) {
722
675
  return true;
723
676
  }
724
-
725
677
  // Check for named colors (basic set)
726
678
  const namedColors = ['white', 'black', 'red', 'green', 'blue', 'yellow', 'cyan', 'magenta'];
727
679
  if (namedColors.includes(color.toLowerCase())) {
728
680
  return true;
729
681
  }
730
-
731
682
  // Check for rgb/rgba format
732
683
  if (/^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/.test(color) ||
733
684
  /^rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[0-1](\.\d+)?\s*\)$/.test(color)) {
734
685
  return true;
735
686
  }
736
-
737
687
  return false;
738
688
  }
739
689
 
@@ -746,8 +696,8 @@ var Chessboard = (function (exports) {
746
696
  validateSquare(square) {
747
697
  if (!this.isValidSquare(square)) {
748
698
  throw new ValidationError(
749
- ERROR_MESSAGES.invalid_square + square,
750
- 'square',
699
+ ERROR_MESSAGES.invalid_square + square,
700
+ 'square',
751
701
  square
752
702
  );
753
703
  }
@@ -763,8 +713,8 @@ var Chessboard = (function (exports) {
763
713
  validatePiece(piece) {
764
714
  if (!this.isValidPiece(piece)) {
765
715
  throw new ValidationError(
766
- ERROR_MESSAGES.invalid_piece + piece,
767
- 'piece',
716
+ ERROR_MESSAGES.invalid_piece + piece,
717
+ 'piece',
768
718
  piece
769
719
  );
770
720
  }
@@ -780,8 +730,8 @@ var Chessboard = (function (exports) {
780
730
  validateFen(fen) {
781
731
  if (!this.isValidFen(fen)) {
782
732
  throw new ValidationError(
783
- ERROR_MESSAGES.invalid_fen + fen,
784
- 'fen',
733
+ ERROR_MESSAGES.invalid_fen + fen,
734
+ 'fen',
785
735
  fen
786
736
  );
787
737
  }
@@ -797,8 +747,8 @@ var Chessboard = (function (exports) {
797
747
  validateMove(move) {
798
748
  if (!this.isValidMove(move)) {
799
749
  throw new ValidationError(
800
- ERROR_MESSAGES.invalid_move + move,
801
- 'move',
750
+ ERROR_MESSAGES.invalid_move + move,
751
+ 'move',
802
752
  move
803
753
  );
804
754
  }
@@ -814,16 +764,15 @@ var Chessboard = (function (exports) {
814
764
  validateOrientation(orientation) {
815
765
  if (!this.isValidOrientation(orientation)) {
816
766
  throw new ValidationError(
817
- ERROR_MESSAGES.invalid_orientation + orientation,
818
- 'orientation',
767
+ ERROR_MESSAGES.invalid_orientation + orientation,
768
+ 'orientation',
819
769
  orientation
820
770
  );
821
771
  }
822
-
823
772
  // Normalize to 'w' or 'b'
824
- return orientation === 'white' ? 'w' :
825
- orientation === 'black' ? 'b' :
826
- orientation;
773
+ return orientation === 'white' ? 'w' :
774
+ orientation === 'black' ? 'b' :
775
+ orientation;
827
776
  }
828
777
 
829
778
  /**
@@ -835,16 +784,15 @@ var Chessboard = (function (exports) {
835
784
  validateColor(color) {
836
785
  if (!this.isValidColor(color)) {
837
786
  throw new ValidationError(
838
- ERROR_MESSAGES.invalid_color + color,
839
- 'color',
787
+ ERROR_MESSAGES.invalid_color + color,
788
+ 'color',
840
789
  color
841
790
  );
842
791
  }
843
-
844
792
  // Normalize to 'w' or 'b'
845
- return color === 'white' ? 'w' :
846
- color === 'black' ? 'b' :
847
- color;
793
+ return color === 'white' ? 'w' :
794
+ color === 'black' ? 'b' :
795
+ color;
848
796
  }
849
797
 
850
798
  /**
@@ -856,8 +804,8 @@ var Chessboard = (function (exports) {
856
804
  validateSize(size) {
857
805
  if (!this.isValidSize(size)) {
858
806
  throw new ValidationError(
859
- ERROR_MESSAGES.invalid_size + size,
860
- 'size',
807
+ ERROR_MESSAGES.invalid_size + size,
808
+ 'size',
861
809
  size
862
810
  );
863
811
  }
@@ -873,36 +821,29 @@ var Chessboard = (function (exports) {
873
821
  validateConfig(config) {
874
822
  if (!config || typeof config !== 'object') {
875
823
  throw new ValidationError(
876
- 'Configuration must be an object',
877
- 'config',
824
+ 'Configuration must be an object',
825
+ 'config',
878
826
  config
879
827
  );
880
828
  }
881
-
882
829
  const errors = [];
883
-
884
830
  // Validate required fields
885
831
  if (!config.id && !config.id_div) {
886
832
  errors.push('Configuration must include id or id_div');
887
833
  }
888
-
889
834
  // Validate optional fields
890
835
  if (config.orientation && !this.isValidOrientation(config.orientation)) {
891
836
  errors.push(ERROR_MESSAGES.invalid_orientation + config.orientation);
892
837
  }
893
-
894
838
  if (config.size && !this.isValidSize(config.size)) {
895
839
  errors.push(ERROR_MESSAGES.invalid_size + config.size);
896
840
  }
897
-
898
841
  if (config.movableColors && !this.isValidMovableColors(config.movableColors)) {
899
842
  errors.push(ERROR_MESSAGES.invalid_color + config.movableColors);
900
843
  }
901
-
902
844
  if (config.dropOffBoard && !this.isValidDropOffBoard(config.dropOffBoard)) {
903
845
  errors.push(ERROR_MESSAGES.invalid_dropOffBoard + config.dropOffBoard);
904
846
  }
905
-
906
847
  // Validate callbacks
907
848
  const callbacks = ['onMove', 'onMoveEnd', 'onChange', 'onDragStart', 'onDragMove', 'onDrop', 'onSnapbackEnd'];
908
849
  for (const callback of callbacks) {
@@ -910,7 +851,6 @@ var Chessboard = (function (exports) {
910
851
  errors.push(`Invalid ${callback} callback`);
911
852
  }
912
853
  }
913
-
914
854
  // Validate colors
915
855
  const colorFields = ['whiteSquare', 'blackSquare', 'highlight', 'hintColor'];
916
856
  for (const field of colorFields) {
@@ -918,15 +858,13 @@ var Chessboard = (function (exports) {
918
858
  errors.push(`Invalid ${field} color: ${config[field]}`);
919
859
  }
920
860
  }
921
-
922
861
  if (errors.length > 0) {
923
862
  throw new ValidationError(
924
- `Configuration validation failed: ${errors.join(', ')}`,
925
- 'config',
863
+ `Configuration validation failed: ${errors.join(', ')}`,
864
+ 'config',
926
865
  config
927
866
  );
928
867
  }
929
-
930
868
  return config;
931
869
  }
932
870
 
@@ -942,7 +880,6 @@ var Chessboard = (function (exports) {
942
880
  const firstKey = this._validationCache.keys().next().value;
943
881
  this._validationCache.delete(firstKey);
944
882
  }
945
-
946
883
  this._validationCache.set(key, result);
947
884
  }
948
885
 
@@ -974,7 +911,6 @@ var Chessboard = (function (exports) {
974
911
  return validations.map(validation => {
975
912
  try {
976
913
  const { type, value } = validation;
977
-
978
914
  switch (type) {
979
915
  case 'square':
980
916
  return { valid: this.isValidSquare(value), value };
@@ -1989,180 +1925,155 @@ var Chessboard = (function (exports) {
1989
1925
  };
1990
1926
 
1991
1927
  /**
1992
- * Service for managing animations and transitions
1928
+ * AnimationService - Gestione centralizzata delle animazioni e transizioni per la scacchiera
1993
1929
  * @module services/AnimationService
1994
1930
  * @since 2.0.0
1995
1931
  */
1996
1932
 
1997
1933
  /**
1998
- * Service responsible for coordinating animations and transitions
1999
- * @class
1934
+ * Service responsabile del coordinamento delle animazioni e delle transizioni
1935
+ * @class AnimationService
2000
1936
  */
2001
1937
  class AnimationService {
2002
1938
  /**
2003
- * Creates a new AnimationService instance
2004
- * @param {ChessboardConfig} config - Board configuration
1939
+ * Crea una nuova istanza di AnimationService
1940
+ * @param {Object} config - Configurazione della scacchiera
2005
1941
  */
2006
1942
  constructor(config) {
1943
+ /** @type {Object} */
2007
1944
  this.config = config;
1945
+ /** @type {Map<number, {element: HTMLElement, animate: Function, callback?: Function}>} */
2008
1946
  this.activeAnimations = new Map();
1947
+ /** @type {number} */
2009
1948
  this.animationId = 0;
2010
1949
  }
2011
1950
 
2012
1951
  /**
2013
- * Creates a timing function for animations
2014
- * @param {string} [type='ease'] - Animation type
2015
- * @returns {Function} Timing function
1952
+ * Crea una funzione di timing per le animazioni
1953
+ * @param {string} [type='ease'] - Tipo di easing
1954
+ * @returns {Function} Funzione di timing
2016
1955
  */
2017
1956
  createTimingFunction(type = 'ease') {
2018
1957
  return (elapsed, duration) => {
2019
- const x = elapsed / duration;
2020
-
1958
+ const x = duration > 0 ? Math.max(0, Math.min(1, elapsed / duration)) : 1;
2021
1959
  switch (type) {
2022
- case 'linear':
2023
- return x;
2024
- case 'ease':
2025
- return (x ** 2) * (3 - 2 * x);
2026
- case 'ease-in':
2027
- return x ** 2;
2028
- case 'ease-out':
2029
- return -1 * (x - 1) ** 2 + 1;
2030
- case 'ease-in-out':
2031
- return (x < 0.5) ? 2 * x ** 2 : 4 * x - 2 * x ** 2 - 1;
2032
- default:
2033
- return x;
1960
+ case 'linear': return x;
1961
+ case 'ease': return (x ** 2) * (3 - 2 * x);
1962
+ case 'ease-in': return x ** 2;
1963
+ case 'ease-out': return -1 * (x - 1) ** 2 + 1;
1964
+ case 'ease-in-out': return (x < 0.5) ? 2 * x ** 2 : 4 * x - 2 * x ** 2 - 1;
1965
+ default: return x;
2034
1966
  }
2035
1967
  };
2036
1968
  }
2037
1969
 
2038
1970
  /**
2039
- * Animates an element with given properties
2040
- * @param {HTMLElement} element - Element to animate
2041
- * @param {Object} properties - Properties to animate
2042
- * @param {number} duration - Animation duration in milliseconds
2043
- * @param {string} [easing='ease'] - Easing function
2044
- * @param {Function} [callback] - Callback when animation completes
2045
- * @returns {number} Animation ID
1971
+ * Anima un elemento HTML con le proprietà specificate
1972
+ * @param {HTMLElement} element - Elemento da animare
1973
+ * @param {Object} properties - Proprietà da animare (es: {left: 100, top: 50, opacity: 1})
1974
+ * @param {number} duration - Durata in ms
1975
+ * @param {string} [easing='ease'] - Tipo di easing
1976
+ * @param {Function} [callback] - Callback al termine
1977
+ * @returns {number} ID dell'animazione
2046
1978
  */
2047
1979
  animate(element, properties, duration, easing = 'ease', callback) {
1980
+ if (!element || typeof properties !== 'object' || typeof duration !== 'number') {
1981
+ throw new Error('AnimationService.animate: parametri non validi');
1982
+ }
2048
1983
  const animationId = ++this.animationId;
2049
1984
  const timingFunction = this.createTimingFunction(easing);
2050
1985
  const startTime = performance.now();
2051
1986
  const startValues = {};
2052
-
2053
- // Store initial values
2054
1987
  Object.keys(properties).forEach(prop => {
2055
1988
  startValues[prop] = this._getInitialValue(element, prop);
2056
1989
  });
2057
-
2058
1990
  const animate = (currentTime) => {
2059
1991
  const elapsed = currentTime - startTime;
2060
1992
  const progress = Math.min(elapsed / duration, 1);
2061
1993
  const easedProgress = timingFunction(elapsed, duration);
2062
-
2063
- // Apply animated values
2064
1994
  Object.keys(properties).forEach(prop => {
2065
1995
  const startValue = startValues[prop];
2066
1996
  const endValue = properties[prop];
2067
1997
  const currentValue = this._interpolateValue(startValue, endValue, easedProgress);
2068
1998
  this._applyValue(element, prop, currentValue);
2069
1999
  });
2070
-
2071
2000
  if (progress < 1 && this.activeAnimations.has(animationId)) {
2072
2001
  requestAnimationFrame(animate);
2073
2002
  } else {
2074
2003
  this.activeAnimations.delete(animationId);
2075
- if (callback) callback();
2004
+ if (typeof callback === 'function') callback();
2076
2005
  }
2077
2006
  };
2078
-
2079
2007
  this.activeAnimations.set(animationId, { element, animate, callback });
2080
2008
  requestAnimationFrame(animate);
2081
-
2082
2009
  return animationId;
2083
2010
  }
2084
2011
 
2085
2012
  /**
2086
- * Cancels an animation
2087
- * @param {number} animationId - Animation ID to cancel
2013
+ * Annulla una animazione attiva
2014
+ * @param {number} animationId - ID dell'animazione
2088
2015
  */
2089
2016
  cancel(animationId) {
2090
- if (this.activeAnimations.has(animationId)) {
2091
- this.activeAnimations.delete(animationId);
2092
- }
2017
+ this.activeAnimations.delete(animationId);
2093
2018
  }
2094
2019
 
2095
2020
  /**
2096
- * Cancels all animations
2021
+ * Annulla tutte le animazioni attive
2097
2022
  */
2098
2023
  cancelAll() {
2099
2024
  this.activeAnimations.clear();
2100
2025
  }
2101
2026
 
2102
2027
  /**
2103
- * Gets initial value for a property
2028
+ * Ottiene il valore iniziale di una proprietà
2104
2029
  * @private
2105
- * @param {HTMLElement} element - Element
2106
- * @param {string} property - Property name
2107
- * @returns {number} Initial value
2030
+ * @param {HTMLElement} element
2031
+ * @param {string} property
2032
+ * @returns {number}
2108
2033
  */
2109
2034
  _getInitialValue(element, property) {
2110
2035
  if (!element || !element.style) return 0;
2111
2036
  switch (property) {
2112
- case 'opacity':
2113
- return parseFloat(getComputedStyle(element).opacity) || 1;
2114
- case 'left':
2115
- return parseFloat(element.style.left) || 0;
2116
- case 'top':
2117
- return parseFloat(element.style.top) || 0;
2118
- case 'scale':
2119
- return 1;
2120
- default:
2121
- return 0;
2037
+ case 'opacity': return parseFloat(getComputedStyle(element).opacity) || 1;
2038
+ case 'left': return parseFloat(element.style.left) || 0;
2039
+ case 'top': return parseFloat(element.style.top) || 0;
2040
+ case 'scale': return 1;
2041
+ default: return 0;
2122
2042
  }
2123
2043
  }
2124
2044
 
2125
2045
  /**
2126
- * Interpolates between two values
2046
+ * Interpola tra due valori
2127
2047
  * @private
2128
- * @param {number} start - Start value
2129
- * @param {number} end - End value
2130
- * @param {number} progress - Progress (0-1)
2131
- * @returns {number} Interpolated value
2048
+ * @param {number} start
2049
+ * @param {number} end
2050
+ * @param {number} progress
2051
+ * @returns {number}
2132
2052
  */
2133
2053
  _interpolateValue(start, end, progress) {
2134
2054
  return start + (end - start) * progress;
2135
2055
  }
2136
2056
 
2137
2057
  /**
2138
- * Applies animated value to element
2058
+ * Applica il valore animato all'elemento
2139
2059
  * @private
2140
- * @param {HTMLElement} element - Element
2141
- * @param {string} property - Property name
2142
- * @param {number} value - Value to apply
2060
+ * @param {HTMLElement} element
2061
+ * @param {string} property
2062
+ * @param {number} value
2143
2063
  */
2144
2064
  _applyValue(element, property, value) {
2145
2065
  if (!element || !element.style) return;
2146
2066
  switch (property) {
2147
- case 'opacity':
2148
- element.style.opacity = value;
2149
- break;
2150
- case 'left':
2151
- element.style.left = value + 'px';
2152
- break;
2153
- case 'top':
2154
- element.style.top = value + 'px';
2155
- break;
2156
- case 'scale':
2157
- element.style.transform = `scale(${value})`;
2158
- break;
2159
- default:
2160
- element.style[property] = value;
2067
+ case 'opacity': element.style.opacity = value; break;
2068
+ case 'left': element.style.left = value + 'px'; break;
2069
+ case 'top': element.style.top = value + 'px'; break;
2070
+ case 'scale': element.style.transform = `scale(${value})`; break;
2071
+ default: element.style[property] = value;
2161
2072
  }
2162
2073
  }
2163
2074
 
2164
2075
  /**
2165
- * Cleans up resources
2076
+ * Cleanup risorse e animazioni
2166
2077
  */
2167
2078
  destroy() {
2168
2079
  this.cancelAll();
@@ -2170,7 +2081,7 @@ var Chessboard = (function (exports) {
2170
2081
  }
2171
2082
 
2172
2083
  /**
2173
- * Service for managing board setup and DOM operations
2084
+ * BoardService - Handles board DOM setup, manipulation, and resource management
2174
2085
  * @module services/BoardService
2175
2086
  * @since 2.0.0
2176
2087
  */
@@ -2178,45 +2089,45 @@ var Chessboard = (function (exports) {
2178
2089
 
2179
2090
  /**
2180
2091
  * Service responsible for board DOM manipulation and setup
2181
- * @class
2092
+ * @class BoardService
2182
2093
  */
2183
2094
  class BoardService {
2184
2095
  /**
2185
- * Creates a new BoardService instance
2186
- * @param {ChessboardConfig} config - Board configuration
2096
+ * Create a new BoardService instance
2097
+ * @param {Object} config - Board configuration
2187
2098
  */
2188
2099
  constructor(config) {
2100
+ /** @type {Object} */
2189
2101
  this.config = config;
2102
+ /** @type {HTMLElement|null} */
2190
2103
  this.element = null;
2104
+ /** @type {Object.<string, Square>} */
2191
2105
  this.squares = {};
2192
2106
  }
2193
2107
 
2194
2108
  /**
2195
- * Builds the board DOM element and attaches it to the configured container
2196
- * @throws {DOMError} When the container element cannot be found
2109
+ * Build the board DOM element and attach it to the configured container
2110
+ * @throws {DOMError} If the container element cannot be found
2197
2111
  */
2198
2112
  buildBoard() {
2199
- console.log('BoardService.buildBoard: Looking for element with ID:', this.config.id_div);
2200
-
2201
2113
  this.element = document.getElementById(this.config.id_div);
2202
2114
  if (!this.element) {
2203
2115
  throw new DOMError(ERROR_MESSAGES.invalid_id_div + this.config.id_div, this.config.id_div);
2204
2116
  }
2205
-
2206
2117
  this.resize(this.config.size);
2207
- this.element.className = "board";
2118
+ this.element.className = 'board';
2208
2119
  }
2209
2120
 
2210
2121
  /**
2211
- * Creates all 64 squares and adds them to the board
2122
+ * Create all 64 squares and add them to the board
2212
2123
  * @param {Function} coordConverter - Function to convert row/col to real coordinates
2213
2124
  */
2214
2125
  buildSquares(coordConverter) {
2126
+ if (!this.element) throw new DOMError('Board element not initialized', this.config.id_div);
2215
2127
  for (let row = 0; row < BOARD_SIZE.ROWS; row++) {
2216
2128
  for (let col = 0; col < BOARD_SIZE.COLS; col++) {
2217
2129
  const [squareRow, squareCol] = coordConverter(row, col);
2218
2130
  const square = new Square(squareRow, squareCol);
2219
-
2220
2131
  this.squares[square.getId()] = square;
2221
2132
  this.element.appendChild(square.element);
2222
2133
  }
@@ -2224,20 +2135,17 @@ var Chessboard = (function (exports) {
2224
2135
  }
2225
2136
 
2226
2137
  /**
2227
- * Removes all squares from the board and cleans up their resources
2228
- * Best practice: always destroy JS objects and DOM nodes, and clear references.
2138
+ * Remove all squares from the board and clean up their resources
2229
2139
  */
2230
2140
  removeSquares() {
2231
2141
  for (const square of Object.values(this.squares)) {
2232
- // Always call destroy to remove DOM and clear piece reference
2233
2142
  square.destroy();
2234
2143
  }
2235
2144
  this.squares = {};
2236
2145
  }
2237
2146
 
2238
2147
  /**
2239
- * Removes all content from the board element
2240
- * Best practice: clear DOM and force element to be re-fetched on next build.
2148
+ * Remove all content from the board element
2241
2149
  */
2242
2150
  removeBoard() {
2243
2151
  if (this.element) {
@@ -2247,9 +2155,9 @@ var Chessboard = (function (exports) {
2247
2155
  }
2248
2156
 
2249
2157
  /**
2250
- * Resizes the board to the specified size
2158
+ * Resize the board to the specified size
2251
2159
  * @param {number|string} value - Size in pixels or 'auto'
2252
- * @throws {ValidationError} When size value is invalid
2160
+ * @throws {ValidationError} If size value is invalid
2253
2161
  */
2254
2162
  resize(value) {
2255
2163
  if (value === 'auto') {
@@ -2263,15 +2171,13 @@ var Chessboard = (function (exports) {
2263
2171
  }
2264
2172
 
2265
2173
  /**
2266
- * Calculates the optimal size when 'auto' is specified
2174
+ * Calculate the optimal size when 'auto' is specified
2267
2175
  * @private
2268
2176
  * @returns {number} Calculated size in pixels
2269
2177
  */
2270
2178
  _calculateAutoSize() {
2271
- if (!this.element) return 400; // Default fallback
2272
-
2179
+ if (!this.element) return 400;
2273
2180
  const { offsetWidth, offsetHeight } = this.element;
2274
-
2275
2181
  if (offsetWidth === 0) {
2276
2182
  return offsetHeight || 400;
2277
2183
  } else if (offsetHeight === 0) {
@@ -2282,8 +2188,8 @@ var Chessboard = (function (exports) {
2282
2188
  }
2283
2189
 
2284
2190
  /**
2285
- * Gets a square by its ID
2286
- * @param {string} squareId - Square identifier (API pubblica)
2191
+ * Get a square by its ID
2192
+ * @param {string} squareId - Square identifier
2287
2193
  * @returns {Square|null} The square or null if not found
2288
2194
  */
2289
2195
  getSquare(squareId) {
@@ -2291,26 +2197,37 @@ var Chessboard = (function (exports) {
2291
2197
  }
2292
2198
 
2293
2199
  /**
2294
- * Highlight a square (solo oggetto)
2200
+ * Highlight a square
2295
2201
  * @param {Square} square
2296
2202
  * @param {Object} [opts]
2297
2203
  */
2298
2204
  highlightSquare(square, opts = {}) {
2299
- if (!square) throw new Error('highlightSquare richiede oggetto Square');
2300
- // ... logica esistente ...
2205
+ if (!square) throw new Error('highlightSquare requires a Square object');
2206
+ // Implement highlight logic or call square.highlight() if available
2207
+ if (typeof square.highlight === 'function') {
2208
+ square.highlight(opts);
2209
+ } else {
2210
+ square.element.classList.add('highlighted');
2211
+ }
2301
2212
  }
2213
+
2302
2214
  /**
2303
- * Dehighlight a square (solo oggetto)
2215
+ * Remove highlight from a square
2304
2216
  * @param {Square} square
2305
2217
  * @param {Object} [opts]
2306
2218
  */
2307
2219
  dehighlightSquare(square, opts = {}) {
2308
- if (!square) throw new Error('dehighlightSquare richiede oggetto Square');
2309
- // ... logica esistente ...
2220
+ if (!square) throw new Error('dehighlightSquare requires a Square object');
2221
+ // Implement dehighlight logic or call square.dehighlight() if available
2222
+ if (typeof square.dehighlight === 'function') {
2223
+ square.dehighlight(opts);
2224
+ } else {
2225
+ square.element.classList.remove('highlighted');
2226
+ }
2310
2227
  }
2311
2228
 
2312
2229
  /**
2313
- * Gets all squares
2230
+ * Get all squares
2314
2231
  * @returns {Object.<string, Square>} All squares indexed by ID
2315
2232
  */
2316
2233
  getAllSquares() {
@@ -2318,7 +2235,7 @@ var Chessboard = (function (exports) {
2318
2235
  }
2319
2236
 
2320
2237
  /**
2321
- * Applies a method to all squares
2238
+ * Apply a method to all squares
2322
2239
  * @param {string} methodName - Name of the method to call on each square
2323
2240
  * @param {...*} args - Arguments to pass to the method
2324
2241
  */
@@ -2331,7 +2248,7 @@ var Chessboard = (function (exports) {
2331
2248
  }
2332
2249
 
2333
2250
  /**
2334
- * Cleans up all resources
2251
+ * Clean up all resources
2335
2252
  */
2336
2253
  destroy() {
2337
2254
  this.removeSquares();
@@ -2342,7 +2259,7 @@ var Chessboard = (function (exports) {
2342
2259
  }
2343
2260
 
2344
2261
  /**
2345
- * Service for managing coordinate conversions and board orientation
2262
+ * CoordinateService - Handles coordinate conversions and board orientation logic
2346
2263
  * @module services/CoordinateService
2347
2264
  * @since 2.0.0
2348
2265
  */
@@ -2350,19 +2267,20 @@ var Chessboard = (function (exports) {
2350
2267
 
2351
2268
  /**
2352
2269
  * Service responsible for coordinate conversions and board orientation
2353
- * @class
2270
+ * @class CoordinateService
2354
2271
  */
2355
2272
  class CoordinateService {
2356
2273
  /**
2357
- * Creates a new CoordinateService instance
2358
- * @param {ChessboardConfig} config - Board configuration
2274
+ * Create a new CoordinateService instance
2275
+ * @param {Object} config - Board configuration
2359
2276
  */
2360
2277
  constructor(config) {
2278
+ /** @type {Object} */
2361
2279
  this.config = config;
2362
2280
  }
2363
2281
 
2364
2282
  /**
2365
- * Converts logical coordinates to real coordinates based on board orientation
2283
+ * Convert logical coordinates to real coordinates based on board orientation
2366
2284
  * @param {number} row - Row index (0-7)
2367
2285
  * @param {number} col - Column index (0-7)
2368
2286
  * @returns {Array<number>} Real coordinates [row, col] (1-8)
@@ -2370,18 +2288,16 @@ var Chessboard = (function (exports) {
2370
2288
  realCoord(row, col) {
2371
2289
  let realRow = row;
2372
2290
  let realCol = col;
2373
-
2374
2291
  if (this.isWhiteOriented()) {
2375
2292
  realRow = 7 - row;
2376
2293
  } else {
2377
2294
  realCol = 7 - col;
2378
2295
  }
2379
-
2380
2296
  return [realRow + 1, realCol + 1];
2381
2297
  }
2382
2298
 
2383
2299
  /**
2384
- * Converts board coordinates to square ID
2300
+ * Convert board coordinates to square ID
2385
2301
  * @param {number} row - Row index (0-7)
2386
2302
  * @param {number} col - Column index (0-7)
2387
2303
  * @returns {string} Square ID (e.g., 'e4')
@@ -2389,7 +2305,6 @@ var Chessboard = (function (exports) {
2389
2305
  getSquareID(row, col) {
2390
2306
  row = parseInt(row);
2391
2307
  col = parseInt(col);
2392
-
2393
2308
  if (this.isWhiteOriented()) {
2394
2309
  row = 8 - row;
2395
2310
  col = col + 1;
@@ -2397,7 +2312,6 @@ var Chessboard = (function (exports) {
2397
2312
  row = row + 1;
2398
2313
  col = 8 - col;
2399
2314
  }
2400
-
2401
2315
  if (col < 1 || col > 8 || row < 1 || row > 8) {
2402
2316
  throw new ValidationError(
2403
2317
  `Invalid board coordinates: row=${row}, col=${col}`,
@@ -2405,13 +2319,12 @@ var Chessboard = (function (exports) {
2405
2319
  { row, col }
2406
2320
  );
2407
2321
  }
2408
-
2409
2322
  const letter = BOARD_LETTERS[col - 1];
2410
2323
  return letter + row;
2411
2324
  }
2412
2325
 
2413
2326
  /**
2414
- * Converts square ID to board coordinates
2327
+ * Convert square ID to board coordinates
2415
2328
  * @param {string} squareId - Square ID (e.g., 'e4')
2416
2329
  * @returns {Array<number>} Board coordinates [row, col] (0-7)
2417
2330
  */
@@ -2423,10 +2336,8 @@ var Chessboard = (function (exports) {
2423
2336
  squareId
2424
2337
  );
2425
2338
  }
2426
-
2427
2339
  const letter = squareId[0];
2428
2340
  const number = parseInt(squareId[1]);
2429
-
2430
2341
  const col = BOARD_LETTERS.indexOf(letter);
2431
2342
  if (col === -1) {
2432
2343
  throw new ValidationError(
@@ -2435,7 +2346,6 @@ var Chessboard = (function (exports) {
2435
2346
  squareId
2436
2347
  );
2437
2348
  }
2438
-
2439
2349
  if (number < 1 || number > 8) {
2440
2350
  throw new ValidationError(
2441
2351
  ERROR_MESSAGES.invalid_square + squareId,
@@ -2443,9 +2353,7 @@ var Chessboard = (function (exports) {
2443
2353
  squareId
2444
2354
  );
2445
2355
  }
2446
-
2447
2356
  let row, boardCol;
2448
-
2449
2357
  if (this.isWhiteOriented()) {
2450
2358
  row = 8 - number;
2451
2359
  boardCol = col;
@@ -2453,12 +2361,11 @@ var Chessboard = (function (exports) {
2453
2361
  row = number - 1;
2454
2362
  boardCol = 7 - col;
2455
2363
  }
2456
-
2457
2364
  return [row, boardCol];
2458
2365
  }
2459
2366
 
2460
2367
  /**
2461
- * Converts pixel coordinates to square ID
2368
+ * Convert pixel coordinates to square ID
2462
2369
  * @param {number} x - X coordinate in pixels
2463
2370
  * @param {number} y - Y coordinate in pixels
2464
2371
  * @param {HTMLElement} boardElement - Board DOM element
@@ -2466,21 +2373,15 @@ var Chessboard = (function (exports) {
2466
2373
  */
2467
2374
  pixelToSquareID(x, y, boardElement) {
2468
2375
  if (!boardElement) return null;
2469
-
2470
2376
  const rect = boardElement.getBoundingClientRect();
2471
2377
  const { width, height } = rect;
2472
-
2473
- // Check if coordinates are within board bounds
2474
2378
  if (x < 0 || x >= width || y < 0 || y >= height) {
2475
2379
  return null;
2476
2380
  }
2477
-
2478
2381
  const squareWidth = width / 8;
2479
2382
  const squareHeight = height / 8;
2480
-
2481
2383
  const col = Math.floor(x / squareWidth);
2482
2384
  const row = Math.floor(y / squareHeight);
2483
-
2484
2385
  try {
2485
2386
  return this.getSquareID(row, col);
2486
2387
  } catch (error) {
@@ -2489,25 +2390,21 @@ var Chessboard = (function (exports) {
2489
2390
  }
2490
2391
 
2491
2392
  /**
2492
- * Converts square ID to pixel coordinates
2393
+ * Convert square ID to pixel coordinates
2493
2394
  * @param {string} squareId - Square ID (e.g., 'e4')
2494
2395
  * @param {HTMLElement} boardElement - Board DOM element
2495
2396
  * @returns {Object|null} Pixel coordinates {x, y} or null if invalid
2496
2397
  */
2497
2398
  squareIDToPixel(squareId, boardElement) {
2498
2399
  if (!boardElement) return null;
2499
-
2500
2400
  try {
2501
2401
  const [row, col] = this.getCoordinatesFromSquareID(squareId);
2502
2402
  const rect = boardElement.getBoundingClientRect();
2503
2403
  const { width, height } = rect;
2504
-
2505
2404
  const squareWidth = width / 8;
2506
2405
  const squareHeight = height / 8;
2507
-
2508
2406
  const x = col * squareWidth;
2509
2407
  const y = row * squareHeight;
2510
-
2511
2408
  return { x, y };
2512
2409
  } catch (error) {
2513
2410
  return null;
@@ -2515,7 +2412,7 @@ var Chessboard = (function (exports) {
2515
2412
  }
2516
2413
 
2517
2414
  /**
2518
- * Gets the center pixel coordinates of a square
2415
+ * Get the center pixel coordinates of a square
2519
2416
  * @param {string} squareId - Square ID (e.g., 'e4')
2520
2417
  * @param {HTMLElement} boardElement - Board DOM element
2521
2418
  * @returns {Object|null} Center coordinates {x, y} or null if invalid
@@ -2523,11 +2420,9 @@ var Chessboard = (function (exports) {
2523
2420
  getSquareCenter(squareId, boardElement) {
2524
2421
  const coords = this.squareIDToPixel(squareId, boardElement);
2525
2422
  if (!coords) return null;
2526
-
2527
2423
  const rect = boardElement.getBoundingClientRect();
2528
2424
  const squareWidth = rect.width / 8;
2529
2425
  const squareHeight = rect.height / 8;
2530
-
2531
2426
  return {
2532
2427
  x: coords.x + squareWidth / 2,
2533
2428
  y: coords.y + squareHeight / 2
@@ -2535,7 +2430,7 @@ var Chessboard = (function (exports) {
2535
2430
  }
2536
2431
 
2537
2432
  /**
2538
- * Calculates the distance between two squares
2433
+ * Calculate the distance between two squares
2539
2434
  * @param {string} fromSquare - Source square ID
2540
2435
  * @param {string} toSquare - Target square ID
2541
2436
  * @returns {number} Distance between squares
@@ -2544,10 +2439,8 @@ var Chessboard = (function (exports) {
2544
2439
  try {
2545
2440
  const [fromRow, fromCol] = this.getCoordinatesFromSquareID(fromSquare);
2546
2441
  const [toRow, toCol] = this.getCoordinatesFromSquareID(toSquare);
2547
-
2548
2442
  const rowDiff = Math.abs(toRow - fromRow);
2549
2443
  const colDiff = Math.abs(toCol - fromCol);
2550
-
2551
2444
  return Math.sqrt(rowDiff * rowDiff + colDiff * colDiff);
2552
2445
  } catch (error) {
2553
2446
  return 0;
@@ -2555,7 +2448,7 @@ var Chessboard = (function (exports) {
2555
2448
  }
2556
2449
 
2557
2450
  /**
2558
- * Checks if the board is oriented from white's perspective
2451
+ * Check if the board is oriented from white's perspective
2559
2452
  * @returns {boolean} True if white-oriented
2560
2453
  */
2561
2454
  isWhiteOriented() {
@@ -2563,7 +2456,7 @@ var Chessboard = (function (exports) {
2563
2456
  }
2564
2457
 
2565
2458
  /**
2566
- * Checks if the board is oriented from black's perspective
2459
+ * Check if the board is oriented from black's perspective
2567
2460
  * @returns {boolean} True if black-oriented
2568
2461
  */
2569
2462
  isBlackOriented() {
@@ -2571,31 +2464,31 @@ var Chessboard = (function (exports) {
2571
2464
  }
2572
2465
 
2573
2466
  /**
2574
- * Flips the board orientation
2467
+ * Flip the board orientation
2575
2468
  */
2576
2469
  flipOrientation() {
2577
2470
  this.config.orientation = this.isWhiteOriented() ? 'b' : 'w';
2578
2471
  }
2579
2472
 
2580
2473
  /**
2581
- * Sets the board orientation
2582
- * @param {string} orientation - 'w' for white, 'b' for black
2583
- * @throws {ValidationError} When orientation is invalid
2474
+ * Set the board orientation
2475
+ * @param {string} orientation - 'w', 'b', 'white', or 'black'
2476
+ * @throws {ValidationError} If orientation is invalid
2584
2477
  */
2585
2478
  setOrientation(orientation) {
2586
- if (orientation !== 'w' && orientation !== 'b') {
2479
+ const normalized = orientation === 'white' ? 'w' : orientation === 'black' ? 'b' : orientation;
2480
+ if (normalized !== 'w' && normalized !== 'b') {
2587
2481
  throw new ValidationError(
2588
2482
  ERROR_MESSAGES.invalid_orientation + orientation,
2589
2483
  'orientation',
2590
2484
  orientation
2591
2485
  );
2592
2486
  }
2593
-
2594
- this.config.orientation = orientation;
2487
+ this.config.orientation = normalized;
2595
2488
  }
2596
2489
 
2597
2490
  /**
2598
- * Gets the current orientation
2491
+ * Get the current orientation
2599
2492
  * @returns {string} Current orientation ('w' or 'b')
2600
2493
  */
2601
2494
  getOrientation() {
@@ -2603,50 +2496,21 @@ var Chessboard = (function (exports) {
2603
2496
  }
2604
2497
 
2605
2498
  /**
2606
- * Sets the orientation
2607
- * @param {string} orientation - New orientation ('w', 'b', 'white', 'black')
2608
- */
2609
- setOrientation(orientation) {
2610
- // Normalize orientation
2611
- const normalizedOrientation = orientation === 'white' ? 'w' :
2612
- orientation === 'black' ? 'b' : orientation;
2613
-
2614
- if (normalizedOrientation !== 'w' && normalizedOrientation !== 'b') {
2615
- throw new ValidationError(
2616
- ERROR_MESSAGES.invalid_orientation + orientation,
2617
- 'orientation',
2618
- orientation
2619
- );
2620
- }
2621
-
2622
- this.config.orientation = normalizedOrientation;
2623
- }
2624
-
2625
- /**
2626
- * Flips the board orientation
2627
- */
2628
- flipOrientation() {
2629
- this.config.orientation = this.isWhiteOriented() ? 'b' : 'w';
2630
- }
2631
-
2632
- /**
2633
- * Gets all square IDs in order
2499
+ * Get all square IDs in order
2634
2500
  * @returns {Array<string>} Array of all square IDs
2635
2501
  */
2636
2502
  getAllSquareIDs() {
2637
2503
  const squares = [];
2638
-
2639
2504
  for (let row = 0; row < 8; row++) {
2640
2505
  for (let col = 0; col < 8; col++) {
2641
2506
  squares.push(this.getSquareID(row, col));
2642
2507
  }
2643
2508
  }
2644
-
2645
2509
  return squares;
2646
2510
  }
2647
2511
 
2648
2512
  /**
2649
- * Gets squares in a specific rank (row)
2513
+ * Get all squares in a specific rank (row)
2650
2514
  * @param {number} rank - Rank number (1-8)
2651
2515
  * @returns {Array<string>} Array of square IDs in the rank
2652
2516
  */
@@ -2658,19 +2522,16 @@ var Chessboard = (function (exports) {
2658
2522
  rank
2659
2523
  );
2660
2524
  }
2661
-
2662
2525
  const squares = [];
2663
-
2664
2526
  for (let col = 0; col < 8; col++) {
2665
2527
  const row = this.isWhiteOriented() ? 8 - rank : rank - 1;
2666
2528
  squares.push(this.getSquareID(row, col));
2667
2529
  }
2668
-
2669
2530
  return squares;
2670
2531
  }
2671
2532
 
2672
2533
  /**
2673
- * Gets squares in a specific file (column)
2534
+ * Get all squares in a specific file (column)
2674
2535
  * @param {string} file - File letter (a-h)
2675
2536
  * @returns {Array<string>} Array of square IDs in the file
2676
2537
  */
@@ -2683,13 +2544,10 @@ var Chessboard = (function (exports) {
2683
2544
  file
2684
2545
  );
2685
2546
  }
2686
-
2687
2547
  const squares = [];
2688
-
2689
2548
  for (let row = 0; row < 8; row++) {
2690
2549
  squares.push(this.getSquareID(row, col));
2691
2550
  }
2692
-
2693
2551
  return squares;
2694
2552
  }
2695
2553
  }
@@ -3069,6 +2927,67 @@ var Chessboard = (function (exports) {
3069
2927
  }
3070
2928
  };
3071
2929
 
2930
+ /**
2931
+ * ListenerManager - Utility per la gestione centralizzata dei listener DOM
2932
+ * Permette di aggiungere, rimuovere e pulire tutti i listener in modo sicuro e senza duplicazioni.
2933
+ * @module utils/listenerManager
2934
+ * @since 2.0.0
2935
+ */
2936
+
2937
+ class ListenerManager {
2938
+ constructor() {
2939
+ /**
2940
+ * Lista di tutti i listener registrati
2941
+ * @type {Array<{element: EventTarget, type: string, handler: Function, options?: any}>}
2942
+ */
2943
+ this.listeners = [];
2944
+ }
2945
+
2946
+ /**
2947
+ * Aggiunge un listener e lo traccia per la rimozione automatica
2948
+ * @param {EventTarget} element - Elemento su cui aggiungere il listener
2949
+ * @param {string} type - Tipo di evento
2950
+ * @param {Function} handler - Funzione handler
2951
+ * @param {Object|boolean} [options] - Opzioni per addEventListener
2952
+ */
2953
+ add(element, type, handler, options) {
2954
+ if (!element || !type || !handler) return;
2955
+ element.addEventListener(type, handler, options);
2956
+ this.listeners.push({ element, type, handler, options });
2957
+ }
2958
+
2959
+ /**
2960
+ * Rimuove un listener specifico
2961
+ * @param {EventTarget} element
2962
+ * @param {string} type
2963
+ * @param {Function} handler
2964
+ */
2965
+ remove(element, type, handler) {
2966
+ if (!element || !type || !handler) return;
2967
+ element.removeEventListener(type, handler);
2968
+ this.listeners = this.listeners.filter(
2969
+ l => !(l.element === element && l.type === type && l.handler === handler)
2970
+ );
2971
+ }
2972
+
2973
+ /**
2974
+ * Rimuove tutti i listener registrati
2975
+ */
2976
+ removeAll() {
2977
+ for (const { element, type, handler, options } of this.listeners) {
2978
+ element.removeEventListener(type, handler, options);
2979
+ }
2980
+ this.listeners = [];
2981
+ }
2982
+
2983
+ /**
2984
+ * Pulizia risorse
2985
+ */
2986
+ destroy() {
2987
+ this.removeAll();
2988
+ }
2989
+ }
2990
+
3072
2991
  /**
3073
2992
  * Service for managing events and user interactions
3074
2993
  * @module services/EventService
@@ -3101,8 +3020,8 @@ var Chessboard = (function (exports) {
3101
3020
  this.promoting = false;
3102
3021
  this.isAnimating = false;
3103
3022
 
3104
- // Event listeners storage for cleanup
3105
- this.eventListeners = new Map();
3023
+ // Listener manager per gestione centralizzata
3024
+ this.listenerManager = new ListenerManager();
3106
3025
  }
3107
3026
 
3108
3027
  /**
@@ -3112,8 +3031,8 @@ var Chessboard = (function (exports) {
3112
3031
  * @param {Function} onPieceLeave - Callback for piece leave
3113
3032
  */
3114
3033
  addListeners(onSquareClick, onPieceHover, onPieceLeave) {
3115
- // Remove existing listeners to avoid duplicates
3116
- this.removeListeners();
3034
+ // Rimuovi tutti i listener esistenti
3035
+ this.listenerManager.removeAll();
3117
3036
 
3118
3037
  const squares = this.boardService.getAllSquares();
3119
3038
 
@@ -3131,8 +3050,6 @@ var Chessboard = (function (exports) {
3131
3050
  * @param {Function} onPieceLeave - Leave callback
3132
3051
  */
3133
3052
  _addSquareListeners(square, onSquareClick, onPieceHover, onPieceLeave) {
3134
- const listeners = [];
3135
-
3136
3053
  // Throttled hover handlers for performance
3137
3054
  const throttledHover = rafThrottle((e) => {
3138
3055
  if (!this.clicked && this.config.hints) {
@@ -3202,26 +3119,13 @@ var Chessboard = (function (exports) {
3202
3119
  }
3203
3120
  };
3204
3121
 
3205
- // Add listeners
3206
- square.element.addEventListener('mouseover', throttledHover);
3207
- square.element.addEventListener('mouseout', throttledLeave);
3208
- square.element.addEventListener('click', handleClick);
3209
- // Touch: separa tap e drag
3210
- square.element.addEventListener('touchstart', handleTouchStart);
3211
- square.element.addEventListener('touchmove', handleTouchMove);
3212
- square.element.addEventListener('touchend', handleTouchEnd);
3213
-
3214
- // Store listeners for cleanup
3215
- listeners.push(
3216
- { element: square.element, type: 'mouseover', handler: throttledHover },
3217
- { element: square.element, type: 'mouseout', handler: throttledLeave },
3218
- { element: square.element, type: 'click', handler: handleClick },
3219
- { element: square.element, type: 'touchstart', handler: handleTouchStart },
3220
- { element: square.element, type: 'touchmove', handler: handleTouchMove },
3221
- { element: square.element, type: 'touchend', handler: handleTouchEnd }
3222
- );
3223
-
3224
- this.eventListeners.set(square.id, listeners);
3122
+ // Usa ListenerManager per aggiungere tutti i listener
3123
+ this.listenerManager.add(square.element, 'mouseover', throttledHover);
3124
+ this.listenerManager.add(square.element, 'mouseout', throttledLeave);
3125
+ this.listenerManager.add(square.element, 'click', handleClick);
3126
+ this.listenerManager.add(square.element, 'touchstart', handleTouchStart);
3127
+ this.listenerManager.add(square.element, 'touchmove', handleTouchMove);
3128
+ this.listenerManager.add(square.element, 'touchend', handleTouchEnd);
3225
3129
  }
3226
3130
 
3227
3131
  /**
@@ -3238,18 +3142,14 @@ var Chessboard = (function (exports) {
3238
3142
  */
3239
3143
  createDragFunction(square, piece, onDragStart, onDragMove, onDrop, onSnapback, onMove, onRemove) {
3240
3144
  return (event) => {
3241
- event.preventDefault();
3242
-
3243
3145
  if (!this.config.draggable || !piece || this.isAnimating) {
3244
3146
  return;
3245
3147
  }
3246
3148
 
3247
3149
  const originalFrom = square;
3248
- let isDragging = false;
3249
3150
  let from = originalFrom;
3250
3151
  let to = square;
3251
3152
  let previousHighlight = null;
3252
-
3253
3153
  const img = piece.element;
3254
3154
 
3255
3155
  if (!this.moveService.canMove(from)) {
@@ -3260,6 +3160,15 @@ var Chessboard = (function (exports) {
3260
3160
  const isTouch = event.type && event.type.startsWith('touch');
3261
3161
  const startX = event.clientX || (event.touches && event.touches[0]?.clientX) || 0;
3262
3162
  const startY = event.clientY || (event.touches && event.touches[0]?.clientY) || 0;
3163
+ let dragStarted = false;
3164
+
3165
+ // --- Calculate offset between cursor/finger and piece top-left at drag start ---
3166
+ const pieceRect = img.getBoundingClientRect();
3167
+ const boardElement = this.boardService.element;
3168
+ const boardRect = boardElement.getBoundingClientRect();
3169
+ // Offset from cursor/finger to top-left of piece
3170
+ const offsetX = startX - pieceRect.left;
3171
+ const offsetY = startY - pieceRect.top;
3263
3172
 
3264
3173
  // --- Touch scroll lock helper ---
3265
3174
  const addScrollLock = () => {
@@ -3271,10 +3180,6 @@ var Chessboard = (function (exports) {
3271
3180
 
3272
3181
  // --- MOVE HANDLER (mouse + touch unified) ---
3273
3182
  const moveAt = (event) => {
3274
- const boardElement = this.boardService.element;
3275
- const squareSize = boardElement.offsetWidth / 8;
3276
-
3277
- // Get mouse/touch coordinates
3278
3183
  let clientX, clientY;
3279
3184
  if (event.touches && event.touches[0]) {
3280
3185
  clientX = event.touches[0].clientX;
@@ -3283,77 +3188,54 @@ var Chessboard = (function (exports) {
3283
3188
  clientX = event.clientX;
3284
3189
  clientY = event.clientY;
3285
3190
  }
3286
-
3287
- // Calculate position relative to board
3288
- const boardRect = boardElement.getBoundingClientRect();
3289
- const x = clientX - boardRect.left - (squareSize / 2);
3290
- const y = clientY - boardRect.top - (squareSize / 2);
3291
-
3191
+ // Position the piece so the cursor/finger stays at the same relative point
3192
+ const x = clientX - boardRect.left - offsetX;
3193
+ const y = clientY - boardRect.top - offsetY;
3292
3194
  img.style.left = x + 'px';
3293
3195
  img.style.top = y + 'px';
3294
-
3295
3196
  return true;
3296
3197
  };
3297
3198
 
3298
3199
  // --- DRAG MOVE (mouse + touch) ---
3200
+ const moveThreshold = 5; // px
3299
3201
  const onPointerMove = (event) => {
3300
- // For touch, only handle the first finger
3301
3202
  if (event.touches && event.touches.length > 1) return;
3302
3203
  if (event.touches && !event.touches[0]) return;
3303
- if (event.type && event.type.startsWith('touch')) event.preventDefault();
3304
-
3305
3204
  const currentX = event.clientX || (event.touches && event.touches[0]?.clientX) || 0;
3306
3205
  const currentY = event.clientY || (event.touches && event.touches[0]?.clientY) || 0;
3307
3206
  const deltaX = Math.abs(currentX - startX);
3308
3207
  const deltaY = Math.abs(currentY - startY);
3309
-
3310
- // Start dragging if mouse/touch moved enough
3311
- if (!isDragging && (deltaX > 3 || deltaY > 3)) {
3312
- isDragging = true;
3313
- // Inizio drag: blocca update board
3208
+ if (!dragStarted && (deltaX > moveThreshold || deltaY > moveThreshold)) {
3209
+ dragStarted = true;
3210
+ if (isTouch) addScrollLock();
3314
3211
  if (this.chessboard) this.chessboard._isDragging = true;
3315
-
3316
- // Mostra hint all'inizio del drag se attivi
3317
3212
  if (this.config.hints && typeof this.chessboard._boundOnPieceHover === 'function') {
3318
3213
  this.chessboard._boundOnPieceHover(from);
3319
3214
  }
3320
-
3321
- // Set up drag state
3322
3215
  if (!this.config.clickable) {
3323
3216
  this.clicked = null;
3324
3217
  this.clicked = from;
3325
3218
  } else if (!this.clicked) {
3326
3219
  this.clicked = from;
3327
3220
  }
3328
-
3329
- // Visual feedback
3330
3221
  if (this.config.clickable) {
3331
3222
  from.select();
3332
3223
  }
3333
-
3334
- // Prepare piece for dragging
3335
3224
  img.style.position = 'absolute';
3336
3225
  img.style.zIndex = '100';
3337
3226
  img.classList.add('dragging');
3338
-
3339
3227
  DragOptimizations.enableForDrag(img);
3340
-
3341
- // Lock scroll for touch
3342
- if (isTouch) addScrollLock();
3343
-
3344
- // Call drag start callback
3345
3228
  if (!onDragStart(square, piece)) {
3346
3229
  return;
3347
3230
  }
3348
3231
  }
3349
-
3350
- if (!isDragging) return;
3351
-
3232
+ // Only block scroll and call preventDefault after drag has started
3233
+ if (dragStarted && isTouch && event.cancelable) {
3234
+ event.preventDefault();
3235
+ }
3236
+ if (!dragStarted) return;
3352
3237
  if (!moveAt(event)) ;
3353
-
3354
3238
  // Update target square
3355
- const boardElement = this.boardService.element;
3356
- const boardRect = boardElement.getBoundingClientRect();
3357
3239
  let clientX, clientY;
3358
3240
  if (event.touches && event.touches[0]) {
3359
3241
  clientX = event.touches[0].clientX;
@@ -3364,22 +3246,16 @@ var Chessboard = (function (exports) {
3364
3246
  }
3365
3247
  const x = clientX - boardRect.left;
3366
3248
  const y = clientY - boardRect.top;
3367
-
3368
3249
  let newTo = null;
3369
3250
  if (x >= 0 && x <= boardRect.width && y >= 0 && y <= boardRect.height) {
3370
3251
  const squareId = this.coordinateService.pixelToSquareID(x, y, boardElement);
3371
3252
  newTo = squareId ? this.boardService.getSquare(squareId) : null;
3372
3253
  }
3373
-
3374
3254
  to = newTo;
3375
3255
  onDragMove(from, to, piece);
3376
-
3377
- // Update visual feedback
3378
- if (to !== previousHighlight) {
3379
- to?.highlight();
3380
- previousHighlight?.dehighlight();
3381
- previousHighlight = to;
3382
- }
3256
+ if (to) to.highlight();
3257
+ if (previousHighlight && previousHighlight !== to) previousHighlight.dehighlight();
3258
+ previousHighlight = to;
3383
3259
  };
3384
3260
 
3385
3261
  // --- DRAG END (mouse + touch) ---
@@ -3390,26 +3266,19 @@ var Chessboard = (function (exports) {
3390
3266
  document.removeEventListener('touchmove', onPointerMove);
3391
3267
  window.removeEventListener('touchend', onPointerUp);
3392
3268
  if (isTouch) removeScrollLock();
3393
- // Fine drag: sblocca update board
3394
3269
  if (this.chessboard) this.chessboard._isDragging = false;
3395
-
3396
- // Rimuovi hint alla fine del drag se attivi
3397
3270
  if (this.config.hints && typeof this.chessboard._boundOnPieceLeave === 'function') {
3398
3271
  this.chessboard._boundOnPieceLeave(from);
3399
3272
  }
3400
-
3401
- if (!isDragging) {
3273
+ if (!dragStarted) {
3402
3274
  return;
3403
3275
  }
3404
-
3405
3276
  img.style.zIndex = '20';
3406
3277
  img.classList.remove('dragging');
3407
3278
  img.style.willChange = 'auto';
3408
-
3409
3279
  // Handle drop
3410
3280
  const dropResult = onDrop(originalFrom, to, piece);
3411
3281
  const isTrashDrop = !to && (this.config.dropOffBoard === 'trash' || dropResult === 'trash');
3412
-
3413
3282
  if (isTrashDrop) {
3414
3283
  this._handleTrashDrop(originalFrom, onRemove);
3415
3284
  } else if (!to) {
@@ -3423,16 +3292,12 @@ var Chessboard = (function (exports) {
3423
3292
  }
3424
3293
  };
3425
3294
 
3426
- // --- Attach listeners (mouse + touch) ---
3295
+ // Attach listeners (mouse + touch)
3427
3296
  window.addEventListener('mouseup', onPointerUp, { once: true });
3428
3297
  document.addEventListener('mousemove', onPointerMove);
3429
3298
  img.addEventListener('mouseup', onPointerUp, { once: true });
3430
- // Touch events
3431
3299
  window.addEventListener('touchend', onPointerUp, { once: true });
3432
3300
  document.addEventListener('touchmove', onPointerMove, { passive: false });
3433
-
3434
- // Per robustezza: se il drag parte da touch, blocca subito lo scroll
3435
- if (isTouch) addScrollLock();
3436
3301
  };
3437
3302
  }
3438
3303
 
@@ -3925,33 +3790,21 @@ var Chessboard = (function (exports) {
3925
3790
  * Removes all existing event listeners
3926
3791
  */
3927
3792
  removeListeners() {
3928
- this.eventListeners.forEach((listeners, squareId) => {
3929
- listeners.forEach(({ element, type, handler }) => {
3930
- element.removeEventListener(type, handler);
3931
- });
3932
- });
3933
-
3934
- this.eventListeners.clear();
3793
+ this.listenerManager.removeAll();
3935
3794
  }
3936
3795
 
3937
3796
  /**
3938
3797
  * Removes all event listeners
3939
3798
  */
3940
3799
  removeAllListeners() {
3941
- this.eventListeners.forEach((listeners, squareId) => {
3942
- listeners.forEach(({ element, type, handler }) => {
3943
- element.removeEventListener(type, handler);
3944
- });
3945
- });
3946
-
3947
- this.eventListeners.clear();
3800
+ this.listenerManager.removeAll();
3948
3801
  }
3949
3802
 
3950
3803
  /**
3951
3804
  * Cleans up resources
3952
3805
  */
3953
3806
  destroy() {
3954
- this.removeAllListeners();
3807
+ this.listenerManager.destroy();
3955
3808
  this.clicked = null;
3956
3809
  this.promoting = false;
3957
3810
  this.isAnimating = false;
@@ -3959,7 +3812,7 @@ var Chessboard = (function (exports) {
3959
3812
  }
3960
3813
 
3961
3814
  /**
3962
- * Service for managing chess moves and move validation
3815
+ * MoveService - Handles chess move management and validation
3963
3816
  * @module services/MoveService
3964
3817
  * @since 2.0.0
3965
3818
  */
@@ -3967,13 +3820,13 @@ var Chessboard = (function (exports) {
3967
3820
 
3968
3821
  /**
3969
3822
  * Service responsible for move management and validation
3970
- * @class
3823
+ * @class MoveService
3971
3824
  */
3972
3825
  class MoveService {
3973
3826
  /**
3974
- * Creates a new MoveService instance
3975
- * @param {ChessboardConfig} config - Board configuration
3976
- * @param {PositionService} positionService - Position service instance
3827
+ * Create a new MoveService instance
3828
+ * @param {Object} config - Board configuration
3829
+ * @param {Object} positionService - Position service instance
3977
3830
  */
3978
3831
  constructor(config, positionService) {
3979
3832
  this.config = config;
@@ -3983,43 +3836,32 @@ var Chessboard = (function (exports) {
3983
3836
  }
3984
3837
 
3985
3838
  /**
3986
- * Checks if a piece on a square can move
3839
+ * Check if a piece on a square can move
3987
3840
  * @param {Square} square - Square to check
3988
3841
  * @returns {boolean} True if piece can move
3989
3842
  */
3990
3843
  canMove(square) {
3991
3844
  if (!square.piece) return false;
3992
-
3993
3845
  const { movableColors, onlyLegalMoves } = this.config;
3994
-
3995
3846
  if (movableColors === 'none') return false;
3996
3847
  if (movableColors === 'w' && square.piece.color === 'b') return false;
3997
3848
  if (movableColors === 'b' && square.piece.color === 'w') return false;
3998
-
3999
3849
  if (!onlyLegalMoves) return true;
4000
-
4001
- // Check if position service and game are available
4002
- if (!this.positionService || !this.positionService.getGame()) {
4003
- return false;
4004
- }
4005
-
3850
+ if (!this.positionService || !this.positionService.getGame()) return false;
4006
3851
  const game = this.positionService.getGame();
4007
3852
  return square.piece.color === game.turn();
4008
3853
  }
4009
3854
 
4010
3855
  /**
4011
- * Converts various move formats to a Move instance
3856
+ * Convert various move formats to a Move instance
4012
3857
  * @param {string|Move|Object} move - Move in various formats
4013
3858
  * @param {Object} squares - All board squares
4014
3859
  * @returns {Move} Move instance
4015
3860
  * @throws {MoveError} When move format is invalid
4016
3861
  */
4017
3862
  convertMove(move, squares) {
4018
- if (move instanceof Move$1) {
4019
- return move;
4020
- }
3863
+ if (move instanceof Move$1) return move;
4021
3864
  if (typeof move === 'object' && move.from && move.to) {
4022
- // Se sono id, converto in oggetti; se sono già oggetti, li uso direttamente
4023
3865
  const fromSquare = typeof move.from === 'string' ? squares[move.from] : move.from;
4024
3866
  const toSquare = typeof move.to === 'string' ? squares[move.to] : move.to;
4025
3867
  if (!fromSquare || !toSquare) throw new MoveError(ERROR_MESSAGES.invalid_move_format, move.from, move.to);
@@ -4038,13 +3880,12 @@ var Chessboard = (function (exports) {
4038
3880
  }
4039
3881
 
4040
3882
  /**
4041
- * Checks if a move is legal
3883
+ * Check if a move is legal
4042
3884
  * @param {Move} move - Move to check
4043
3885
  * @returns {boolean} True if move is legal
4044
3886
  */
4045
3887
  isLegalMove(move) {
4046
3888
  const legalMoves = this.getLegalMoves(move.from.id);
4047
-
4048
3889
  return legalMoves.some(legalMove =>
4049
3890
  legalMove.to === move.to.id &&
4050
3891
  move.promotion === legalMove.promotion
@@ -4052,180 +3893,100 @@ var Chessboard = (function (exports) {
4052
3893
  }
4053
3894
 
4054
3895
  /**
4055
- * Gets all legal moves for a square or the entire position
3896
+ * Get all legal moves for a square or the entire position
4056
3897
  * @param {string} [from] - Square to get moves from (optional)
4057
3898
  * @param {boolean} [verbose=true] - Whether to return verbose move objects
4058
3899
  * @returns {Array} Array of legal moves
4059
3900
  */
4060
3901
  getLegalMoves(from = null, verbose = true) {
4061
- // Check if position service and game are available
4062
- if (!this.positionService || !this.positionService.getGame()) {
4063
- return [];
4064
- }
4065
-
3902
+ if (!this.positionService || !this.positionService.getGame()) return [];
4066
3903
  const game = this.positionService.getGame();
4067
-
4068
3904
  if (!game) return [];
4069
-
4070
3905
  const options = { verbose };
4071
- if (from) {
4072
- options.square = from;
4073
- }
4074
-
3906
+ if (from) options.square = from;
4075
3907
  return game.moves(options);
4076
3908
  }
4077
3909
 
4078
3910
  /**
4079
- * Gets legal moves with caching for performance
3911
+ * Get legal moves with caching for performance
4080
3912
  * @param {Square} square - Square to get moves from
4081
3913
  * @returns {Array} Array of legal moves
4082
3914
  */
4083
3915
  getCachedLegalMoves(square) {
4084
- // Check if position service and game are available
4085
- if (!this.positionService || !this.positionService.getGame()) {
4086
- return [];
4087
- }
4088
-
3916
+ if (!this.positionService || !this.positionService.getGame()) return [];
4089
3917
  const game = this.positionService.getGame();
4090
3918
  if (!game) return [];
4091
-
4092
3919
  const cacheKey = `${square.id}-${game.fen()}`;
4093
3920
  let moves = this._movesCache.get(cacheKey);
4094
-
4095
3921
  if (!moves) {
4096
3922
  moves = game.moves({ square: square.id, verbose: true });
4097
3923
  this._movesCache.set(cacheKey, moves);
4098
-
4099
- // Clear cache after a short delay to prevent memory buildup
4100
- if (this._cacheTimeout) {
4101
- clearTimeout(this._cacheTimeout);
4102
- }
4103
-
3924
+ if (this._cacheTimeout) clearTimeout(this._cacheTimeout);
4104
3925
  this._cacheTimeout = setTimeout(() => {
4105
3926
  this._movesCache.clear();
4106
3927
  }, 1000);
4107
3928
  }
4108
-
4109
3929
  return moves;
4110
3930
  }
4111
3931
 
4112
3932
  /**
4113
- * Executes a move on the game
4114
- * @param {Move} move - Move to execute (deve essere oggetto Move)
3933
+ * Execute a move on the game
3934
+ * @param {Move} move - Move to execute (must be a Move object)
4115
3935
  * @returns {Object|null} Move result from chess.js or null if invalid
4116
3936
  */
4117
3937
  executeMove(move) {
4118
- if (!(move instanceof Move$1)) throw new Error('executeMove richiede un oggetto Move');
4119
- if (!this.positionService || !this.positionService.getGame()) {
4120
- return null;
4121
- }
3938
+ if (!(move instanceof Move$1)) throw new Error('executeMove requires a Move object');
3939
+ if (!this.positionService || !this.positionService.getGame()) return null;
4122
3940
  const game = this.positionService.getGame();
4123
3941
  if (!game) return null;
4124
- const moveOptions = {
4125
- from: move.from.id,
4126
- to: move.to.id
4127
- };
4128
- if (move.hasPromotion()) {
4129
- moveOptions.promotion = move.promotion;
4130
- }
3942
+ const moveOptions = { from: move.from.id, to: move.to.id };
3943
+ if (move.hasPromotion()) moveOptions.promotion = move.promotion;
4131
3944
  const result = game.move(moveOptions);
4132
3945
  return result;
4133
3946
  }
4134
3947
 
4135
3948
  /**
4136
- * Determina se una mossa richiede promozione
4137
- * @param {Move} move - Deve essere oggetto Move
3949
+ * Determine if a move requires promotion
3950
+ * @param {Move} move - Must be a Move object
4138
3951
  * @returns {boolean}
4139
3952
  */
4140
3953
  requiresPromotion(move) {
4141
- if (!(move instanceof Move$1)) throw new Error('requiresPromotion richiede un oggetto Move');
4142
- console.log('Checking if move requires promotion:', move.from.id, '->', move.to.id);
4143
-
4144
- if (!this.config.onlyLegalMoves) {
4145
- console.log('Not in legal moves mode, no promotion required');
4146
- return false;
4147
- }
4148
-
3954
+ if (!(move instanceof Move$1)) throw new Error('requiresPromotion requires a Move object');
3955
+ if (!this.config.onlyLegalMoves) return false;
4149
3956
  const game = this.positionService.getGame();
4150
- if (!game) {
4151
- console.log('No game instance available');
4152
- return false;
4153
- }
4154
-
3957
+ if (!game) return false;
4155
3958
  const piece = game.get(move.from.id);
4156
- if (!piece || piece.type !== 'p') {
4157
- console.log('Not a pawn move, no promotion required');
4158
- return false;
4159
- }
4160
-
3959
+ if (!piece || piece.type !== 'p') return false;
4161
3960
  const targetRank = move.to.row;
4162
- if (targetRank !== 1 && targetRank !== 8) {
4163
- console.log('Not reaching promotion rank, no promotion required');
4164
- return false;
4165
- }
4166
-
4167
- console.log('Pawn reaching promotion rank, validating move...');
4168
-
4169
- // Additional validation: check if the pawn can actually reach this square
4170
- if (!this._isPawnMoveValid(move.from, move.to, piece.color)) {
4171
- console.log('Pawn move not valid, no promotion required');
4172
- return false;
4173
- }
4174
-
4175
- // First check if the move is legal without promotion
4176
- const simpleMoveObj = {
4177
- from: move.from.id,
4178
- to: move.to.id
4179
- };
4180
-
3961
+ if (targetRank !== 1 && targetRank !== 8) return false;
3962
+ if (!this._isPawnMoveValid(move.from, move.to, piece.color)) return false;
3963
+ // Try move without promotion
3964
+ const simpleMoveObj = { from: move.from.id, to: move.to.id };
4181
3965
  try {
4182
- console.log('Testing move without promotion:', simpleMoveObj);
4183
- // Test if the move is legal without promotion first
4184
3966
  const testMove = game.move(simpleMoveObj);
4185
3967
  if (testMove) {
4186
- // Move was successful, but check if it was a promotion
4187
3968
  const wasPromotion = testMove.promotion;
4188
-
4189
- // Undo the test move
4190
3969
  game.undo();
4191
-
4192
- console.log('Move successful without promotion, was promotion:', wasPromotion !== undefined);
4193
-
4194
- // If it was a promotion, return true
4195
3970
  return wasPromotion !== undefined;
4196
3971
  }
4197
3972
  } catch (error) {
4198
- console.log('Move failed without promotion, trying with promotion:', error.message);
4199
-
4200
- // If simple move fails, try with promotion
4201
- const promotionMoveObj = {
4202
- from: move.from.id,
4203
- to: move.to.id,
4204
- promotion: 'q' // test with queen
4205
- };
4206
-
3973
+ // Try with promotion
3974
+ const promotionMoveObj = { from: move.from.id, to: move.to.id, promotion: 'q' };
4207
3975
  try {
4208
- console.log('Testing move with promotion:', promotionMoveObj);
4209
3976
  const testMove = game.move(promotionMoveObj);
4210
3977
  if (testMove) {
4211
- // Undo the test move
4212
3978
  game.undo();
4213
- console.log('Move successful with promotion, promotion required');
4214
3979
  return true;
4215
3980
  }
4216
3981
  } catch (promotionError) {
4217
- console.log('Move failed even with promotion:', promotionError.message);
4218
- // Move is not legal even with promotion
4219
3982
  return false;
4220
3983
  }
4221
3984
  }
4222
-
4223
- console.log('Move validation complete, no promotion required');
4224
3985
  return false;
4225
3986
  }
4226
3987
 
4227
3988
  /**
4228
- * Validates if a pawn move is theoretically possible
3989
+ * Validate if a pawn move is theoretically possible
4229
3990
  * @private
4230
3991
  * @param {Square} from - Source square
4231
3992
  * @param {Square} to - Target square
@@ -4237,48 +3998,21 @@ var Chessboard = (function (exports) {
4237
3998
  const toRank = to.row;
4238
3999
  const fromFile = from.col;
4239
4000
  const toFile = to.col;
4240
-
4241
- console.log(`Validating pawn move: ${from.id} -> ${to.id} (${color})`);
4242
- console.log(`Ranks: ${fromRank} -> ${toRank}, Files: ${fromFile} -> ${toFile}`);
4243
-
4244
- // Direction of pawn movement
4245
4001
  const direction = color === 'w' ? 1 : -1;
4246
4002
  const rankDiff = toRank - fromRank;
4247
4003
  const fileDiff = Math.abs(toFile - fromFile);
4248
-
4249
- // Pawn can only move forward
4250
- if (rankDiff * direction <= 0) {
4251
- console.log('Invalid: Pawn cannot move backward or stay in place');
4252
- return false;
4253
- }
4254
-
4255
- // Pawn can only move 1 rank at a time (except for double move from starting position)
4256
- if (Math.abs(rankDiff) > 2) {
4257
- console.log('Invalid: Pawn cannot move more than 2 ranks');
4258
- return false;
4259
- }
4260
-
4261
- // If moving 2 ranks, must be from starting position
4004
+ if (rankDiff * direction <= 0) return false;
4005
+ if (Math.abs(rankDiff) > 2) return false;
4262
4006
  if (Math.abs(rankDiff) === 2) {
4263
4007
  const startingRank = color === 'w' ? 2 : 7;
4264
- if (fromRank !== startingRank) {
4265
- console.log(`Invalid: Pawn cannot move 2 ranks from rank ${fromRank}`);
4266
- return false;
4267
- }
4008
+ if (fromRank !== startingRank) return false;
4268
4009
  }
4269
-
4270
- // Pawn can only move to adjacent files (diagonal capture) or same file (forward move)
4271
- if (fileDiff > 1) {
4272
- console.log('Invalid: Pawn cannot move more than 1 file');
4273
- return false;
4274
- }
4275
-
4276
- console.log('Pawn move validation passed');
4010
+ if (fileDiff > 1) return false;
4277
4011
  return true;
4278
4012
  }
4279
4013
 
4280
4014
  /**
4281
- * Handles promotion UI setup
4015
+ * Handle promotion UI setup
4282
4016
  * @param {Move} move - Move requiring promotion
4283
4017
  * @param {Object} squares - All board squares
4284
4018
  * @param {Function} onPromotionSelect - Callback when promotion piece is selected
@@ -4287,55 +4021,33 @@ var Chessboard = (function (exports) {
4287
4021
  */
4288
4022
  setupPromotion(move, squares, onPromotionSelect, onPromotionCancel) {
4289
4023
  if (!this.requiresPromotion(move)) return false;
4290
-
4291
- // Check if position service and game are available
4292
- if (!this.positionService || !this.positionService.getGame()) {
4293
- return false;
4294
- }
4295
-
4024
+ if (!this.positionService || !this.positionService.getGame()) return false;
4296
4025
  const game = this.positionService.getGame();
4297
4026
  const piece = game.get(move.from.id);
4298
4027
  const targetSquare = move.to;
4299
-
4300
- // Clear any existing promotion UI
4301
4028
  Object.values(squares).forEach(square => {
4302
4029
  square.removePromotion();
4303
4030
  square.removeCover();
4304
4031
  });
4305
-
4306
- // Always show promotion choices in a column
4307
4032
  this._showPromotionInColumn(targetSquare, piece, squares, onPromotionSelect, onPromotionCancel);
4308
-
4309
4033
  return true;
4310
4034
  }
4311
4035
 
4312
4036
  /**
4313
- * Shows promotion choices in a column
4037
+ * Show promotion choices in a column
4314
4038
  * @private
4315
4039
  */
4316
4040
  _showPromotionInColumn(targetSquare, piece, squares, onPromotionSelect, onPromotionCancel) {
4317
- console.log('Setting up promotion for', targetSquare.id, 'piece color:', piece.color);
4318
-
4319
- // Set up promotion choices starting from border row
4320
4041
  PROMOTION_PIECES.forEach((pieceType, index) => {
4321
4042
  const choiceSquare = this._findPromotionSquare(targetSquare, index, squares);
4322
-
4323
4043
  if (choiceSquare) {
4324
4044
  const pieceId = pieceType + piece.color;
4325
4045
  const piecePath = this._getPiecePathForPromotion(pieceId);
4326
-
4327
- console.log('Setting up promotion choice:', pieceType, 'on square:', choiceSquare.id);
4328
-
4329
4046
  choiceSquare.putPromotion(piecePath, () => {
4330
- console.log('Promotion choice selected:', pieceType);
4331
4047
  onPromotionSelect(pieceType);
4332
4048
  });
4333
- } else {
4334
- console.log('Could not find square for promotion choice:', pieceType, 'index:', index);
4335
4049
  }
4336
4050
  });
4337
-
4338
- // Set up cover squares (for cancellation)
4339
4051
  Object.values(squares).forEach(square => {
4340
4052
  if (!square.hasPromotion()) {
4341
4053
  square.putCover(() => {
@@ -4343,181 +4055,119 @@ var Chessboard = (function (exports) {
4343
4055
  });
4344
4056
  }
4345
4057
  });
4346
-
4347
4058
  return true;
4348
4059
  }
4349
4060
 
4350
4061
  /**
4351
- * Finds the appropriate square for a promotion piece
4062
+ * Find the appropriate square for a promotion piece
4352
4063
  * @private
4353
4064
  * @param {Square} targetSquare - Target square of the promotion move
4354
- * @param {number} distance - Distance from target square
4065
+ * @param {number} index - Distance from target square
4355
4066
  * @param {Object} squares - All board squares
4356
4067
  * @returns {Square|null} Square for promotion piece or null
4357
4068
  */
4358
4069
  _findPromotionSquare(targetSquare, index, squares) {
4359
4070
  const col = targetSquare.col;
4360
4071
  const baseRow = targetSquare.row;
4361
-
4362
- console.log('Looking for promotion square - target:', targetSquare.id, 'index:', index, 'col:', col, 'baseRow:', baseRow);
4363
-
4364
- // Calculate row based on index and promotion direction
4365
- // Start from the border row (1 or 8) and go inward
4366
4072
  let row;
4367
4073
  if (baseRow === 8) {
4368
- // White promotion: start from row 8 and go down
4369
4074
  row = 8 - index;
4370
4075
  } else if (baseRow === 1) {
4371
- // Black promotion: start from row 1 and go up
4372
4076
  row = 1 + index;
4373
4077
  } else {
4374
- console.log('Invalid promotion row:', baseRow);
4375
4078
  return null;
4376
4079
  }
4377
-
4378
- console.log('Calculated row:', row);
4379
-
4380
- // Ensure row is within bounds
4381
- if (row < 1 || row > 8) {
4382
- console.log('Row out of bounds:', row);
4383
- return null;
4384
- }
4385
-
4386
- // Find square by row/col
4080
+ if (row < 1 || row > 8) return null;
4387
4081
  for (const square of Object.values(squares)) {
4388
4082
  if (square.col === col && square.row === row) {
4389
- console.log('Found promotion square:', square.id);
4390
4083
  return square;
4391
4084
  }
4392
4085
  }
4393
-
4394
- console.log('No square found for col:', col, 'row:', row);
4395
4086
  return null;
4396
4087
  }
4397
4088
 
4398
4089
  /**
4399
- * Gets piece path for promotion UI
4090
+ * Get piece path for promotion UI
4400
4091
  * @private
4401
4092
  * @param {string} pieceId - Piece identifier
4402
4093
  * @returns {string} Path to piece asset
4403
4094
  */
4404
4095
  _getPiecePathForPromotion(pieceId) {
4405
- // This would typically use the PieceService
4406
- // For now, we'll use a simple implementation
4407
4096
  const { piecesPath } = this.config;
4408
-
4409
4097
  if (typeof piecesPath === 'string') {
4410
4098
  return `${piecesPath}/${pieceId}.svg`;
4411
4099
  }
4412
-
4413
- // Fallback for other path types
4414
4100
  return `assets/pieces/${pieceId}.svg`;
4415
4101
  }
4416
4102
 
4417
4103
  /**
4418
- * Parses a move string into a move object
4104
+ * Parse a move string into a move object
4419
4105
  * @param {string} moveString - Move string (e.g., 'e2e4', 'e7e8q')
4420
4106
  * @returns {Object|null} Move object or null if invalid
4421
4107
  */
4422
4108
  parseMove(moveString) {
4423
- if (typeof moveString !== 'string' || moveString.length < 4 || moveString.length > 5) {
4424
- return null;
4425
- }
4426
-
4109
+ if (typeof moveString !== 'string' || moveString.length < 4 || moveString.length > 5) return null;
4427
4110
  const from = moveString.slice(0, 2);
4428
4111
  const to = moveString.slice(2, 4);
4429
4112
  const promotion = moveString.slice(4, 5);
4430
-
4431
- // Basic validation
4432
- if (!/^[a-h][1-8]$/.test(from) || !/^[a-h][1-8]$/.test(to)) {
4433
- return null;
4434
- }
4435
-
4436
- if (promotion && !['q', 'r', 'b', 'n'].includes(promotion.toLowerCase())) {
4437
- return null;
4438
- }
4439
-
4440
- return {
4441
- from: from,
4442
- to: to,
4443
- promotion: promotion || null
4444
- };
4113
+ if (!/^[a-h][1-8]$/.test(from) || !/^[a-h][1-8]$/.test(to)) return null;
4114
+ if (promotion && !['q', 'r', 'b', 'n'].includes(promotion.toLowerCase())) return null;
4115
+ return { from, to, promotion: promotion || null };
4445
4116
  }
4446
4117
 
4447
4118
  /**
4448
- * Checks if a move is a castle move
4119
+ * Check if a move is a castle move
4449
4120
  * @param {Object} gameMove - Game move object from chess.js
4450
4121
  * @returns {boolean} True if move is castle
4451
4122
  */
4452
4123
  isCastle(gameMove) {
4453
- return gameMove && (gameMove.isKingsideCastle() || gameMove.isQueensideCastle());
4124
+ return gameMove && (gameMove.isKingsideCastle && gameMove.isKingsideCastle() || gameMove.isQueensideCastle && gameMove.isQueensideCastle());
4454
4125
  }
4455
4126
 
4456
4127
  /**
4457
- * Gets the rook move for a castle move
4128
+ * Get the rook move for a castle move
4458
4129
  * @param {Object} gameMove - Game move object from chess.js
4459
4130
  * @returns {Object|null} Rook move object or null if not castle
4460
4131
  */
4461
4132
  getCastleRookMove(gameMove) {
4462
- if (!this.isCastle(gameMove)) {
4463
- return null;
4464
- }
4465
-
4133
+ if (!this.isCastle(gameMove)) return null;
4466
4134
  const isKingSide = gameMove.isKingsideCastle();
4467
4135
  const isWhite = gameMove.color === 'w';
4468
-
4469
4136
  if (isKingSide) {
4470
- // King side castle
4471
- if (isWhite) {
4472
- return { from: 'h1', to: 'f1' };
4473
- } else {
4474
- return { from: 'h8', to: 'f8' };
4475
- }
4137
+ return isWhite ? { from: 'h1', to: 'f1' } : { from: 'h8', to: 'f8' };
4476
4138
  } else {
4477
- // Queen side castle
4478
- if (isWhite) {
4479
- return { from: 'a1', to: 'd1' };
4480
- } else {
4481
- return { from: 'a8', to: 'd8' };
4482
- }
4139
+ return isWhite ? { from: 'a1', to: 'd1' } : { from: 'a8', to: 'd8' };
4483
4140
  }
4484
4141
  }
4485
4142
 
4486
4143
  /**
4487
- * Checks if a move is en passant
4144
+ * Check if a move is en passant
4488
4145
  * @param {Object} gameMove - Game move object from chess.js
4489
4146
  * @returns {boolean} True if move is en passant
4490
4147
  */
4491
4148
  isEnPassant(gameMove) {
4492
- return gameMove && gameMove.isEnPassant();
4149
+ return gameMove && gameMove.isEnPassant && gameMove.isEnPassant();
4493
4150
  }
4494
4151
 
4495
4152
  /**
4496
- * Gets the captured pawn square for en passant
4153
+ * Get the captured pawn square for en passant
4497
4154
  * @param {Object} gameMove - Game move object from chess.js
4498
4155
  * @returns {string|null} Square of captured pawn or null if not en passant
4499
4156
  */
4500
4157
  getEnPassantCapturedSquare(gameMove) {
4501
- if (!this.isEnPassant(gameMove)) {
4502
- return null;
4503
- }
4504
-
4158
+ if (!this.isEnPassant(gameMove)) return null;
4505
4159
  const toSquare = gameMove.to;
4506
4160
  const rank = parseInt(toSquare[1]);
4507
4161
  const file = toSquare[0];
4508
-
4509
- // The captured pawn is on the same file but different rank
4510
4162
  if (gameMove.color === 'w') {
4511
- // White captures black pawn one rank below
4512
4163
  return file + (rank - 1);
4513
4164
  } else {
4514
- // Black captures white pawn one rank above
4515
4165
  return file + (rank + 1);
4516
4166
  }
4517
4167
  }
4518
4168
 
4519
4169
  /**
4520
- * Clears the moves cache
4170
+ * Clear the moves cache
4521
4171
  */
4522
4172
  clearCache() {
4523
4173
  this._movesCache.clear();
@@ -4528,7 +4178,7 @@ var Chessboard = (function (exports) {
4528
4178
  }
4529
4179
 
4530
4180
  /**
4531
- * Cleans up resources
4181
+ * Clean up resources
4532
4182
  */
4533
4183
  destroy() {
4534
4184
  this.clearCache();
@@ -4537,7 +4187,7 @@ var Chessboard = (function (exports) {
4537
4187
  }
4538
4188
 
4539
4189
  /**
4540
- * Service for managing chess pieces and their operations
4190
+ * PieceService - Handles chess piece management and operations
4541
4191
  * @module services/PieceService
4542
4192
  * @since 2.0.0
4543
4193
  */
@@ -4545,11 +4195,11 @@ var Chessboard = (function (exports) {
4545
4195
 
4546
4196
  /**
4547
4197
  * Service responsible for piece management and operations
4548
- * @class
4198
+ * @class PieceService
4549
4199
  */
4550
4200
  class PieceService {
4551
4201
  /**
4552
- * Creates a new PieceService instance
4202
+ * Create a new PieceService instance
4553
4203
  * @param {ChessboardConfig} config - Board configuration
4554
4204
  */
4555
4205
  constructor(config) {
@@ -4557,14 +4207,13 @@ var Chessboard = (function (exports) {
4557
4207
  }
4558
4208
 
4559
4209
  /**
4560
- * Gets the path to a piece asset
4210
+ * Get the path to a piece asset
4561
4211
  * @param {string} piece - Piece identifier (e.g., 'wK', 'bP')
4562
4212
  * @returns {string} Path to piece asset
4563
4213
  * @throws {ValidationError} When piecesPath configuration is invalid
4564
4214
  */
4565
4215
  getPiecePath(piece) {
4566
4216
  const { piecesPath } = this.config;
4567
-
4568
4217
  if (typeof piecesPath === 'string') {
4569
4218
  return `${piecesPath}/${piece}.svg`;
4570
4219
  } else if (typeof piecesPath === 'object' && piecesPath !== null) {
@@ -4577,7 +4226,7 @@ var Chessboard = (function (exports) {
4577
4226
  }
4578
4227
 
4579
4228
  /**
4580
- * Converts various piece formats to a Piece instance
4229
+ * Convert various piece formats to a Piece instance
4581
4230
  * @param {string|Piece} piece - Piece in various formats
4582
4231
  * @returns {Piece} Piece instance
4583
4232
  * @throws {PieceError} When piece format is invalid
@@ -4586,54 +4235,41 @@ var Chessboard = (function (exports) {
4586
4235
  if (piece instanceof Piece) {
4587
4236
  return piece;
4588
4237
  }
4589
-
4590
4238
  if (typeof piece === 'string' && piece.length === 2) {
4591
4239
  const [first, second] = piece.split('');
4592
4240
  let type, color;
4593
-
4594
- // Check format: [type][color] (e.g., 'pw')
4595
4241
  if (PIECE_TYPES.includes(first.toLowerCase()) && PIECE_COLORS.includes(second)) {
4596
4242
  type = first.toLowerCase();
4597
4243
  color = second;
4598
- }
4599
- // Check format: [color][type] (e.g., 'wP')
4600
- else if (PIECE_COLORS.includes(first) && PIECE_TYPES.includes(second.toLowerCase())) {
4244
+ } else if (PIECE_COLORS.includes(first) && PIECE_TYPES.includes(second.toLowerCase())) {
4601
4245
  color = first;
4602
4246
  type = second.toLowerCase();
4603
4247
  } else {
4604
4248
  throw new PieceError(ERROR_MESSAGES.invalid_piece + piece, piece);
4605
4249
  }
4606
-
4607
4250
  const piecePath = this.getPiecePath(type + color);
4608
4251
  return new Piece(color, type, piecePath);
4609
4252
  }
4610
-
4611
4253
  throw new PieceError(ERROR_MESSAGES.invalid_piece + piece, piece);
4612
4254
  }
4613
4255
 
4614
4256
  /**
4615
- * Adds a piece to a square with optional fade-in animation
4616
- * @param {Square} square - Target square (oggetto)
4617
- * @param {Piece} piece - Piece to add (oggetto)
4257
+ * Add a piece to a square with optional fade-in animation
4258
+ * @param {Square} square - Target square
4259
+ * @param {Piece} piece - Piece to add
4618
4260
  * @param {boolean} [fade=true] - Whether to fade in the piece
4619
4261
  * @param {Function} dragFunction - Function to handle drag events
4620
4262
  * @param {Function} [callback] - Callback when animation completes
4621
4263
  */
4622
4264
  addPieceOnSquare(square, piece, fade = true, dragFunction, callback) {
4623
- if (!square || !piece) throw new Error('addPieceOnSquare richiede oggetti Square e Piece');
4624
- console.debug(`[PieceService] addPieceOnSquare: ${piece.id} to ${square.id}`);
4265
+ if (!square || !piece) throw new Error('addPieceOnSquare requires Square and Piece objects');
4625
4266
  square.putPiece(piece);
4626
-
4627
- // Imposta sempre il drag (touch e mouse)
4628
4267
  if (dragFunction) {
4629
4268
  piece.setDrag(dragFunction(square, piece));
4630
4269
  }
4631
- // Forza il drag touch se manca (debug/robustezza)
4632
4270
  if (!piece.element.ontouchstart) {
4633
4271
  piece.element.ontouchstart = dragFunction ? dragFunction(square, piece) : () => { };
4634
- console.debug(`[PieceService] Forzato ontouchstart su ${piece.id}`);
4635
4272
  }
4636
-
4637
4273
  if (fade && this.config.fadeTime > 0) {
4638
4274
  piece.fadeIn(
4639
4275
  this.config.fadeTime,
@@ -4644,28 +4280,24 @@ var Chessboard = (function (exports) {
4644
4280
  } else {
4645
4281
  if (callback) callback();
4646
4282
  }
4647
-
4648
4283
  piece.visible();
4649
4284
  }
4650
4285
 
4651
4286
  /**
4652
- * Rimuove un pezzo da una casella
4653
- * @param {Square} square - Oggetto Square
4287
+ * Remove a piece from a square
4288
+ * @param {Square} square - Square object
4654
4289
  * @param {boolean} [fade=true]
4655
4290
  * @param {Function} [callback]
4656
- * @returns {Piece} Il pezzo rimosso
4291
+ * @returns {Piece} The removed piece
4657
4292
  */
4658
4293
  removePieceFromSquare(square, fade = true, callback) {
4659
- if (!square) throw new Error('removePieceFromSquare richiede oggetto Square');
4660
- console.debug(`[PieceService] removePieceFromSquare: ${square.id}`);
4294
+ if (!square) throw new Error('removePieceFromSquare requires a Square object');
4661
4295
  square.check();
4662
-
4663
4296
  const piece = square.piece;
4664
4297
  if (!piece) {
4665
4298
  if (callback) callback();
4666
4299
  throw new PieceError(ERROR_MESSAGES.square_no_piece, null, square.getId());
4667
4300
  }
4668
-
4669
4301
  if (fade && this.config.fadeTime > 0) {
4670
4302
  piece.fadeOut(
4671
4303
  this.config.fadeTime,
@@ -4676,26 +4308,22 @@ var Chessboard = (function (exports) {
4676
4308
  } else {
4677
4309
  if (callback) callback();
4678
4310
  }
4679
-
4680
4311
  square.removePiece();
4681
4312
  return piece;
4682
4313
  }
4683
4314
 
4684
4315
  /**
4685
- * Moves a piece to a new position with animation
4316
+ * Move a piece to a new position with animation
4686
4317
  * @param {Piece} piece - Piece to move
4687
4318
  * @param {Square} targetSquare - Target square
4688
4319
  * @param {number} duration - Animation duration
4689
4320
  * @param {Function} [callback] - Callback function when animation completes
4690
4321
  */
4691
4322
  movePiece(piece, targetSquare, duration, callback) {
4692
- console.debug(`[PieceService] movePiece: ${piece.id} to ${targetSquare.id}`);
4693
4323
  if (!piece || !piece.element) {
4694
- console.warn(`[PieceService] movePiece: piece or element is null, skipping animation`);
4695
4324
  if (callback) callback();
4696
4325
  return;
4697
4326
  }
4698
-
4699
4327
  piece.translate(
4700
4328
  targetSquare,
4701
4329
  duration,
@@ -4706,7 +4334,7 @@ var Chessboard = (function (exports) {
4706
4334
  }
4707
4335
 
4708
4336
  /**
4709
- * Handles piece translation with optional capture
4337
+ * Handle piece translation with optional capture
4710
4338
  * @param {Move} move - Move object containing from/to squares and piece
4711
4339
  * @param {boolean} removeTarget - Whether to remove piece from target square
4712
4340
  * @param {boolean} animate - Whether to animate the move
@@ -4714,54 +4342,37 @@ var Chessboard = (function (exports) {
4714
4342
  * @param {Function} [callback] - Callback function when complete
4715
4343
  */
4716
4344
  translatePiece(move, removeTarget, animate, dragFunction = null, callback = null) {
4717
- console.debug(`[PieceService] translatePiece: ${move.piece.id} from ${move.from.id} to ${move.to.id}`);
4718
4345
  if (!move.piece) {
4719
- console.warn('PieceService.translatePiece: move.piece is null, skipping translation');
4720
4346
  if (callback) callback();
4721
4347
  return;
4722
4348
  }
4723
-
4724
4349
  if (removeTarget) {
4725
- // Deselect the captured piece before removing it
4726
4350
  move.to.deselect();
4727
4351
  this.removePieceFromSquare(move.to, false);
4728
4352
  }
4729
-
4730
4353
  const changeSquareCallback = () => {
4731
- // Check if piece still exists and is on the source square
4732
4354
  if (move.from.piece === move.piece) {
4733
- move.from.removePiece(true); // Preserve the piece when moving
4355
+ move.from.removePiece(true);
4734
4356
  }
4735
-
4736
- // Only put piece if destination square doesn't already have it
4737
4357
  if (move.to.piece !== move.piece) {
4738
4358
  move.to.putPiece(move.piece);
4739
-
4740
- // Re-attach drag handler if provided
4741
4359
  if (dragFunction && this.config.draggable && move.piece.element) {
4742
4360
  move.piece.setDrag(dragFunction(move.to, move.piece));
4743
4361
  }
4744
4362
  }
4745
-
4746
4363
  if (callback) callback();
4747
4364
  };
4748
-
4749
- // Check if piece is currently being dragged
4750
4365
  const isDragging = move.piece.element && move.piece.element.classList.contains('dragging');
4751
-
4752
4366
  if (isDragging) {
4753
- // If piece is being dragged, don't animate - just move it immediately
4754
- // The piece is already visually in the correct position from the drag
4755
4367
  changeSquareCallback();
4756
4368
  } else {
4757
- // Normal animation
4758
4369
  const duration = animate ? this.config.moveTime : 0;
4759
4370
  this.movePiece(move.piece, move.to, duration, changeSquareCallback);
4760
4371
  }
4761
4372
  }
4762
4373
 
4763
4374
  /**
4764
- * Snaps a piece back to its original position
4375
+ * Snap a piece back to its original position
4765
4376
  * @param {Square} square - Square containing the piece
4766
4377
  * @param {boolean} [animate=true] - Whether to animate the snapback
4767
4378
  */
@@ -4771,7 +4382,6 @@ var Chessboard = (function (exports) {
4771
4382
  }
4772
4383
  const piece = square.piece;
4773
4384
  const duration = animate ? this.config.snapbackTime : 0;
4774
- console.debug(`[PieceService] snapbackPiece: ${piece.id} on ${square.id}`);
4775
4385
  piece.translate(
4776
4386
  square,
4777
4387
  duration,
@@ -4781,7 +4391,7 @@ var Chessboard = (function (exports) {
4781
4391
  }
4782
4392
 
4783
4393
  /**
4784
- * Centers a piece in its square with animation (after successful drop)
4394
+ * Center a piece in its square with animation (after successful drop)
4785
4395
  * @param {Square} square - Square containing the piece to center
4786
4396
  * @param {boolean} animate - Whether to animate the centering
4787
4397
  */
@@ -4791,14 +4401,12 @@ var Chessboard = (function (exports) {
4791
4401
  }
4792
4402
  const piece = square.piece;
4793
4403
  const duration = animate ? this.config.dropCenterTime : 0;
4794
- console.debug(`[PieceService] centerPiece: ${piece.id} on ${square.id}`);
4795
4404
  piece.translate(
4796
4405
  square,
4797
4406
  duration,
4798
4407
  this._getTransitionTimingFunction(),
4799
4408
  this.config.dropCenterAnimation,
4800
4409
  () => {
4801
- // After animation, reset all drag-related styles
4802
4410
  if (!piece.element) return;
4803
4411
  piece.element.style.position = '';
4804
4412
  piece.element.style.left = '';
@@ -4810,14 +4418,13 @@ var Chessboard = (function (exports) {
4810
4418
  }
4811
4419
 
4812
4420
  /**
4813
- * Gets the transition timing function for animations
4421
+ * Get the transition timing function for animations
4814
4422
  * @private
4815
4423
  * @returns {Function} Timing function
4816
4424
  */
4817
4425
  _getTransitionTimingFunction() {
4818
4426
  return (elapsed, duration, type = 'ease') => {
4819
4427
  const x = elapsed / duration;
4820
-
4821
4428
  switch (type) {
4822
4429
  case 'linear':
4823
4430
  return x;
@@ -4836,7 +4443,7 @@ var Chessboard = (function (exports) {
4836
4443
  }
4837
4444
 
4838
4445
  /**
4839
- * Cleans up resources
4446
+ * Clean up resources
4840
4447
  */
4841
4448
  destroy() {
4842
4449
  // Cleanup any cached pieces or references
@@ -6827,7 +6434,7 @@ var Chessboard = (function (exports) {
6827
6434
  }
6828
6435
 
6829
6436
  /**
6830
- * Service for managing chess positions and FEN conversion
6437
+ * PositionService - Handles chess position management and FEN conversion
6831
6438
  * @module services/PositionService
6832
6439
  * @since 2.0.0
6833
6440
  */
@@ -6835,11 +6442,11 @@ var Chessboard = (function (exports) {
6835
6442
 
6836
6443
  /**
6837
6444
  * Service responsible for position management and FEN operations
6838
- * @class
6445
+ * @class PositionService
6839
6446
  */
6840
6447
  class PositionService {
6841
6448
  /**
6842
- * Creates a new PositionService instance
6449
+ * Create a new PositionService instance
6843
6450
  * @param {ChessboardConfig} config - Board configuration
6844
6451
  */
6845
6452
  constructor(config) {
@@ -6848,7 +6455,7 @@ var Chessboard = (function (exports) {
6848
6455
  }
6849
6456
 
6850
6457
  /**
6851
- * Converts various position formats to FEN string
6458
+ * Convert various position formats to FEN string
6852
6459
  * @param {string|Object} position - Position in various formats
6853
6460
  * @returns {string} FEN string representation
6854
6461
  * @throws {ValidationError} When position format is invalid
@@ -6864,7 +6471,7 @@ var Chessboard = (function (exports) {
6864
6471
  }
6865
6472
 
6866
6473
  /**
6867
- * Converts string position to FEN
6474
+ * Convert string position to FEN
6868
6475
  * @private
6869
6476
  * @param {string} position - String position
6870
6477
  * @returns {string} FEN string
@@ -6873,35 +6480,29 @@ var Chessboard = (function (exports) {
6873
6480
  if (position === 'start') {
6874
6481
  return DEFAULT_STARTING_POSITION;
6875
6482
  }
6876
-
6877
6483
  if (this.validateFen(position)) {
6878
6484
  return position;
6879
6485
  }
6880
-
6881
6486
  if (STANDARD_POSITIONS[position]) {
6882
6487
  return STANDARD_POSITIONS[position];
6883
6488
  }
6884
-
6885
6489
  throw new ValidationError(ERROR_MESSAGES.invalid_position + position, 'position', position);
6886
6490
  }
6887
6491
 
6888
6492
  /**
6889
- * Converts object position to FEN
6493
+ * Convert object position to FEN
6890
6494
  * @private
6891
6495
  * @param {Object} position - Object with square->piece mapping
6892
6496
  * @returns {string} FEN string
6893
6497
  */
6894
6498
  _convertObjectPosition(position) {
6895
6499
  const parts = [];
6896
-
6897
6500
  for (let row = 0; row < 8; row++) {
6898
6501
  const rowParts = [];
6899
6502
  let empty = 0;
6900
-
6901
6503
  for (let col = 0; col < 8; col++) {
6902
6504
  const square = this._getSquareID(row, col);
6903
6505
  const piece = position[square];
6904
-
6905
6506
  if (piece) {
6906
6507
  if (empty > 0) {
6907
6508
  rowParts.push(empty);
@@ -6914,25 +6515,21 @@ var Chessboard = (function (exports) {
6914
6515
  empty++;
6915
6516
  }
6916
6517
  }
6917
-
6918
6518
  if (empty > 0) {
6919
6519
  rowParts.push(empty);
6920
6520
  }
6921
-
6922
6521
  parts.push(rowParts.join(''));
6923
6522
  }
6924
-
6925
6523
  return parts.join('/') + ' w KQkq - 0 1';
6926
6524
  }
6927
6525
 
6928
6526
  /**
6929
- * Sets up the game with the given position
6527
+ * Set up the game with the given position
6930
6528
  * @param {string|Object} position - Position to set
6931
6529
  * @param {Object} [options] - Additional options for game setup
6932
6530
  */
6933
6531
  setGame(position, options = {}) {
6934
6532
  const fen = this.convertFen(position);
6935
-
6936
6533
  if (this.game) {
6937
6534
  this.game.load(fen, options);
6938
6535
  } else {
@@ -6941,7 +6538,7 @@ var Chessboard = (function (exports) {
6941
6538
  }
6942
6539
 
6943
6540
  /**
6944
- * Gets the current game instance
6541
+ * Get the current game instance
6945
6542
  * @returns {Chess} Current chess.js game instance
6946
6543
  */
6947
6544
  getGame() {
@@ -6949,7 +6546,7 @@ var Chessboard = (function (exports) {
6949
6546
  }
6950
6547
 
6951
6548
  /**
6952
- * Validates a FEN string
6549
+ * Validate a FEN string
6953
6550
  * @param {string} fen - FEN string to validate
6954
6551
  * @returns {boolean} True if valid, false otherwise
6955
6552
  */
@@ -6958,7 +6555,7 @@ var Chessboard = (function (exports) {
6958
6555
  }
6959
6556
 
6960
6557
  /**
6961
- * Gets piece information for a specific square
6558
+ * Get piece information for a specific square
6962
6559
  * @param {string} squareId - Square identifier
6963
6560
  * @returns {string|null} Piece ID or null if no piece
6964
6561
  */
@@ -6969,7 +6566,7 @@ var Chessboard = (function (exports) {
6969
6566
  }
6970
6567
 
6971
6568
  /**
6972
- * Checks if a specific piece is on a specific square
6569
+ * Check if a specific piece is on a specific square
6973
6570
  * @param {string} piece - Piece ID to check
6974
6571
  * @param {string} square - Square to check
6975
6572
  * @returns {boolean} True if piece is on square
@@ -6979,7 +6576,7 @@ var Chessboard = (function (exports) {
6979
6576
  }
6980
6577
 
6981
6578
  /**
6982
- * Converts board coordinates to square ID
6579
+ * Convert board coordinates to square ID
6983
6580
  * @private
6984
6581
  * @param {number} row - Row index (0-7)
6985
6582
  * @param {number} col - Column index (0-7)
@@ -6988,7 +6585,6 @@ var Chessboard = (function (exports) {
6988
6585
  _getSquareID(row, col) {
6989
6586
  row = parseInt(row);
6990
6587
  col = parseInt(col);
6991
-
6992
6588
  if (this.config.orientation === 'w') {
6993
6589
  row = 8 - row;
6994
6590
  col = col + 1;
@@ -6996,13 +6592,12 @@ var Chessboard = (function (exports) {
6996
6592
  row = row + 1;
6997
6593
  col = 8 - col;
6998
6594
  }
6999
-
7000
6595
  const letter = BOARD_LETTERS[col - 1];
7001
6596
  return letter + row;
7002
6597
  }
7003
6598
 
7004
6599
  /**
7005
- * Changes the turn in a FEN string
6600
+ * Change the turn in a FEN string
7006
6601
  * @param {string} fen - Original FEN string
7007
6602
  * @param {string} color - New turn color ('w' or 'b')
7008
6603
  * @returns {string} Modified FEN string
@@ -7014,35 +6609,33 @@ var Chessboard = (function (exports) {
7014
6609
  }
7015
6610
 
7016
6611
  /**
7017
- * Gets the current position as an object
6612
+ * Get the current position as an object
7018
6613
  * @returns {Object} Position object with piece placements
7019
6614
  */
7020
6615
  getPosition() {
7021
6616
  const position = {};
7022
6617
  const game = this.getGame();
7023
-
7024
- // Convert chess.js board to position object
7025
- const squares = ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1',
7026
- 'a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2',
7027
- 'a3', 'b3', 'c3', 'd3', 'e3', 'f3', 'g3', 'h3',
7028
- 'a4', 'b4', 'c4', 'd4', 'e4', 'f4', 'g4', 'h4',
7029
- 'a5', 'b5', 'c5', 'd5', 'e5', 'f5', 'g5', 'h5',
7030
- 'a6', 'b6', 'c6', 'd6', 'e6', 'f6', 'g6', 'h6',
7031
- 'a7', 'b7', 'c7', 'd7', 'e7', 'f7', 'g7', 'h7',
7032
- 'a8', 'b8', 'c8', 'd8', 'e8', 'f8', 'g8', 'h8'];
7033
-
6618
+ const squares = [
6619
+ 'a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1',
6620
+ 'a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2',
6621
+ 'a3', 'b3', 'c3', 'd3', 'e3', 'f3', 'g3', 'h3',
6622
+ 'a4', 'b4', 'c4', 'd4', 'e4', 'f4', 'g4', 'h4',
6623
+ 'a5', 'b5', 'c5', 'd5', 'e5', 'f5', 'g5', 'h5',
6624
+ 'a6', 'b6', 'c6', 'd6', 'e6', 'f6', 'g6', 'h6',
6625
+ 'a7', 'b7', 'c7', 'd7', 'e7', 'f7', 'g7', 'h7',
6626
+ 'a8', 'b8', 'c8', 'd8', 'e8', 'f8', 'g8', 'h8'
6627
+ ];
7034
6628
  for (const square of squares) {
7035
6629
  const piece = game.get(square);
7036
6630
  if (piece) {
7037
6631
  position[square] = piece.type + piece.color;
7038
6632
  }
7039
6633
  }
7040
-
7041
6634
  return position;
7042
6635
  }
7043
6636
 
7044
6637
  /**
7045
- * Toggles the turn in a FEN string
6638
+ * Toggle the turn in a FEN string
7046
6639
  * @param {string} fen - Original FEN string
7047
6640
  * @returns {string} Modified FEN string
7048
6641
  */
@@ -7053,7 +6646,7 @@ var Chessboard = (function (exports) {
7053
6646
  }
7054
6647
 
7055
6648
  /**
7056
- * Cleans up resources
6649
+ * Clean up resources
7057
6650
  */
7058
6651
  destroy() {
7059
6652
  this.game = null;
@@ -7061,7 +6654,8 @@ var Chessboard = (function (exports) {
7061
6654
  }
7062
6655
 
7063
6656
  /**
7064
- * Main Chessboard class - Orchestrates all services and components
6657
+ * Chessboard - Main class orchestrating all services and components
6658
+ * Implements the Facade pattern for the chessboard API
7065
6659
  * @module core/Chessboard
7066
6660
  * @since 2.0.0
7067
6661
  */
@@ -7069,12 +6663,11 @@ var Chessboard = (function (exports) {
7069
6663
 
7070
6664
  /**
7071
6665
  * Main Chessboard class responsible for coordinating all services
7072
- * Implements the Facade pattern to provide a unified interface
7073
- * @class
6666
+ * @class Chessboard
7074
6667
  */
7075
6668
  let Chessboard$1 = class Chessboard {
7076
6669
  /**
7077
- * Creates a new Chessboard instance
6670
+ * Create a new Chessboard instance
7078
6671
  * @param {Object} config - Configuration object
7079
6672
  * @throws {ConfigurationError} If configuration is invalid
7080
6673
  */
@@ -8334,7 +7927,7 @@ var Chessboard = (function (exports) {
8334
7927
  getPiece(square) {
8335
7928
  // Sempre leggi lo stato aggiornato dal boardService
8336
7929
  const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8337
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[getPiece] Parametro square non valido');
7930
+ if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[getPiece] Parameter square is invalid');
8338
7931
  // Forza sync prima di leggere
8339
7932
  this._updateBoardPieces(false, false);
8340
7933
  const piece = squareObj.piece;
@@ -8368,9 +7961,9 @@ var Chessboard = (function (exports) {
8368
7961
  }
8369
7962
  }
8370
7963
  const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8371
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[putPiece] Parametro square non valido');
7964
+ if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[putPiece] Parameter square is invalid');
8372
7965
  const pieceObj = this.pieceService.convertPiece(pieceStr);
8373
- if (!pieceObj || typeof pieceObj !== 'object' || !('type' in pieceObj)) throw new Error('[putPiece] Parametro piece non valido');
7966
+ if (!pieceObj || typeof pieceObj !== 'object' || !('type' in pieceObj)) throw new Error('[putPiece] Parameter piece is invalid');
8374
7967
  // Aggiorna solo il motore chess.js
8375
7968
  const chessJsPiece = { type: pieceObj.type, color: pieceObj.color };
8376
7969
  const game = this.positionService.getGame();
@@ -8391,7 +7984,7 @@ var Chessboard = (function (exports) {
8391
7984
  removePiece(square, opts = {}) {
8392
7985
  const animate = opts.animate !== undefined ? opts.animate : true;
8393
7986
  const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8394
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[removePiece] Parametro square non valido');
7987
+ if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[removePiece] Parameter square is invalid');
8395
7988
  // Aggiorna solo il motore chess.js
8396
7989
  const game = this.positionService.getGame();
8397
7990
  game.remove(squareObj.id);
@@ -8467,7 +8060,7 @@ var Chessboard = (function (exports) {
8467
8060
  highlight(square, opts = {}) {
8468
8061
  // API: accetta id, converte subito in oggetto
8469
8062
  const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8470
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[highlight] Parametro square non valido');
8063
+ if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[highlight] Parameter square is invalid');
8471
8064
  if (this.boardService && this.boardService.highlightSquare) {
8472
8065
  this.boardService.highlightSquare(squareObj, opts);
8473
8066
  } else if (this.eventService && this.eventService.highlightSquare) {
@@ -8482,7 +8075,7 @@ var Chessboard = (function (exports) {
8482
8075
  dehighlight(square, opts = {}) {
8483
8076
  // API: accetta id, converte subito in oggetto
8484
8077
  const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
8485
- if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[dehighlight] Parametro square non valido');
8078
+ if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[dehighlight] Parameter square is invalid');
8486
8079
  if (this.boardService && this.boardService.dehighlightSquare) {
8487
8080
  this.boardService.dehighlightSquare(squareObj, opts);
8488
8081
  } else if (this.eventService && this.eventService.dehighlightSquare) {
@@ -8551,9 +8144,47 @@ var Chessboard = (function (exports) {
8551
8144
 
8552
8145
  // --- LIFECYCLE ---
8553
8146
  /**
8554
- * Destroy the board and cleanup
8147
+ * Destroy the board and cleanup all resources
8555
8148
  */
8556
- destroy() { /* TODO: robust destroy logic */ }
8149
+ destroy() {
8150
+ // Remove event listeners
8151
+ if (this.eventService && typeof this.eventService.removeListeners === 'function') {
8152
+ this.eventService.removeListeners();
8153
+ }
8154
+ // Clear timeouts
8155
+ if (this._updateTimeout) {
8156
+ clearTimeout(this._updateTimeout);
8157
+ this._updateTimeout = null;
8158
+ }
8159
+ // Destroy all services
8160
+ [
8161
+ 'validationService',
8162
+ 'coordinateService',
8163
+ 'positionService',
8164
+ 'boardService',
8165
+ 'pieceService',
8166
+ 'animationService',
8167
+ 'moveService',
8168
+ 'eventService',
8169
+ ].forEach(service => {
8170
+ if (this[service] && typeof this[service].destroy === 'function') {
8171
+ this[service].destroy();
8172
+ }
8173
+ this[service] = null;
8174
+ });
8175
+ // Nullify DOM references
8176
+ this._performanceMonitor = null;
8177
+ this._boundUpdateBoardPieces = null;
8178
+ this._boundOnSquareClick = null;
8179
+ this._boundOnPieceHover = null;
8180
+ this._boundOnPieceLeave = null;
8181
+ this._undoneMoves = null;
8182
+ // Remove board DOM if possible
8183
+ const boardContainer = document.getElementById(this.config?.id_div);
8184
+ if (boardContainer) {
8185
+ boardContainer.innerHTML = '';
8186
+ }
8187
+ }
8557
8188
  /**
8558
8189
  * Rebuild the board
8559
8190
  */