מדיה ויקי:Gadget-pgnviewer.js: הבדלים בין גרסאות בדף

יותר מדויק
מ 85 גרסאות של הדף wikipedia:he:מדיה_ויקי:Gadget-pgnviewer.js יובאו
 
(28 גרסאות ביניים של 2 משתמשים אינן מוצגות)
שורה 22: שורה 22:
defaultBlockSize = 36,
defaultBlockSize = 36,
sides = ['n', 'e', 's', 'w'], // used for legends
sides = ['n', 'e', 's', 'w'], // used for legends
rowClassPrefix = 'pgn-prow-',
fileClassPrefix = 'pgn-pfile-',
allFilesAndColumnClasses = "01234567"
allFilesAndColumnClasses = "01234567"
.split("")
.split("")
.map(function(i) { return 'pgn-prow-' + i + ' pgn-pfile-' + i;})
.map( function( i ) { return rowClassPrefix + i + ' ' + fileClassPrefix + i; } )
.join(" "),
.join(" "),
rowClassPrefix = 'pgn-prow-',
hiddenPiece = 'pgn-piece-hidden',
  fileClassPrefix = 'pgn-pfile-',
chessboardClass = 'pgn-board-img',
chessboardClass = 'pgn-board-img',
buttonClass = 'pgn-button',
resetGameButtonClass = 'pgn-button-tostart',
resetGameButtonClass = 'pgn-button-tostart',
ffButtonClass = 'pgn-button-toend',
ffButtonClass = 'pgn-button-toend',
שורה 35: שורה 35:
retreatButtonClass = 'pgn-button-retreat',
retreatButtonClass = 'pgn-button-retreat',
playButtonClass = 'pgn-button-play',
playButtonClass = 'pgn-button-play',
pauseButtonClass = 'pgn-button-pause',
fasterButtonClass = 'pgn-button-faster',
fasterButtonClass = 'pgn-button-faster',
slowerButtonClass = 'pgn-button-slower',
slowerButtonClass = 'pgn-button-slower',
flipBoardButtonClass = 'pgn-button-flip',
flipBoardButtonClass = 'pgn-button-flip',
ccButtonClass = 'pgn-button-cc';
ccButtonClass = 'pgn-button-cc',
 
 
var
mw = window.mw,
mw = window.mw,
$ = window.$,
$ = window.$,
domParser = new DOMParser(),
mobile = mw.config.get('skin') === 'minerva';
mobile = mw.config.get('skin') === 'minerva';
שורה 55: שורה 54:
function indexOfMoveNotation(notation) {
function indexOfMoveNotation(notation) {
var match = notation.match( /(\d+)([ld])/ );
var match = notation.match( /(\d+)([ld])/ );
if (match) return parseInt( match[1] ) + ( match[2] === 'l' ? 1 : 2 );
if (match) return ( parseInt( match[1] ) - 1 ) * 2 + ( match[2] === 'l' ? 1 : 2 );
return 0;
return 0;
}
}
שורה 70: שורה 69:
return res.reverse().join('/'); // fen begins with row 8 file a - go figure...
return res.reverse().join('/'); // fen begins with row 8 file a - go figure...
}
}
 
