@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.
- package/dist/chessboard.cjs.js +437 -806
- package/dist/chessboard.css +8 -0
- package/dist/chessboard.esm.js +437 -806
- package/dist/chessboard.iife.js +437 -806
- package/dist/chessboard.umd.js +437 -806
- package/package.json +1 -1
- package/src/core/Chessboard.js +50 -12
- package/src/services/AnimationService.js +57 -82
- package/src/services/BoardService.js +40 -34
- package/src/services/CoordinateService.js +25 -84
- package/src/services/EventService.js +41 -104
- package/src/services/MoveService.js +63 -266
- package/src/services/PieceService.js +21 -64
- package/src/services/PositionService.js +26 -40
- package/src/services/ValidationService.js +54 -118
- package/src/services/index.js +1 -0
- package/src/styles/board.css +8 -0
- package/src/utils/listenerManager.js +62 -0
package/dist/chessboard.iife.js
CHANGED
|
@@ -348,7 +348,7 @@ var Chessboard = (function (exports) {
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
/**
|
|
351
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
430
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
456
|
-
const
|
|
457
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
540
|
-
halfmove >= 0 && fullmove >= 1;
|
|
507
|
+
return !isNaN(halfmove) && !isNaN(fullmove) &&
|
|
508
|
+
halfmove >= 0 && fullmove >= 1;
|
|
541
509
|
}
|
|
542
510
|
|
|
543
511
|
/**
|
|
544
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
658
|
-
|
|
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
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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
|
-
|
|
826
|
-
|
|
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
|
-
|
|
847
|
-
|
|
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
|
-
*
|
|
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
|
|
1999
|
-
* @class
|
|
1934
|
+
* Service responsabile del coordinamento delle animazioni e delle transizioni
|
|
1935
|
+
* @class AnimationService
|
|
2000
1936
|
*/
|
|
2001
1937
|
class AnimationService {
|
|
2002
1938
|
/**
|
|
2003
|
-
*
|
|
2004
|
-
* @param {
|
|
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
|
-
*
|
|
2014
|
-
* @param {string} [type='ease'] -
|
|
2015
|
-
* @returns {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
|
-
|
|
2024
|
-
case 'ease':
|
|
2025
|
-
|
|
2026
|
-
case 'ease-in':
|
|
2027
|
-
|
|
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
|
-
*
|
|
2040
|
-
* @param {HTMLElement} element -
|
|
2041
|
-
* @param {Object} properties -
|
|
2042
|
-
* @param {number} duration -
|
|
2043
|
-
* @param {string} [easing='ease'] -
|
|
2044
|
-
* @param {Function} [callback] - Callback
|
|
2045
|
-
* @returns {number}
|
|
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
|
-
*
|
|
2087
|
-
* @param {number} animationId -
|
|
2013
|
+
* Annulla una animazione attiva
|
|
2014
|
+
* @param {number} animationId - ID dell'animazione
|
|
2088
2015
|
*/
|
|
2089
2016
|
cancel(animationId) {
|
|
2090
|
-
|
|
2091
|
-
this.activeAnimations.delete(animationId);
|
|
2092
|
-
}
|
|
2017
|
+
this.activeAnimations.delete(animationId);
|
|
2093
2018
|
}
|
|
2094
2019
|
|
|
2095
2020
|
/**
|
|
2096
|
-
*
|
|
2021
|
+
* Annulla tutte le animazioni attive
|
|
2097
2022
|
*/
|
|
2098
2023
|
cancelAll() {
|
|
2099
2024
|
this.activeAnimations.clear();
|
|
2100
2025
|
}
|
|
2101
2026
|
|
|
2102
2027
|
/**
|
|
2103
|
-
*
|
|
2028
|
+
* Ottiene il valore iniziale di una proprietà
|
|
2104
2029
|
* @private
|
|
2105
|
-
* @param {HTMLElement} element
|
|
2106
|
-
* @param {string} property
|
|
2107
|
-
* @returns {number}
|
|
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
|
-
|
|
2114
|
-
case '
|
|
2115
|
-
|
|
2116
|
-
|
|
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
|
-
*
|
|
2046
|
+
* Interpola tra due valori
|
|
2127
2047
|
* @private
|
|
2128
|
-
* @param {number} start
|
|
2129
|
-
* @param {number} end
|
|
2130
|
-
* @param {number} progress
|
|
2131
|
-
* @returns {number}
|
|
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
|
-
*
|
|
2058
|
+
* Applica il valore animato all'elemento
|
|
2139
2059
|
* @private
|
|
2140
|
-
* @param {HTMLElement} element
|
|
2141
|
-
* @param {string} property
|
|
2142
|
-
* @param {number} value
|
|
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
|
-
|
|
2149
|
-
|
|
2150
|
-
case '
|
|
2151
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
2186
|
-
* @param {
|
|
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
|
-
*
|
|
2196
|
-
* @throws {DOMError}
|
|
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 =
|
|
2118
|
+
this.element.className = 'board';
|
|
2208
2119
|
}
|
|
2209
2120
|
|
|
2210
2121
|
/**
|
|
2211
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
2158
|
+
* Resize the board to the specified size
|
|
2251
2159
|
* @param {number|string} value - Size in pixels or 'auto'
|
|
2252
|
-
* @throws {ValidationError}
|
|
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
|
-
*
|
|
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;
|
|
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
|
-
*
|
|
2286
|
-
* @param {string} squareId - Square identifier
|
|
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
|
|
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
|
|
2300
|
-
//
|
|
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
|
-
*
|
|
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
|
|
2309
|
-
//
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
2358
|
-
* @param {
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
2582
|
-
* @param {string} orientation - 'w'
|
|
2583
|
-
* @throws {ValidationError}
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
//
|
|
3105
|
-
this.
|
|
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
|
-
//
|
|
3116
|
-
this.
|
|
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
|
-
//
|
|
3206
|
-
square.element
|
|
3207
|
-
square.element
|
|
3208
|
-
square.element
|
|
3209
|
-
|
|
3210
|
-
square.element
|
|
3211
|
-
square.element
|
|
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
|
-
|
|
3288
|
-
const
|
|
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
|
-
|
|
3311
|
-
|
|
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 (
|
|
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
|
-
|
|
3378
|
-
|
|
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
|
-
//
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
*
|
|
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
|
-
*
|
|
3975
|
-
* @param {
|
|
3976
|
-
* @param {
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
4114
|
-
* @param {Move} move - Move to execute (
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
4137
|
-
* @param {Move} 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
|
|
4142
|
-
|
|
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
|
-
|
|
4164
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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}
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
4432
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
4616
|
-
* @param {Square} square - Target square
|
|
4617
|
-
* @param {Piece} piece - Piece to add
|
|
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
|
|
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
|
-
*
|
|
4653
|
-
* @param {Square} 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}
|
|
4291
|
+
* @returns {Piece} The removed piece
|
|
4657
4292
|
*/
|
|
4658
4293
|
removePieceFromSquare(square, fade = true, callback) {
|
|
4659
|
-
if (!square) throw new Error('removePieceFromSquare
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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);
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
7073
|
-
* @class
|
|
6666
|
+
* @class Chessboard
|
|
7074
6667
|
*/
|
|
7075
6668
|
let Chessboard$1 = class Chessboard {
|
|
7076
6669
|
/**
|
|
7077
|
-
*
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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() {
|
|
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
|
*/
|