@alepot55/chessboardjs 2.3.7 → 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 +1171 -1204
- package/dist/chessboard.css +8 -0
- package/dist/chessboard.esm.js +1171 -1204
- package/dist/chessboard.iife.js +1171 -1204
- package/dist/chessboard.umd.js +1171 -1204
- package/package.json +1 -1
- package/src/components/Piece.js +1 -13
- package/src/core/Chessboard.js +700 -325
- package/src/core/ChessboardConfig.js +18 -33
- package/src/core/ChessboardFactory.js +0 -5
- package/src/core/index.js +34 -15
- package/src/errors/messages.js +0 -1
- package/src/index.js +13 -13
- 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 -131
- package/src/services/index.js +1 -0
- package/src/styles/board.css +8 -0
- package/src/utils/listenerManager.js +62 -0
- package/src/utils/validation.js +1 -5
package/dist/chessboard.iife.js
CHANGED
|
@@ -127,7 +127,6 @@ var Chessboard = (function (exports) {
|
|
|
127
127
|
invalid_fadeTime: 'Invalid fadeTime: ',
|
|
128
128
|
invalid_fadeAnimation: 'Invalid fadeAnimation: ',
|
|
129
129
|
invalid_ratio: 'Invalid ratio: ',
|
|
130
|
-
invalid_animationStyle: 'Invalid animationStyle: ',
|
|
131
130
|
animation_failed: 'Animation failed: ',
|
|
132
131
|
|
|
133
132
|
// Event handlers
|
|
@@ -349,7 +348,7 @@ var Chessboard = (function (exports) {
|
|
|
349
348
|
}
|
|
350
349
|
|
|
351
350
|
/**
|
|
352
|
-
*
|
|
351
|
+
* ValidationService - Handles input validation and data sanitization
|
|
353
352
|
* @module services/ValidationService
|
|
354
353
|
* @since 2.0.0
|
|
355
354
|
*/
|
|
@@ -379,7 +378,6 @@ var Chessboard = (function (exports) {
|
|
|
379
378
|
movableColors: ['w', 'b', 'white', 'black', 'both', 'none'],
|
|
380
379
|
dropOffBoard: ['snapback', 'trash'],
|
|
381
380
|
easingTypes: ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'],
|
|
382
|
-
animationStyles: ['sequential', 'simultaneous'],
|
|
383
381
|
modes: ['normal', 'creative', 'analysis'],
|
|
384
382
|
promotionPieces: ['q', 'r', 'b', 'n', 'Q', 'R', 'B', 'N']
|
|
385
383
|
});
|
|
@@ -399,45 +397,39 @@ var Chessboard = (function (exports) {
|
|
|
399
397
|
/**
|
|
400
398
|
* Service responsible for validating inputs and data
|
|
401
399
|
* Implements caching for performance optimization
|
|
402
|
-
* @class
|
|
400
|
+
* @class ValidationService
|
|
403
401
|
*/
|
|
404
402
|
class ValidationService {
|
|
405
403
|
/**
|
|
406
|
-
*
|
|
404
|
+
* Create a new ValidationService instance
|
|
407
405
|
*/
|
|
408
406
|
constructor() {
|
|
409
|
-
// Cache for validation results to improve performance
|
|
410
407
|
this._validationCache = new Map();
|
|
411
408
|
this._cacheMaxSize = 1000;
|
|
412
|
-
|
|
413
|
-
// Compile patterns for reuse
|
|
414
409
|
this._patterns = VALIDATION_PATTERNS;
|
|
415
410
|
this._validValues = VALID_VALUES;
|
|
416
411
|
this._constraints = SIZE_CONSTRAINTS;
|
|
417
412
|
}
|
|
418
413
|
|
|
419
414
|
/**
|
|
420
|
-
*
|
|
415
|
+
* Validate a square identifier with caching
|
|
421
416
|
* @param {string} square - Square to validate (e.g., 'e4')
|
|
422
417
|
* @returns {boolean} True if valid
|
|
423
418
|
*/
|
|
424
419
|
isValidSquare(square) {
|
|
425
420
|
const cacheKey = `square:${square}`;
|
|
426
|
-
|
|
427
421
|
if (this._validationCache.has(cacheKey)) {
|
|
428
422
|
return this._validationCache.get(cacheKey);
|
|
429
423
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
this._patterns.square.test(square);
|
|
434
|
-
|
|
424
|
+
const isValid = typeof square === 'string' &&
|
|
425
|
+
square.length === 2 &&
|
|
426
|
+
this._patterns.square.test(square);
|
|
435
427
|
this._cacheValidationResult(cacheKey, isValid);
|
|
436
428
|
return isValid;
|
|
437
429
|
}
|
|
438
430
|
|
|
439
431
|
/**
|
|
440
|
-
*
|
|
432
|
+
* Validate a piece identifier with enhanced format support
|
|
441
433
|
* @param {string} piece - Piece to validate (e.g., 'wK', 'bp')
|
|
442
434
|
* @returns {boolean} True if valid
|
|
443
435
|
*/
|
|
@@ -445,28 +437,22 @@ var Chessboard = (function (exports) {
|
|
|
445
437
|
if (typeof piece !== 'string' || piece.length !== 2) {
|
|
446
438
|
return false;
|
|
447
439
|
}
|
|
448
|
-
|
|
449
440
|
const cacheKey = `piece:${piece}`;
|
|
450
|
-
|
|
451
441
|
if (this._validationCache.has(cacheKey)) {
|
|
452
442
|
return this._validationCache.get(cacheKey);
|
|
453
443
|
}
|
|
454
|
-
|
|
455
444
|
const [first, second] = piece.split('');
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
const format2 = PIECE_COLORS.includes(first) &&
|
|
461
|
-
PIECE_TYPES.includes(second.toLowerCase());
|
|
462
|
-
|
|
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());
|
|
463
449
|
const isValid = format1 || format2;
|
|
464
450
|
this._cacheValidationResult(cacheKey, isValid);
|
|
465
451
|
return isValid;
|
|
466
452
|
}
|
|
467
453
|
|
|
468
454
|
/**
|
|
469
|
-
*
|
|
455
|
+
* Validate a FEN string with comprehensive checks
|
|
470
456
|
* @param {string} fen - FEN string to validate
|
|
471
457
|
* @returns {boolean} True if valid
|
|
472
458
|
*/
|
|
@@ -474,86 +460,64 @@ var Chessboard = (function (exports) {
|
|
|
474
460
|
if (typeof fen !== 'string') {
|
|
475
461
|
return false;
|
|
476
462
|
}
|
|
477
|
-
|
|
478
463
|
const cacheKey = `fen:${fen}`;
|
|
479
|
-
|
|
480
464
|
if (this._validationCache.has(cacheKey)) {
|
|
481
465
|
return this._validationCache.get(cacheKey);
|
|
482
466
|
}
|
|
483
|
-
|
|
484
|
-
// Basic pattern check
|
|
485
467
|
if (!this._patterns.fen.test(fen)) {
|
|
486
468
|
this._cacheValidationResult(cacheKey, false);
|
|
487
469
|
return false;
|
|
488
470
|
}
|
|
489
|
-
|
|
490
|
-
// Additional FEN validation
|
|
491
471
|
const isValid = this._validateFenStructure(fen);
|
|
492
472
|
this._cacheValidationResult(cacheKey, isValid);
|
|
493
473
|
return isValid;
|
|
494
474
|
}
|
|
495
475
|
|
|
496
476
|
/**
|
|
497
|
-
*
|
|
477
|
+
* Validate FEN structure in detail
|
|
498
478
|
* @private
|
|
499
479
|
* @param {string} fen - FEN string to validate
|
|
500
480
|
* @returns {boolean} True if valid
|
|
501
481
|
*/
|
|
502
482
|
_validateFenStructure(fen) {
|
|
503
483
|
const parts = fen.split(' ');
|
|
504
|
-
|
|
505
484
|
if (parts.length !== 6) {
|
|
506
485
|
return false;
|
|
507
486
|
}
|
|
508
|
-
|
|
509
|
-
// Validate piece placement
|
|
510
487
|
const ranks = parts[0].split('/');
|
|
511
488
|
if (ranks.length !== 8) {
|
|
512
489
|
return false;
|
|
513
490
|
}
|
|
514
|
-
|
|
515
|
-
// Validate each rank
|
|
516
491
|
for (const rank of ranks) {
|
|
517
492
|
if (!this._validateRank(rank)) {
|
|
518
493
|
return false;
|
|
519
494
|
}
|
|
520
495
|
}
|
|
521
|
-
|
|
522
|
-
// Validate active color
|
|
523
496
|
if (!['w', 'b'].includes(parts[1])) {
|
|
524
497
|
return false;
|
|
525
498
|
}
|
|
526
|
-
|
|
527
|
-
// Validate castling rights
|
|
528
499
|
if (!/^[KQkq-]*$/.test(parts[2])) {
|
|
529
500
|
return false;
|
|
530
501
|
}
|
|
531
|
-
|
|
532
|
-
// Validate en passant target
|
|
533
502
|
if (parts[3] !== '-' && !this.isValidSquare(parts[3])) {
|
|
534
503
|
return false;
|
|
535
504
|
}
|
|
536
|
-
|
|
537
|
-
// Validate halfmove clock and fullmove number
|
|
538
505
|
const halfmove = parseInt(parts[4], 10);
|
|
539
506
|
const fullmove = parseInt(parts[5], 10);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
halfmove >= 0 && fullmove >= 1;
|
|
507
|
+
return !isNaN(halfmove) && !isNaN(fullmove) &&
|
|
508
|
+
halfmove >= 0 && fullmove >= 1;
|
|
543
509
|
}
|
|
544
510
|
|
|
545
511
|
/**
|
|
546
|
-
*
|
|
512
|
+
* Validate a single rank in FEN notation
|
|
547
513
|
* @private
|
|
548
514
|
* @param {string} rank - Rank to validate
|
|
549
515
|
* @returns {boolean} True if valid
|
|
550
516
|
*/
|
|
551
517
|
_validateRank(rank) {
|
|
552
518
|
let squares = 0;
|
|
553
|
-
|
|
554
519
|
for (let i = 0; i < rank.length; i++) {
|
|
555
520
|
const char = rank[i];
|
|
556
|
-
|
|
557
521
|
if (/[1-8]/.test(char)) {
|
|
558
522
|
squares += parseInt(char, 10);
|
|
559
523
|
} else if (/[prnbqkPRNBQK]/.test(char)) {
|
|
@@ -562,12 +526,11 @@ var Chessboard = (function (exports) {
|
|
|
562
526
|
return false;
|
|
563
527
|
}
|
|
564
528
|
}
|
|
565
|
-
|
|
566
529
|
return squares === 8;
|
|
567
530
|
}
|
|
568
531
|
|
|
569
532
|
/**
|
|
570
|
-
*
|
|
533
|
+
* Validate a move string with comprehensive format support
|
|
571
534
|
* @param {string} move - Move string to validate (e.g., 'e2e4', 'e7e8q')
|
|
572
535
|
* @returns {boolean} True if valid
|
|
573
536
|
*/
|
|
@@ -575,20 +538,17 @@ var Chessboard = (function (exports) {
|
|
|
575
538
|
if (typeof move !== 'string') {
|
|
576
539
|
return false;
|
|
577
540
|
}
|
|
578
|
-
|
|
579
541
|
const cacheKey = `move:${move}`;
|
|
580
|
-
|
|
581
542
|
if (this._validationCache.has(cacheKey)) {
|
|
582
543
|
return this._validationCache.get(cacheKey);
|
|
583
544
|
}
|
|
584
|
-
|
|
585
545
|
const isValid = this._validateMoveFormat(move);
|
|
586
546
|
this._cacheValidationResult(cacheKey, isValid);
|
|
587
547
|
return isValid;
|
|
588
548
|
}
|
|
589
549
|
|
|
590
550
|
/**
|
|
591
|
-
*
|
|
551
|
+
* Validate move format in detail
|
|
592
552
|
* @private
|
|
593
553
|
* @param {string} move - Move string to validate
|
|
594
554
|
* @returns {boolean} True if valid
|
|
@@ -597,21 +557,15 @@ var Chessboard = (function (exports) {
|
|
|
597
557
|
if (move.length < 4 || move.length > 5) {
|
|
598
558
|
return false;
|
|
599
559
|
}
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
const to = move.slice(2, 4);
|
|
603
|
-
const promotion = move.slice(4, 5);
|
|
604
|
-
|
|
560
|
+
const from = move.substring(0, 2);
|
|
561
|
+
const to = move.substring(2, 4);
|
|
605
562
|
if (!this.isValidSquare(from) || !this.isValidSquare(to)) {
|
|
606
563
|
return false;
|
|
607
564
|
}
|
|
608
|
-
|
|
609
|
-
if (promotion && !this._validValues.promotionPieces.includes(promotion)) {
|
|
565
|
+
if (move.length === 5 && !/[qrnb]/i.test(move[4])) {
|
|
610
566
|
return false;
|
|
611
567
|
}
|
|
612
|
-
|
|
613
|
-
// Additional move validation (e.g., source and target different)
|
|
614
|
-
return from !== to;
|
|
568
|
+
return true;
|
|
615
569
|
}
|
|
616
570
|
|
|
617
571
|
/**
|
|
@@ -641,11 +595,9 @@ var Chessboard = (function (exports) {
|
|
|
641
595
|
if (size === 'auto') {
|
|
642
596
|
return true;
|
|
643
597
|
}
|
|
644
|
-
|
|
645
598
|
if (typeof size === 'number') {
|
|
646
599
|
return size >= this._constraints.min && size <= this._constraints.max;
|
|
647
600
|
}
|
|
648
|
-
|
|
649
601
|
return false;
|
|
650
602
|
}
|
|
651
603
|
|
|
@@ -655,9 +607,9 @@ var Chessboard = (function (exports) {
|
|
|
655
607
|
* @returns {boolean} True if valid
|
|
656
608
|
*/
|
|
657
609
|
isValidTime(time) {
|
|
658
|
-
return typeof time === 'number' &&
|
|
659
|
-
|
|
660
|
-
|
|
610
|
+
return typeof time === 'number' &&
|
|
611
|
+
time >= 0 &&
|
|
612
|
+
time <= this._constraints.maxTime;
|
|
661
613
|
}
|
|
662
614
|
|
|
663
615
|
/**
|
|
@@ -675,10 +627,10 @@ var Chessboard = (function (exports) {
|
|
|
675
627
|
* @returns {boolean} True if valid
|
|
676
628
|
*/
|
|
677
629
|
isValidElementId(id) {
|
|
678
|
-
return typeof id === 'string' &&
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
|
682
634
|
}
|
|
683
635
|
|
|
684
636
|
/**
|
|
@@ -708,14 +660,6 @@ var Chessboard = (function (exports) {
|
|
|
708
660
|
return this._validValues.easingTypes.includes(easing);
|
|
709
661
|
}
|
|
710
662
|
|
|
711
|
-
/**
|
|
712
|
-
* Validates animation style
|
|
713
|
-
* @param {string} style - Animation style to validate
|
|
714
|
-
* @returns {boolean} True if valid
|
|
715
|
-
*/
|
|
716
|
-
isValidAnimationStyle(style) {
|
|
717
|
-
return this._validValues.animationStyles.includes(style);
|
|
718
|
-
}
|
|
719
663
|
|
|
720
664
|
/**
|
|
721
665
|
* Validates CSS color format
|
|
@@ -726,24 +670,20 @@ var Chessboard = (function (exports) {
|
|
|
726
670
|
if (typeof color !== 'string') {
|
|
727
671
|
return false;
|
|
728
672
|
}
|
|
729
|
-
|
|
730
673
|
// Check for hex colors
|
|
731
674
|
if (this._patterns.color.test(color)) {
|
|
732
675
|
return true;
|
|
733
676
|
}
|
|
734
|
-
|
|
735
677
|
// Check for named colors (basic set)
|
|
736
678
|
const namedColors = ['white', 'black', 'red', 'green', 'blue', 'yellow', 'cyan', 'magenta'];
|
|
737
679
|
if (namedColors.includes(color.toLowerCase())) {
|
|
738
680
|
return true;
|
|
739
681
|
}
|
|
740
|
-
|
|
741
682
|
// Check for rgb/rgba format
|
|
742
683
|
if (/^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/.test(color) ||
|
|
743
684
|
/^rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[0-1](\.\d+)?\s*\)$/.test(color)) {
|
|
744
685
|
return true;
|
|
745
686
|
}
|
|
746
|
-
|
|
747
687
|
return false;
|
|
748
688
|
}
|
|
749
689
|
|
|
@@ -756,8 +696,8 @@ var Chessboard = (function (exports) {
|
|
|
756
696
|
validateSquare(square) {
|
|
757
697
|
if (!this.isValidSquare(square)) {
|
|
758
698
|
throw new ValidationError(
|
|
759
|
-
ERROR_MESSAGES.invalid_square + square,
|
|
760
|
-
'square',
|
|
699
|
+
ERROR_MESSAGES.invalid_square + square,
|
|
700
|
+
'square',
|
|
761
701
|
square
|
|
762
702
|
);
|
|
763
703
|
}
|
|
@@ -773,8 +713,8 @@ var Chessboard = (function (exports) {
|
|
|
773
713
|
validatePiece(piece) {
|
|
774
714
|
if (!this.isValidPiece(piece)) {
|
|
775
715
|
throw new ValidationError(
|
|
776
|
-
ERROR_MESSAGES.invalid_piece + piece,
|
|
777
|
-
'piece',
|
|
716
|
+
ERROR_MESSAGES.invalid_piece + piece,
|
|
717
|
+
'piece',
|
|
778
718
|
piece
|
|
779
719
|
);
|
|
780
720
|
}
|
|
@@ -790,8 +730,8 @@ var Chessboard = (function (exports) {
|
|
|
790
730
|
validateFen(fen) {
|
|
791
731
|
if (!this.isValidFen(fen)) {
|
|
792
732
|
throw new ValidationError(
|
|
793
|
-
ERROR_MESSAGES.invalid_fen + fen,
|
|
794
|
-
'fen',
|
|
733
|
+
ERROR_MESSAGES.invalid_fen + fen,
|
|
734
|
+
'fen',
|
|
795
735
|
fen
|
|
796
736
|
);
|
|
797
737
|
}
|
|
@@ -807,8 +747,8 @@ var Chessboard = (function (exports) {
|
|
|
807
747
|
validateMove(move) {
|
|
808
748
|
if (!this.isValidMove(move)) {
|
|
809
749
|
throw new ValidationError(
|
|
810
|
-
ERROR_MESSAGES.invalid_move + move,
|
|
811
|
-
'move',
|
|
750
|
+
ERROR_MESSAGES.invalid_move + move,
|
|
751
|
+
'move',
|
|
812
752
|
move
|
|
813
753
|
);
|
|
814
754
|
}
|
|
@@ -824,16 +764,15 @@ var Chessboard = (function (exports) {
|
|
|
824
764
|
validateOrientation(orientation) {
|
|
825
765
|
if (!this.isValidOrientation(orientation)) {
|
|
826
766
|
throw new ValidationError(
|
|
827
|
-
ERROR_MESSAGES.invalid_orientation + orientation,
|
|
828
|
-
'orientation',
|
|
767
|
+
ERROR_MESSAGES.invalid_orientation + orientation,
|
|
768
|
+
'orientation',
|
|
829
769
|
orientation
|
|
830
770
|
);
|
|
831
771
|
}
|
|
832
|
-
|
|
833
772
|
// Normalize to 'w' or 'b'
|
|
834
|
-
return orientation === 'white' ? 'w' :
|
|
835
|
-
|
|
836
|
-
|
|
773
|
+
return orientation === 'white' ? 'w' :
|
|
774
|
+
orientation === 'black' ? 'b' :
|
|
775
|
+
orientation;
|
|
837
776
|
}
|
|
838
777
|
|
|
839
778
|
/**
|
|
@@ -845,16 +784,15 @@ var Chessboard = (function (exports) {
|
|
|
845
784
|
validateColor(color) {
|
|
846
785
|
if (!this.isValidColor(color)) {
|
|
847
786
|
throw new ValidationError(
|
|
848
|
-
ERROR_MESSAGES.invalid_color + color,
|
|
849
|
-
'color',
|
|
787
|
+
ERROR_MESSAGES.invalid_color + color,
|
|
788
|
+
'color',
|
|
850
789
|
color
|
|
851
790
|
);
|
|
852
791
|
}
|
|
853
|
-
|
|
854
792
|
// Normalize to 'w' or 'b'
|
|
855
|
-
return color === 'white' ? 'w' :
|
|
856
|
-
|
|
857
|
-
|
|
793
|
+
return color === 'white' ? 'w' :
|
|
794
|
+
color === 'black' ? 'b' :
|
|
795
|
+
color;
|
|
858
796
|
}
|
|
859
797
|
|
|
860
798
|
/**
|
|
@@ -866,8 +804,8 @@ var Chessboard = (function (exports) {
|
|
|
866
804
|
validateSize(size) {
|
|
867
805
|
if (!this.isValidSize(size)) {
|
|
868
806
|
throw new ValidationError(
|
|
869
|
-
ERROR_MESSAGES.invalid_size + size,
|
|
870
|
-
'size',
|
|
807
|
+
ERROR_MESSAGES.invalid_size + size,
|
|
808
|
+
'size',
|
|
871
809
|
size
|
|
872
810
|
);
|
|
873
811
|
}
|
|
@@ -883,40 +821,29 @@ var Chessboard = (function (exports) {
|
|
|
883
821
|
validateConfig(config) {
|
|
884
822
|
if (!config || typeof config !== 'object') {
|
|
885
823
|
throw new ValidationError(
|
|
886
|
-
'Configuration must be an object',
|
|
887
|
-
'config',
|
|
824
|
+
'Configuration must be an object',
|
|
825
|
+
'config',
|
|
888
826
|
config
|
|
889
827
|
);
|
|
890
828
|
}
|
|
891
|
-
|
|
892
829
|
const errors = [];
|
|
893
|
-
|
|
894
830
|
// Validate required fields
|
|
895
831
|
if (!config.id && !config.id_div) {
|
|
896
832
|
errors.push('Configuration must include id or id_div');
|
|
897
833
|
}
|
|
898
|
-
|
|
899
834
|
// Validate optional fields
|
|
900
835
|
if (config.orientation && !this.isValidOrientation(config.orientation)) {
|
|
901
836
|
errors.push(ERROR_MESSAGES.invalid_orientation + config.orientation);
|
|
902
837
|
}
|
|
903
|
-
|
|
904
838
|
if (config.size && !this.isValidSize(config.size)) {
|
|
905
839
|
errors.push(ERROR_MESSAGES.invalid_size + config.size);
|
|
906
840
|
}
|
|
907
|
-
|
|
908
841
|
if (config.movableColors && !this.isValidMovableColors(config.movableColors)) {
|
|
909
842
|
errors.push(ERROR_MESSAGES.invalid_color + config.movableColors);
|
|
910
843
|
}
|
|
911
|
-
|
|
912
844
|
if (config.dropOffBoard && !this.isValidDropOffBoard(config.dropOffBoard)) {
|
|
913
845
|
errors.push(ERROR_MESSAGES.invalid_dropOffBoard + config.dropOffBoard);
|
|
914
846
|
}
|
|
915
|
-
|
|
916
|
-
if (config.animationStyle && !this.isValidAnimationStyle(config.animationStyle)) {
|
|
917
|
-
errors.push(ERROR_MESSAGES.invalid_animationStyle + config.animationStyle);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
847
|
// Validate callbacks
|
|
921
848
|
const callbacks = ['onMove', 'onMoveEnd', 'onChange', 'onDragStart', 'onDragMove', 'onDrop', 'onSnapbackEnd'];
|
|
922
849
|
for (const callback of callbacks) {
|
|
@@ -924,7 +851,6 @@ var Chessboard = (function (exports) {
|
|
|
924
851
|
errors.push(`Invalid ${callback} callback`);
|
|
925
852
|
}
|
|
926
853
|
}
|
|
927
|
-
|
|
928
854
|
// Validate colors
|
|
929
855
|
const colorFields = ['whiteSquare', 'blackSquare', 'highlight', 'hintColor'];
|
|
930
856
|
for (const field of colorFields) {
|
|
@@ -932,15 +858,13 @@ var Chessboard = (function (exports) {
|
|
|
932
858
|
errors.push(`Invalid ${field} color: ${config[field]}`);
|
|
933
859
|
}
|
|
934
860
|
}
|
|
935
|
-
|
|
936
861
|
if (errors.length > 0) {
|
|
937
862
|
throw new ValidationError(
|
|
938
|
-
`Configuration validation failed: ${errors.join(', ')}`,
|
|
939
|
-
'config',
|
|
863
|
+
`Configuration validation failed: ${errors.join(', ')}`,
|
|
864
|
+
'config',
|
|
940
865
|
config
|
|
941
866
|
);
|
|
942
867
|
}
|
|
943
|
-
|
|
944
868
|
return config;
|
|
945
869
|
}
|
|
946
870
|
|
|
@@ -956,7 +880,6 @@ var Chessboard = (function (exports) {
|
|
|
956
880
|
const firstKey = this._validationCache.keys().next().value;
|
|
957
881
|
this._validationCache.delete(firstKey);
|
|
958
882
|
}
|
|
959
|
-
|
|
960
883
|
this._validationCache.set(key, result);
|
|
961
884
|
}
|
|
962
885
|
|
|
@@ -988,7 +911,6 @@ var Chessboard = (function (exports) {
|
|
|
988
911
|
return validations.map(validation => {
|
|
989
912
|
try {
|
|
990
913
|
const { type, value } = validation;
|
|
991
|
-
|
|
992
914
|
switch (type) {
|
|
993
915
|
case 'square':
|
|
994
916
|
return { valid: this.isValidSquare(value), value };
|
|
@@ -1094,8 +1016,7 @@ var Chessboard = (function (exports) {
|
|
|
1094
1016
|
fadeAnimation: 'ease',
|
|
1095
1017
|
ratio: 0.9,
|
|
1096
1018
|
piecesPath: '../assets/themes/default',
|
|
1097
|
-
|
|
1098
|
-
simultaneousAnimationDelay: 0,
|
|
1019
|
+
simultaneousAnimationDelay: 100,
|
|
1099
1020
|
onMove: () => true,
|
|
1100
1021
|
onMoveEnd: () => true,
|
|
1101
1022
|
onChange: () => true,
|
|
@@ -1129,19 +1050,19 @@ var Chessboard = (function (exports) {
|
|
|
1129
1050
|
constructor(settings = {}) {
|
|
1130
1051
|
// Initialize validation service
|
|
1131
1052
|
this._validationService = new ValidationService();
|
|
1132
|
-
|
|
1053
|
+
|
|
1133
1054
|
// Validate input
|
|
1134
1055
|
this._validateInput(settings);
|
|
1135
|
-
|
|
1056
|
+
|
|
1136
1057
|
// Merge with defaults
|
|
1137
1058
|
const config = this._mergeWithDefaults(settings);
|
|
1138
|
-
|
|
1059
|
+
|
|
1139
1060
|
// Process and validate configuration
|
|
1140
1061
|
this._processConfiguration(config);
|
|
1141
|
-
|
|
1062
|
+
|
|
1142
1063
|
// Set CSS properties
|
|
1143
1064
|
this._setCSSProperties(config);
|
|
1144
|
-
|
|
1065
|
+
|
|
1145
1066
|
// Configure mode-specific settings
|
|
1146
1067
|
this._configureModeSettings();
|
|
1147
1068
|
}
|
|
@@ -1156,7 +1077,7 @@ var Chessboard = (function (exports) {
|
|
|
1156
1077
|
if (settings !== null && typeof settings !== 'object') {
|
|
1157
1078
|
throw new ConfigurationError('Settings must be an object', 'settings', settings);
|
|
1158
1079
|
}
|
|
1159
|
-
|
|
1080
|
+
|
|
1160
1081
|
// Validate using validation service
|
|
1161
1082
|
try {
|
|
1162
1083
|
this._validationService.validateConfig(settings);
|
|
@@ -1190,7 +1111,7 @@ var Chessboard = (function (exports) {
|
|
|
1190
1111
|
this.size = config.size;
|
|
1191
1112
|
this.movableColors = config.movableColors;
|
|
1192
1113
|
this.piecesPath = config.piecesPath;
|
|
1193
|
-
|
|
1114
|
+
|
|
1194
1115
|
// Event handlers
|
|
1195
1116
|
this.onMove = this._validateCallback(config.onMove);
|
|
1196
1117
|
this.onMoveEnd = this._validateCallback(config.onMoveEnd);
|
|
@@ -1218,9 +1139,8 @@ var Chessboard = (function (exports) {
|
|
|
1218
1139
|
this.snapbackTime = this._setTime(config.snapbackTime);
|
|
1219
1140
|
this.dropCenterTime = this._setTime(config.dropCenterTime);
|
|
1220
1141
|
this.fadeTime = this._setTime(config.fadeTime);
|
|
1221
|
-
|
|
1142
|
+
|
|
1222
1143
|
// Animation style properties
|
|
1223
|
-
this.animationStyle = this._validateAnimationStyle(config.animationStyle);
|
|
1224
1144
|
this.simultaneousAnimationDelay = this._validateDelay(config.simultaneousAnimationDelay);
|
|
1225
1145
|
}
|
|
1226
1146
|
|
|
@@ -1281,19 +1201,6 @@ var Chessboard = (function (exports) {
|
|
|
1281
1201
|
return callback;
|
|
1282
1202
|
}
|
|
1283
1203
|
|
|
1284
|
-
/**
|
|
1285
|
-
* Validates animation style
|
|
1286
|
-
* @private
|
|
1287
|
-
* @param {string} style - Animation style
|
|
1288
|
-
* @returns {string} Validated style
|
|
1289
|
-
* @throws {ConfigurationError} If style is invalid
|
|
1290
|
-
*/
|
|
1291
|
-
_validateAnimationStyle(style) {
|
|
1292
|
-
if (!this._validationService.isValidAnimationStyle(style)) {
|
|
1293
|
-
throw new ConfigurationError('Invalid animation style', 'animationStyle', style);
|
|
1294
|
-
}
|
|
1295
|
-
return style;
|
|
1296
|
-
}
|
|
1297
1204
|
|
|
1298
1205
|
/**
|
|
1299
1206
|
* Validates animation delay
|
|
@@ -1351,11 +1258,11 @@ var Chessboard = (function (exports) {
|
|
|
1351
1258
|
}
|
|
1352
1259
|
return value;
|
|
1353
1260
|
}
|
|
1354
|
-
|
|
1261
|
+
|
|
1355
1262
|
if (typeof value === 'string' && value in ANIMATION_TIMES) {
|
|
1356
1263
|
return ANIMATION_TIMES[value];
|
|
1357
1264
|
}
|
|
1358
|
-
|
|
1265
|
+
|
|
1359
1266
|
throw new ConfigurationError('Invalid time value', 'time', value);
|
|
1360
1267
|
}
|
|
1361
1268
|
|
|
@@ -1370,11 +1277,11 @@ var Chessboard = (function (exports) {
|
|
|
1370
1277
|
if (typeof value === 'boolean') {
|
|
1371
1278
|
return value;
|
|
1372
1279
|
}
|
|
1373
|
-
|
|
1280
|
+
|
|
1374
1281
|
if (value in BOOLEAN_VALUES) {
|
|
1375
1282
|
return BOOLEAN_VALUES[value];
|
|
1376
1283
|
}
|
|
1377
|
-
|
|
1284
|
+
|
|
1378
1285
|
throw new ConfigurationError('Invalid boolean value', 'boolean', value);
|
|
1379
1286
|
}
|
|
1380
1287
|
|
|
@@ -1390,17 +1297,17 @@ var Chessboard = (function (exports) {
|
|
|
1390
1297
|
if (typeof value === 'boolean') {
|
|
1391
1298
|
return value ? TRANSITION_FUNCTIONS.ease : null;
|
|
1392
1299
|
}
|
|
1393
|
-
|
|
1300
|
+
|
|
1394
1301
|
// Handle string values
|
|
1395
1302
|
if (typeof value === 'string' && value in TRANSITION_FUNCTIONS) {
|
|
1396
1303
|
return TRANSITION_FUNCTIONS[value];
|
|
1397
1304
|
}
|
|
1398
|
-
|
|
1305
|
+
|
|
1399
1306
|
// Handle null/undefined
|
|
1400
1307
|
if (value === null || value === undefined) {
|
|
1401
1308
|
return null;
|
|
1402
1309
|
}
|
|
1403
|
-
|
|
1310
|
+
|
|
1404
1311
|
throw new ConfigurationError('Invalid transition function', 'transitionFunction', value);
|
|
1405
1312
|
}
|
|
1406
1313
|
|
|
@@ -1431,7 +1338,6 @@ var Chessboard = (function (exports) {
|
|
|
1431
1338
|
fadeTime: this.fadeTime,
|
|
1432
1339
|
fadeAnimation: this.fadeAnimation,
|
|
1433
1340
|
piecesPath: this.piecesPath,
|
|
1434
|
-
animationStyle: this.animationStyle,
|
|
1435
1341
|
simultaneousAnimationDelay: this.simultaneousAnimationDelay,
|
|
1436
1342
|
onlyLegalMoves: this.onlyLegalMoves
|
|
1437
1343
|
};
|
|
@@ -1453,7 +1359,7 @@ var Chessboard = (function (exports) {
|
|
|
1453
1359
|
|
|
1454
1360
|
// Apply updates
|
|
1455
1361
|
const newConfig = Object.assign({}, this.toObject(), updates);
|
|
1456
|
-
|
|
1362
|
+
|
|
1457
1363
|
// Re-process configuration
|
|
1458
1364
|
this._processConfiguration(newConfig);
|
|
1459
1365
|
this._setCSSProperties(newConfig);
|
|
@@ -1676,22 +1582,10 @@ var Chessboard = (function (exports) {
|
|
|
1676
1582
|
|
|
1677
1583
|
setDrag(f) {
|
|
1678
1584
|
if (!this.element) { console.debug(`[Piece] setDrag: ${this.id} - element is null`); return; }
|
|
1679
|
-
// Remove previous handlers
|
|
1680
|
-
this.element.onmousedown = null;
|
|
1681
|
-
this.element.ontouchstart = null;
|
|
1682
|
-
this.element.ondragstart = null;
|
|
1683
|
-
if (window.PointerEvent) {
|
|
1684
|
-
this.element.onpointerdown = null;
|
|
1685
|
-
}
|
|
1686
|
-
// Set new handlers
|
|
1687
1585
|
this.element.ondragstart = (e) => { e.preventDefault(); };
|
|
1688
1586
|
this.element.onmousedown = f;
|
|
1689
1587
|
this.element.ontouchstart = f; // Drag touch
|
|
1690
|
-
|
|
1691
|
-
this.element.onpointerdown = f;
|
|
1692
|
-
console.debug(`[Piece] setDrag: pointerdown set for ${this.id}`);
|
|
1693
|
-
}
|
|
1694
|
-
console.debug(`[Piece] setDrag: mousedown/ontouchstart set for ${this.id}`);
|
|
1588
|
+
console.debug(`[Piece] setDrag: ${this.id}`);
|
|
1695
1589
|
}
|
|
1696
1590
|
|
|
1697
1591
|
destroy() {
|
|
@@ -2031,180 +1925,155 @@ var Chessboard = (function (exports) {
|
|
|
2031
1925
|
};
|
|
2032
1926
|
|
|
2033
1927
|
/**
|
|
2034
|
-
*
|
|
1928
|
+
* AnimationService - Gestione centralizzata delle animazioni e transizioni per la scacchiera
|
|
2035
1929
|
* @module services/AnimationService
|
|
2036
1930
|
* @since 2.0.0
|
|
2037
1931
|
*/
|
|
2038
1932
|
|
|
2039
1933
|
/**
|
|
2040
|
-
* Service
|
|
2041
|
-
* @class
|
|
1934
|
+
* Service responsabile del coordinamento delle animazioni e delle transizioni
|
|
1935
|
+
* @class AnimationService
|
|
2042
1936
|
*/
|
|
2043
1937
|
class AnimationService {
|
|
2044
1938
|
/**
|
|
2045
|
-
*
|
|
2046
|
-
* @param {
|
|
1939
|
+
* Crea una nuova istanza di AnimationService
|
|
1940
|
+
* @param {Object} config - Configurazione della scacchiera
|
|
2047
1941
|
*/
|
|
2048
1942
|
constructor(config) {
|
|
1943
|
+
/** @type {Object} */
|
|
2049
1944
|
this.config = config;
|
|
1945
|
+
/** @type {Map<number, {element: HTMLElement, animate: Function, callback?: Function}>} */
|
|
2050
1946
|
this.activeAnimations = new Map();
|
|
1947
|
+
/** @type {number} */
|
|
2051
1948
|
this.animationId = 0;
|
|
2052
1949
|
}
|
|
2053
1950
|
|
|
2054
1951
|
/**
|
|
2055
|
-
*
|
|
2056
|
-
* @param {string} [type='ease'] -
|
|
2057
|
-
* @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
|
|
2058
1955
|
*/
|
|
2059
1956
|
createTimingFunction(type = 'ease') {
|
|
2060
1957
|
return (elapsed, duration) => {
|
|
2061
|
-
const x = elapsed / duration;
|
|
2062
|
-
|
|
1958
|
+
const x = duration > 0 ? Math.max(0, Math.min(1, elapsed / duration)) : 1;
|
|
2063
1959
|
switch (type) {
|
|
2064
|
-
case 'linear':
|
|
2065
|
-
|
|
2066
|
-
case 'ease':
|
|
2067
|
-
|
|
2068
|
-
case 'ease-in':
|
|
2069
|
-
|
|
2070
|
-
case 'ease-out':
|
|
2071
|
-
return -1 * (x - 1) ** 2 + 1;
|
|
2072
|
-
case 'ease-in-out':
|
|
2073
|
-
return (x < 0.5) ? 2 * x ** 2 : 4 * x - 2 * x ** 2 - 1;
|
|
2074
|
-
default:
|
|
2075
|
-
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;
|
|
2076
1966
|
}
|
|
2077
1967
|
};
|
|
2078
1968
|
}
|
|
2079
1969
|
|
|
2080
1970
|
/**
|
|
2081
|
-
*
|
|
2082
|
-
* @param {HTMLElement} element -
|
|
2083
|
-
* @param {Object} properties -
|
|
2084
|
-
* @param {number} duration -
|
|
2085
|
-
* @param {string} [easing='ease'] -
|
|
2086
|
-
* @param {Function} [callback] - Callback
|
|
2087
|
-
* @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
|
|
2088
1978
|
*/
|
|
2089
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
|
+
}
|
|
2090
1983
|
const animationId = ++this.animationId;
|
|
2091
1984
|
const timingFunction = this.createTimingFunction(easing);
|
|
2092
1985
|
const startTime = performance.now();
|
|
2093
1986
|
const startValues = {};
|
|
2094
|
-
|
|
2095
|
-
// Store initial values
|
|
2096
1987
|
Object.keys(properties).forEach(prop => {
|
|
2097
1988
|
startValues[prop] = this._getInitialValue(element, prop);
|
|
2098
1989
|
});
|
|
2099
|
-
|
|
2100
1990
|
const animate = (currentTime) => {
|
|
2101
1991
|
const elapsed = currentTime - startTime;
|
|
2102
1992
|
const progress = Math.min(elapsed / duration, 1);
|
|
2103
1993
|
const easedProgress = timingFunction(elapsed, duration);
|
|
2104
|
-
|
|
2105
|
-
// Apply animated values
|
|
2106
1994
|
Object.keys(properties).forEach(prop => {
|
|
2107
1995
|
const startValue = startValues[prop];
|
|
2108
1996
|
const endValue = properties[prop];
|
|
2109
1997
|
const currentValue = this._interpolateValue(startValue, endValue, easedProgress);
|
|
2110
1998
|
this._applyValue(element, prop, currentValue);
|
|
2111
1999
|
});
|
|
2112
|
-
|
|
2113
2000
|
if (progress < 1 && this.activeAnimations.has(animationId)) {
|
|
2114
2001
|
requestAnimationFrame(animate);
|
|
2115
2002
|
} else {
|
|
2116
2003
|
this.activeAnimations.delete(animationId);
|
|
2117
|
-
if (callback) callback();
|
|
2004
|
+
if (typeof callback === 'function') callback();
|
|
2118
2005
|
}
|
|
2119
2006
|
};
|
|
2120
|
-
|
|
2121
2007
|
this.activeAnimations.set(animationId, { element, animate, callback });
|
|
2122
2008
|
requestAnimationFrame(animate);
|
|
2123
|
-
|
|
2124
2009
|
return animationId;
|
|
2125
2010
|
}
|
|
2126
2011
|
|
|
2127
2012
|
/**
|
|
2128
|
-
*
|
|
2129
|
-
* @param {number} animationId -
|
|
2013
|
+
* Annulla una animazione attiva
|
|
2014
|
+
* @param {number} animationId - ID dell'animazione
|
|
2130
2015
|
*/
|
|
2131
2016
|
cancel(animationId) {
|
|
2132
|
-
|
|
2133
|
-
this.activeAnimations.delete(animationId);
|
|
2134
|
-
}
|
|
2017
|
+
this.activeAnimations.delete(animationId);
|
|
2135
2018
|
}
|
|
2136
2019
|
|
|
2137
2020
|
/**
|
|
2138
|
-
*
|
|
2021
|
+
* Annulla tutte le animazioni attive
|
|
2139
2022
|
*/
|
|
2140
2023
|
cancelAll() {
|
|
2141
2024
|
this.activeAnimations.clear();
|
|
2142
2025
|
}
|
|
2143
2026
|
|
|
2144
2027
|
/**
|
|
2145
|
-
*
|
|
2028
|
+
* Ottiene il valore iniziale di una proprietà
|
|
2146
2029
|
* @private
|
|
2147
|
-
* @param {HTMLElement} element
|
|
2148
|
-
* @param {string} property
|
|
2149
|
-
* @returns {number}
|
|
2030
|
+
* @param {HTMLElement} element
|
|
2031
|
+
* @param {string} property
|
|
2032
|
+
* @returns {number}
|
|
2150
2033
|
*/
|
|
2151
2034
|
_getInitialValue(element, property) {
|
|
2152
2035
|
if (!element || !element.style) return 0;
|
|
2153
2036
|
switch (property) {
|
|
2154
|
-
case 'opacity':
|
|
2155
|
-
|
|
2156
|
-
case '
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
return parseFloat(element.style.top) || 0;
|
|
2160
|
-
case 'scale':
|
|
2161
|
-
return 1;
|
|
2162
|
-
default:
|
|
2163
|
-
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;
|
|
2164
2042
|
}
|
|
2165
2043
|
}
|
|
2166
2044
|
|
|
2167
2045
|
/**
|
|
2168
|
-
*
|
|
2046
|
+
* Interpola tra due valori
|
|
2169
2047
|
* @private
|
|
2170
|
-
* @param {number} start
|
|
2171
|
-
* @param {number} end
|
|
2172
|
-
* @param {number} progress
|
|
2173
|
-
* @returns {number}
|
|
2048
|
+
* @param {number} start
|
|
2049
|
+
* @param {number} end
|
|
2050
|
+
* @param {number} progress
|
|
2051
|
+
* @returns {number}
|
|
2174
2052
|
*/
|
|
2175
2053
|
_interpolateValue(start, end, progress) {
|
|
2176
2054
|
return start + (end - start) * progress;
|
|
2177
2055
|
}
|
|
2178
2056
|
|
|
2179
2057
|
/**
|
|
2180
|
-
*
|
|
2058
|
+
* Applica il valore animato all'elemento
|
|
2181
2059
|
* @private
|
|
2182
|
-
* @param {HTMLElement} element
|
|
2183
|
-
* @param {string} property
|
|
2184
|
-
* @param {number} value
|
|
2060
|
+
* @param {HTMLElement} element
|
|
2061
|
+
* @param {string} property
|
|
2062
|
+
* @param {number} value
|
|
2185
2063
|
*/
|
|
2186
2064
|
_applyValue(element, property, value) {
|
|
2187
2065
|
if (!element || !element.style) return;
|
|
2188
2066
|
switch (property) {
|
|
2189
|
-
case 'opacity':
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
case '
|
|
2193
|
-
|
|
2194
|
-
break;
|
|
2195
|
-
case 'top':
|
|
2196
|
-
element.style.top = value + 'px';
|
|
2197
|
-
break;
|
|
2198
|
-
case 'scale':
|
|
2199
|
-
element.style.transform = `scale(${value})`;
|
|
2200
|
-
break;
|
|
2201
|
-
default:
|
|
2202
|
-
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;
|
|
2203
2072
|
}
|
|
2204
2073
|
}
|
|
2205
2074
|
|
|
2206
2075
|
/**
|
|
2207
|
-
*
|
|
2076
|
+
* Cleanup risorse e animazioni
|
|
2208
2077
|
*/
|
|
2209
2078
|
destroy() {
|
|
2210
2079
|
this.cancelAll();
|
|
@@ -2212,7 +2081,7 @@ var Chessboard = (function (exports) {
|
|
|
2212
2081
|
}
|
|
2213
2082
|
|
|
2214
2083
|
/**
|
|
2215
|
-
*
|
|
2084
|
+
* BoardService - Handles board DOM setup, manipulation, and resource management
|
|
2216
2085
|
* @module services/BoardService
|
|
2217
2086
|
* @since 2.0.0
|
|
2218
2087
|
*/
|
|
@@ -2220,45 +2089,45 @@ var Chessboard = (function (exports) {
|
|
|
2220
2089
|
|
|
2221
2090
|
/**
|
|
2222
2091
|
* Service responsible for board DOM manipulation and setup
|
|
2223
|
-
* @class
|
|
2092
|
+
* @class BoardService
|
|
2224
2093
|
*/
|
|
2225
2094
|
class BoardService {
|
|
2226
2095
|
/**
|
|
2227
|
-
*
|
|
2228
|
-
* @param {
|
|
2096
|
+
* Create a new BoardService instance
|
|
2097
|
+
* @param {Object} config - Board configuration
|
|
2229
2098
|
*/
|
|
2230
2099
|
constructor(config) {
|
|
2100
|
+
/** @type {Object} */
|
|
2231
2101
|
this.config = config;
|
|
2102
|
+
/** @type {HTMLElement|null} */
|
|
2232
2103
|
this.element = null;
|
|
2104
|
+
/** @type {Object.<string, Square>} */
|
|
2233
2105
|
this.squares = {};
|
|
2234
2106
|
}
|
|
2235
2107
|
|
|
2236
2108
|
/**
|
|
2237
|
-
*
|
|
2238
|
-
* @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
|
|
2239
2111
|
*/
|
|
2240
2112
|
buildBoard() {
|
|
2241
|
-
console.log('BoardService.buildBoard: Looking for element with ID:', this.config.id_div);
|
|
2242
|
-
|
|
2243
2113
|
this.element = document.getElementById(this.config.id_div);
|
|
2244
2114
|
if (!this.element) {
|
|
2245
2115
|
throw new DOMError(ERROR_MESSAGES.invalid_id_div + this.config.id_div, this.config.id_div);
|
|
2246
2116
|
}
|
|
2247
|
-
|
|
2248
2117
|
this.resize(this.config.size);
|
|
2249
|
-
this.element.className =
|
|
2118
|
+
this.element.className = 'board';
|
|
2250
2119
|
}
|
|
2251
2120
|
|
|
2252
2121
|
/**
|
|
2253
|
-
*
|
|
2122
|
+
* Create all 64 squares and add them to the board
|
|
2254
2123
|
* @param {Function} coordConverter - Function to convert row/col to real coordinates
|
|
2255
2124
|
*/
|
|
2256
2125
|
buildSquares(coordConverter) {
|
|
2126
|
+
if (!this.element) throw new DOMError('Board element not initialized', this.config.id_div);
|
|
2257
2127
|
for (let row = 0; row < BOARD_SIZE.ROWS; row++) {
|
|
2258
2128
|
for (let col = 0; col < BOARD_SIZE.COLS; col++) {
|
|
2259
2129
|
const [squareRow, squareCol] = coordConverter(row, col);
|
|
2260
2130
|
const square = new Square(squareRow, squareCol);
|
|
2261
|
-
|
|
2262
2131
|
this.squares[square.getId()] = square;
|
|
2263
2132
|
this.element.appendChild(square.element);
|
|
2264
2133
|
}
|
|
@@ -2266,20 +2135,17 @@ var Chessboard = (function (exports) {
|
|
|
2266
2135
|
}
|
|
2267
2136
|
|
|
2268
2137
|
/**
|
|
2269
|
-
*
|
|
2270
|
-
* Best practice: always destroy JS objects and DOM nodes, and clear references.
|
|
2138
|
+
* Remove all squares from the board and clean up their resources
|
|
2271
2139
|
*/
|
|
2272
2140
|
removeSquares() {
|
|
2273
2141
|
for (const square of Object.values(this.squares)) {
|
|
2274
|
-
// Always call destroy to remove DOM and clear piece reference
|
|
2275
2142
|
square.destroy();
|
|
2276
2143
|
}
|
|
2277
2144
|
this.squares = {};
|
|
2278
2145
|
}
|
|
2279
2146
|
|
|
2280
2147
|
/**
|
|
2281
|
-
*
|
|
2282
|
-
* Best practice: clear DOM and force element to be re-fetched on next build.
|
|
2148
|
+
* Remove all content from the board element
|
|
2283
2149
|
*/
|
|
2284
2150
|
removeBoard() {
|
|
2285
2151
|
if (this.element) {
|
|
@@ -2289,9 +2155,9 @@ var Chessboard = (function (exports) {
|
|
|
2289
2155
|
}
|
|
2290
2156
|
|
|
2291
2157
|
/**
|
|
2292
|
-
*
|
|
2158
|
+
* Resize the board to the specified size
|
|
2293
2159
|
* @param {number|string} value - Size in pixels or 'auto'
|
|
2294
|
-
* @throws {ValidationError}
|
|
2160
|
+
* @throws {ValidationError} If size value is invalid
|
|
2295
2161
|
*/
|
|
2296
2162
|
resize(value) {
|
|
2297
2163
|
if (value === 'auto') {
|
|
@@ -2305,15 +2171,13 @@ var Chessboard = (function (exports) {
|
|
|
2305
2171
|
}
|
|
2306
2172
|
|
|
2307
2173
|
/**
|
|
2308
|
-
*
|
|
2174
|
+
* Calculate the optimal size when 'auto' is specified
|
|
2309
2175
|
* @private
|
|
2310
2176
|
* @returns {number} Calculated size in pixels
|
|
2311
2177
|
*/
|
|
2312
2178
|
_calculateAutoSize() {
|
|
2313
|
-
if (!this.element) return 400;
|
|
2314
|
-
|
|
2179
|
+
if (!this.element) return 400;
|
|
2315
2180
|
const { offsetWidth, offsetHeight } = this.element;
|
|
2316
|
-
|
|
2317
2181
|
if (offsetWidth === 0) {
|
|
2318
2182
|
return offsetHeight || 400;
|
|
2319
2183
|
} else if (offsetHeight === 0) {
|
|
@@ -2324,8 +2188,8 @@ var Chessboard = (function (exports) {
|
|
|
2324
2188
|
}
|
|
2325
2189
|
|
|
2326
2190
|
/**
|
|
2327
|
-
*
|
|
2328
|
-
* @param {string} squareId - Square identifier
|
|
2191
|
+
* Get a square by its ID
|
|
2192
|
+
* @param {string} squareId - Square identifier
|
|
2329
2193
|
* @returns {Square|null} The square or null if not found
|
|
2330
2194
|
*/
|
|
2331
2195
|
getSquare(squareId) {
|
|
@@ -2333,26 +2197,37 @@ var Chessboard = (function (exports) {
|
|
|
2333
2197
|
}
|
|
2334
2198
|
|
|
2335
2199
|
/**
|
|
2336
|
-
* Highlight a square
|
|
2200
|
+
* Highlight a square
|
|
2337
2201
|
* @param {Square} square
|
|
2338
2202
|
* @param {Object} [opts]
|
|
2339
2203
|
*/
|
|
2340
2204
|
highlightSquare(square, opts = {}) {
|
|
2341
|
-
if (!square) throw new Error('highlightSquare
|
|
2342
|
-
//
|
|
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
|
+
}
|
|
2343
2212
|
}
|
|
2213
|
+
|
|
2344
2214
|
/**
|
|
2345
|
-
*
|
|
2215
|
+
* Remove highlight from a square
|
|
2346
2216
|
* @param {Square} square
|
|
2347
2217
|
* @param {Object} [opts]
|
|
2348
2218
|
*/
|
|
2349
2219
|
dehighlightSquare(square, opts = {}) {
|
|
2350
|
-
if (!square) throw new Error('dehighlightSquare
|
|
2351
|
-
//
|
|
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
|
+
}
|
|
2352
2227
|
}
|
|
2353
2228
|
|
|
2354
2229
|
/**
|
|
2355
|
-
*
|
|
2230
|
+
* Get all squares
|
|
2356
2231
|
* @returns {Object.<string, Square>} All squares indexed by ID
|
|
2357
2232
|
*/
|
|
2358
2233
|
getAllSquares() {
|
|
@@ -2360,7 +2235,7 @@ var Chessboard = (function (exports) {
|
|
|
2360
2235
|
}
|
|
2361
2236
|
|
|
2362
2237
|
/**
|
|
2363
|
-
*
|
|
2238
|
+
* Apply a method to all squares
|
|
2364
2239
|
* @param {string} methodName - Name of the method to call on each square
|
|
2365
2240
|
* @param {...*} args - Arguments to pass to the method
|
|
2366
2241
|
*/
|
|
@@ -2373,7 +2248,7 @@ var Chessboard = (function (exports) {
|
|
|
2373
2248
|
}
|
|
2374
2249
|
|
|
2375
2250
|
/**
|
|
2376
|
-
*
|
|
2251
|
+
* Clean up all resources
|
|
2377
2252
|
*/
|
|
2378
2253
|
destroy() {
|
|
2379
2254
|
this.removeSquares();
|
|
@@ -2384,7 +2259,7 @@ var Chessboard = (function (exports) {
|
|
|
2384
2259
|
}
|
|
2385
2260
|
|
|
2386
2261
|
/**
|
|
2387
|
-
*
|
|
2262
|
+
* CoordinateService - Handles coordinate conversions and board orientation logic
|
|
2388
2263
|
* @module services/CoordinateService
|
|
2389
2264
|
* @since 2.0.0
|
|
2390
2265
|
*/
|
|
@@ -2392,19 +2267,20 @@ var Chessboard = (function (exports) {
|
|
|
2392
2267
|
|
|
2393
2268
|
/**
|
|
2394
2269
|
* Service responsible for coordinate conversions and board orientation
|
|
2395
|
-
* @class
|
|
2270
|
+
* @class CoordinateService
|
|
2396
2271
|
*/
|
|
2397
2272
|
class CoordinateService {
|
|
2398
2273
|
/**
|
|
2399
|
-
*
|
|
2400
|
-
* @param {
|
|
2274
|
+
* Create a new CoordinateService instance
|
|
2275
|
+
* @param {Object} config - Board configuration
|
|
2401
2276
|
*/
|
|
2402
2277
|
constructor(config) {
|
|
2278
|
+
/** @type {Object} */
|
|
2403
2279
|
this.config = config;
|
|
2404
2280
|
}
|
|
2405
2281
|
|
|
2406
2282
|
/**
|
|
2407
|
-
*
|
|
2283
|
+
* Convert logical coordinates to real coordinates based on board orientation
|
|
2408
2284
|
* @param {number} row - Row index (0-7)
|
|
2409
2285
|
* @param {number} col - Column index (0-7)
|
|
2410
2286
|
* @returns {Array<number>} Real coordinates [row, col] (1-8)
|
|
@@ -2412,18 +2288,16 @@ var Chessboard = (function (exports) {
|
|
|
2412
2288
|
realCoord(row, col) {
|
|
2413
2289
|
let realRow = row;
|
|
2414
2290
|
let realCol = col;
|
|
2415
|
-
|
|
2416
2291
|
if (this.isWhiteOriented()) {
|
|
2417
2292
|
realRow = 7 - row;
|
|
2418
2293
|
} else {
|
|
2419
2294
|
realCol = 7 - col;
|
|
2420
2295
|
}
|
|
2421
|
-
|
|
2422
2296
|
return [realRow + 1, realCol + 1];
|
|
2423
2297
|
}
|
|
2424
2298
|
|
|
2425
2299
|
/**
|
|
2426
|
-
*
|
|
2300
|
+
* Convert board coordinates to square ID
|
|
2427
2301
|
* @param {number} row - Row index (0-7)
|
|
2428
2302
|
* @param {number} col - Column index (0-7)
|
|
2429
2303
|
* @returns {string} Square ID (e.g., 'e4')
|
|
@@ -2431,7 +2305,6 @@ var Chessboard = (function (exports) {
|
|
|
2431
2305
|
getSquareID(row, col) {
|
|
2432
2306
|
row = parseInt(row);
|
|
2433
2307
|
col = parseInt(col);
|
|
2434
|
-
|
|
2435
2308
|
if (this.isWhiteOriented()) {
|
|
2436
2309
|
row = 8 - row;
|
|
2437
2310
|
col = col + 1;
|
|
@@ -2439,7 +2312,6 @@ var Chessboard = (function (exports) {
|
|
|
2439
2312
|
row = row + 1;
|
|
2440
2313
|
col = 8 - col;
|
|
2441
2314
|
}
|
|
2442
|
-
|
|
2443
2315
|
if (col < 1 || col > 8 || row < 1 || row > 8) {
|
|
2444
2316
|
throw new ValidationError(
|
|
2445
2317
|
`Invalid board coordinates: row=${row}, col=${col}`,
|
|
@@ -2447,13 +2319,12 @@ var Chessboard = (function (exports) {
|
|
|
2447
2319
|
{ row, col }
|
|
2448
2320
|
);
|
|
2449
2321
|
}
|
|
2450
|
-
|
|
2451
2322
|
const letter = BOARD_LETTERS[col - 1];
|
|
2452
2323
|
return letter + row;
|
|
2453
2324
|
}
|
|
2454
2325
|
|
|
2455
2326
|
/**
|
|
2456
|
-
*
|
|
2327
|
+
* Convert square ID to board coordinates
|
|
2457
2328
|
* @param {string} squareId - Square ID (e.g., 'e4')
|
|
2458
2329
|
* @returns {Array<number>} Board coordinates [row, col] (0-7)
|
|
2459
2330
|
*/
|
|
@@ -2465,10 +2336,8 @@ var Chessboard = (function (exports) {
|
|
|
2465
2336
|
squareId
|
|
2466
2337
|
);
|
|
2467
2338
|
}
|
|
2468
|
-
|
|
2469
2339
|
const letter = squareId[0];
|
|
2470
2340
|
const number = parseInt(squareId[1]);
|
|
2471
|
-
|
|
2472
2341
|
const col = BOARD_LETTERS.indexOf(letter);
|
|
2473
2342
|
if (col === -1) {
|
|
2474
2343
|
throw new ValidationError(
|
|
@@ -2477,7 +2346,6 @@ var Chessboard = (function (exports) {
|
|
|
2477
2346
|
squareId
|
|
2478
2347
|
);
|
|
2479
2348
|
}
|
|
2480
|
-
|
|
2481
2349
|
if (number < 1 || number > 8) {
|
|
2482
2350
|
throw new ValidationError(
|
|
2483
2351
|
ERROR_MESSAGES.invalid_square + squareId,
|
|
@@ -2485,9 +2353,7 @@ var Chessboard = (function (exports) {
|
|
|
2485
2353
|
squareId
|
|
2486
2354
|
);
|
|
2487
2355
|
}
|
|
2488
|
-
|
|
2489
2356
|
let row, boardCol;
|
|
2490
|
-
|
|
2491
2357
|
if (this.isWhiteOriented()) {
|
|
2492
2358
|
row = 8 - number;
|
|
2493
2359
|
boardCol = col;
|
|
@@ -2495,12 +2361,11 @@ var Chessboard = (function (exports) {
|
|
|
2495
2361
|
row = number - 1;
|
|
2496
2362
|
boardCol = 7 - col;
|
|
2497
2363
|
}
|
|
2498
|
-
|
|
2499
2364
|
return [row, boardCol];
|
|
2500
2365
|
}
|
|
2501
2366
|
|
|
2502
2367
|
/**
|
|
2503
|
-
*
|
|
2368
|
+
* Convert pixel coordinates to square ID
|
|
2504
2369
|
* @param {number} x - X coordinate in pixels
|
|
2505
2370
|
* @param {number} y - Y coordinate in pixels
|
|
2506
2371
|
* @param {HTMLElement} boardElement - Board DOM element
|
|
@@ -2508,21 +2373,15 @@ var Chessboard = (function (exports) {
|
|
|
2508
2373
|
*/
|
|
2509
2374
|
pixelToSquareID(x, y, boardElement) {
|
|
2510
2375
|
if (!boardElement) return null;
|
|
2511
|
-
|
|
2512
2376
|
const rect = boardElement.getBoundingClientRect();
|
|
2513
2377
|
const { width, height } = rect;
|
|
2514
|
-
|
|
2515
|
-
// Check if coordinates are within board bounds
|
|
2516
2378
|
if (x < 0 || x >= width || y < 0 || y >= height) {
|
|
2517
2379
|
return null;
|
|
2518
2380
|
}
|
|
2519
|
-
|
|
2520
2381
|
const squareWidth = width / 8;
|
|
2521
2382
|
const squareHeight = height / 8;
|
|
2522
|
-
|
|
2523
2383
|
const col = Math.floor(x / squareWidth);
|
|
2524
2384
|
const row = Math.floor(y / squareHeight);
|
|
2525
|
-
|
|
2526
2385
|
try {
|
|
2527
2386
|
return this.getSquareID(row, col);
|
|
2528
2387
|
} catch (error) {
|
|
@@ -2531,25 +2390,21 @@ var Chessboard = (function (exports) {
|
|
|
2531
2390
|
}
|
|
2532
2391
|
|
|
2533
2392
|
/**
|
|
2534
|
-
*
|
|
2393
|
+
* Convert square ID to pixel coordinates
|
|
2535
2394
|
* @param {string} squareId - Square ID (e.g., 'e4')
|
|
2536
2395
|
* @param {HTMLElement} boardElement - Board DOM element
|
|
2537
2396
|
* @returns {Object|null} Pixel coordinates {x, y} or null if invalid
|
|
2538
2397
|
*/
|
|
2539
2398
|
squareIDToPixel(squareId, boardElement) {
|
|
2540
2399
|
if (!boardElement) return null;
|
|
2541
|
-
|
|
2542
2400
|
try {
|
|
2543
2401
|
const [row, col] = this.getCoordinatesFromSquareID(squareId);
|
|
2544
2402
|
const rect = boardElement.getBoundingClientRect();
|
|
2545
2403
|
const { width, height } = rect;
|
|
2546
|
-
|
|
2547
2404
|
const squareWidth = width / 8;
|
|
2548
2405
|
const squareHeight = height / 8;
|
|
2549
|
-
|
|
2550
2406
|
const x = col * squareWidth;
|
|
2551
2407
|
const y = row * squareHeight;
|
|
2552
|
-
|
|
2553
2408
|
return { x, y };
|
|
2554
2409
|
} catch (error) {
|
|
2555
2410
|
return null;
|
|
@@ -2557,7 +2412,7 @@ var Chessboard = (function (exports) {
|
|
|
2557
2412
|
}
|
|
2558
2413
|
|
|
2559
2414
|
/**
|
|
2560
|
-
*
|
|
2415
|
+
* Get the center pixel coordinates of a square
|
|
2561
2416
|
* @param {string} squareId - Square ID (e.g., 'e4')
|
|
2562
2417
|
* @param {HTMLElement} boardElement - Board DOM element
|
|
2563
2418
|
* @returns {Object|null} Center coordinates {x, y} or null if invalid
|
|
@@ -2565,11 +2420,9 @@ var Chessboard = (function (exports) {
|
|
|
2565
2420
|
getSquareCenter(squareId, boardElement) {
|
|
2566
2421
|
const coords = this.squareIDToPixel(squareId, boardElement);
|
|
2567
2422
|
if (!coords) return null;
|
|
2568
|
-
|
|
2569
2423
|
const rect = boardElement.getBoundingClientRect();
|
|
2570
2424
|
const squareWidth = rect.width / 8;
|
|
2571
2425
|
const squareHeight = rect.height / 8;
|
|
2572
|
-
|
|
2573
2426
|
return {
|
|
2574
2427
|
x: coords.x + squareWidth / 2,
|
|
2575
2428
|
y: coords.y + squareHeight / 2
|
|
@@ -2577,7 +2430,7 @@ var Chessboard = (function (exports) {
|
|
|
2577
2430
|
}
|
|
2578
2431
|
|
|
2579
2432
|
/**
|
|
2580
|
-
*
|
|
2433
|
+
* Calculate the distance between two squares
|
|
2581
2434
|
* @param {string} fromSquare - Source square ID
|
|
2582
2435
|
* @param {string} toSquare - Target square ID
|
|
2583
2436
|
* @returns {number} Distance between squares
|
|
@@ -2586,10 +2439,8 @@ var Chessboard = (function (exports) {
|
|
|
2586
2439
|
try {
|
|
2587
2440
|
const [fromRow, fromCol] = this.getCoordinatesFromSquareID(fromSquare);
|
|
2588
2441
|
const [toRow, toCol] = this.getCoordinatesFromSquareID(toSquare);
|
|
2589
|
-
|
|
2590
2442
|
const rowDiff = Math.abs(toRow - fromRow);
|
|
2591
2443
|
const colDiff = Math.abs(toCol - fromCol);
|
|
2592
|
-
|
|
2593
2444
|
return Math.sqrt(rowDiff * rowDiff + colDiff * colDiff);
|
|
2594
2445
|
} catch (error) {
|
|
2595
2446
|
return 0;
|
|
@@ -2597,7 +2448,7 @@ var Chessboard = (function (exports) {
|
|
|
2597
2448
|
}
|
|
2598
2449
|
|
|
2599
2450
|
/**
|
|
2600
|
-
*
|
|
2451
|
+
* Check if the board is oriented from white's perspective
|
|
2601
2452
|
* @returns {boolean} True if white-oriented
|
|
2602
2453
|
*/
|
|
2603
2454
|
isWhiteOriented() {
|
|
@@ -2605,7 +2456,7 @@ var Chessboard = (function (exports) {
|
|
|
2605
2456
|
}
|
|
2606
2457
|
|
|
2607
2458
|
/**
|
|
2608
|
-
*
|
|
2459
|
+
* Check if the board is oriented from black's perspective
|
|
2609
2460
|
* @returns {boolean} True if black-oriented
|
|
2610
2461
|
*/
|
|
2611
2462
|
isBlackOriented() {
|
|
@@ -2613,31 +2464,31 @@ var Chessboard = (function (exports) {
|
|
|
2613
2464
|
}
|
|
2614
2465
|
|
|
2615
2466
|
/**
|
|
2616
|
-
*
|
|
2467
|
+
* Flip the board orientation
|
|
2617
2468
|
*/
|
|
2618
2469
|
flipOrientation() {
|
|
2619
2470
|
this.config.orientation = this.isWhiteOriented() ? 'b' : 'w';
|
|
2620
2471
|
}
|
|
2621
2472
|
|
|
2622
2473
|
/**
|
|
2623
|
-
*
|
|
2624
|
-
* @param {string} orientation - 'w'
|
|
2625
|
-
* @throws {ValidationError}
|
|
2474
|
+
* Set the board orientation
|
|
2475
|
+
* @param {string} orientation - 'w', 'b', 'white', or 'black'
|
|
2476
|
+
* @throws {ValidationError} If orientation is invalid
|
|
2626
2477
|
*/
|
|
2627
2478
|
setOrientation(orientation) {
|
|
2628
|
-
|
|
2479
|
+
const normalized = orientation === 'white' ? 'w' : orientation === 'black' ? 'b' : orientation;
|
|
2480
|
+
if (normalized !== 'w' && normalized !== 'b') {
|
|
2629
2481
|
throw new ValidationError(
|
|
2630
2482
|
ERROR_MESSAGES.invalid_orientation + orientation,
|
|
2631
2483
|
'orientation',
|
|
2632
2484
|
orientation
|
|
2633
2485
|
);
|
|
2634
2486
|
}
|
|
2635
|
-
|
|
2636
|
-
this.config.orientation = orientation;
|
|
2487
|
+
this.config.orientation = normalized;
|
|
2637
2488
|
}
|
|
2638
2489
|
|
|
2639
2490
|
/**
|
|
2640
|
-
*
|
|
2491
|
+
* Get the current orientation
|
|
2641
2492
|
* @returns {string} Current orientation ('w' or 'b')
|
|
2642
2493
|
*/
|
|
2643
2494
|
getOrientation() {
|
|
@@ -2645,50 +2496,21 @@ var Chessboard = (function (exports) {
|
|
|
2645
2496
|
}
|
|
2646
2497
|
|
|
2647
2498
|
/**
|
|
2648
|
-
*
|
|
2649
|
-
* @param {string} orientation - New orientation ('w', 'b', 'white', 'black')
|
|
2650
|
-
*/
|
|
2651
|
-
setOrientation(orientation) {
|
|
2652
|
-
// Normalize orientation
|
|
2653
|
-
const normalizedOrientation = orientation === 'white' ? 'w' :
|
|
2654
|
-
orientation === 'black' ? 'b' : orientation;
|
|
2655
|
-
|
|
2656
|
-
if (normalizedOrientation !== 'w' && normalizedOrientation !== 'b') {
|
|
2657
|
-
throw new ValidationError(
|
|
2658
|
-
ERROR_MESSAGES.invalid_orientation + orientation,
|
|
2659
|
-
'orientation',
|
|
2660
|
-
orientation
|
|
2661
|
-
);
|
|
2662
|
-
}
|
|
2663
|
-
|
|
2664
|
-
this.config.orientation = normalizedOrientation;
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
|
-
/**
|
|
2668
|
-
* Flips the board orientation
|
|
2669
|
-
*/
|
|
2670
|
-
flipOrientation() {
|
|
2671
|
-
this.config.orientation = this.isWhiteOriented() ? 'b' : 'w';
|
|
2672
|
-
}
|
|
2673
|
-
|
|
2674
|
-
/**
|
|
2675
|
-
* Gets all square IDs in order
|
|
2499
|
+
* Get all square IDs in order
|
|
2676
2500
|
* @returns {Array<string>} Array of all square IDs
|
|
2677
2501
|
*/
|
|
2678
2502
|
getAllSquareIDs() {
|
|
2679
2503
|
const squares = [];
|
|
2680
|
-
|
|
2681
2504
|
for (let row = 0; row < 8; row++) {
|
|
2682
2505
|
for (let col = 0; col < 8; col++) {
|
|
2683
2506
|
squares.push(this.getSquareID(row, col));
|
|
2684
2507
|
}
|
|
2685
2508
|
}
|
|
2686
|
-
|
|
2687
2509
|
return squares;
|
|
2688
2510
|
}
|
|
2689
2511
|
|
|
2690
2512
|
/**
|
|
2691
|
-
*
|
|
2513
|
+
* Get all squares in a specific rank (row)
|
|
2692
2514
|
* @param {number} rank - Rank number (1-8)
|
|
2693
2515
|
* @returns {Array<string>} Array of square IDs in the rank
|
|
2694
2516
|
*/
|
|
@@ -2700,19 +2522,16 @@ var Chessboard = (function (exports) {
|
|
|
2700
2522
|
rank
|
|
2701
2523
|
);
|
|
2702
2524
|
}
|
|
2703
|
-
|
|
2704
2525
|
const squares = [];
|
|
2705
|
-
|
|
2706
2526
|
for (let col = 0; col < 8; col++) {
|
|
2707
2527
|
const row = this.isWhiteOriented() ? 8 - rank : rank - 1;
|
|
2708
2528
|
squares.push(this.getSquareID(row, col));
|
|
2709
2529
|
}
|
|
2710
|
-
|
|
2711
2530
|
return squares;
|
|
2712
2531
|
}
|
|
2713
2532
|
|
|
2714
2533
|
/**
|
|
2715
|
-
*
|
|
2534
|
+
* Get all squares in a specific file (column)
|
|
2716
2535
|
* @param {string} file - File letter (a-h)
|
|
2717
2536
|
* @returns {Array<string>} Array of square IDs in the file
|
|
2718
2537
|
*/
|
|
@@ -2725,13 +2544,10 @@ var Chessboard = (function (exports) {
|
|
|
2725
2544
|
file
|
|
2726
2545
|
);
|
|
2727
2546
|
}
|
|
2728
|
-
|
|
2729
2547
|
const squares = [];
|
|
2730
|
-
|
|
2731
2548
|
for (let row = 0; row < 8; row++) {
|
|
2732
2549
|
squares.push(this.getSquareID(row, col));
|
|
2733
2550
|
}
|
|
2734
|
-
|
|
2735
2551
|
return squares;
|
|
2736
2552
|
}
|
|
2737
2553
|
}
|
|
@@ -3111,6 +2927,67 @@ var Chessboard = (function (exports) {
|
|
|
3111
2927
|
}
|
|
3112
2928
|
};
|
|
3113
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
|
+
|
|
3114
2991
|
/**
|
|
3115
2992
|
* Service for managing events and user interactions
|
|
3116
2993
|
* @module services/EventService
|
|
@@ -3143,8 +3020,8 @@ var Chessboard = (function (exports) {
|
|
|
3143
3020
|
this.promoting = false;
|
|
3144
3021
|
this.isAnimating = false;
|
|
3145
3022
|
|
|
3146
|
-
//
|
|
3147
|
-
this.
|
|
3023
|
+
// Listener manager per gestione centralizzata
|
|
3024
|
+
this.listenerManager = new ListenerManager();
|
|
3148
3025
|
}
|
|
3149
3026
|
|
|
3150
3027
|
/**
|
|
@@ -3154,8 +3031,8 @@ var Chessboard = (function (exports) {
|
|
|
3154
3031
|
* @param {Function} onPieceLeave - Callback for piece leave
|
|
3155
3032
|
*/
|
|
3156
3033
|
addListeners(onSquareClick, onPieceHover, onPieceLeave) {
|
|
3157
|
-
//
|
|
3158
|
-
this.
|
|
3034
|
+
// Rimuovi tutti i listener esistenti
|
|
3035
|
+
this.listenerManager.removeAll();
|
|
3159
3036
|
|
|
3160
3037
|
const squares = this.boardService.getAllSquares();
|
|
3161
3038
|
|
|
@@ -3173,8 +3050,6 @@ var Chessboard = (function (exports) {
|
|
|
3173
3050
|
* @param {Function} onPieceLeave - Leave callback
|
|
3174
3051
|
*/
|
|
3175
3052
|
_addSquareListeners(square, onSquareClick, onPieceHover, onPieceLeave) {
|
|
3176
|
-
const listeners = [];
|
|
3177
|
-
|
|
3178
3053
|
// Throttled hover handlers for performance
|
|
3179
3054
|
const throttledHover = rafThrottle((e) => {
|
|
3180
3055
|
if (!this.clicked && this.config.hints) {
|
|
@@ -3244,26 +3119,13 @@ var Chessboard = (function (exports) {
|
|
|
3244
3119
|
}
|
|
3245
3120
|
};
|
|
3246
3121
|
|
|
3247
|
-
//
|
|
3248
|
-
square.element
|
|
3249
|
-
square.element
|
|
3250
|
-
square.element
|
|
3251
|
-
|
|
3252
|
-
square.element
|
|
3253
|
-
square.element
|
|
3254
|
-
square.element.addEventListener('touchend', handleTouchEnd);
|
|
3255
|
-
|
|
3256
|
-
// Store listeners for cleanup
|
|
3257
|
-
listeners.push(
|
|
3258
|
-
{ element: square.element, type: 'mouseover', handler: throttledHover },
|
|
3259
|
-
{ element: square.element, type: 'mouseout', handler: throttledLeave },
|
|
3260
|
-
{ element: square.element, type: 'click', handler: handleClick },
|
|
3261
|
-
{ element: square.element, type: 'touchstart', handler: handleTouchStart },
|
|
3262
|
-
{ element: square.element, type: 'touchmove', handler: handleTouchMove },
|
|
3263
|
-
{ element: square.element, type: 'touchend', handler: handleTouchEnd }
|
|
3264
|
-
);
|
|
3265
|
-
|
|
3266
|
-
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);
|
|
3267
3129
|
}
|
|
3268
3130
|
|
|
3269
3131
|
/**
|
|
@@ -3280,18 +3142,14 @@ var Chessboard = (function (exports) {
|
|
|
3280
3142
|
*/
|
|
3281
3143
|
createDragFunction(square, piece, onDragStart, onDragMove, onDrop, onSnapback, onMove, onRemove) {
|
|
3282
3144
|
return (event) => {
|
|
3283
|
-
event.preventDefault();
|
|
3284
|
-
|
|
3285
3145
|
if (!this.config.draggable || !piece || this.isAnimating) {
|
|
3286
3146
|
return;
|
|
3287
3147
|
}
|
|
3288
3148
|
|
|
3289
3149
|
const originalFrom = square;
|
|
3290
|
-
let isDragging = false;
|
|
3291
3150
|
let from = originalFrom;
|
|
3292
3151
|
let to = square;
|
|
3293
3152
|
let previousHighlight = null;
|
|
3294
|
-
|
|
3295
3153
|
const img = piece.element;
|
|
3296
3154
|
|
|
3297
3155
|
if (!this.moveService.canMove(from)) {
|
|
@@ -3302,6 +3160,15 @@ var Chessboard = (function (exports) {
|
|
|
3302
3160
|
const isTouch = event.type && event.type.startsWith('touch');
|
|
3303
3161
|
const startX = event.clientX || (event.touches && event.touches[0]?.clientX) || 0;
|
|
3304
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;
|
|
3305
3172
|
|
|
3306
3173
|
// --- Touch scroll lock helper ---
|
|
3307
3174
|
const addScrollLock = () => {
|
|
@@ -3313,10 +3180,6 @@ var Chessboard = (function (exports) {
|
|
|
3313
3180
|
|
|
3314
3181
|
// --- MOVE HANDLER (mouse + touch unified) ---
|
|
3315
3182
|
const moveAt = (event) => {
|
|
3316
|
-
const boardElement = this.boardService.element;
|
|
3317
|
-
const squareSize = boardElement.offsetWidth / 8;
|
|
3318
|
-
|
|
3319
|
-
// Get mouse/touch coordinates
|
|
3320
3183
|
let clientX, clientY;
|
|
3321
3184
|
if (event.touches && event.touches[0]) {
|
|
3322
3185
|
clientX = event.touches[0].clientX;
|
|
@@ -3325,77 +3188,54 @@ var Chessboard = (function (exports) {
|
|
|
3325
3188
|
clientX = event.clientX;
|
|
3326
3189
|
clientY = event.clientY;
|
|
3327
3190
|
}
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
const
|
|
3331
|
-
const x = clientX - boardRect.left - (squareSize / 2);
|
|
3332
|
-
const y = clientY - boardRect.top - (squareSize / 2);
|
|
3333
|
-
|
|
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;
|
|
3334
3194
|
img.style.left = x + 'px';
|
|
3335
3195
|
img.style.top = y + 'px';
|
|
3336
|
-
|
|
3337
3196
|
return true;
|
|
3338
3197
|
};
|
|
3339
3198
|
|
|
3340
3199
|
// --- DRAG MOVE (mouse + touch) ---
|
|
3200
|
+
const moveThreshold = 5; // px
|
|
3341
3201
|
const onPointerMove = (event) => {
|
|
3342
|
-
// For touch, only handle the first finger
|
|
3343
3202
|
if (event.touches && event.touches.length > 1) return;
|
|
3344
3203
|
if (event.touches && !event.touches[0]) return;
|
|
3345
|
-
if (event.type && event.type.startsWith('touch')) event.preventDefault();
|
|
3346
|
-
|
|
3347
3204
|
const currentX = event.clientX || (event.touches && event.touches[0]?.clientX) || 0;
|
|
3348
3205
|
const currentY = event.clientY || (event.touches && event.touches[0]?.clientY) || 0;
|
|
3349
3206
|
const deltaX = Math.abs(currentX - startX);
|
|
3350
3207
|
const deltaY = Math.abs(currentY - startY);
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
isDragging = true;
|
|
3355
|
-
// Inizio drag: blocca update board
|
|
3208
|
+
if (!dragStarted && (deltaX > moveThreshold || deltaY > moveThreshold)) {
|
|
3209
|
+
dragStarted = true;
|
|
3210
|
+
if (isTouch) addScrollLock();
|
|
3356
3211
|
if (this.chessboard) this.chessboard._isDragging = true;
|
|
3357
|
-
|
|
3358
|
-
// Mostra hint all'inizio del drag se attivi
|
|
3359
3212
|
if (this.config.hints && typeof this.chessboard._boundOnPieceHover === 'function') {
|
|
3360
3213
|
this.chessboard._boundOnPieceHover(from);
|
|
3361
3214
|
}
|
|
3362
|
-
|
|
3363
|
-
// Set up drag state
|
|
3364
3215
|
if (!this.config.clickable) {
|
|
3365
3216
|
this.clicked = null;
|
|
3366
3217
|
this.clicked = from;
|
|
3367
3218
|
} else if (!this.clicked) {
|
|
3368
3219
|
this.clicked = from;
|
|
3369
3220
|
}
|
|
3370
|
-
|
|
3371
|
-
// Visual feedback
|
|
3372
3221
|
if (this.config.clickable) {
|
|
3373
3222
|
from.select();
|
|
3374
3223
|
}
|
|
3375
|
-
|
|
3376
|
-
// Prepare piece for dragging
|
|
3377
3224
|
img.style.position = 'absolute';
|
|
3378
3225
|
img.style.zIndex = '100';
|
|
3379
3226
|
img.classList.add('dragging');
|
|
3380
|
-
|
|
3381
3227
|
DragOptimizations.enableForDrag(img);
|
|
3382
|
-
|
|
3383
|
-
// Lock scroll for touch
|
|
3384
|
-
if (isTouch) addScrollLock();
|
|
3385
|
-
|
|
3386
|
-
// Call drag start callback
|
|
3387
3228
|
if (!onDragStart(square, piece)) {
|
|
3388
3229
|
return;
|
|
3389
3230
|
}
|
|
3390
3231
|
}
|
|
3391
|
-
|
|
3392
|
-
if (
|
|
3393
|
-
|
|
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;
|
|
3394
3237
|
if (!moveAt(event)) ;
|
|
3395
|
-
|
|
3396
3238
|
// Update target square
|
|
3397
|
-
const boardElement = this.boardService.element;
|
|
3398
|
-
const boardRect = boardElement.getBoundingClientRect();
|
|
3399
3239
|
let clientX, clientY;
|
|
3400
3240
|
if (event.touches && event.touches[0]) {
|
|
3401
3241
|
clientX = event.touches[0].clientX;
|
|
@@ -3406,22 +3246,16 @@ var Chessboard = (function (exports) {
|
|
|
3406
3246
|
}
|
|
3407
3247
|
const x = clientX - boardRect.left;
|
|
3408
3248
|
const y = clientY - boardRect.top;
|
|
3409
|
-
|
|
3410
3249
|
let newTo = null;
|
|
3411
3250
|
if (x >= 0 && x <= boardRect.width && y >= 0 && y <= boardRect.height) {
|
|
3412
3251
|
const squareId = this.coordinateService.pixelToSquareID(x, y, boardElement);
|
|
3413
3252
|
newTo = squareId ? this.boardService.getSquare(squareId) : null;
|
|
3414
3253
|
}
|
|
3415
|
-
|
|
3416
3254
|
to = newTo;
|
|
3417
3255
|
onDragMove(from, to, piece);
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
to?.highlight();
|
|
3422
|
-
previousHighlight?.dehighlight();
|
|
3423
|
-
previousHighlight = to;
|
|
3424
|
-
}
|
|
3256
|
+
if (to) to.highlight();
|
|
3257
|
+
if (previousHighlight && previousHighlight !== to) previousHighlight.dehighlight();
|
|
3258
|
+
previousHighlight = to;
|
|
3425
3259
|
};
|
|
3426
3260
|
|
|
3427
3261
|
// --- DRAG END (mouse + touch) ---
|
|
@@ -3432,26 +3266,19 @@ var Chessboard = (function (exports) {
|
|
|
3432
3266
|
document.removeEventListener('touchmove', onPointerMove);
|
|
3433
3267
|
window.removeEventListener('touchend', onPointerUp);
|
|
3434
3268
|
if (isTouch) removeScrollLock();
|
|
3435
|
-
// Fine drag: sblocca update board
|
|
3436
3269
|
if (this.chessboard) this.chessboard._isDragging = false;
|
|
3437
|
-
|
|
3438
|
-
// Rimuovi hint alla fine del drag se attivi
|
|
3439
3270
|
if (this.config.hints && typeof this.chessboard._boundOnPieceLeave === 'function') {
|
|
3440
3271
|
this.chessboard._boundOnPieceLeave(from);
|
|
3441
3272
|
}
|
|
3442
|
-
|
|
3443
|
-
if (!isDragging) {
|
|
3273
|
+
if (!dragStarted) {
|
|
3444
3274
|
return;
|
|
3445
3275
|
}
|
|
3446
|
-
|
|
3447
3276
|
img.style.zIndex = '20';
|
|
3448
3277
|
img.classList.remove('dragging');
|
|
3449
3278
|
img.style.willChange = 'auto';
|
|
3450
|
-
|
|
3451
3279
|
// Handle drop
|
|
3452
3280
|
const dropResult = onDrop(originalFrom, to, piece);
|
|
3453
3281
|
const isTrashDrop = !to && (this.config.dropOffBoard === 'trash' || dropResult === 'trash');
|
|
3454
|
-
|
|
3455
3282
|
if (isTrashDrop) {
|
|
3456
3283
|
this._handleTrashDrop(originalFrom, onRemove);
|
|
3457
3284
|
} else if (!to) {
|
|
@@ -3465,16 +3292,12 @@ var Chessboard = (function (exports) {
|
|
|
3465
3292
|
}
|
|
3466
3293
|
};
|
|
3467
3294
|
|
|
3468
|
-
//
|
|
3295
|
+
// Attach listeners (mouse + touch)
|
|
3469
3296
|
window.addEventListener('mouseup', onPointerUp, { once: true });
|
|
3470
3297
|
document.addEventListener('mousemove', onPointerMove);
|
|
3471
3298
|
img.addEventListener('mouseup', onPointerUp, { once: true });
|
|
3472
|
-
// Touch events
|
|
3473
3299
|
window.addEventListener('touchend', onPointerUp, { once: true });
|
|
3474
3300
|
document.addEventListener('touchmove', onPointerMove, { passive: false });
|
|
3475
|
-
|
|
3476
|
-
// Per robustezza: se il drag parte da touch, blocca subito lo scroll
|
|
3477
|
-
if (isTouch) addScrollLock();
|
|
3478
3301
|
};
|
|
3479
3302
|
}
|
|
3480
3303
|
|
|
@@ -3967,33 +3790,21 @@ var Chessboard = (function (exports) {
|
|
|
3967
3790
|
* Removes all existing event listeners
|
|
3968
3791
|
*/
|
|
3969
3792
|
removeListeners() {
|
|
3970
|
-
this.
|
|
3971
|
-
listeners.forEach(({ element, type, handler }) => {
|
|
3972
|
-
element.removeEventListener(type, handler);
|
|
3973
|
-
});
|
|
3974
|
-
});
|
|
3975
|
-
|
|
3976
|
-
this.eventListeners.clear();
|
|
3793
|
+
this.listenerManager.removeAll();
|
|
3977
3794
|
}
|
|
3978
3795
|
|
|
3979
3796
|
/**
|
|
3980
3797
|
* Removes all event listeners
|
|
3981
3798
|
*/
|
|
3982
3799
|
removeAllListeners() {
|
|
3983
|
-
this.
|
|
3984
|
-
listeners.forEach(({ element, type, handler }) => {
|
|
3985
|
-
element.removeEventListener(type, handler);
|
|
3986
|
-
});
|
|
3987
|
-
});
|
|
3988
|
-
|
|
3989
|
-
this.eventListeners.clear();
|
|
3800
|
+
this.listenerManager.removeAll();
|
|
3990
3801
|
}
|
|
3991
3802
|
|
|
3992
3803
|
/**
|
|
3993
3804
|
* Cleans up resources
|
|
3994
3805
|
*/
|
|
3995
3806
|
destroy() {
|
|
3996
|
-
this.
|
|
3807
|
+
this.listenerManager.destroy();
|
|
3997
3808
|
this.clicked = null;
|
|
3998
3809
|
this.promoting = false;
|
|
3999
3810
|
this.isAnimating = false;
|
|
@@ -4001,7 +3812,7 @@ var Chessboard = (function (exports) {
|
|
|
4001
3812
|
}
|
|
4002
3813
|
|
|
4003
3814
|
/**
|
|
4004
|
-
*
|
|
3815
|
+
* MoveService - Handles chess move management and validation
|
|
4005
3816
|
* @module services/MoveService
|
|
4006
3817
|
* @since 2.0.0
|
|
4007
3818
|
*/
|
|
@@ -4009,13 +3820,13 @@ var Chessboard = (function (exports) {
|
|
|
4009
3820
|
|
|
4010
3821
|
/**
|
|
4011
3822
|
* Service responsible for move management and validation
|
|
4012
|
-
* @class
|
|
3823
|
+
* @class MoveService
|
|
4013
3824
|
*/
|
|
4014
3825
|
class MoveService {
|
|
4015
3826
|
/**
|
|
4016
|
-
*
|
|
4017
|
-
* @param {
|
|
4018
|
-
* @param {
|
|
3827
|
+
* Create a new MoveService instance
|
|
3828
|
+
* @param {Object} config - Board configuration
|
|
3829
|
+
* @param {Object} positionService - Position service instance
|
|
4019
3830
|
*/
|
|
4020
3831
|
constructor(config, positionService) {
|
|
4021
3832
|
this.config = config;
|
|
@@ -4025,43 +3836,32 @@ var Chessboard = (function (exports) {
|
|
|
4025
3836
|
}
|
|
4026
3837
|
|
|
4027
3838
|
/**
|
|
4028
|
-
*
|
|
3839
|
+
* Check if a piece on a square can move
|
|
4029
3840
|
* @param {Square} square - Square to check
|
|
4030
3841
|
* @returns {boolean} True if piece can move
|
|
4031
3842
|
*/
|
|
4032
3843
|
canMove(square) {
|
|
4033
3844
|
if (!square.piece) return false;
|
|
4034
|
-
|
|
4035
3845
|
const { movableColors, onlyLegalMoves } = this.config;
|
|
4036
|
-
|
|
4037
3846
|
if (movableColors === 'none') return false;
|
|
4038
3847
|
if (movableColors === 'w' && square.piece.color === 'b') return false;
|
|
4039
3848
|
if (movableColors === 'b' && square.piece.color === 'w') return false;
|
|
4040
|
-
|
|
4041
3849
|
if (!onlyLegalMoves) return true;
|
|
4042
|
-
|
|
4043
|
-
// Check if position service and game are available
|
|
4044
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
4045
|
-
return false;
|
|
4046
|
-
}
|
|
4047
|
-
|
|
3850
|
+
if (!this.positionService || !this.positionService.getGame()) return false;
|
|
4048
3851
|
const game = this.positionService.getGame();
|
|
4049
3852
|
return square.piece.color === game.turn();
|
|
4050
3853
|
}
|
|
4051
3854
|
|
|
4052
3855
|
/**
|
|
4053
|
-
*
|
|
3856
|
+
* Convert various move formats to a Move instance
|
|
4054
3857
|
* @param {string|Move|Object} move - Move in various formats
|
|
4055
3858
|
* @param {Object} squares - All board squares
|
|
4056
3859
|
* @returns {Move} Move instance
|
|
4057
3860
|
* @throws {MoveError} When move format is invalid
|
|
4058
3861
|
*/
|
|
4059
3862
|
convertMove(move, squares) {
|
|
4060
|
-
if (move instanceof Move$1)
|
|
4061
|
-
return move;
|
|
4062
|
-
}
|
|
3863
|
+
if (move instanceof Move$1) return move;
|
|
4063
3864
|
if (typeof move === 'object' && move.from && move.to) {
|
|
4064
|
-
// Se sono id, converto in oggetti; se sono già oggetti, li uso direttamente
|
|
4065
3865
|
const fromSquare = typeof move.from === 'string' ? squares[move.from] : move.from;
|
|
4066
3866
|
const toSquare = typeof move.to === 'string' ? squares[move.to] : move.to;
|
|
4067
3867
|
if (!fromSquare || !toSquare) throw new MoveError(ERROR_MESSAGES.invalid_move_format, move.from, move.to);
|
|
@@ -4080,13 +3880,12 @@ var Chessboard = (function (exports) {
|
|
|
4080
3880
|
}
|
|
4081
3881
|
|
|
4082
3882
|
/**
|
|
4083
|
-
*
|
|
3883
|
+
* Check if a move is legal
|
|
4084
3884
|
* @param {Move} move - Move to check
|
|
4085
3885
|
* @returns {boolean} True if move is legal
|
|
4086
3886
|
*/
|
|
4087
3887
|
isLegalMove(move) {
|
|
4088
3888
|
const legalMoves = this.getLegalMoves(move.from.id);
|
|
4089
|
-
|
|
4090
3889
|
return legalMoves.some(legalMove =>
|
|
4091
3890
|
legalMove.to === move.to.id &&
|
|
4092
3891
|
move.promotion === legalMove.promotion
|
|
@@ -4094,180 +3893,100 @@ var Chessboard = (function (exports) {
|
|
|
4094
3893
|
}
|
|
4095
3894
|
|
|
4096
3895
|
/**
|
|
4097
|
-
*
|
|
3896
|
+
* Get all legal moves for a square or the entire position
|
|
4098
3897
|
* @param {string} [from] - Square to get moves from (optional)
|
|
4099
3898
|
* @param {boolean} [verbose=true] - Whether to return verbose move objects
|
|
4100
3899
|
* @returns {Array} Array of legal moves
|
|
4101
3900
|
*/
|
|
4102
3901
|
getLegalMoves(from = null, verbose = true) {
|
|
4103
|
-
|
|
4104
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
4105
|
-
return [];
|
|
4106
|
-
}
|
|
4107
|
-
|
|
3902
|
+
if (!this.positionService || !this.positionService.getGame()) return [];
|
|
4108
3903
|
const game = this.positionService.getGame();
|
|
4109
|
-
|
|
4110
3904
|
if (!game) return [];
|
|
4111
|
-
|
|
4112
3905
|
const options = { verbose };
|
|
4113
|
-
if (from)
|
|
4114
|
-
options.square = from;
|
|
4115
|
-
}
|
|
4116
|
-
|
|
3906
|
+
if (from) options.square = from;
|
|
4117
3907
|
return game.moves(options);
|
|
4118
3908
|
}
|
|
4119
3909
|
|
|
4120
3910
|
/**
|
|
4121
|
-
*
|
|
3911
|
+
* Get legal moves with caching for performance
|
|
4122
3912
|
* @param {Square} square - Square to get moves from
|
|
4123
3913
|
* @returns {Array} Array of legal moves
|
|
4124
3914
|
*/
|
|
4125
3915
|
getCachedLegalMoves(square) {
|
|
4126
|
-
|
|
4127
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
4128
|
-
return [];
|
|
4129
|
-
}
|
|
4130
|
-
|
|
3916
|
+
if (!this.positionService || !this.positionService.getGame()) return [];
|
|
4131
3917
|
const game = this.positionService.getGame();
|
|
4132
3918
|
if (!game) return [];
|
|
4133
|
-
|
|
4134
3919
|
const cacheKey = `${square.id}-${game.fen()}`;
|
|
4135
3920
|
let moves = this._movesCache.get(cacheKey);
|
|
4136
|
-
|
|
4137
3921
|
if (!moves) {
|
|
4138
3922
|
moves = game.moves({ square: square.id, verbose: true });
|
|
4139
3923
|
this._movesCache.set(cacheKey, moves);
|
|
4140
|
-
|
|
4141
|
-
// Clear cache after a short delay to prevent memory buildup
|
|
4142
|
-
if (this._cacheTimeout) {
|
|
4143
|
-
clearTimeout(this._cacheTimeout);
|
|
4144
|
-
}
|
|
4145
|
-
|
|
3924
|
+
if (this._cacheTimeout) clearTimeout(this._cacheTimeout);
|
|
4146
3925
|
this._cacheTimeout = setTimeout(() => {
|
|
4147
3926
|
this._movesCache.clear();
|
|
4148
3927
|
}, 1000);
|
|
4149
3928
|
}
|
|
4150
|
-
|
|
4151
3929
|
return moves;
|
|
4152
3930
|
}
|
|
4153
3931
|
|
|
4154
3932
|
/**
|
|
4155
|
-
*
|
|
4156
|
-
* @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)
|
|
4157
3935
|
* @returns {Object|null} Move result from chess.js or null if invalid
|
|
4158
3936
|
*/
|
|
4159
3937
|
executeMove(move) {
|
|
4160
|
-
if (!(move instanceof Move$1)) throw new Error('executeMove
|
|
4161
|
-
if (!this.positionService || !this.positionService.getGame())
|
|
4162
|
-
return null;
|
|
4163
|
-
}
|
|
3938
|
+
if (!(move instanceof Move$1)) throw new Error('executeMove requires a Move object');
|
|
3939
|
+
if (!this.positionService || !this.positionService.getGame()) return null;
|
|
4164
3940
|
const game = this.positionService.getGame();
|
|
4165
3941
|
if (!game) return null;
|
|
4166
|
-
const moveOptions = {
|
|
4167
|
-
|
|
4168
|
-
to: move.to.id
|
|
4169
|
-
};
|
|
4170
|
-
if (move.hasPromotion()) {
|
|
4171
|
-
moveOptions.promotion = move.promotion;
|
|
4172
|
-
}
|
|
3942
|
+
const moveOptions = { from: move.from.id, to: move.to.id };
|
|
3943
|
+
if (move.hasPromotion()) moveOptions.promotion = move.promotion;
|
|
4173
3944
|
const result = game.move(moveOptions);
|
|
4174
3945
|
return result;
|
|
4175
3946
|
}
|
|
4176
3947
|
|
|
4177
3948
|
/**
|
|
4178
|
-
*
|
|
4179
|
-
* @param {Move} move -
|
|
3949
|
+
* Determine if a move requires promotion
|
|
3950
|
+
* @param {Move} move - Must be a Move object
|
|
4180
3951
|
* @returns {boolean}
|
|
4181
3952
|
*/
|
|
4182
3953
|
requiresPromotion(move) {
|
|
4183
|
-
if (!(move instanceof Move$1)) throw new Error('requiresPromotion
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
if (!this.config.onlyLegalMoves) {
|
|
4187
|
-
console.log('Not in legal moves mode, no promotion required');
|
|
4188
|
-
return false;
|
|
4189
|
-
}
|
|
4190
|
-
|
|
3954
|
+
if (!(move instanceof Move$1)) throw new Error('requiresPromotion requires a Move object');
|
|
3955
|
+
if (!this.config.onlyLegalMoves) return false;
|
|
4191
3956
|
const game = this.positionService.getGame();
|
|
4192
|
-
if (!game)
|
|
4193
|
-
console.log('No game instance available');
|
|
4194
|
-
return false;
|
|
4195
|
-
}
|
|
4196
|
-
|
|
3957
|
+
if (!game) return false;
|
|
4197
3958
|
const piece = game.get(move.from.id);
|
|
4198
|
-
if (!piece || piece.type !== 'p')
|
|
4199
|
-
console.log('Not a pawn move, no promotion required');
|
|
4200
|
-
return false;
|
|
4201
|
-
}
|
|
4202
|
-
|
|
3959
|
+
if (!piece || piece.type !== 'p') return false;
|
|
4203
3960
|
const targetRank = move.to.row;
|
|
4204
|
-
if (targetRank !== 1 && targetRank !== 8)
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
}
|
|
4208
|
-
|
|
4209
|
-
console.log('Pawn reaching promotion rank, validating move...');
|
|
4210
|
-
|
|
4211
|
-
// Additional validation: check if the pawn can actually reach this square
|
|
4212
|
-
if (!this._isPawnMoveValid(move.from, move.to, piece.color)) {
|
|
4213
|
-
console.log('Pawn move not valid, no promotion required');
|
|
4214
|
-
return false;
|
|
4215
|
-
}
|
|
4216
|
-
|
|
4217
|
-
// First check if the move is legal without promotion
|
|
4218
|
-
const simpleMoveObj = {
|
|
4219
|
-
from: move.from.id,
|
|
4220
|
-
to: move.to.id
|
|
4221
|
-
};
|
|
4222
|
-
|
|
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 };
|
|
4223
3965
|
try {
|
|
4224
|
-
console.log('Testing move without promotion:', simpleMoveObj);
|
|
4225
|
-
// Test if the move is legal without promotion first
|
|
4226
3966
|
const testMove = game.move(simpleMoveObj);
|
|
4227
3967
|
if (testMove) {
|
|
4228
|
-
// Move was successful, but check if it was a promotion
|
|
4229
3968
|
const wasPromotion = testMove.promotion;
|
|
4230
|
-
|
|
4231
|
-
// Undo the test move
|
|
4232
3969
|
game.undo();
|
|
4233
|
-
|
|
4234
|
-
console.log('Move successful without promotion, was promotion:', wasPromotion !== undefined);
|
|
4235
|
-
|
|
4236
|
-
// If it was a promotion, return true
|
|
4237
3970
|
return wasPromotion !== undefined;
|
|
4238
3971
|
}
|
|
4239
3972
|
} catch (error) {
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
// If simple move fails, try with promotion
|
|
4243
|
-
const promotionMoveObj = {
|
|
4244
|
-
from: move.from.id,
|
|
4245
|
-
to: move.to.id,
|
|
4246
|
-
promotion: 'q' // test with queen
|
|
4247
|
-
};
|
|
4248
|
-
|
|
3973
|
+
// Try with promotion
|
|
3974
|
+
const promotionMoveObj = { from: move.from.id, to: move.to.id, promotion: 'q' };
|
|
4249
3975
|
try {
|
|
4250
|
-
console.log('Testing move with promotion:', promotionMoveObj);
|
|
4251
3976
|
const testMove = game.move(promotionMoveObj);
|
|
4252
3977
|
if (testMove) {
|
|
4253
|
-
// Undo the test move
|
|
4254
3978
|
game.undo();
|
|
4255
|
-
console.log('Move successful with promotion, promotion required');
|
|
4256
3979
|
return true;
|
|
4257
3980
|
}
|
|
4258
3981
|
} catch (promotionError) {
|
|
4259
|
-
console.log('Move failed even with promotion:', promotionError.message);
|
|
4260
|
-
// Move is not legal even with promotion
|
|
4261
3982
|
return false;
|
|
4262
3983
|
}
|
|
4263
3984
|
}
|
|
4264
|
-
|
|
4265
|
-
console.log('Move validation complete, no promotion required');
|
|
4266
3985
|
return false;
|
|
4267
3986
|
}
|
|
4268
3987
|
|
|
4269
3988
|
/**
|
|
4270
|
-
*
|
|
3989
|
+
* Validate if a pawn move is theoretically possible
|
|
4271
3990
|
* @private
|
|
4272
3991
|
* @param {Square} from - Source square
|
|
4273
3992
|
* @param {Square} to - Target square
|
|
@@ -4279,48 +3998,21 @@ var Chessboard = (function (exports) {
|
|
|
4279
3998
|
const toRank = to.row;
|
|
4280
3999
|
const fromFile = from.col;
|
|
4281
4000
|
const toFile = to.col;
|
|
4282
|
-
|
|
4283
|
-
console.log(`Validating pawn move: ${from.id} -> ${to.id} (${color})`);
|
|
4284
|
-
console.log(`Ranks: ${fromRank} -> ${toRank}, Files: ${fromFile} -> ${toFile}`);
|
|
4285
|
-
|
|
4286
|
-
// Direction of pawn movement
|
|
4287
4001
|
const direction = color === 'w' ? 1 : -1;
|
|
4288
4002
|
const rankDiff = toRank - fromRank;
|
|
4289
4003
|
const fileDiff = Math.abs(toFile - fromFile);
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
if (rankDiff * direction <= 0) {
|
|
4293
|
-
console.log('Invalid: Pawn cannot move backward or stay in place');
|
|
4294
|
-
return false;
|
|
4295
|
-
}
|
|
4296
|
-
|
|
4297
|
-
// Pawn can only move 1 rank at a time (except for double move from starting position)
|
|
4298
|
-
if (Math.abs(rankDiff) > 2) {
|
|
4299
|
-
console.log('Invalid: Pawn cannot move more than 2 ranks');
|
|
4300
|
-
return false;
|
|
4301
|
-
}
|
|
4302
|
-
|
|
4303
|
-
// If moving 2 ranks, must be from starting position
|
|
4004
|
+
if (rankDiff * direction <= 0) return false;
|
|
4005
|
+
if (Math.abs(rankDiff) > 2) return false;
|
|
4304
4006
|
if (Math.abs(rankDiff) === 2) {
|
|
4305
4007
|
const startingRank = color === 'w' ? 2 : 7;
|
|
4306
|
-
if (fromRank !== startingRank)
|
|
4307
|
-
console.log(`Invalid: Pawn cannot move 2 ranks from rank ${fromRank}`);
|
|
4308
|
-
return false;
|
|
4309
|
-
}
|
|
4310
|
-
}
|
|
4311
|
-
|
|
4312
|
-
// Pawn can only move to adjacent files (diagonal capture) or same file (forward move)
|
|
4313
|
-
if (fileDiff > 1) {
|
|
4314
|
-
console.log('Invalid: Pawn cannot move more than 1 file');
|
|
4315
|
-
return false;
|
|
4008
|
+
if (fromRank !== startingRank) return false;
|
|
4316
4009
|
}
|
|
4317
|
-
|
|
4318
|
-
console.log('Pawn move validation passed');
|
|
4010
|
+
if (fileDiff > 1) return false;
|
|
4319
4011
|
return true;
|
|
4320
4012
|
}
|
|
4321
4013
|
|
|
4322
4014
|
/**
|
|
4323
|
-
*
|
|
4015
|
+
* Handle promotion UI setup
|
|
4324
4016
|
* @param {Move} move - Move requiring promotion
|
|
4325
4017
|
* @param {Object} squares - All board squares
|
|
4326
4018
|
* @param {Function} onPromotionSelect - Callback when promotion piece is selected
|
|
@@ -4329,55 +4021,33 @@ var Chessboard = (function (exports) {
|
|
|
4329
4021
|
*/
|
|
4330
4022
|
setupPromotion(move, squares, onPromotionSelect, onPromotionCancel) {
|
|
4331
4023
|
if (!this.requiresPromotion(move)) return false;
|
|
4332
|
-
|
|
4333
|
-
// Check if position service and game are available
|
|
4334
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
4335
|
-
return false;
|
|
4336
|
-
}
|
|
4337
|
-
|
|
4024
|
+
if (!this.positionService || !this.positionService.getGame()) return false;
|
|
4338
4025
|
const game = this.positionService.getGame();
|
|
4339
4026
|
const piece = game.get(move.from.id);
|
|
4340
4027
|
const targetSquare = move.to;
|
|
4341
|
-
|
|
4342
|
-
// Clear any existing promotion UI
|
|
4343
4028
|
Object.values(squares).forEach(square => {
|
|
4344
4029
|
square.removePromotion();
|
|
4345
4030
|
square.removeCover();
|
|
4346
4031
|
});
|
|
4347
|
-
|
|
4348
|
-
// Always show promotion choices in a column
|
|
4349
4032
|
this._showPromotionInColumn(targetSquare, piece, squares, onPromotionSelect, onPromotionCancel);
|
|
4350
|
-
|
|
4351
4033
|
return true;
|
|
4352
4034
|
}
|
|
4353
4035
|
|
|
4354
4036
|
/**
|
|
4355
|
-
*
|
|
4037
|
+
* Show promotion choices in a column
|
|
4356
4038
|
* @private
|
|
4357
4039
|
*/
|
|
4358
4040
|
_showPromotionInColumn(targetSquare, piece, squares, onPromotionSelect, onPromotionCancel) {
|
|
4359
|
-
console.log('Setting up promotion for', targetSquare.id, 'piece color:', piece.color);
|
|
4360
|
-
|
|
4361
|
-
// Set up promotion choices starting from border row
|
|
4362
4041
|
PROMOTION_PIECES.forEach((pieceType, index) => {
|
|
4363
4042
|
const choiceSquare = this._findPromotionSquare(targetSquare, index, squares);
|
|
4364
|
-
|
|
4365
4043
|
if (choiceSquare) {
|
|
4366
4044
|
const pieceId = pieceType + piece.color;
|
|
4367
4045
|
const piecePath = this._getPiecePathForPromotion(pieceId);
|
|
4368
|
-
|
|
4369
|
-
console.log('Setting up promotion choice:', pieceType, 'on square:', choiceSquare.id);
|
|
4370
|
-
|
|
4371
4046
|
choiceSquare.putPromotion(piecePath, () => {
|
|
4372
|
-
console.log('Promotion choice selected:', pieceType);
|
|
4373
4047
|
onPromotionSelect(pieceType);
|
|
4374
4048
|
});
|
|
4375
|
-
} else {
|
|
4376
|
-
console.log('Could not find square for promotion choice:', pieceType, 'index:', index);
|
|
4377
4049
|
}
|
|
4378
4050
|
});
|
|
4379
|
-
|
|
4380
|
-
// Set up cover squares (for cancellation)
|
|
4381
4051
|
Object.values(squares).forEach(square => {
|
|
4382
4052
|
if (!square.hasPromotion()) {
|
|
4383
4053
|
square.putCover(() => {
|
|
@@ -4385,181 +4055,119 @@ var Chessboard = (function (exports) {
|
|
|
4385
4055
|
});
|
|
4386
4056
|
}
|
|
4387
4057
|
});
|
|
4388
|
-
|
|
4389
4058
|
return true;
|
|
4390
4059
|
}
|
|
4391
4060
|
|
|
4392
4061
|
/**
|
|
4393
|
-
*
|
|
4062
|
+
* Find the appropriate square for a promotion piece
|
|
4394
4063
|
* @private
|
|
4395
4064
|
* @param {Square} targetSquare - Target square of the promotion move
|
|
4396
|
-
* @param {number}
|
|
4065
|
+
* @param {number} index - Distance from target square
|
|
4397
4066
|
* @param {Object} squares - All board squares
|
|
4398
4067
|
* @returns {Square|null} Square for promotion piece or null
|
|
4399
4068
|
*/
|
|
4400
4069
|
_findPromotionSquare(targetSquare, index, squares) {
|
|
4401
4070
|
const col = targetSquare.col;
|
|
4402
4071
|
const baseRow = targetSquare.row;
|
|
4403
|
-
|
|
4404
|
-
console.log('Looking for promotion square - target:', targetSquare.id, 'index:', index, 'col:', col, 'baseRow:', baseRow);
|
|
4405
|
-
|
|
4406
|
-
// Calculate row based on index and promotion direction
|
|
4407
|
-
// Start from the border row (1 or 8) and go inward
|
|
4408
4072
|
let row;
|
|
4409
4073
|
if (baseRow === 8) {
|
|
4410
|
-
// White promotion: start from row 8 and go down
|
|
4411
4074
|
row = 8 - index;
|
|
4412
4075
|
} else if (baseRow === 1) {
|
|
4413
|
-
// Black promotion: start from row 1 and go up
|
|
4414
4076
|
row = 1 + index;
|
|
4415
4077
|
} else {
|
|
4416
|
-
console.log('Invalid promotion row:', baseRow);
|
|
4417
4078
|
return null;
|
|
4418
4079
|
}
|
|
4419
|
-
|
|
4420
|
-
console.log('Calculated row:', row);
|
|
4421
|
-
|
|
4422
|
-
// Ensure row is within bounds
|
|
4423
|
-
if (row < 1 || row > 8) {
|
|
4424
|
-
console.log('Row out of bounds:', row);
|
|
4425
|
-
return null;
|
|
4426
|
-
}
|
|
4427
|
-
|
|
4428
|
-
// Find square by row/col
|
|
4080
|
+
if (row < 1 || row > 8) return null;
|
|
4429
4081
|
for (const square of Object.values(squares)) {
|
|
4430
4082
|
if (square.col === col && square.row === row) {
|
|
4431
|
-
console.log('Found promotion square:', square.id);
|
|
4432
4083
|
return square;
|
|
4433
4084
|
}
|
|
4434
4085
|
}
|
|
4435
|
-
|
|
4436
|
-
console.log('No square found for col:', col, 'row:', row);
|
|
4437
4086
|
return null;
|
|
4438
4087
|
}
|
|
4439
4088
|
|
|
4440
4089
|
/**
|
|
4441
|
-
*
|
|
4090
|
+
* Get piece path for promotion UI
|
|
4442
4091
|
* @private
|
|
4443
4092
|
* @param {string} pieceId - Piece identifier
|
|
4444
4093
|
* @returns {string} Path to piece asset
|
|
4445
4094
|
*/
|
|
4446
4095
|
_getPiecePathForPromotion(pieceId) {
|
|
4447
|
-
// This would typically use the PieceService
|
|
4448
|
-
// For now, we'll use a simple implementation
|
|
4449
4096
|
const { piecesPath } = this.config;
|
|
4450
|
-
|
|
4451
4097
|
if (typeof piecesPath === 'string') {
|
|
4452
4098
|
return `${piecesPath}/${pieceId}.svg`;
|
|
4453
4099
|
}
|
|
4454
|
-
|
|
4455
|
-
// Fallback for other path types
|
|
4456
4100
|
return `assets/pieces/${pieceId}.svg`;
|
|
4457
4101
|
}
|
|
4458
4102
|
|
|
4459
4103
|
/**
|
|
4460
|
-
*
|
|
4104
|
+
* Parse a move string into a move object
|
|
4461
4105
|
* @param {string} moveString - Move string (e.g., 'e2e4', 'e7e8q')
|
|
4462
4106
|
* @returns {Object|null} Move object or null if invalid
|
|
4463
4107
|
*/
|
|
4464
4108
|
parseMove(moveString) {
|
|
4465
|
-
if (typeof moveString !== 'string' || moveString.length < 4 || moveString.length > 5)
|
|
4466
|
-
return null;
|
|
4467
|
-
}
|
|
4468
|
-
|
|
4109
|
+
if (typeof moveString !== 'string' || moveString.length < 4 || moveString.length > 5) return null;
|
|
4469
4110
|
const from = moveString.slice(0, 2);
|
|
4470
4111
|
const to = moveString.slice(2, 4);
|
|
4471
4112
|
const promotion = moveString.slice(4, 5);
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
return null;
|
|
4476
|
-
}
|
|
4477
|
-
|
|
4478
|
-
if (promotion && !['q', 'r', 'b', 'n'].includes(promotion.toLowerCase())) {
|
|
4479
|
-
return null;
|
|
4480
|
-
}
|
|
4481
|
-
|
|
4482
|
-
return {
|
|
4483
|
-
from: from,
|
|
4484
|
-
to: to,
|
|
4485
|
-
promotion: promotion || null
|
|
4486
|
-
};
|
|
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 };
|
|
4487
4116
|
}
|
|
4488
4117
|
|
|
4489
4118
|
/**
|
|
4490
|
-
*
|
|
4119
|
+
* Check if a move is a castle move
|
|
4491
4120
|
* @param {Object} gameMove - Game move object from chess.js
|
|
4492
4121
|
* @returns {boolean} True if move is castle
|
|
4493
4122
|
*/
|
|
4494
4123
|
isCastle(gameMove) {
|
|
4495
|
-
return gameMove && (gameMove.isKingsideCastle() || gameMove.isQueensideCastle());
|
|
4124
|
+
return gameMove && (gameMove.isKingsideCastle && gameMove.isKingsideCastle() || gameMove.isQueensideCastle && gameMove.isQueensideCastle());
|
|
4496
4125
|
}
|
|
4497
4126
|
|
|
4498
4127
|
/**
|
|
4499
|
-
*
|
|
4128
|
+
* Get the rook move for a castle move
|
|
4500
4129
|
* @param {Object} gameMove - Game move object from chess.js
|
|
4501
4130
|
* @returns {Object|null} Rook move object or null if not castle
|
|
4502
4131
|
*/
|
|
4503
4132
|
getCastleRookMove(gameMove) {
|
|
4504
|
-
if (!this.isCastle(gameMove))
|
|
4505
|
-
return null;
|
|
4506
|
-
}
|
|
4507
|
-
|
|
4133
|
+
if (!this.isCastle(gameMove)) return null;
|
|
4508
4134
|
const isKingSide = gameMove.isKingsideCastle();
|
|
4509
4135
|
const isWhite = gameMove.color === 'w';
|
|
4510
|
-
|
|
4511
4136
|
if (isKingSide) {
|
|
4512
|
-
|
|
4513
|
-
if (isWhite) {
|
|
4514
|
-
return { from: 'h1', to: 'f1' };
|
|
4515
|
-
} else {
|
|
4516
|
-
return { from: 'h8', to: 'f8' };
|
|
4517
|
-
}
|
|
4137
|
+
return isWhite ? { from: 'h1', to: 'f1' } : { from: 'h8', to: 'f8' };
|
|
4518
4138
|
} else {
|
|
4519
|
-
|
|
4520
|
-
if (isWhite) {
|
|
4521
|
-
return { from: 'a1', to: 'd1' };
|
|
4522
|
-
} else {
|
|
4523
|
-
return { from: 'a8', to: 'd8' };
|
|
4524
|
-
}
|
|
4139
|
+
return isWhite ? { from: 'a1', to: 'd1' } : { from: 'a8', to: 'd8' };
|
|
4525
4140
|
}
|
|
4526
4141
|
}
|
|
4527
4142
|
|
|
4528
4143
|
/**
|
|
4529
|
-
*
|
|
4144
|
+
* Check if a move is en passant
|
|
4530
4145
|
* @param {Object} gameMove - Game move object from chess.js
|
|
4531
4146
|
* @returns {boolean} True if move is en passant
|
|
4532
4147
|
*/
|
|
4533
4148
|
isEnPassant(gameMove) {
|
|
4534
|
-
return gameMove && gameMove.isEnPassant();
|
|
4149
|
+
return gameMove && gameMove.isEnPassant && gameMove.isEnPassant();
|
|
4535
4150
|
}
|
|
4536
4151
|
|
|
4537
4152
|
/**
|
|
4538
|
-
*
|
|
4153
|
+
* Get the captured pawn square for en passant
|
|
4539
4154
|
* @param {Object} gameMove - Game move object from chess.js
|
|
4540
4155
|
* @returns {string|null} Square of captured pawn or null if not en passant
|
|
4541
4156
|
*/
|
|
4542
4157
|
getEnPassantCapturedSquare(gameMove) {
|
|
4543
|
-
if (!this.isEnPassant(gameMove))
|
|
4544
|
-
return null;
|
|
4545
|
-
}
|
|
4546
|
-
|
|
4158
|
+
if (!this.isEnPassant(gameMove)) return null;
|
|
4547
4159
|
const toSquare = gameMove.to;
|
|
4548
4160
|
const rank = parseInt(toSquare[1]);
|
|
4549
4161
|
const file = toSquare[0];
|
|
4550
|
-
|
|
4551
|
-
// The captured pawn is on the same file but different rank
|
|
4552
4162
|
if (gameMove.color === 'w') {
|
|
4553
|
-
// White captures black pawn one rank below
|
|
4554
4163
|
return file + (rank - 1);
|
|
4555
4164
|
} else {
|
|
4556
|
-
// Black captures white pawn one rank above
|
|
4557
4165
|
return file + (rank + 1);
|
|
4558
4166
|
}
|
|
4559
4167
|
}
|
|
4560
4168
|
|
|
4561
4169
|
/**
|
|
4562
|
-
*
|
|
4170
|
+
* Clear the moves cache
|
|
4563
4171
|
*/
|
|
4564
4172
|
clearCache() {
|
|
4565
4173
|
this._movesCache.clear();
|
|
@@ -4570,7 +4178,7 @@ var Chessboard = (function (exports) {
|
|
|
4570
4178
|
}
|
|
4571
4179
|
|
|
4572
4180
|
/**
|
|
4573
|
-
*
|
|
4181
|
+
* Clean up resources
|
|
4574
4182
|
*/
|
|
4575
4183
|
destroy() {
|
|
4576
4184
|
this.clearCache();
|
|
@@ -4579,7 +4187,7 @@ var Chessboard = (function (exports) {
|
|
|
4579
4187
|
}
|
|
4580
4188
|
|
|
4581
4189
|
/**
|
|
4582
|
-
*
|
|
4190
|
+
* PieceService - Handles chess piece management and operations
|
|
4583
4191
|
* @module services/PieceService
|
|
4584
4192
|
* @since 2.0.0
|
|
4585
4193
|
*/
|
|
@@ -4587,11 +4195,11 @@ var Chessboard = (function (exports) {
|
|
|
4587
4195
|
|
|
4588
4196
|
/**
|
|
4589
4197
|
* Service responsible for piece management and operations
|
|
4590
|
-
* @class
|
|
4198
|
+
* @class PieceService
|
|
4591
4199
|
*/
|
|
4592
4200
|
class PieceService {
|
|
4593
4201
|
/**
|
|
4594
|
-
*
|
|
4202
|
+
* Create a new PieceService instance
|
|
4595
4203
|
* @param {ChessboardConfig} config - Board configuration
|
|
4596
4204
|
*/
|
|
4597
4205
|
constructor(config) {
|
|
@@ -4599,14 +4207,13 @@ var Chessboard = (function (exports) {
|
|
|
4599
4207
|
}
|
|
4600
4208
|
|
|
4601
4209
|
/**
|
|
4602
|
-
*
|
|
4210
|
+
* Get the path to a piece asset
|
|
4603
4211
|
* @param {string} piece - Piece identifier (e.g., 'wK', 'bP')
|
|
4604
4212
|
* @returns {string} Path to piece asset
|
|
4605
4213
|
* @throws {ValidationError} When piecesPath configuration is invalid
|
|
4606
4214
|
*/
|
|
4607
4215
|
getPiecePath(piece) {
|
|
4608
4216
|
const { piecesPath } = this.config;
|
|
4609
|
-
|
|
4610
4217
|
if (typeof piecesPath === 'string') {
|
|
4611
4218
|
return `${piecesPath}/${piece}.svg`;
|
|
4612
4219
|
} else if (typeof piecesPath === 'object' && piecesPath !== null) {
|
|
@@ -4619,7 +4226,7 @@ var Chessboard = (function (exports) {
|
|
|
4619
4226
|
}
|
|
4620
4227
|
|
|
4621
4228
|
/**
|
|
4622
|
-
*
|
|
4229
|
+
* Convert various piece formats to a Piece instance
|
|
4623
4230
|
* @param {string|Piece} piece - Piece in various formats
|
|
4624
4231
|
* @returns {Piece} Piece instance
|
|
4625
4232
|
* @throws {PieceError} When piece format is invalid
|
|
@@ -4628,54 +4235,41 @@ var Chessboard = (function (exports) {
|
|
|
4628
4235
|
if (piece instanceof Piece) {
|
|
4629
4236
|
return piece;
|
|
4630
4237
|
}
|
|
4631
|
-
|
|
4632
4238
|
if (typeof piece === 'string' && piece.length === 2) {
|
|
4633
4239
|
const [first, second] = piece.split('');
|
|
4634
4240
|
let type, color;
|
|
4635
|
-
|
|
4636
|
-
// Check format: [type][color] (e.g., 'pw')
|
|
4637
4241
|
if (PIECE_TYPES.includes(first.toLowerCase()) && PIECE_COLORS.includes(second)) {
|
|
4638
4242
|
type = first.toLowerCase();
|
|
4639
4243
|
color = second;
|
|
4640
|
-
}
|
|
4641
|
-
// Check format: [color][type] (e.g., 'wP')
|
|
4642
|
-
else if (PIECE_COLORS.includes(first) && PIECE_TYPES.includes(second.toLowerCase())) {
|
|
4244
|
+
} else if (PIECE_COLORS.includes(first) && PIECE_TYPES.includes(second.toLowerCase())) {
|
|
4643
4245
|
color = first;
|
|
4644
4246
|
type = second.toLowerCase();
|
|
4645
4247
|
} else {
|
|
4646
4248
|
throw new PieceError(ERROR_MESSAGES.invalid_piece + piece, piece);
|
|
4647
4249
|
}
|
|
4648
|
-
|
|
4649
4250
|
const piecePath = this.getPiecePath(type + color);
|
|
4650
4251
|
return new Piece(color, type, piecePath);
|
|
4651
4252
|
}
|
|
4652
|
-
|
|
4653
4253
|
throw new PieceError(ERROR_MESSAGES.invalid_piece + piece, piece);
|
|
4654
4254
|
}
|
|
4655
4255
|
|
|
4656
4256
|
/**
|
|
4657
|
-
*
|
|
4658
|
-
* @param {Square} square - Target square
|
|
4659
|
-
* @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
|
|
4660
4260
|
* @param {boolean} [fade=true] - Whether to fade in the piece
|
|
4661
4261
|
* @param {Function} dragFunction - Function to handle drag events
|
|
4662
4262
|
* @param {Function} [callback] - Callback when animation completes
|
|
4663
4263
|
*/
|
|
4664
4264
|
addPieceOnSquare(square, piece, fade = true, dragFunction, callback) {
|
|
4665
|
-
if (!square || !piece) throw new Error('addPieceOnSquare
|
|
4666
|
-
console.debug(`[PieceService] addPieceOnSquare: ${piece.id} to ${square.id}`);
|
|
4265
|
+
if (!square || !piece) throw new Error('addPieceOnSquare requires Square and Piece objects');
|
|
4667
4266
|
square.putPiece(piece);
|
|
4668
|
-
|
|
4669
|
-
// Imposta sempre il drag (touch e mouse)
|
|
4670
4267
|
if (dragFunction) {
|
|
4671
4268
|
piece.setDrag(dragFunction(square, piece));
|
|
4672
4269
|
}
|
|
4673
|
-
// Forza il drag touch se manca (debug/robustezza)
|
|
4674
4270
|
if (!piece.element.ontouchstart) {
|
|
4675
4271
|
piece.element.ontouchstart = dragFunction ? dragFunction(square, piece) : () => { };
|
|
4676
|
-
console.debug(`[PieceService] Forzato ontouchstart su ${piece.id}`);
|
|
4677
4272
|
}
|
|
4678
|
-
|
|
4679
4273
|
if (fade && this.config.fadeTime > 0) {
|
|
4680
4274
|
piece.fadeIn(
|
|
4681
4275
|
this.config.fadeTime,
|
|
@@ -4686,28 +4280,24 @@ var Chessboard = (function (exports) {
|
|
|
4686
4280
|
} else {
|
|
4687
4281
|
if (callback) callback();
|
|
4688
4282
|
}
|
|
4689
|
-
|
|
4690
4283
|
piece.visible();
|
|
4691
4284
|
}
|
|
4692
4285
|
|
|
4693
4286
|
/**
|
|
4694
|
-
*
|
|
4695
|
-
* @param {Square} square -
|
|
4287
|
+
* Remove a piece from a square
|
|
4288
|
+
* @param {Square} square - Square object
|
|
4696
4289
|
* @param {boolean} [fade=true]
|
|
4697
4290
|
* @param {Function} [callback]
|
|
4698
|
-
* @returns {Piece}
|
|
4291
|
+
* @returns {Piece} The removed piece
|
|
4699
4292
|
*/
|
|
4700
4293
|
removePieceFromSquare(square, fade = true, callback) {
|
|
4701
|
-
if (!square) throw new Error('removePieceFromSquare
|
|
4702
|
-
console.debug(`[PieceService] removePieceFromSquare: ${square.id}`);
|
|
4294
|
+
if (!square) throw new Error('removePieceFromSquare requires a Square object');
|
|
4703
4295
|
square.check();
|
|
4704
|
-
|
|
4705
4296
|
const piece = square.piece;
|
|
4706
4297
|
if (!piece) {
|
|
4707
4298
|
if (callback) callback();
|
|
4708
4299
|
throw new PieceError(ERROR_MESSAGES.square_no_piece, null, square.getId());
|
|
4709
4300
|
}
|
|
4710
|
-
|
|
4711
4301
|
if (fade && this.config.fadeTime > 0) {
|
|
4712
4302
|
piece.fadeOut(
|
|
4713
4303
|
this.config.fadeTime,
|
|
@@ -4718,26 +4308,22 @@ var Chessboard = (function (exports) {
|
|
|
4718
4308
|
} else {
|
|
4719
4309
|
if (callback) callback();
|
|
4720
4310
|
}
|
|
4721
|
-
|
|
4722
4311
|
square.removePiece();
|
|
4723
4312
|
return piece;
|
|
4724
4313
|
}
|
|
4725
4314
|
|
|
4726
4315
|
/**
|
|
4727
|
-
*
|
|
4316
|
+
* Move a piece to a new position with animation
|
|
4728
4317
|
* @param {Piece} piece - Piece to move
|
|
4729
4318
|
* @param {Square} targetSquare - Target square
|
|
4730
4319
|
* @param {number} duration - Animation duration
|
|
4731
4320
|
* @param {Function} [callback] - Callback function when animation completes
|
|
4732
4321
|
*/
|
|
4733
4322
|
movePiece(piece, targetSquare, duration, callback) {
|
|
4734
|
-
console.debug(`[PieceService] movePiece: ${piece.id} to ${targetSquare.id}`);
|
|
4735
4323
|
if (!piece || !piece.element) {
|
|
4736
|
-
console.warn(`[PieceService] movePiece: piece or element is null, skipping animation`);
|
|
4737
4324
|
if (callback) callback();
|
|
4738
4325
|
return;
|
|
4739
4326
|
}
|
|
4740
|
-
|
|
4741
4327
|
piece.translate(
|
|
4742
4328
|
targetSquare,
|
|
4743
4329
|
duration,
|
|
@@ -4748,7 +4334,7 @@ var Chessboard = (function (exports) {
|
|
|
4748
4334
|
}
|
|
4749
4335
|
|
|
4750
4336
|
/**
|
|
4751
|
-
*
|
|
4337
|
+
* Handle piece translation with optional capture
|
|
4752
4338
|
* @param {Move} move - Move object containing from/to squares and piece
|
|
4753
4339
|
* @param {boolean} removeTarget - Whether to remove piece from target square
|
|
4754
4340
|
* @param {boolean} animate - Whether to animate the move
|
|
@@ -4756,54 +4342,37 @@ var Chessboard = (function (exports) {
|
|
|
4756
4342
|
* @param {Function} [callback] - Callback function when complete
|
|
4757
4343
|
*/
|
|
4758
4344
|
translatePiece(move, removeTarget, animate, dragFunction = null, callback = null) {
|
|
4759
|
-
console.debug(`[PieceService] translatePiece: ${move.piece.id} from ${move.from.id} to ${move.to.id}`);
|
|
4760
4345
|
if (!move.piece) {
|
|
4761
|
-
console.warn('PieceService.translatePiece: move.piece is null, skipping translation');
|
|
4762
4346
|
if (callback) callback();
|
|
4763
4347
|
return;
|
|
4764
4348
|
}
|
|
4765
|
-
|
|
4766
4349
|
if (removeTarget) {
|
|
4767
|
-
// Deselect the captured piece before removing it
|
|
4768
4350
|
move.to.deselect();
|
|
4769
4351
|
this.removePieceFromSquare(move.to, false);
|
|
4770
4352
|
}
|
|
4771
|
-
|
|
4772
4353
|
const changeSquareCallback = () => {
|
|
4773
|
-
// Check if piece still exists and is on the source square
|
|
4774
4354
|
if (move.from.piece === move.piece) {
|
|
4775
|
-
move.from.removePiece(true);
|
|
4355
|
+
move.from.removePiece(true);
|
|
4776
4356
|
}
|
|
4777
|
-
|
|
4778
|
-
// Only put piece if destination square doesn't already have it
|
|
4779
4357
|
if (move.to.piece !== move.piece) {
|
|
4780
4358
|
move.to.putPiece(move.piece);
|
|
4781
|
-
|
|
4782
|
-
// Re-attach drag handler if provided
|
|
4783
4359
|
if (dragFunction && this.config.draggable && move.piece.element) {
|
|
4784
4360
|
move.piece.setDrag(dragFunction(move.to, move.piece));
|
|
4785
4361
|
}
|
|
4786
4362
|
}
|
|
4787
|
-
|
|
4788
4363
|
if (callback) callback();
|
|
4789
4364
|
};
|
|
4790
|
-
|
|
4791
|
-
// Check if piece is currently being dragged
|
|
4792
4365
|
const isDragging = move.piece.element && move.piece.element.classList.contains('dragging');
|
|
4793
|
-
|
|
4794
4366
|
if (isDragging) {
|
|
4795
|
-
// If piece is being dragged, don't animate - just move it immediately
|
|
4796
|
-
// The piece is already visually in the correct position from the drag
|
|
4797
4367
|
changeSquareCallback();
|
|
4798
4368
|
} else {
|
|
4799
|
-
// Normal animation
|
|
4800
4369
|
const duration = animate ? this.config.moveTime : 0;
|
|
4801
4370
|
this.movePiece(move.piece, move.to, duration, changeSquareCallback);
|
|
4802
4371
|
}
|
|
4803
4372
|
}
|
|
4804
4373
|
|
|
4805
4374
|
/**
|
|
4806
|
-
*
|
|
4375
|
+
* Snap a piece back to its original position
|
|
4807
4376
|
* @param {Square} square - Square containing the piece
|
|
4808
4377
|
* @param {boolean} [animate=true] - Whether to animate the snapback
|
|
4809
4378
|
*/
|
|
@@ -4813,7 +4382,6 @@ var Chessboard = (function (exports) {
|
|
|
4813
4382
|
}
|
|
4814
4383
|
const piece = square.piece;
|
|
4815
4384
|
const duration = animate ? this.config.snapbackTime : 0;
|
|
4816
|
-
console.debug(`[PieceService] snapbackPiece: ${piece.id} on ${square.id}`);
|
|
4817
4385
|
piece.translate(
|
|
4818
4386
|
square,
|
|
4819
4387
|
duration,
|
|
@@ -4823,7 +4391,7 @@ var Chessboard = (function (exports) {
|
|
|
4823
4391
|
}
|
|
4824
4392
|
|
|
4825
4393
|
/**
|
|
4826
|
-
*
|
|
4394
|
+
* Center a piece in its square with animation (after successful drop)
|
|
4827
4395
|
* @param {Square} square - Square containing the piece to center
|
|
4828
4396
|
* @param {boolean} animate - Whether to animate the centering
|
|
4829
4397
|
*/
|
|
@@ -4833,14 +4401,12 @@ var Chessboard = (function (exports) {
|
|
|
4833
4401
|
}
|
|
4834
4402
|
const piece = square.piece;
|
|
4835
4403
|
const duration = animate ? this.config.dropCenterTime : 0;
|
|
4836
|
-
console.debug(`[PieceService] centerPiece: ${piece.id} on ${square.id}`);
|
|
4837
4404
|
piece.translate(
|
|
4838
4405
|
square,
|
|
4839
4406
|
duration,
|
|
4840
4407
|
this._getTransitionTimingFunction(),
|
|
4841
4408
|
this.config.dropCenterAnimation,
|
|
4842
4409
|
() => {
|
|
4843
|
-
// After animation, reset all drag-related styles
|
|
4844
4410
|
if (!piece.element) return;
|
|
4845
4411
|
piece.element.style.position = '';
|
|
4846
4412
|
piece.element.style.left = '';
|
|
@@ -4852,14 +4418,13 @@ var Chessboard = (function (exports) {
|
|
|
4852
4418
|
}
|
|
4853
4419
|
|
|
4854
4420
|
/**
|
|
4855
|
-
*
|
|
4421
|
+
* Get the transition timing function for animations
|
|
4856
4422
|
* @private
|
|
4857
4423
|
* @returns {Function} Timing function
|
|
4858
4424
|
*/
|
|
4859
4425
|
_getTransitionTimingFunction() {
|
|
4860
4426
|
return (elapsed, duration, type = 'ease') => {
|
|
4861
4427
|
const x = elapsed / duration;
|
|
4862
|
-
|
|
4863
4428
|
switch (type) {
|
|
4864
4429
|
case 'linear':
|
|
4865
4430
|
return x;
|
|
@@ -4878,7 +4443,7 @@ var Chessboard = (function (exports) {
|
|
|
4878
4443
|
}
|
|
4879
4444
|
|
|
4880
4445
|
/**
|
|
4881
|
-
*
|
|
4446
|
+
* Clean up resources
|
|
4882
4447
|
*/
|
|
4883
4448
|
destroy() {
|
|
4884
4449
|
// Cleanup any cached pieces or references
|
|
@@ -6869,7 +6434,7 @@ var Chessboard = (function (exports) {
|
|
|
6869
6434
|
}
|
|
6870
6435
|
|
|
6871
6436
|
/**
|
|
6872
|
-
*
|
|
6437
|
+
* PositionService - Handles chess position management and FEN conversion
|
|
6873
6438
|
* @module services/PositionService
|
|
6874
6439
|
* @since 2.0.0
|
|
6875
6440
|
*/
|
|
@@ -6877,11 +6442,11 @@ var Chessboard = (function (exports) {
|
|
|
6877
6442
|
|
|
6878
6443
|
/**
|
|
6879
6444
|
* Service responsible for position management and FEN operations
|
|
6880
|
-
* @class
|
|
6445
|
+
* @class PositionService
|
|
6881
6446
|
*/
|
|
6882
6447
|
class PositionService {
|
|
6883
6448
|
/**
|
|
6884
|
-
*
|
|
6449
|
+
* Create a new PositionService instance
|
|
6885
6450
|
* @param {ChessboardConfig} config - Board configuration
|
|
6886
6451
|
*/
|
|
6887
6452
|
constructor(config) {
|
|
@@ -6890,7 +6455,7 @@ var Chessboard = (function (exports) {
|
|
|
6890
6455
|
}
|
|
6891
6456
|
|
|
6892
6457
|
/**
|
|
6893
|
-
*
|
|
6458
|
+
* Convert various position formats to FEN string
|
|
6894
6459
|
* @param {string|Object} position - Position in various formats
|
|
6895
6460
|
* @returns {string} FEN string representation
|
|
6896
6461
|
* @throws {ValidationError} When position format is invalid
|
|
@@ -6906,7 +6471,7 @@ var Chessboard = (function (exports) {
|
|
|
6906
6471
|
}
|
|
6907
6472
|
|
|
6908
6473
|
/**
|
|
6909
|
-
*
|
|
6474
|
+
* Convert string position to FEN
|
|
6910
6475
|
* @private
|
|
6911
6476
|
* @param {string} position - String position
|
|
6912
6477
|
* @returns {string} FEN string
|
|
@@ -6915,35 +6480,29 @@ var Chessboard = (function (exports) {
|
|
|
6915
6480
|
if (position === 'start') {
|
|
6916
6481
|
return DEFAULT_STARTING_POSITION;
|
|
6917
6482
|
}
|
|
6918
|
-
|
|
6919
6483
|
if (this.validateFen(position)) {
|
|
6920
6484
|
return position;
|
|
6921
6485
|
}
|
|
6922
|
-
|
|
6923
6486
|
if (STANDARD_POSITIONS[position]) {
|
|
6924
6487
|
return STANDARD_POSITIONS[position];
|
|
6925
6488
|
}
|
|
6926
|
-
|
|
6927
6489
|
throw new ValidationError(ERROR_MESSAGES.invalid_position + position, 'position', position);
|
|
6928
6490
|
}
|
|
6929
6491
|
|
|
6930
6492
|
/**
|
|
6931
|
-
*
|
|
6493
|
+
* Convert object position to FEN
|
|
6932
6494
|
* @private
|
|
6933
6495
|
* @param {Object} position - Object with square->piece mapping
|
|
6934
6496
|
* @returns {string} FEN string
|
|
6935
6497
|
*/
|
|
6936
6498
|
_convertObjectPosition(position) {
|
|
6937
6499
|
const parts = [];
|
|
6938
|
-
|
|
6939
6500
|
for (let row = 0; row < 8; row++) {
|
|
6940
6501
|
const rowParts = [];
|
|
6941
6502
|
let empty = 0;
|
|
6942
|
-
|
|
6943
6503
|
for (let col = 0; col < 8; col++) {
|
|
6944
6504
|
const square = this._getSquareID(row, col);
|
|
6945
6505
|
const piece = position[square];
|
|
6946
|
-
|
|
6947
6506
|
if (piece) {
|
|
6948
6507
|
if (empty > 0) {
|
|
6949
6508
|
rowParts.push(empty);
|
|
@@ -6956,25 +6515,21 @@ var Chessboard = (function (exports) {
|
|
|
6956
6515
|
empty++;
|
|
6957
6516
|
}
|
|
6958
6517
|
}
|
|
6959
|
-
|
|
6960
6518
|
if (empty > 0) {
|
|
6961
6519
|
rowParts.push(empty);
|
|
6962
6520
|
}
|
|
6963
|
-
|
|
6964
6521
|
parts.push(rowParts.join(''));
|
|
6965
6522
|
}
|
|
6966
|
-
|
|
6967
6523
|
return parts.join('/') + ' w KQkq - 0 1';
|
|
6968
6524
|
}
|
|
6969
6525
|
|
|
6970
6526
|
/**
|
|
6971
|
-
*
|
|
6527
|
+
* Set up the game with the given position
|
|
6972
6528
|
* @param {string|Object} position - Position to set
|
|
6973
6529
|
* @param {Object} [options] - Additional options for game setup
|
|
6974
6530
|
*/
|
|
6975
6531
|
setGame(position, options = {}) {
|
|
6976
6532
|
const fen = this.convertFen(position);
|
|
6977
|
-
|
|
6978
6533
|
if (this.game) {
|
|
6979
6534
|
this.game.load(fen, options);
|
|
6980
6535
|
} else {
|
|
@@ -6983,7 +6538,7 @@ var Chessboard = (function (exports) {
|
|
|
6983
6538
|
}
|
|
6984
6539
|
|
|
6985
6540
|
/**
|
|
6986
|
-
*
|
|
6541
|
+
* Get the current game instance
|
|
6987
6542
|
* @returns {Chess} Current chess.js game instance
|
|
6988
6543
|
*/
|
|
6989
6544
|
getGame() {
|
|
@@ -6991,7 +6546,7 @@ var Chessboard = (function (exports) {
|
|
|
6991
6546
|
}
|
|
6992
6547
|
|
|
6993
6548
|
/**
|
|
6994
|
-
*
|
|
6549
|
+
* Validate a FEN string
|
|
6995
6550
|
* @param {string} fen - FEN string to validate
|
|
6996
6551
|
* @returns {boolean} True if valid, false otherwise
|
|
6997
6552
|
*/
|
|
@@ -7000,7 +6555,7 @@ var Chessboard = (function (exports) {
|
|
|
7000
6555
|
}
|
|
7001
6556
|
|
|
7002
6557
|
/**
|
|
7003
|
-
*
|
|
6558
|
+
* Get piece information for a specific square
|
|
7004
6559
|
* @param {string} squareId - Square identifier
|
|
7005
6560
|
* @returns {string|null} Piece ID or null if no piece
|
|
7006
6561
|
*/
|
|
@@ -7011,7 +6566,7 @@ var Chessboard = (function (exports) {
|
|
|
7011
6566
|
}
|
|
7012
6567
|
|
|
7013
6568
|
/**
|
|
7014
|
-
*
|
|
6569
|
+
* Check if a specific piece is on a specific square
|
|
7015
6570
|
* @param {string} piece - Piece ID to check
|
|
7016
6571
|
* @param {string} square - Square to check
|
|
7017
6572
|
* @returns {boolean} True if piece is on square
|
|
@@ -7021,7 +6576,7 @@ var Chessboard = (function (exports) {
|
|
|
7021
6576
|
}
|
|
7022
6577
|
|
|
7023
6578
|
/**
|
|
7024
|
-
*
|
|
6579
|
+
* Convert board coordinates to square ID
|
|
7025
6580
|
* @private
|
|
7026
6581
|
* @param {number} row - Row index (0-7)
|
|
7027
6582
|
* @param {number} col - Column index (0-7)
|
|
@@ -7030,7 +6585,6 @@ var Chessboard = (function (exports) {
|
|
|
7030
6585
|
_getSquareID(row, col) {
|
|
7031
6586
|
row = parseInt(row);
|
|
7032
6587
|
col = parseInt(col);
|
|
7033
|
-
|
|
7034
6588
|
if (this.config.orientation === 'w') {
|
|
7035
6589
|
row = 8 - row;
|
|
7036
6590
|
col = col + 1;
|
|
@@ -7038,13 +6592,12 @@ var Chessboard = (function (exports) {
|
|
|
7038
6592
|
row = row + 1;
|
|
7039
6593
|
col = 8 - col;
|
|
7040
6594
|
}
|
|
7041
|
-
|
|
7042
6595
|
const letter = BOARD_LETTERS[col - 1];
|
|
7043
6596
|
return letter + row;
|
|
7044
6597
|
}
|
|
7045
6598
|
|
|
7046
6599
|
/**
|
|
7047
|
-
*
|
|
6600
|
+
* Change the turn in a FEN string
|
|
7048
6601
|
* @param {string} fen - Original FEN string
|
|
7049
6602
|
* @param {string} color - New turn color ('w' or 'b')
|
|
7050
6603
|
* @returns {string} Modified FEN string
|
|
@@ -7056,35 +6609,33 @@ var Chessboard = (function (exports) {
|
|
|
7056
6609
|
}
|
|
7057
6610
|
|
|
7058
6611
|
/**
|
|
7059
|
-
*
|
|
6612
|
+
* Get the current position as an object
|
|
7060
6613
|
* @returns {Object} Position object with piece placements
|
|
7061
6614
|
*/
|
|
7062
6615
|
getPosition() {
|
|
7063
6616
|
const position = {};
|
|
7064
6617
|
const game = this.getGame();
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
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
|
+
];
|
|
7076
6628
|
for (const square of squares) {
|
|
7077
6629
|
const piece = game.get(square);
|
|
7078
6630
|
if (piece) {
|
|
7079
6631
|
position[square] = piece.type + piece.color;
|
|
7080
6632
|
}
|
|
7081
6633
|
}
|
|
7082
|
-
|
|
7083
6634
|
return position;
|
|
7084
6635
|
}
|
|
7085
6636
|
|
|
7086
6637
|
/**
|
|
7087
|
-
*
|
|
6638
|
+
* Toggle the turn in a FEN string
|
|
7088
6639
|
* @param {string} fen - Original FEN string
|
|
7089
6640
|
* @returns {string} Modified FEN string
|
|
7090
6641
|
*/
|
|
@@ -7095,7 +6646,7 @@ var Chessboard = (function (exports) {
|
|
|
7095
6646
|
}
|
|
7096
6647
|
|
|
7097
6648
|
/**
|
|
7098
|
-
*
|
|
6649
|
+
* Clean up resources
|
|
7099
6650
|
*/
|
|
7100
6651
|
destroy() {
|
|
7101
6652
|
this.game = null;
|
|
@@ -7103,7 +6654,8 @@ var Chessboard = (function (exports) {
|
|
|
7103
6654
|
}
|
|
7104
6655
|
|
|
7105
6656
|
/**
|
|
7106
|
-
* Main
|
|
6657
|
+
* Chessboard - Main class orchestrating all services and components
|
|
6658
|
+
* Implements the Facade pattern for the chessboard API
|
|
7107
6659
|
* @module core/Chessboard
|
|
7108
6660
|
* @since 2.0.0
|
|
7109
6661
|
*/
|
|
@@ -7111,12 +6663,11 @@ var Chessboard = (function (exports) {
|
|
|
7111
6663
|
|
|
7112
6664
|
/**
|
|
7113
6665
|
* Main Chessboard class responsible for coordinating all services
|
|
7114
|
-
*
|
|
7115
|
-
* @class
|
|
6666
|
+
* @class Chessboard
|
|
7116
6667
|
*/
|
|
7117
|
-
class Chessboard {
|
|
6668
|
+
let Chessboard$1 = class Chessboard {
|
|
7118
6669
|
/**
|
|
7119
|
-
*
|
|
6670
|
+
* Create a new Chessboard instance
|
|
7120
6671
|
* @param {Object} config - Configuration object
|
|
7121
6672
|
* @throws {ConfigurationError} If configuration is invalid
|
|
7122
6673
|
*/
|
|
@@ -7139,6 +6690,8 @@ var Chessboard = (function (exports) {
|
|
|
7139
6690
|
} catch (error) {
|
|
7140
6691
|
this._handleConstructorError(error);
|
|
7141
6692
|
}
|
|
6693
|
+
this._undoneMoves = [];
|
|
6694
|
+
this._updateBoardPieces(true, true); // Forza popolamento DOM subito
|
|
7142
6695
|
}
|
|
7143
6696
|
|
|
7144
6697
|
/**
|
|
@@ -7182,6 +6735,31 @@ var Chessboard = (function (exports) {
|
|
|
7182
6735
|
}
|
|
7183
6736
|
}
|
|
7184
6737
|
|
|
6738
|
+
/**
|
|
6739
|
+
* Cleans up any partially initialized resources (safe to call multiple times)
|
|
6740
|
+
* @private
|
|
6741
|
+
*/
|
|
6742
|
+
_cleanup() {
|
|
6743
|
+
// Remove event listeners if present
|
|
6744
|
+
if (this.eventService && typeof this.eventService.removeListeners === 'function') {
|
|
6745
|
+
this.eventService.removeListeners();
|
|
6746
|
+
}
|
|
6747
|
+
// Clear timeouts
|
|
6748
|
+
if (this._updateTimeout) {
|
|
6749
|
+
clearTimeout(this._updateTimeout);
|
|
6750
|
+
this._updateTimeout = null;
|
|
6751
|
+
}
|
|
6752
|
+
// Null all services
|
|
6753
|
+
this.validationService = null;
|
|
6754
|
+
this.coordinateService = null;
|
|
6755
|
+
this.positionService = null;
|
|
6756
|
+
this.boardService = null;
|
|
6757
|
+
this.pieceService = null;
|
|
6758
|
+
this.animationService = null;
|
|
6759
|
+
this.moveService = null;
|
|
6760
|
+
this.eventService = null;
|
|
6761
|
+
}
|
|
6762
|
+
|
|
7185
6763
|
/**
|
|
7186
6764
|
* Initializes all services
|
|
7187
6765
|
* @private
|
|
@@ -7250,8 +6828,23 @@ var Chessboard = (function (exports) {
|
|
|
7250
6828
|
/**
|
|
7251
6829
|
* Builds the board DOM structure
|
|
7252
6830
|
* @private
|
|
6831
|
+
* Best practice: always remove squares (destroy JS/DOM) before clearing the board container.
|
|
7253
6832
|
*/
|
|
7254
6833
|
_buildBoard() {
|
|
6834
|
+
console.log('CHIAMATO: _buildBoard');
|
|
6835
|
+
if (this._isUndoRedo) {
|
|
6836
|
+
console.log('SKIP _buildBoard per undo/redo');
|
|
6837
|
+
return;
|
|
6838
|
+
}
|
|
6839
|
+
// Forza la pulizia completa del contenitore board (DOM)
|
|
6840
|
+
const boardContainer = document.getElementById(this.config.id_div);
|
|
6841
|
+
if (boardContainer) boardContainer.innerHTML = '';
|
|
6842
|
+
// Force remove all pieces from all squares (no animation, best practice)
|
|
6843
|
+
if (this.boardService && this.boardService.squares) {
|
|
6844
|
+
Object.values(this.boardService.squares).forEach(sq => sq && sq.forceRemoveAllPieces && sq.forceRemoveAllPieces());
|
|
6845
|
+
}
|
|
6846
|
+
if (this.boardService && this.boardService.removeSquares) this.boardService.removeSquares();
|
|
6847
|
+
if (this.boardService && this.boardService.removeBoard) this.boardService.removeBoard();
|
|
7255
6848
|
this.boardService.buildBoard();
|
|
7256
6849
|
}
|
|
7257
6850
|
|
|
@@ -7260,6 +6853,14 @@ var Chessboard = (function (exports) {
|
|
|
7260
6853
|
* @private
|
|
7261
6854
|
*/
|
|
7262
6855
|
_buildSquares() {
|
|
6856
|
+
console.log('CHIAMATO: _buildSquares');
|
|
6857
|
+
if (this._isUndoRedo) {
|
|
6858
|
+
console.log('SKIP _buildSquares per undo/redo');
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
if (this.boardService && this.boardService.removeSquares) {
|
|
6862
|
+
this.boardService.removeSquares();
|
|
6863
|
+
}
|
|
7263
6864
|
this.boardService.buildSquares((row, col) => {
|
|
7264
6865
|
return this.coordinateService.realCoord(row, col);
|
|
7265
6866
|
});
|
|
@@ -7464,7 +7065,7 @@ var Chessboard = (function (exports) {
|
|
|
7464
7065
|
const capturedPiece = move.to.piece;
|
|
7465
7066
|
|
|
7466
7067
|
// For castle moves in simultaneous mode, we need to coordinate both animations
|
|
7467
|
-
if (isCastleMove
|
|
7068
|
+
if (isCastleMove) {
|
|
7468
7069
|
// Start king animation
|
|
7469
7070
|
this.pieceService.translatePiece(
|
|
7470
7071
|
move,
|
|
@@ -7629,6 +7230,7 @@ var Chessboard = (function (exports) {
|
|
|
7629
7230
|
* @param {boolean} [isPositionLoad=false] - Whether this is a position load
|
|
7630
7231
|
*/
|
|
7631
7232
|
_updateBoardPieces(animation = false, isPositionLoad = false) {
|
|
7233
|
+
console.log('CHIAMATO: _updateBoardPieces', { animation, isPositionLoad, isUndoRedo: this._isUndoRedo });
|
|
7632
7234
|
// Check if services are available
|
|
7633
7235
|
if (!this.positionService || !this.moveService || !this.eventService) {
|
|
7634
7236
|
console.log('Cannot update board pieces - services not available');
|
|
@@ -7688,112 +7290,125 @@ var Chessboard = (function (exports) {
|
|
|
7688
7290
|
}
|
|
7689
7291
|
|
|
7690
7292
|
/**
|
|
7691
|
-
*
|
|
7293
|
+
* Aggiorna i pezzi sulla scacchiera con animazione e delay configurabile (greedy matching)
|
|
7692
7294
|
* @private
|
|
7693
|
-
* @param {boolean} [animation=false] -
|
|
7694
|
-
* @param {boolean} [isPositionLoad=false] -
|
|
7295
|
+
* @param {boolean} [animation=false] - Se animare
|
|
7296
|
+
* @param {boolean} [isPositionLoad=false] - Se è un caricamento posizione (delay 0)
|
|
7695
7297
|
*/
|
|
7696
7298
|
_doUpdateBoardPieces(animation = false, isPositionLoad = false) {
|
|
7697
|
-
|
|
7698
|
-
if (this._isPromoting)
|
|
7699
|
-
|
|
7700
|
-
return;
|
|
7701
|
-
}
|
|
7702
|
-
|
|
7703
|
-
// Check if services are available
|
|
7704
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
7705
|
-
console.log('Cannot update board pieces - position service not available');
|
|
7706
|
-
return;
|
|
7707
|
-
}
|
|
7708
|
-
|
|
7299
|
+
if (this._isDragging) return;
|
|
7300
|
+
if (this._isPromoting) return;
|
|
7301
|
+
if (!this.positionService || !this.positionService.getGame()) return;
|
|
7709
7302
|
const squares = this.boardService.getAllSquares();
|
|
7710
7303
|
const gameStateBefore = this.positionService.getGame().fen();
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
} else {
|
|
7723
|
-
console.log('Using sequential animation');
|
|
7724
|
-
this._doSequentialUpdate(squares, gameStateBefore, animation);
|
|
7304
|
+
if (/^8\/8\/8\/8\/8\/8\/8\/8/.test(gameStateBefore)) {
|
|
7305
|
+
const boardContainer = document.getElementById(this.config.id_div);
|
|
7306
|
+
if (boardContainer) {
|
|
7307
|
+
const pieceElements = boardContainer.querySelectorAll('.piece');
|
|
7308
|
+
pieceElements.forEach(element => element.remove());
|
|
7309
|
+
}
|
|
7310
|
+
Object.values(squares).forEach(sq => { if (sq && sq.piece) sq.piece = null; });
|
|
7311
|
+
this._clearVisualState();
|
|
7312
|
+
this._addListeners();
|
|
7313
|
+
if (this.config.onChange) this.config.onChange(gameStateBefore);
|
|
7314
|
+
return;
|
|
7725
7315
|
}
|
|
7726
|
-
}
|
|
7727
7316
|
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
* @param {Object} squares - All squares
|
|
7732
|
-
* @param {string} gameStateBefore - Game state before update
|
|
7733
|
-
* @param {boolean} animation - Whether to animate
|
|
7734
|
-
*/
|
|
7735
|
-
_doSequentialUpdate(squares, gameStateBefore, animation) {
|
|
7736
|
-
// Update each square sequentially
|
|
7317
|
+
// --- Matching greedy tra attuale e atteso ---
|
|
7318
|
+
const currentMap = {};
|
|
7319
|
+
const expectedMap = {};
|
|
7737
7320
|
Object.values(squares).forEach(square => {
|
|
7738
|
-
const expectedPieceId = this.positionService.getGamePieceId(square.id);
|
|
7739
7321
|
const currentPiece = square.piece;
|
|
7740
|
-
const
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7322
|
+
const expectedPieceId = this.positionService.getGamePieceId(square.id);
|
|
7323
|
+
if (currentPiece) {
|
|
7324
|
+
const key = (currentPiece.color + currentPiece.type).toLowerCase();
|
|
7325
|
+
if (!currentMap[key]) currentMap[key] = [];
|
|
7326
|
+
currentMap[key].push({ square, id: square.id, piece: currentPiece });
|
|
7327
|
+
}
|
|
7328
|
+
if (expectedPieceId) {
|
|
7329
|
+
const key = expectedPieceId.toLowerCase();
|
|
7330
|
+
if (!expectedMap[key]) expectedMap[key] = [];
|
|
7331
|
+
expectedMap[key].push({ square, id: square.id });
|
|
7332
|
+
}
|
|
7333
|
+
});
|
|
7334
|
+
const animationDelay = isPositionLoad ? 0 : this.config.simultaneousAnimationDelay || 0;
|
|
7335
|
+
let totalAnimations = 0;
|
|
7336
|
+
let animationsCompleted = 0;
|
|
7754
7337
|
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7338
|
+
// 1. Matching greedy: trova i movimenti
|
|
7339
|
+
const moves = [];
|
|
7340
|
+
const fromMatched = {};
|
|
7341
|
+
const toMatched = {};
|
|
7342
|
+
const unchanged = [];
|
|
7343
|
+
Object.keys(expectedMap).forEach(key => {
|
|
7344
|
+
const fromList = (currentMap[key] || []).slice();
|
|
7345
|
+
const toList = expectedMap[key].slice();
|
|
7346
|
+
const localFromMatched = new Array(fromList.length).fill(false);
|
|
7347
|
+
const localToMatched = new Array(toList.length).fill(false);
|
|
7348
|
+
// Matrice delle distanze
|
|
7349
|
+
const distances = [];
|
|
7350
|
+
for (let i = 0; i < fromList.length; i++) {
|
|
7351
|
+
distances[i] = [];
|
|
7352
|
+
for (let j = 0; j < toList.length; j++) {
|
|
7353
|
+
distances[i][j] = Math.abs(fromList[i].square.row - toList[j].square.row) +
|
|
7354
|
+
Math.abs(fromList[i].square.col - toList[j].square.col);
|
|
7355
|
+
}
|
|
7356
|
+
}
|
|
7357
|
+
while (true) {
|
|
7358
|
+
let minDist = Infinity, minI = -1, minJ = -1;
|
|
7359
|
+
for (let i = 0; i < fromList.length; i++) {
|
|
7360
|
+
if (localFromMatched[i]) continue;
|
|
7361
|
+
for (let j = 0; j < toList.length; j++) {
|
|
7362
|
+
if (localToMatched[j]) continue;
|
|
7363
|
+
if (distances[i][j] < minDist) {
|
|
7364
|
+
minDist = distances[i][j];
|
|
7365
|
+
minI = i;
|
|
7366
|
+
minJ = j;
|
|
7367
|
+
}
|
|
7764
7368
|
}
|
|
7765
7369
|
}
|
|
7370
|
+
if (minI === -1 || minJ === -1) break;
|
|
7371
|
+
// Se la posizione è la stessa E il Piece è lo stesso oggetto, non fare nulla (pezzo unchanged)
|
|
7372
|
+
if (fromList[minI].square === toList[minJ].square && squares[toList[minJ].square.id].piece === fromList[minI].piece) {
|
|
7373
|
+
unchanged.push({ square: fromList[minI].square, piece: fromList[minI].piece });
|
|
7374
|
+
localFromMatched[minI] = true;
|
|
7375
|
+
localToMatched[minJ] = true;
|
|
7376
|
+
fromMatched[fromList[minI].square.id] = true;
|
|
7377
|
+
toMatched[toList[minJ].square.id] = true;
|
|
7378
|
+
continue;
|
|
7379
|
+
}
|
|
7380
|
+
// Altrimenti, sposta il pezzo
|
|
7381
|
+
moves.push({ from: fromList[minI].square, to: toList[minJ].square, piece: fromList[minI].piece });
|
|
7382
|
+
localFromMatched[minI] = true;
|
|
7383
|
+
localToMatched[minJ] = true;
|
|
7384
|
+
fromMatched[fromList[minI].square.id] = true;
|
|
7385
|
+
toMatched[toList[minJ].square.id] = true;
|
|
7766
7386
|
}
|
|
7767
7387
|
});
|
|
7768
7388
|
|
|
7769
|
-
//
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7389
|
+
// 2. Rimozione: pezzi presenti solo in attuale (non matched)
|
|
7390
|
+
const removes = [];
|
|
7391
|
+
Object.keys(currentMap).forEach(key => {
|
|
7392
|
+
currentMap[key].forEach(({ square, piece }) => {
|
|
7393
|
+
if (!fromMatched[square.id]) {
|
|
7394
|
+
removes.push({ square, piece });
|
|
7395
|
+
}
|
|
7396
|
+
});
|
|
7397
|
+
});
|
|
7778
7398
|
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
// Analyze what changes need to be made
|
|
7790
|
-
const changeAnalysis = this._analyzePositionChanges(squares);
|
|
7399
|
+
// 3. Aggiunta: pezzi presenti solo in atteso (non matched)
|
|
7400
|
+
const adds = [];
|
|
7401
|
+
Object.keys(expectedMap).forEach(key => {
|
|
7402
|
+
expectedMap[key].forEach(({ square, id }) => {
|
|
7403
|
+
if (!toMatched[square.id]) {
|
|
7404
|
+
adds.push({ square, pieceId: key });
|
|
7405
|
+
}
|
|
7406
|
+
});
|
|
7407
|
+
});
|
|
7791
7408
|
|
|
7792
|
-
|
|
7793
|
-
|
|
7409
|
+
totalAnimations = moves.length + removes.length + adds.length;
|
|
7410
|
+
if (totalAnimations === 0) {
|
|
7794
7411
|
this._addListeners();
|
|
7795
|
-
|
|
7796
|
-
// Trigger change event if position changed
|
|
7797
7412
|
const gameStateAfter = this.positionService.getGame().fen();
|
|
7798
7413
|
if (gameStateBefore !== gameStateAfter) {
|
|
7799
7414
|
this.config.onChange(gameStateAfter);
|
|
@@ -7801,10 +7416,64 @@ var Chessboard = (function (exports) {
|
|
|
7801
7416
|
return;
|
|
7802
7417
|
}
|
|
7803
7418
|
|
|
7804
|
-
|
|
7419
|
+
// Debug: logga i pezzi unchanged
|
|
7420
|
+
if (unchanged.length > 0) {
|
|
7421
|
+
console.debug('[Chessboard] Unchanged pieces:', unchanged.map(u => u.piece.id + '@' + u.square.id));
|
|
7422
|
+
}
|
|
7805
7423
|
|
|
7806
|
-
|
|
7807
|
-
|
|
7424
|
+
const onAnimationComplete = () => {
|
|
7425
|
+
animationsCompleted++;
|
|
7426
|
+
if (animationsCompleted === totalAnimations) {
|
|
7427
|
+
// Pulizia finale robusta: rimuovi tutti i pezzi orfani dal DOM e dal riferimento JS
|
|
7428
|
+
Object.values(this.boardService.getAllSquares()).forEach(square => {
|
|
7429
|
+
const expectedPieceId = this.positionService.getGamePieceId(square.id);
|
|
7430
|
+
if (!expectedPieceId && typeof square.forceRemoveAllPieces === 'function') {
|
|
7431
|
+
square.forceRemoveAllPieces();
|
|
7432
|
+
}
|
|
7433
|
+
});
|
|
7434
|
+
this._addListeners();
|
|
7435
|
+
const gameStateAfter = this.positionService.getGame().fen();
|
|
7436
|
+
if (gameStateBefore !== gameStateAfter) {
|
|
7437
|
+
this.config.onChange(gameStateAfter);
|
|
7438
|
+
}
|
|
7439
|
+
}
|
|
7440
|
+
};
|
|
7441
|
+
|
|
7442
|
+
// 4. Esegui tutte le animazioni con delay
|
|
7443
|
+
let idx = 0;
|
|
7444
|
+
moves.forEach(move => {
|
|
7445
|
+
setTimeout(() => {
|
|
7446
|
+
this.pieceService.translatePiece(
|
|
7447
|
+
move,
|
|
7448
|
+
false,
|
|
7449
|
+
animation,
|
|
7450
|
+
this._createDragFunction.bind(this),
|
|
7451
|
+
onAnimationComplete
|
|
7452
|
+
);
|
|
7453
|
+
}, idx++ * animationDelay);
|
|
7454
|
+
});
|
|
7455
|
+
removes.forEach(op => {
|
|
7456
|
+
setTimeout(() => {
|
|
7457
|
+
if (typeof op.square.forceRemoveAllPieces === 'function') {
|
|
7458
|
+
op.square.forceRemoveAllPieces();
|
|
7459
|
+
onAnimationComplete();
|
|
7460
|
+
} else {
|
|
7461
|
+
this.pieceService.removePieceFromSquare(op.square, animation, onAnimationComplete);
|
|
7462
|
+
}
|
|
7463
|
+
}, idx++ * animationDelay);
|
|
7464
|
+
});
|
|
7465
|
+
adds.forEach(op => {
|
|
7466
|
+
setTimeout(() => {
|
|
7467
|
+
const newPiece = this.pieceService.convertPiece(op.pieceId);
|
|
7468
|
+
this.pieceService.addPieceOnSquare(
|
|
7469
|
+
op.square,
|
|
7470
|
+
newPiece,
|
|
7471
|
+
animation,
|
|
7472
|
+
this._createDragFunction.bind(this),
|
|
7473
|
+
onAnimationComplete
|
|
7474
|
+
);
|
|
7475
|
+
}, idx++ * animationDelay);
|
|
7476
|
+
});
|
|
7808
7477
|
}
|
|
7809
7478
|
|
|
7810
7479
|
/**
|
|
@@ -8130,99 +7799,418 @@ var Chessboard = (function (exports) {
|
|
|
8130
7799
|
}
|
|
8131
7800
|
|
|
8132
7801
|
// -------------------
|
|
8133
|
-
// Public API Methods
|
|
7802
|
+
// Public API Methods (Refactored)
|
|
8134
7803
|
// -------------------
|
|
8135
7804
|
|
|
7805
|
+
// --- POSITION & STATE ---
|
|
8136
7806
|
/**
|
|
8137
|
-
*
|
|
8138
|
-
* @returns {string}
|
|
7807
|
+
* Get the current position as FEN
|
|
7808
|
+
* @returns {string}
|
|
8139
7809
|
*/
|
|
8140
|
-
fen()
|
|
8141
|
-
|
|
7810
|
+
getPosition() { return this.fen(); }
|
|
7811
|
+
/**
|
|
7812
|
+
* Set the board position (FEN or object)
|
|
7813
|
+
* @param {string|Object} position
|
|
7814
|
+
* @param {Object} [opts]
|
|
7815
|
+
* @param {boolean} [opts.animate=true]
|
|
7816
|
+
* @returns {boolean}
|
|
7817
|
+
*/
|
|
7818
|
+
setPosition(position, opts = {}) {
|
|
7819
|
+
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
7820
|
+
// Remove highlights and selections
|
|
7821
|
+
if (this.boardService && this.boardService.applyToAllSquares) {
|
|
7822
|
+
this.boardService.applyToAllSquares('removeHint');
|
|
7823
|
+
this.boardService.applyToAllSquares('deselect');
|
|
7824
|
+
this.boardService.applyToAllSquares('unmoved');
|
|
7825
|
+
}
|
|
7826
|
+
if (this.positionService && this.positionService.setGame) {
|
|
7827
|
+
this.positionService.setGame(position);
|
|
7828
|
+
}
|
|
7829
|
+
if (this._updateBoardPieces) {
|
|
7830
|
+
this._updateBoardPieces(animate, true);
|
|
7831
|
+
}
|
|
7832
|
+
// Forza la sincronizzazione dopo setPosition
|
|
7833
|
+
this._updateBoardPieces(true, false);
|
|
7834
|
+
return true;
|
|
7835
|
+
}
|
|
7836
|
+
/**
|
|
7837
|
+
* Reset the board to the starting position
|
|
7838
|
+
* @param {Object} [opts]
|
|
7839
|
+
* @param {boolean} [opts.animate=true]
|
|
7840
|
+
* @returns {boolean}
|
|
7841
|
+
*/
|
|
7842
|
+
reset(opts = {}) {
|
|
7843
|
+
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
7844
|
+
// Use the default starting position from config or fallback
|
|
7845
|
+
const startPosition = this.config && this.config.position ? this.config.position : 'start';
|
|
7846
|
+
this._updateBoardPieces(animate);
|
|
7847
|
+
const result = this.setPosition(startPosition, { animate });
|
|
7848
|
+
// Forza la sincronizzazione dopo reset
|
|
7849
|
+
this._updateBoardPieces(true, false);
|
|
7850
|
+
return result;
|
|
7851
|
+
}
|
|
7852
|
+
/**
|
|
7853
|
+
* Clear the board
|
|
7854
|
+
* @param {Object} [opts]
|
|
7855
|
+
* @param {boolean} [opts.animate=true]
|
|
7856
|
+
* @returns {boolean}
|
|
7857
|
+
*/
|
|
7858
|
+
clear(opts = {}) {
|
|
7859
|
+
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
7860
|
+
if (!this.positionService || !this.positionService.getGame()) {
|
|
7861
|
+
return false;
|
|
7862
|
+
}
|
|
7863
|
+
if (this._clearVisualState) this._clearVisualState();
|
|
7864
|
+
this.positionService.getGame().clear();
|
|
7865
|
+
// Forza la rimozione di tutti i pezzi dal DOM
|
|
7866
|
+
if (this.boardService && this.boardService.squares) {
|
|
7867
|
+
Object.values(this.boardService.squares).forEach(sq => {
|
|
7868
|
+
if (sq && sq.piece) sq.piece = null;
|
|
7869
|
+
});
|
|
7870
|
+
}
|
|
7871
|
+
if (this._updateBoardPieces) {
|
|
7872
|
+
this._updateBoardPieces(animate, true);
|
|
7873
|
+
}
|
|
7874
|
+
// Forza la sincronizzazione dopo clear
|
|
7875
|
+
this._updateBoardPieces(true, false);
|
|
7876
|
+
return true;
|
|
8142
7877
|
}
|
|
8143
7878
|
|
|
7879
|
+
// --- MOVE MANAGEMENT ---
|
|
8144
7880
|
/**
|
|
8145
|
-
*
|
|
8146
|
-
* @
|
|
7881
|
+
* Undo last move
|
|
7882
|
+
* @param {Object} [opts]
|
|
7883
|
+
* @param {boolean} [opts.animate=true]
|
|
7884
|
+
* @returns {boolean}
|
|
8147
7885
|
*/
|
|
8148
|
-
|
|
8149
|
-
|
|
7886
|
+
undoMove(opts = {}) {
|
|
7887
|
+
const undone = this.positionService.getGame().undo();
|
|
7888
|
+
if (undone) {
|
|
7889
|
+
this._undoneMoves.push(undone);
|
|
7890
|
+
// Forza refresh completo di tutti i pezzi dopo undo
|
|
7891
|
+
this._updateBoardPieces(true, true);
|
|
7892
|
+
return undone;
|
|
7893
|
+
}
|
|
7894
|
+
return null;
|
|
8150
7895
|
}
|
|
7896
|
+
/**
|
|
7897
|
+
* Redo last undone move
|
|
7898
|
+
* @param {Object} [opts]
|
|
7899
|
+
* @param {boolean} [opts.animate=true]
|
|
7900
|
+
* @returns {boolean}
|
|
7901
|
+
*/
|
|
7902
|
+
redoMove(opts = {}) {
|
|
7903
|
+
if (this._undoneMoves && this._undoneMoves.length > 0) {
|
|
7904
|
+
const move = this._undoneMoves.pop();
|
|
7905
|
+
const moveObj = { from: move.from, to: move.to };
|
|
7906
|
+
if (move.promotion) moveObj.promotion = move.promotion;
|
|
7907
|
+
const result = this.positionService.getGame().move(moveObj);
|
|
7908
|
+
// Forza refresh completo di tutti i pezzi dopo redo
|
|
7909
|
+
this._updateBoardPieces(true, true);
|
|
7910
|
+
return result;
|
|
7911
|
+
}
|
|
7912
|
+
return false;
|
|
7913
|
+
}
|
|
7914
|
+
/**
|
|
7915
|
+
* Get legal moves for a square
|
|
7916
|
+
* @param {string} square
|
|
7917
|
+
* @returns {Array}
|
|
7918
|
+
*/
|
|
7919
|
+
getLegalMoves(square) { return this.legalMoves(square); }
|
|
8151
7920
|
|
|
7921
|
+
// --- PIECE MANAGEMENT ---
|
|
8152
7922
|
/**
|
|
8153
|
-
*
|
|
8154
|
-
* @param {string
|
|
8155
|
-
* @
|
|
8156
|
-
* @param {boolean} [animation=true] - Whether to animate
|
|
7923
|
+
* Get the piece at a square
|
|
7924
|
+
* @param {string} square
|
|
7925
|
+
* @returns {string|null}
|
|
8157
7926
|
*/
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
this.boardService.
|
|
8161
|
-
|
|
7927
|
+
getPiece(square) {
|
|
7928
|
+
// Sempre leggi lo stato aggiornato dal boardService
|
|
7929
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
7930
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[getPiece] Parameter square is invalid');
|
|
7931
|
+
// Forza sync prima di leggere
|
|
7932
|
+
this._updateBoardPieces(false, false);
|
|
7933
|
+
const piece = squareObj.piece;
|
|
7934
|
+
if (!piece) return null;
|
|
7935
|
+
return (piece.color + piece.type).toLowerCase();
|
|
7936
|
+
}
|
|
7937
|
+
/**
|
|
7938
|
+
* Put a piece on a square
|
|
7939
|
+
* @param {string|Piece} piece
|
|
7940
|
+
* @param {string|Square} square
|
|
7941
|
+
* @param {Object} [opts]
|
|
7942
|
+
* @param {boolean} [opts.animate=true]
|
|
7943
|
+
* @returns {boolean}
|
|
7944
|
+
*/
|
|
7945
|
+
putPiece(piece, square, opts = {}) {
|
|
7946
|
+
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
7947
|
+
let pieceStr = piece;
|
|
7948
|
+
if (typeof piece === 'object' && piece.type && piece.color) {
|
|
7949
|
+
pieceStr = (piece.color + piece.type).toLowerCase();
|
|
7950
|
+
} else if (typeof piece === 'string' && piece.length === 2) {
|
|
7951
|
+
const a = piece[0].toLowerCase();
|
|
7952
|
+
const b = piece[1].toLowerCase();
|
|
7953
|
+
const types = 'kqrbnp';
|
|
7954
|
+
const colors = 'wb';
|
|
7955
|
+
if (types.includes(a) && colors.includes(b)) {
|
|
7956
|
+
pieceStr = b + a;
|
|
7957
|
+
} else if (colors.includes(a) && types.includes(b)) {
|
|
7958
|
+
pieceStr = a + b;
|
|
7959
|
+
} else {
|
|
7960
|
+
throw new Error(`[putPiece] Invalid piece: ${piece}`);
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
7964
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[putPiece] Parameter square is invalid');
|
|
7965
|
+
const pieceObj = this.pieceService.convertPiece(pieceStr);
|
|
7966
|
+
if (!pieceObj || typeof pieceObj !== 'object' || !('type' in pieceObj)) throw new Error('[putPiece] Parameter piece is invalid');
|
|
7967
|
+
// Aggiorna solo il motore chess.js
|
|
7968
|
+
const chessJsPiece = { type: pieceObj.type, color: pieceObj.color };
|
|
7969
|
+
const game = this.positionService.getGame();
|
|
7970
|
+
const result = game.put(chessJsPiece, squareObj.id);
|
|
7971
|
+
if (!result) throw new Error(`[putPiece] Game.put failed for ${pieceStr} on ${squareObj.id}`);
|
|
7972
|
+
// Non aggiornare direttamente square.piece!
|
|
7973
|
+
// Riallinea la board JS allo stato del motore
|
|
7974
|
+
this._updateBoardPieces(animate);
|
|
7975
|
+
return true;
|
|
7976
|
+
}
|
|
7977
|
+
/**
|
|
7978
|
+
* Remove a piece from a square
|
|
7979
|
+
* @param {string|Square} square
|
|
7980
|
+
* @param {Object} [opts]
|
|
7981
|
+
* @param {boolean} [opts.animate=true]
|
|
7982
|
+
* @returns {string|null}
|
|
7983
|
+
*/
|
|
7984
|
+
removePiece(square, opts = {}) {
|
|
7985
|
+
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
7986
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
7987
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[removePiece] Parameter square is invalid');
|
|
7988
|
+
// Aggiorna solo il motore chess.js
|
|
7989
|
+
const game = this.positionService.getGame();
|
|
7990
|
+
game.remove(squareObj.id);
|
|
7991
|
+
// Non aggiornare direttamente square.piece!
|
|
7992
|
+
// Riallinea la board JS allo stato del motore
|
|
7993
|
+
this._updateBoardPieces(animate);
|
|
7994
|
+
return true;
|
|
7995
|
+
}
|
|
8162
7996
|
|
|
8163
|
-
|
|
8164
|
-
|
|
7997
|
+
// --- BOARD CONTROL ---
|
|
7998
|
+
/**
|
|
7999
|
+
* Flip the board orientation
|
|
8000
|
+
* @param {Object} [opts]
|
|
8001
|
+
* @param {boolean} [opts.animate=true]
|
|
8002
|
+
*/
|
|
8003
|
+
flipBoard(opts = {}) {
|
|
8004
|
+
if (this.coordinateService && this.coordinateService.flipOrientation) {
|
|
8005
|
+
this.coordinateService.flipOrientation();
|
|
8006
|
+
}
|
|
8007
|
+
if (this._buildBoard) this._buildBoard();
|
|
8008
|
+
if (this._buildSquares) this._buildSquares();
|
|
8009
|
+
if (this._addListeners) this._addListeners();
|
|
8010
|
+
if (this._updateBoardPieces) this._updateBoardPieces(opts.animate !== false);
|
|
8011
|
+
console.log('FEN dopo flip:', this.fen(), 'Orientamento:', this.coordinateService.getOrientation());
|
|
8012
|
+
}
|
|
8013
|
+
/**
|
|
8014
|
+
* Set the board orientation
|
|
8015
|
+
* @param {'w'|'b'} color
|
|
8016
|
+
* @param {Object} [opts]
|
|
8017
|
+
* @param {boolean} [opts.animate=true]
|
|
8018
|
+
*/
|
|
8019
|
+
setOrientation(color, opts = {}) {
|
|
8020
|
+
if (this.validationService.isValidOrientation(color)) {
|
|
8021
|
+
this.coordinateService.setOrientation(color);
|
|
8022
|
+
if (this._buildBoard) this._buildBoard();
|
|
8023
|
+
if (this._buildSquares) this._buildSquares();
|
|
8024
|
+
if (this._addListeners) this._addListeners();
|
|
8025
|
+
if (this._updateBoardPieces) this._updateBoardPieces(opts.animate !== false);
|
|
8026
|
+
}
|
|
8027
|
+
return this.coordinateService.getOrientation();
|
|
8028
|
+
}
|
|
8029
|
+
/**
|
|
8030
|
+
* Get the current orientation
|
|
8031
|
+
* @returns {'w'|'b'}
|
|
8032
|
+
*/
|
|
8033
|
+
getOrientation() { return this.orientation(); }
|
|
8034
|
+
/**
|
|
8035
|
+
* Resize the board
|
|
8036
|
+
* @param {number|string} size
|
|
8037
|
+
*/
|
|
8038
|
+
resizeBoard(size) {
|
|
8039
|
+
if (size === 'auto') {
|
|
8040
|
+
this.config.size = 'auto';
|
|
8041
|
+
document.documentElement.style.setProperty('--dimBoard', 'auto');
|
|
8042
|
+
this._updateBoardPieces(false);
|
|
8043
|
+
return true;
|
|
8044
|
+
}
|
|
8045
|
+
if (typeof size !== 'number' || size < 50 || size > 3000) {
|
|
8046
|
+
throw new Error(`[resizeBoard] Invalid size: ${size}`);
|
|
8047
|
+
}
|
|
8048
|
+
this.config.size = size;
|
|
8049
|
+
document.documentElement.style.setProperty('--dimBoard', `${size}px`);
|
|
8050
|
+
this._updateBoardPieces(false);
|
|
8051
|
+
return true;
|
|
8165
8052
|
}
|
|
8166
8053
|
|
|
8054
|
+
// --- HIGHLIGHTING & UI ---
|
|
8167
8055
|
/**
|
|
8168
|
-
*
|
|
8056
|
+
* Highlight a square
|
|
8057
|
+
* @param {string|Square} square
|
|
8058
|
+
* @param {Object} [opts]
|
|
8169
8059
|
*/
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
this.boardService.
|
|
8173
|
-
|
|
8174
|
-
this.
|
|
8175
|
-
|
|
8176
|
-
this.
|
|
8177
|
-
|
|
8060
|
+
highlight(square, opts = {}) {
|
|
8061
|
+
// API: accetta id, converte subito in oggetto
|
|
8062
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
8063
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[highlight] Parameter square is invalid');
|
|
8064
|
+
if (this.boardService && this.boardService.highlightSquare) {
|
|
8065
|
+
this.boardService.highlightSquare(squareObj, opts);
|
|
8066
|
+
} else if (this.eventService && this.eventService.highlightSquare) {
|
|
8067
|
+
this.eventService.highlightSquare(squareObj, opts);
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
/**
|
|
8071
|
+
* Remove highlight from a square
|
|
8072
|
+
* @param {string|Square} square
|
|
8073
|
+
* @param {Object} [opts]
|
|
8074
|
+
*/
|
|
8075
|
+
dehighlight(square, opts = {}) {
|
|
8076
|
+
// API: accetta id, converte subito in oggetto
|
|
8077
|
+
const squareObj = typeof square === 'string' ? this.boardService.getSquare(square) : square;
|
|
8078
|
+
if (!squareObj || typeof squareObj !== 'object' || !('id' in squareObj)) throw new Error('[dehighlight] Parameter square is invalid');
|
|
8079
|
+
if (this.boardService && this.boardService.dehighlightSquare) {
|
|
8080
|
+
this.boardService.dehighlightSquare(squareObj, opts);
|
|
8081
|
+
} else if (this.eventService && this.eventService.dehighlightSquare) {
|
|
8082
|
+
this.eventService.dehighlightSquare(squareObj, opts);
|
|
8083
|
+
}
|
|
8084
|
+
}
|
|
8085
|
+
|
|
8086
|
+
// --- GAME INFO ---
|
|
8087
|
+
/**
|
|
8088
|
+
* Get FEN string
|
|
8089
|
+
* @returns {string}
|
|
8090
|
+
*/
|
|
8091
|
+
fen() {
|
|
8092
|
+
// Avoid recursion: call the underlying game object's fen()
|
|
8093
|
+
const game = this.positionService.getGame();
|
|
8094
|
+
if (!game || typeof game.fen !== 'function') return '';
|
|
8095
|
+
return game.fen();
|
|
8096
|
+
}
|
|
8097
|
+
/**
|
|
8098
|
+
* Get current turn
|
|
8099
|
+
* @returns {'w'|'b'}
|
|
8100
|
+
*/
|
|
8101
|
+
turn() { return this.positionService.getGame().turn(); }
|
|
8102
|
+
/**
|
|
8103
|
+
* Is the game over?
|
|
8104
|
+
* @returns {boolean}
|
|
8105
|
+
*/
|
|
8106
|
+
isGameOver() {
|
|
8107
|
+
// Forza sync prima di interrogare il motore
|
|
8108
|
+
this._updateBoardPieces(false, false);
|
|
8109
|
+
const game = this.positionService.getGame();
|
|
8110
|
+
if (!game) return false;
|
|
8111
|
+
if (game.isGameOver) return game.isGameOver();
|
|
8112
|
+
// Fallback: checkmate or draw
|
|
8113
|
+
if (game.isCheckmate && game.isCheckmate()) return true;
|
|
8114
|
+
if (game.isDraw && game.isDraw()) return true;
|
|
8115
|
+
return false;
|
|
8116
|
+
}
|
|
8117
|
+
/**
|
|
8118
|
+
* Is it checkmate?
|
|
8119
|
+
* @returns {boolean}
|
|
8120
|
+
*/
|
|
8121
|
+
isCheckmate() {
|
|
8122
|
+
const game = this.positionService.getGame();
|
|
8123
|
+
if (!game) return false;
|
|
8124
|
+
return game.isCheckmate ? game.isCheckmate() : false;
|
|
8125
|
+
}
|
|
8126
|
+
/**
|
|
8127
|
+
* Is it draw?
|
|
8128
|
+
* @returns {boolean}
|
|
8129
|
+
*/
|
|
8130
|
+
isDraw() {
|
|
8131
|
+
const game = this.positionService.getGame();
|
|
8132
|
+
if (!game) return false;
|
|
8133
|
+
return game.isDraw ? game.isDraw() : false;
|
|
8134
|
+
}
|
|
8135
|
+
/**
|
|
8136
|
+
* Get move history
|
|
8137
|
+
* @returns {Array}
|
|
8138
|
+
*/
|
|
8139
|
+
getHistory() {
|
|
8140
|
+
const game = this.positionService.getGame();
|
|
8141
|
+
if (!game) return [];
|
|
8142
|
+
return game.history ? game.history() : [];
|
|
8143
|
+
}
|
|
8178
8144
|
|
|
8145
|
+
// --- LIFECYCLE ---
|
|
8146
|
+
/**
|
|
8147
|
+
* Destroy the board and cleanup all resources
|
|
8148
|
+
*/
|
|
8149
|
+
destroy() {
|
|
8150
|
+
// Remove event listeners
|
|
8151
|
+
if (this.eventService && typeof this.eventService.removeListeners === 'function') {
|
|
8152
|
+
this.eventService.removeListeners();
|
|
8153
|
+
}
|
|
8154
|
+
// Clear timeouts
|
|
8179
8155
|
if (this._updateTimeout) {
|
|
8180
8156
|
clearTimeout(this._updateTimeout);
|
|
8181
8157
|
this._updateTimeout = null;
|
|
8182
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
|
+
}
|
|
8183
8187
|
}
|
|
8184
|
-
|
|
8185
8188
|
/**
|
|
8186
|
-
*
|
|
8187
|
-
* @param {number|string} size - New size
|
|
8189
|
+
* Rebuild the board
|
|
8188
8190
|
*/
|
|
8189
|
-
|
|
8190
|
-
this.boardService.resize(size);
|
|
8191
|
-
this._updateBoardPieces();
|
|
8192
|
-
}
|
|
8191
|
+
rebuild() { this._initialize(); }
|
|
8193
8192
|
|
|
8193
|
+
// --- CONFIGURATION ---
|
|
8194
8194
|
/**
|
|
8195
|
-
*
|
|
8195
|
+
* Get current config
|
|
8196
|
+
* @returns {Object}
|
|
8196
8197
|
*/
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
// Check if there are any pieces on the board
|
|
8204
|
-
const position = this.positionService.getPosition();
|
|
8205
|
-
const hasPieces = Object.keys(position).length > 0;
|
|
8206
|
-
|
|
8207
|
-
if (hasPieces) {
|
|
8208
|
-
currentPosition = this.positionService.getGame().fen();
|
|
8209
|
-
}
|
|
8210
|
-
} catch (error) {
|
|
8211
|
-
console.log('No valid position to save during flip');
|
|
8212
|
-
}
|
|
8198
|
+
getConfig() { return this.config; }
|
|
8199
|
+
/**
|
|
8200
|
+
* Set new config
|
|
8201
|
+
* @param {Object} newConfig
|
|
8202
|
+
*/
|
|
8203
|
+
setConfig(newConfig) { this.setConfig(newConfig); }
|
|
8213
8204
|
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
this._initParams();
|
|
8205
|
+
// --- ALIASES/DEPRECATED ---
|
|
8206
|
+
// Note: These methods are now implemented as aliases at the end of the class
|
|
8217
8207
|
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
this.
|
|
8223
|
-
this.
|
|
8224
|
-
this._addListeners();
|
|
8225
|
-
this._updateBoardPieces(true, true);
|
|
8208
|
+
/**
|
|
8209
|
+
* Alias for flipBoard (for backward compatibility)
|
|
8210
|
+
*/
|
|
8211
|
+
flip(opts = {}) {
|
|
8212
|
+
this._updateBoardPieces(opts.animate !== false);
|
|
8213
|
+
return this.flipBoard(opts);
|
|
8226
8214
|
}
|
|
8227
8215
|
|
|
8228
8216
|
/**
|
|
@@ -8245,51 +8233,19 @@ var Chessboard = (function (exports) {
|
|
|
8245
8233
|
this.load(position, {}, animate); // load() already handles isPositionLoad=true
|
|
8246
8234
|
}
|
|
8247
8235
|
|
|
8248
|
-
/**
|
|
8249
|
-
* Makes a move on the board
|
|
8250
|
-
* @param {string|Object} move - Move to make
|
|
8251
|
-
* @param {boolean} [animate=true] - Whether to animate
|
|
8252
|
-
* @returns {boolean} True if move was successful
|
|
8253
|
-
*/
|
|
8254
|
-
move(move, animate = true) {
|
|
8255
|
-
if (typeof move === 'string') {
|
|
8256
|
-
// Parse move string (e.g., 'e2e4')
|
|
8257
|
-
const moveObj = this.moveService.parseMove(move);
|
|
8258
|
-
if (!moveObj) return false;
|
|
8259
|
-
|
|
8260
|
-
const fromSquare = this.boardService.getSquare(moveObj.from);
|
|
8261
|
-
const toSquare = this.boardService.getSquare(moveObj.to);
|
|
8262
|
-
|
|
8263
|
-
if (!fromSquare || !toSquare) return false;
|
|
8264
|
-
|
|
8265
|
-
return this._onMove(fromSquare, toSquare, moveObj.promotion, animate);
|
|
8266
|
-
} else if (move && move.from && move.to) {
|
|
8267
|
-
// Handle move object
|
|
8268
|
-
const fromSquare = this.boardService.getSquare(move.from);
|
|
8269
|
-
const toSquare = this.boardService.getSquare(move.to);
|
|
8270
|
-
|
|
8271
|
-
if (!fromSquare || !toSquare) return false;
|
|
8272
|
-
|
|
8273
|
-
return this._onMove(fromSquare, toSquare, move.promotion, animate);
|
|
8274
|
-
}
|
|
8275
|
-
|
|
8276
|
-
return false;
|
|
8277
|
-
}
|
|
8278
|
-
|
|
8279
8236
|
/**
|
|
8280
8237
|
* Undoes the last move
|
|
8281
8238
|
* @param {boolean} [animate=true] - Whether to animate
|
|
8282
8239
|
* @returns {boolean} True if undo was successful
|
|
8283
8240
|
*/
|
|
8284
8241
|
undo(animate = true) {
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
}
|
|
8242
|
+
const undone = this.positionService.getGame().undo();
|
|
8243
|
+
if (undone) {
|
|
8244
|
+
this._undoneMoves.push(undone);
|
|
8245
|
+
this._updateBoardPieces(animate);
|
|
8246
|
+
return undone;
|
|
8291
8247
|
}
|
|
8292
|
-
return
|
|
8248
|
+
return null;
|
|
8293
8249
|
}
|
|
8294
8250
|
|
|
8295
8251
|
/**
|
|
@@ -8298,12 +8254,13 @@ var Chessboard = (function (exports) {
|
|
|
8298
8254
|
* @returns {boolean} True if redo was successful
|
|
8299
8255
|
*/
|
|
8300
8256
|
redo(animate = true) {
|
|
8301
|
-
if (this.
|
|
8302
|
-
const
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8257
|
+
if (this._undoneMoves && this._undoneMoves.length > 0) {
|
|
8258
|
+
const move = this._undoneMoves.pop();
|
|
8259
|
+
const moveObj = { from: move.from, to: move.to };
|
|
8260
|
+
if (move.promotion) moveObj.promotion = move.promotion;
|
|
8261
|
+
const result = this.positionService.getGame().move(moveObj);
|
|
8262
|
+
this._updateBoardPieces(animate);
|
|
8263
|
+
return result;
|
|
8307
8264
|
}
|
|
8308
8265
|
return false;
|
|
8309
8266
|
}
|
|
@@ -8465,112 +8422,6 @@ var Chessboard = (function (exports) {
|
|
|
8465
8422
|
}
|
|
8466
8423
|
}
|
|
8467
8424
|
|
|
8468
|
-
/**
|
|
8469
|
-
* Clears the board
|
|
8470
|
-
* @param {boolean} [animate=true] - Whether to animate
|
|
8471
|
-
*/
|
|
8472
|
-
clear(animate = true) {
|
|
8473
|
-
// Check if services are available
|
|
8474
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
8475
|
-
console.log('Cannot clear - position service not available');
|
|
8476
|
-
return;
|
|
8477
|
-
}
|
|
8478
|
-
|
|
8479
|
-
// Clear visual state first
|
|
8480
|
-
this._clearVisualState();
|
|
8481
|
-
|
|
8482
|
-
// Clear game state
|
|
8483
|
-
this.positionService.getGame().clear();
|
|
8484
|
-
|
|
8485
|
-
// Update board visually
|
|
8486
|
-
this._updateBoardPieces(animate, true); // Position change
|
|
8487
|
-
}
|
|
8488
|
-
|
|
8489
|
-
/**
|
|
8490
|
-
* Resets the board to starting position
|
|
8491
|
-
* @param {boolean} [animate=true] - Whether to animate
|
|
8492
|
-
*/
|
|
8493
|
-
reset(animate = true) {
|
|
8494
|
-
// Check if services are available
|
|
8495
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
8496
|
-
console.log('Cannot reset - position service not available');
|
|
8497
|
-
return;
|
|
8498
|
-
}
|
|
8499
|
-
|
|
8500
|
-
this.positionService.getGame().reset();
|
|
8501
|
-
this._updateBoardPieces(animate, true); // Position change
|
|
8502
|
-
}
|
|
8503
|
-
|
|
8504
|
-
/**
|
|
8505
|
-
* Puts a piece on a square
|
|
8506
|
-
* @param {string} square - Square to put piece on
|
|
8507
|
-
* @param {string} piece - Piece to put
|
|
8508
|
-
* @param {boolean} [animate=true] - Whether to animate
|
|
8509
|
-
* @returns {boolean} True if successful
|
|
8510
|
-
*/
|
|
8511
|
-
put(square, piece, animate = true) {
|
|
8512
|
-
console.log(`put() called with square: ${square}, piece: ${piece}`);
|
|
8513
|
-
|
|
8514
|
-
if (!this.validationService.isValidSquare(square) || !this.validationService.isValidPiece(piece)) {
|
|
8515
|
-
console.log('Validation failed');
|
|
8516
|
-
return false;
|
|
8517
|
-
}
|
|
8518
|
-
|
|
8519
|
-
// Check if services are available
|
|
8520
|
-
if (!this.positionService || !this.positionService.getGame()) {
|
|
8521
|
-
console.log('Cannot put piece - position service not available');
|
|
8522
|
-
return false;
|
|
8523
|
-
}
|
|
8524
|
-
|
|
8525
|
-
const pieceObj = this.pieceService.convertPiece(piece);
|
|
8526
|
-
console.log(`Converted piece:`, pieceObj);
|
|
8527
|
-
console.log(`Piece type: ${pieceObj.type}, color: ${pieceObj.color}`);
|
|
8528
|
-
|
|
8529
|
-
const squareObj = this.boardService.getSquare(square);
|
|
8530
|
-
|
|
8531
|
-
if (!squareObj) {
|
|
8532
|
-
console.log('Square not found');
|
|
8533
|
-
return false;
|
|
8534
|
-
}
|
|
8535
|
-
|
|
8536
|
-
// Update game state - note: chess.js expects (piece, square) order
|
|
8537
|
-
const chessJsPiece = { type: pieceObj.type.toLowerCase(), color: pieceObj.color.toLowerCase() };
|
|
8538
|
-
console.log(`Chess.js piece:`, chessJsPiece);
|
|
8539
|
-
|
|
8540
|
-
const result = this.positionService.getGame().put(chessJsPiece, square);
|
|
8541
|
-
console.log(`Chess.js put result:`, result);
|
|
8542
|
-
|
|
8543
|
-
if (result) {
|
|
8544
|
-
// Update visual representation
|
|
8545
|
-
this._updateBoardPieces(animate, true); // Position change
|
|
8546
|
-
}
|
|
8547
|
-
|
|
8548
|
-
return result;
|
|
8549
|
-
}
|
|
8550
|
-
|
|
8551
|
-
/**
|
|
8552
|
-
* Removes a piece from a square
|
|
8553
|
-
* @param {string} square - Square to remove piece from
|
|
8554
|
-
* @param {boolean} [animate=true] - Whether to animate
|
|
8555
|
-
* @returns {boolean} True if successful
|
|
8556
|
-
*/
|
|
8557
|
-
remove(square, animate = true) {
|
|
8558
|
-
if (!this.validationService.isValidSquare(square)) {
|
|
8559
|
-
return false;
|
|
8560
|
-
}
|
|
8561
|
-
|
|
8562
|
-
const squareObj = this.boardService.getSquare(square);
|
|
8563
|
-
if (!squareObj) return false;
|
|
8564
|
-
|
|
8565
|
-
// Update game state
|
|
8566
|
-
this.positionService.getGame().remove(square);
|
|
8567
|
-
|
|
8568
|
-
// Update visual representation
|
|
8569
|
-
this._updateBoardPieces(animate, true); // Position change
|
|
8570
|
-
|
|
8571
|
-
return true;
|
|
8572
|
-
}
|
|
8573
|
-
|
|
8574
8425
|
/**
|
|
8575
8426
|
* Gets configuration options
|
|
8576
8427
|
* @returns {Object} Configuration object
|
|
@@ -8596,23 +8447,6 @@ var Chessboard = (function (exports) {
|
|
|
8596
8447
|
}
|
|
8597
8448
|
}
|
|
8598
8449
|
|
|
8599
|
-
/**
|
|
8600
|
-
* Gets or sets the animation style
|
|
8601
|
-
* @param {string} [style] - New animation style ('sequential' or 'simultaneous')
|
|
8602
|
-
* @returns {string} Current animation style
|
|
8603
|
-
*/
|
|
8604
|
-
animationStyle(style) {
|
|
8605
|
-
if (style === undefined) {
|
|
8606
|
-
return this.config.animationStyle;
|
|
8607
|
-
}
|
|
8608
|
-
|
|
8609
|
-
if (this.validationService.isValidAnimationStyle(style)) {
|
|
8610
|
-
this.config.animationStyle = style;
|
|
8611
|
-
}
|
|
8612
|
-
|
|
8613
|
-
return this.config.animationStyle;
|
|
8614
|
-
}
|
|
8615
|
-
|
|
8616
8450
|
/**
|
|
8617
8451
|
* Gets or sets the simultaneous animation delay
|
|
8618
8452
|
* @param {number} [delay] - New delay in milliseconds
|
|
@@ -8632,7 +8466,97 @@ var Chessboard = (function (exports) {
|
|
|
8632
8466
|
|
|
8633
8467
|
// Additional API methods would be added here following the same pattern
|
|
8634
8468
|
// This is a good starting point for the refactored architecture
|
|
8635
|
-
|
|
8469
|
+
|
|
8470
|
+
// Additional API methods and aliases for backward compatibility
|
|
8471
|
+
insert(square, piece) { return this.putPiece(piece, square); }
|
|
8472
|
+
build() { return this._initialize(); }
|
|
8473
|
+
ascii() { return this.positionService.getGame().ascii(); }
|
|
8474
|
+
board() { return this.positionService.getGame().board(); }
|
|
8475
|
+
getCastlingRights(color) { return this.positionService.getGame().getCastlingRights(color); }
|
|
8476
|
+
getComment() { return this.positionService.getGame().getComment(); }
|
|
8477
|
+
getComments() { return this.positionService.getGame().getComments(); }
|
|
8478
|
+
lastMove() { return this.positionService.getGame().lastMove(); }
|
|
8479
|
+
moveNumber() { return this.positionService.getGame().moveNumber(); }
|
|
8480
|
+
moves(options = {}) { return this.positionService.getGame().moves(options); }
|
|
8481
|
+
squareColor(squareId) { return this.boardService.getSquare(squareId).isWhite() ? 'light' : 'dark'; }
|
|
8482
|
+
isDrawByFiftyMoves() { return this.positionService.getGame().isDrawByFiftyMoves(); }
|
|
8483
|
+
isInsufficientMaterial() { return this.positionService.getGame().isInsufficientMaterial(); }
|
|
8484
|
+
removeComment() { return this.positionService.getGame().removeComment(); }
|
|
8485
|
+
removeComments() { return this.positionService.getGame().removeComments(); }
|
|
8486
|
+
removeHeader(field) { return this.positionService.getGame().removeHeader(field); }
|
|
8487
|
+
setCastlingRights(color, rights) { return this.positionService.getGame().setCastlingRights(color, rights); }
|
|
8488
|
+
setComment(comment) { return this.positionService.getGame().setComment(comment); }
|
|
8489
|
+
setHeader(key, value) { return this.positionService.getGame().setHeader(key, value); }
|
|
8490
|
+
validateFen(fen) { return this.positionService.getGame().validateFen(fen); }
|
|
8491
|
+
|
|
8492
|
+
// Implementazioni reali per highlight/dehighlight
|
|
8493
|
+
highlightSquare(square) {
|
|
8494
|
+
return this.boardService.highlight(square);
|
|
8495
|
+
}
|
|
8496
|
+
dehighlightSquare(square) {
|
|
8497
|
+
return this.boardService.dehighlight(square);
|
|
8498
|
+
}
|
|
8499
|
+
forceSync() { this._updateBoardPieces(true, true); this._updateBoardPieces(true, false); }
|
|
8500
|
+
|
|
8501
|
+
// Metodi mancanti che causano fallimenti nei test
|
|
8502
|
+
/**
|
|
8503
|
+
* Move a piece from one square to another
|
|
8504
|
+
* @param {string|Object} move - Move in format 'e2e4' or {from: 'e2', to: 'e4'}
|
|
8505
|
+
* @param {Object} [opts] - Options
|
|
8506
|
+
* @param {boolean} [opts.animate=true] - Whether to animate
|
|
8507
|
+
* @returns {boolean} True if move was successful
|
|
8508
|
+
*/
|
|
8509
|
+
movePiece(move, opts = {}) {
|
|
8510
|
+
const animate = opts.animate !== undefined ? opts.animate : true;
|
|
8511
|
+
|
|
8512
|
+
// --- API: accetta id/stringhe, ma converte subito in oggetti ---
|
|
8513
|
+
let fromSquareObj, toSquareObj, promotion;
|
|
8514
|
+
if (typeof move === 'string') {
|
|
8515
|
+
if (move.length === 4) {
|
|
8516
|
+
fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
|
|
8517
|
+
toSquareObj = this.boardService.getSquare(move.substring(2, 4));
|
|
8518
|
+
} else if (move.length === 5) {
|
|
8519
|
+
fromSquareObj = this.boardService.getSquare(move.substring(0, 2));
|
|
8520
|
+
toSquareObj = this.boardService.getSquare(move.substring(2, 4));
|
|
8521
|
+
promotion = move.substring(4, 5);
|
|
8522
|
+
} else {
|
|
8523
|
+
throw new Error(`Invalid move format: ${move}`);
|
|
8524
|
+
}
|
|
8525
|
+
} else if (typeof move === 'object' && move.from && move.to) {
|
|
8526
|
+
// Se sono id, converto in oggetti; se sono già oggetti, li uso direttamente
|
|
8527
|
+
fromSquareObj = typeof move.from === 'string' ? this.boardService.getSquare(move.from) : move.from;
|
|
8528
|
+
toSquareObj = typeof move.to === 'string' ? this.boardService.getSquare(move.to) : move.to;
|
|
8529
|
+
promotion = move.promotion;
|
|
8530
|
+
} else {
|
|
8531
|
+
throw new Error(`Invalid move: ${move}`);
|
|
8532
|
+
}
|
|
8533
|
+
|
|
8534
|
+
if (!fromSquareObj || !toSquareObj) {
|
|
8535
|
+
throw new Error(`Invalid squares: ${move.from || move.substring(0, 2)} or ${move.to || move.substring(2, 4)}`);
|
|
8536
|
+
}
|
|
8537
|
+
|
|
8538
|
+
// --- Internamente: lavora solo con oggetti ---
|
|
8539
|
+
const result = this._onMove(fromSquareObj, toSquareObj, promotion, animate);
|
|
8540
|
+
// Dopo ogni mossa, forza la sincronizzazione della board
|
|
8541
|
+
this._updateBoardPieces(true, false);
|
|
8542
|
+
return result;
|
|
8543
|
+
}
|
|
8544
|
+
|
|
8545
|
+
// Aliases for backward compatibility
|
|
8546
|
+
move(move, animate = true) {
|
|
8547
|
+
// On any new move, clear the redo stack
|
|
8548
|
+
this._undoneMoves = [];
|
|
8549
|
+
return this.movePiece(move, { animate });
|
|
8550
|
+
}
|
|
8551
|
+
get(square) { return this.getPiece(square); }
|
|
8552
|
+
piece(square) { return this.getPiece(square); }
|
|
8553
|
+
put(piece, square, opts = {}) { return this.putPiece(piece, square, opts); }
|
|
8554
|
+
remove(square, opts = {}) { return this.removePiece(square, opts); }
|
|
8555
|
+
load(position, opts = {}) { return this.setPosition(position, opts); }
|
|
8556
|
+
resize(size) { return this.resizeBoard(size); }
|
|
8557
|
+
start(opts = {}) { return this.reset(opts); }
|
|
8558
|
+
clearBoard(opts = {}) { return this.clear(opts); }
|
|
8559
|
+
};
|
|
8636
8560
|
|
|
8637
8561
|
/**
|
|
8638
8562
|
* Structured logging system for Chessboard.js
|
|
@@ -9090,7 +9014,6 @@ var Chessboard = (function (exports) {
|
|
|
9090
9014
|
hints: true,
|
|
9091
9015
|
clickable: true,
|
|
9092
9016
|
moveHighlight: true,
|
|
9093
|
-
animationStyle: 'simultaneous'
|
|
9094
9017
|
});
|
|
9095
9018
|
|
|
9096
9019
|
// Tournament template
|
|
@@ -9101,7 +9024,6 @@ var Chessboard = (function (exports) {
|
|
|
9101
9024
|
clickable: true,
|
|
9102
9025
|
moveHighlight: true,
|
|
9103
9026
|
onlyLegalMoves: true,
|
|
9104
|
-
animationStyle: 'sequential'
|
|
9105
9027
|
});
|
|
9106
9028
|
|
|
9107
9029
|
// Analysis template
|
|
@@ -9112,7 +9034,6 @@ var Chessboard = (function (exports) {
|
|
|
9112
9034
|
clickable: true,
|
|
9113
9035
|
moveHighlight: true,
|
|
9114
9036
|
mode: 'analysis',
|
|
9115
|
-
animationStyle: 'simultaneous'
|
|
9116
9037
|
});
|
|
9117
9038
|
|
|
9118
9039
|
// Puzzle template
|
|
@@ -9123,7 +9044,6 @@ var Chessboard = (function (exports) {
|
|
|
9123
9044
|
clickable: true,
|
|
9124
9045
|
moveHighlight: true,
|
|
9125
9046
|
onlyLegalMoves: true,
|
|
9126
|
-
animationStyle: 'sequential'
|
|
9127
9047
|
});
|
|
9128
9048
|
|
|
9129
9049
|
// Demo template
|
|
@@ -9133,7 +9053,6 @@ var Chessboard = (function (exports) {
|
|
|
9133
9053
|
hints: false,
|
|
9134
9054
|
clickable: false,
|
|
9135
9055
|
moveHighlight: true,
|
|
9136
|
-
animationStyle: 'simultaneous'
|
|
9137
9056
|
});
|
|
9138
9057
|
}
|
|
9139
9058
|
|
|
@@ -9179,7 +9098,7 @@ var Chessboard = (function (exports) {
|
|
|
9179
9098
|
this.validationService.validateConfig(finalConfig);
|
|
9180
9099
|
|
|
9181
9100
|
// Create chessboard instance
|
|
9182
|
-
const chessboard = new Chessboard(finalConfig);
|
|
9101
|
+
const chessboard = new Chessboard$1(finalConfig);
|
|
9183
9102
|
|
|
9184
9103
|
// Store instance for management
|
|
9185
9104
|
this.instances.set(containerId, {
|
|
@@ -9439,13 +9358,44 @@ var Chessboard = (function (exports) {
|
|
|
9439
9358
|
*/
|
|
9440
9359
|
|
|
9441
9360
|
|
|
9361
|
+
/**
|
|
9362
|
+
* Main Chessboard factory function for backward compatibility
|
|
9363
|
+
* Supports both legacy and modern calling conventions
|
|
9364
|
+
* @param {string|Object} containerElm - Container element ID or configuration object
|
|
9365
|
+
* @param {Object} [config={}] - Configuration options (when first param is string)
|
|
9366
|
+
* @returns {ChessboardClass} Chessboard instance
|
|
9367
|
+
*/
|
|
9368
|
+
function Chessboard(containerElm, config = {}) {
|
|
9369
|
+
const factoryLogger = logger.child('ChessboardFactory');
|
|
9370
|
+
|
|
9371
|
+
try {
|
|
9372
|
+
// If first parameter is an object, treat it as config
|
|
9373
|
+
if (typeof containerElm === 'object' && containerElm !== null) {
|
|
9374
|
+
factoryLogger.debug('Creating chessboard with config object');
|
|
9375
|
+
return new Chessboard$1(containerElm);
|
|
9376
|
+
}
|
|
9377
|
+
|
|
9378
|
+
// Otherwise, treat first parameter as element ID
|
|
9379
|
+
if (typeof containerElm === 'string') {
|
|
9380
|
+
factoryLogger.debug('Creating chessboard with element ID', { elementId: containerElm });
|
|
9381
|
+
const fullConfig = { ...config, id: containerElm };
|
|
9382
|
+
return new Chessboard$1(fullConfig);
|
|
9383
|
+
}
|
|
9384
|
+
|
|
9385
|
+
throw new Error('Invalid parameters: first parameter must be string or object');
|
|
9386
|
+
} catch (error) {
|
|
9387
|
+
factoryLogger.error('Failed to create chessboard instance', { error });
|
|
9388
|
+
throw error;
|
|
9389
|
+
}
|
|
9390
|
+
}
|
|
9391
|
+
|
|
9442
9392
|
/**
|
|
9443
9393
|
* Wrapper class that handles both calling conventions
|
|
9444
9394
|
* Provides enhanced error handling and logging
|
|
9445
9395
|
* @class
|
|
9446
9396
|
* @extends ChessboardClass
|
|
9447
9397
|
*/
|
|
9448
|
-
class ChessboardWrapper extends Chessboard {
|
|
9398
|
+
class ChessboardWrapper extends Chessboard$1 {
|
|
9449
9399
|
/**
|
|
9450
9400
|
* Creates a new ChessboardWrapper instance
|
|
9451
9401
|
* @param {string|Object} containerElm - Container element ID or configuration object
|
|
@@ -9474,32 +9424,53 @@ var Chessboard = (function (exports) {
|
|
|
9474
9424
|
}
|
|
9475
9425
|
}
|
|
9476
9426
|
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
Chessboard.Factory = ChessboardFactory;
|
|
9427
|
+
/**
|
|
9428
|
+
* Refactored Chessboard API - see Chessboard.js for full method docs
|
|
9429
|
+
* @typedef {import('./Chessboard.js').Chessboard} Chessboard
|
|
9430
|
+
*/
|
|
9482
9431
|
|
|
9483
|
-
//
|
|
9432
|
+
// --- STATIC/FACTORY METHODS ---
|
|
9433
|
+
/**
|
|
9434
|
+
* Create a new Chessboard instance
|
|
9435
|
+
* @param {string|Object} containerElm
|
|
9436
|
+
* @param {Object} [config]
|
|
9437
|
+
* @returns {Chessboard}
|
|
9438
|
+
*/
|
|
9484
9439
|
Chessboard.create = createChessboard;
|
|
9485
|
-
|
|
9440
|
+
/**
|
|
9441
|
+
* Create a Chessboard from a template
|
|
9442
|
+
* @param {string|Object} containerElm
|
|
9443
|
+
* @param {string} templateName
|
|
9444
|
+
* @param {Object} [config]
|
|
9445
|
+
* @returns {Chessboard}
|
|
9446
|
+
*/
|
|
9447
|
+
Chessboard.fromTemplate = createChessboardFromTemplate;
|
|
9486
9448
|
Chessboard.factory = chessboardFactory;
|
|
9487
9449
|
|
|
9488
|
-
//
|
|
9450
|
+
// --- INSTANCE MANAGEMENT ---
|
|
9489
9451
|
Chessboard.getInstance = (containerId) => chessboardFactory.getInstance(containerId);
|
|
9490
9452
|
Chessboard.destroyInstance = (containerId) => chessboardFactory.destroy(containerId);
|
|
9491
9453
|
Chessboard.destroyAll = () => chessboardFactory.destroyAll();
|
|
9492
9454
|
Chessboard.listInstances = () => chessboardFactory.listInstances();
|
|
9493
9455
|
|
|
9494
|
-
//
|
|
9456
|
+
// --- TEMPLATE MANAGEMENT ---
|
|
9495
9457
|
Chessboard.registerTemplate = (name, config) => chessboardFactory.registerTemplate(name, config);
|
|
9496
9458
|
Chessboard.removeTemplate = (name) => chessboardFactory.removeTemplate(name);
|
|
9497
9459
|
Chessboard.getTemplate = (name) => chessboardFactory.getTemplate(name);
|
|
9498
9460
|
Chessboard.listTemplates = () => chessboardFactory.listTemplates();
|
|
9499
9461
|
|
|
9500
|
-
//
|
|
9462
|
+
// --- STATS & DEBUG ---
|
|
9501
9463
|
Chessboard.getStats = () => chessboardFactory.getStats();
|
|
9502
9464
|
|
|
9465
|
+
// --- DEPRECATED/LEGACY ALIASES ---
|
|
9466
|
+
/**
|
|
9467
|
+
* @deprecated Use Chessboard.create instead
|
|
9468
|
+
*/
|
|
9469
|
+
Chessboard.Class = ChessboardWrapper;
|
|
9470
|
+
Chessboard.Chessboard = ChessboardWrapper;
|
|
9471
|
+
Chessboard.Config = ChessboardConfig;
|
|
9472
|
+
Chessboard.Factory = ChessboardFactory;
|
|
9473
|
+
|
|
9503
9474
|
/**
|
|
9504
9475
|
* Coordinate utilities for Chessboard.js
|
|
9505
9476
|
*/
|
|
@@ -9889,11 +9860,7 @@ var Chessboard = (function (exports) {
|
|
|
9889
9860
|
if (config.moveAnimation && !isValidEasing(config.moveAnimation)) {
|
|
9890
9861
|
errors.push('Invalid moveAnimation. Must be a valid easing function');
|
|
9891
9862
|
}
|
|
9892
|
-
|
|
9893
|
-
if (config.animationStyle && !['sequential', 'simultaneous'].includes(config.animationStyle)) {
|
|
9894
|
-
errors.push('Invalid animationStyle. Must be "sequential" or "simultaneous"');
|
|
9895
|
-
}
|
|
9896
|
-
|
|
9863
|
+
|
|
9897
9864
|
return {
|
|
9898
9865
|
success: errors.length === 0,
|
|
9899
9866
|
errors
|