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("&amp;", "&lt;", "&gt;"), $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 = "&nbsp;&nbsp;&nbsp;&nbsp;&lt;&nbsp;&nbsp;&nbsp;&nbsp;";
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 = "&nbsp;&nbsp;&nbsp;&nbsp;" + res + "&nbsp;&nbsp;&nbsp;&nbsp;";
436    document.getElementById("rightButton").title = solutionSoFar() + " ..." + outcome;
437  } else {
438    document.getElementById("rightButtonLink").innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&gt;&nbsp;&nbsp;&nbsp;&nbsp;";
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