function Button( className, action, stateful) {
// Classes
var button = this,
function Button( className, action, stateful ) {
classArr = $.isArray( className )
var button = this;
? [ className[0], className[1] ]
: [ className, className ];
$.extend( button, {
$.extend( button, {
state: 0,
state: 0,
שורה 84: שורה 81:
if ( stateful )
if ( stateful )
this.elem
this.elem
.toggleClass( 'pgn-image-button-on', state )
.toggleClass( 'pgn-image-button-on', !!state )
.toggleClass( 'pgn-image-button-off', !state )
.toggleClass( 'pgn-image-button-off', !state );
.removeClass( classArr.join(" ") )
.addClass( classArr[state] );
if ( ( !stateful || state != oldState ) && typeof( action ) == 'function' )
if ( ( !stateful || state != oldState ) && typeof( action ) == 'function' )
action( state );
action( state );
},
setVisible: function( visible ) {
this.elem.toggle( !! visible );
},
},
elem: $( '<div>' )
elem: $( '<div>' )
.addClass( 'pgn-image-button pgn-image-button-off ' + classArr[0] )
.addClass( 'pgn-image-button pgn-image-button-off ' + className )
.click( function() {
.click( function() {
button.setState( ! button.state );
button.setState( ! button.state );
שורה 110: שורה 108:
showDetails:false,
showDetails:false,
timer: null,
timer: null,
autoPlayDelay: 750,
// actions
faster: function() {  
faster: function() {  
gameSet.autoPlayDelay -= 500;  
gameSet.autoPlayDelay -= 500;  
שורה 119: שורה 119:
gameSet.reportDelay();  
gameSet.reportDelay();  
},
},
flipButton: new Button( flipBoardButtonClass, function( state ) {
gameSet.doFlip( state );
}, true ),
slowerButton:  new Button( slowerButtonClass, function() { gameSet.slower(); } ),
autoPlayButton: new Button( [ playButtonClass, pauseButtonClass ], function( state ) { gameSet.toggleAutoPlay( state );
}, true ),
fasterButton:  new Button( fasterButtonClass, function() { gameSet.faster(); } ),
commentsToggleButton: new Button( ccButtonClass, function( state ) { gameSet.hideComments( state ) }, true ),
autoPlayDelay: 750,
reportDelay: function() {
reportDelay: function() {
gameSet.toggleAutoPlay(); // no param means keep state, but use new delay
gameSet.toggleAutoPlay(); // no param means keep state, but use new delay
שורה 137: שורה 128:
},
},
// 6 is half letter size (assuming font-size 0.875em)
// 6 is half letter size (assuming font-size 0.875em)
top: function(row, l) { return (((this.flipButton.state ? row : (7 - row)) + (l ? 0.3 : 0)) * this.blockSize + (l? boardPadding-6: boardPadding)) + 'px'; },
top: function(row, l) { return (((this.isFlipped ? row : (7 - row)) + (l ? 0.3 : 0)) * this.blockSize + (l? boardPadding-6: boardPadding)) + 'px'; },
left: function(file, l) { return (((this.flipButton.state ? 7 - file : file) + (l ? 0.5 : 0)) * this.blockSize + (l? boardPadding-6: boardPadding)) + 'px'; },
left: function(file, l) { return (((this.isFlipped ? 7 - file : file) + (l ? 0.5 : 0)) * this.blockSize + (l? boardPadding-6: boardPadding)) + 'px'; },
legendLocation: function(side, num) {
legendLocation: function(side, num) {
var n = 0.5 + num;
switch (side) {  
switch (side) {  
case 'n':
case 'n': return {top: 0, left: this.left(num, true)};
return {top: 0, left: this.left(num, true)};
case 'e': return {top: this.top(num, true), left: this.blockSize * 8 + boardPadding + 5};
case 'e':
case 's': return {top: this.blockSize * 8 + 20, left: this.left(num, true)};
return {top: this.top(num, true), left: this.blockSize * 8 + boardPadding + 5};
case 'w': return {top: this.top(num, true), left: 5};
case 's':
return {top: this.blockSize * 8 + 20, left: this.left(num, true)};
case 'w':
return {top: this.top(num, true), left: 5};
}
}
},
},
שורה 162: שורה 148:
game.analyzePgn();
game.analyzePgn();
this.currentGame = game;
this.currentGame = game;
this.ccButton.setVisible( game.hasComments() );
game.show();
game.show();
}
}
שורה 169: שורה 156:
this.fenDiv.text( boardToFen(board) );
this.fenDiv.text( boardToFen(board) );
},
},
drawIfNeedRefresh: function(again) {
drawIfNeedRefresh: function() {
if (this.currentGame)
if (this.currentGame)
this.currentGame.drawBoard();
this.currentGame.drawBoard();
שורה 193: שורה 180:
this.wrapperDiv.toggleClass( 'pgn-comments-hidden', state );
this.wrapperDiv.toggleClass( 'pgn-comments-hidden', state );
},
},
isFlipped: false,
doFlip: function( state ) {
doFlip: function( state ) {
this.isFlipped = state;
this.changeAppearance();
this.changeAppearance();
},
},
playing: false,
toggleAutoPlay: function( state ) {
toggleAutoPlay: function( state ) {
clearInterval(this.timer);
clearInterval(this.timer);
if (state == undefined) state = gameSet.autoPlayButton.state;
if (state == undefined) state = this.playing;
this.playing = state;
if ( state ) {
if ( state ) {
this.currentGame.wrapAround();
this.currentGame.wrapAround();
this.timer = setInterval(function() { gameSet.currentGame.advance() }, gameSet.autoPlayDelay);
this.timer = setInterval(function() { gameSet.currentGame.advance() }, gameSet.autoPlayDelay);
} else gameSet.autoPlayButton.setState( false );
}
},
stopAutoPlay: function() {
gameSet.autoPlayButton.setState( false );
}
}
});
});
}
}
   
   
function transitionstart() {
$(this).addClass('moving');
}
function transitionend() {
$(this).removeClass('moving');
}
function ChessPiece(type, color, game) {
function ChessPiece(type, color, game) {
this.game = game;
this.game = game;
שורה 221: שורה 207:
this.avatar = $('<div>')
this.avatar = $('<div>')
.addClass('pgn-chessPiece pgn-ptype-color-' + type + color)
.addClass('pgn-chessPiece pgn-ptype-color-' + type + color)
.on('transitionstart', transitionstart)
.on('transitionstart', function(){ $(this).addClass('moving'); } ) //supposedly elevates z-index
.on('transitionend', transitionend);
.on('transitionend', function(){ $(this).removeClass('moving'); } );
var piece = this;
$.extend( piece, {
appear: function(file, row) {
if (game.gs.isFlipped) {
file = 7 - file;
row = 7 - row;
}
this.avatar
.removeClass(hiddenPiece)
.removeClass(allFilesAndColumnClasses)  // remove them all
.addClass(rowClassPrefix + row) //
.addClass(fileClassPrefix + file);
},
disappear: function() { return this.avatar.addClass( hiddenPiece ); },
setSquare: function( file, row ) {
this.file = file;
this.row = row;
this.onBoard = true;
},
capture: function( file, row ) {
if (this.type == 'p' && !this.game.pieceAt(file, row))  // en passant
this.game.clearPieceAt(file, this.row);
else
this.game.clearPieceAt(file, row);
this.move(file, row);
},
move: function(file, row) {
// with chess960 castling, we sometimes have to test.
if ( this.game.pieceAt( this.file, this.row ) == this )
this.game.clearSquare(this.file, this.row);
this.game.pieceAt(file, row, this); // place it on the board)
},
pawnDirection: function() { return this.color == WHITE ? 1 : -1; },
toString: function() { return this.type + this.color; },
fen: function() { return this.color == WHITE ? this.type.toUpperCase() : this.type; },
pawnStart: function() { return this.color == WHITE ? 1 : 6; },
remove: function() { this.onBoard = false; },
canMoveTo: function(file, row, capture) {
if (!this.onBoard)
return false;
var rd = Math.abs(this.row - row), fd = Math.abs(this.file - file);
switch(this.type) {
case 'n':
return rd * fd == 2; // how nice that 2 is prime: its only factors are 2 and 1....
case 'p':
var dir = this.pawnDirection();
return (
((this.row == this.pawnStart() && row ==  this.row + dir * 2 && !fd && this.game.roadIsClear(this.file, file, this.row, row) && !capture)
|| (this.row + dir == row && fd == !!capture))); // advance 1, and either stay in file and no capture, or move exactly one
case 'k':
return (rd | fd) == 1; // we'll accept 1 and 1 or 1 and 0.
case 'q':
return (rd - fd) * rd * fd == 0 && this.game.roadIsClear(this.file, file, this.row, row); // same row, same file or same diagonal.
case 'r':
return rd * fd == 0 && this.game.roadIsClear(this.file, file, this.row, row);
case 'b':
return rd == fd && this.game.roadIsClear(this.file, file, this.row, row);
}
}, // function canMoveTo
matches: function(oldFile, oldRow, isCapture, file, row) {
if (typeof oldFile == 'number' && oldFile != this.file)
return false;
if (typeof oldRow  == 'number' && oldRow != this.row)
return false;
return this.canMoveTo(file, row, isCapture);
}
} ); // extend
}
}
   
   
ChessPiece.prototype.appear = function(file, row) {
if (this.game.gs.flipButton.state) {
file = 7 - file;
row = 7 - row;
}
this.avatar
.removeClass('pgn-piece-hidden ' + allFilesAndColumnClasses )
.addClass('pgn-piece-visible pgn-prow-' + row + ' pgn-pfile-' + file);
};
ChessPiece.prototype.disappear = function() {
return this.avatar
.removeClass('pgn-piece-visible')
.addClass('pgn-piece-hidden');
};
ChessPiece.prototype.setSquare = function(file, row) {
this.file = file;
this.row = row;
this.onBoard = true;
};
ChessPiece.prototype.capture = function(file, row) {
if (this.type == 'p' && !this.game.pieceAt(file, row))  // en passant
this.game.clearPieceAt(file, this.row);
else
this.game.clearPieceAt(file, row);
this.move(file, row);
};
ChessPiece.prototype.move = function(file, row) {
        if ( this.game.pieceAt( this.file, this.row ) == this ) // with chess960 castling, we sometimes have to test.
  this.game.clearSquare(this.file, this.row);
this.game.pieceAt(file, row, this); // place it on the board)
};
ChessPiece.prototype.pawnDirection = function() { return this.color == WHITE ? 1 : -1; };
ChessPiece.prototype.toString = function(fen) { return this.type + this.color; };
ChessPiece.prototype.fen = function() { return this.color == WHITE ? this.type.toUpperCase() : this.type; };
ChessPiece.prototype.pawnStart = function() { return this.color == WHITE ? 1 : 6; };
ChessPiece.prototype.remove = function() { this.onBoard = false; };
ChessPiece.prototype.canMoveTo = function(file, row, capture) {
if (!this.onBoard)
return false;
var rd = Math.abs(this.row - row), fd = Math.abs(this.file - file);
switch(this.type) {
case 'n':
return rd * fd == 2; // how nice that 2 is prime: its only factors are 2 and 1....
case 'p':
var dir = this.pawnDirection();
return (
((this.row == this.pawnStart() && row ==  this.row + dir * 2 && !fd && this.game.roadIsClear(this.file, file, this.row, row) && !capture)
|| (this.row + dir == row && fd == !!capture))); // advance 1, and either stay in file and no capture, or move exactly one
case 'k':
return (rd | fd) == 1; // we'll accept 1 and 1 or 1 and 0.
case 'q':
return (rd - fd) * rd * fd == 0 && this.game.roadIsClear(this.file, file, this.row, row); // same row, same file or same diagonal.
case 'r':
return rd * fd == 0 && this.game.roadIsClear(this.file, file, this.row, row);
case 'b':
return rd == fd && this.game.roadIsClear(this.file, file, this.row, row);
}
};
ChessPiece.prototype.matches = function(oldFile, oldRow, isCapture, file, row) {
if (typeof oldFile == 'number' && oldFile != this.file)
return false;
if (typeof oldRow  == 'number' && oldRow != this.row)
return false;
return this.canMoveTo(file, row, isCapture);
};
   
   
function Game(gameSet) {
function Game(gameSet) {
שורה 345: שורה 322:
if (trow) {
if (trow) {
try {
try {
notation = notation.replace(/[12345678]/g, function(c) { return trowrow[c] || c; } );
notation = notation.replace(/[12345678]/g, function(c) { return trow[c] || c; } );
} catch (e) {
} catch (e) {
mw.log('bad config.translate.row');
mw.log('bad config.translate.row');
שורה 352: שורה 329:
}
}


return notation.replace(/-/g, '\u2011').replace(/^\s*/, ' ').replace(/\s*$/, ' '); // replace hyphen with unbreakable, make sure before and after exactly one space
return $.trim(notation.replace(/-/g, '\u2011')) + ' ';
}
}
   
   
שורה 358: שורה 335:
var desc = $.extend({}, this.descriptions),
var desc = $.extend({}, this.descriptions),
rtl = desc['Direction'] == 'rtl',
rtl = desc['Direction'] == 'rtl',
num = '',
gs = this.gs;
gs = this.gs;
   
   
// cleanup from previous game.
// cleanup from previous game.
gs.toggleAutoPlay( false );
gs.stopAutoPlay();


// setup descriptions
// setup descriptions
שורה 369: שורה 345:
.empty()
.empty()
.css( { direction: rtl ? 'rtl' : 'ltr', textAlign: rtl ? 'right' : 'left' } );
.css( { direction: rtl ? 'rtl' : 'ltr', textAlign: rtl ? 'right' : 'left' } );
$.each(desc, function(key, val) { gs.descriptionsDiv.append(key + ': ' + val + '<br />'); });
$.each(desc, function(key, val) {  
var line = mw.html.escape(key + ': ' + val)  + '<br />';
gs.descriptionsDiv.append(line);  
} );
   
   
// setup pgn section
// setup pgn section
שורה 441: שורה 420:
if (0 <= m && m < this.boards.length) {
if (0 <= m && m < this.boards.length) {
this.drawBoard(m);
this.drawBoard(m);
} else this.gs.toggleAutoPlay(false);
} else this.gs.autoPlayButton.setState(false);
}
}
   
   
שורה 566: שורה 545:
}
}
Game.prototype.addComment = function(str) {
Game.prototype.addComment = function(str) {
if ( ! str ) return;
if ( ! str ) return;
this.notations.push( $( '<p>' )
this.notations.push( $( '<p>' )
שורה 573: שורה 552:
);
);
}
}
 
