1<?php 2 3/* 4 * pgn4web javascript chessboard 5 * copyright (C) 2009-2016 Paolo Casaschi 6 * see README file and http://pgn4web.casaschi.net 7 * for credits, license and more details 8 */ 9 10error_reporting(E_ALL | E_STRICT); 11 12// add temporarily blocked sites here 13$blockedReferrers = array(); 14 15 16$referrerHost = isset($_SERVER['HTTP_REFERER']) ? parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) : ""; 17if ($referrerHost) { 18 foreach ($blockedReferrers as $blockedReferrer) { 19 if (strstr($referrerHost, $blockedReferrer)) { 20 $thisPage = curPageURL(); 21 $thisPage .= ((strstr($thisPage, "?") ? "&" : "?") . "selfReferred=true"); 22 print <<<END 23<!DOCTYPE HTML> 24<html> 25<head> 26</head> 27<body style="padding:10px; font-size:x-small; font-family:sans-serif;"> 28<p style="font-weight:bold;">pgn4web chess puzzler: warning</p><p>Your site generates a substantial load on the pgn4web chess puzzler server. Please install the pgn4web chess puzzler on your own server following these <a href="http://pgn4web-project.casaschi.net/wiki/ServiceAvailability/" target="_blank">instructions</a>. Sorry for any inconvenience, previous attempts contacting your site's administrators were unsuccessful.</p><p>Click <a href="$thisPage">here</a> to view the pgn4web chess puzzler.</p> 29</body> 30</html> 31END; 32 exit; 33 } 34 } 35} 36 37$debugInfo = "\n"; 38 39function get_param($param, $shortParam, $default) { 40 if (isset($_REQUEST[$param])) { return $_REQUEST[$param]; } 41 if (isset($_REQUEST[$shortParam])) { return $_REQUEST[$shortParam]; } 42 return $default; 43} 44 45 46$pgnData = get_param("pgnData", "pd", "tactics.pgn"); 47 48function get_pgnText($pgnUrl) { 49 if (strpos($pgnUrl, ":") || (strpos($pgnUrl, "%3A"))) { return "[Event \"error: invalid pgnData parameter\"]\n"; } 50 $fileLimitBytes = 10000000; // 10Mb 51 $pgnText = file_get_contents($pgnUrl, NULL, NULL, 0, $fileLimitBytes + 1); 52 if (!$pgnText) { return "[Event \"error: failed to get pgnData content\"]\n"; } 53 $pgnText = str_replace(array("&", "<", ">"), array("&", "<", ">"), $pgnText); 54 return $pgnText; 55} 56 57$pgnText = get_pgnText($pgnData); 58 59// for simplicity, remove all comments from the game text 60// to avoid spurious [ in comments breaking the regular expression 61// splitting the PGN data into games 62$pgnText = preg_replace("/{[^}]*}/", "", $pgnText); 63$pgnText = preg_replace("/;[^\n$]*/", "", $pgnText); 64$pgnText = preg_replace("/(\n|^)%[^\n$]*/", "", $pgnText); 65 66$numGames = preg_match_all("/(\s*\[\s*(\w+)\s*\"([^\"]*)\"\s*\]\s*)+[^\[]*/", $pgnText, $games ); 67 68 69$gameNum = get_param("gameNum", "gn", ""); 70 71$expiresDate = ""; 72if ($gameNum == "random") { $gameNum = rand(1, $numGames); } 73else if (!preg_match("/^\d+$/", $gameNum)) { 74 $timeNow = time(); 75 $expiresDate = gmdate("D, d M Y H:i:s", (floor($timeNow / (60 * 60 * 24)) + 1) * (60 * 60 * 24)) . " GMT"; 76 if (!preg_match("/^[ +-]\d+$/", $gameNum)) { $gameNum = 0; } // space is needed since + is urldecoded as space 77 $gameNum = floor(($gameNum + ($timeNow / (60 * 60 * 24))) % $numGames) + 1; 78} 79else if ($gameNum < 1) { $gameNum = 1; } 80else if ($gameNum > $numGames) { $gameNum = $numGames; } 81$debugInfo .= "#" . ($gameNum ^ $numGames) . "." . $numGames . "\n"; 82$gameNum -= 1; 83 84$pgnGame = $games[0][$gameNum]; 85 86 87$lightColorHex = get_param("lightColorHex", "lch", "EFF4EC"); // FFCC99 88$lightColorHexCss = "#" . $lightColorHex; 89$darkColorHex = get_param("darkColorHex", "dch", "C6CEC3"); // CC9966 90$darkColorHexCss = "#" . $darkColorHex; 91 92$controlBackgroundColorHex = get_param("controlBackgroundColorHex", "cbch", "EFF4EC"); // FFCC99 93$controlBackgroundColorHexCss = "#" . $controlBackgroundColorHex; 94$controlTextColorHex = get_param("controlTextColorHex", "ctch", "888888"); // 663300 95$controlTextColorHexCss = "#" . $controlTextColorHex; 96 97 98$squareSize = get_param("squareSize", "ss", "30"); 99$squareSizeCss = $squareSize . "px"; 100if ($squareSize < 20) { $squareSize = 20; } 101if ($squareSize < 30) { 102 $borderStyleCss = "none"; 103 $highlightBorderStyleCss = "none"; 104 $borderSize = 0; 105 $borderSizeCss = $borderSize; 106} else { 107 $borderStyleCss = "solid"; 108 $highlightBorderStyleCss = "inset"; 109 $borderSize = ceil($squareSize / 50); 110 $borderSizeCss = $borderSize . "px"; 111} 112$bareSquareSize = $squareSize - 2 * $borderSize; 113$bareSquareSizeCss = $bareSquareSize . "px"; 114 115function defaultPieceSize($ss) { 116 $pieceSizeOptions = array(20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 52, 56, 60, 64, 72, 80, 88, 96, 112, 128, 144, 300); 117 $targetPieceSize = floor(0.8 * $ss); 118 for ($ii=count($pieceSizeOptions)-1; $ii>=0; $ii--) { 119 if ($pieceSizeOptions[$ii] <= $targetPieceSize) { return $pieceSizeOptions[$ii]; } 120 } 121 return $pieceSizeOptions[0]; 122} 123$pieceSize = defaultPieceSize($squareSize - 2 * $borderSize); 124$pieceSizeCss = $pieceSize . "px"; 125 126 127$pieceFont = get_param("pieceFont", "pf", "default"); 128if ($pieceFont == "a") { $pieceFont = "alpha"; } 129if ($pieceFont == "m") { $pieceFont = "merida"; } 130if ($pieceFont == "u") { $pieceFont = "uscf"; } 131if (($pieceFont == "random") || ($pieceFont == "r")) { 132 $randomPiece = rand(0, 2); 133 switch ($randomPiece) { 134 case 1: $pieceFont = "alpha"; break; 135 case 2: $pieceFont = "merida"; break; 136 default: $pieceFont = "uscf"; break; 137 } 138} 139if (($pieceFont == "hash") || ($pieceFont == "h")) { 140// $hashPiece = strlen($pgnGame) % 3; 141 $hashPiece = $gameNum % 3; 142 switch ($hashPiece) { 143 case 1: $pieceFont = "alpha"; break; 144 case 2: $pieceFont = "merida"; break; 145 default: $pieceFont = "uscf"; break; 146 } 147} 148if (($pieceFont == "default") || ($pieceFont == "d")) { 149 if ($pieceSize < 28) { $pieceFont = "uscf"; } 150 else { 151 if ($pieceSize > 39) { $pieceFont = "merida"; } 152 else { $pieceFont = "alpha"; } 153 } 154} 155 156 157$boardSize = $squareSize * 8; 158$boardSizeCss = $boardSize . "px"; 159 160 161$buttonHeight = $squareSize; 162$buttonHeightCss = $buttonHeight . "px"; 163$buttonWidth = $squareSize * 4; 164$buttonWidthCss = $buttonWidth . "px"; 165$buttonFontSize = floor($squareSize / 2.5); 166if ($buttonFontSize < 10) { $buttonFontSize = 10; } 167$buttonFontSizeCss = $buttonFontSize . "px"; 168$buttonPadding = floor($squareSize / 10); 169 170 171$sidetomoveBorder = floor($buttonFontSize / 18) + 1; 172$sidetomoveBorderCss = $sidetomoveBorder . "px"; 173$sidetomoveHeight = floor(0.8 * $buttonFontSize - 2 * $sidetomoveBorder); 174$sidetomoveHeightCss = $sidetomoveHeight . "px"; 175$sidetomoveWidth = $sidetomoveHeight; 176$sidetomoveWidthCss = $sidetomoveWidth . "px"; 177 178 179$frameBorderColorHex = get_param("frameBorderColorHex", "fbch", "C6CEC3"); 180if ($frameBorderColorHex == "none") { 181 $frameBorderStyleCss = "none"; 182 $frameBorderWidth = 0; 183 $frameBorderWidthCss = "0"; 184 $frameBorderColorHex = "000000"; 185} else { 186 $frameBorderStyleCss = "outset"; 187 $frameBorderWidth = ceil($squareSize / 50); 188 $frameBorderWidthCss = $frameBorderWidth . "px"; 189} 190$frameBorderColorHexCss = "#" . $frameBorderColorHex; 191 192$frameWidth = $boardSize; 193$frameWidthCss = $frameWidth . "px"; 194$frameHeight = $boardSize + $buttonHeight; 195$frameHeightCss = $frameHeight . "px"; 196 197 198// undocumented features 199 200$backgroundColorHex = get_param("backgroundColorHex", "bch", "transparent"); 201if (preg_match("/^[0123456789ABCDEF]{6}$/i", $backgroundColorHex)) { 202 $backgroundColorHexCss = "#" . $backgroundColorHex; 203} else { 204 $backgroundColorHexCss = $backgroundColorHex; 205} 206 207$framePadding = get_param("framePadding", "fp", 0); 208if ($framePadding != 0) { 209 $framePaddingCss = $framePadding . "px"; 210} else { 211 $framePaddingCss = $framePadding; 212} 213 214$rawGame = ""; 215 216$fenString = get_param("fenString", "fs", ""); 217if (($fenString == "true") || ($fenString == "t")) { 218 if (preg_match('/\[\s*FEN\s*"([^"]*)"\s*\]/', $pgnGame, $matches)) { $rawGame = $matches[1]; } 219} 220 221$pgnOnly = get_param("pgnOnly", "po", ""); 222if (($pgnOnly == "true") || ($pgnOnly == "t")) { 223 $rawGame = $pgnGame; 224} 225 226$pgnOnlyMini = get_param("pgnOnlyMini", "pom", ""); 227if (($pgnOnlyMini == "true") || ($pgnOnlyMini == "t")) { 228 if (preg_match('/\[\s*FEN\s*"[^"]*"\s*\]/', $pgnGame, $matches)) { $rawGame = "[SetUp \"1\"]\n" . $matches[0] . "\n\n"; } 229 $rawGame = $rawGame . preg_replace('/\[\s*\w+\s*"[^"]*"\s*\]\s*/', "", $pgnGame); 230} 231 232// end of undocumented features 233 234 235$outerFrameWidth = $frameWidth + 2 * $frameBorderWidth + 2 * $framePadding; 236$outerFrameHeight = $frameHeight + 2 * $frameBorderWidth + 2 * $framePadding; 237 238 239function curPageURL() { 240 if ((isset($_SERVER["SERVER_NAME"])) && (isset($_SERVER["REQUEST_URI"]))) { 241 $pageURL = 'http'; 242 if ((isset($_SERVER["HTTPS"])) && ($_SERVER["HTTPS"] == "on")) { $pageURL .= "s"; } 243 $pageURL .= "://"; 244 if ((isset($_SERVER["SERVER_PORT"])) && ($_SERVER["SERVER_PORT"] != "80")) { 245 $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"]; 246 } else { 247 $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]; 248 } 249 } else { 250 $pageURL = ""; 251 } 252 return $pageURL; 253} 254$thisPage = curPageURL(); 255 256 257$expiresMeta = ""; 258if ($expiresDate) { 259 header("expires: " . $expiresDate); 260 $expiresMeta = "<meta http-equiv=\"expires\" content=\"" . $expiresDate . "\">"; 261} 262 263if ($rawGame) { 264 header("content-type: application/x-chess-pgn; charset=utf-8"); 265 header("content-disposition: inline; filename=puzzler.pgn"); 266 print $rawGame; 267 exit; 268} 269 270header("content-type: text/html; charset=utf-8"); 271 272print <<<END 273<!DOCTYPE HTML> 274<html> 275 276<head> 277 278<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> 279$expiresMeta 280 281<title>chess puzzler</title> 282 283<!-- debug info 284$debugInfo 285end of debug info --> 286 287<style type="text/css"> 288 289html, 290body { 291 margin: 0px; 292 padding: 0px; 293} 294 295body { 296 padding: $framePaddingCss; 297 background: $backgroundColorHexCss; 298} 299 300.container { 301 width: $frameWidthCss; 302 height: $frameHeightCss; 303 border-style: $frameBorderStyleCss; 304 border-width: $frameBorderWidthCss; 305 border-color: $frameBorderColorHexCss; 306} 307 308.boardTable { 309 width: $boardSizeCss; 310 height: $boardSizeCss; 311 border-width: 0px; 312} 313 314.pieceImage { 315 width: $pieceSizeCss; 316 height: $pieceSizeCss; 317} 318 319.whiteSquare, 320.blackSquare, 321.highlightWhiteSquare, 322.highlightBlackSquare { 323 width: $bareSquareSizeCss; 324 height: $bareSquareSizeCss; 325 border-style: $borderStyleCss; 326 border-width: $borderSizeCss; 327} 328 329.whiteSquare, 330.highlightWhiteSquare { 331 border-color: $lightColorHexCss; 332 background: $lightColorHexCss; 333} 334 335.blackSquare, 336.highlightBlackSquare { 337 border-color: $darkColorHexCss; 338 background: $darkColorHexCss; 339} 340 341.highlightWhiteSquare, 342.highlightBlackSquare { 343 border-style: $highlightBorderStyleCss; 344} 345 346.buttonTable { 347 width: $boardSizeCss; 348 height: $buttonHeightCss; 349 background-color: $controlBackgroundColorHexCss; 350} 351 352.buttonCell { 353 width: $buttonWidthCss; 354 height: $buttonHeightCss; 355 white-space: nowrap; 356 overflow: hidden; 357} 358 359.buttonCellLink { 360 font-family: sans-serif; 361 font-size: $buttonFontSizeCss; 362 font-weight: 900; 363 color: $controlTextColorHexCss; 364 text-decoration: none; 365} 366 367.sidetomoveBox { 368 display: inline-block; 369 width: $sidetomoveWidthCss; 370 height: $sidetomoveHeightCss; 371 border-style: solid; 372 border-width: $sidetomoveBorderCss; 373 border-color: $controlTextColorHexCss; 374} 375 376</style> 377 378<link rel="icon" sizes="16x16" href="pawn.ico" /> 379 380<script src="pgn4web.js" type="text/javascript"></script> 381 382<script type="text/javascript"> 383"use strict"; 384 385SetImagePath("images/$pieceFont/$pieceSize"); 386SetShortcutKeysEnabled(false); 387 388function displayPuzzlerHelp() { 389 var puzzlerHelp = "pgn4web chess puzzler" + "\\n\\n"; 390 puzzlerHelp += "- the white or black small square below the chessboard's left side indicates the side to move" + "\\n\\n"; 391 puzzlerHelp += "- show the puzzler's solution step by step on the chessboard by clicking the > button below the chessboard's right side" + "\\n\\n"; 392 puzzlerHelp += "- step backwards one move by clicking the < button below the chessboard's left side" + "\\n\\n"; 393 puzzlerHelp += "click OK for more information, including how to add the chess puzzler to a website or blog"; 394 if (confirm(puzzlerHelp)) { window.open("http://pgn4web-project.casaschi.net/wiki/Example_Puzzler/", "_blank"); } 395} 396 397boardShortcut("F8", "chess puzzler help", function(t,e){ displayPuzzlerHelp(); }); 398clearShortcutSquares("E", "8"); 399clearShortcutSquares("BCDEFGH", "7"); 400clearShortcutSquares("ABCDEFGH", "23456"); 401clearShortcutSquares("BCFG", "1"); 402 403function solutionSoFar() { 404 var sol = ""; 405 for (var thisPly = StartPly; thisPly < CurrentPly; thisPly++) { 406 var moveCount = Math.floor(thisPly/2)+1; 407 if ((thisPly % 2 === 0) || (thisPly === StartPly)) { 408 sol += (Math.floor(thisPly/2)+1) + "."; 409 if (thisPly % 2) { sol += ".."; } 410 sol += " "; 411 } 412 sol += Moves[thisPly] + " "; 413 } 414 return sol; 415} 416 417function customFunctionOnMove() { 418 var res, outcome; 419 420 if (CurrentPly == StartPly) { 421 document.getElementById("leftButtonLink").innerHTML = "<span class='sidetomoveBox' style='background-color:" + (CurrentPly % 2 ? "black" : "white" ) + ";'></span>"; 422 document.getElementById("leftButton").title = ((CurrentPly % 2) ? "Black" : "White") + " to play: find the best move"; 423 } else { 424 document.getElementById("leftButtonLink").innerHTML = " < "; 425 document.getElementById("leftButton").title = "click < to step backwards one move"; 426 } 427 428 if (CurrentPly == StartPly+PlyNumber) { 429 switch (res = gameResult[currentGame]) { 430 case "1-0": outcome = "white wins"; break; 431 case "0-1": outcome = "black wins"; break; 432 case "1/2-1/2": outcome = "draw"; break; 433 default: outcome = "end"; res = "*"; break; 434 } 435 document.getElementById("rightButtonLink").innerHTML = " " + res + " "; 436 document.getElementById("rightButton").title = solutionSoFar() + " ..." + outcome; 437 } else { 438 document.getElementById("rightButtonLink").innerHTML = " > "; 439 if (CurrentPly == StartPly) { 440 document.getElementById("rightButton").title = "click > to show the puzzler's solution step by step on the chessboard"; 441 } else { 442 document.getElementById("rightButton").title = solutionSoFar() + " ...click > to continue showing the puzzler's solution step by step on the chessboard"; 443 } 444 } 445 446} 447 448</script> 449 450 451<!-- DeploymentCheck: google analytics code --> 452 453<!-- end DeploymentCheck --> 454 455 456</head> 457 458<body> 459 460<!-- paste your PGN below and make sure you dont specify an external source with SetPgnUrl() --> 461<form style="display: none;"><textarea style="display: none;" id="pgnText"> 462 463$pgnGame 464 465</textarea></form> 466<!-- paste your PGN above and make sure you dont specify an external source with SetPgnUrl() --> 467 468<center> 469<div class="container"> 470<div id="GameBoard"></div> 471<table class="buttonTable" border="0" cellspacing="0" cellpadding="0"> 472<tr> 473<td id="leftButton" title="" class="buttonCell" onClick="javascript:GoToMove(CurrentPly - 1);" align="center" valign="middle"> 474<a id="leftButtonLink" class="buttonCellLink" href="javascript:void(0);" onfocus="blur();"></a> 475</td> 476<td id="rightButton" title="" class="buttonCell" onClick="javascript:GoToMove(CurrentPly + 1);" align="center" valign="middle"> 477<a id="rightButtonLink" class="buttonCellLink" href="javascript:void(0);" onfocus="blur();"></a> 478</td> 479</tr> 480</table> 481</div> 482</center> 483 484</body> 485 486</html> 487END; 488?> 489