מדיה ויקי:Gadget-pgnviewer.js: הבדלים בין גרסאות בדף
ניסוי |
מ 85 גרסאות של הדף wikipedia:he:מדיה_ויקי:Gadget-pgnviewer.js יובאו |
||
| (41 גרסאות ביניים של 3 משתמשים אינן מוצגות) | |||
| שורה 12: | שורה 12: | ||
"use strict"; | "use strict"; | ||
window.mw.hook( 'wikipage.content' ).add( function ( $content ) { | window.mw.hook( 'wikipage.content' ).add( function ( $content ) { | ||
var // const, really, but linter... | |||
WHITE = 'l', | WHITE = 'l', | ||
BLACK = 'd', | BLACK = 'd', | ||
acode = 'a'.charCodeAt(0), | acode = 'a'.charCodeAt(0), | ||
minBlockSize = 20, | minBlockSize = 20, | ||
maxBlockSize = 60, | maxBlockSize = 60, | ||
boardPadding = 20, | |||
wrapperSelector = 'div.pgn-source-wrapper', | wrapperSelector = 'div.pgn-source-wrapper', | ||
defaultBlockSize = 36, | |||
sides = ['n', 'e', 's', 'w'], // used for legends | |||
rowClassPrefix = 'pgn-prow-', | |||
fileClassPrefix = 'pgn-pfile-', | |||
allFilesAndColumnClasses = "01234567" | |||
.split("") | |||
.map( function( i ) { return rowClassPrefix + i + ' ' + fileClassPrefix + i; } ) | |||
.join(" "), | |||
hiddenPiece = 'pgn-piece-hidden', | |||
chessboardClass = 'pgn-board-img', | |||
resetGameButtonClass = 'pgn-button-tostart', | |||
ffButtonClass = 'pgn-button-toend', | |||
advanceButtonClass = 'pgn-button-advance', | |||
retreatButtonClass = 'pgn-button-retreat', | |||
playButtonClass = 'pgn-button-play', | |||
fasterButtonClass = 'pgn-button-faster', | |||
slowerButtonClass = 'pgn-button-slower', | |||
flipBoardButtonClass = 'pgn-button-flip', | |||
ccButtonClass = 'pgn-button-cc', | |||
mw = window.mw, | |||
$ = window.$, | |||
domParser = new DOMParser(), | |||
mobile = mw.config.get('skin') === 'minerva'; | |||
// some global, utility functions. | // some global, utility functions. | ||
function bindex(file, row) { return 8 * file + row; } | function bindex(file, row) { return row == undefined ? file : 8 * file + row; } | ||
function file(ind) { return Math.floor(ind / 8);} | function file(ind) { return Math.floor(ind / 8);} | ||
function row(ind) { return ind % 8; } | function row(ind) { return ind % 8; } | ||
| שורה 40: | שורה 52: | ||
function fileOfStr(file) { return file && file.charCodeAt(0) - acode;} | function fileOfStr(file) { return file && file.charCodeAt(0) - acode;} | ||
function rowOfStr(row) { return row && (row - 1);} | function rowOfStr(row) { return row && (row - 1);} | ||
function indexOfMoveNotation(notation) { | |||
var match = notation.match( /(\d+)([ld])/ ); | |||
if (match) return ( parseInt( match[1] ) - 1 ) * 2 + ( match[2] === 'l' ? 1 : 2 ); | |||
return 0; | |||
} | |||
function boardToFen(board) { | function boardToFen(board) { | ||
var res = [], | var res = [], | ||
| שורה 51: | שורה 68: | ||
} | } | ||
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( | // Classes | ||
var button = this | function Button( className, action, stateful ) { | ||
var button = this; | |||
$.extend( button, { | $.extend( button, { | ||
state: 0, | state: 0, | ||
| שורה 81: | שורה 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 ); | ||
if ( ( !stateful || state != oldState ) && typeof( action ) == 'function' ) | if ( ( !stateful || state != oldState ) && typeof( action ) == 'function' ) | ||
action( state ); | action( state ); | ||
}, | }, | ||
elem: $( '< | setVisible: function( visible ) { | ||
this.elem.toggle( !! visible ); | |||
}, | |||
elem: $( '<div>' ) | |||
.addClass( 'pgn-image-button pgn-image-button-off ' + className ) | |||
.click( function() { | .click( function() { | ||
button.setState( ! button.state ); | button.setState( ! button.state ); | ||
| שורה 101: | שורה 104: | ||
tabberDiv: null, | tabberDiv: null, | ||
blockSize: defaultBlockSize, | blockSize: defaultBlockSize, | ||
allGames: [], | allGames: [], | ||
currentGame: null, | currentGame: null, | ||
showDetails:false, | showDetails:false, | ||
timer: null, | timer: null, | ||
autoPlayDelay: 750, | |||
// actions | |||
faster: function() { | faster: function() { | ||
gameSet.autoPlayDelay -= 500; | gameSet.autoPlayDelay -= 500; | ||
if (gameSet.autoPlayDelay < | if (gameSet.autoPlayDelay < 500) gameSet.autoPlayDelay = 500; | ||
gameSet.reportDelay(); | gameSet.reportDelay(); | ||
}, | }, | ||
slower: function() { | slower: function() { | ||
gameSet.autoPlayDelay += 500; | gameSet.autoPlayDelay += 500; | ||
gameSet.reportDelay(); | gameSet.reportDelay(); | ||
}, | }, | ||
reportDelay: function() { | reportDelay: function() { | ||
gameSet.toggleAutoPlay(); // no param means keep state, but use new delay | |||
var message = gameSet.config.delay_msg | |||
? gameSet.config.delay_msg.replace('$sec$', 0.001 * gameSet.autoPlayDelay) | |||
: 0.001 * gameSet.autoPlayDelay; | |||
mw.notify( message, { tag: 'delay' } ); | |||
// eventurally use config and format better message. | |||
}, | }, | ||
top: function(row, l) { return (((this. | // 6 is half letter size (assuming font-size 0.875em) | ||
left: function(file, l) { return (((this. | 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.isFlipped ? 7 - file : file) + (l ? 0.5 : 0)) * this.blockSize + (l? boardPadding-6: boardPadding)) + 'px'; }, | |||
legendLocation: function(side, num) { | legendLocation: function(side, num) { | ||
switch (side) { | |||
switch (side) { | case 'n': return {top: 0, left: this.left(num, true)}; | ||
case 'n': | case 'e': return {top: this.top(num, true), left: this.blockSize * 8 + boardPadding + 5}; | ||
case 's': return {top: this.blockSize * 8 + 20, left: this.left(num, true)}; | |||
case 'e': | case 'w': return {top: this.top(num, true), left: 5}; | ||
case 's': | |||
case 'w': | |||
} | } | ||
}, | }, | ||
| שורה 149: | שורה 148: | ||
game.analyzePgn(); | game.analyzePgn(); | ||
this.currentGame = game; | this.currentGame = game; | ||
this.ccButton.setVisible( game.hasComments() ); | |||
game.show(); | game.show(); | ||
} | } | ||
}, | }, | ||
| שורה 157: | שורה 156: | ||
this.fenDiv.text( boardToFen(board) ); | this.fenDiv.text( boardToFen(board) ); | ||
}, | }, | ||
drawIfNeedRefresh: function( | drawIfNeedRefresh: function() { | ||
if (this.currentGame) | |||
if ( | |||
this.currentGame.drawBoard(); | this.currentGame.drawBoard(); | ||
}, | }, | ||
changeAppearance: function() { | changeAppearance: function() { | ||
this.currentGame.drawBoard(); | this.currentGame.drawBoard(); | ||
this.relocateLegends(); | this.relocateLegends(); | ||
| שורה 177: | שורה 173: | ||
.find( 'div' ).css( { height: mobile ? widthPxPlus : widthPx - 20 } ); | .find( 'div' ).css( { height: mobile ? widthPxPlus : widthPx - 20 } ); | ||
this.blockSize = width; | this.blockSize = width; | ||
this. | this.piecesDiv.css({width: widthPx, height: widthPx}); | ||
this | this.boardDiv.css({width: widthPxPlus, height: widthPxPlus}); | ||
this.changeAppearance(); | this.changeAppearance(); | ||
}, | }, | ||
hideComments: function( state ) { | hideComments: function( state ) { | ||
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 ) { | |||
if ( this. | clearInterval(this.timer); | ||
if (state == undefined) state = this.playing; | |||
this.playing = state; | |||
if ( state ) { | |||
this.currentGame.wrapAround(); | |||
this.timer = setInterval(function() { gameSet.currentGame.advance() }, gameSet.autoPlayDelay); | |||
} | } | ||
}, | }, | ||
stopAutoPlay: function() { | |||
gameSet.autoPlayButton.setState( false ); | |||
} | } | ||
}); | }); | ||
| שורה 218: | שורה 205: | ||
this.type = type; | this.type = type; | ||
this.color = color; | this.color = color; | ||
this. | this.avatar = $('<div>') | ||
.addClass('pgn-chessPiece pgn-ptype-color-' + type + color) | |||
.on('transitionstart', function(){ $(this).addClass('moving'); } ) //supposedly elevates z-index | |||
.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 | |||
} | } | ||
function Game(gameSet) { | |||
function Game( | |||
$.extend(this, { | $.extend(this, { | ||
board: [], | board: [], | ||
boards: [], | boards: [], | ||
pieces: [], | pieces: [], | ||
notations: [], | |||
moves: [], | moves: [], | ||
index: 0, | index: 0, | ||
piecesByTypeCol: {}, | piecesByTypeCol: {}, | ||
| שורה 327: | שורה 291: | ||
comments: [], | comments: [], | ||
analyzed: false, | analyzed: false, | ||
gs: gameSet}); | gs: gameSet}); | ||
} | } | ||
Game.prototype.moveLinkText = function( | Game.prototype.moveLinkText = function(notation, color) { | ||
var config = this.gs.config, | var config = this.gs.config, | ||
tpiece = config && config.translate && config.translate.piece, | |||
tfile = config && config.translate && config.translate.file, | |||
trow = config && config.translate && config.translate.row; | |||
if (tpiece) { | |||
tpiece = color == WHITE && tpiece.white | |||
if ( | || color == BLACK && tpiece.black | ||
|| tpiece; | |||
|| | |||
|| | |||
try { | try { | ||
regex = new RegExp('(' + Object.keys( | var regex = new RegExp('(' + Object.keys(tpiece).join("|") + ')', 'g'); | ||
notation = notation.replace(regex, function(c) { return | notation = notation.replace(regex, function(c) { return tpiece[c] || c; } ); | ||
} catch (e) { | } catch (e) { | ||
mw.log('bad config.translate.pieces'); | mw.log('bad config.translate.pieces'); | ||
| שורה 351: | שורה 312: | ||
} | } | ||
if ( | if (tfile) { | ||
try { | try { | ||
notation = notation.replace(/[abcdefgh]/g, function(c) { return | notation = notation.replace(/[abcdefgh]/g, function(c) { return tfile[c] || c; } ); | ||
} catch (e) { | } catch (e) { | ||
mw.log('bad config.translate.file'); | mw.log('bad config.translate.file'); | ||
| שורה 359: | שורה 320: | ||
} | } | ||
} | } | ||
if ( | if (trow) { | ||
try { | try { | ||
notation = notation.replace(/[12345678]/g, function(c) { return | 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'); | ||
| שורה 368: | שורה 329: | ||
} | } | ||
return | return $.trim(notation.replace(/-/g, '\u2011')) + ' '; | ||
} | } | ||
| שורה 374: | שורה 335: | ||
var desc = $.extend({}, this.descriptions), | var desc = $.extend({}, this.descriptions), | ||
rtl = desc['Direction'] == 'rtl', | rtl = desc['Direction'] == 'rtl', | ||
gs = this.gs; | |||
// cleanup from previous game. | // cleanup from previous game. | ||
gs.stopAutoPlay(); | |||
// setup descriptions | // setup descriptions | ||
delete desc['Direction']; | delete desc['Direction']; | ||
gs.descriptionsDiv | |||
$.each(desc, function(key, val) { | .empty() | ||
.css( { direction: rtl ? 'rtl' : 'ltr', textAlign: rtl ? 'right' : 'left' } ); | |||
$.each(desc, function(key, val) { | |||
var line = mw.html.escape(key + ': ' + val) + '<br />'; | |||
gs.descriptionsDiv.append(line); | |||
} ); | |||
// setup pgn section | // setup pgn section | ||
gs.pgnDiv.empty().append( this.notations ); | |||
// set the board. | // set the board. | ||
var hiddenAvatars = this.pieces.map( function(piece) { return piece.disappear(); } ); | |||
this. | this.gs.piecesDiv.empty().append( hiddenAvatars ); | ||
this. | |||
this.drawBoard(); | |||
} | } | ||
Game.prototype.done = function() { return this. | Game.prototype.done = function() { return this.boards.length - 1 <= this.index; } | ||
Game.prototype.pieceAt = function(file, row, piece) { | Game.prototype.pieceAt = function(file, row, piece) { | ||
| שורה 442: | שורה 381: | ||
piece.remove(); | piece.remove(); | ||
this.clearSquare(file, row); | this.clearSquare(file, row); | ||
} | } | ||
| שורה 448: | שורה 386: | ||
var file = file1, | var file = file1, | ||
row = row1, | row = row1, | ||
steps = 0, | |||
dfile = sign(file1, file2), | dfile = sign(file1, file2), | ||
drow = sign(row1, row2); | drow = sign(row1, row2); | ||
| שורה 458: | שורה 396: | ||
if (this.pieceAt(file, row)) | if (this.pieceAt(file, row)) | ||
return false; | return false; | ||
if ( | if (steps++ > 10) | ||
throw 'something is wrong in function roadIsClear.' + | throw 'something is wrong in function roadIsClear.' + | ||
' file=' + file + ' file1=' + file1 + ' file2=' + file2 + | ' file=' + file + ' file1=' + file1 + ' file2=' + file2 + | ||
| שורה 477: | שורה 415: | ||
byTypeCol.push(piece); | byTypeCol.push(piece); | ||
}; | }; | ||
Game.prototype.advance = function(delta) { | Game.prototype.advance = function(delta) { | ||
var m = this.index + (delta || 1); // no param means 1 forward. | var m = this.index + (delta || 1); // no param means 1 forward. | ||
if (0 <= m && m < this. | if (0 <= m && m < this.boards.length) { | ||
this. | this.drawBoard(m); | ||
} else this.gs.autoPlayButton.setState(false); | |||
} | } | ||
Game.prototype.showCurrentMoveLink = function() { | Game.prototype.showCurrentMoveLink = function() { | ||
var moveLink = this. | var moveLink = this.moves[this.index]; | ||
if (moveLink) { | if (moveLink) { | ||
moveLink.addClass('pgn-current-move').siblings().removeClass('pgn-current-move'); | moveLink.addClass('pgn-current-move').siblings().removeClass('pgn-current-move'); | ||
| שורה 508: | שורה 434: | ||
} | } | ||
Game.prototype. | Game.prototype.drawBoard = function(index) { | ||
if ( index == undefined) index = this.index; | |||
if ( | if ( index < 0 ) index += this.boards.length; | ||
this.index = index; | |||
var board = this.boards[index]; | |||
for (var i in this.pieces) this.pieces[i].disappear(); | |||
for (var i in board) if (board[i]) board[i].appear(file(i), row(i)); | |||
this.showCurrentMoveLink(); | this.showCurrentMoveLink(); | ||
this.gs.refreshFEN(); | this.gs.refreshFEN(); | ||
} | } | ||
Game.prototype.wrapAround = function() { | Game.prototype.wrapAround = function() { | ||
if (this.index >= this.boards.length - 1) | if (this.index >= this.boards.length - 1) | ||
this. | this.drawBoard(0); | ||
} | } | ||
| שורה 558: | שורה 475: | ||
this.clearPieceAt(file, row); | this.clearPieceAt(file, row); | ||
var newPiece = this.createPiece(type, piece.color, file, row); | var newPiece = this.createPiece(type, piece.color, file, row); | ||
} | } | ||
| שורה 579: | שורה 495: | ||
return moveStr; // end of game - white wins, black wins, draw, game halted/abandoned/unknown. | return moveStr; // end of game - white wins, black wins, draw, game halted/abandoned/unknown. | ||
var match = moveStr.match(/([RNBKQ])?([a-h])?([1-8])?(x)?([a-h])([1-8])(=[RNBKQ])?/); | var match = moveStr.match(/([RNBKQ])?([a-h])?([1-8])?(x)?([a-h])([1-8])(=[RNBKQ])?/); | ||
if (!match) | |||
if (!match) return false; | |||
var type = match[1] ? match[1].toLowerCase() : 'p', | var type = match[1] ? match[1].toLowerCase() : 'p', | ||
| שורה 599: | שורה 514: | ||
for (var i = 0; i < 2; i++) { | for (var i = 0; i < 2; i++) { | ||
var piece = thePiece[i]; | var piece = thePiece[i]; | ||
// lift the piece, check if the king is under threat | |||
delete this.board[bindex(piece.file, piece.row)]; | delete this.board[bindex(piece.file, piece.row)]; | ||
for (var j in this.board) { | for (var j in this.board) { | ||
var threat = this.board[j]; | var threat = this.board[j]; | ||
if (threat && threat.color != color && threat.canMoveTo(king.file, king.row, true)) { | if (threat && threat.color != color && threat.canMoveTo(king.file, king.row, true)) { // found that this piece can't move, so it's the other one... | ||
ok = true; | ok = true; | ||
thePiece = thePiece[1-i]; | thePiece = thePiece[1-i]; | ||
| שורה 608: | שורה 524: | ||
} | } | ||
} | } | ||
this. | // put the piece back in place | ||
this.board[bindex( piece.file, piece.row )] = piece; | |||
if (ok) | if (ok) | ||
break; | break; | ||
| שורה 627: | שורה 544: | ||
return moveStr; | return moveStr; | ||
} | } | ||
if ( str ) | Game.prototype.addComment = function(str) { | ||
if ( ! str ) return; | |||
this.notations.push( $( '<p>' ) | |||
.addClass( 'pgn-comment' ) | |||
.text( str.replace( /[{}()]/g, '' ) ) | |||
); | |||
} | } | ||
Game.prototype.addDescription = function(description) { | Game.prototype.addDescription = function(description) { | ||
description = $.trim(description); | description = $.trim(description); | ||
| שורה 639: | שורה 560: | ||
} | } | ||
Game.prototype.description = function( | Game.prototype.description = function() { | ||
var d = this.descriptions, | var d = this.descriptions, | ||
event = d['Event'] || d['אירוע'], | |||
round = d['Round'] || d['סיבוב'], | |||
white = d['White'] || d['לבן'], | |||
black = d['Black'] || d['שחור'], | |||
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) { | ||
| שורה 674: | שורה 595: | ||
match, | match, | ||
turn, | turn, | ||
moveNum = '', | moveNum = '', | ||
game = this, | |||
pgn = this.pgn; | pgn = this.pgn; | ||
| שורה 688: | שורה 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]; | ||
} | |||
function addMoveLink(str, isMove, color) { | |||
var notation = $( '<span>' ) | |||
.addClass( isMove ? 'pgn-movelink' : 'pgn-steplink' ) | |||
.text( game.moveLinkText( str, color ) ) | |||
game.notations.push( notation ); | |||
if ( isMove ) { | |||
game.boards.push( game.board.slice() ); | |||
game.moves.push( notation ); | |||
} else if ( game.moves.length == 0 ) | |||
game.moves.push( notation ); | |||
var index = game.boards.length - 1; | |||
notation.on( 'click', function() { | |||
game.gs.stopAutoPlay(); | |||
game.drawBoard(index); | |||
} ); | |||
} | } | ||
pgn = pgn.replace(/;(.*)\n/g, ' {$1} ').replace(/\s+/g, ' '); // replace to-end-of-line comments with block comments, remove newlines and noramlize spaces to 1 | pgn = pgn.replace(/;(.*)\n/g, ' {$1} ').replace(/\s+/g, ' '); // replace to-end-of-line comments with block comments, remove newlines and noramlize spaces to 1 | ||
this.populateBoard( this.descriptions.FEN || 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR' ); | this.populateBoard( this.descriptions.FEN || 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR' ); | ||
this.boards.push(this.board.slice()); | |||
var prevLen = -1; | var prevLen = -1; | ||
while (pgn.length) { | while (pgn.length) { | ||
if (prevLen == pgn.length) | if (prevLen == pgn.length) | ||
| שורה 704: | שורה 645: | ||
this.addComment( tryMatch( /^\s*\([^\)]*\)\s*/ ) ); | this.addComment( tryMatch( /^\s*\([^\)]*\)\s*/ ) ); | ||
if (match = tryMatch(/^\s*(\d+)\.+/)) { | if (match = tryMatch(/^\s*(\d+)\.+/)) { | ||
turn = /\. | turn = /\.{3}/.test(match) ? BLACK : WHITE; | ||
addMoveLink(match, false, turn); | |||
continue; | continue; | ||
} | } | ||
if (match = tryMatch(/^\s*[^ ]+ ?/)) { | if (match = tryMatch(/^\s*[^ ]+ ?/)) { | ||
this.createMove(turn, match); | this.createMove(turn, match); | ||
addMoveLink(match, true, turn); | |||
turn = BLACK; | turn = BLACK; | ||
} | } | ||
} | } | ||
this.index = this.descriptions.FirstMove | |||
? indexOfMoveNotation(this.descriptions.FirstMove) | |||
: this.descriptions.FEN ? 0 : this.boards.length - 1; | |||
} | } | ||
| שורה 736: | שורה 678: | ||
} | } | ||
} | } | ||
} | } | ||
function | |||
var | Game.prototype.hasComments = function() { | ||
var hasComment = function( n ){ return n.hasClass( 'pgn-comment' ); }; | |||
return !! this.notations.filter( hasComment ).length; | |||
} | } | ||
function buildBoardDiv(container, selector, gameSet, ind) { | function buildBoardDiv(container, selector, gameSet, ind) { | ||
var | var | ||
| שורה 773: | שורה 692: | ||
infoId = 'pgn-info-' + id, | infoId = 'pgn-info-' + id, | ||
fenId = 'pgn-fen-' + id, | fenId = 'pgn-fen-' + id, | ||
controlsDiv, | controlsDiv, | ||
gotoend = | createBotton = function( className, param, todraw ) { | ||
forward = | return new Button( className, | ||
backstep = | function() { | ||
gameSet.stopAutoPlay(); | |||
if ( todraw ) gameSet.currentGame.drawBoard( param ) | |||
else gameSet.currentGame.advance( param ); | |||
} ) | |||
}, | |||
gotoend = createBotton( ffButtonClass, -1, true ), | |||
forward = createBotton( advanceButtonClass, 1 ), | |||
backstep = createBotton( retreatButtonClass, -1 ), | |||
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', | ||
| שורה 785: | שורה 715: | ||
fen: 'FEN' }, | fen: 'FEN' }, | ||
config.tab_names ); | config.tab_names ); | ||
gameSet.autoPlayButton = autoplay; | |||
gameSet. | gameSet.ccButton = commentsToggle | ||
gameSet.descriptionsDiv = $('<div>', {'class': 'pgn-descriptions', id: infoId }) | |||
gameSet.fixedDelay = 'delay' in config; | gameSet.fixedDelay = 'delay' in config; | ||
if ( gameSet.fixedDelay ) gameSet.autoPlayDelay = config.delay; | if ( gameSet.fixedDelay ) gameSet.autoPlayDelay = Math.max(500, config.delay); | ||
if ( $( '#' + notationId ) ) notationId += '_1'; | if ( $( '#' + notationId ) ) notationId += '_1'; | ||
| שורה 808: | שורה 738: | ||
) | ) | ||
.append( gameSet.pgnDiv ) | .append( gameSet.pgnDiv ) | ||
.append( descriptionsDiv ) | .append( gameSet.descriptionsDiv ) | ||
.append( gameSet.fenDiv ) | .append( gameSet.fenDiv ) | ||
.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({ | .css( { textAlign: 'center' } ) // todo: move to css | ||
.append( | .append( buttons.map( function(x){ return x.elem; } ) ); | ||
gameSet.boardDiv = $('<div>' | gameSet.boardDiv = $('<div>') | ||
gameSet. | .addClass('pgn-board-div'); | ||
. | |||
gameSet.piecesDiv = $('<div>').css({position:'absolute', left: '20px', top: '20px' }) | |||
.addClass( chessboardClass ) | |||
.appendTo(gameSet.boardDiv); | .appendTo(gameSet.boardDiv); | ||
| שורה 858: | שורה 784: | ||
container | container | ||
.append( selector || '' ) | .append( selector || '' ) | ||
.append( | .append($('<div>') | ||
.append(gameSet.boardDiv) | .append(gameSet.boardDiv) | ||
.append( gameSet.tabberDiv ) | |||
.append(controlsDiv) | .append(controlsDiv) | ||
); | |||
} | } | ||
function doIt() { | function doIt() { | ||
$(wrapperSelector | $(wrapperSelector).each(function( ind ) { | ||
var | var | ||
wrapperDiv = $(this), | wrapperDiv = $(this), | ||
initial = wrapperDiv.text(), | |||
pgnSource = $('div.pgn-sourcegame', wrapperDiv), | pgnSource = $('div.pgn-sourcegame', wrapperDiv), | ||
selector, | selector, | ||
gameSet = new Gameset( wrapperDiv ); | gameSet = new Gameset( wrapperDiv ); | ||
try { | |||
if (pgnSource.length > 1) | |||
selector = $('<select>', { 'class': 'pgn-selector' } ) | |||
.change( function() { gameSet.selectGame(this.value); } ); | |||
buildBoardDiv(wrapperDiv, selector, gameSet, ind); | |||
var game, ind = 0; | |||
pgnSource.each(function() { | |||
try { | |||
game = new Game(gameSet); | |||
game.preAnalyzePgn($(this).text()); | |||
wrapperDiv.data({currentGame: game}); | |||
ind++; | |||
gameSet.allGames.push(game); | |||
if (selector) | |||
selector.append($('<option>', {value: gameSet.allGames.length - 1, text: game.description()}) | |||
.css('direction', game.descriptions['Direction'] || 'ltr') | |||
); | |||
} catch (e) { | |||
mw.log('exception in game ' + ind + ' problem is: "' + e + '"'); | |||
if (game && game.descriptions) | |||
for (var d in game.descriptions) | |||
mw.log(d + ':' + game.descriptions[d]); | |||
} | |||
}); | |||
gameSet.selectGame(0); | |||
gameSet.setWidth(); | |||
} catch(e) { | |||
console.error(e); | |||
console.error( 'exception analyzing game :', initial ); | |||
wrapperDiv.empty(); | |||
} | |||
}) | }) | ||
} | } | ||
if ( $(wrapperSelector, $content).length) { | |||
if ( mobile ) | |||
doIt(); | |||
else | |||
mw.loader.using( 'jquery.ui' ).done( doIt ); | |||
} | } | ||
}); | }); | ||