Game.prototype.addDescription = function(description) {
Game.prototype.addDescription = function(description) {
description = $.trim(description);
description = $.trim(description);
שורה 581: שורה 560:
}
}
   
   
Game.prototype.description = function(pgn) {
Game.prototype.description = function() {
var d = this.descriptions,
var d = this.descriptions,
round = d['Round'] ? ' (' + d['Round'] + ')' : '',
event = d['Event'] || d['אירוע'],
s = d['Name']  
round =  d['Round'] ||  d['סיבוב'],
|| d['שם']  
white = d['White'] || d['לבן'],
|| ( (d['Event'] || d['אירוע'] || '') + ': ' + (d['White'] || d['לבן'] || '') + ' - ' + (d['Black'] || d['שחור'] || '') + round);
black = d['Black'] || d['שחור'],
return s;
name = d['Name'] || d['שם'];
return name
|| ( event ? event + ': ' : '')
+ (white ? white : '')
+ (white || black ? ' – ' : '')
+ (black ? black : '')
+ (round ? ' (' + round + ')' : '');
}
}
 
Game.prototype.preAnalyzePgn = function(pgn) {
Game.prototype.preAnalyzePgn = function(pgn) {
function tryMatch(regex) {
function tryMatch(regex) {
שורה 624: שורה 609:
if (rmatch) {
if (rmatch) {
removeHead(rmatch[0]);
removeHead(rmatch[0]);
moveNum = rmatch[1] || moveNum;;
moveNum = rmatch[1] || moveNum;
}
}
return rmatch && rmatch[0];
return rmatch && rmatch[0];
שורה 643: שורה 628:
var index = game.boards.length - 1;
var index = game.boards.length - 1;
notation.on( 'click', function() { game.drawBoard(index); } );  
notation.on( 'click', function() {  
game.gs.stopAutoPlay();
game.drawBoard(index);  
} );  
}
}
          
          
שורה 690: שורה 678:
}
}
}
}
}  
}
function selectGame() {
var gameSet = $(this).data('gameSet');
Game.prototype.hasComments = function() {
gameSet.selectGame(this.value);
var hasComment = function( n ){ return n.hasClass( 'pgn-comment' ); };
return !! this.notations.filter( hasComment ).length;
}
}


