1<!DOCTYPE HTML>
2<html>
3
4<!--
5  pgn4web javascript chessboard
6  copyright (C) 2009-2015 Paolo Casaschi
7  see README file and http://pgn4web.casaschi.net
8  for credits, license and more details
9-->
10
11<head>
12
13<title>pgn4web analysis board</title>
14
15<!-- AppCheck: meta -->
16
17<link rel="icon" sizes="16x16" href="pawn.ico" />
18
19<style type="text/css">
20
21html,
22body {
23  margin: 0px;
24  padding: 0px;
25}
26
27body {
28  font-family: sans-serif;
29  overflow: hidden;
30  color: #F4F4F4;
31  background: #F4F4F4;
32
33  -webkit-user-select: none;
34  -moz-user-select: none;
35  -ms-user-select: none;
36  -o-user-select: none;
37  user-select: none;
38  -webkit-text-size-adjust: none;
39  -moz-text-size-adjust: none;
40  -ms-text-size-adjust: none;
41  -o-text-size-adjust: none;
42  text-size-adjust: none;
43  -webkit-touch-callout: none;
44}
45
46a {
47  text-decoration: none;
48}
49
50.container {
51  overflow: hidden;
52  position: relative;
53}
54
55.gameCustomButtons {
56  width: 100%;
57}
58
59.gameCustomButtonsHidden {
60  visibility: hidden;
61}
62
63.gameEval {
64  position: absolute;
65  right: -5px; /* this and the next fix some pv showing at the right of the eval */
66  padding-right: 5px;
67  bottom: 0px;
68  z-index: 3;
69}
70
71.gameTablebase {
72  display: none;
73  padding-right: 1em;
74}
75
76.gameAnalysis {
77  white-space: nowrap;
78  text-align: left;
79}
80
81.gameAnalysisHidden {
82  visibility: hidden;
83}
84
85.gameFlagAndMoves {
86  position: absolute;
87  bottom: 0px;
88  z-index: 2;
89  white-space: nowrap;
90}
91
92.gameFlagToMove {
93  display: inline-block;
94  border-style: solid;
95  border-width: 1px;
96}
97
98.gameShowFen {
99  display: inline-block;
100  overflow: hidden;
101}
102
103.gameMoves {
104  white-space: nowrap;
105}
106
107</style>
108
109<style type="text/css">
110
111/* for dynamically redefined variables */
112
113</style>
114
115<!-- AppCheck: fonts -->
116
117
118<script type="text/javascript">
119"use strict";
120
121var thisParamString = window.location.search || window.location.hash;
122
123var thisRegExp;
124
125var fenString;
126thisRegExp = /(&|\?)(fenString|fs)=([^&]*)(&|$)/i;
127if (thisParamString.match(thisRegExp) !== null) {
128   fenString = unescape(thisParamString.match(thisRegExp)[3]);
129}
130// action on fenString postponed after definying pgnText in the html body
131
132var defaultAnalysisSeconds = 13;
133var analysisSeconds = defaultAnalysisSeconds;
134var minAnalysisSeconds = 3;
135var maxAnalysisSeconds = 313;
136thisRegExp = /(&|\?)(analysisSeconds|as)=([1-9][0-9]*)(&|$)/i;
137if (thisParamString.match(thisRegExp) !== null) {
138   analysisSeconds = parseInt(unescape(thisParamString.match(thisRegExp)[3]), 10);
139   if (analysisSeconds < minAnalysisSeconds) { analysisSeconds = minAnalysisSeconds; }
140   if (analysisSeconds > maxAnalysisSeconds) { analysisSeconds = maxAnalysisSeconds; }
141   defaultAnalysisSeconds = analysisSeconds;
142}
143
144var disableEngine;
145thisRegExp = /(&|\?)(disableEngine|de)=([^&]*)(&|$)/i;
146if (thisParamString.match(thisRegExp) !== null) {
147   disableEngine = unescape(thisParamString.match(thisRegExp)[3]);
148}
149disableEngine = ((disableEngine == "true") || (disableEngine == "t"));
150// action on disableEngine postponed at the end of this file when all engine functions are ready
151
152var disableInputs;
153thisRegExp = /(&|\?)(disableInputs|di)=([^&]*)(&|$)/i;
154if (thisParamString.match(thisRegExp) !== null) {
155   disableInputs = unescape(thisParamString.match(thisRegExp)[3]);
156}
157disableInputs = ((disableInputs == "true") || (disableInputs == "t"));
158
159var autoUpdate;
160thisRegExp = /(&|\?)(autoUpdate|au)=([^&]*)(&|$)/i;
161if (thisParamString.match(thisRegExp) !== null) {
162   autoUpdate = unescape(thisParamString.match(thisRegExp)[3]);
163}
164autoUpdate = ((autoUpdate == "true") || (autoUpdate == "t"));
165
166
167var lightColorHex = "#F4F4F4";
168thisRegExp = /(&|\?)(lightColorHex|lch)=([0-9A-F]*)(&|$)/i;
169if (thisParamString.match(thisRegExp) !== null) {
170   lightColorHex = "#" + unescape(thisParamString.match(thisRegExp)[3]);
171}
172var darkColorHex = "#DDDDDD";
173thisRegExp = /(&|\?)(darkColorHex|dch)=([0-9A-F]*)(&|$)/i;
174if (thisParamString.match(thisRegExp) !== null) {
175   darkColorHex = "#" + unescape(thisParamString.match(thisRegExp)[3]);
176}
177var highlightColorHex = "#BBBBBB";
178thisRegExp = /(&|\?)(highlightColorHex|hch)=([0-9A-F]*)(&|$)/i;
179if (thisParamString.match(thisRegExp) !== null) {
180   highlightColorHex = "#" + unescape(thisParamString.match(thisRegExp)[3]);
181}
182var fontMovesColorHex = "#000000";
183thisRegExp = /(&|\?)(fontMovesColorHex|fmch)=([0-9A-F]*)(&|$)/i;
184if (thisParamString.match(thisRegExp) !== null) {
185   fontMovesColorHex = "#" + unescape(thisParamString.match(thisRegExp)[3]);
186}
187var controlTextColorHex = "#BBBBBB";
188thisRegExp = /(&|\?)(controlTextColorHex|ctch)=([0-9A-F]*)(&|$)/i;
189if (thisParamString.match(thisRegExp) !== null) {
190   controlTextColorHex = "#" + unescape(thisParamString.match(thisRegExp)[3]);
191}
192var backgroundColorHex = lightColorHex;
193thisRegExp = /(&|\?)(backgroundColorHex|bch)=([0-9A-F]*)(&|$)/i;
194if (thisParamString.match(thisRegExp) !== null) {
195   backgroundColorHex = "#" + unescape(thisParamString.match(thisRegExp)[3]);
196}
197
198var framePaddingRatio = 1;
199thisRegExp = /(&|\?)(framePaddingRatio|fpr)=([0-9.]+)(&|$)/i;
200if (thisParamString.match(thisRegExp) !== null) {
201   framePaddingRatio = parseFloat(unescape(thisParamString.match(thisRegExp)[3]));
202}
203
204var squareSize;
205thisRegExp = /(&|\?)(squareSize|ss)=([1-9][0-9]*)(&|$)/i;
206if (thisParamString.match(thisRegExp) !== null) {
207   squareSize = parseInt(unescape(thisParamString.match(thisRegExp)[3]), 10);
208}
209
210var pieceSizeOptions = new 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);
211function defaultPieceSize(squareSize) {
212   var targetPieceSize = Math.floor(0.8 * squareSize);
213   for (var ii=(pieceSizeOptions.length-1); ii>=0; ii--) {
214      if (pieceSizeOptions[ii] <= targetPieceSize) { return pieceSizeOptions[ii]; }
215   }
216   return pieceSizeOptions[0];
217}
218var pieceSize;
219thisRegExp = /(&|\?)(pieceSize|ps)=([1-9][0-9]*)(&|$)/i;
220if (thisParamString.match(thisRegExp) !== null) {
221   pieceSize = parseInt(unescape(thisParamString.match(thisRegExp)[3]), 10);
222}
223
224// undocumented feature
225var fixedPieceImageSize;
226thisRegExp = /(&|\?)(fixedPieceImageSize|fpis)=([1-9][0-9]*)(&|$)/i;
227if (thisParamString.match(thisRegExp) !== null) {
228   fixedPieceImageSize = parseInt(unescape(thisParamString.match(thisRegExp)[3]), 10);
229}
230
231var pieceFont = "alpha";
232thisRegExp = /(&|\?)(pieceFont|pf)=([^&]*)(&|$)/i;
233if (thisParamString.match(thisRegExp) !== null) {
234   pieceFont = unescape(thisParamString.match(thisRegExp)[3]);
235   if (pieceFont == "a") { pieceFont = "alpha"; }
236   if (pieceFont == "m") { pieceFont = "merida"; }
237   if (pieceFont == "u") { pieceFont = "uscf"; }
238   if ((pieceFont != "alpha") && (pieceFont != "merida") && (pieceFont != "uscf")) { pieceFont = "alpha"; }
239}
240
241var fontMovesSize;
242thisRegExp = /(&|\?)(fontMovesSize|fms)=([1-9][0-9]*)(&|$)/i;
243if (thisParamString.match(thisRegExp) !== null) {
244   fontMovesSize = parseInt(unescape(thisParamString.match(thisRegExp)[3]), 10);
245}
246var fontCommentsSize;
247thisRegExp = /(&|\?)(fontCommentsSize|fcs)=([1-9][0-9]*)(&|$)/i;
248if (thisParamString.match(thisRegExp) !== null) {
249   fontCommentsSize = parseInt(unescape(thisParamString.match(thisRegExp)[3]), 10);
250}
251
252thisRegExp = /(&|\?)(help|h)=(t|true)(&|$)/i;
253if (thisParamString.match(thisRegExp) !== null) {
254   document.write("<PRE>");
255   document.write("URL parameters\n");
256   document.write("\n");
257   document.write(" - fenString = FEN position to analyze\n");
258   document.write(" - analysisSeconds = analysis timeout in seconds (default 13)\n");
259   document.write(" - disableEngine = true | false (default false)\n");
260   document.write(" - disableInputs = true | false (default false)\n");
261   document.write(" - autoUpdate = true | false (default false)\n");
262   // document.write(" - autoPlay = true | false (default false)\n");
263   // document.write(" - enableLocalStorage = true | false (default false)\n");
264   // document.write(" - engineSignature = positive number (default -1)\n");
265   document.write("\n");
266   document.write(" - squareSize = size of square (default selects square size based on window size)\n");
267   document.write(" - pieceSize = size of pieces | default (default selects piece size based on square size)\n");
268   // document.write(" - fixedPieceImageSize = size of piece images (default as size of pieces)\n");
269   document.write(" - pieceFont = alpha | merida | uscf (default alpha)\n");
270   document.write("\n");
271   document.write(" - lightColorHex = light square color hex code, like FF0000 (default F4F4F4)\n");
272   document.write(" - darkColorHex = dark square color hex code, like FF0000 (default DDDDDD)\n");
273   document.write(" - highlightColorHex = highlight color hex code, like FF0000 (default BBBBBB)\n");
274   document.write(" - backgroundColorHex = page background color hex code, like FF0000 (default as lightColorHex)\n");
275   document.write(" - controlTextColorHex = control buttons text color hex code, like FF0000 (default BBBBBB)\n");
276   document.write(" - fontMovesColorHex = moves color hex code, like FF0000 (default 000000)\n");
277   document.write("\n");
278   document.write(" - fontMovesSize = moves font size (default selects moves font size based on square size)\n");
279   document.write(" - fontCommentsSize = analysis comments font size (default selects comments font size based on square size)\n");
280   document.write("\n");
281   document.write(" - framePaddingRatio = frame padding as a square ratio (default 1)\n");
282   document.write("\n");
283   document.write(" - help = true\n");
284   document.write("\n");
285   document.write("</PRE>");
286}
287
288
289// undocumented feature
290var autoPlay;
291thisRegExp = /(&|\?)(autoPlay|ap)=([^&]*)(&|$)/i;
292if (thisParamString.match(thisRegExp) !== null) {
293   autoPlay = unescape(thisParamString.match(thisRegExp)[3]);
294}
295autoPlay = ((autoPlay == "true") || (autoPlay == "t"));
296
297// undocumented feature
298var enableLocalStorage;
299thisRegExp = /(&|\?)(enableLocalStorage|els)=([^&]*)(&|$)/i;
300if (thisParamString.match(thisRegExp) !== null) {
301   enableLocalStorage = unescape(thisParamString.match(thisRegExp)[3]);
302}
303enableLocalStorage = ((enableLocalStorage == "true") || (enableLocalStorage == "t"));
304
305
306function myRulesLength(sheet) {
307   if (sheet.cssRules) { return sheet.cssRules.length; }
308   if (sheet.rules) { return sheet.rules.length; }
309   return null;
310}
311
312function myInsertRule(sheet, selector, declaration) {
313   if (sheet.insertRule) { sheet.insertRule(selector + "{ " + declaration + " }", myRulesLength(sheet)); }
314   else if (sheet.addRule) { sheet.addRule(selector, declaration); }
315}
316
317function myDeleteRule(sheet, index) {
318   if (sheet.deleteRule) { sheet.deleteRule(index); }
319   else if (sheet.removeRule) { sheet.removeRule(index); }
320}
321
322var mySheet = document.styleSheets[0];
323myInsertRule(mySheet, "body", "color: " + fontMovesColorHex + "; background: " + backgroundColorHex + ";");
324myInsertRule(mySheet, "a", "color: " + fontMovesColorHex + ";");
325myInsertRule(mySheet, ".boardTable", "background: " + lightColorHex + ";");
326myInsertRule(mySheet, ".whiteSquare", "background: " + lightColorHex + ";");
327myInsertRule(mySheet, ".highlightWhiteSquare", "background: " + highlightColorHex + ";");
328myInsertRule(mySheet, ".blackSquare", "background: " + darkColorHex + ";");
329myInsertRule(mySheet, ".highlightBlackSquare", "background: " + highlightColorHex + ";");
330myInsertRule(mySheet, ".gameCustomButtons", "color: " + controlTextColorHex + ";");
331myInsertRule(mySheet, ".gameFlagToMove", "border-color: " + fontMovesColorHex + ";");
332myInsertRule(mySheet, ".gameEval", "background: " + backgroundColorHex + ";");
333
334var oldSquareSizeCss;
335function myOnResize() {
336
337   if (!fixedPieceImageSize && oldSquareSizeCss) { return; }
338
339   var sheet = document.styleSheets[1];
340   var oldRules = myRulesLength(sheet);
341
342   var squareSizeCss;
343   if (typeof(squareSize) != "undefined") { squareSizeCss = squareSize; }
344   else {
345      var ww = 0, wh = 0;
346      if (window.innerWidth && window.innerHeight) { ww = window.innerWidth; wh = window.innerHeight; }
347      else if (document.documentElement && document.documentElement.clientWidth) { ww = document.documentElement.clientWidth; wh = document.documentElement.clientHeight; }
348      else if (document.body && document.body.clientWidth) { ww = document.body.clientWidth; wh = document.body.clientHeight; }
349      if (Math.min(ww, wh) > 0) {
350         squareSizeCss = Math.floor(Math.min(ww / (8 + 2 * framePaddingRatio), wh / (10 + 2 * framePaddingRatio)));
351      } else {
352         squareSizeCss = 30;
353      }
354   }
355   if (squareSizeCss < 20) { squareSizeCss = 20; }
356
357   if (squareSizeCss === oldSquareSizeCss) { return; }
358
359   var framePaddingCss = framePaddingRatio * squareSizeCss;
360   var pieceSizeCss = typeof(pieceSize) != "undefined" ? pieceSize : defaultPieceSize(squareSizeCss);
361   var fontMovesSizeCss = typeof(fontMovesSize) != "undefined" ? fontMovesSize : Math.ceil(squareSizeCss * 11 / 30);
362   var fontCommentsSizeCss = typeof(fontCommentsSize) != "undefined" ? fontCommentsSize : Math.ceil(squareSizeCss * 19 / 30);
363
364   myInsertRule(sheet, "body", "padding: " + framePaddingCss + "px;" + (wh ? " height: " + (wh - 2 * framePaddingCss) + "px;" : ""));
365   myInsertRule(sheet, ".boardTable", "width: " + (squareSizeCss * 8) + "px; height: " + (squareSizeCss * 8) + "px;");
366   myInsertRule(sheet, ".pieceImage", "width: " + pieceSizeCss + "px; height: " + pieceSizeCss + "px;");
367   myInsertRule(sheet, ".whiteSquare", "width: " + squareSizeCss + "px; height: " + squareSizeCss + "px;");
368   myInsertRule(sheet, ".highlightWhiteSquare", "width: " + squareSizeCss + "px; height: " + squareSizeCss + "px;");
369   myInsertRule(sheet, ".blackSquare", "width: " + squareSizeCss + "px; height: " + squareSizeCss + "px;");
370   myInsertRule(sheet, ".highlightBlackSquare", "width: " + squareSizeCss + "px; height: " + squareSizeCss + "px;");
371   myInsertRule(sheet, ".container", "width: " + (squareSizeCss * 8) + "px;");
372   myInsertRule(sheet, ".gameCustomButtons", "height: " + squareSizeCss + "px; padding-bottom: " + Math.floor(squareSizeCss / 6) + "px; font-size: " + fontMovesSizeCss + "px; margin-left: " + Math.floor((2 * squareSizeCss - 5 * Math.floor(squareSizeCss * 0.4)) / 2) + "px;");
373   myInsertRule(sheet, ".gameButtonCell", "width: " + squareSizeCss + "px;");
374   myInsertRule(sheet, ".gameButtonSpacer", "width: " + Math.floor(squareSizeCss * 0.4) + "px;");
375   myInsertRule(sheet, ".gameAnalysis", "height: " + squareSizeCss + "px;");
376   myInsertRule(sheet, ".gameFlagToMove", "height: " + Math.floor(fontMovesSizeCss / 2) + "px; width: " + Math.floor(fontMovesSizeCss / 2) + "px;");
377   myInsertRule(sheet, ".gameShowFen", "width: " + (squareSizeCss - 2 * Math.floor(squareSizeCss / 3)) + "px; margin-left:" + Math.floor(squareSizeCss / 3) + "px; margin-right:" + Math.floor(squareSizeCss / 3) + "px;");
378   myInsertRule(sheet, ".gameMoves", "font-size: " + fontMovesSizeCss + "px;");
379   myInsertRule(sheet, ".gameTablebase", "font-size: " + fontMovesSizeCss + "px;");
380   myInsertRule(sheet, ".gameEval", "padding-left: " + squareSizeCss + "px; font-size: " + fontCommentsSizeCss + "px;");
381
382   SetImagePath("images/" + pieceFont + "/" + (fixedPieceImageSize ? fixedPieceImageSize : pieceSizeCss));
383
384   for (var ii = 0; ii < oldRules; ii++) { myDeleteRule(sheet, 0); }
385
386   var theObj;
387   if (theObj = document.getElementById("boardTable")) { theObj.style.height = (8 * squareSizeCss) + "px"; }
388
389   oldSquareSizeCss = squareSizeCss;
390}
391
392</script>
393
394<script src="pgn4web.js" type="text/javascript"></script>
395<!-- patch: fonts folder: if the "fonts" folder is relocated please update the next line accordingly -->
396<script src="fonts/chess-informant-NAG-symbols.js" type="text/javascript"></script>
397
398<script type="text/javascript">
399"use strict";
400
401SetImageType("png");
402SetHighlightOption(false);
403SetShortcutKeysEnabled(true);
404myOnResize();
405
406<!-- AppCheck: myOnOrientationchange -->
407
408
409</script>
410
411</head>
412
413<body onResize="myOnResize();">
414
415<!-- paste your PGN below and make sure you dont specify an external source with SetPgnUrl() -->
416<form style="display: none;"><textarea style="display: none;" id="pgnText">
417
418</textarea></form>
419<!-- paste your PGN above and make sure you dont specify an external source with SetPgnUrl() -->
420
421<center>
422<div class="container">
423<div id="GameBoard"></div>
424<table id="GameCustomButtons" class="gameCustomButtons gameCustomButtonsHidden" cellspacing="0" cellpadding="0" border="0"><tr valign="bottom">
425<td id="GameAnalysisFlag" class="gameButtonCell" align="center" onclick="clickedGameAnalysisFlag(this, event);">&nbsp;</td>
426<td class="gameButtonSpacer"></td>
427<td class="gameButtonCell" align="center" onclick="clickedButtonStart(this, event);" title="go to start">&lt;&lt;</td>
428<td class="gameButtonSpacer"></td>
429<td class="gameButtonCell" align="center" onclick="clickedButtonBackward(this, event);" title="move backward">&lt;</td>
430<td class="gameButtonSpacer"></td>
431<td class="gameButtonCell" align="center" onclick="clickedButtonForward(this, event);" title="move forward">&gt;</td>
432<td class="gameButtonSpacer"></td>
433<td class="gameButtonCell" align="center" onclick="clickedButtonEnd(this, event);" title="go to end">&gt;&gt;</td>
434<td class="gameButtonSpacer"></td>
435<td id="GameAutoUpdateFlag" class="gameButtonCell" align="center" onclick="clickedGameAutoUpdateFlag(this, event);">&nbsp;</td>
436</tr></table>
437<div class="gameAnalysis gameAnalysisHidden" id="GameAnalysis">
438<div class="gameEval" id="GameEval" onclick="clickedGameEval(this, event);">&nbsp;</div>
439</div>
440<div class="gameFlagAndMoves">
441<span class="gameFlagToMove" id="GameFlagToMove" onclick="clickedGameFlagToMove(this, event);"></span><span class="gameShowFen" onclick="clickedGameShowFen(this, event);">&nbsp;</span><span class="gameTablebase" id="GameTablebase" onclick="clickedGameTablebase(this, event);" title="probe endgame tablebase">&perp;</span><span id="GameMoves" class="gameMoves" onclick="clickedGameMoves(this, event);">&nbsp;</span>&nbsp;
442</div>
443</div>
444</center>
445
446<script type="text/javascript">
447"use strict";
448
449var theObj;
450
451if (typeof(fenString) == "undefined") { fenString = FenStringStart; }
452if (theObj = document.getElementById("pgnText")) {
453   theObj.innerHTML = '[Setup "1"]\n[FEN "' + fenString + '"]\n';
454}
455
456
457function clickedButtonStart(t,e) {
458   if (e.shiftKey) {
459      analysisSeconds = analysisSeconds <= defaultAnalysisSeconds ? minAnalysisSeconds : defaultAnalysisSeconds;
460   } else {
461      if (CurrentPly > StartPlyVar[0]) { GoToMove(StartPlyVar[0], 0); }
462   }
463}
464
465function clickedButtonBackward(t,e) {
466   if (e.shiftKey) {
467      analysisSeconds = Math.max(Math.floor(analysisSeconds / 1.15), minAnalysisSeconds);
468   } else {
469     if (CurrentPly > StartPlyVar[0]) { GoToMove(CurrentPly - 1); }
470   }
471}
472
473function clickedButtonForward(t,e) {
474   if (e.shiftKey) {
475      analysisSeconds = Math.min(Math.ceil(analysisSeconds * 1.15), maxAnalysisSeconds);
476   } else {
477      if (CurrentPly < StartPlyVar[0] + PlyNumberVar[0]) { GoToMove(CurrentPly + 1); }
478   }
479}
480
481function clickedButtonEnd(t,e) {
482   if (e.shiftKey) {
483      analysisSeconds = analysisSeconds >= defaultAnalysisSeconds ? maxAnalysisSeconds : defaultAnalysisSeconds;
484   } else {
485      if (CurrentPly < StartPlyVar[0] + PlyNumberVar[0]) { GoToMove(StartPlyVar[0] + PlyNumberVar[0], 0); }
486   }
487}
488
489function clickedGameMoves(t,e) {
490   if (e.shiftKey) {
491      save_cache_to_localStorage();
492   } else {
493      var candidateMove = t.innerHTML.replace(/^\s*(\S+).*$/, "$1");
494      if (candidateMove) { addPly(candidateMove); }
495   }
496}
497
498function clickedGameEval(t,e) {
499   if (e.shiftKey) {
500      if (!disableEngine) {
501         displayHelp("informant_symbols");
502      }
503   } else {
504      setDisableEngine(!disableEngine);
505   }
506}
507
508var maxMenInTablebase = 0;
509var minMenInTablebase = 3;
510function probeTablebase() {}
511
512
513// DeploymentCheck: tablebase glue code
514
515// end DeploymentCheck
516
517
518function clickedGameTablebase() {
519   var menPosition = CurrentFEN().replace(/\s.*$/, "").replace(/[0-9\/]/g, "").length;
520   if ((menPosition >= minMenInTablebase) && (menPosition <= maxMenInTablebase)) {
521      probeTablebase();
522   } else {
523      myAlert("warning: endgame tablebase only supports positions with " + minMenInTablebase + " to " + maxMenInTablebase + " men");
524   }
525}
526
527function updateTablebaseFlag() {
528   var menPosition = CurrentFEN().replace(/\s.*$/, "").replace(/[0-9\/]/g, "").length;
529   var theObj = document.getElementById("GameTablebase");
530   var showTablebase = (menPosition >= minMenInTablebase) && (menPosition <= maxMenInTablebase) && (!g_initErrors) && (!disableEngine);
531   if (theObj) {
532     theObj.style.display = showTablebase ? "inline" : "none";
533   }
534}
535
536
537function clickedGameAutoUpdateFlag(t,e) {
538   if (openerCheck()) {
539      autoUpdate = !autoUpdate;
540      if ((autoUpdate) && ((CurrentFEN() !== window.opener.CurrentFEN()) || (!g_backgroundEngine && !disableEngine))) {
541         window.opener.showEngineAnalysisBoard(disableEngine);
542      }
543
544<!-- AppCheck: clickedGameAutoUpdateFlag -->
545
546   }
547   updateGameAutoUpdateFlag();
548}
549
550function clickedGameAnalysisFlag(t,e) {
551   if (e.shiftKey) {
552      cache_clear();
553      clear_cache_from_localStorage();
554   } else {
555      if (g_backgroundEngine) {
556         StopBackgroundEngine();
557      } else {
558         if (openerCheck()) {
559            window.opener.showEngineAnalysisBoard(disableEngine);
560         } else {
561            StartEngineAnalysis();
562         }
563      }
564   }
565}
566
567function clickedGameFlagToMove(t,e) {
568   if (e.shiftKey) {
569      if (autoPlay) { stopAutoPlay(); }
570      else { startAutoPlay(); }
571   } else {
572      if ((!autoPlay) && (!IsCheck(PieceCol[MoveColor][0], PieceRow[MoveColor][0], MoveColor))) {
573         if ((CurrentPly > StartPly) && (Moves[CurrentPly - 1] == "--")) { MoveBackward(1); }
574         else { addPly("--"); }
575      }
576   }
577}
578
579function clickedGameShowFen(t, e) {
580   if (e.shiftKey) { displayFenData(); }
581   else if (PlyNumber > 0) {
582      var oldCurrentPly = CurrentPly;
583      MoveBackward(CurrentPly - StartPly, true);
584      displayFenData(true);
585      MoveForward(oldCurrentPly - CurrentPly);
586   }
587}
588
589function pgn4web_handleKey(e) {
590  var keycode, colRow, colRowList, theObj;
591
592  if (!e) { e = window.event; }
593
594  keycode = e.keyCode;
595
596  if (e.altKey || e.ctrlKey || e.metaKey) { return true; }
597
598  if (!shortcutKeysEnabled) { return true; }
599
600  switch (keycode) {
601
602    case 32: // space
603      if (theObj = document.getElementById("GameMoves")) {
604        clickedGameMoves(theObj, {shiftKey: false});
605      }
606      break;
607
608    case 189: // dash
609      if (colRowList = prompt("Enter shortcut square coordinates to click:", "")) {
610        colRowList = colRowList.toUpperCase().replace(/[^A-Z0-9]/g,"");
611        while (colRow = colRowFromSquare(colRowList)) {
612          boardOnClick[colRow.col][colRow.row]({"id": "img_tcol" + colRow.col + "trow" + colRow.row}, e);
613          colRowList = colRowList.substr(2);
614        }
615      }
616      break;
617
618    case 90: // z
619      if (e.shiftKey) { window.open(pgn4web_project_url); }
620      else { displayDebugInfo(); }
621      break;
622
623    case 37: // left-arrow
624    case 74: // j
625      backButton(e);
626      break;
627
628    case 38: // up-arrow
629    case 72: // h
630      startButton(e);
631      break;
632
633    case 39: // right-arrow
634    case 75: // k
635      forwardButton(e);
636      break;
637
638    case 40: // down-arrow
639    case 76: // l
640      endButton(e);
641      break;
642
643<!-- AppCheck: pgn4web_handleKey -->
644
645    case 82: // r
646      setDisableEngine(true);
647      break;
648
649    case 89: // y
650      setDisableEngine(false);
651      break;
652
653    case 70: // f
654      clickedGameFlagToMove(null, {shiftKey: false});
655      break;
656
657    default:
658      return true;
659  }
660  return stopEvProp(e);
661}
662
663
664var pgn4web_chess_engine_id = "garbochess-pgn4web-" + pgn4web_version;
665
666var engineWorker = "libs/garbochess/garbochess.js";
667
668var g_backgroundEngine;
669var g_topNodesPerSecond = 0;
670var g_ev = "";
671var g_pv = "";
672var g_depth = "";
673var g_nodes = "";
674var g_initErrors = 0;
675var g_lastFenError = "";
676
677function InitializeBackgroundEngine() {
678   var theObj;
679
680   if (!g_backgroundEngine) {
681      try {
682          g_backgroundEngine = new Worker(engineWorker);
683          g_backgroundEngine.addEventListener("message", function (e) {
684             var theObj;
685             if ((e.data.match("^pv")) && (fenString == CurrentFEN())) {
686                var matches = e.data.substr(3, e.data.length - 3).match(/Ply:(\d+) Score:(-*\d+) Nodes:(\d+) NPS:(\d+) (.*)/);
687                if (matches) {
688                   g_depth = parseInt(matches[1], 10);
689                   if (isNaN(g_ev = parseInt(matches[2], 10))) {
690                      g_ev = "";
691                   } else {
692                      var maxEv = 99.9;
693                      g_ev = Math.round(g_ev / 100) / 10;
694                      if (g_ev < -maxEv) { g_ev = -maxEv; } else if (g_ev > maxEv) { g_ev = maxEv; }
695                      if (fenString.indexOf(" b ") !== -1) { g_ev = -g_ev; }
696                   }
697                   g_nodes = parseInt(matches[3], 10);
698                   var nodesPerSecond = parseInt(matches[4], 10);
699                   g_topNodesPerSecond = Math.max(nodesPerSecond, g_topNodesPerSecond);
700                   g_pv = matches[5].replace(/(^\s+|\s*[x+=]|\s+$)/g, "").replace(/\s*stalemate/, "=").replace(/\s*checkmate/, "#"); // patch: pgn notation: remove/add '+' 'x' '=' chars for full chess informant style or pgn style for the game text
701                   validateSearchWithCache();
702                   if (theObj = document.getElementById("GameEval")) {
703                      theObj.innerHTML = ev2NAG(g_ev);
704                      theObj.title = (g_ev > 0 ? " +" : " ") + g_ev + (g_ev == Math.floor(g_ev) ? ".0 " : " ");
705                   }
706                   if (theObj = document.getElementById("GameMoves")) {
707                      theObj.innerHTML = g_pv;
708                      theObj.title = g_pv;
709                   }
710                   updateGameAnalysisFlag();
711                   if (detectGameEnd(g_pv, "")) { StopBackgroundEngine(); }
712                }
713             } else if (e.data.match("^message Invalid FEN")) {
714                if (theObj = document.getElementById("GameEval")) {
715                   theObj.innerHTML = NAG[2];
716                   theObj.title = "?";
717                }
718                if (theObj = document.getElementById("GameMoves")) {
719                   theObj.innerHTML = "invalid position";
720                   theObj.title = e.data.replace(/^message /, "");
721                }
722                if (fenString != g_lastFenError) {
723                   g_lastFenError = fenString;
724                   myAlert("error: engine: " + e.data.replace(/^message /, "") + "\n" + fenString, false);
725                }
726             }
727          }, false);
728          g_initErrors = 0;
729          return true;
730      } catch(e) {
731         if (theObj = document.getElementById("GameEval")) {
732            theObj.innerHTML = useNAGeval ? translateNAGs("$255") + "<span class='NAGs'>&nbsp;&nbsp;&nbsp;</span>" + translateNAGs("$147") : "X X";
733            theObj.title = "engine analysis unavailable";
734         }
735         if (theObj = document.getElementById("GameMoves")) {
736            theObj.innerHTML = "&nbsp;";
737            theObj.title = "";
738         }
739         if (!g_initErrors++) { myAlert("error: engine exception " + e); }
740         updateTablebaseFlag();
741         return false;
742      }
743   }
744}
745
746
747var moderateDefiniteThreshold = 1.85;
748var slightModerateThreshold = 0.85;
749var equalSlightThreshold = 0.25;
750
751var useNAGeval = (NAGstyle != 'default');
752function ev2NAG(ev) {
753   if ((ev === null) || (ev === "") || (isNaN(ev = parseFloat(ev)))) { return ""; }
754   if (!useNAGeval) { return (ev > 0 ? "+" : "") + ev + (ev == Math.floor(ev) ? ".0" : ""); }
755   if (ev < -moderateDefiniteThreshold) { return NAG[19]; } // -+
756   if (ev >  moderateDefiniteThreshold) { return NAG[18]; } // +-
757   if (ev < -slightModerateThreshold)   { return NAG[17]; } // -/+
758   if (ev >  slightModerateThreshold)   { return NAG[16]; } // +/-
759   if (ev < -equalSlightThreshold)      { return NAG[15]; } // =/+
760   if (ev >  equalSlightThreshold)      { return NAG[14]; } // +/=
761   return NAG[11];                                          // =
762}
763
764
765var localStorage_supported;
766try { localStorage_supported = ((enableLocalStorage) && ("localStorage" in window) && (window["localStorage"] !== null)); }
767catch (e) { localStorage_supported = false; }
768
769var cache_local_storage_prefix = "pgn4web_chess_engine_cache_"; // default "pgn4web_chess_engine_cache_"
770
771function load_cache_from_localStorage() {
772   if (!localStorage_supported) { return; }
773   if (pgn4web_chess_engine_id != localStorage[cache_local_storage_prefix + "id"]) {
774      clear_cache_from_localStorage();
775      localStorage[cache_local_storage_prefix + "id"] = pgn4web_chess_engine_id;
776      return;
777   }
778   if (cache_pointer = localStorage[cache_local_storage_prefix + "pointer"]) {
779      cache_pointer = parseInt(cache_pointer, 10) % cache_max;
780   } else { cache_pointer = -1; }
781   if (cache_fen = localStorage[cache_local_storage_prefix + "fen"]) {
782      cache_fen = cache_fen.split(",");
783   } else { cache_fen = new Array(); }
784   if (cache_ev = localStorage[cache_local_storage_prefix + "ev"]) {
785      cache_ev = cache_ev.split(",").map(parseFloat);
786      if (typeof(cache_ev.map == "function")) { cache_ev = cache_ev.map(parseFloat); }
787   } else { cache_ev = new Array(); }
788   if (cache_pv = localStorage[cache_local_storage_prefix + "pv"]) {
789      cache_pv = cache_pv.split(",");
790   } else { cache_pv = new Array(); }
791   if (cache_depth = localStorage[cache_local_storage_prefix + "depth"]) {
792      cache_depth = cache_depth.split(",");
793      if (typeof(cache_depth.map == "function")) { cache_depth = cache_depth.map(parseFloat); }
794   } else { cache_depth = new Array(); }
795   cache_needs_sync = 0;
796   if ((cache_fen.length !== cache_ev.length) || (cache_fen.length !== cache_pv.length) || (cache_fen.length !== cache_depth.length)) {
797      clear_cache_from_localStorage();
798      cache_clear();
799   }
800}
801
802function save_cache_to_localStorage() {
803   if (!localStorage_supported) { return; }
804   if (!cache_needs_sync) { return; }
805   localStorage[cache_local_storage_prefix + "pointer"] = cache_pointer;
806   localStorage[cache_local_storage_prefix + "fen"] = cache_fen.toString();
807   localStorage[cache_local_storage_prefix + "ev"] = cache_ev.toString();
808   localStorage[cache_local_storage_prefix + "pv"] = cache_pv.toString();
809   localStorage[cache_local_storage_prefix + "depth"] = cache_depth.toString();
810   cache_needs_sync = 0;
811}
812
813function clear_cache_from_localStorage() {
814   if (!localStorage_supported) { return; }
815   localStorage.removeItem(cache_local_storage_prefix + "pointer");
816   localStorage.removeItem(cache_local_storage_prefix + "fen");
817   localStorage.removeItem(cache_local_storage_prefix + "ev");
818   localStorage.removeItem(cache_local_storage_prefix + "pv");
819   localStorage.removeItem(cache_local_storage_prefix + "depth");
820   localStorage.removeItem(cache_local_storage_prefix + "nodes"); // backward compatibility
821   cache_needs_sync++;
822}
823
824function cacheDebugInfo() {
825   var dbg = "";
826   if (localStorage_supported) {
827      dbg += " cache=";
828      try {
829         dbg += num2string(localStorage[cache_local_storage_prefix + "pointer"].length + localStorage[cache_local_storage_prefix + "fen"].length + localStorage[cache_local_storage_prefix + "ev"].length + localStorage[cache_local_storage_prefix + "pv"].length + localStorage[cache_local_storage_prefix + "depth"].length);
830      } catch(e) {
831         dbg += "0";
832      }
833   }
834   return dbg;
835}
836
837var cache_pointer = -1;
838var cache_max = 2000; // ~ 16 games of 60 moves ~ 256KB of local storage
839var cache_fen = new Array();
840var cache_ev = new Array();
841var cache_pv = new Array();
842var cache_depth = new Array();
843
844var cache_needs_sync = 0;
845
846load_cache_from_localStorage();
847
848function validateSearchWithCache() {
849   var id = cache_fen_lastIndexOf(fenString);
850   if (id == -1) {
851      cache_last = cache_pointer = (cache_pointer + 1) % cache_max;
852      cache_fen[cache_pointer] = fenString.replace(/\s+\d+\s+\d+\s*$/, "");
853      cache_ev[cache_pointer] = g_ev;
854      cache_pv[cache_pointer] = g_pv;
855      cache_depth[cache_pointer] = g_depth;
856      cache_needs_sync++;
857   } else {
858      if (g_depth > cache_depth[id]) {
859         cache_ev[id] = g_ev;
860         cache_pv[id] = g_pv;
861         cache_depth[id] = g_depth;
862         cache_needs_sync++;
863      } else {
864         g_ev = parseFloat(cache_ev[id]);
865         g_pv = cache_pv[id];
866         g_depth = parseInt(cache_depth[id], 10);
867      }
868   }
869   if (cache_needs_sync > cache_max / 10) { save_cache_to_localStorage(); }
870}
871
872var cache_last = 0;
873function cache_fen_lastIndexOf(fenString) {
874   fenString = fenString.replace(/\s+\d+\s+\d+\s*$/, "");
875   if (fenString === cache_fen[cache_last]) { return cache_last; }
876   if (typeof(cache_fen.lastIndexOf) == "function") { return (cache_last = cache_fen.lastIndexOf(fenString)); }
877   for (var n = cache_fen.length - 1; n >= 0; n--) {
878      if (fenString === cache_fen[n]) { return (cache_last = n); }
879   }
880   return -1;
881}
882
883function cache_clear() {
884   cache_pointer = -1;
885   cache_fen = new Array();
886   cache_ev = new Array();
887   cache_pv = new Array();
888   cache_depth = new Array();
889}
890
891
892function StopBackgroundEngine() {
893   if (analysisTimeout) { clearTimeout(analysisTimeout); }
894   if (g_backgroundEngine) {
895      g_backgroundEngine.terminate();
896      g_backgroundEngine = null;
897      updateGameAnalysisFlag();
898      if ((autoPlay) && (g_pv !== "")) {
899         if (detectGameEnd(g_pv, CurrentFEN()) === true) {
900            stopAutoPlay();
901         } else {
902            addPly(g_pv.replace(/^\s*(\S+).*$/, "$1"));
903         }
904      }
905      g_pv = "";
906   }
907   updateTablebaseFlag();
908}
909
910var analysisTimeout;
911function setAnalysisTimeout(seconds) {
912   if (analysisTimeout) { clearTimeout(analysisTimeout); }
913   analysisTimeout = setTimeout("analysisTimeout = null; save_cache_to_localStorage(); StopBackgroundEngine();", seconds * 1000);
914}
915
916function StartEngineAnalysis() {
917   StopBackgroundEngine();
918   if (InitializeBackgroundEngine()) {
919      fenString = CurrentFEN();
920      g_backgroundEngine.postMessage("position " + fenString);
921      g_backgroundEngine.postMessage("analyze");
922      setAnalysisTimeout(analysisSeconds);
923   }
924   updateTablebaseFlag();
925}
926
927
928function openerCheck(skipSignature) {
929   try { // bugfix: Opera generating bogus security error
930      return ((typeof(window.opener) == "object") && (window.opener !== null) && (!window.opener.closed) && (typeof(window.opener.pgn4web_engineWinSignature) != "undefined") && ((window.opener.pgn4web_engineWinSignature === engineSignature) || (skipSignature)));
931   } catch(e) { return false; }
932}
933
934function updateGameAnalysisFlag() {
935   var theObj = document.getElementById("GameAnalysisFlag");
936   if (theObj = document.getElementById("GameAnalysisFlag")) {
937      if (g_backgroundEngine) {
938         theObj.innerHTML = "=";
939         theObj.title = "pause engine analysis";
940      } else {
941         if ((openerCheck()) && (CurrentFEN() != window.opener.CurrentFEN())) {
942            theObj.innerHTML = "+";
943            theObj.title = "update analysis board";
944         } else if (disableEngine) {
945            theObj.innerHTML = "&nbsp;";
946            theObj.title = "";
947         } else {
948            theObj.innerHTML = "&middot;";
949            theObj.title = "restart engine analysis";
950         }
951      }
952   }
953}
954
955function updateGameAutoUpdateFlag() {
956   var theObj = document.getElementById("GameAutoUpdateFlag");
957   if (theObj) {
958      if (openerCheck()) {
959         if (autoUpdate) {
960            theObj.innerHTML = "=";
961            theObj.title = "pause auto updating analysis board";
962         } else {
963            theObj.innerHTML = "+";
964            theObj.title = "start auto updating analysis board";
965         }
966
967<!-- AppCheck: updateGameAutoUpdateFlag -->
968
969      } else {
970         theObj.innerHTML = "&nbsp;";
971         theObj.title = "";
972      }
973   }
974}
975
976function updateGameFlagToMove() {
977   var theObj = document.getElementById("GameFlagToMove");
978   if (theObj) {
979      theObj.style.backgroundColor = CurrentPly % 2 ? "black" : "white";
980      theObj.title = (CurrentPly % 2 ? "black" : "white") + " to move" + (autoPlay ? ": autoplay" : "");
981   }
982}
983
984
985var firstCustomFunctionOnPgnTextLoad = true;
986function customFunctionOnPgnTextLoad() {
987   if (firstCustomFunctionOnPgnTextLoad) {
988      firstCustomFunctionOnPgnTextLoad = false;
989      setDisableEngine(disableEngine);
990      var theObj;
991      if (theObj = document.getElementById("GameAnalysis")) {
992         theObj.className = "gameAnalysis";
993      }
994      if ((!disableInputs) && (!autoPlay) && (theObj = document.getElementById("GameCustomButtons"))) {
995         theObj.className = "gameCustomButtons";
996      }
997
998      // undocumented parameter for internal use after pgn4web has started
999      thisRegExp = /(&|\?)(engineSignature|es)=([1-9][0-9]*)(&|$)/i;
1000      if (thisParamString.match(thisRegExp) !== null) {
1001         engineSignature = parseInt(unescape(thisParamString.match(thisRegExp)[3]), 10);
1002      }
1003      updateGameAnalysisFlag();
1004      updateGameAutoUpdateFlag();
1005   }
1006}
1007
1008function customFunctionOnMove() {
1009   var theObj;
1010
1011   updateGameFlagToMove();
1012
1013   if (!disableEngine) {
1014      var id = cache_fen_lastIndexOf(CurrentFEN());
1015      if (theObj = document.getElementById("GameMoves")) {
1016         theObj.innerHTML = (id != -1) ? cache_pv[id] : "";
1017         theObj.title = "";
1018      }
1019      if (theObj = document.getElementById("GameEval")) {
1020         theObj.innerHTML = (id != -1) ? ev2NAG(parseFloat(cache_ev[id])) : "";
1021         theObj.title = "";
1022      }
1023      StartEngineAnalysis();
1024   }
1025
1026   if (clickFromCol !== "") {
1027      highlightSquare("abcdefgh".indexOf(clickFromCol), "12345678".indexOf(clickFromRow), false);
1028   }
1029   clickFromCol = "";
1030   clickFromRow = "";
1031   clickFromPiece = "";
1032
1033   updateTablebaseFlag();
1034   updateGameAnalysisFlag();
1035   updateGameAutoUpdateFlag();
1036}
1037
1038function customDebugInfo() {
1039   var dbg = "autoUpdate=" + autoUpdate;
1040   dbg += " engine=";
1041   if (disableEngine) { dbg += "disabled"; }
1042   else if (!window.Worker) { dbg += "unavailable"; }
1043   else { dbg += (g_backgroundEngine ? (autoPlay ? "autoplay" : "pondering") : "idle") + " analysisSeconds=" + analysisSeconds + " topNodesPerSecond=" + num2string(g_topNodesPerSecond) + cacheDebugInfo(); }
1044   return dbg;
1045}
1046
1047function num2string(num) {
1048   var unit = "";
1049   if (num >= Math.pow(10, 12)) { num = Math.round(num / Math.pow(10, 11)) / 10;  unit = "T"; }
1050   else if (num >= Math.pow(10, 9)) { num = Math.round(num / Math.pow(10, 8)) / 10;  unit = "G"; }
1051   else if (num >= Math.pow(10, 6)) { num = Math.round(num / Math.pow(10, 5)) / 10; unit = "M"; }
1052   else if (num >= Math.pow(10, 3)) { num = Math.round(num / Math.pow(10, 2)) / 10; unit = "K"; }
1053   if ((unit !== "") && (num === Math.floor(num))) { num += ".0"; }
1054   return num + unit;
1055}
1056
1057
1058var overwrittenPly = "";
1059var overwrittenPlyNumber;
1060function addPly(thisPly) {
1061   if (!thisPly) { return; }
1062   if ((PlyNumber < CurrentPly + 1 - StartPly) || (thisPly !== Moves[CurrentPly])) {
1063      overwrittenPly = Moves[CurrentPly];
1064      Moves[CurrentPly] = MovesVar[0][CurrentPly] = thisPly;
1065      overwrittenPlyNumber = PlyNumber;
1066      PlyNumber = PlyNumberVar[0] = CurrentPly + 1 - StartPly;
1067   } else {
1068      overwrittenPly = "";
1069   }
1070   MoveForward(1);
1071}
1072
1073function customFunctionOnAlert(msg) {
1074   if (msg.indexOf("error: invalid ply") !== 0) { return; }
1075   stopAlertPrompt();
1076   if (overwrittenPly === "") { return; }
1077   Moves[CurrentPly] = MovesVar[0][CurrentPly] = overwrittenPly;
1078   PlyNumber = PlyNumberVar[0] = overwrittenPlyNumber;
1079   overwrittenPly = "";
1080}
1081
1082for (var cc=0; cc<8; cc++) { for (var rr=0; rr<8; rr++) {
1083   boardShortcut("ABCDEFGH".charAt(cc) + "12345678".charAt(rr), "explore variations by clicking from/to squares for the intended move", detectClick, false);
1084} }
1085
1086var doubleclickH8Timeout = null;
1087var clickFromCol = "";
1088var clickFromRow = "";
1089var clickFromPiece = "";
1090function detectClick(t,e) {
1091   if (disableInputs) { return; }
1092   var matches = t.id.match(/img_tcol([0-7])trow([0-7])/);
1093   if (!matches) { return; }
1094   var thisCol = IsRotated ? 7 - matches[1] : matches[1];
1095   var thisColChar = "abcdefgh".charAt(thisCol);
1096   var thisRow = IsRotated ? matches[2] : 7 - matches[2];
1097   var thisRowChar = "12345678".charAt(thisRow);
1098   if (e !== null) {
1099      if (doubleclickH8Timeout !== null) {
1100         clearTimeout(doubleclickH8Timeout);
1101         doubleclickH8Timeout = null;
1102         if ((thisColChar == "h") && (thisRowChar == "8")) {
1103            displayHelp();
1104            return;
1105         }
1106      }
1107      if ((thisColChar == "h") && (thisRowChar == "8")) {
1108         doubleclickH8Timeout = setTimeout('doubleclickH8Timeout = null;', 333);
1109      }
1110   }
1111   if (clickFromCol !== "") {
1112      var thisCurrentPly = CurrentPly;
1113      setTimeout('highlightSquare("abcdefgh".indexOf("' + clickFromCol + '"), "12345678".indexOf("' + clickFromRow + '"), false);', 77);
1114      var clickToCol = thisColChar;
1115      var clickToRow = thisRowChar;
1116      if ((clickFromCol !== clickToCol) || (clickFromRow !== clickToRow)) {
1117         var thisMove = clickFromPiece + clickFromCol + clickFromRow + clickToCol + clickToRow;
1118         if (thisMove == "Ke1g1") { thisMove = "O-O"; }
1119         else if (thisMove == "Ke1c1") { thisMove = "O-O-O"; }
1120         else if (thisMove == "Ke8g8") { thisMove = "O-O"; }
1121         else if (thisMove == "Ke8c8") { thisMove = "O-O-O"; }
1122         else if (("KQRBN".indexOf(thisMove.charAt(0)) == -1) && (thisMove.charAt(3) == (CurrentPly % 2 ? "1" : "8"))) {
1123            thisMove += "Q";
1124         }
1125         if (autoPlay) { stopAutoPlay(); }
1126         addPly(thisMove);
1127      }
1128      clickFromCol = clickFromRow = clickFromPiece = "";
1129      if (CurrentPly === thisCurrentPly) {
1130         detectClick({id: "img_tcol" + matches[1] + "trow" + matches[2]}, null);
1131      }
1132   } else if ((CurrentPly > StartPly) && (matches = Moves[CurrentPly - 1].match(new RegExp("^(.*" + thisColChar + thisRowChar + ")([QRBN])$", "")))) {
1133      MoveBackward(1);
1134      addPly(matches[1] + "RBNQ".charAt("QRBN".indexOf(matches[2])));
1135   } else {
1136      cc = CurrentPly % 2;
1137      for (var ii=0; ii<16; ii++) {
1138         if ((PieceCol[cc][ii] == thisCol) && (PieceRow[cc][ii] == thisRow)) {
1139            if (PieceType[cc][ii] != -1) {
1140               clickFromPiece = " KQRBNP".charAt(PieceType[cc][ii]);
1141               if (clickFromPiece == "P") { clickFromPiece = ""; }
1142               clickFromCol = thisColChar;
1143               clickFromRow = thisRowChar;
1144               setTimeout('highlightSquare(' + thisCol + ', ' + thisRow + ', true);', 77);
1145            }
1146         }
1147      }
1148   }
1149}
1150
1151function detectGameEnd(pv, FEN) {
1152   if ((pv !== "") && (pv.match(/^[#=]/))) { return true; }
1153   var matches = FEN.match(/\s*\S+\s+\S+\s+\S+\s+\S+\s+(\d+)\s+\S+\s*/);
1154   if (matches) {
1155      if (parseInt(matches[1], 10) > 100) { return true; }
1156   }
1157   return false;
1158}
1159
1160function startAutoPlay() {
1161   if ((disableEngine) || (!window.Worker)) { return; }
1162   var theObj = document.getElementById("GameCustomButtons");
1163   if (theObj) {
1164      theObj.className = "gameCustomButtons gameCustomButtonsHidden";
1165   }
1166   autoPlay = true;
1167   updateGameFlagToMove();
1168   if (autoUpdate) {
1169      autoUpdate = false;
1170      updateGameAutoUpdateFlag();
1171   }
1172   if (!g_backgroundEngine) { StartEngineAnalysis(); }
1173}
1174
1175function stopAutoPlay() {
1176   autoPlay = false;
1177   StopBackgroundEngine();
1178   updateGameFlagToMove();
1179   var theObj = document.getElementById("GameCustomButtons");
1180   if ((!disableInputs) && (!autoPlay) && (theObj)) {
1181      theObj.className = "gameCustomButtons";
1182   }
1183}
1184
1185
1186var engineSignature = -1;
1187
1188function updateFEN(newFEN) {
1189   if (autoPlay) { stopAutoPlay(); }
1190   var theObj = document.getElementById("pgnText");
1191   if (theObj) {
1192      theObj.innerHTML = '[Setup "1"]\n[FEN "' + newFEN + '"]\n';
1193   }
1194   firstStart = true;
1195   start_pgn4web();
1196}
1197
1198function setDisableEngine(de) {
1199   if (disableEngine = de) {
1200      if (autoPlay) { stopAutoPlay(); }
1201      else { StopBackgroundEngine(); }
1202      var theObj;
1203      if (theObj = document.getElementById("GameEval")) {
1204         theObj.innerHTML = useNAGeval ? translateNAGs("$147") : "X";
1205         theObj.title = "engine analysis disabled";
1206      }
1207      if (theObj = document.getElementById("GameMoves")) {
1208         theObj.innerHTML = "";
1209         theObj.title = "";
1210      }
1211      updateGameAnalysisFlag();
1212   } else {
1213      StartEngineAnalysis();
1214   }
1215}
1216
1217function sameEngineDisabled(engineDisabled) {
1218   return ((typeof(engineDisabled) == "undefined") || (engineDisabled && disableEngine) || (!engineDisabled && !disableEngine));
1219}
1220
1221
1222function customFunctionOnTouch(deltaX, deltaY) {
1223   if (disableInputs) { return; }
1224   if (Math.max(Math.abs(deltaX), Math.abs(deltaY)) < 13) { return; }
1225   if (Math.abs(deltaY) > 1.5 * Math.abs(deltaX)) { // vertical up or down
1226      GoToMove(StartPlyVar[CurrentVar] + (deltaY > 0 ? PlyNumberVar[CurrentVar] : 0));
1227   } else if (Math.abs(deltaX) > 1.5 * Math.abs(deltaY)) { // horizontal left or right
1228      GoToMove(CurrentPly + sign(deltaX));
1229   }
1230}
1231
1232// touchGestures_helpActions unchanged
1233touchGestures_helpText = [ "go to variation end", "go to variation start", "move forward", "move backward" ];
1234
1235<!-- AppCheck: footer -->
1236
1237</script>
1238
1239</body>
1240
1241</html>
1242