2018-10-28 17:59:15 +04:00
/ *
* pgn4web javascript chessboard
* copyright ( C ) 2009 - 2014 Paolo Casaschi
* see README file and http : //pgn4web.casaschi.net
* for credits , license and more details
* /
"use strict" ;
var pgn4web _version = '2.80' ;
var pgn4web _project _url = "http://pgn4web.casaschi.net" ;
var pgn4web _project _author = "Paolo Casaschi" ;
var pgn4web _project _email ; // preassigned in pgn4web-server-config.js
if ( typeof ( pgn4web _project _email ) == "undefined" ) { pgn4web _project _email = "pgn4web@casaschi.net" ; }
var helpWin ;
function displayHelp ( section ) {
if ( helpWin && ! helpWin . closed ) { helpWin . close ( ) ; }
helpWin = window . open ( detectHelpLocation ( ) + ( section ? "?" + section : "" ) , "pgn4web_help" ,
"resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no" ) ;
if ( helpWin && window . focus ) { helpWin . focus ( ) ; }
}
// empty event APIs to be redefined
function customFunctionOnPgnTextLoad ( ) { }
function customFunctionOnPgnGameLoad ( ) { }
function customFunctionOnMove ( ) { }
function customFunctionOnAlert ( msg ) { }
function customFunctionOnCheckLiveBroadcastStatus ( ) { }
// custom header tags APIs for customFunctionOnPgnGameLoad
function customPgnHeaderTag ( customTag , htmlElementId , gameNum ) {
var matches , tag = "" ;
customTag = customTag . replace ( /\W+/g , "" ) ;
if ( gameNum === undefined ) { gameNum = currentGame ; }
if ( ( pgnHeader [ gameNum ] ) && ( matches = pgnHeader [ gameNum ] . match ( '\\[\\s*' + customTag + '\\s*\"([^\"]+)\"\\s*\\]' ) ) ) { tag = matches [ 1 ] ; }
if ( htmlElementId ) {
var theObj = document . getElementById ( htmlElementId ) ;
if ( ( theObj ) && ( typeof ( theObj . innerHTML ) == "string" ) ) { theObj . innerHTML = tag ; }
}
return tag ;
}
// custom comment tags API for customFunctionOnMove
function customPgnCommentTag ( customTag , htmlElementId , plyNum , varId ) {
var matches , tag = "" , theObj ;
customTag = customTag . replace ( /\W+/g , "" ) ;
if ( typeof ( varId ) == "undefined" ) { varId = 0 ; }
if ( typeof ( plyNum ) == "undefined" ) { plyNum = CurrentPly ; }
if ( ( MoveCommentsVar [ varId ] [ plyNum ] ) && ( matches = MoveCommentsVar [ varId ] [ plyNum ] . match ( '\\[%' + customTag + '\\s+((?:,?(?:"[^"]*"|[^,\\]]*))*)\\s*\\]' ) ) ) { tag = matches [ 1 ] . replace ( /\s+$/ , "" ) ; }
if ( ( htmlElementId ) && ( theObj = document . getElementById ( htmlElementId ) ) && ( typeof ( theObj . innerHTML ) == "string" ) ) { theObj . innerHTML = tag ; }
return tag ;
}
function simpleAddEvent ( obj , evt , cbk ) {
if ( obj . addEventListener ) { obj . addEventListener ( evt , cbk , false ) ; }
else if ( obj . attachEvent ) { obj . attachEvent ( "on" + evt , cbk ) ; } // IE8-
}
simpleAddEvent ( document , "keydown" , pgn4web _handleKey _event ) ;
simpleAddEvent ( window , "load" , pgn4web _onload _event ) ;
function pgn4web _onload _event ( e ) {
pgn4web _onload ( e ) ;
}
function pgn4web _onload ( e ) {
start _pgn4web ( ) ;
}
function start _pgn4web ( ) {
// keep startup logs at the very first run, otherwise reset
if ( alertFirstResetLoadingPgn ) { alertFirstResetLoadingPgn = false ; }
else { resetAlert ( ) ; }
InitImages ( ) ;
createBoard ( ) ;
if ( LiveBroadcastDelay > 0 ) { restartLiveBroadcastTimeout ( ) ; }
pgn4web _initTouchEvents ( ) ;
}
var alertLog ;
var alertLast ;
var alertNum ;
var alertNumSinceReset ;
var fatalErrorNumSinceReset ;
var alertPromptInterval = null ;
var alertPromptOn = false ;
var alertFirstResetLoadingPgn = true ;
resetAlert ( ) ;
function resetAlert ( ) {
alertLog = new Array ( 5 ) ;
alertLast = alertLog . length - 1 ;
alertNum = alertNumSinceReset = fatalErrorNumSinceReset = 0 ;
stopAlertPrompt ( ) ;
if ( ! alertFirstResetLoadingPgn ) {
if ( boardIsDefault ( debugShortcutSquare ) ) { boardShortcut ( debugShortcutSquare , "pgn4web v" + pgn4web _version + " debug info" , null , true ) ; }
}
}
function myAlert ( msg , fatalError , doNotPrompt ) {
alertNum ++ ;
alertNumSinceReset ++ ;
if ( fatalError ) { fatalErrorNumSinceReset ++ ; }
alertLast = ( alertLast + 1 ) % alertLog . length ;
alertLog [ alertLast ] = msg + "\n" + ( new Date ( ) ) . toLocaleString ( ) ;
if ( boardIsDefault ( debugShortcutSquare ) ) { boardShortcut ( debugShortcutSquare , "pgn4web v" + pgn4web _version + " debug info, " + alertNum + " alert" + ( alertNum > 1 ? "s" : "" ) , null , true ) ; }
if ( ( ! doNotPrompt ) && ( ( LiveBroadcastDelay === 0 ) || ( LiveBroadcastAlert === true ) ) && ( boardIsDefault ( debugShortcutSquare ) ) ) { startAlertPrompt ( ) ; }
customFunctionOnAlert ( msg ) ;
}
function startAlertPrompt ( ) {
if ( alertPromptOn ) { return ; } // dont start flashing twice
if ( alertPromptInterval ) { clearTimeout ( alertPromptInterval ) ; }
alertPromptInterval = setTimeout ( "alertPromptTick(true);" , 500 ) ;
}
function stopAlertPrompt ( ) {
if ( alertPromptInterval ) {
clearTimeout ( alertPromptInterval ) ;
alertPromptInterval = null ;
}
if ( alertPromptOn ) { alertPromptTick ( false ) ; }
}
function alertPromptTick ( restart ) {
if ( alertPromptInterval ) {
clearTimeout ( alertPromptInterval ) ;
alertPromptInterval = null ;
}
var colRow = colRowFromSquare ( debugShortcutSquare ) ;
if ( ! colRow ) { return ; }
var alertPromptDelay = 1500 ; // for alerts before the board is printed
var theObj = document . getElementById ( 'tcol' + colRow . col + 'trow' + colRow . row ) ;
if ( theObj ) {
if ( alertPromptOn ) {
if ( ( highlightOption ) &&
( ( colFromHighlighted === 0 && rowFromHighlighted === 7 ) ||
( colToHighlighted === 0 && rowToHighlighted === 7 ) ) ) {
theObj . className = 'highlightWhiteSquare' ;
} else { theObj . className = 'whiteSquare' ; }
} else { theObj . className = 'blackSquare' ; }
alertPromptOn = ! alertPromptOn ;
if ( alertPromptOn ) { alertPromptDelay = 500 ; }
else { alertPromptDelay = 3000 ; }
}
if ( restart ) { alertPromptInterval = setTimeout ( "alertPromptTick(true);" , alertPromptDelay ) ; }
}
function stopEvProp ( e ) {
e . cancelBubble = true ;
if ( e . stopPropagation ) { e . stopPropagation ( ) ; }
if ( e . preventDefault ) { e . preventDefault ( ) ; }
return false ;
}
// for onFocus/onBlur textbox events, allowing text typing
var shortcutKeysWereEnabled = false ;
function disableShortcutKeysAndStoreStatus ( ) {
if ( ( shortcutKeysWereEnabled = shortcutKeysEnabled ) === true ) {
SetShortcutKeysEnabled ( false ) ;
}
}
function restoreShortcutKeysStatus ( ) {
if ( shortcutKeysWereEnabled === true ) { SetShortcutKeysEnabled ( true ) ; }
shortcutKeysWereEnabled = false ;
}
function customShortcutKey _Shift _0 ( ) { }
function customShortcutKey _Shift _1 ( ) { }
function customShortcutKey _Shift _2 ( ) { }
function customShortcutKey _Shift _3 ( ) { }
function customShortcutKey _Shift _4 ( ) { }
function customShortcutKey _Shift _5 ( ) { }
function customShortcutKey _Shift _6 ( ) { }
function customShortcutKey _Shift _7 ( ) { }
function customShortcutKey _Shift _8 ( ) { }
function customShortcutKey _Shift _9 ( ) { }
function pgn4web _handleKey _event ( e ) {
pgn4web _handleKey ( e ) ;
}
var shortcutKeysEnabled = false ;
function pgn4web _handleKey ( e ) {
var keycode , oldPly , oldVar , colRow , colRowList ;
if ( ! e ) { e = window . event ; }
keycode = e . keyCode ;
if ( e . altKey || e . ctrlKey || e . metaKey ) { return true ; }
// shift-escape always enabled: toggle shortcut keys
if ( ! shortcutKeysEnabled && ! ( keycode == 27 && e . shiftKey ) ) { return true ; }
switch ( keycode ) {
case 8 : // backspace
case 9 : // tab
case 16 : // shift
case 17 : // ctrl
case 18 : // alt
case 32 : // space
case 33 : // page-up
case 34 : // page-down
case 35 : // end
case 36 : // home
case 92 : // super
case 93 : // menu
case 188 : // comma
return true ;
case 27 : // escape
if ( e . shiftKey ) { interactivelyToggleShortcutKeys ( ) ; }
else { displayHelp ( ) ; }
break ;
case 189 : // dash
if ( colRowList = prompt ( "Enter shortcut square coordinates to click:" , "" ) ) {
colRowList = colRowList . toUpperCase ( ) . replace ( /[^A-Z0-9]/g , "" ) ;
while ( colRow = colRowFromSquare ( colRowList ) ) {
boardOnClick [ colRow . col ] [ colRow . row ] ( { "id" : "img_tcol" + colRow . col + "trow" + colRow . row } , e ) ;
colRowList = colRowList . substr ( 2 ) ;
}
}
break ;
case 90 : // z
if ( e . shiftKey ) { window . open ( pgn4web _project _url ) ; }
else { displayDebugInfo ( ) ; }
break ;
case 37 : // left-arrow
case 74 : // j
backButton ( e ) ;
break ;
case 38 : // up-arrow
case 72 : // h
startButton ( e ) ;
break ;
case 39 : // right-arrow
case 75 : // k
forwardButton ( e ) ;
break ;
case 40 : // down-arrow
case 76 : // l
endButton ( e ) ;
break ;
case 73 : // i
MoveToPrevComment ( e . shiftKey ) ;
break ;
case 79 : // o
MoveToNextComment ( e . shiftKey ) ;
break ;
case 190 : // dot
if ( e . shiftKey ) { goToFirstChild ( ) ; }
else { goToNextVariationSibling ( ) ; }
break ;
case 85 : // u
if ( e . shiftKey ) { undoStackRedo ( ) ; }
else { undoStackUndo ( ) ; }
break ;
case 45 : // insert
undoStackRedo ( ) ;
break ;
case 46 : // delete
undoStackUndo ( ) ;
break ;
case 83 : // s
if ( e . shiftKey ) { searchPgnGame ( "" ) ; }
else { searchPgnGamePrompt ( ) ; }
break ;
case 13 : // enter
if ( e . shiftKey ) { searchPgnGame ( lastSearchPgnExpression , true ) ; }
else { searchPgnGame ( lastSearchPgnExpression ) ; }
break ;
case 68 : // d
if ( e . shiftKey ) { displayFenData ( ) ; }
else { displayPgnData ( true ) ; }
break ;
case 187 : // equal
SwitchAutoPlay ( ) ;
break ;
case 65 : // a
GoToMove ( CurrentPly + 1 ) ;
SetAutoPlay ( true ) ;
break ;
case 48 : // 0
if ( e . shiftKey ) { customShortcutKey _Shift _0 ( ) ; }
else { SetAutoPlay ( false ) ; }
break ;
case 49 : // 1
if ( e . shiftKey ) { customShortcutKey _Shift _1 ( ) ; }
else { SetAutoplayDelayAndStart ( 1 * 1000 ) ; }
break ;
case 50 : // 2
if ( e . shiftKey ) { customShortcutKey _Shift _2 ( ) ; }
else { SetAutoplayDelayAndStart ( 2 * 1000 ) ; }
break ;
case 51 : // 3
if ( e . shiftKey ) { customShortcutKey _Shift _3 ( ) ; }
else { SetAutoplayDelayAndStart ( 3 * 1000 ) ; }
break ;
case 52 : // 4
if ( e . shiftKey ) { customShortcutKey _Shift _4 ( ) ; }
else { SetAutoplayDelayAndStart ( 4 * 1000 ) ; }
break ;
case 53 : // 5
if ( e . shiftKey ) { customShortcutKey _Shift _5 ( ) ; }
else { SetAutoplayDelayAndStart ( 5 * 1000 ) ; }
break ;
case 54 : // 6
if ( e . shiftKey ) { customShortcutKey _Shift _6 ( ) ; }
else { SetAutoplayDelayAndStart ( 6 * 1000 ) ; }
break ;
case 55 : // 7
if ( e . shiftKey ) { customShortcutKey _Shift _7 ( ) ; }
else { SetAutoplayDelayAndStart ( 7 * 1000 ) ; }
break ;
case 56 : // 8
if ( e . shiftKey ) { customShortcutKey _Shift _8 ( ) ; }
else { SetAutoplayDelayAndStart ( 8 * 1000 ) ; }
break ;
case 57 : // 9
if ( e . shiftKey ) { customShortcutKey _Shift _9 ( ) ; }
else { setCustomAutoplayDelay ( ) ; }
break ;
case 81 : // q
SetAutoplayDelayAndStart ( 10 * 1000 ) ;
break ;
case 87 : // w
SetAutoplayDelayAndStart ( 20 * 1000 ) ;
break ;
case 69 : // e
SetAutoplayDelayAndStart ( 30 * 1000 ) ;
break ;
case 82 : // r
pauseLiveBroadcast ( ) ;
break ;
case 84 : // t
if ( e . shiftKey ) { LiveBroadcastSteppingMode = ! LiveBroadcastSteppingMode ; }
else { refreshPgnSource ( ) ; }
break ;
case 89 : // y
restartLiveBroadcast ( ) ;
break ;
case 70 : // f
if ( ! e . shiftKey || IsRotated ) { FlipBoard ( ) ; }
break ;
case 71 : // g
SetHighlight ( ! highlightOption ) ;
break ;
case 88 : // x
randomGameRandomPly ( ) ;
break ;
case 67 : // c
if ( numberOfGames > 1 ) { Init ( Math . floor ( Math . random ( ) * numberOfGames ) ) ; }
break ;
case 86 : // v
if ( numberOfGames > 1 ) { Init ( 0 ) ; }
break ;
case 66 : // b
Init ( currentGame - 1 ) ;
break ;
case 78 : // n
Init ( currentGame + 1 ) ;
break ;
case 77 : // m
if ( numberOfGames > 1 ) { Init ( numberOfGames - 1 ) ; }
break ;
case 80 : // p
if ( e . shiftKey ) { SetCommentsOnSeparateLines ( ! commentsOnSeparateLines ) ; }
else { SetCommentsIntoMoveText ( ! commentsIntoMoveText ) ; }
oldPly = CurrentPly ;
oldVar = CurrentVar ;
Init ( ) ;
GoToMove ( oldPly , oldVar ) ;
break ;
default :
return true ;
}
return stopEvProp ( e ) ;
}
var boardOnClick = new Array ( 8 ) ;
var boardTitle = new Array ( 8 ) ;
var boardDefault = new Array ( 8 ) ;
for ( var col = 0 ; col < 8 ; col ++ ) {
boardOnClick [ col ] = new Array ( 8 ) ;
boardTitle [ col ] = new Array ( 8 ) ;
boardDefault [ col ] = new Array ( 8 ) ;
}
clearShortcutSquares ( "ABCDEFGH" , "12345678" ) ;
function colRowFromSquare ( square ) {
if ( ( typeof ( square ) != "string" ) || ( ! square ) ) { return null ; }
var col = square . charCodeAt ( 0 ) - 65 ; // 65="A"
if ( ( col < 0 ) || ( col > 7 ) ) { return null ; }
var row = 56 - square . charCodeAt ( 1 ) ; // 56="8"
if ( ( row < 0 ) || ( row > 7 ) ) { return null ; }
return { "col" : col , "row" : row } ;
}
function clearShortcutSquares ( cols , rows ) {
if ( ( typeof ( cols ) != "string" ) || ( typeof ( rows ) != "string" ) ) { return ; }
for ( var c = 0 ; c < cols . length ; c ++ ) { for ( var r = 0 ; r < rows . length ; r ++ ) {
boardShortcut ( cols . charAt ( c ) . toUpperCase ( ) + rows . charAt ( r ) , "" , function ( t , e ) { } ) ;
} }
}
function boardIsDefault ( square ) {
var colRow = colRowFromSquare ( square ) ;
if ( ! colRow ) { return false ; }
return boardDefault [ colRow . col ] [ colRow . row ] ;
}
function boardShortcut ( square , title , functionPointer , defaultSetting ) {
var theObj , colRow = colRowFromSquare ( square ) ;
if ( ! colRow ) { return ; }
else { var col = colRow . col ; var row = colRow . row ; }
boardTitle [ col ] [ row ] = title ;
if ( functionPointer ) { boardOnClick [ col ] [ row ] = functionPointer ; }
boardDefault [ col ] [ row ] = defaultSetting ? true : false ;
if ( theObj = document . getElementById ( 'img_tcol' + col + 'trow' + row ) ) {
if ( IsRotated ) { square = String . fromCharCode ( 72 - col , 49 + row ) ; }
theObj . title = square + ( boardTitle [ col ] [ row ] ? ': ' + boardTitle [ col ] [ row ] : '' ) ;
}
}
// boardShortcut() always assumes 'square' defined as with white on bottom
var debugShortcutSquare = "A8" ;
boardShortcut ( "A8" , "pgn4web v" + pgn4web _version + " debug info" , function ( t , e ) { displayDebugInfo ( ) ; } , true ) ;
boardShortcut ( "B8" , "show this position FEN string" , function ( t , e ) { displayFenData ( ) ; } , true ) ;
boardShortcut ( "C8" , "show this game PGN source data" , function ( t , e ) { if ( e . shiftKey ) { savePgnData ( true ) ; } else { displayPgnData ( true ) ; } } , true ) ;
boardShortcut ( "D8" , "show full PGN source data" , function ( t , e ) { if ( e . shiftKey ) { savePgnData ( ) ; } else { displayPgnData ( ) ; } } , true ) ;
boardShortcut ( "E8" , "search help" , function ( t , e ) { displayHelp ( "search_tool" ) ; } , true ) ;
boardShortcut ( "F8" , "shortcut keys help" , function ( t , e ) { displayHelp ( "shortcut_keys" ) ; } , true ) ;
boardShortcut ( "G8" , "shortcut squares help" , function ( t , e ) { displayHelp ( e . shiftKey ? "informant_symbols" : "shortcut_squares" ) ; } , true ) ;
boardShortcut ( "H8" , "pgn4web help" , function ( t , e ) { displayHelp ( e . shiftKey ? "credits_and_license" : "" ) ; } , true ) ;
boardShortcut ( "A7" , "pgn4web website" , function ( t , e ) { window . open ( pgn4web _project _url ) ; } , true ) ;
boardShortcut ( "B7" , "undo last chessboard position update" , function ( t , e ) { undoStackUndo ( ) ; } , true ) ;
boardShortcut ( "C7" , "redo last undo" , function ( t , e ) { undoStackRedo ( ) ; } , true ) ;
boardShortcut ( "D7" , "toggle highlight last move" , function ( t , e ) { SetHighlight ( ! highlightOption ) ; } , true ) ;
boardShortcut ( "E7" , "flip board" , function ( t , e ) { if ( ! e . shiftKey || IsRotated ) { FlipBoard ( ) ; } } , true ) ;
boardShortcut ( "F7" , "toggle show comments in game text" , function ( t , e ) { if ( e . shiftKey ) { SetCommentsOnSeparateLines ( ! commentsOnSeparateLines ) ; } else { SetCommentsIntoMoveText ( ! commentsIntoMoveText ) ; } var oldPly = CurrentPly ; var oldVar = CurrentVar ; Init ( ) ; GoToMove ( oldPly , oldVar ) ; } , true ) ;
boardShortcut ( "G7" , "" , function ( t , e ) { } , true ) ; // see setG7A6B6H6...
boardShortcut ( "H7" , "toggle enabling shortcut keys" , function ( t , e ) { interactivelyToggleShortcutKeys ( ) ; } , true ) ;
boardShortcut ( "A6" , "" , function ( t , e ) { } , true ) ; // see setG7A6B6H6...
boardShortcut ( "B6" , "" , function ( t , e ) { } , true ) ; // see setG7A6B6H6...
boardShortcut ( "C6" , "search previous finished game" , function ( t , e ) { searchPgnGame ( '\\[\\s*Result\\s*"(?!\\*"\\s*\\])' , true ) ; } ) ;
boardShortcut ( "D6" , "search previous unfinished game" , function ( t , e ) { searchPgnGame ( '\\[\\s*Result\\s*"\\*"\\s*\\]' , true ) ; } ) ;
boardShortcut ( "E6" , "search next unfinished game" , function ( t , e ) { searchPgnGame ( '\\[\\s*Result\\s*"\\*"\\s*\\]' , false ) ; } , true ) ;
boardShortcut ( "F6" , "search next finished game" , function ( t , e ) { searchPgnGame ( '\\[\\s*Result\\s*"(?!\\*"\\s*\\])' , false ) ; } , true ) ;
boardShortcut ( "G6" , "" , function ( t , e ) { } , true ) ;
boardShortcut ( "H6" , "" , function ( t , e ) { } , true ) ; // see setG7A6B6H6...
boardShortcut ( "A5" , "repeat last search backward" , function ( t , e ) { searchPgnGame ( lastSearchPgnExpression , true ) ; } , true ) ;
boardShortcut ( "B5" , "search prompt" , function ( t , e ) { if ( e . shiftKey ) { searchPgnGame ( "" ) ; } else { searchPgnGamePrompt ( ) ; } } , true ) ;
boardShortcut ( "C5" , "repeat last search" , function ( t , e ) { searchPgnGame ( lastSearchPgnExpression ) ; } , true ) ;
boardShortcut ( "D5" , "search previous win result" , function ( t , e ) { searchPgnGame ( '\\[\\s*Result\\s*"(1-0|0-1)"\\s*\\]' , true ) ; } , true ) ;
boardShortcut ( "E5" , "search next win result" , function ( t , e ) { searchPgnGame ( '\\[\\s*Result\\s*"(1-0|0-1)"\\s*\\]' , false ) ; } , true ) ;
boardShortcut ( "F5" , "" , function ( t , e ) { } , true ) ;
boardShortcut ( "G5" , "" , function ( t , e ) { } , true ) ;
boardShortcut ( "H5" , "" , function ( t , e ) { } , true ) ;
boardShortcut ( "A4" , "search previous event" , function ( t , e ) { searchPgnGame ( '\\[\\s*Event\\s*"(?!' + fixRegExp ( gameEvent [ currentGame ] ) + '"\\s*\\])' , true ) ; } , true ) ;
boardShortcut ( "B4" , "search previous round of same event" , function ( t , e ) { searchPgnGame ( '\\[\\s*Event\\s*"' + fixRegExp ( gameEvent [ currentGame ] ) + '"\\s*\\].*\\[\\s*Round\\s*"(?!' + fixRegExp ( gameRound [ currentGame ] ) + '"\\s*\\])|\\[\\s*Round\\s*"(?!' + fixRegExp ( gameRound [ currentGame ] ) + '"\\s*\\]).*\\[\\s*Event\\s*"' + fixRegExp ( gameEvent [ currentGame ] ) + '"\\s*\\]' , true ) ; } , true ) ;
boardShortcut ( "C4" , "search previous game of same black player" , function ( t , e ) { searchPgnGame ( '\\[\\s*' + ( e . shiftKey ? 'White' : 'Black' ) + '\\s*"' + fixRegExp ( gameBlack [ currentGame ] ) + '"\\s*\\]' , true ) ; } , true ) ;
boardShortcut ( "D4" , "search previous game of same white player" , function ( t , e ) { searchPgnGame ( '\\[\\s*' + ( e . shiftKey ? 'Black' : 'White' ) + '\\s*"' + fixRegExp ( gameWhite [ currentGame ] ) + '"\\s*\\]' , true ) ; } , true ) ;
boardShortcut ( "E4" , "search next game of same white player" , function ( t , e ) { searchPgnGame ( '\\[\\s*' + ( e . shiftKey ? 'Black' : 'White' ) + '\\s*"' + fixRegExp ( gameWhite [ currentGame ] ) + '"\\s*\\]' , false ) ; } , true ) ;
boardShortcut ( "F4" , "search next game of same black player" , function ( t , e ) { searchPgnGame ( '\\[\\s*' + ( e . shiftKey ? 'White' : 'Black' ) + '\\s*"' + fixRegExp ( gameBlack [ currentGame ] ) + '"\\s*\\]' , false ) ; } , true ) ;
boardShortcut ( "G4" , "search next round of same event" , function ( t , e ) { searchPgnGame ( '\\[\\s*Event\\s*"' + fixRegExp ( gameEvent [ currentGame ] ) + '"\\s*\\].*\\[\\s*Round\\s*"(?!' + fixRegExp ( gameRound [ currentGame ] ) + '"\\s*\\])|\\[\\s*Round\\s*"(?!' + fixRegExp ( gameRound [ currentGame ] ) + '"\\s*\\]).*\\[\\s*Event\\s*"' + fixRegExp ( gameEvent [ currentGame ] ) + '"\\s*\\]' , false ) ; } , true ) ;
boardShortcut ( "H4" , "search next event" , function ( t , e ) { searchPgnGame ( '\\[\\s*Event\\s*"(?!' + fixRegExp ( gameEvent [ currentGame ] ) + '"\\s*\\])' , false ) ; } , true ) ;
boardShortcut ( "A3" , "load first game" , function ( t , e ) { if ( numberOfGames > 1 ) { Init ( 0 ) ; } } , true ) ;
boardShortcut ( "B3" , "jump to previous games decile" , function ( t , e ) { if ( currentGame > 0 ) { calculateDeciles ( ) ; for ( var ii = ( deciles . length - 2 ) ; ii >= 0 ; ii -- ) { if ( currentGame > deciles [ ii ] ) { Init ( deciles [ ii ] ) ; break ; } } } } , true ) ;
boardShortcut ( "C3" , "load previous game" , function ( t , e ) { Init ( currentGame - 1 ) ; } , true ) ;
boardShortcut ( "D3" , "load random game" , function ( t , e ) { if ( numberOfGames > 1 ) { Init ( Math . floor ( Math . random ( ) * numberOfGames ) ) ; } } , true ) ;
boardShortcut ( "E3" , "load random game at random position" , function ( t , e ) { randomGameRandomPly ( ) ; } , true ) ;
boardShortcut ( "F3" , "load next game" , function ( t , e ) { Init ( currentGame + 1 ) ; } , true ) ;
boardShortcut ( "G3" , "jump to next games decile" , function ( t , e ) { if ( currentGame < numberOfGames - 1 ) { calculateDeciles ( ) ; for ( var ii = 1 ; ii < deciles . length ; ii ++ ) { if ( currentGame < deciles [ ii ] ) { Init ( deciles [ ii ] ) ; break ; } } } } , true ) ;
boardShortcut ( "H3" , "load last game" , function ( t , e ) { if ( numberOfGames > 1 ) { Init ( numberOfGames - 1 ) ; } } , true ) ;
boardShortcut ( "A2" , "stop autoplay" , function ( t , e ) { SetAutoPlay ( e . shiftKey ) ; } , true ) ;
boardShortcut ( "B2" , "toggle autoplay" , function ( t , e ) { SwitchAutoPlay ( ) ; } , true ) ;
boardShortcut ( "C2" , "autoplay 1 second" , function ( t , e ) { SetAutoplayDelayAndStart ( ( e . shiftKey ? 10 : 1 ) * 1000 ) ; } , true ) ;
boardShortcut ( "D2" , "autoplay 2 seconds" , function ( t , e ) { SetAutoplayDelayAndStart ( ( e . shiftKey ? 20 : 2 ) * 1000 ) ; } , true ) ;
boardShortcut ( "E2" , "autoplay 5 seconds" , function ( t , e ) { SetAutoplayDelayAndStart ( ( e . shiftKey ? 50 : 5 ) * 1000 ) ; } , true ) ;
boardShortcut ( "F2" , "autoplay custom delay" , function ( t , e ) { setCustomAutoplayDelay ( ) ; } , true ) ;
boardShortcut ( "G2" , "replay up to 6 previous half-moves, then autoplay forward" , function ( t , e ) { replayPreviousMoves ( e . shiftKey ? 10 : 6 ) ; } , true ) ;
boardShortcut ( "H2" , "replay the previous half-move, then autoplay forward" , function ( t , e ) { replayPreviousMoves ( e . shiftKey ? 3 : 1 ) ; } , true ) ;
boardShortcut ( "A1" , "go to game start" , function ( t , e ) { startButton ( e ) ; } , true ) ;
boardShortcut ( "B1" , "" , function ( t , e ) { } , true ) ; // see setB1C1F1G1...
boardShortcut ( "C1" , "" , function ( t , e ) { } , true ) ; // see setB1C1F1G1...
boardShortcut ( "D1" , "move backward" , function ( t , e ) { GoToMove ( CurrentPly - 1 ) ; } , true ) ;
boardShortcut ( "E1" , "move forward" , function ( t , e ) { GoToMove ( CurrentPly + 1 ) ; } , true ) ;
boardShortcut ( "F1" , "" , function ( t , e ) { } , true ) ; // see setB1C1F1G1...
boardShortcut ( "G1" , "" , function ( t , e ) { } , true ) ; // see setB1C1F1G1...
boardShortcut ( "H1" , "go to game end" , function ( t , e ) { endButton ( e ) ; } , true ) ;
setG7A6B6H7boardShortcuts ( ) ;
function setG7A6B6H7boardShortcuts ( ) {
if ( LiveBroadcastDelay > 0 ) {
if ( boardIsDefault ( "G7" ) ) { boardShortcut ( "G7" , "" , function ( t , e ) { } , true ) ; }
if ( boardIsDefault ( "A6" ) ) { boardShortcut ( "A6" , "pause live broadcast automatic games refresh" , function ( t , e ) { pauseLiveBroadcast ( ) ; } , true ) ; }
if ( boardIsDefault ( "B6" ) ) { boardShortcut ( "B6" , "restart live broadcast automatic games refresh" , function ( t , e ) { restartLiveBroadcast ( ) ; } , true ) ; }
if ( boardIsDefault ( "H6" ) ) { boardShortcut ( "H6" , "force live broadcast games refresh" , function ( t , e ) { refreshPgnSource ( ) ; } , true ) ; }
} else {
if ( boardIsDefault ( "G7" ) ) { boardShortcut ( "G7" , "toggle autoplay next game" , function ( t , e ) { SetAutoplayNextGame ( ! autoplayNextGame ) ; } , true ) ; }
if ( boardIsDefault ( "A6" ) ) { boardShortcut ( "A6" , "" , function ( t , e ) { } , true ) ; }
if ( boardIsDefault ( "B6" ) ) { boardShortcut ( "B6" , "" , function ( t , e ) { } , true ) ; }
if ( boardIsDefault ( "H6" ) ) { boardShortcut ( "H6" , "" , function ( t , e ) { } , true ) ; }
}
}
setB1C1F1G1boardShortcuts ( ) ;
function setB1C1F1G1boardShortcuts ( ) {
if ( commentsIntoMoveText && GameHasComments ) {
if ( boardIsDefault ( "B1" ) ) { boardShortcut ( "B1" , "go to previous comment or variation" , function ( t , e ) { if ( e . shiftKey ) { GoToMove ( CurrentPly - 10 ) ; } else { MoveToPrevComment ( ) ; } } , true ) ; }
if ( boardIsDefault ( "G1" ) ) { boardShortcut ( "G1" , "go to next comment or variation" , function ( t , e ) { if ( e . shiftKey ) { GoToMove ( CurrentPly + 10 ) ; } else { MoveToNextComment ( ) ; } } , true ) ; }
} else {
if ( boardIsDefault ( "B1" ) ) { boardShortcut ( "B1" , "move 10 half-moves backward" , function ( t , e ) { GoToMove ( CurrentPly - 10 ) ; } , true ) ; }
if ( boardIsDefault ( "G1" ) ) { boardShortcut ( "G1" , "move 10 half-moves forward" , function ( t , e ) { GoToMove ( CurrentPly + 10 ) ; } , true ) ; }
}
if ( commentsIntoMoveText && GameHasVariations ) {
if ( boardIsDefault ( "C1" ) ) { boardShortcut ( "C1" , "go to parent variation" , function ( t , e ) { if ( e . shiftKey ) { GoToMove ( CurrentPly - 6 ) ; } else { GoToMove ( StartPlyVar [ CurrentVar ] ) ; } } , true ) ; }
if ( boardIsDefault ( "F1" ) ) { boardShortcut ( "F1" , "cycle through alternative variations, if any, otherwise move forward" , function ( t , e ) { if ( e . shiftKey ) { GoToMove ( CurrentPly + 6 ) ; } else { if ( ! goToNextVariationSibling ( ) ) { GoToMove ( CurrentPly + 1 ) ; } } } , true ) ; }
} else {
if ( boardIsDefault ( "C1" ) ) { boardShortcut ( "C1" , "move 6 half-moves backward" , function ( t , e ) { GoToMove ( CurrentPly - 6 ) ; } , true ) ; }
if ( boardIsDefault ( "F1" ) ) { boardShortcut ( "F1" , "move 6 half-moves forward" , function ( t , e ) { GoToMove ( CurrentPly + 6 ) ; } , true ) ; }
}
}
var deciles = new Array ( 11 ) ;
function calculateDeciles ( ) {
for ( var ii = 0 ; ii < deciles . length ; ii ++ ) {
deciles [ ii ] = Math . round ( ( numberOfGames - 1 ) * ii / ( deciles . length - 1 ) ) ;
}
}
function replayPreviousMoves ( numPlies ) {
var thisPly = numPlies ? CurrentPly - numPlies : StartPly ;
if ( thisPly < StartPlyVar [ CurrentVar ] ) {
thisPly = StartPlyVar [ CurrentVar ] + ( CurrentVar === 0 ? 0 : 1 ) ;
}
if ( thisPly !== CurrentPly ) { GoToMove ( thisPly ) ; }
SetAutoPlay ( true ) ;
}
function detectJavascriptLocation ( jsre ) {
if ( typeof ( jsre ) == "undefined" ) { jsre = new RegExp ( "(pgn4web|pgn4web-compacted)\.js$" , "" ) ; }
var e = document . getElementsByTagName ( "script" ) ;
for ( var i = 0 ; i < e . length ; i ++ ) {
if ( ( e [ i ] . src ) && ( e [ i ] . src . match ( jsre ) ) ) {
return e [ i ] . src ;
}
}
return "" ;
}
function detectHelpLocation ( ) {
return detectJavascriptLocation ( ) . replace ( /(pgn4web|pgn4web-compacted)\.js/ , "pgn4web-help.html" ) ;
}
function detectBaseLocation ( ) {
var e = document . getElementsByTagName ( "base" ) ;
for ( var i = 0 ; i < e . length ; i ++ ) {
if ( e [ i ] . href ) { return e [ i ] . href ; }
}
return "" ;
}
var debugWin ;
function displayDebugInfo ( ) {
var theObj ;
var base = detectBaseLocation ( ) ;
var jsurl = detectJavascriptLocation ( ) ;
stopAlertPrompt ( ) ;
var dbg1 = 'pgn4web: version=' + pgn4web _version + ' homepage=' + pgn4web _project _url + '\n\n' +
'HTMLURL: length=' + location . href . length + ' url=' ;
var dbg2 = location . href . length < 100 ? location . href : ( location . href . substring ( 0 , 99 ) + '...' ) ;
var dbg3 = '\n' +
( base ? 'BASEURL: url=' + base + '\n' : '' ) +
( jsurl != 'pgn4web.js' ? 'JSURL: url=' + jsurl + '\n' : '' ) ;
if ( pgnUrl ) {
dbg3 += 'PGNURL: url=' + pgnUrl ;
} else {
if ( theObj = document . getElementById ( "pgnText" ) ) {
dbg3 += 'PGNTEXT: length=' + ( theObj . tagName . toLowerCase ( ) == "textarea" ? theObj . value . length : "?" ) ;
}
}
dbg3 += '\n\n' +
'GAME: current=' + ( currentGame + 1 ) + ' number=' + numberOfGames + '\n' +
'VARIATION: current=' + CurrentVar + ' number=' + ( numberOfVars - 1 ) + '\n' +
'PLY: start=' + StartPly + ' current=' + CurrentPly + ' number=' + PlyNumber + '\n' +
'AUTOPLAY: status=' + ( isAutoPlayOn ? 'on' : 'off' ) + ' delay=' + Delay + 'ms' + ' next=' + autoplayNextGame +
'\n\n' ;
if ( LiveBroadcastDelay > 0 ) {
dbg3 += 'LIVEBROADCAST: status=' + liveStatusDebug ( ) + ' ticker=' + LiveBroadcastTicker + ' delay=' + LiveBroadcastDelay + 'm' + '\n' + 'refreshed: ' + LiveBroadcastLastRefreshedLocal + '\n' + 'received: ' + LiveBroadcastLastReceivedLocal + '\n' + 'modified (server time): ' + LiveBroadcastLastModified _ServerTime ( ) +
'\n\n' ;
}
if ( typeof ( engineWinCheck ) == "function" ) {
dbg3 += "ANALYSIS: " + ( engineWinCheck ( ) ? "board=connected " + engineWin . customDebugInfo ( ) : "board=disconnected" ) + "\n\n" ;
}
var thisInfo = customDebugInfo ( ) ;
if ( thisInfo ) { dbg3 += "CUSTOM: " + thisInfo + "\n\n" ; }
dbg3 += 'ALERTLOG: fatalnew=' + fatalErrorNumSinceReset + ' new=' + alertNumSinceReset +
' shown=' + Math . min ( alertNum , alertLog . length ) + ' total=' + alertNum + '\n--' ;
if ( alertNum > 0 ) {
for ( var ii = 0 ; ii < alertLog . length ; ii ++ ) {
if ( alertLog [ ( alertNum - 1 - ii ) % alertLog . length ] === undefined ) { break ; }
else { dbg3 += "\n" + alertLog [ ( alertNum - 1 - ii ) % alertLog . length ] + "\n--" ; }
}
}
if ( confirm ( dbg1 + dbg2 + dbg3 + '\n\nclick OK to show this debug info in a browser window for cut and paste' ) ) {
if ( debugWin && ! debugWin . closed ) { debugWin . close ( ) ; }
debugWin = window . open ( "" , "pgn4web_debug_data" , "resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no" ) ;
if ( debugWin ) {
debugWin . document . open ( "text/html" , "replace" ) ;
debugWin . document . write ( "<html><head><title>pgn4web debug info</title>" +
"<link rel='shortcut icon' href='pawn.ico' /></head>" +
"<body>\n<pre>\n" + dbg1 + location . href + " " + dbg3 +
"\n</pre>\n</body></html>" ) ;
debugWin . document . close ( ) ;
if ( window . focus ) { debugWin . focus ( ) ; }
}
}
alertNumSinceReset = fatalErrorNumSinceReset = 0 ;
}
function liveStatusDebug ( ) {
if ( LiveBroadcastEnded ) { return "ended" ; }
if ( LiveBroadcastPaused ) { return "paused" ; }
if ( LiveBroadcastStarted ) { return "started" ; }
return "waiting" ;
}
function customDebugInfo ( ) { return "" ; }
var pgnWin ;
function displayPgnData ( oneGameOnly ) {
if ( pgnWin && ! pgnWin . closed ) { pgnWin . close ( ) ; }
pgnWin = window . open ( "" , "pgn4web_pgn_data" , "resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no" ) ;
if ( pgnWin ) {
var text = "<html><head><title>pgn4web PGN source</title>" +
"<link rel='shortcut icon' href='pawn.ico' /></head><body>\n<pre>\n" ;
if ( oneGameOnly ) { text += fullPgnGame ( currentGame ) + "\n\n" ; }
else { for ( var ii = 0 ; ii < numberOfGames ; ++ ii ) { text += fullPgnGame ( ii ) + "\n\n" ; } }
text += "\n</pre>\n</body></html>" ;
pgnWin . document . open ( "text/html" , "replace" ) ;
pgnWin . document . write ( text ) ;
pgnWin . document . close ( ) ;
if ( window . focus ) { pgnWin . focus ( ) ; }
}
}
function savePgnData ( oneGameOnly ) {
if ( pgnUrl && ! oneGameOnly ) { location . href = pgnUrl ; }
else {
displayPgnData ( oneGameOnly ) ; // fallback on displayPgnData for now
}
}
function CurrentFEN ( ) {
var thisFEN = "" ;
var emptySquares = 0 ;
for ( var row = 7 ; row >= 0 ; row -- ) {
for ( var col = 0 ; col <= 7 ; col ++ ) {
if ( Board [ col ] [ row ] === 0 ) { emptySquares ++ ; }
else {
if ( emptySquares ) {
thisFEN += emptySquares ;
emptySquares = 0 ;
}
if ( Board [ col ] [ row ] > 0 ) { thisFEN += FenPieceName . charAt ( Board [ col ] [ row ] - 1 ) . toUpperCase ( ) ; }
else if ( Board [ col ] [ row ] < 0 ) { thisFEN += FenPieceName . charAt ( - Board [ col ] [ row ] - 1 ) . toLowerCase ( ) ; }
}
}
if ( emptySquares ) {
thisFEN += emptySquares ;
emptySquares = 0 ;
}
if ( row > 0 ) { thisFEN += "/" ; }
}
thisFEN += CurrentPly % 2 ? " b" : " w" ;
// castling availability: always in the KQkq form
// note: wrong FEN for Chess960 positions with inner castling rook
var CastlingFEN = "" ;
if ( RookForOOCastling ( 0 ) !== null ) { CastlingFEN += FenPieceName . charAt ( 0 ) . toUpperCase ( ) ; }
if ( RookForOOOCastling ( 0 ) !== null ) { CastlingFEN += FenPieceName . charAt ( 1 ) . toUpperCase ( ) ; }
if ( RookForOOCastling ( 1 ) !== null ) { CastlingFEN += FenPieceName . charAt ( 0 ) . toLowerCase ( ) ; }
if ( RookForOOOCastling ( 1 ) !== null ) { CastlingFEN += FenPieceName . charAt ( 1 ) . toLowerCase ( ) ; }
thisFEN += " " + ( CastlingFEN || "-" ) ;
if ( HistEnPassant [ CurrentPly ] ) {
thisFEN += " " + String . fromCharCode ( HistEnPassantCol [ CurrentPly ] + 97 ) ;
thisFEN += CurrentPly % 2 ? "3" : "6" ;
} else { thisFEN += " -" ; }
var HalfMoveClock = InitialHalfMoveClock ;
for ( var thisPly = StartPly ; thisPly < CurrentPly ; thisPly ++ ) {
if ( ( HistType [ 0 ] [ thisPly ] == 6 ) || ( HistPieceId [ 1 ] [ thisPly ] >= 16 ) ) { HalfMoveClock = 0 ; }
else { HalfMoveClock ++ ; }
}
thisFEN += " " + HalfMoveClock ;
thisFEN += " " + ( Math . floor ( CurrentPly / 2 ) + 1 ) ;
return thisFEN ;
}
var fenWin ;
function displayFenData ( addGametext ) {
if ( fenWin && ! fenWin . closed ) { fenWin . close ( ) ; }
var thisFEN = CurrentFEN ( ) ;
var movesStr = "" ;
var lineStart = 0 ;
if ( addGametext ) {
for ( var thisPly = CurrentPly ; thisPly <= StartPly + PlyNumber ; thisPly ++ ) {
var addStr = "" ;
if ( thisPly == StartPly + PlyNumber ) {
addStr = ( CurrentVar ? "*" : gameResult [ currentGame ] || "*" ) ;
} else {
if ( thisPly % 2 === 0 ) { addStr = ( Math . floor ( thisPly / 2 ) + 1 ) + ". " ; }
else if ( thisPly == CurrentPly ) { addStr = ( Math . floor ( thisPly / 2 ) + 1 ) + "... " ; }
addStr += Moves [ thisPly ] ;
}
if ( movesStr . length + addStr . length + 1 > lineStart + 80 ) {
lineStart = movesStr . length ;
movesStr += "\n" + addStr ;
} else {
if ( movesStr . length > 0 ) { movesStr += " " ; }
movesStr += addStr ;
}
}
}
fenWin = window . open ( "" , "pgn4web_fen_data" , "resizable=yes,scrollbars=yes,toolbar=no,location=no,menubar=no,status=no" ) ;
if ( fenWin ) {
var text = "<html>" +
"<head><title>pgn4web FEN string</title><link rel='shortcut icon' href='pawn.ico' /></head>" +
"<body>\n<b><pre>\n\n" + thisFEN + "\n\n</pre></b>\n<hr>\n<pre>\n\n" ;
if ( addGametext ) {
text += "[Event \"" + ( ( CurrentVar ? "" : gameEvent [ currentGame ] ) || "?" ) + "\"]\n" +
"[Site \"" + ( ( CurrentVar ? "" : gameSite [ currentGame ] ) || "?" ) + "\"]\n" +
"[Date \"" + ( ( CurrentVar ? "" : gameDate [ currentGame ] ) || "????.??.??" ) + "\"]\n" +
"[Round \"" + ( ( CurrentVar ? "" : gameRound [ currentGame ] ) || "?" ) + "\"]\n" +
"[White \"" + ( ( CurrentVar ? "" : gameWhite [ currentGame ] ) || "?" ) + "\"]\n" +
"[Black \"" + ( ( CurrentVar ? "" : gameBlack [ currentGame ] ) || "?" ) + "\"]\n" +
"[Result \"" + ( ( CurrentVar ? "" : gameResult [ currentGame ] ) || "*" ) + "\"]\n" ;
}
if ( ( thisFEN != FenStringStart ) || ( ! addGametext ) ) {
text += "[SetUp \"1\"]\n" + "[FEN \"" + thisFEN + "\"]\n" ;
}
if ( gameVariant [ currentGame ] !== "" ) { text += "[Variant \"" + gameVariant [ currentGame ] + "\"]\n" ; }
if ( addGametext ) { text += "\n" + movesStr + "\n" ; }
text += "</pre>\n</body></html>" ;
fenWin . document . open ( "text/html" , "replace" ) ;
fenWin . document . write ( text ) ;
fenWin . document . close ( ) ;
if ( window . focus ) { fenWin . focus ( ) ; }
}
}
var pgnHeader = new Array ( ) ;
var pgnGame = new Array ( ) ;
var numberOfGames = - 1 ;
var currentGame = - 1 ;
var firstStart = true ;
var gameDate = new Array ( ) ;
var gameWhite = new Array ( ) ;
var gameBlack = new Array ( ) ;
var gameEvent = new Array ( ) ;
var gameSite = new Array ( ) ;
var gameRound = new Array ( ) ;
var gameResult = new Array ( ) ;
var gameSetUp = new Array ( ) ;
var gameFEN = new Array ( ) ;
var gameInitialWhiteClock = new Array ( ) ;
var gameInitialBlackClock = new Array ( ) ;
var gameVariant = new Array ( ) ;
var highlightedMoveId = "" ;
var isAutoPlayOn = false ;
var AutoPlayInterval = null ;
var Delay = 1000 ; // milliseconds
var autostartAutoplay = false ;
var autoplayNextGame = false ;
var initialGame = 1 ;
var initialVariation = 0 ;
var initialHalfmove = 0 ;
var alwaysInitialHalfmove = false ;
var LiveBroadcastInterval = null ;
var LiveBroadcastDelay = 0 ; // minutes
var LiveBroadcastAlert = false ;
var LiveBroadcastDemo = false ;
var LiveBroadcastStarted = false ;
var LiveBroadcastEnded = false ;
var LiveBroadcastPaused = false ;
var LiveBroadcastTicker = 0 ;
var LiveBroadcastGamesRunning = 0 ;
var LiveBroadcastStatusString = "" ;
var LiveBroadcastLastModified = new Date ( 0 ) ; // default to epoch start
var LiveBroadcastLastModifiedHeader = LiveBroadcastLastModified . toUTCString ( ) ;
var LiveBroadcastLastReceivedLocal = 'unavailable' ;
var LiveBroadcastLastRefreshedLocal = 'unavailable' ;
var LiveBroadcastPlaceholderEvent = 'live chess broadcast' ;
var LiveBroadcastPlaceholderPgn = '[Event "' + LiveBroadcastPlaceholderEvent + '"]' ;
var gameDemoMaxPly = new Array ( ) ;
var gameDemoLength = new Array ( ) ;
var LiveBroadcastSteppingMode = false ;
var ParseLastMoveError = false ;
var castleRook = - 1 ;
var mvCapture = 0 ;
var mvIsCastling = 0 ;
var mvIsPromotion = 0 ;
var mvFromCol = - 1 ;
var mvFromRow = - 1 ;
var mvToCol = - 1 ;
var mvToRow = - 1 ;
var mvPiece = - 1 ;
var mvPieceId = - 1 ;
var mvPieceOnTo = - 1 ;
var mvCaptured = - 1 ;
var mvCapturedId = - 1 ;
var mvIsNull = 0 ;
var Board = new Array ( 8 ) ;
for ( var i = 0 ; i < 8 ; ++ i ) { Board [ i ] = new Array ( 8 ) ; }
// HistCol, HistRow: move history up to last replayed ply
// HistCol[0], HistRow[0]: "square from"; 0..7, 0..7 from A1
// HistCol[1], HistRow[1]: castling/capture
// HistCol[2], HistRow[2]: "square to"; 0..7, 0..7 from A1
var HistCol = new Array ( 3 ) ;
var HistRow = new Array ( 3 ) ;
var HistPieceId = new Array ( 2 ) ;
var HistType = new Array ( 2 ) ;
var HistVar = new Array ( ) ;
var PieceCol = new Array ( 2 ) ;
var PieceRow = new Array ( 2 ) ;
var PieceType = new Array ( 2 ) ;
var PieceMoveCounter = new Array ( 2 ) ;
for ( i = 0 ; i < 2 ; ++ i ) {
PieceCol [ i ] = new Array ( 16 ) ;
PieceRow [ i ] = new Array ( 16 ) ;
PieceType [ i ] = new Array ( 16 ) ;
PieceMoveCounter [ i ] = new Array ( 16 ) ;
HistType [ i ] = new Array ( ) ;
HistPieceId [ i ] = new Array ( ) ;
}
for ( i = 0 ; i < 3 ; ++ i ) {
HistCol [ i ] = new Array ( ) ;
HistRow [ i ] = new Array ( ) ;
}
var HistEnPassant = new Array ( ) ;
HistEnPassant [ 0 ] = false ;
var HistEnPassantCol = new Array ( ) ;
HistEnPassantCol [ 0 ] = - 1 ;
var HistNull = new Array ( ) ;
HistNull [ 0 ] = 0 ;
var FenPieceName = "KQRBNP" ;
var PieceCode = FenPieceName . split ( "" ) ; // IE needs array for [index]
var FenStringStart = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" ;
var columnsLetters = "ABCDEFGH" ;
var InitialHalfMoveClock = 0 ;
var PieceImg = new Array ( new Array ( 6 ) , new Array ( 6 ) ) ;
var ClearImg ;
var ImagePath = 'images' ;
var ImagePathOld = null ;
var imageType = 'png' ;
var defaultImagesSize = 40 ;
var highlightOption = true ;
var commentsIntoMoveText = true ;
var commentsOnSeparateLines = false ;
var pgnUrl = '' ;
var CastlingLong = new Array ( 2 ) ;
var CastlingShort = new Array ( 2 ) ;
var Moves = new Array ( ) ;
var MoveComments = new Array ( ) ;
var MoveColor ;
var MoveCount ;
var PlyNumber ;
var StartPly ;
var CurrentPly ;
var IsRotated = false ;
var pgnHeaderTagRegExp = /\[\s*(\w+)\s*"([^"]*)"\s*\]/ ;
var pgnHeaderTagRegExpGlobal = /\[\s*(\w+)\s*"([^"]*)"\s*\]/g ;
var pgnHeaderBlockRegExp = /\s*(\[\s*\w+\s*"[^"]*"\s*\]\s*)+/ ;
var emptyPgnHeader = '[Event ""]\n[Site ""]\n[Date ""]\n[Round ""]\n[White ""]\n[Black ""]\n[Result ""]\n' ;
var alertPgn = emptyPgnHeader + "\n{error: click on the top left chessboard square for debug info}" ;
var pgn4webVariationRegExp = /\[%pgn4web_variation (\d+)\]/ ;
var pgn4webVariationRegExpGlobal = /\[%pgn4web_variation (\d+)\]/g ;
var gameSelectorHead = ' ···' ;
var gameSelectorMono = true ;
var gameSelectorNum = false ;
var gameSelectorNumLenght = 0 ;
var gameSelectorChEvent = 0 ;
var gameSelectorChSite = 0 ;
var gameSelectorChRound = 0 ;
var gameSelectorChWhite = 15 ;
var gameSelectorChBlack = 15 ;
var gameSelectorChResult = 0 ;
var gameSelectorChDate = 10 ;
function CheckLegality ( what , plyCount ) {
var retVal , thisCol ;
if ( what == '--' ) {
StoreMove ( plyCount ) ;
return true ;
}
// castling
if ( what == 'O-O' ) {
if ( ! CheckLegalityOO ( ) ) { return false ; }
for ( thisCol = PieceCol [ MoveColor ] [ 0 ] ; thisCol < 7 ; thisCol ++ ) {
if ( IsCheck ( thisCol , MoveColor * 7 , MoveColor ) ) { return false ; }
}
StoreMove ( plyCount ) ;
return true ;
} else if ( what == 'O-O-O' ) {
if ( ! CheckLegalityOOO ( ) ) { return false ; }
for ( thisCol = PieceCol [ MoveColor ] [ 0 ] ; thisCol > 1 ; thisCol -- ) {
if ( IsCheck ( thisCol , MoveColor * 7 , MoveColor ) ) { return false ; }
}
StoreMove ( plyCount ) ;
return true ;
}
// capture: "square to" occupied by opposite color piece (except en-passant)
// promotion: "square to" moved piece different from piece
if ( ! mvCapture ) {
if ( Board [ mvToCol ] [ mvToRow ] !== 0 ) { return false ; }
}
if ( ( mvCapture ) && ( Color ( Board [ mvToCol ] [ mvToRow ] ) != 1 - MoveColor ) ) {
if ( ( mvPiece != 6 ) || ( ! HistEnPassant [ plyCount ] ) || ( HistEnPassantCol [ plyCount ] != mvToCol ) ||
( mvToRow != 5 - 3 * MoveColor ) ) { return false ; }
}
if ( mvIsPromotion ) {
if ( mvPiece != 6 ) { return false ; }
if ( mvPieceOnTo >= 6 ) { return false ; }
if ( mvToRow != 7 * ( 1 - MoveColor ) ) { return false ; }
}
// piece move: which same type piece could move there?
for ( var pieceId = 0 ; pieceId < 16 ; ++ pieceId ) {
if ( PieceType [ MoveColor ] [ pieceId ] == mvPiece ) {
if ( mvPiece == 1 ) { retVal = CheckLegalityKing ( pieceId ) ; }
else if ( mvPiece == 2 ) { retVal = CheckLegalityQueen ( pieceId ) ; }
else if ( mvPiece == 3 ) { retVal = CheckLegalityRook ( pieceId ) ; }
else if ( mvPiece == 4 ) { retVal = CheckLegalityBishop ( pieceId ) ; }
else if ( mvPiece == 5 ) { retVal = CheckLegalityKnight ( pieceId ) ; }
else if ( mvPiece == 6 ) { retVal = CheckLegalityPawn ( pieceId ) ; }
if ( retVal ) {
mvPieceId = pieceId ;
// board updated: king check?
StoreMove ( plyCount ) ;
if ( ! IsCheck ( PieceCol [ MoveColor ] [ 0 ] , PieceRow [ MoveColor ] [ 0 ] , MoveColor ) ) { return true ; }
else { UndoMove ( plyCount ) ; }
}
}
}
return false ;
}
function CheckLegalityKing ( thisKing ) {
if ( ( mvFromCol >= 0 ) && ( mvFromCol != PieceCol [ MoveColor ] [ thisKing ] ) ) { return false ; }
if ( ( mvFromRow >= 0 ) && ( mvFromRow != PieceRow [ MoveColor ] [ thisKing ] ) ) { return false ; }
if ( Math . abs ( PieceCol [ MoveColor ] [ thisKing ] - mvToCol ) > 1 ) { return false ; }
if ( Math . abs ( PieceRow [ MoveColor ] [ thisKing ] - mvToRow ) > 1 ) { return false ; }
return true ;
}
function CheckLegalityQueen ( thisQueen ) {
if ( ( mvFromCol >= 0 ) && ( mvFromCol != PieceCol [ MoveColor ] [ thisQueen ] ) ) { return false ; }
if ( ( mvFromRow >= 0 ) && ( mvFromRow != PieceRow [ MoveColor ] [ thisQueen ] ) ) { return false ; }
if ( ( ( PieceCol [ MoveColor ] [ thisQueen ] - mvToCol ) * ( PieceRow [ MoveColor ] [ thisQueen ] - mvToRow ) !== 0 ) && ( Math . abs ( PieceCol [ MoveColor ] [ thisQueen ] - mvToCol ) != Math . abs ( PieceRow [ MoveColor ] [ thisQueen ] - mvToRow ) ) ) { return false ; }
if ( ! CheckClearWay ( thisQueen ) ) { return false ; }
return true ;
}
function CheckLegalityRook ( thisRook ) {
if ( ( mvFromCol >= 0 ) && ( mvFromCol != PieceCol [ MoveColor ] [ thisRook ] ) ) { return false ; }
if ( ( mvFromRow >= 0 ) && ( mvFromRow != PieceRow [ MoveColor ] [ thisRook ] ) ) { return false ; }
if ( ( PieceCol [ MoveColor ] [ thisRook ] - mvToCol ) * ( PieceRow [ MoveColor ] [ thisRook ] - mvToRow ) !== 0 ) { return false ; }
if ( ! CheckClearWay ( thisRook ) ) { return false ; }
return true ;
}
function CheckLegalityBishop ( thisBishop ) {
if ( ( mvFromCol >= 0 ) && ( mvFromCol != PieceCol [ MoveColor ] [ thisBishop ] ) ) { return false ; }
if ( ( mvFromRow >= 0 ) && ( mvFromRow != PieceRow [ MoveColor ] [ thisBishop ] ) ) { return false ; }
if ( Math . abs ( PieceCol [ MoveColor ] [ thisBishop ] - mvToCol ) != Math . abs ( PieceRow [ MoveColor ] [ thisBishop ] - mvToRow ) ) { return false ; }
if ( ! CheckClearWay ( thisBishop ) ) { return false ; }
return true ;
}
function CheckLegalityKnight ( thisKnight ) {
if ( ( mvFromCol >= 0 ) && ( mvFromCol != PieceCol [ MoveColor ] [ thisKnight ] ) ) { return false ; }
if ( ( mvFromRow >= 0 ) && ( mvFromRow != PieceRow [ MoveColor ] [ thisKnight ] ) ) { return false ; }
if ( Math . abs ( PieceCol [ MoveColor ] [ thisKnight ] - mvToCol ) * Math . abs ( PieceRow [ MoveColor ] [ thisKnight ] - mvToRow ) != 2 ) { return false ; }
return true ;
}
function CheckLegalityPawn ( thisPawn ) {
if ( ( mvFromCol >= 0 ) && ( mvFromCol != PieceCol [ MoveColor ] [ thisPawn ] ) ) { return false ; }
if ( ( mvFromRow >= 0 ) && ( mvFromRow != PieceRow [ MoveColor ] [ thisPawn ] ) ) { return false ; }
if ( Math . abs ( PieceCol [ MoveColor ] [ thisPawn ] - mvToCol ) != mvCapture ) { return false ; }
if ( mvCapture ) {
if ( PieceRow [ MoveColor ] [ thisPawn ] - mvToRow != 2 * MoveColor - 1 ) { return false ; }
} else {
if ( PieceRow [ MoveColor ] [ thisPawn ] - mvToRow == 4 * MoveColor - 2 ) {
if ( PieceRow [ MoveColor ] [ thisPawn ] != 1 + 5 * MoveColor ) { return false ; }
if ( Board [ mvToCol ] [ mvToRow + 2 * MoveColor - 1 ] !== 0 ) { return false ; }
} else {
if ( PieceRow [ MoveColor ] [ thisPawn ] - mvToRow != 2 * MoveColor - 1 ) { return false ; }
}
}
return true ;
}
function RookForOOCastling ( color ) {
if ( CastlingShort [ color ] < 0 ) { return null ; }
if ( PieceMoveCounter [ color ] [ 0 ] > 0 ) { return null ; }
var legal = false ;
for ( var thisRook = 0 ; thisRook < 16 ; thisRook ++ ) {
if ( ( PieceCol [ color ] [ thisRook ] == CastlingShort [ color ] ) &&
( PieceCol [ color ] [ thisRook ] > PieceCol [ color ] [ 0 ] ) &&
( PieceRow [ color ] [ thisRook ] == color * 7 ) &&
( PieceType [ color ] [ thisRook ] == 3 ) ) {
legal = true ;
break ;
}
}
if ( ! legal ) { return null ; }
if ( PieceMoveCounter [ color ] [ thisRook ] > 0 ) { return null ; }
return thisRook ;
}
function CheckLegalityOO ( ) {
var thisRook = RookForOOCastling ( MoveColor ) ;
if ( thisRook === null ) { return false ; }
// check no piece between king and rook
// clear king/rook squares for Chess960
Board [ PieceCol [ MoveColor ] [ 0 ] ] [ MoveColor * 7 ] = 0 ;
Board [ PieceCol [ MoveColor ] [ thisRook ] ] [ MoveColor * 7 ] = 0 ;
var col = PieceCol [ MoveColor ] [ thisRook ] ;
if ( col < 6 ) { col = 6 ; }
while ( ( col > PieceCol [ MoveColor ] [ 0 ] ) || ( col >= 5 ) ) {
if ( Board [ col ] [ MoveColor * 7 ] !== 0 ) { return false ; }
-- col ;
}
castleRook = thisRook ;
return true ;
}
function RookForOOOCastling ( color ) {
if ( CastlingLong [ color ] < 0 ) { return null ; }
if ( PieceMoveCounter [ color ] [ 0 ] > 0 ) { return null ; }
var legal = false ;
for ( var thisRook = 0 ; thisRook < 16 ; thisRook ++ ) {
if ( ( PieceCol [ color ] [ thisRook ] == CastlingLong [ color ] ) &&
( PieceCol [ color ] [ thisRook ] < PieceCol [ color ] [ 0 ] ) &&
( PieceRow [ color ] [ thisRook ] == color * 7 ) &&
( PieceType [ color ] [ thisRook ] == 3 ) ) {
legal = true ;
break ;
}
}
if ( ! legal ) { return null ; }
if ( PieceMoveCounter [ color ] [ thisRook ] > 0 ) { return null ; }
return thisRook ;
}
function CheckLegalityOOO ( ) {
var thisRook = RookForOOOCastling ( MoveColor ) ;
if ( thisRook === null ) { return false ; }
// check no piece between king and rook
// clear king/rook squares for Chess960
Board [ PieceCol [ MoveColor ] [ 0 ] ] [ MoveColor * 7 ] = 0 ;
Board [ PieceCol [ MoveColor ] [ thisRook ] ] [ MoveColor * 7 ] = 0 ;
var col = PieceCol [ MoveColor ] [ thisRook ] ;
if ( col > 2 ) { col = 2 ; }
while ( ( col < PieceCol [ MoveColor ] [ 0 ] ) || ( col <= 3 ) ) {
if ( Board [ col ] [ MoveColor * 7 ] !== 0 ) { return false ; }
++ col ;
}
castleRook = thisRook ;
return true ;
}
function CheckClearWay ( thisPiece ) {
var stepCol = sign ( mvToCol - PieceCol [ MoveColor ] [ thisPiece ] ) ;
var stepRow = sign ( mvToRow - PieceRow [ MoveColor ] [ thisPiece ] ) ;
var startCol = PieceCol [ MoveColor ] [ thisPiece ] + stepCol ;
var startRow = PieceRow [ MoveColor ] [ thisPiece ] + stepRow ;
while ( ( startCol != mvToCol ) || ( startRow != mvToRow ) ) {
if ( Board [ startCol ] [ startRow ] !== 0 ) { return false ; }
startCol += stepCol ;
startRow += stepRow ;
}
return true ;
}
function CleanMove ( move ) {
move = move . replace ( /[^a-wyzA-WYZ0-9#-]*/g , '' ) ; // patch: remove/add '+' 'x' '=' chars for full chess informant style or pgn style for the game text
if ( move . match ( /^[Oo0]/ ) ) { move = move . replace ( /[o0]/g , 'O' ) . replace ( /O(?=O)/g , 'O-' ) ; }
move = move . replace ( /ep/i , '' ) ;
return move ;
}
function GoToMove ( thisPly , thisVar ) {
SetAutoPlay ( false ) ;
if ( typeof ( thisVar ) == "undefined" ) { thisVar = CurrentVar ; }
else {
if ( thisVar < 0 ) { thisVar = 0 ; }
else if ( thisVar >= numberOfVars ) { thisVar = numberOfVars - 1 ; }
}
if ( thisPly < 0 ) { thisPly = 0 ; }
else if ( thisPly >= StartPlyVar [ thisVar ] + PlyNumberVar [ thisVar ] ) {
thisPly = StartPlyVar [ thisVar ] + PlyNumberVar [ thisVar ] ;
}
if ( thisVar === CurrentVar ) {
var diff = thisPly - CurrentPly ;
if ( diff > 0 ) { MoveForward ( diff ) ; }
else { MoveBackward ( - diff ) ; }
} else {
var backStart = StartPly ;
loopCommonPredecessor :
for ( var ii = PredecessorsVars [ CurrentVar ] . length - 1 ; ii >= 0 ; ii -- ) {
for ( var jj = PredecessorsVars [ thisVar ] . length - 1 ; jj >= 0 ; jj -- ) {
if ( PredecessorsVars [ CurrentVar ] [ ii ] === PredecessorsVars [ thisVar ] [ jj ] ) {
backStart = Math . min ( PredecessorsVars [ CurrentVar ] [ ii + 1 ] ? StartPlyVar [ PredecessorsVars [ CurrentVar ] [ ii + 1 ] ] : CurrentPly , PredecessorsVars [ thisVar ] [ jj + 1 ] ? StartPlyVar [ PredecessorsVars [ thisVar ] [ jj + 1 ] ] : thisPly ) ;
break loopCommonPredecessor ;
}
}
}
MoveBackward ( CurrentPly - backStart , true ) ;
MoveForward ( thisPly - backStart , thisVar ) ;
}
}
function SetShortcutKeysEnabled ( onOff ) {
shortcutKeysEnabled = onOff ;
}
function interactivelyToggleShortcutKeys ( ) {
if ( confirm ( "Shortcut keys currently " + ( shortcutKeysEnabled ? "enabled" : "disabled" ) + ".\nToggle shortcut keys to " + ( shortcutKeysEnabled ? "DISABLED" : "ENABLED" ) + "?" ) ) {
SetShortcutKeysEnabled ( ! shortcutKeysEnabled ) ;
}
}
function SetCommentsIntoMoveText ( onOff ) {
commentsIntoMoveText = onOff ;
}
function SetCommentsOnSeparateLines ( onOff ) {
commentsOnSeparateLines = onOff ;
}
function SetAutostartAutoplay ( onOff ) {
autostartAutoplay = onOff ;
}
function SetAutoplayNextGame ( onOff ) {
autoplayNextGame = onOff ;
}
function SetInitialHalfmove ( number _or _string , always ) {
alwaysInitialHalfmove = ( always === true ) ;
if ( number _or _string === undefined ) { initialHalfmove = 0 ; return ; }
initialHalfmove = number _or _string ;
if ( ( typeof number _or _string == "string" ) && ( number _or _string . match ( /^(start|end|random|comment)$/ ) ) ) { return ; }
if ( isNaN ( initialHalfmove = parseInt ( initialHalfmove , 10 ) ) ) { initialHalfmove = 0 ; }
}
function SetInitialVariation ( number ) {
initialVariation = isNaN ( number = parseInt ( number , 10 ) ) ? 0 : number ;
}
function SetInitialGame ( number _or _string ) {
initialGame = typeof ( number _or _string ) == "undefined" ? 1 : number _or _string ;
}
function randomGameRandomPly ( ) {
if ( numberOfGames > 1 ) {
var oldInitialHalfmove = initialHalfmove ;
var oldAlwaysInitialHalfmove = alwaysInitialHalfmove ;
SetInitialHalfmove ( "random" , true ) ;
Init ( Math . floor ( Math . random ( ) * numberOfGames ) ) ;
SetInitialHalfmove ( oldInitialHalfmove , oldAlwaysInitialHalfmove ) ;
}
}
// clock detection as [%clk 01:02]
function clockFromComment ( plyNum ) {
return customPgnCommentTag ( "clk" , null , plyNum ) ;
}
function clockFromHeader ( whiteToMove ) {
var clockString = customPgnHeaderTag ( "Clock" ) + "" ;
var matches = clockString . match ( "^" + ( whiteToMove ? "W" : "B" ) + "/(.*)$" ) ;
if ( matches ) { return matches [ 1 ] ; }
else { return null ; }
}
function HighlightLastMove ( ) {
var theObj , moveId , text , ii , clockString , clockRegExp , clockMatch ;
undoStackStore ( ) ;
// remove old move highlighting
if ( highlightedMoveId ) {
if ( theObj = document . getElementById ( highlightedMoveId ) ) {
theObj . className = ( highlightedMoveId . match ( /Var0Mv/ ) ? 'move' : 'variation' ) + ' notranslate' ;
}
}
// halfmove to be highlighted, negative for starting position
var showThisMove = CurrentPly - 1 ;
if ( showThisMove > StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ) { showThisMove = StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ; }
if ( theObj = document . getElementById ( "GameLastComment" ) ) {
if ( commentsIntoMoveText ) {
variationTextDepth = CurrentVar === 0 ? 0 : 1 ;
text = '<SPAN CLASS="comment">' +
strippedMoveComment ( showThisMove + 1 , CurrentVar , true ) . replace ( /\sID="[^"]*"/g , '' ) +
'</SPAN>' ;
} else { text = '' ; }
theObj . innerHTML = text ;
}
// side to move
var whiteToMove = ( ( showThisMove + 1 ) % 2 === 0 ) ;
text = whiteToMove ? 'white' : 'black' ;
if ( theObj = document . getElementById ( "GameSideToMove" ) )
{ theObj . innerHTML = text ; }
// clock
var lastMoverClockObj = document . getElementById ( whiteToMove ?
"GameBlackClock" : "GameWhiteClock" ) ;
var initialLastMoverClock = whiteToMove ?
gameInitialBlackClock [ currentGame ] : gameInitialWhiteClock [ currentGame ] ;
var beforeLastMoverClockObj = document . getElementById ( whiteToMove ?
"GameWhiteClock" : "GameBlackClock" ) ;
var initialBeforeLastMoverClock = whiteToMove ?
gameInitialWhiteClock [ currentGame ] : gameInitialBlackClock [ currentGame ] ;
if ( lastMoverClockObj ) {
clockString = ( ( showThisMove + 1 === StartPly + PlyNumber ) &&
( ( ! LiveBroadcastDemo ) || ( gameResult [ currentGame ] !== "*" ) ) ) ?
clockFromHeader ( ! whiteToMove ) : null ;
if ( clockString === null ) {
clockString = showThisMove + 1 > StartPly ?
clockFromComment ( showThisMove + 1 ) : initialLastMoverClock ;
if ( ! clockString && ( CurrentPly === StartPly + PlyNumber ) ) {
// support for time info in the last comment as { White Time: 0h:12min Black Time: 1h:23min }
clockRegExp = new RegExp ( ( whiteToMove ? "Black" : "White" ) + "\\s+Time:\\s*(\\S+)" , "i" ) ;
if ( clockMatch = strippedMoveComment ( StartPly + PlyNumber ) . match ( clockRegExp ) ) {
clockString = clockMatch [ 1 ] ;
}
}
}
lastMoverClockObj . innerHTML = clockString ;
}
if ( beforeLastMoverClockObj ) {
clockString = ( ( showThisMove + 1 === StartPly + PlyNumber ) &&
( ( ! LiveBroadcastDemo ) || ( gameResult [ currentGame ] !== "*" ) ) ) ?
clockFromHeader ( whiteToMove ) : null ;
if ( clockString === null ) {
clockString = showThisMove > StartPly ?
clockFromComment ( showThisMove ) : initialBeforeLastMoverClock ;
if ( ! clockString && ( CurrentPly === StartPly + PlyNumber ) ) {
// see comment above
clockRegExp = new RegExp ( ( whiteToMove ? "White" : "Black" ) + "\\s+Time:\\s*(\\S+)" , "i" ) ;
if ( clockMatch = strippedMoveComment ( StartPly + PlyNumber ) . match ( clockRegExp ) ) {
clockString = clockMatch [ 1 ] ;
}
}
}
beforeLastMoverClockObj . innerHTML = clockString ;
}
if ( lastMoverClockObj && beforeLastMoverClockObj ) {
if ( lastMoverClockObj . innerHTML && ! beforeLastMoverClockObj . innerHTML ) {
beforeLastMoverClockObj . innerHTML = "-" ;
} else if ( ! lastMoverClockObj . innerHTML && beforeLastMoverClockObj . innerHTML ) {
lastMoverClockObj . innerHTML = "-" ;
}
}
// next move
if ( theObj = document . getElementById ( "GameNextMove" ) ) {
if ( CurrentVar === 0 && showThisMove + 1 >= StartPly + PlyNumber ) {
text = '<SPAN CLASS="move notranslate">' + gameResult [ currentGame ] + '</SPAN>' ;
} else if ( typeof ( Moves [ showThisMove + 1 ] ) == "undefined" ) {
text = "" ;
} else {
text = printMoveText ( showThisMove + 1 , CurrentVar , ( CurrentVar !== 0 ) , true , false ) ;
}
theObj . innerHTML = text ;
}
// next variations
if ( theObj = document . getElementById ( "GameNextVariations" ) ) {
text = '' ;
if ( commentsIntoMoveText ) {
var children = childrenVars ( showThisMove + 1 , CurrentVar ) ;
for ( ii = 0 ; ii < children . length ; ii ++ ) {
if ( children [ ii ] !== CurrentVar ) {
text += ' ' + printMoveText ( showThisMove + 1 , children [ ii ] , ( children [ ii ] !== 0 ) , true , false ) ;
}
}
}
theObj . innerHTML = text ;
}
// last move
if ( theObj = document . getElementById ( "GameLastMove" ) ) {
if ( ( showThisMove >= StartPly ) && Moves [ showThisMove ] ) {
text = printMoveText ( showThisMove , CurrentVar , ( CurrentVar !== 0 ) , true , false ) ;
} else if ( showThisMove === StartPly - 1 ) {
text = '<SPAN CLASS="' + ( CurrentVar > 0 ? 'variation' : 'move' ) + ' notranslate">' +
( Math . floor ( ( showThisMove + 1 ) / 2 ) + 1 ) + ( ( ( showThisMove + 1 ) % 2 ) ? "..." : "." ) +
'</SPAN>' ;
} else { text = '' ; }
theObj . innerHTML = text ;
}
// last variations
if ( theObj = document . getElementById ( "GameLastVariations" ) ) {
text = '' ;
if ( commentsIntoMoveText ) {
var siblings = childrenVars ( showThisMove , HistVar [ showThisMove ] ) ;
for ( ii = 0 ; ii < siblings . length ; ii ++ ) {
if ( siblings [ ii ] !== CurrentVar ) {
text += ' ' + printMoveText ( showThisMove , siblings [ ii ] , ( siblings [ ii ] !== 0 ) , true , false ) ;
}
}
}
theObj . innerHTML = text ;
}
if ( showThisMove >= ( StartPlyVar [ CurrentVar ] - 1 ) ) {
moveId = 'Var' + CurrentVar + 'Mv' + ( showThisMove + 1 ) ;
if ( theObj = document . getElementById ( moveId ) ) {
theObj . className = ( CurrentVar ? 'variation variationOn' : 'move moveOn' ) + ' notranslate' ;
}
highlightedMoveId = moveId ;
if ( highlightOption ) {
var colFrom , rowFrom , colTo , rowTo ;
if ( ( showThisMove < StartPly ) || HistNull [ showThisMove ] ) {
colFrom = rowFrom = - 1 ;
colTo = rowTo = - 1 ;
} else {
colFrom = HistCol [ 0 ] [ showThisMove ] === undefined ? - 1 : HistCol [ 0 ] [ showThisMove ] ;
rowFrom = HistRow [ 0 ] [ showThisMove ] === undefined ? - 1 : HistRow [ 0 ] [ showThisMove ] ;
colTo = HistCol [ 2 ] [ showThisMove ] === undefined ? - 1 : HistCol [ 2 ] [ showThisMove ] ;
rowTo = HistRow [ 2 ] [ showThisMove ] === undefined ? - 1 : HistRow [ 2 ] [ showThisMove ] ;
}
highlightMove ( colFrom , rowFrom , colTo , rowTo ) ;
}
}
}
function SetHighlightOption ( on ) {
highlightOption = on ;
}
function SetHighlight ( on ) {
SetHighlightOption ( on ) ;
if ( on ) { HighlightLastMove ( ) ; }
else { highlightMove ( - 1 , - 1 , - 1 , - 1 ) ; }
}
var colFromHighlighted = - 1 ;
var rowFromHighlighted = - 1 ;
var colToHighlighted = - 1 ;
var rowToHighlighted = - 1 ;
function highlightMove ( colFrom , rowFrom , colTo , rowTo ) {
highlightSquare ( colFromHighlighted , rowFromHighlighted , false ) ;
highlightSquare ( colToHighlighted , rowToHighlighted , false ) ;
if ( highlightSquare ( colFrom , rowFrom , true ) ) {
colFromHighlighted = colFrom ;
rowFromHighlighted = rowFrom ;
} else { colFromHighlighted = rowFromHighlighted = - 1 ; }
if ( highlightSquare ( colTo , rowTo , true ) ) {
colToHighlighted = colTo ;
rowToHighlighted = rowTo ;
} else { colToHighlighted = rowToHighlighted = - 1 ; }
}
function highlightSquare ( col , row , on ) {
if ( ( col === undefined ) || ( row === undefined ) ) { return false ; }
if ( ! SquareOnBoard ( col , row ) ) { return false ; }
var trow = IsRotated ? row : 7 - row ;
var tcol = IsRotated ? 7 - col : col ;
var theObj = document . getElementById ( 'tcol' + tcol + 'trow' + trow ) ;
if ( ! theObj ) { return false ; }
if ( on ) { theObj . className = ( trow + tcol ) % 2 === 0 ? "highlightWhiteSquare" : "highlightBlackSquare" ; }
else { theObj . className = ( trow + tcol ) % 2 === 0 ? "whiteSquare" : "blackSquare" ; }
return true ;
}
var undoStackMax = 1000 ;
var undoStackGame = new Array ( undoStackMax ) ;
var undoStackVar = new Array ( undoStackMax ) ;
var undoStackPly = new Array ( undoStackMax ) ;
var undoStackStart = 0 ;
var undoStackCurrent = 0 ;
var undoStackEnd = 0 ;
var undoRedoInProgress = false ;
function undoStackReset ( ) {
undoStackGame = new Array ( undoStackMax ) ;
undoStackVar = new Array ( undoStackMax ) ;
undoStackPly = new Array ( undoStackMax ) ;
undoStackStart = undoStackCurrent = undoStackEnd = 0 ;
}
function undoStackStore ( ) {
if ( undoRedoInProgress ) { return false ; }
if ( ( undoStackStart === undoStackCurrent ) ||
( currentGame !== undoStackGame [ undoStackCurrent ] ) ||
( CurrentVar !== undoStackVar [ undoStackCurrent ] ) ||
( CurrentPly !== undoStackPly [ undoStackCurrent ] ) ) {
undoStackCurrent = ( undoStackCurrent + 1 ) % undoStackMax ;
undoStackGame [ undoStackCurrent ] = currentGame ;
undoStackVar [ undoStackCurrent ] = CurrentVar ;
undoStackPly [ undoStackCurrent ] = CurrentPly ;
undoStackEnd = undoStackCurrent ;
if ( undoStackStart === undoStackCurrent ) { undoStackStart = ( undoStackStart + 1 ) % undoStackMax ; }
}
return true ;
}
function undoStackUndo ( ) {
if ( ( undoStackCurrent - 1 + undoStackMax ) % undoStackMax === undoStackStart ) { return false ; }
undoRedoInProgress = true ;
undoStackCurrent = ( undoStackCurrent - 1 + undoStackMax ) % undoStackMax ;
if ( undoStackGame [ undoStackCurrent ] !== currentGame ) { Init ( undoStackGame [ undoStackCurrent ] ) ; }
GoToMove ( undoStackPly [ undoStackCurrent ] , undoStackVar [ undoStackCurrent ] ) ;
undoRedoInProgress = false ;
return true ;
}
function undoStackRedo ( ) {
if ( undoStackCurrent === undoStackEnd ) { return false ; }
undoRedoInProgress = true ;
undoStackCurrent = ( undoStackCurrent + 1 ) % undoStackMax ;
if ( undoStackGame [ undoStackCurrent ] !== currentGame ) { Init ( undoStackGame [ undoStackCurrent ] ) ; }
GoToMove ( undoStackPly [ undoStackCurrent ] , undoStackVar [ undoStackCurrent ] ) ;
undoRedoInProgress = false ;
return true ;
}
function fixCommonPgnMistakes ( text ) {
text = text . replace ( /[\u00A0\u180E\u2000-\u200A\u202F\u205F\u3000]/g , " " ) ; // some spaces to plain space
text = text . replace ( /\u00BD/g , "1/2" ) ; // "half fraction" to "1/2"
text = text . replace ( /[\u2010-\u2015]/g , "-" ) ; // "hyphens" to "-"
text = text . replace ( /\u2024/g , "." ) ; // "one dot leader" to "."
text = text . replace ( /[\u2025-\u2026]/g , "..." ) ; // "two dot leader" and "ellipsis" to "..."
text = text . replace ( /\\"/g , "'" ) ; // fix [Opening "Queen\"s Gambit"]
return text ;
}
function fullPgnGame ( gameNum ) {
var res = pgnHeader [ gameNum ] ? pgnHeader [ gameNum ] . replace ( /^[^[]*/g , "" ) : "" ;
res = res . replace ( /\[\s*(\w+)\s*"([^"]*)"\s*\][^[]*/g , '[$1 "$2"]\n' ) ;
res += "\n" ;
res += pgnGame [ gameNum ] ? pgnGame [ gameNum ] . replace ( /(^[\s]*|[\s]*$)/g , "" ) : "" ;
return res ;
}
function pgnGameFromPgnText ( pgnText ) {
var headMatch , prevHead , newHead , startNew , afterNew , lastOpen , checkedGame , validHead ;
pgnText = simpleHtmlentities ( fixCommonPgnMistakes ( pgnText ) ) ;
// PGN standard: ignore lines starting with %
pgnText = pgnText . replace ( /(^|\n)%.*(\n|$)/g , "\n" ) ;
numberOfGames = 0 ;
checkedGame = "" ;
while ( headMatch = pgnHeaderBlockRegExp . exec ( pgnText ) ) {
newHead = headMatch [ 0 ] ;
startNew = pgnText . indexOf ( newHead ) ;
afterNew = startNew + newHead . length ;
if ( prevHead ) {
checkedGame += pgnText . slice ( 0 , startNew ) ;
validHead = ( ( lastOpen = checkedGame . lastIndexOf ( "{" ) ) < 0 ) || ( checkedGame . lastIndexOf ( "}" ) ) > lastOpen ;
if ( validHead ) {
pgnHeader [ numberOfGames ] = prevHead ;
pgnGame [ numberOfGames ++ ] = checkedGame ;
checkedGame = "" ;
} else {
checkedGame += newHead ;
}
} else {
validHead = true ;
}
if ( validHead ) { prevHead = newHead ; }
pgnText = pgnText . slice ( afterNew ) ;
}
if ( prevHead ) {
pgnHeader [ numberOfGames ] = prevHead ;
checkedGame += pgnText ;
pgnGame [ numberOfGames ++ ] = checkedGame ;
}
return ( numberOfGames > 0 ) ;
}
function pgnGameFromHttpRequest ( httpResponseData ) {
// process here any special file types, for instance zipfiles:
// if (pgnUrl && pgnUrl.replace(/[?#].*/, "").match(/\.zip$/i)) { return pgnGameFromPgnText(unzipPgnFiles(httpResponseData)); }
// remember to fix function loadPgnFromPgnUrl() for binary data
return pgnGameFromPgnText ( httpResponseData ) ;
}
var http _request _last _processed _id = 0 ;
function updatePgnFromHttpRequest ( this _http _request , this _http _request _id ) {
var res = LOAD _PGN _FAIL ;
if ( this _http _request . readyState != 4 ) { return ; }
if ( this _http _request _id < http _request _last _processed _id ) { return ; }
else { http _request _last _processed _id = this _http _request _id ; }
if ( ( this _http _request . status == 200 ) || ( this _http _request . status === 0 ) || ( this _http _request . status == 304 ) ) {
if ( this _http _request . status == 304 ) {
if ( LiveBroadcastDelay > 0 ) {
res = LOAD _PGN _UNMODIFIED ;
} else {
myAlert ( 'error: unmodified PGN URL when not in live mode' ) ;
}
// patch Opera's failure reporting 304 status (up to Opera v12)
} else if ( window . opera && ( ! this _http _request . responseText ) && ( this _http _request . status === 0 ) ) {
this _http _request . abort ( ) ;
res = LOAD _PGN _UNMODIFIED ;
// end of patch
} else if ( ! this _http _request . responseText ) {
myAlert ( 'error: no data received from PGN URL\n' + pgnUrl , true ) ;
} else if ( ! pgnGameFromHttpRequest ( this _http _request . responseText ) ) {
myAlert ( 'error: no games found at PGN URL\n' + pgnUrl , true ) ;
} else {
if ( LiveBroadcastDelay > 0 ) {
LiveBroadcastLastReceivedLocal = ( new Date ( ) ) . toLocaleString ( ) ;
if ( LiveBroadcastLastModifiedHeader = this _http _request . getResponseHeader ( "Last-Modified" ) ) {
LiveBroadcastLastModified = new Date ( LiveBroadcastLastModifiedHeader ) ;
} else { LiveBroadcastLastModified _Reset ( ) ; }
}
res = LOAD _PGN _OK ;
}
} else {
myAlert ( 'error: failed reading PGN URL\n' + pgnUrl , true ) ;
}
if ( LiveBroadcastDemo && ( res == LOAD _PGN _UNMODIFIED ) ) {
res = LOAD _PGN _OK ;
}
loadPgnCheckingLiveStatus ( res ) ;
}
var LOAD _PGN _FAIL = 0 ;
var LOAD _PGN _OK = 1 ;
var LOAD _PGN _UNMODIFIED = 2 ;
function loadPgnCheckingLiveStatus ( res ) {
switch ( res ) {
case LOAD _PGN _OK :
if ( LiveBroadcastDelay > 0 ) {
firstStart = true ;
var oldParseLastMoveError = ParseLastMoveError ;
if ( ! LiveBroadcastStarted ) {
LiveBroadcastStarted = true ;
} else {
var oldWhite = gameWhite [ currentGame ] ;
var oldBlack = gameBlack [ currentGame ] ;
var oldEvent = gameEvent [ currentGame ] ;
var oldRound = gameRound [ currentGame ] ;
var oldSite = gameSite [ currentGame ] ;
var oldDate = gameDate [ currentGame ] ;
initialGame = currentGame + 1 ;
LiveBroadcastOldCurrentVar = CurrentVar ;
LiveBroadcastOldCurrentPly = CurrentPly ;
LiveBroadcastOldCurrentPlyLast = ( CurrentVar === 0 && CurrentPly === StartPlyVar [ 0 ] + PlyNumberVar [ 0 ] ) ;
var oldAutoplay = isAutoPlayOn ;
if ( isAutoPlayOn ) { SetAutoPlay ( false ) ; }
LoadGameHeaders ( ) ;
LiveBroadcastFoundOldGame = false ;
for ( var ii = 0 ; ii < numberOfGames ; ii ++ ) {
LiveBroadcastFoundOldGame =
( gameWhite [ ii ] == oldWhite ) && ( gameBlack [ ii ] == oldBlack ) &&
( gameEvent [ ii ] == oldEvent ) && ( gameRound [ ii ] == oldRound ) &&
( gameSite [ ii ] == oldSite ) && ( gameDate [ ii ] == oldDate ) ;
if ( LiveBroadcastFoundOldGame ) { break ; }
}
if ( LiveBroadcastFoundOldGame ) { initialGame = ii + 1 ; }
if ( LiveBroadcastFoundOldGame ) {
var oldInitialVariation = initialVariation ;
var oldInitialHalfmove = initialHalfmove ;
initialVariation = CurrentVar ;
if ( LiveBroadcastSteppingMode ) {
initialHalfmove = ( LiveBroadcastOldCurrentPlyLast || oldParseLastMoveError ) ?
LiveBroadcastOldCurrentPly + 1 : LiveBroadcastOldCurrentPly ;
} else {
initialHalfmove = ( LiveBroadcastOldCurrentPlyLast || oldParseLastMoveError ) ?
"end" : LiveBroadcastOldCurrentPly ;
}
}
}
}
undoStackReset ( ) ;
Init ( ) ;
if ( LiveBroadcastDelay > 0 ) {
if ( LiveBroadcastFoundOldGame ) {
initialHalfmove = oldInitialHalfmove ;
initialVariation = oldInitialVariation ;
}
checkLiveBroadcastStatus ( ) ;
}
customFunctionOnPgnTextLoad ( ) ;
if ( LiveBroadcastDelay > 0 ) {
if ( LiveBroadcastFoundOldGame ) {
if ( LiveBroadcastSteppingMode ) {
if ( oldAutoplay || LiveBroadcastOldCurrentPlyLast || oldParseLastMoveError ) { SetAutoPlay ( true ) ; }
} else {
if ( oldAutoplay ) { SetAutoPlay ( true ) ; }
}
}
}
break ;
case LOAD _PGN _UNMODIFIED :
if ( LiveBroadcastDelay > 0 ) {
checkLiveBroadcastStatus ( ) ;
}
break ;
case LOAD _PGN _FAIL :
default :
if ( LiveBroadcastDelay === 0 ) {
pgnGameFromPgnText ( alertPgn ) ;
undoStackReset ( ) ;
Init ( ) ;
customFunctionOnPgnTextLoad ( ) ;
} else { // live broadcast: wait for live show start
if ( ! LiveBroadcastStarted ) {
pgnGameFromPgnText ( LiveBroadcastPlaceholderPgn ) ;
firstStart = true ;
undoStackReset ( ) ;
Init ( ) ;
checkLiveBroadcastStatus ( ) ;
customFunctionOnPgnTextLoad ( ) ;
} else { checkLiveBroadcastStatus ( ) ; }
}
break ;
}
if ( LiveBroadcastDelay > 0 ) { restartLiveBroadcastTimeout ( ) ; }
}
var http _request _last _id = 0 ;
function loadPgnFromPgnUrl ( pgnUrl ) {
LiveBroadcastLastRefreshedLocal = ( new Date ( ) ) . toLocaleString ( ) ;
var http _request = false ;
if ( window . XMLHttpRequest ) {
http _request = new XMLHttpRequest ( ) ;
if ( http _request . overrideMimeType ) {
http _request . overrideMimeType ( "text/plain" ) ;
// if pgnGameFromHttpRequest() deals with binary files, for those use:
// http_request.overrideMimeType("text/plain; charset=x-user-defined");
}
} else if ( window . ActiveXObject ) { // IE
try { http _request = new ActiveXObject ( "Msxml2.XMLHTTP" ) ; }
catch ( e ) {
try { http _request = new ActiveXObject ( "Microsoft.XMLHTTP" ) ; }
catch ( e ) {
myAlert ( 'error: XMLHttpRequest unavailable for PGN URL\n' + pgnUrl , true ) ;
return false ;
}
}
}
if ( ! http _request ) {
myAlert ( 'error: failed creating XMLHttpRequest for PGN URL\n' + pgnUrl , true ) ;
return false ;
}
var http _request _id = http _request _last _id ++ ;
http _request . onreadystatechange = function ( ) { updatePgnFromHttpRequest ( http _request , http _request _id ) ; } ;
try {
var randomizer = "" ;
// anti-caching #1
if ( ( LiveBroadcastDelay > 0 ) && ( pgnUrl . indexOf ( "?" ) == - 1 ) && ( pgnUrl . indexOf ( "#" ) == - 1 ) ) {
randomizer = "?noCache=" + ( 0x1000000000 + Math . floor ( ( Math . random ( ) * 0xF000000000 ) ) ) . toString ( 16 ) . toUpperCase ( ) ;
}
http _request . open ( "GET" , pgnUrl + randomizer ) ;
// anti-caching #2
if ( LiveBroadcastDelay > 0 ) {
http _request . setRequestHeader ( "If-Modified-Since" , LiveBroadcastLastModifiedHeader ) ;
}
http _request . send ( null ) ;
} catch ( e ) {
myAlert ( 'error: failed sending XMLHttpRequest for PGN URL\n' + pgnUrl , true ) ;
return false ;
}
return true ;
}
function SetPgnUrl ( url ) {
pgnUrl = url ;
}
function LiveBroadcastLastModified _Reset ( ) {
LiveBroadcastLastModified = new Date ( 0 ) ;
LiveBroadcastLastModifiedHeader = LiveBroadcastLastModified . toUTCString ( ) ;
}
function LiveBroadcastLastReceivedLocal _Reset ( ) {
LiveBroadcastLastReceivedLocal = 'unavailable' ;
}
function LiveBroadcastLastModified _ServerTime ( ) {
return LiveBroadcastLastModified . getTime ( ) === 0 ? 'unavailable' : LiveBroadcastLastModifiedHeader ;
}
function pauseLiveBroadcast ( ) {
if ( LiveBroadcastDelay === 0 ) { return ; }
LiveBroadcastPaused = true ;
clearTimeout ( LiveBroadcastInterval ) ;
LiveBroadcastInterval = null ;
}
function restartLiveBroadcast ( ) {
if ( LiveBroadcastDelay === 0 ) { return ; }
LiveBroadcastPaused = false ;
refreshPgnSource ( ) ;
}
function checkLiveBroadcastStatus ( ) {
var theTitle , theObj , ii ;
var tick = " " + ( LiveBroadcastTicker % 2 ? "<>" : "><" ) + " " ;
if ( LiveBroadcastDelay === 0 ) { return ; }
// broadcast started yet?
if ( LiveBroadcastStarted === false || typeof ( pgnHeader ) == "undefined" || ( numberOfGames == 1 && gameEvent [ 0 ] == LiveBroadcastPlaceholderEvent ) ) {
// no
LiveBroadcastEnded = false ;
LiveBroadcastGamesRunning = 0 ;
LiveBroadcastStatusString = "0 " + tick + " 0" ;
theTitle = "live broadcast yet to start" ;
} else {
// yes
var lbgr = 0 ;
for ( ii = 0 ; ii < numberOfGames ; ii ++ ) {
if ( gameResult [ ii ] . indexOf ( '*' ) >= 0 ) { lbgr ++ ; }
}
LiveBroadcastEnded = ( lbgr === 0 ) ;
LiveBroadcastGamesRunning = lbgr ;
LiveBroadcastStatusString = lbgr + " " + tick + " " + numberOfGames ;
theTitle = LiveBroadcastEnded ? "live broadcast ended" : lbgr + " live game" + ( lbgr > 1 ? "s" : "" ) + " out of " + numberOfGames ;
}
if ( theObj = document . getElementById ( "GameLiveStatus" ) ) {
theObj . innerHTML = LiveBroadcastStatusString ;
theObj . title = theTitle ;
}
if ( theObj = document . getElementById ( "GameLiveLastRefreshed" ) ) { theObj . innerHTML = LiveBroadcastLastRefreshedLocal ; }
if ( theObj = document . getElementById ( "GameLiveLastReceived" ) ) { theObj . innerHTML = LiveBroadcastLastReceivedLocal ; }
if ( theObj = document . getElementById ( "GameLiveLastModifiedServer" ) ) { theObj . innerHTML = LiveBroadcastLastModified _ServerTime ( ) ; }
customFunctionOnCheckLiveBroadcastStatus ( ) ;
}
function restartLiveBroadcastTimeout ( ) {
if ( LiveBroadcastDelay === 0 ) { return ; }
if ( LiveBroadcastInterval ) { clearTimeout ( LiveBroadcastInterval ) ; LiveBroadcastInterval = null ; }
if ( ( ! LiveBroadcastEnded ) && ( ! LiveBroadcastPaused ) ) {
LiveBroadcastInterval = setTimeout ( "refreshPgnSource()" , LiveBroadcastDelay * 60000 ) ;
}
LiveBroadcastTicker ++ ;
}
var LiveBroadcastFoundOldGame = false ;
var LiveBroadcastOldCurrentVar ;
var LiveBroadcastOldCurrentPly ;
var LiveBroadcastOldCurrentPlyLast = false ;
function refreshPgnSource ( ) {
if ( LiveBroadcastDelay === 0 ) { return ; }
if ( LiveBroadcastInterval ) { clearTimeout ( LiveBroadcastInterval ) ; LiveBroadcastInterval = null ; }
if ( LiveBroadcastDemo ) {
var newPly , addedPly = 0 ;
for ( var ii = 0 ; ii < numberOfGames ; ii ++ ) {
// 5% 15% 40%
newPly = [ 3 , 2 , 2 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ] [ Math . floor ( 20 * Math . random ( ) ) ] || 0 ;
if ( gameDemoMaxPly [ ii ] <= gameDemoLength [ ii ] ) {
gameDemoMaxPly [ ii ] += newPly ;
addedPly += newPly ;
}
}
if ( addedPly > 0 ) { LiveBroadcastLastReceivedLocal = ( new Date ( ) ) . toLocaleString ( ) ; }
}
if ( pgnUrl ) {
loadPgnFromPgnUrl ( pgnUrl ) ;
} else if ( document . getElementById ( "pgnText" ) ) {
loadPgnFromTextarea ( "pgnText" ) ;
} else {
pgnGameFromPgnText ( alertPgn ) ;
undoStackReset ( ) ;
Init ( ) ;
customFunctionOnPgnTextLoad ( ) ;
myAlert ( 'error: missing PGN URL location and pgnText object in the HTML file' , true ) ;
}
}
function loadPgnFromTextarea ( textareaId ) {
var res = LOAD _PGN _FAIL , text , theObj ;
LiveBroadcastLastRefreshedLocal = ( new Date ( ) ) . toLocaleString ( ) ;
if ( ! ( theObj = document . getElementById ( textareaId ) ) ) {
myAlert ( 'error: missing ' + textareaId + ' textarea object in the HTML file' , true ) ;
} else {
if ( document . getElementById ( textareaId ) . tagName . toLowerCase ( ) == "textarea" ) {
text = document . getElementById ( textareaId ) . value ;
} else { // compatibility with pgn4web up to 1.77: <span> used for pgnText
text = document . getElementById ( textareaId ) . innerHTML ;
// fixes browser issue removing \n from innerHTML
if ( text . indexOf ( '\n' ) < 0 ) { text = text . replace ( /((\[[^\[\]]*\]\s*)+)/g , "\n$1\n" ) ; }
// fixes browser issue replacing quotes with "
if ( text . indexOf ( '"' ) < 0 ) { text = text . replace ( /(")/g , '"' ) ; }
}
// no header: add emptyPgnHeader
if ( pgnHeaderTagRegExp . test ( text ) === false ) { text = emptyPgnHeader + "\n" + text ; }
if ( pgnGameFromPgnText ( text ) ) {
res = LOAD _PGN _OK ;
LiveBroadcastLastReceivedLocal = ( new Date ( ) ) . toLocaleString ( ) ;
} else {
myAlert ( 'error: no games found in ' + textareaId + ' object in the HTML file' ) ;
}
}
loadPgnCheckingLiveStatus ( res ) ;
}
function createBoard ( ) {
var theObj = document . getElementById ( "GameBoard" ) ;
if ( theObj ) {
theObj . innerHTML = '<DIV STYLE="font-size: small; font-family: sans-serif; ' +
'padding: 10px; text-align: center;">' +
'...loading PGN data<br />please wait...</DIV>' ;
}
if ( pgnUrl ) {
loadPgnFromPgnUrl ( pgnUrl ) ;
} else if ( document . getElementById ( "pgnText" ) ) {
loadPgnFromTextarea ( "pgnText" ) ;
} else {
pgnGameFromPgnText ( alertPgn ) ;
undoStackReset ( ) ;
Init ( ) ;
customFunctionOnPgnTextLoad ( ) ;
myAlert ( 'error: missing PGN URL location or pgnText in the HTML file' , true ) ;
}
}
function setCurrentGameFromInitialGame ( ) {
switch ( initialGame ) {
case "first" :
currentGame = 0 ;
break ;
case "last" :
currentGame = numberOfGames - 1 ;
break ;
case "random" :
currentGame = Math . floor ( Math . random ( ) * numberOfGames ) ;
break ;
default :
if ( isNaN ( parseInt ( initialGame , 10 ) ) ) {
currentGame = gameNumberSearchPgn ( initialGame , false , true ) ;
if ( ! currentGame ) { currentGame = 0 ; }
} else {
initialGame = parseInt ( initialGame , 10 ) ;
initialGame = initialGame < 0 ? - Math . floor ( - initialGame ) : Math . floor ( initialGame ) ;
if ( initialGame < - numberOfGames ) { currentGame = 0 ; }
else if ( initialGame < 0 ) { currentGame = numberOfGames + initialGame ; }
else if ( initialGame === 0 ) { currentGame = Math . floor ( Math . random ( ) * numberOfGames ) ; }
else if ( initialGame <= numberOfGames ) { currentGame = ( initialGame - 1 ) ; }
else { currentGame = numberOfGames - 1 ; }
}
break ;
}
}
function GoToInitialHalfmove ( ) {
var iv , ih ;
if ( initialVariation < 0 ) { iv = Math . max ( numberOfVars + initialVariations , 0 ) ; }
else { iv = Math . min ( initialVariation , numberOfVars - 1 ) ; }
switch ( initialHalfmove ) {
case "start" :
GoToMove ( 0 , iv ) ;
break ;
case "end" :
GoToMove ( StartPlyVar [ iv ] + PlyNumberVar [ iv ] , iv ) ;
break ;
case "random" :
GoToMove ( StartPlyVar [ iv ] + Math . floor ( Math . random ( ) * ( StartPlyVar [ iv ] + PlyNumberVar [ iv ] ) ) , iv ) ;
break ;
case "comment" :
case "variation" :
GoToMove ( 0 , iv ) ;
MoveToNextComment ( initialHalfmove == "variation" ) ;
break ;
default :
if ( isNaN ( initialHalfmove = parseInt ( initialHalfmove , 10 ) ) ) { initialHalfmove = 0 ; }
if ( initialHalfmove < 0 ) { ih = Math . max ( StartPlyVar [ iv ] + PlyNumberVar [ iv ] + 1 + initialHalfmove , 0 ) ; }
else { ih = Math . min ( initialHalfmove , StartPlyVar [ iv ] + PlyNumberVar [ iv ] ) ; }
GoToMove ( ih , iv ) ;
break ;
}
}
function Init ( nextGame ) {
if ( nextGame !== undefined ) {
if ( ( ! isNaN ( nextGame ) ) && ( nextGame >= 0 ) && ( nextGame < numberOfGames ) ) {
currentGame = parseInt ( nextGame , 10 ) ;
} else { return ; }
}
if ( isAutoPlayOn ) { SetAutoPlay ( false ) ; }
InitImages ( ) ;
if ( firstStart ) {
LoadGameHeaders ( ) ;
setCurrentGameFromInitialGame ( ) ;
}
if ( ( gameSetUp [ currentGame ] !== undefined ) && ( gameSetUp [ currentGame ] != "1" ) ) { InitFEN ( ) ; }
else { InitFEN ( gameFEN [ currentGame ] ) ; }
OpenGame ( currentGame ) ;
CurrentPly = StartPly ;
if ( firstStart || alwaysInitialHalfmove ) {
GoToInitialHalfmove ( ) ;
setTimeout ( "autoScrollToCurrentMoveIfEnabled();" , Math . min ( 666 , 0.9 * Delay ) ) ;
} else {
synchMoves ( ) ;
RefreshBoard ( ) ;
HighlightLastMove ( ) ;
autoScrollToCurrentMoveIfEnabled ( ) ;
// customFunctionOnMove here for consistency: null move starting new game
customFunctionOnMove ( ) ;
if ( typeof ( engineWinOnMove ) == "function" ) { engineWinOnMove ( ) ; }
}
if ( ( firstStart ) && ( autostartAutoplay ) ) { SetAutoPlay ( true ) ; }
customFunctionOnPgnGameLoad ( ) ;
initialVariation = 0 ;
firstStart = false ;
}
function myAlertFEN ( FenString , text ) {
myAlert ( "error: invalid FEN in game " + ( currentGame + 1 ) + ": " + text + "\n" + FenString , true ) ;
}
function InitFEN ( startingFEN ) {
var ii , jj , cc , color , castlingRookCol , fullMoveNumber ;
var FenString = typeof ( startingFEN ) != "string" ? FenStringStart :
startingFEN . replace ( /\\/g , "/" ) . replace ( /[^a-zA-Z0-9\s\/-]/g , " " ) . replace ( /(^\s*|\s*$)/g , "" ) . replace ( /\s+/g , " " ) ;
for ( ii = 0 ; ii < 8 ; ++ ii ) {
for ( jj = 0 ; jj < 8 ; ++ jj ) {
Board [ ii ] [ jj ] = 0 ;
}
}
StartPly = 0 ;
MoveCount = StartPly ;
MoveColor = StartPly % 2 ;
var newEnPassant = false ;
var newEnPassantCol ;
CastlingLong = [ 0 , 0 ] ;
CastlingShort = [ 7 , 7 ] ;
InitialHalfMoveClock = 0 ;
HistVar [ StartPly ] = 0 ;
HistNull [ StartPly ] = 0 ;
if ( FenString == FenStringStart ) {
for ( color = 0 ; color < 2 ; color ++ ) {
// K Q N B R p
PieceType [ color ] = [ 1 , 2 , 5 , 5 , 4 , 4 , 3 , 3 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 ] ;
PieceCol [ color ] = [ 4 , 3 , 1 , 6 , 2 , 5 , 0 , 7 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
PieceMoveCounter [ color ] = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
PieceRow [ color ] = color ? [ 7 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 ] :
[ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ] ;
for ( ii = 0 ; ii < 16 ; ii ++ ) {
var col = PieceCol [ color ] [ ii ] ;
var row = PieceRow [ color ] [ ii ] ;
Board [ col ] [ row ] = ( 1 - 2 * color ) * PieceType [ color ] [ ii ] ;
}
}
} else {
var kk , ll , nn , mm ;
for ( ii = 0 ; ii < 2 ; ii ++ ) {
for ( jj = 0 ; jj < 16 ; jj ++ ) {
PieceType [ ii ] [ jj ] = - 1 ;
PieceCol [ ii ] [ jj ] = 0 ;
PieceRow [ ii ] [ jj ] = 0 ;
PieceMoveCounter [ ii ] [ jj ] = 0 ;
}
}
ii = 0 ; jj = 7 ; ll = 0 ; nn = 1 ; mm = 1 ; cc = FenString . charAt ( ll ++ ) ;
while ( cc != " " ) {
if ( cc == "/" ) {
if ( ii != 8 ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
ii = 0 ;
jj -- ;
}
if ( ii == 8 ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
if ( ! isNaN ( cc ) ) {
ii += parseInt ( cc , 10 ) ;
if ( ( ii < 0 ) || ( ii > 8 ) ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
}
if ( cc . charCodeAt ( 0 ) == FenPieceName . toUpperCase ( ) . charCodeAt ( 0 ) ) {
if ( PieceType [ 0 ] [ 0 ] != - 1 ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
PieceType [ 0 ] [ 0 ] = 1 ;
PieceCol [ 0 ] [ 0 ] = ii ;
PieceRow [ 0 ] [ 0 ] = jj ;
ii ++ ;
}
if ( cc . charCodeAt ( 0 ) == FenPieceName . toLowerCase ( ) . charCodeAt ( 0 ) ) {
if ( PieceType [ 1 ] [ 0 ] != - 1 ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
PieceType [ 1 ] [ 0 ] = 1 ;
PieceCol [ 1 ] [ 0 ] = ii ;
PieceRow [ 1 ] [ 0 ] = jj ;
ii ++ ;
}
for ( kk = 1 ; kk < 6 ; kk ++ ) {
if ( cc . charCodeAt ( 0 ) == FenPieceName . toUpperCase ( ) . charCodeAt ( kk ) ) {
if ( nn == 16 ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
PieceType [ 0 ] [ nn ] = kk + 1 ;
PieceCol [ 0 ] [ nn ] = ii ;
PieceRow [ 0 ] [ nn ] = jj ;
nn ++ ;
ii ++ ;
}
if ( cc . charCodeAt ( 0 ) == FenPieceName . toLowerCase ( ) . charCodeAt ( kk ) ) {
if ( mm == 16 ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
PieceType [ 1 ] [ mm ] = kk + 1 ;
PieceCol [ 1 ] [ mm ] = ii ;
PieceRow [ 1 ] [ mm ] = jj ;
mm ++ ;
ii ++ ;
}
}
cc = ll < FenString . length ? FenString . charAt ( ll ++ ) : " " ;
}
if ( ( ii != 8 ) || ( jj !== 0 ) ) {
myAlertFEN ( FenString , "char " + ll ) ;
InitFEN ( ) ;
return ;
}
if ( ( PieceType [ 0 ] [ 0 ] == - 1 ) || ( PieceType [ 1 ] [ 0 ] == - 1 ) ) {
myAlertFEN ( FenString , "missing King" ) ;
InitFEN ( ) ;
return ;
}
if ( ll == FenString . length ) {
FenString += "w " + assumedCastleRights ( ) + " - 0 1" ;
}
cc = FenString . charAt ( ll ++ ) ;
if ( ( cc == "w" ) || ( cc == "b" ) ) {
if ( cc == "b" ) {
StartPly += 1 ;
MoveColor = 1 ;
}
} else {
myAlertFEN ( FenString , "invalid active color" ) ;
}
// set board
for ( color = 0 ; color < 2 ; ++ color ) {
for ( ii = 0 ; ii < 16 ; ii ++ ) {
if ( PieceType [ color ] [ ii ] != - 1 ) {
col = PieceCol [ color ] [ ii ] ;
row = PieceRow [ color ] [ ii ] ;
Board [ col ] [ row ] = ( 1 - 2 * color ) * ( PieceType [ color ] [ ii ] ) ;
}
}
}
ll ++ ;
if ( ll >= FenString . length ) {
myAlertFEN ( FenString , "missing castling availability" ) ;
FenString += " " + assumedCastleRights ( ) + " - 0 1" ;
ll ++ ;
}
CastlingLong = [ - 1 , - 1 ] ;
CastlingShort = [ - 1 , - 1 ] ;
cc = FenString . charAt ( ll ++ ) ;
while ( cc != " " ) {
if ( cc . charCodeAt ( 0 ) == FenPieceName . toUpperCase ( ) . charCodeAt ( 0 ) ) {
for ( CastlingShort [ 0 ] = 7 ; CastlingShort [ 0 ] > PieceCol [ 0 ] [ 0 ] ; CastlingShort [ 0 ] -- ) {
if ( Board [ CastlingShort [ 0 ] ] [ 0 ] == 3 ) { break ; }
}
if ( CastlingShort [ 0 ] <= PieceCol [ 0 ] [ 0 ] ) {
myAlertFEN ( FenString , "missing castling Rook " + cc ) ;
CastlingShort [ 0 ] = - 1 ;
}
}
if ( cc . charCodeAt ( 0 ) == FenPieceName . toUpperCase ( ) . charCodeAt ( 1 ) ) {
for ( CastlingLong [ 0 ] = 0 ; CastlingLong [ 0 ] < PieceCol [ 0 ] [ 0 ] ; CastlingLong [ 0 ] ++ ) {
if ( Board [ CastlingLong [ 0 ] ] [ 0 ] == 3 ) { break ; }
}
if ( CastlingLong [ 0 ] >= PieceCol [ 0 ] [ 0 ] ) {
myAlertFEN ( FenString , "missing castling Rook " + cc ) ;
CastlingLong [ 0 ] = - 1 ;
}
}
if ( cc . charCodeAt ( 0 ) == FenPieceName . toLowerCase ( ) . charCodeAt ( 0 ) ) {
for ( CastlingShort [ 1 ] = 7 ; CastlingShort [ 1 ] > PieceCol [ 1 ] [ 0 ] ; CastlingShort [ 1 ] -- ) {
if ( Board [ CastlingShort [ 1 ] ] [ 7 ] == - 3 ) { break ; }
}
if ( CastlingShort [ 1 ] <= PieceCol [ 1 ] [ 0 ] ) {
myAlertFEN ( FenString , "missing castling Rook " + cc ) ;
CastlingShort [ 1 ] = - 1 ;
}
}
if ( cc . charCodeAt ( 0 ) == FenPieceName . toLowerCase ( ) . charCodeAt ( 1 ) ) {
for ( CastlingLong [ 1 ] = 0 ; CastlingLong [ 1 ] < PieceCol [ 1 ] [ 0 ] ; CastlingLong [ 1 ] ++ ) {
if ( Board [ CastlingLong [ 1 ] ] [ 7 ] == - 3 ) { break ; }
}
if ( CastlingLong [ 1 ] >= PieceCol [ 1 ] [ 0 ] ) {
myAlertFEN ( FenString , "missing castling Rook " + cc ) ;
CastlingLong [ 1 ] = - 1 ;
}
}
castlingRookCol = columnsLetters . toUpperCase ( ) . indexOf ( cc ) ;
if ( castlingRookCol >= 0 ) { color = 0 ; }
else {
castlingRookCol = columnsLetters . toLowerCase ( ) . indexOf ( cc ) ;
if ( castlingRookCol >= 0 ) { color = 1 ; }
}
if ( castlingRookCol >= 0 ) {
if ( Board [ castlingRookCol ] [ color * 7 ] == ( 1 - 2 * color ) * 3 ) {
if ( castlingRookCol > PieceCol [ color ] [ 0 ] ) { CastlingShort [ color ] = castlingRookCol ; }
if ( castlingRookCol < PieceCol [ color ] [ 0 ] ) { CastlingLong [ color ] = castlingRookCol ; }
} else {
myAlertFEN ( FenString , "missing castling Rook " + cc ) ;
}
}
cc = ll < FenString . length ? FenString . charAt ( ll ++ ) : " " ;
}
if ( ll >= FenString . length ) {
myAlertFEN ( FenString , "missing en passant square" ) ;
FenString += " - 0 1" ;
ll ++ ;
}
cc = FenString . charAt ( ll ++ ) ;
while ( cc != " " ) {
if ( ( cc . charCodeAt ( 0 ) - 97 >= 0 ) && ( cc . charCodeAt ( 0 ) - 97 <= 7 ) ) {
newEnPassant = true ;
newEnPassantCol = cc . charCodeAt ( 0 ) - 97 ;
}
cc = ll < FenString . length ? FenString . charAt ( ll ++ ) : " " ;
}
if ( ll >= FenString . length ) {
myAlertFEN ( FenString , "missing halfmove clock" ) ;
FenString += " 0 1" ;
ll ++ ;
}
InitialHalfMoveClock = 0 ;
cc = FenString . charAt ( ll ++ ) ;
while ( cc != " " ) {
if ( isNaN ( cc ) ) {
myAlertFEN ( FenString , "invalid halfmove clock" ) ;
break ;
}
InitialHalfMoveClock = InitialHalfMoveClock * 10 + parseInt ( cc , 10 ) ;
cc = ll < FenString . length ? FenString . charAt ( ll ++ ) : " " ;
}
if ( ll >= FenString . length ) {
myAlertFEN ( FenString , "missing fullmove number" ) ;
FenString += " 1" ;
ll ++ ;
}
fullMoveNumber = 0 ;
cc = FenString . charAt ( ll ++ ) ;
while ( cc != " " ) {
if ( isNaN ( cc ) ) {
myAlertFEN ( FenString , "invalid fullmove number" ) ;
fullMoveNumber = 1 ;
break ;
}
fullMoveNumber = fullMoveNumber * 10 + parseInt ( cc , 10 ) ;
cc = ll < FenString . length ? FenString . charAt ( ll ++ ) : " " ;
}
if ( fullMoveNumber === 0 ) {
myAlertFEN ( FenString , "invalid fullmove 0 set to 1" ) ;
fullMoveNumber = 1 ;
}
StartPly += 2 * ( fullMoveNumber - 1 ) ;
HistEnPassant [ StartPly ] = newEnPassant ;
HistEnPassantCol [ StartPly ] = newEnPassantCol ;
HistNull [ StartPly ] = 0 ;
HistVar [ StartPly ] = 0 ;
}
}
// castling rights assuming Kings and Rooks starting positions as in normal chess
function assumedCastleRights ( ) {
var ii , rights = "" ;
if ( ( PieceRow [ 0 ] [ 0 ] === 0 ) && ( PieceCol [ 0 ] [ 0 ] === 4 ) ) {
for ( ii = 0 ; ii < PieceType [ 0 ] . length ; ii ++ ) {
if ( ( PieceType [ 0 ] [ ii ] === 3 ) && ( PieceRow [ 0 ] [ ii ] === 0 ) && ( PieceCol [ 0 ] [ ii ] === 7 ) ) {
rights += FenPieceName . charAt ( 0 ) . toUpperCase ( ) ;
}
if ( ( PieceType [ 0 ] [ ii ] === 3 ) && ( PieceRow [ 0 ] [ ii ] === 0 ) && ( PieceCol [ 0 ] [ ii ] === 0 ) ) {
rights += FenPieceName . charAt ( 1 ) . toUpperCase ( ) ;
}
}
}
if ( ( PieceRow [ 1 ] [ 0 ] === 7 ) && ( PieceCol [ 1 ] [ 0 ] === 4 ) ) {
for ( ii = 0 ; ii < PieceType [ 1 ] . length ; ii ++ ) {
if ( ( PieceType [ 1 ] [ ii ] === 3 ) && ( PieceRow [ 1 ] [ ii ] === 7 ) && ( PieceCol [ 1 ] [ ii ] === 7 ) ) {
rights += FenPieceName . charAt ( 0 ) . toLowerCase ( ) ;
}
if ( ( PieceType [ 1 ] [ ii ] === 3 ) && ( PieceRow [ 1 ] [ ii ] === 7 ) && ( PieceCol [ 1 ] [ ii ] === 0 ) ) {
rights += FenPieceName . charAt ( 1 ) . toLowerCase ( ) ;
}
}
}
return rights || "-" ;
}
function SetImageType ( extension ) {
imageType = extension ;
}
function InitImages ( ) {
if ( ImagePathOld === ImagePath ) { return ; }
if ( ( ImagePath . length > 0 ) && ( ImagePath [ ImagePath . length - 1 ] != '/' ) ) {
ImagePath += '/' ;
}
ClearImg = new Image ( ) ;
ClearImg . src = ImagePath + 'clear.' + imageType ;
var ColorName = new Array ( "w" , "b" ) ;
var PiecePrefix = new Array ( "k" , "q" , "r" , "b" , "n" , "p" ) ;
for ( var c = 0 ; c < 2 ; ++ c ) {
for ( var p = 1 ; p < 7 ; p ++ ) {
PieceImg [ c ] [ p ] = new Image ( ) ;
PieceImg [ c ] [ p ] . src = ImagePath + ColorName [ c ] + PiecePrefix [ p - 1 ] + '.' + imageType ;
}
}
ImagePathOld = ImagePath ;
}
function IsCheck ( col , row , color ) {
var ii , jj ;
var sign = 2 * color - 1 ; // white or black
// other king giving check?
if ( ( Math . abs ( PieceCol [ 1 - color ] [ 0 ] - col ) <= 1 ) &&
( Math . abs ( PieceRow [ 1 - color ] [ 0 ] - row ) <= 1 ) ) { return true ; }
// knight?
for ( ii = - 2 ; ii <= 2 ; ii += 4 ) {
for ( jj = - 1 ; jj <= 1 ; jj += 2 ) {
if ( SquareOnBoard ( col + ii , row + jj ) ) {
if ( Board [ col + ii ] [ row + jj ] == sign * 5 ) { return true ; }
}
if ( SquareOnBoard ( col + jj , row + ii ) ) {
if ( Board [ col + jj ] [ row + ii ] == sign * 5 ) { return true ; }
}
}
}
// pawn?
for ( ii = - 1 ; ii <= 1 ; ii += 2 ) {
if ( SquareOnBoard ( col + ii , row - sign ) ) {
if ( Board [ col + ii ] [ row - sign ] == sign * 6 ) { return true ; }
}
}
// queens, rooks, bishops?
for ( ii = - 1 ; ii <= 1 ; ++ ii ) {
for ( jj = - 1 ; jj <= 1 ; ++ jj ) {
if ( ( ii !== 0 ) || ( jj !== 0 ) ) {
var checkCol = col + ii ;
var checkRow = row + jj ;
var thisPiece = 0 ;
while ( SquareOnBoard ( checkCol , checkRow ) && ( thisPiece === 0 ) ) {
thisPiece = Board [ checkCol ] [ checkRow ] ;
if ( thisPiece === 0 ) {
checkCol += ii ;
checkRow += jj ;
} else {
if ( thisPiece == sign * 2 ) { return true ; }
if ( ( thisPiece == sign * 3 ) && ( ( ii === 0 ) || ( jj === 0 ) ) ) { return true ; }
if ( ( thisPiece == sign * 4 ) && ( ( ii !== 0 ) && ( jj !== 0 ) ) ) { return true ; }
}
}
}
}
}
return false ;
}
function fixRegExp ( exp ) {
return exp . replace ( /([\[\]\(\)\{\}\.\*\+\^\$\|\?\\])/g , "\\$1" ) ;
}
function LoadGameHeaders ( ) {
var ii ;
var parse ;
gameEvent . length = gameSite . length = gameRound . length = gameDate . length = 0 ;
gameWhite . length = gameBlack . length = gameResult . length = 0 ;
gameSetUp . length = gameFEN . length = 0 ;
gameInitialWhiteClock . length = gameInitialBlackClock . length = 0 ;
gameVariant . length = 0 ;
pgnHeaderTagRegExpGlobal . lastIndex = 0 ; // resets global regular expression
for ( ii = 0 ; ii < numberOfGames ; ++ ii ) {
var ss = pgnHeader [ ii ] ;
gameEvent [ ii ] = gameSite [ ii ] = gameRound [ ii ] = gameDate [ ii ] = "" ;
gameWhite [ ii ] = gameBlack [ ii ] = gameResult [ ii ] = "" ;
gameInitialWhiteClock [ ii ] = gameInitialBlackClock [ ii ] = "" ;
gameVariant [ ii ] = "" ;
while ( parse = pgnHeaderTagRegExpGlobal . exec ( ss ) ) {
switch ( parse [ 1 ] ) {
case 'Event' : gameEvent [ ii ] = parse [ 2 ] ; break ;
case 'Site' : gameSite [ ii ] = parse [ 2 ] ; break ;
case 'Round' : gameRound [ ii ] = parse [ 2 ] ; break ;
case 'Date' : gameDate [ ii ] = parse [ 2 ] ; break ;
case 'White' : gameWhite [ ii ] = parse [ 2 ] ; break ;
case 'Black' : gameBlack [ ii ] = parse [ 2 ] ; break ;
case 'Result' : gameResult [ ii ] = parse [ 2 ] ; break ;
case 'SetUp' : gameSetUp [ ii ] = parse [ 2 ] ; break ;
case 'FEN' : gameFEN [ ii ] = parse [ 2 ] ; break ;
case 'WhiteClock' : gameInitialWhiteClock [ ii ] = parse [ 2 ] ; break ;
case 'BlackClock' : gameInitialBlackClock [ ii ] = parse [ 2 ] ; break ;
case 'Variant' : gameVariant [ ii ] = parse [ 2 ] ; break ;
default : break ;
}
}
}
if ( ( LiveBroadcastDemo ) && ( numberOfGames > 0 ) ) {
for ( ii = 0 ; ii < numberOfGames ; ++ ii ) {
if ( ( gameDemoLength [ ii ] === undefined ) || ( gameDemoLength [ ii ] === 0 ) ) {
InitFEN ( gameFEN [ ii ] ) ;
ParsePGNGameString ( pgnGame [ ii ] ) ;
gameDemoLength [ ii ] = PlyNumber ;
}
if ( gameDemoMaxPly [ ii ] === undefined ) { gameDemoMaxPly [ ii ] = 0 ; }
if ( gameDemoMaxPly [ ii ] <= gameDemoLength [ ii ] ) { gameResult [ ii ] = '*' ; }
}
}
return ;
}
function MoveBackward ( diff , scanOnly ) {
// CurrentPly counts from 1, starting position 0
var goFromPly = CurrentPly - 1 ;
var goToPly = goFromPly - diff ;
if ( goToPly < StartPly ) { goToPly = StartPly - 1 ; }
// reconstruct old position
for ( var thisPly = goFromPly ; thisPly > goToPly ; -- thisPly ) {
CurrentPly -- ;
MoveColor = 1 - MoveColor ;
CurrentVar = HistVar [ thisPly ] ;
UndoMove ( thisPly ) ;
}
if ( scanOnly ) { return ; }
synchMoves ( ) ;
// old position reconstructed: refresh board
RefreshBoard ( ) ;
HighlightLastMove ( ) ;
autoScrollToCurrentMoveIfEnabled ( ) ;
// autoplay: restart timeout
if ( AutoPlayInterval ) { clearTimeout ( AutoPlayInterval ) ; AutoPlayInterval = null ; }
if ( isAutoPlayOn ) {
if ( goToPly >= StartPlyVar [ CurrentVar ] ) { AutoPlayInterval = setTimeout ( "MoveBackward(1)" , Delay ) ; }
else { SetAutoPlay ( false ) ; }
}
customFunctionOnMove ( ) ;
if ( typeof ( engineWinOnMove ) == "function" ) { engineWinOnMove ( ) ; }
}
function MoveForward ( diff , targetVar , scanOnly ) {
var nextVar , nextVarStartPly , move , text ;
var oldVar = - 1 ;
if ( typeof ( targetVar ) == "undefined" ) { targetVar = CurrentVar ; }
// CurrentPly counts from 1, starting position 0
var goToPly = CurrentPly + parseInt ( diff , 10 ) ;
if ( goToPly > StartPlyVar [ targetVar ] + PlyNumberVar [ targetVar ] ) {
goToPly = StartPlyVar [ targetVar ] + PlyNumberVar [ targetVar ] ;
}
// reach to selected move checking legality
for ( var thisPly = CurrentPly ; thisPly < goToPly ; ++ thisPly ) {
if ( targetVar !== CurrentVar ) {
for ( var ii = 0 ; ii < PredecessorsVars [ targetVar ] . length ; ii ++ ) {
if ( PredecessorsVars [ targetVar ] [ ii ] === CurrentVar ) { break ; }
}
if ( ii === PredecessorsVars [ targetVar ] . length ) {
myAlert ( "error: unknown path to variation " + targetVar + " from " + CurrentVar + " in game " + ( currentGame + 1 ) , true ) ;
return ;
} else {
nextVarStartPly = StartPlyVar [ PredecessorsVars [ targetVar ] [ ii + 1 ] ] ;
for ( ii = ii + 1 ; ii < PredecessorsVars [ targetVar ] . length - 1 ; ii ++ ) {
if ( StartPlyVar [ PredecessorsVars [ targetVar ] [ ii + 1 ] ] !== StartPlyVar [ PredecessorsVars [ targetVar ] [ ii ] ] ) { break ; }
}
nextVar = PredecessorsVars [ targetVar ] [ ii ] ;
}
} else { nextVar = nextVarStartPly = - 1 ; }
if ( thisPly === nextVarStartPly ) {
oldVar = CurrentVar ;
CurrentVar = nextVar ;
}
if ( typeof ( move = MovesVar [ CurrentVar ] [ thisPly ] ) == "undefined" ) { break ; }
if ( ParseLastMoveError = ! ParseMove ( move , thisPly ) ) {
text = ( Math . floor ( thisPly / 2 ) + 1 ) + ( ( thisPly % 2 ) === 0 ? '. ' : '... ' ) ;
myAlert ( 'error: invalid ply ' + text + move + ' in game ' + ( currentGame + 1 ) + ' variation ' + CurrentVar , true ) ;
if ( thisPly === nextVarStartPly ) { CurrentVar = oldVar ; }
break ;
}
MoveColor = 1 - MoveColor ;
}
// new position: update ply count, then refresh board
CurrentPly = thisPly ;
if ( scanOnly ) { return ; }
synchMoves ( ) ;
RefreshBoard ( ) ;
HighlightLastMove ( ) ;
autoScrollToCurrentMoveIfEnabled ( ) ;
// autoplay: restart timeout
if ( AutoPlayInterval ) { clearTimeout ( AutoPlayInterval ) ; AutoPlayInterval = null ; }
if ( ParseLastMoveError ) { SetAutoPlay ( false ) ; }
else if ( thisPly == goToPly ) {
if ( isAutoPlayOn ) {
if ( goToPly < StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ) {
AutoPlayInterval = setTimeout ( "MoveForward(1)" , Delay ) ;
} else {
if ( autoplayNextGame && ( CurrentVar === 0 ) ) { AutoPlayInterval = setTimeout ( "AutoplayNextGame()" , Delay ) ; }
else { SetAutoPlay ( false ) ; }
}
}
}
customFunctionOnMove ( ) ;
if ( typeof ( engineWinOnMove ) == "function" ) { engineWinOnMove ( ) ; }
}
var lastSynchCurrentVar = - 1 ;
function synchMoves ( ) {
var start , end ;
if ( CurrentVar === lastSynchCurrentVar ) { return ; }
Moves = new Array ( ) ;
MoveComments = new Array ( ) ;
for ( var ii = 0 ; ii < PredecessorsVars [ CurrentVar ] . length ; ii ++ ) {
start = StartPlyVar [ PredecessorsVars [ CurrentVar ] [ ii ] ] ;
if ( ii < PredecessorsVars [ CurrentVar ] . length - 1 ) {
end = StartPlyVar [ PredecessorsVars [ CurrentVar ] [ ii + 1 ] ] ;
} else {
end = StartPlyVar [ PredecessorsVars [ CurrentVar ] [ ii ] ] + PlyNumberVar [ PredecessorsVars [ CurrentVar ] [ ii ] ] ;
}
for ( var jj = start ; jj < end ; jj ++ ) {
Moves [ jj ] = MovesVar [ PredecessorsVars [ CurrentVar ] [ ii ] ] [ jj ] ;
MoveComments [ jj ] = MoveCommentsVar [ PredecessorsVars [ CurrentVar ] [ ii ] ] [ jj ] || "" ;
}
}
MoveComments [ jj ] = MoveCommentsVar [ PredecessorsVars [ CurrentVar ] [ ii - 1 ] ] [ jj ] || "" ;
PlyNumber = StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] - StartPly ;
lastSynchCurrentVar = CurrentVar ;
}
function AutoplayNextGame ( ) {
if ( fatalErrorNumSinceReset === 0 ) {
if ( numberOfGames > 0 ) {
Init ( ( currentGame + 1 ) % numberOfGames ) ;
if ( ( numberOfGames > 1 ) || ( PlyNumber > 0 ) ) {
SetAutoPlay ( true ) ;
return ;
}
}
}
SetAutoPlay ( false ) ;
}
function MoveToNextComment ( varOnly ) {
for ( var ii = CurrentPly + 1 ; ii <= StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ; ii ++ ) {
if ( MoveComments [ ii ] . match ( pgn4webVariationRegExp ) || ( ! varOnly && strippedMoveComment ( ii ) ) ) { GoToMove ( ii ) ; break ; }
}
}
function MoveToPrevComment ( varOnly ) {
for ( var ii = ( CurrentPly - 1 ) ; ii >= StartPly ; ii -- ) {
if ( ( ii > 0 || CurrentVar > 0 ) && ii === StartPlyVar [ HistVar [ ii + 1 ] ] ) { GoToMove ( ii + 1 , HistVar [ ii ] ) ; break ; }
if ( MoveComments [ ii ] . match ( pgn4webVariationRegExp ) || ( ! varOnly && strippedMoveComment ( ii ) ) ) { GoToMove ( ii ) ; break ; }
}
}
function OpenGame ( gameId ) {
ParsePGNGameString ( pgnGame [ gameId ] ) ;
currentGame = gameId ;
ParseLastMoveError = false ;
if ( LiveBroadcastDemo ) {
if ( gameDemoMaxPly [ gameId ] <= PlyNumber ) { PlyNumber = PlyNumberVar [ 0 ] = gameDemoMaxPly [ gameId ] ; }
}
PrintHTML ( ) ;
}
var CurrentVar = - 1 ;
var lastVarWithNoMoves ;
var numberOfVars ;
var MovesVar ;
var MoveCommentsVar ;
var GameHasComments ;
var GameHasVariations ;
var StartPlyVar ;
var PlyNumberVar ;
var CurrentVarStack ;
var PlyNumberStack ;
var PredecessorsVars ;
function initVar ( ) {
MovesVar = new Array ( ) ;
MoveCommentsVar = new Array ( ) ;
GameHasComments = false ;
GameHasVariations = false ;
StartPlyVar = new Array ( ) ;
PlyNumberVar = new Array ( ) ;
CurrentVar = - 1 ;
lastVarWithNoMoves = [ false ] ;
numberOfVars = 0 ;
CurrentVarStack = new Array ( ) ;
PlyNumber = 1 ;
PlyNumberStack = new Array ( ) ;
PredecessorsVars = new Array ( ) ;
startVar ( false ) ;
}
function startVar ( isContinuation ) {
if ( CurrentVar >= 0 ) {
CurrentVarStack . push ( CurrentVar ) ;
PlyNumberStack . push ( PlyNumber ) ;
}
CurrentVar = numberOfVars ++ ;
PredecessorsVars [ CurrentVar ] = CurrentVarStack . slice ( 0 ) ;
PredecessorsVars [ CurrentVar ] . push ( CurrentVar ) ;
MovesVar [ CurrentVar ] = new Array ( ) ;
MoveCommentsVar [ CurrentVar ] = new Array ( ) ;
if ( ! isContinuation ) {
if ( lastVarWithNoMoves [ lastVarWithNoMoves . length - 1 ] ) {
myAlert ( "warning: malformed PGN data in game " + ( currentGame + 1 ) + ": variant " + CurrentVar + " starting before parent" , true ) ;
} else {
PlyNumber -= 1 ;
}
}
lastVarWithNoMoves . push ( true ) ;
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] = "" ;
StartPlyVar [ CurrentVar ] = StartPly + PlyNumber ;
}
function closeVar ( ) {
if ( StartPly + PlyNumber === StartPlyVar [ CurrentVar ] ) {
myAlert ( "warning: empty variation " + CurrentVar + " in game " + ( currentGame + 1 ) , false ) ;
} else {
GameHasVariations = true ;
}
lastVarWithNoMoves . pop ( ) ;
PlyNumberVar [ CurrentVar ] = StartPly + PlyNumber - StartPlyVar [ CurrentVar ] ;
for ( var ii = StartPlyVar [ CurrentVar ] ; ii <= StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ; ii ++ ) {
if ( MoveCommentsVar [ CurrentVar ] [ ii ] ) {
MoveCommentsVar [ CurrentVar ] [ ii ] = MoveCommentsVar [ CurrentVar ] [ ii ] . replace ( /\s+/g , ' ' ) ;
MoveCommentsVar [ CurrentVar ] [ ii ] = translateNAGs ( MoveCommentsVar [ CurrentVar ] [ ii ] ) ;
MoveCommentsVar [ CurrentVar ] [ ii ] = MoveCommentsVar [ CurrentVar ] [ ii ] . replace ( /\s+$/g , '' ) ;
} else {
MoveCommentsVar [ CurrentVar ] [ ii ] = '' ;
}
}
if ( CurrentVarStack . length ) {
CurrentVar = CurrentVarStack . pop ( ) ;
PlyNumber = PlyNumberStack . pop ( ) ;
} else {
myAlert ( "error: closeVar error" + " in game " + ( currentGame + 1 ) , true ) ;
}
}
function childrenVars ( thisPly , thisVar ) {
if ( typeof ( thisVar ) == "undefined" ) { thisVar = CurrentVar ; }
if ( typeof ( thisPly ) == "undefined" ) { thisPly = CurrentPly ; }
var children = new Array ( ) ;
for ( var ii = thisVar ; ii < numberOfVars ; ii ++ ) {
if ( ( ii === thisVar && StartPlyVar [ ii ] + PlyNumberVar [ ii ] > thisPly ) || ( realParentVar ( ii ) === thisVar && StartPlyVar [ ii ] === thisPly && PlyNumberVar [ ii ] > 0 ) ) {
children . push ( ii ) ;
}
}
return children ;
}
function realParentVar ( childVar ) {
for ( var ii = PredecessorsVars [ childVar ] . length - 1 ; ii > 0 ; ii -- ) {
if ( StartPlyVar [ PredecessorsVars [ childVar ] [ ii ] ] !== StartPlyVar [ PredecessorsVars [ childVar ] [ ii - 1 ] ] ) {
return PredecessorsVars [ childVar ] [ ii - 1 ] ;
}
}
return PredecessorsVars [ childVar ] [ ii ] ;
}
function goToNextVariationSibling ( ) {
if ( CurrentPly === StartPly ) { return false ; }
var siblings = childrenVars ( CurrentPly - 1 , HistVar [ CurrentPly - 1 ] ) ;
if ( siblings . length < 2 ) { return false ; }
for ( var ii = 0 ; ii < siblings . length ; ii ++ ) {
if ( siblings [ ii ] === CurrentVar ) { break ; }
}
if ( siblings [ ii ] !== CurrentVar ) { return false ; }
GoToMove ( CurrentPly , siblings [ ( ii + 1 ) % siblings . length ] ) ;
return true ;
}
function goToFirstChild ( ) {
var children = childrenVars ( CurrentPly , CurrentVar ) ;
if ( children . length < 1 ) { return false ; }
if ( children [ 0 ] === CurrentVar ) {
if ( children . length < 2 ) { return false ; }
GoToMove ( CurrentPly + 1 , children [ 1 ] ) ;
} else {
GoToMove ( CurrentPly + 1 , children [ 0 ] ) ;
}
return true ;
}
function ParsePGNGameString ( gameString ) {
var ii , start , end , move , moveCount , needle , commentStart , commentEnd , isContinuation ;
var ssRep , ss = gameString , ssComm ;
ss = ss . replace ( pgn4webVariationRegExpGlobal , "[%_pgn4web_variation_ $1]" ) ;
// empty variations to comments
while ( ( ssRep = ss . replace ( /\((([\?!+#\s]|\$\d+|{[^}]*})*)\)/g , ' $1 ' ) ) !== ss ) { ss = ssRep ; }
ss = ss . replace ( /^\s/ , '' ) ;
ss = ss . replace ( /\s$/ , '' ) ;
initVar ( ) ;
PlyNumber = 0 ;
for ( start = 0 ; start < ss . length ; start ++ ) {
switch ( ss . charAt ( start ) ) {
case ' ' :
case '\b' :
case '\f' :
case '\n' :
case '\r' :
case '\t' :
break ;
case '$' :
commentStart = start ;
commentEnd = commentStart + 1 ;
while ( '0123456789' . indexOf ( ss . charAt ( commentEnd ) ) >= 0 ) {
commentEnd ++ ;
if ( commentEnd >= ss . length ) { break ; }
}
if ( MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] ) { MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ' ' ; }
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += translateNAGs ( ss . substring ( commentStart , commentEnd ) . replace ( /(^\s*|\s*$)/ , '' ) ) ;
start = commentEnd - 1 ;
break ;
case '!' :
case '?' :
commentStart = start ;
commentEnd = commentStart + 1 ;
while ( '!?' . indexOf ( ss . charAt ( commentEnd ) ) >= 0 ) {
commentEnd ++ ;
if ( commentEnd >= ss . length ) { break ; }
}
if ( MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] ) { MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ' ' ; }
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ss . substring ( commentStart , commentEnd ) ;
start = commentEnd - 1 ;
break ;
case '{' :
commentStart = start + 1 ;
commentEnd = ss . indexOf ( '}' , start + 1 ) ;
if ( commentEnd < 0 ) {
myAlert ( 'error: missing end comment } in game ' + ( currentGame + 1 ) , true ) ;
commentEnd = ss . length ;
}
if ( MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] ) { MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ' ' ; }
ssComm = translateNAGs ( ss . substring ( commentStart , commentEnd ) . replace ( /(^\s*|\s*$)/ , '' ) ) ;
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ssComm ;
GameHasComments = GameHasComments || ssComm . replace ( /\[%[^\]]*\]\s*/g , '' ) . replace ( basicNAGs , '' ) . replace ( /^\s+$/ , '' ) !== '' ;
start = commentEnd ;
break ;
case '%' :
// % must be first char of the line
if ( ( start > 0 ) && ( ss . charAt ( start - 1 ) != '\n' ) ) { break ; }
commentStart = start + 1 ;
commentEnd = ss . indexOf ( '\n' , start + 1 ) ;
if ( commentEnd < 0 ) { commentEnd = ss . length ; }
start = commentEnd ;
break ;
case ';' :
commentStart = start + 1 ;
commentEnd = ss . indexOf ( '\n' , start + 1 ) ;
if ( commentEnd < 0 ) { commentEnd = ss . length ; }
if ( MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] ) { MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ' ' ; }
ssComm = translateNAGs ( ss . substring ( commentStart , commentEnd ) . replace ( /(^\s*|\s*$)/ , '' ) ) ;
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ssComm ;
GameHasComments = GameHasComments || ssComm . replace ( /\[%[^\]]*\]\s*/g , '' ) . replace ( basicNAGs , '' ) . replace ( /^\s+$/ , '' ) !== '' ;
start = commentEnd ;
break ;
case '(' :
if ( isContinuation = ( ss . charAt ( start + 1 ) == '*' ) ) { start += 1 ; }
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ' [%pgn4web_variation ' + numberOfVars + '] ' ;
startVar ( isContinuation ) ;
break ;
case ')' :
closeVar ( ) ;
break ;
case '&' : // nullmove "<>" became "<>"
if ( ss . substr ( start , 8 ) == "<>" ) {
ss = ss . slice ( 0 , start ) + " -- " + ss . slice ( start + 8 ) ;
start += 4 ;
break ;
}
// dont add "break;"
default :
needle = new Array ( '1-0' , '0-1' , '1/2-1/2' , '*' ) ;
for ( ii = 0 ; ii < needle . length ; ii ++ ) {
if ( ss . indexOf ( needle [ ii ] , start ) == start ) {
if ( CurrentVar === 0 ) { end = ss . length ; }
else {
end = start + needle [ ii ] . length ;
if ( MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] ) { MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += ' ' ; }
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] += needle [ ii ] ;
}
start = end ;
break ;
}
}
if ( start == ss . length ) { break ; }
moveCount = Math . floor ( ( StartPly + PlyNumber ) / 2 ) + 1 ;
needle = moveCount . toString ( ) ;
if ( ss . indexOf ( needle , start ) == start ) {
start += needle . length ;
while ( ' .\n\r' . indexOf ( ss . charAt ( start ) ) != - 1 ) { start ++ ; }
}
if ( ( end = start + ss . substr ( start ) . search ( /[\s${;!?()]/ ) ) < start ) { end = ss . length ; }
move = ss . substring ( start , end ) ;
MovesVar [ CurrentVar ] [ StartPly + PlyNumber ] = CleanMove ( move ) ;
lastVarWithNoMoves [ lastVarWithNoMoves . length - 1 ] = false ;
if ( ss . charAt ( end ) == ' ' ) { start = end ; }
else { start = end - 1 ; }
if ( ! MovesVar [ CurrentVar ] [ StartPly + PlyNumber ] . match ( /^[\s+#]*$/ ) ) { // to cope with malsformed PGN data
PlyNumber ++ ;
MoveCommentsVar [ CurrentVar ] [ StartPly + PlyNumber ] = '' ;
}
break ;
}
}
if ( CurrentVar !== 0 ) {
myAlert ( "error: ParsePGNGameString ends with CurrentVar " + CurrentVar + " in game " + ( currentGame + 1 ) , true ) ;
while ( CurrentVar > 0 ) { closeVar ( ) ; }
}
StartPlyVar [ 0 ] = StartPly ;
PlyNumberVar [ 0 ] = PlyNumber ;
GameHasComments = GameHasComments || GameHasVariations ;
lastSynchCurrentVar = - 1 ;
}
var NAGstyle = 'default' ;
var NAG = new Array ( ) ;
NAG [ 0 ] = '' ;
NAG [ 1 ] = '!' ; // 'good move';
NAG [ 2 ] = '?' ; // 'bad move';
NAG [ 3 ] = '!!' ; // 'very good move';
NAG [ 4 ] = '??' ; // 'very bad move';
NAG [ 5 ] = '!?' ; // 'speculative move';
NAG [ 6 ] = '?!' ; // 'questionable move';
NAG [ 7 ] = 'forced move' ; // '[]';
NAG [ 8 ] = 'singular move' ; // '[]';
NAG [ 9 ] = 'worst move' ; // '??';
NAG [ 10 ] = 'drawish position' ; // '=';
NAG [ 11 ] = 'equal chances, quiet position' ; // '=';
NAG [ 12 ] = 'equal chances, active position' ; // '=';
NAG [ 13 ] = 'unclear position' ; // '~~';
NAG [ 14 ] = 'White has a slight advantage' ; // NAG[15] = '+/=';
NAG [ 16 ] = 'White has a moderate advantage' ; // NAG[17] = '+/-';
NAG [ 18 ] = 'White has a decisive advantage' ; // NAG[19] = '+-';
NAG [ 20 ] = 'White has a crushing advantage' ; // NAG[21] = '+-';
NAG [ 22 ] = 'White is in zugzwang' ; // NAG[23] = '(.)';
NAG [ 24 ] = 'White has a slight space advantage' ; // NAG[25] = '()';
NAG [ 26 ] = 'White has a moderate space advantage' ; // NAG[27] = '()';
NAG [ 28 ] = 'White has a decisive space advantage' ; // NAG[29] = '()';
NAG [ 30 ] = 'White has a slight time (development) advantage' ; // NAG[31] = '@';
NAG [ 32 ] = 'White has a moderate time (development) advantage' ; // NAG[33] = '@';
NAG [ 34 ] = 'White has a decisive time (development) advantage' ; // NAG[35] = '@';
NAG [ 36 ] = 'White has the initiative' ; // NAG[37] = '|^';
NAG [ 38 ] = 'White has a lasting initiative' ; // NAG[39] = '|^';
NAG [ 40 ] = 'White has the attack' ; // NAG[41] = '->';
NAG [ 42 ] = 'White has insufficient compensation for material deficit' ;
NAG [ 44 ] = 'White has sufficient compensation for material deficit' ; // NAG[45] = '=/~';
NAG [ 46 ] = 'White has more than adequate compensation for material deficit' ; // NAG[47] = '=/~';
NAG [ 48 ] = 'White has a slight center control advantage' ; // NAG[49] = '[+]';
NAG [ 50 ] = 'White has a moderate center control advantage' ; // NAG[51] = '[+]';
NAG [ 52 ] = 'White has a decisive center control advantage' ; // NAG[53] = '[+]';
NAG [ 54 ] = 'White has a slight kingside control advantage' ; // NAG[55] = '>>';
NAG [ 56 ] = 'White has a moderate kingside control advantage' ; // NAG[57] = '>>';
NAG [ 58 ] = 'White has a decisive kingside control advantage' ; // NAG[59] = '>>';
NAG [ 60 ] = 'White has a slight queenside control advantage' ; // NAG[61] = '<<';
NAG [ 62 ] = 'White has a moderate queenside control advantage' ; // NAG[63] = '<<';
NAG [ 64 ] = 'White has a decisive queenside control advantage' ; // NAG[65] = '<<';
NAG [ 66 ] = 'White has a vulnerable first rank' ;
NAG [ 68 ] = 'White has a well protected first rank' ;
NAG [ 70 ] = 'White has a poorly protected king' ;
NAG [ 72 ] = 'White has a well protected king' ;
NAG [ 74 ] = 'White has a poorly placed king' ;
NAG [ 76 ] = 'White has a well placed king' ;
NAG [ 78 ] = 'White has a very weak pawn structure' ;
NAG [ 80 ] = 'White has a moderately weak pawn structure' ;
NAG [ 82 ] = 'White has a moderately strong pawn structure' ;
NAG [ 84 ] = 'White has a very strong pawn structure' ;
NAG [ 86 ] = 'White has poor knight placement' ;
NAG [ 88 ] = 'White has good knight placement' ;
NAG [ 90 ] = 'White has poor bishop placement' ;
NAG [ 92 ] = 'White has good bishop placement' ;
NAG [ 94 ] = 'White has poor rook placement' ;
NAG [ 96 ] = 'White has good rook placement' ;
NAG [ 98 ] = 'White has poor queen placement' ;
NAG [ 100 ] = 'White has good queen placement' ;
NAG [ 102 ] = 'White has poor piece coordination' ;
NAG [ 104 ] = 'White has good piece coordination' ;
NAG [ 106 ] = 'White has played the opening very poorly' ;
NAG [ 108 ] = 'White has played the opening poorly' ;
NAG [ 110 ] = 'White has played the opening well' ;
NAG [ 112 ] = 'White has played the opening very well' ;
NAG [ 114 ] = 'White has played the middlegame very poorly' ;
NAG [ 116 ] = 'White has played the middlegame poorly' ;
NAG [ 118 ] = 'White has played the middlegame well' ;
NAG [ 120 ] = 'White has played the middlegame very well' ;
NAG [ 122 ] = 'White has played the ending very poorly' ;
NAG [ 124 ] = 'White has played the ending poorly' ;
NAG [ 126 ] = 'White has played the ending well' ;
NAG [ 128 ] = 'White has played the ending very well' ;
NAG [ 130 ] = 'White has slight counterplay' ; // NAG[131] = '<=>';
NAG [ 132 ] = 'White has moderate counterplay' ; // NAG[133] = '<=>';
NAG [ 134 ] = 'White has decisive counterplay' ; // NAG[135] = '<=>';
NAG [ 136 ] = 'White has moderate time control pressure' ; // NAG[137] = '(+)';
NAG [ 138 ] = 'White has severe time control pressure' ; // NAG[139] = '(+)';
for ( i = 14 ; i < 139 ; i += 2 ) { NAG [ i + 1 ] = NAG [ i ] . replace ( "White" , "Black" ) ; }
function translateNAGs ( comment ) {
var matches = comment . match ( /\$+[0-9]+/g ) ;
if ( matches ) {
for ( var ii = 0 ; ii < matches . length ; ii ++ ) {
var nag = matches [ ii ] . substr ( 1 ) ;
if ( NAG [ nag ] !== undefined ) {
comment = comment . replace ( new RegExp ( "\\$+" + nag + "(?!\\d)" ) , NAG [ nag ] ) ;
}
}
}
return comment ;
}
function ParseMove ( move , plyCount ) {
var ii , ll ;
var rem ;
var toRowMarker = - 1 ;
castleRook = - 1 ;
mvIsCastling = 0 ;
mvIsPromotion = 0 ;
mvCapture = 0 ;
mvFromCol = - 1 ;
mvFromRow = - 1 ;
mvToCol = - 1 ;
mvToRow = - 1 ;
mvPiece = - 1 ;
mvPieceId = - 1 ;
mvPieceOnTo = - 1 ;
mvCaptured = - 1 ;
mvCapturedId = - 1 ;
mvIsNull = 0 ;
if ( typeof ( move ) == "undefined" ) { return false ; }
HistEnPassant [ plyCount + 1 ] = false ;
HistEnPassantCol [ plyCount + 1 ] = - 1 ;
if ( move . indexOf ( '--' ) === 0 ) {
mvIsNull = 1 ;
CheckLegality ( '--' , plyCount ) ;
return true ;
}
// get destination column/row remembering what's left e.g. Rdxc3 exf8=Q#
for ( ii = move . length - 1 ; ii > 0 ; ii -- ) {
if ( ! isNaN ( move . charAt ( ii ) ) ) {
mvToCol = move . charCodeAt ( ii - 1 ) - 97 ;
mvToRow = move . charAt ( ii ) - 1 ;
rem = move . substring ( 0 , ii - 1 ) ;
toRowMarker = ii ;
break ;
}
}
// final square did not make sense: maybe a castle?
if ( ( mvToCol < 0 ) || ( mvToCol > 7 ) || ( mvToRow < 0 ) || ( mvToRow > 7 ) ) {
// long castling first: looking for o-o will get o-o-o too
if ( move . indexOf ( 'O-O-O' ) === 0 ) {
mvIsCastling = 1 ;
mvPiece = 1 ;
mvPieceId = 0 ;
mvPieceOnTo = 1 ;
mvFromCol = 4 ;
mvToCol = 2 ;
mvFromRow = 7 * MoveColor ;
mvToRow = 7 * MoveColor ;
return CheckLegality ( 'O-O-O' , plyCount ) ;
} else if ( move . indexOf ( 'O-O' ) === 0 ) {
mvIsCastling = 1 ;
mvPiece = 1 ;
mvPieceId = 0 ;
mvPieceOnTo = 1 ;
mvFromCol = 4 ;
mvToCol = 6 ;
mvFromRow = 7 * MoveColor ;
mvToRow = 7 * MoveColor ;
return CheckLegality ( 'O-O' , plyCount ) ;
} else { return false ; }
}
rem = rem . replace ( /-/g , '' ) ;
// get piece and origin square: mark captures ('x' is there)
ll = rem . length ;
if ( ll > 4 ) { return false ; }
mvPiece = - 1 ; // make sure mvPiece is properly assigned later
if ( ll === 0 ) { mvPiece = 6 ; }
else {
for ( ii = 5 ; ii > 0 ; ii -- ) { if ( rem . charAt ( 0 ) == PieceCode [ ii - 1 ] ) { mvPiece = ii ; break ; } }
if ( mvPiece == - 1 ) { if ( columnsLetters . toLowerCase ( ) . indexOf ( rem . charAt ( 0 ) ) >= 0 ) { mvPiece = 6 ; } }
if ( mvPiece == - 1 ) { return false ; }
if ( rem . charAt ( ll - 1 ) == 'x' ) { mvCapture = 1 ; }
if ( isNaN ( move . charAt ( ll - 1 - mvCapture ) ) ) {
mvFromCol = move . charCodeAt ( ll - 1 - mvCapture ) - 97 ;
if ( ( mvFromCol < 0 ) || ( mvFromCol > 7 ) ) { mvFromCol = - 1 ; }
} else {
mvFromRow = move . charAt ( ll - 1 - mvCapture ) - 1 ;
if ( ( mvFromRow < 0 ) || ( mvFromRow > 7 ) ) { mvFromRow = - 1 ; }
else {
mvFromCol = move . charCodeAt ( ll - 2 - mvCapture ) - 97 ;
if ( ( mvFromCol < 0 ) || ( mvFromCol > 7 ) ) { mvFromCol = - 1 ; }
}
}
if ( ( ll > 1 ) && ( ! mvCapture ) && ( mvFromCol == - 1 ) && ( mvFromRow == - 1 ) ) { return false ; }
if ( ( mvPiece == 6 ) && ( ! mvCapture ) && ( mvFromCol == - 1 ) && ( mvFromRow == - 1 ) ) { return false ; }
}
mvPieceOnTo = mvPiece ;
// "square to" occupied: capture (note en-passant case)
if ( ( Board [ mvToCol ] [ mvToRow ] !== 0 ) ||
( ( mvPiece == 6 ) &&
( HistEnPassant [ plyCount ] ) &&
( mvToCol == HistEnPassantCol [ plyCount ] ) &&
( mvToRow == 5 - 3 * MoveColor ) ) )
{ mvCapture = 1 ; }
if ( mvPiece == 6 ) {
// move contains '=' or char after destination row: might be a promotion
ii = move . indexOf ( '=' ) ;
if ( ii < 0 ) { ii = toRowMarker ; }
if ( ( ii > 0 ) && ( ii < move . length - 1 ) ) {
var newPiece = move . charAt ( ii + 1 ) ;
if ( newPiece == PieceCode [ 1 ] ) { mvPieceOnTo = 2 ; }
else if ( newPiece == PieceCode [ 2 ] ) { mvPieceOnTo = 3 ; }
else if ( newPiece == PieceCode [ 3 ] ) { mvPieceOnTo = 4 ; }
else if ( newPiece == PieceCode [ 4 ] ) { mvPieceOnTo = 5 ; }
if ( mvPieceOnTo != mvPiece ) { mvIsPromotion = 1 ; }
}
if ( ( mvToRow == 7 * ( 1 - MoveColor ) ) ? ! mvIsPromotion : mvIsPromotion ) { return false ; }
}
// which captured piece: if nothing found must be en-passant
if ( mvCapture ) {
for ( mvCapturedId = 15 ; mvCapturedId >= 0 ; mvCapturedId -- ) {
if ( ( PieceType [ 1 - MoveColor ] [ mvCapturedId ] > 0 ) &&
( PieceCol [ 1 - MoveColor ] [ mvCapturedId ] == mvToCol ) &&
( PieceRow [ 1 - MoveColor ] [ mvCapturedId ] == mvToRow ) ) {
mvCaptured = PieceType [ 1 - MoveColor ] [ mvCapturedId ] ;
if ( mvCaptured == 1 ) { return false ; }
break ;
}
}
if ( ( mvPiece == 6 ) && ( mvCapturedId < 1 ) && ( HistEnPassant [ plyCount ] ) ) {
for ( mvCapturedId = 15 ; mvCapturedId >= 0 ; mvCapturedId -- ) {
if ( ( PieceType [ 1 - MoveColor ] [ mvCapturedId ] == 6 ) &&
( PieceCol [ 1 - MoveColor ] [ mvCapturedId ] == mvToCol ) &&
( PieceRow [ 1 - MoveColor ] [ mvCapturedId ] == 4 - MoveColor ) ) {
mvCaptured = PieceType [ 1 - MoveColor ] [ mvCapturedId ] ;
break ;
}
}
}
}
// check move legality
if ( ! CheckLegality ( PieceCode [ mvPiece - 1 ] , plyCount ) ) { return false ; }
// pawn moved: check en-passant possibility
if ( mvPiece == 6 ) {
if ( Math . abs ( HistRow [ 0 ] [ plyCount ] - mvToRow ) == 2 ) {
HistEnPassant [ plyCount + 1 ] = true ;
HistEnPassantCol [ plyCount + 1 ] = mvToCol ;
}
}
return true ;
}
function SetGameSelectorOptions ( head , num , chEvent , chSite , chRound , chWhite , chBlack , chResult , chDate ) {
if ( typeof ( head ) == "string" ) { gameSelectorHead = head ; }
gameSelectorNum = ( num === true ) ;
gameSelectorChEvent = Math . max ( Math . min ( chEvent , 32 ) || 0 , 0 ) || 0 ;
gameSelectorChSite = Math . max ( Math . min ( chSite , 32 ) || 0 , 0 ) || 0 ;
gameSelectorChRound = Math . max ( Math . min ( chRound , 32 ) || 0 , 0 ) || 0 ;
gameSelectorChWhite = Math . max ( Math . min ( chWhite , 32 ) || 0 , 0 ) || 0 ;
gameSelectorChBlack = Math . max ( Math . min ( chBlack , 32 ) || 0 , 0 ) || 0 ;
gameSelectorChResult = Math . max ( Math . min ( chResult , 32 ) || 0 , 0 ) || 0 ;
gameSelectorChDate = Math . max ( Math . min ( chDate , 32 ) || 0 , 0 ) || 0 ;
}
var clickedSquareInterval = null ;
function clickedSquare ( ii , jj ) {
if ( clickedSquareInterval ) { return ; } // dont trigger twice
var squareId = 'tcol' + jj + 'trow' + ii ;
var theObj = document . getElementById ( squareId ) ;
if ( theObj ) {
var oldClass = theObj . className ;
theObj . className = ( ii + jj ) % 2 === 0 ? "blackSquare" : "whiteSquare" ;
clickedSquareInterval = setTimeout ( "reset_after_click(" + ii + "," + jj + ",'" + oldClass + "','" + theObj . className + "')" , 66 ) ;
clearSelectedText ( ) ;
}
}
function reset _after _click ( ii , jj , oldClass , newClass ) {
var theObj = document . getElementById ( 'tcol' + jj + 'trow' + ii ) ;
if ( theObj ) {
// square class changed again by pgn4web already: dont touch it anymore e.g. autoplay
if ( theObj . className == newClass ) { theObj . className = oldClass ; }
clickedSquareInterval = null ;
}
}
var lastSearchPgnExpression = "" ;
function gameNumberSearchPgn ( searchExpression , backward , includeCurrent ) {
lastSearchPgnExpression = searchExpression ;
if ( searchExpression === "" ) { return false ; }
// replace newline with spaces so that we can use regexp "." on whole game
var newlinesRegExp = new RegExp ( "[\n\r]" , "gm" ) ;
var searchExpressionRegExp = new RegExp ( searchExpression , "im" ) ;
// at start currentGame might still be -1
var thisCurrentGame = ( currentGame < 0 ) || ( currentGame >= numberOfGames ) ? 0 : currentGame ;
var needle = fullPgnGame ( thisCurrentGame ) ;
if ( includeCurrent && needle . replace ( newlinesRegExp , " " ) . match ( searchExpressionRegExp ) ) {
return thisCurrentGame ;
}
var delta = backward ? - 1 : + 1 ;
for ( var thisGame = ( thisCurrentGame + delta + numberOfGames ) % numberOfGames ;
thisGame != thisCurrentGame ;
thisGame = ( thisGame + delta + numberOfGames ) % numberOfGames ) {
needle = fullPgnGame ( thisGame ) ;
if ( needle . replace ( newlinesRegExp , " " ) . match ( searchExpressionRegExp ) ) {
return thisGame ;
}
}
return false ;
}
function searchPgnGame ( searchExpression , backward ) {
if ( typeof ( searchExpression ) == "undefined" ) { searchExpression = "" ; }
lastSearchPgnExpression = searchExpression ;
var theObj = document . getElementById ( 'searchPgnExpression' ) ;
if ( theObj ) { theObj . value = searchExpression ; }
if ( ( searchExpression === "" ) || ( numberOfGames < 2 ) ) { return ; }
var thisGame = gameNumberSearchPgn ( searchExpression , backward , false ) ;
if ( ( thisGame !== false ) && ( thisGame != currentGame ) ) { Init ( thisGame ) ; }
}
function searchPgnGamePrompt ( ) {
if ( numberOfGames < 2 ) {
alert ( "info: search prompt disabled with less than 2 games" ) ;
return ;
}
var searchExpression = prompt ( "Please enter search pattern for PGN games:" , lastSearchPgnExpression ) ;
if ( searchExpression ) { searchPgnGame ( searchExpression ) ; }
}
function searchPgnGameForm ( ) {
var theObj = document . getElementById ( 'searchPgnExpression' ) ;
if ( theObj ) { searchPgnGame ( document . getElementById ( 'searchPgnExpression' ) . value ) ; }
}
var chessMovesRegExp = new RegExp ( "\\b((\\d+(\\.{1,3}|\\s)\\s*)?((([KQRBN][a-h1-8]?)|[a-h])?x?[a-h][1-8](=[QRNB])?|O-O-O|O-O)\\b[!?+#]*)" , "g" ) ;
function fixCommentForDisplay ( comment ) {
return comment . replace ( chessMovesRegExp , '<SPAN CLASS="commentMove">$1</SPAN>' ) ;
}
var tableSize = 0 ;
var textSelectOptions = '' ;
function PrintHTML ( ) {
var ii , jj , text , theObj , squareId , imageId , squareCoord , squareTitle , numText , textSO ;
// chessboard
if ( theObj = document . getElementById ( "GameBoard" ) ) {
text = '<TABLE CLASS="boardTable" ID="boardTable" CELLSPACING=0 CELLPADDING=0' ;
text += ( tableSize > 0 ) ? ' STYLE="width: ' + tableSize + 'px; height: ' + tableSize + 'px;">' : '>' ;
for ( ii = 0 ; ii < 8 ; ++ ii ) {
text += '<TR>' ;
for ( jj = 0 ; jj < 8 ; ++ jj ) {
squareId = 'tcol' + jj + 'trow' + ii ;
imageId = 'img_' + squareId ;
text += ( ii + jj ) % 2 === 0 ?
'<TD CLASS="whiteSquare" ID="' + squareId + '" BGCOLOR="#FFFFFF"' :
'<TD CLASS="blackSquare" ID="' + squareId + '" BGCOLOR="#D3D3D3"' ;
text += ' ALIGN="center" VALIGN="middle" ONCLICK="clickedSquare(' + ii + ',' + jj + ')">' ;
squareCoord = IsRotated ? String . fromCharCode ( 72 - jj , 49 + ii ) : String . fromCharCode ( jj + 65 , 56 - ii ) ;
squareTitle = squareCoord ;
if ( boardTitle [ jj ] [ ii ] !== '' ) { squareTitle += ': ' + boardTitle [ jj ] [ ii ] ; }
text += '<IMG SRC="' + ClearImg . src + '" ' +
'CLASS="pieceImage" STYLE="border: none; display: block; vertical-align: middle;" ' +
'ONCLICK="boardOnClick[' + jj + '][' + ii + '](this, event);" ' +
'ID="' + imageId + '" TITLE="' + squareTitle + '" ' + 'ONFOCUS="this.blur()" />' +
'</TD>' ;
}
text += '</TR>' ;
}
text += '</TABLE>' ;
theObj . innerHTML = text ;
}
if ( theObj = document . getElementById ( "boardTable" ) ) {
tableSize = theObj . offsetWidth ;
if ( tableSize > 0 ) { // coping with browser always returning 0 to offsetWidth
theObj . style . height = tableSize + "px" ;
}
}
// control buttons
if ( theObj = document . getElementById ( "GameButtons" ) ) {
var numButtons = 5 ;
var spaceSize = 3 ;
var buttonSize = ( tableSize - spaceSize * ( numButtons - 1 ) ) / numButtons ;
text = '<FORM NAME="GameButtonsForm" STYLE="display:inline;">' +
'<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">' +
'<TR><TD>' +
'<INPUT ID="startButton" TYPE="BUTTON" VALUE="<<" STYLE="' ;
if ( buttonSize > 0 ) { text += 'width: ' + buttonSize + 'px;' ; }
text += '"; CLASS="buttonControl" TITLE="go to game start" ' +
' ID="btnGoToStart" onClick="clickedBbtn(this,event);" ONFOCUS="this.blur();">' +
'</TD>' +
'<TD CLASS="buttonControlSpace" WIDTH="' + spaceSize + '">' +
'</TD><TD>' +
'<INPUT ID="backButton" TYPE="BUTTON" VALUE="<" STYLE="' ;
if ( buttonSize > 0 ) { text += 'width: ' + buttonSize + 'px;' ; }
text += '"; CLASS="buttonControl" TITLE="move backward" ' +
' ID="btnMoveBackward1" onClick="clickedBbtn(this,event);" ONFOCUS="this.blur();">' +
'</TD>' +
'<TD CLASS="buttonControlSpace" WIDTH="' + spaceSize + '">' +
'</TD><TD>' ;
text += '<INPUT ID="autoplayButton" TYPE="BUTTON" VALUE=' +
( isAutoPlayOn ? "=" : "+" ) + ' STYLE="' ;
if ( buttonSize > 0 ) { text += 'width: ' + buttonSize + 'px;' ; }
text += isAutoPlayOn ?
'"; CLASS="buttonControlStop" TITLE="toggle autoplay (stop)" ' :
'"; CLASS="buttonControlPlay" TITLE="toggle autoplay (start)" ' ;
text += ' ID="btnPlay" NAME="AutoPlay" onClick="clickedBbtn(this,event);" ONFOCUS="this.blur();">' +
'</TD>' +
'<TD CLASS="buttonControlSpace" WIDTH="' + spaceSize + '">' +
'</TD><TD>' +
'<INPUT ID="forwardButton" TYPE="BUTTON" VALUE=">" STYLE="' ;
if ( buttonSize > 0 ) { text += 'width: ' + buttonSize + 'px;' ; }
text += '"; CLASS="buttonControl" TITLE="move forward" ' +
' ID="btnMoveForward1" onClick="clickedBbtn(this,event);" ONFOCUS="this.blur();">' +
'</TD>' +
'<TD CLASS="buttonControlSpace" WIDTH="' + spaceSize + '">' +
'</TD><TD>' +
'<INPUT ID="endButton" TYPE="BUTTON" VALUE=">>" STYLE="' ;
if ( buttonSize > 0 ) { text += 'width: ' + buttonSize + 'px;' ; }
text += '"; CLASS="buttonControl" TITLE="go to game end" ' +
' ID="btnGoToEnd" onClick="clickedBbtn(this,event);" ONFOCUS="this.blur();">' +
'</TD></TR></TABLE></FORM>' ;
theObj . innerHTML = text ;
}
// game selector
if ( theObj = document . getElementById ( "GameSelector" ) ) {
if ( firstStart ) { textSelectOptions = '' ; }
if ( numberOfGames < 2 ) {
// theObj.innerHTML = ''; // replaced with code below to cope with IE bug
while ( theObj . firstChild ) { theObj . removeChild ( theObj . firstChild ) ; }
textSelectOptions = '' ;
} else {
if ( textSelectOptions === '' ) {
if ( gameSelectorNum ) { gameSelectorNumLenght = Math . floor ( Math . log ( numberOfGames ) / Math . log ( 10 ) ) + 1 ; }
text = '<FORM NAME="GameSel" STYLE="display:inline;"> ' +
'<SELECT ID="GameSelSelect" NAME="GameSelSelect" STYLE="' ;
if ( tableSize > 0 ) { text += 'width: ' + tableSize + 'px; ' ; }
text += 'font-family: monospace;" CLASS="selectControl" TITLE="select a game" ' +
'ONCHANGE="this.blur(); if (this.value >= 0) { Init(this.value); this.value = -1; }" ' +
'ONFOCUS="disableShortcutKeysAndStoreStatus();" ONBLUR="restoreShortcutKeysStatus();" ' +
'> ' +
'<OPTION CLASS="optionSelectControl" value=-1>' ;
var blanks = '' ; for ( ii = 0 ; ii < 32 ; ii ++ ) { blanks += ' ' ; }
var headDisplay = ( gameSelectorNum ? blanks . substring ( 0 , gameSelectorNumLenght ) + ' ' : '' ) + gameSelectorHead ;
text += headDisplay . replace ( / /g , ' ' ) ;
for ( ii = 0 ; ii < numberOfGames ; ii ++ ) {
textSelectOptions += '<OPTION CLASS="optionSelectControl" value=' + ii + '>' ;
textSO = '' ;
if ( gameSelectorNum ) {
numText = ' ' + ( ii + 1 ) ;
textSO += blanks . substr ( 0 , gameSelectorNumLenght - ( numText . length - 1 ) ) +
numText + ' ' ;
}
if ( gameSelectorChEvent > 0 ) {
textSO += ' ' + gameEvent [ ii ] . substring ( 0 , gameSelectorChEvent ) +
blanks . substr ( 0 , gameSelectorChEvent - gameEvent [ ii ] . length ) + ' ' ;
}
if ( gameSelectorChSite > 0 ) {
textSO += ' ' + gameSite [ ii ] . substring ( 0 , gameSelectorChSite ) +
blanks . substr ( 0 , gameSelectorChSite - gameSite [ ii ] . length ) + ' ' ;
}
if ( gameSelectorChRound > 0 ) {
textSO += ' ' + blanks . substr ( 0 , gameSelectorChRound - gameRound [ ii ] . length ) +
gameRound [ ii ] . substring ( 0 , gameSelectorChRound ) + ' ' ;
}
if ( gameSelectorChWhite > 0 ) {
textSO += ' ' + gameWhite [ ii ] . substring ( 0 , gameSelectorChWhite ) +
blanks . substr ( 0 , gameSelectorChWhite - gameWhite [ ii ] . length ) + ' ' ;
}
if ( gameSelectorChBlack > 0 ) {
textSO += ' ' + gameBlack [ ii ] . substring ( 0 , gameSelectorChBlack ) +
blanks . substr ( 0 , gameSelectorChBlack - gameBlack [ ii ] . length ) + ' ' ;
}
if ( gameSelectorChResult > 0 ) {
textSO += ' ' + gameResult [ ii ] . substring ( 0 , gameSelectorChResult ) +
blanks . substr ( 0 , gameSelectorChResult - gameResult [ ii ] . length ) + ' ' ;
}
if ( gameSelectorChDate > 0 ) {
textSO += ' ' + gameDate [ ii ] . substring ( 0 , gameSelectorChDate ) +
blanks . substr ( 0 , gameSelectorChDate - gameDate [ ii ] . length ) + ' ' ;
}
textSelectOptions += textSO . replace ( / /g , ' ' ) ;
}
text += textSelectOptions . replace ( /&(amp|lt|gt);/g , '&$1;' ) + '</SELECT></FORM>' ; // see function simpleHtmlentities()
theObj . innerHTML = text ;
}
}
}
// game event
if ( theObj = document . getElementById ( "GameEvent" ) )
{ theObj . innerHTML = gameEvent [ currentGame ] ; }
// game round
if ( theObj = document . getElementById ( "GameRound" ) )
{ theObj . innerHTML = gameRound [ currentGame ] ; }
// game site
if ( theObj = document . getElementById ( "GameSite" ) )
{ theObj . innerHTML = gameSite [ currentGame ] ; }
// game date
if ( theObj = document . getElementById ( "GameDate" ) ) {
theObj . innerHTML = gameDate [ currentGame ] ;
theObj . style . whiteSpace = "nowrap" ;
}
// game white
if ( theObj = document . getElementById ( "GameWhite" ) )
{ theObj . innerHTML = gameWhite [ currentGame ] ; }
// game black
if ( theObj = document . getElementById ( "GameBlack" ) )
{ theObj . innerHTML = gameBlack [ currentGame ] ; }
// game result
if ( theObj = document . getElementById ( "GameResult" ) ) {
theObj . innerHTML = gameResult [ currentGame ] ;
theObj . style . whiteSpace = "nowrap" ;
}
// game text
if ( theObj = document . getElementById ( "GameText" ) ) {
variationTextDepth = - 1 ;
text = '<SPAN ID="ShowPgnText">' + variationTextFromId ( 0 ) ; + '</SPAN>' ;
theObj . innerHTML = text ;
}
setB1C1F1G1boardShortcuts ( ) ; // depend on presence of comments
// game searchbox
if ( ( theObj = document . getElementById ( "GameSearch" ) ) && firstStart ) {
if ( numberOfGames < 2 ) {
// theObj.innerHTML = ''; // replaced with code below to cope with IE bug
while ( theObj . firstChild ) { theObj . removeChild ( theObj . firstChild ) ; }
} else {
text = '<FORM ID="searchPgnForm" STYLE="display: inline;" ' +
'ACTION="javascript:searchPgnGameForm();">' ;
text += '<INPUT ID="searchPgnButton" CLASS="searchPgnButton" STYLE="display: inline; ' ;
if ( tableSize > 0 ) { text += 'width: ' + ( tableSize / 4 ) + 'px; ' ; }
text += '" TITLE="find games matching the search string (regular expression)" ' +
'TYPE="submit" VALUE="?">' +
'<INPUT ID="searchPgnExpression" CLASS="searchPgnExpression" ' +
'TITLE="find games matching the search string (regular expression)" ' +
'TYPE="input" VALUE="" STYLE="display: inline; box-sizing: border-box; ' +
'-moz-box-sizing: border-box; -webkit-box-sizing: border-box;' ; // remove when non-prefixed option supported by firefox and safari
if ( tableSize > 0 ) { text += 'width: ' + ( 3 * tableSize / 4 ) + 'px; ' ; }
text += '" ONFOCUS="disableShortcutKeysAndStoreStatus();" ONBLUR="restoreShortcutKeysStatus();">' ;
text += '</FORM>' ;
theObj . innerHTML = text ;
theObj = document . getElementById ( 'searchPgnExpression' ) ;
if ( theObj ) { theObj . value = lastSearchPgnExpression ; }
}
}
}
function startButton ( e ) {
if ( e . shiftKey ) {
GoToMove ( StartPlyVar [ CurrentVar ] + ( CurrentPly <= StartPlyVar [ CurrentVar ] + 1 ? 0 : 1 ) ) ;
} else { GoToMove ( StartPlyVar [ 0 ] , 0 ) ; }
}
function backButton ( e ) {
if ( e . shiftKey ) { GoToMove ( StartPlyVar [ CurrentVar ] ) ; }
else { GoToMove ( CurrentPly - 1 ) ; }
}
function forwardButton ( e ) {
if ( e . shiftKey ) { if ( ! goToNextVariationSibling ( ) ) { GoToMove ( CurrentPly + 1 ) ; } }
else { GoToMove ( CurrentPly + 1 ) ; }
}
function endButton ( e ) {
if ( e . shiftKey ) {
if ( CurrentPly === StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ) { goToFirstChild ( ) ; }
else { GoToMove ( StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ) ; }
} else { GoToMove ( StartPlyVar [ 0 ] + PlyNumberVar [ 0 ] , 0 ) ; }
}
function clickedBbtn ( t , e ) {
switch ( t . id ) {
case "startButton" :
startButton ( e ) ;
break ;
case "backButton" :
backButton ( e ) ;
break ;
case "autoplayButton" :
if ( e . shiftKey ) { goToNextVariationSibling ( ) ; }
else { SwitchAutoPlay ( ) ; }
break ;
case "forwardButton" :
forwardButton ( e ) ;
break ;
case "endButton" :
endButton ( e ) ;
break ;
default :
break ;
}
}
var basicNAGs = /^[\?!+#\s]+(\s|$)/ ;
function strippedMoveComment ( plyNum , varId , addHtmlTags ) {
if ( typeof ( addHtmlTags ) == "undefined" ) { addHtmlTags = false ; }
if ( typeof ( varId ) == "undefined" ) { varId = CurrentVar ; }
if ( ! MoveCommentsVar [ varId ] [ plyNum ] ) { return "" ; }
return fixCommentForDisplay ( MoveCommentsVar [ varId ] [ plyNum ] ) . replace ( pgn4webVariationRegExpGlobal , function ( m ) { return variationTextFromTag ( m , addHtmlTags ) ; } ) . replace ( /\[%[^\]]*\]\s*/g , '' ) . replace ( basicNAGs , '' ) . replace ( /^\s+$/ , '' ) ;
}
function basicNAGsMoveComment ( plyNum , varId ) {
if ( typeof ( varId ) == "undefined" ) { varId = CurrentVar ; }
if ( ! MoveCommentsVar [ varId ] [ plyNum ] ) { return "" ; }
var thisBasicNAGs = MoveCommentsVar [ varId ] [ plyNum ] . replace ( /\[%[^\]]*\]\s*/g , '' ) . match ( basicNAGs , '' ) ;
return thisBasicNAGs ? thisBasicNAGs [ 0 ] . replace ( /\s+(?!class=)/gi , '' ) : '' ;
}
function variationTextFromTag ( variationTag , addHtmlTags ) {
if ( typeof ( addHtmlTags ) == "undefined" ) { addHtmlTags = false ; }
var varId = variationTag . replace ( pgn4webVariationRegExp , "$1" ) ;
if ( isNaN ( varId ) ) {
myAlert ( "error: issue parsing variation tag " + variationTag + " in game " + ( currentGame + 1 ) , true ) ;
return "" ;
}
var text = variationTextFromId ( varId ) ;
if ( text ) {
if ( addHtmlTags ) { text = '</SPAN>' + text + '<SPAN CLASS="comment">' ; }
} else { text = '' ; }
return text ;
}
var variationTextDepth , printedComment , printedVariation ;
function variationTextFromId ( varId ) {
var punctChars = ",.;:!?" , thisComment ;
if ( isNaN ( varId ) || varId < 0 || varId >= numberOfVars || typeof ( StartPlyVar [ varId ] ) == "undefined" || typeof ( PlyNumberVar [ varId ] ) == "undefined" ) {
myAlert ( "error: issue parsing variation id " + varId + " in game " + ( currentGame + 1 ) , true ) ;
return "" ;
}
var text = ++ variationTextDepth ? ( '<SPAN CLASS="variation">' + ( printedVariation ? ' ' : '' ) + ( variationTextDepth > 1 ? '(' : '[' ) ) + '</SPAN>' : '' ;
printedVariation = false ;
for ( var ii = StartPlyVar [ varId ] ; ii < StartPlyVar [ varId ] + PlyNumberVar [ varId ] ; ii ++ ) {
printedComment = false ;
if ( commentsIntoMoveText && ( thisComment = strippedMoveComment ( ii , varId , true ) ) ) {
if ( commentsOnSeparateLines && variationTextDepth === 0 && ii > StartPlyVar [ varId ] ) {
text += '<DIV CLASS="comment" STYLE="line-height: 33%;"> </DIV>' ;
}
if ( printedVariation ) { if ( punctChars . indexOf ( thisComment . charAt ( 0 ) ) == - 1 ) { text += '<SPAN CLASS="variation"> </SPAN>' ; } }
else { printedVariation = variationTextDepth > 0 ; }
text += '<SPAN CLASS="comment">' + thisComment + '</SPAN>' ;
if ( commentsOnSeparateLines && variationTextDepth === 0 ) {
text += '<DIV CLASS="comment" STYLE="line-height: 33%;"> </DIV>' ;
}
printedComment = true ;
}
if ( printedComment || printedVariation ) { text += '<SPAN CLASS="variation"> </SPAN>' ; }
printedVariation = true ;
text += printMoveText ( ii , varId , ( variationTextDepth > 0 ) , ( ( printedComment ) || ( ii == StartPlyVar [ varId ] ) ) , true ) ;
}
if ( commentsIntoMoveText && ( thisComment = strippedMoveComment ( StartPlyVar [ varId ] + PlyNumberVar [ varId ] , varId , true ) ) ) {
if ( commentsOnSeparateLines && variationTextDepth === 0 ) {
text += '<DIV CLASS="comment" STYLE="line-height: 33%;"> </DIV>' ;
}
if ( printedVariation && ( punctChars . indexOf ( thisComment . charAt ( 0 ) ) == - 1 ) ) { text += '<SPAN CLASS="comment notranslate"> </SPAN>' ; }
text += '<SPAN CLASS="comment">' + thisComment + '</SPAN>' ;
printedComment = true ;
}
text += variationTextDepth -- ? ( '<SPAN CLASS="variation">' + ( variationTextDepth ? ')' : ']' ) + '</SPAN>' ) : '' ;
printedVariation = true ;
return text ;
}
function printMoveText ( thisPly , thisVar , isVar , hasLeadingNum , hasId ) {
if ( typeof ( thisVar ) == "undefined" ) { thisVar = CurrentVar ; }
if ( typeof ( thisPly ) == "undefined" ) { thisPly = CurrentPly ; }
var text = '' ;
if ( thisVar >= numberOfVars ||
thisPly < StartPlyVar [ thisVar ] ||
thisPly > StartPlyVar [ thisVar ] + PlyNumberVar [ thisVar ] ) {
return text ;
}
var moveCount = Math . floor ( thisPly / 2 ) + 1 ;
if ( thisPly % 2 === 0 ) {
text += '<SPAN CLASS="' + ( isVar ? 'variation' : 'move' ) +
' notranslate">' + moveCount + '. </SPAN>' ;
} else {
if ( hasLeadingNum ) {
text += '<SPAN CLASS="' + ( isVar ? 'variation' : 'move' ) +
' notranslate">' + moveCount + '... </SPAN>' ;
}
}
var jj = thisPly + 1 ;
text += '<A HREF="javascript:void(0);" ONCLICK="GoToMove(' + jj + ', ' + thisVar + ');" ' +
'CLASS="' + ( isVar ? 'variation' : 'move' ) + ' notranslate" ' +
( hasId ? ( 'ID="Var' + thisVar + 'Mv' + jj + '" ' ) : '' ) +
'ONFOCUS="this.blur();">' + MovesVar [ thisVar ] [ thisPly ] ;
if ( commentsIntoMoveText ) { text += basicNAGsMoveComment ( jj , thisVar ) ; }
text += '</A>' ;
return text ;
}
// undocumented API to autoscroll gane text to show current move
// enable with "enableAutoScrollToCurrentMove(objId)", with objId as the game text container object id
// add onresize="autoScrollToCurrentMoveIfEnabled" to the body tag for autoscrolling on page resize
function enableAutoScrollToCurrentMove ( objId ) { autoScrollToCurrentMove _objId = objId ; }
function disableAutoScrollToCurrentMove ( ) { autoScrollToCurrentMove _objId = "" ; }
function toggleAutoScrollToCurrentMove ( objId ) { autoScrollToCurrentMove _objId = autoScrollToCurrentMove _objId ? "" : objId ; }
var autoScrollToCurrentMove _objId = "" ;
function autoScrollToCurrentMoveIfEnabled ( ) { autoScrollToCurrentMove ( autoScrollToCurrentMove _objId ) ; }
function objOffsetVeryTop ( obj ) {
for ( var offset = obj . offsetTop ; obj = obj . offsetParent ; /* */ ) { offset += obj . offsetTop + obj . clientTop ; }
return offset ;
}
function autoScrollToCurrentMove ( objId ) {
if ( ! objId ) { return ; }
var theContainerObj = document . getElementById ( objId ) ;
if ( theContainerObj ) {
if ( CurrentPly == StartPly ) { theContainerObj . scrollTop = 0 ; }
else {
var theMoveObj = document . getElementById ( 'Var' + CurrentVar + 'Mv' + CurrentPly ) ;
if ( theMoveObj ) {
var theContainerObjOffsetVeryTop = objOffsetVeryTop ( theContainerObj ) ;
var theMoveObjOffsetVeryTop = objOffsetVeryTop ( theMoveObj ) ;
if ( ( theMoveObjOffsetVeryTop + theMoveObj . offsetHeight >
theContainerObjOffsetVeryTop + theContainerObj . scrollTop + theContainerObj . clientHeight ) ||
( theMoveObjOffsetVeryTop < theContainerObjOffsetVeryTop + theContainerObj . scrollTop ) ) {
theContainerObj . scrollTop = theMoveObjOffsetVeryTop - theContainerObjOffsetVeryTop ;
}
}
}
}
}
function FlipBoard ( ) {
var oldHighlightOption = highlightOption ;
if ( oldHighlightOption ) { SetHighlight ( false ) ; }
IsRotated = ! IsRotated ;
PrintHTML ( ) ;
RefreshBoard ( ) ;
if ( oldHighlightOption ) { SetHighlight ( true ) ; }
}
function RefreshBoard ( ) {
for ( var jj = 0 ; jj < 8 ; ++ jj ) {
for ( var ii = 0 ; ii < 8 ; ++ ii ) {
if ( Board [ jj ] [ ii ] === 0 ) { SetImage ( jj , ii , ClearImg . src ) ; }
}
}
for ( jj = 0 ; jj < 2 ; ++ jj ) {
for ( ii = 0 ; ii < 16 ; ++ ii ) {
if ( PieceType [ jj ] [ ii ] > 0 ) {
SetImage ( PieceCol [ jj ] [ ii ] , PieceRow [ jj ] [ ii ] , PieceImg [ jj ] [ PieceType [ jj ] [ ii ] ] . src ) ;
}
}
}
}
function SetAutoPlay ( vv ) {
isAutoPlayOn = vv ;
if ( AutoPlayInterval ) { clearTimeout ( AutoPlayInterval ) ; AutoPlayInterval = null ; }
if ( isAutoPlayOn ) {
if ( document . GameButtonsForm ) {
if ( document . GameButtonsForm . AutoPlay ) {
document . GameButtonsForm . AutoPlay . value = "=" ;
document . GameButtonsForm . AutoPlay . title = "toggle autoplay (stop)" ;
document . GameButtonsForm . AutoPlay . className = "buttonControlStop" ;
}
}
if ( CurrentPly < StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ) { AutoPlayInterval = setTimeout ( "MoveForward(1)" , Delay ) ; }
else {
if ( autoplayNextGame && ( CurrentVar === 0 ) ) { AutoPlayInterval = setTimeout ( "AutoplayNextGame()" , Delay ) ; }
else { SetAutoPlay ( false ) ; }
}
} else {
if ( document . GameButtonsForm ) {
if ( document . GameButtonsForm . AutoPlay ) {
document . GameButtonsForm . AutoPlay . value = "+" ;
document . GameButtonsForm . AutoPlay . title = "toggle autoplay (start)" ;
document . GameButtonsForm . AutoPlay . className = "buttonControlPlay" ;
}
}
}
}
var minAutoplayDelay = 500 ;
var maxAutoplayDelay = 300000 ;
function setCustomAutoplayDelay ( ) {
var newDelaySec = prompt ( "Enter custom autoplay delay, in seconds, between " + ( minAutoplayDelay / 1000 ) + " and " + ( maxAutoplayDelay / 1000 ) + ":" , Math . floor ( Delay / 100 ) / 10 ) ;
if ( ! isNaN ( newDelaySec = parseInt ( newDelaySec , 10 ) ) ) {
SetAutoplayDelayAndStart ( newDelaySec * 1000 ) ;
}
}
function SetAutoplayDelay ( vv ) {
if ( isNaN ( vv = parseInt ( vv , 10 ) ) ) { return ; }
Delay = Math . min ( Math . max ( vv , minAutoplayDelay ) , maxAutoplayDelay ) ;
}
function SetAutoplayDelayAndStart ( vv ) {
MoveForward ( 1 ) ;
SetAutoplayDelay ( vv ) ;
SetAutoPlay ( true ) ;
}
function SetLiveBroadcast ( delay , alertFlag , demoFlag , stepFlag ) {
LiveBroadcastDelay = delay ; // zero delay: no live broadcast
LiveBroadcastAlert = ( alertFlag === true ) ;
LiveBroadcastDemo = ( demoFlag === true ) ;
LiveBroadcastSteppingMode = ( stepFlag === true ) ;
setG7A6B6H7boardShortcuts ( ) ;
}
function SetImage ( col , row , image ) {
var trow = IsRotated ? row : 7 - row ;
var tcol = IsRotated ? 7 - col : col ;
var theObj = document . getElementById ( 'img_' + 'tcol' + tcol + 'trow' + trow ) ;
if ( ( theObj ) && ( theObj . src != image ) ) { theObj . src = image ; }
}
function SetImagePath ( path ) {
ImagePath = path ;
}
function SwitchAutoPlay ( ) {
if ( ! isAutoPlayOn ) { MoveForward ( 1 ) ; }
SetAutoPlay ( ! isAutoPlayOn ) ;
}
function StoreMove ( thisPly ) {
HistVar [ thisPly + 1 ] = CurrentVar ;
if ( HistNull [ thisPly ] = mvIsNull ) { return ; }
// "square from" history
HistPieceId [ 0 ] [ thisPly ] = mvPieceId ;
HistCol [ 0 ] [ thisPly ] = PieceCol [ MoveColor ] [ mvPieceId ] ;
HistRow [ 0 ] [ thisPly ] = PieceRow [ MoveColor ] [ mvPieceId ] ;
HistType [ 0 ] [ thisPly ] = PieceType [ MoveColor ] [ mvPieceId ] ;
// "square to" history
HistCol [ 2 ] [ thisPly ] = mvToCol ;
HistRow [ 2 ] [ thisPly ] = mvToRow ;
if ( mvIsCastling ) {
HistPieceId [ 1 ] [ thisPly ] = castleRook ;
HistCol [ 1 ] [ thisPly ] = PieceCol [ MoveColor ] [ castleRook ] ;
HistRow [ 1 ] [ thisPly ] = PieceRow [ MoveColor ] [ castleRook ] ;
HistType [ 1 ] [ thisPly ] = PieceType [ MoveColor ] [ castleRook ] ;
} else if ( mvCapturedId >= 0 ) {
HistPieceId [ 1 ] [ thisPly ] = mvCapturedId + 16 ;
HistCol [ 1 ] [ thisPly ] = PieceCol [ 1 - MoveColor ] [ mvCapturedId ] ;
HistRow [ 1 ] [ thisPly ] = PieceRow [ 1 - MoveColor ] [ mvCapturedId ] ;
HistType [ 1 ] [ thisPly ] = PieceType [ 1 - MoveColor ] [ mvCapturedId ] ;
} else {
HistPieceId [ 1 ] [ thisPly ] = - 1 ;
}
// update "square from" and captured square (not "square to" for en-passant)
Board [ PieceCol [ MoveColor ] [ mvPieceId ] ] [ PieceRow [ MoveColor ] [ mvPieceId ] ] = 0 ;
// mark captured piece
if ( mvCapturedId >= 0 ) {
PieceType [ 1 - MoveColor ] [ mvCapturedId ] = - 1 ;
PieceMoveCounter [ 1 - MoveColor ] [ mvCapturedId ] ++ ;
Board [ PieceCol [ 1 - MoveColor ] [ mvCapturedId ] ] [ PieceRow [ 1 - MoveColor ] [ mvCapturedId ] ] = 0 ;
}
// update piece arrays: promotion changes piece type
PieceType [ MoveColor ] [ mvPieceId ] = mvPieceOnTo ;
PieceMoveCounter [ MoveColor ] [ mvPieceId ] ++ ;
PieceCol [ MoveColor ] [ mvPieceId ] = mvToCol ;
PieceRow [ MoveColor ] [ mvPieceId ] = mvToRow ;
if ( mvIsCastling ) {
PieceMoveCounter [ MoveColor ] [ castleRook ] ++ ;
PieceCol [ MoveColor ] [ castleRook ] = mvToCol == 2 ? 3 : 5 ;
PieceRow [ MoveColor ] [ castleRook ] = mvToRow ;
}
// update board
Board [ mvToCol ] [ mvToRow ] = PieceType [ MoveColor ] [ mvPieceId ] * ( 1 - 2 * MoveColor ) ;
if ( mvIsCastling ) {
Board [ PieceCol [ MoveColor ] [ castleRook ] ] [ PieceRow [ MoveColor ] [ castleRook ] ] =
PieceType [ MoveColor ] [ castleRook ] * ( 1 - 2 * MoveColor ) ;
}
return ;
}
function UndoMove ( thisPly ) {
if ( HistNull [ thisPly ] ) { return ; }
// moved piece back to original square
var chgPiece = HistPieceId [ 0 ] [ thisPly ] ;
Board [ PieceCol [ MoveColor ] [ chgPiece ] ] [ PieceRow [ MoveColor ] [ chgPiece ] ] = 0 ;
Board [ HistCol [ 0 ] [ thisPly ] ] [ HistRow [ 0 ] [ thisPly ] ] = HistType [ 0 ] [ thisPly ] * ( 1 - 2 * MoveColor ) ;
PieceType [ MoveColor ] [ chgPiece ] = HistType [ 0 ] [ thisPly ] ;
PieceCol [ MoveColor ] [ chgPiece ] = HistCol [ 0 ] [ thisPly ] ;
PieceRow [ MoveColor ] [ chgPiece ] = HistRow [ 0 ] [ thisPly ] ;
PieceMoveCounter [ MoveColor ] [ chgPiece ] -- ;
// castling: rook back to original square
chgPiece = HistPieceId [ 1 ] [ thisPly ] ;
if ( ( chgPiece >= 0 ) && ( chgPiece < 16 ) ) {
Board [ PieceCol [ MoveColor ] [ chgPiece ] ] [ PieceRow [ MoveColor ] [ chgPiece ] ] = 0 ;
Board [ HistCol [ 1 ] [ thisPly ] ] [ HistRow [ 1 ] [ thisPly ] ] = HistType [ 1 ] [ thisPly ] * ( 1 - 2 * MoveColor ) ;
PieceType [ MoveColor ] [ chgPiece ] = HistType [ 1 ] [ thisPly ] ;
PieceCol [ MoveColor ] [ chgPiece ] = HistCol [ 1 ] [ thisPly ] ;
PieceRow [ MoveColor ] [ chgPiece ] = HistRow [ 1 ] [ thisPly ] ;
PieceMoveCounter [ MoveColor ] [ chgPiece ] -- ;
}
// capture: captured piece back to original square
chgPiece -= 16 ;
if ( ( chgPiece >= 0 ) && ( chgPiece < 16 ) ) {
Board [ PieceCol [ 1 - MoveColor ] [ chgPiece ] ] [ PieceRow [ 1 - MoveColor ] [ chgPiece ] ] = 0 ;
Board [ HistCol [ 1 ] [ thisPly ] ] [ HistRow [ 1 ] [ thisPly ] ] = HistType [ 1 ] [ thisPly ] * ( 2 * MoveColor - 1 ) ;
PieceType [ 1 - MoveColor ] [ chgPiece ] = HistType [ 1 ] [ thisPly ] ;
PieceCol [ 1 - MoveColor ] [ chgPiece ] = HistCol [ 1 ] [ thisPly ] ;
PieceRow [ 1 - MoveColor ] [ chgPiece ] = HistRow [ 1 ] [ thisPly ] ;
PieceMoveCounter [ 1 - MoveColor ] [ chgPiece ] -- ;
}
}
function Color ( nn ) {
if ( nn < 0 ) { return 1 ; }
if ( nn > 0 ) { return 0 ; }
return 2 ;
}
function sign ( nn ) {
if ( nn > 0 ) { return 1 ; }
if ( nn < 0 ) { return - 1 ; }
return 0 ;
}
function SquareOnBoard ( col , row ) {
return col >= 0 && col <= 7 && row >= 0 && row <= 7 ;
}
var pgn4webMaxTouches = 0 ;
var pgn4webOngoingTouches = new Array ( ) ;
function pgn4webOngoingTouchIndexById ( needle ) {
var id ;
for ( var ii = 0 ; ii < pgn4webOngoingTouches . length ; ii ++ ) {
id = pgn4webOngoingTouches [ ii ] . identifier ;
if ( pgn4webOngoingTouches [ ii ] . identifier === needle ) { return ii ; }
}
return - 1 ;
}
function pgn4web _handleTouchStart ( e ) {
e . stopPropagation ( ) ;
for ( var ii = 0 ; ii < e . changedTouches . length ; ii ++ ) {
pgn4webMaxTouches ++ ;
pgn4webOngoingTouches . push ( { identifier : e . changedTouches [ ii ] . identifier , clientX : e . changedTouches [ ii ] . clientX , clientY : e . changedTouches [ ii ] . clientY } ) ;
}
}
function pgn4web _handleTouchMove ( e ) {
e . stopPropagation ( ) ;
e . preventDefault ( ) ;
}
function pgn4web _handleTouchEnd ( e ) {
e . stopPropagation ( ) ;
var jj ;
for ( var ii = 0 ; ii < e . changedTouches . length ; ii ++ ) {
if ( ( jj = pgn4webOngoingTouchIndexById ( e . changedTouches [ ii ] . identifier ) ) != - 1 ) {
if ( pgn4webOngoingTouches . length == 1 ) {
customFunctionOnTouch ( e . changedTouches [ ii ] . clientX - pgn4webOngoingTouches [ jj ] . clientX , e . changedTouches [ ii ] . clientY - pgn4webOngoingTouches [ jj ] . clientY ) ;
pgn4webMaxTouches = 0 ;
}
pgn4webOngoingTouches . splice ( jj , 1 ) ;
}
}
clearSelectedText ( ) ;
}
function pgn4web _handleTouchCancel ( e ) {
e . stopPropagation ( ) ;
var jj ;
for ( var ii = 0 ; ii < e . changedTouches . length ; ii ++ ) {
if ( ( jj = pgn4webOngoingTouchIndexById ( e . changedTouches [ ii ] . identifier ) ) != - 1 ) {
pgn4webOngoingTouches . splice ( jj , 1 ) ;
if ( pgn4webOngoingTouches . length === 0 ) { pgn4webMaxTouches = 0 ; }
}
}
clearSelectedText ( ) ;
}
function pgn4web _initTouchEvents ( ) {
var theObj = document . getElementById ( "GameBoard" ) ;
if ( theObj && touchEventEnabled ) {
simpleAddEvent ( theObj , "touchstart" , pgn4web _handleTouchStart ) ;
simpleAddEvent ( theObj , "touchmove" , pgn4web _handleTouchMove ) ;
simpleAddEvent ( theObj , "touchend" , pgn4web _handleTouchEnd ) ;
simpleAddEvent ( theObj , "touchleave" , pgn4web _handleTouchEnd ) ;
simpleAddEvent ( theObj , "touchcancel" , pgn4web _handleTouchCancel ) ;
}
}
var waitForDoubleLeftTouchTimer = null ;
function customFunctionOnTouch ( deltaX , deltaY ) {
if ( Math . max ( Math . abs ( deltaX ) , Math . abs ( deltaY ) ) < 13 ) { return ; }
if ( Math . abs ( deltaY ) > 1.5 * Math . abs ( deltaX ) ) { // vertical up or down
if ( numberOfGames > 1 ) {
if ( ( currentGame === 0 ) && ( deltaY < 0 ) ) { Init ( numberOfGames - 1 ) ; }
else if ( ( currentGame === numberOfGames - 1 ) && ( deltaY > 0 ) ) { Init ( 0 ) ; }
else { Init ( currentGame + sign ( deltaY ) ) ; }
}
} else if ( Math . abs ( deltaX ) > 1.5 * Math . abs ( deltaY ) ) {
if ( deltaX > 0 ) { // horizontal right
if ( isAutoPlayOn ) { GoToMove ( StartPlyVar [ CurrentVar ] + PlyNumberVar [ CurrentVar ] ) ; }
else { SwitchAutoPlay ( ) ; }
} else { // horizontal left
if ( isAutoPlayOn && ! waitForDoubleLeftTouchTimer ) { SwitchAutoPlay ( ) ; }
else {
if ( waitForDoubleLeftTouchTimer ) {
clearTimeout ( waitForDoubleLeftTouchTimer ) ;
waitForDoubleLeftTouchTimer = null ;
}
if ( ( LiveBroadcastDelay > 0 ) && ( CurrentVar === 0 ) && ( CurrentPly === StartPly + PlyNumber ) ) {
waitForDoubleLeftTouchTimer = setTimeout ( "waitForDoubleLeftTouchTimer = null;" , 900 ) ;
replayPreviousMoves ( 6 ) ;
} else { GoToMove ( StartPlyVar [ CurrentVar ] + ( ( ( CurrentPly <= StartPlyVar [ CurrentVar ] + 1 ) || ( CurrentVar === 0 ) ) ? 0 : 1 ) ) ; }
}
}
}
}
var touchEventEnabled = true ;
function SetTouchEventEnabled ( onOff ) {
touchEventEnabled = onOff ;
}
function clearSelectedText ( ) {
if ( window . getSelection ) {
if ( window . getSelection ( ) . empty ) { window . getSelection ( ) . empty ( ) ; }
else if ( window . getSelection ( ) . removeAllRanges ) { window . getSelection ( ) . removeAllRanges ( ) ; }
} else if ( document . selection && document . selection . empty ) { document . selection . empty ( ) ; }
}
function simpleHtmlentities ( text ) {
return text . replace ( /&/g , "&" ) . replace ( /</g , "<" ) . replace ( />/g , ">" ) ;
}
function simpleHtmlentitiesDecode ( text ) {
return text . replace ( />/g , ">" ) . replace ( /</g , "<" ) . replace ( /&/g , "&" ) ;
}