שורה 704: שורה 693:
fenId = 'pgn-fen-' + id,
fenId = 'pgn-fen-' + id,
controlsDiv,
controlsDiv,
gotoend = new Button( ffButtonClass,  
createBotton = function( className, param, todraw ) {
function() { gameSet.currentGame.drawBoard( -1 ); } ),
return new Button( className,  
forward = new Button( advanceButtonClass,  
function() {
function() { gameSet.currentGame.advance( 1 ); } ),
gameSet.stopAutoPlay();
backstep = new Button( retreatButtonClass,  
if ( todraw ) gameSet.currentGame.drawBoard( param )
function() { gameSet.currentGame.advance( -1 ); } ),
else gameSet.currentGame.advance( param );
gotostart = new Button( resetGameButtonClass,  
} )
function() {  
},
gameSet.toggleAutoPlay( false );
gotoend = createBotton( ffButtonClass, -1, true ),
gameSet.currentGame.drawBoard( 0 );
forward = createBotton( advanceButtonClass,  1 ),
} ),
backstep = createBotton( retreatButtonClass, -1 ),
collapsibleDiv = $( '<div>' ), // residue. to be renamed or remove
gotostart = createBotton( resetGameButtonClass, 0, true ),
flip = new Button( flipBoardButtonClass, function( state ){ gameSet.doFlip( state ); }, true ),
slower = new Button( slowerButtonClass, function(){ gameSet.slower(); } ),
faster = new Button( fasterButtonClass, function(){ gameSet.faster(); } ),
autoplay = new Button( playButtonClass, function( state ){ gameSet.toggleAutoPlay( state ); }, true ),
commentsToggle = new Button( ccButtonClass, function( state ){ gameSet.hideComments( state ); }, true ),
tabnames = $.extend( {
tabnames = $.extend( {
notation: 'Game Notation',
notation: 'Game Notation',
שורה 721: שורה 715:
fen: 'FEN' },  
fen: 'FEN' },  
config.tab_names );
config.tab_names );
 
gameSet.autoPlayButton = autoplay;
gameSet.ccButton = commentsToggle
gameSet.descriptionsDiv = $('<div>', {'class': 'pgn-descriptions', id: infoId })
gameSet.descriptionsDiv = $('<div>', {'class': 'pgn-descriptions', id: infoId })
gameSet.fixedDelay = 'delay' in config;
gameSet.fixedDelay = 'delay' in config;
שורה 747: שורה 742:
.tabs();
.tabs();
}
}
var buttons = gameSet.fixedDelay
? [gotostart, backstep, autoplay, forward, gotoend, flip, commentsToggle]
: [gotostart, backstep, slower, autoplay, faster, forward, gotoend, flip, commentsToggle]
controlsDiv = $('<div>', {'class': 'pgn-controls'})
controlsDiv = $('<div>', {'class': 'pgn-controls'})
.css({textAlign: 'center'})
.css( { textAlign: 'center' } ) // todo: move to css
.append(gotostart.elem)
.append( buttons.map( function(x){ return x.elem; } ) );
.append(backstep.elem);
if ( ! gameSet.fixedDelay ) controlsDiv.append( gameSet.slowerButton.elem );
controlsDiv.append(gameSet.autoPlayButton.elem);
if ( ! gameSet.fixedDelay ) controlsDiv.append( gameSet.fasterButton.elem );
controlsDiv
.append(forward.elem)
.append(gotoend.elem)
.append(gameSet.flipButton.elem)
.append(gameSet.commentsToggleButton.elem)
;


gameSet.boardDiv = $('<div>')
gameSet.boardDiv = $('<div>')
שורה 795: שורה 784:
container
container
.append( selector || '' )
.append( selector || '' )
.append( collapsibleDiv
.append($('<div>')
.append(gameSet.boardDiv)
.append(gameSet.boardDiv)
.append( gameSet.tabberDiv )
.append( gameSet.tabberDiv )
.append(controlsDiv)
.append(controlsDiv)
);
);
}
}
   
   
function doIt() {
function doIt() {
$(wrapperSelector, $content).each(function( ind ) {
$(wrapperSelector).each(function( ind ) {
var
var
wrapperDiv = $(this),
wrapperDiv = $(this),
initial = wrapperDiv.text(),
initial = wrapperDiv.text(),
pgnSource = $('div.pgn-sourcegame', wrapperDiv),
pgnSource = $('div.pgn-sourcegame', wrapperDiv),
boardDiv,
selector,
selector,
gameSet = new Gameset( wrapperDiv );
gameSet = new Gameset( wrapperDiv );
try {
try {
if (pgnSource.length > 1)
if (pgnSource.length > 1)
selector = $('<select>', { 'class': 'pgn-selector' } ).data({gameSet: gameSet}).change(selectGame);
selector = $('<select>', { 'class': 'pgn-selector' } )
.change( function() { gameSet.selectGame(this.value); } );


buildBoardDiv(wrapperDiv, selector, gameSet, ind);
buildBoardDiv(wrapperDiv, selector, gameSet, ind);
שורה 849: שורה 838:
doIt();
doIt();
else
else
mw.loader.using( 'jquery.ui.tabs' ).done( doIt );
mw.loader.using( 'jquery.ui' ).done( doIt );
}
}
});
});