1 | <?php |
---|
2 | /********************************************************************************** |
---|
3 | * @Subject Parse options and generate HTML iframe to run board.html |
---|
4 | * @package pgn4spip plugin to embed pgn4web Chessboard in a SPIP 2.x or 3.x article |
---|
5 | * @version 2.61.0 |
---|
6 | * @copyright copyright (c) 2012 Matt Chesstale |
---|
7 | * @license GNU General Public License version 3 |
---|
8 | * @compatible SPIP 2.x and CFG 1.15 or SPIP 3.x |
---|
9 | * Penknife or Swiss Army Knife 1.8 if pgn4spip is enabled after Swiss Knife |
---|
10 | * If you see .pgn header with French double quotes, disable and reenable pgn4spip. |
---|
11 | * @language PHP for SPIP 2 or 3 |
---|
12 | * |
---|
13 | * @history: |
---|
14 | * 2.61.0: Initial version for SPIP 2.1 |
---|
15 | * @credits: Paolo Casaschi |
---|
16 | * @reference: http://pgn4web.casaschi.net |
---|
17 | * @tutorial: http://code.google.com/p/pgn4web/wiki/User_Notes_joomla |
---|
18 | * @Usage |
---|
19 | In a SPIP article, enter the following tag: |
---|
20 | [pgn parameter1=value1 prm2=value2 ...] chess games notation in PGN format [/pgn] |
---|
21 | |
---|
22 | Tag parameters: see the configuration of the module in CFG for the help |
---|
23 | layout=horizontal|vertical |
---|
24 | height=auto|'number' |
---|
25 | movesDisplay=figurine|text|puzzle|hidden |
---|
26 | initialGame=first|last|random|'number' |
---|
27 | initialVariation='number' |
---|
28 | initialHalfmove=start|end|random|comment|'number' |
---|
29 | autoplayMode=game|loop|none |
---|
30 | |
---|
31 | Example in a SPIP article: |
---|
32 | Before |
---|
33 | [pgn autoplayMode=loop initialHalfmove=end] 1. e4 Nf6 2. e5 Nd5 [/pgn] |
---|
34 | After |
---|
35 | **********************************************************************************/ |
---|
36 | if (!defined("_ECRIRE_INC_VERSION")) return; // No direct access allowed to this file |
---|
37 | |
---|
38 | define('PLUGIN_Name', "pgn4spip"); |
---|
39 | define('PATH_Conf', PLUGIN_Name . '/' . PLUGIN_Name . '_conf.php'); |
---|
40 | if (!function_exists('ReadCurrentConfiguration')) require _DIR_PLUGINS . PATH_Conf; |
---|
41 | |
---|
42 | define('TAG_pgn', "[pP][gG][nN]"); // pgn or PGN |
---|
43 | define('PATH_board', PLUGIN_Name . "/pgn4web/board.html"); |
---|
44 | define('PATH_live', PLUGIN_Name . "/boardLive.html"); |
---|
45 | define('indFullPattern', 0); |
---|
46 | define('indPgnOption', 1); |
---|
47 | define('indPgnGame', 2); |
---|
48 | define('NO_iFrame', "your web browser or your host do not support iframes as required to display the chessboard"); |
---|
49 | define('DEFAULT_HEIGHT', 60); // 3*2 border + 13*2 padding + 28 buttons without 26*8 squares |
---|
50 | |
---|
51 | define('CLS_CHESSS', "class='chessboard-wrapper'"); // chessboard-wrapper class for the div |
---|
52 | |
---|
53 | // Parse options, prepare HTML content for output, generate the HTML iframe |
---|
54 | // pipeline "pre_propre" is called for the description of the article then its body before "post_propre" |
---|
55 | // @in: $flux the full article including the [pgn] ... [/pgn] tags |
---|
56 | // @return $flux with the pgn tags replaced with the HTML iframe to run board.html |
---|
57 | function pgn4spip_prepropre($flux) |
---|
58 | { |
---|
59 | global $optValue; // Values of options |
---|
60 | |
---|
61 | if ($optValue == NULL) // True in SPIP 3 since pgn4spip_options.php is not called |
---|
62 | { // Init $optValue with default values of options overriden with the current configuration |
---|
63 | ReadCurrentConfiguration($optValue); |
---|
64 | } |
---|
65 | $regex = "@\[" . TAG_pgn . "(.*?)\](.*?)\[/" . TAG_pgn . "\]@s"; // Expression to search for |
---|
66 | preg_match_all($regex, $flux, $matches); // find all instances of plugin and put in $matches |
---|
67 | $countPlugins = count($matches[2] ); // Number of plugins |
---|
68 | if ($countPlugins > 0) // PGN game(s) found? |
---|
69 | { |
---|
70 | NameOptionPrm(/* out */ $optName, $skipPrms, $pgnPrms); |
---|
71 | for ( $indPlugin=0; $indPlugin < $countPlugins; $indPlugin++ ) // For each game |
---|
72 | { |
---|
73 | $pgnOptionsInput = $matches[indPgnOption][$indPlugin]; |
---|
74 | $pgnOptions = OptionParser($pgnOptionsInput, $optName); |
---|
75 | $pgnSource = ExtendedOptionParser($pgnOptions, $optName, $skipPrms, $pgnPrms, |
---|
76 | /* out */ $isNewPgnSource, $pgnSource); |
---|
77 | $pgnText = SpipSpecific($matches[indPgnGame][$indPlugin], /* out */ $idTxtArea); |
---|
78 | $height = $optValue['fh']; // Either "" for automatic evaluation of the height or fh=nbr |
---|
79 | $isLive = IsMatchFirst($optValue['md'], 'live'); |
---|
80 | $optValue['fh'] = FrameHeightEval($pgnText, $isLive, $optValue['hl'], $isNewPgnSource, $optValue['md'], |
---|
81 | /* i/o */ $height); // The return value could be not numeric like "b" |
---|
82 | $strHtml = GenHtml($pgnText, $idTxtArea, $isLive, $isNewPgnSource, $pgnSource, $height); |
---|
83 | $flux = str_replace($matches[indFullPattern][$indPlugin], $strHtml, $flux); |
---|
84 | InitOptionValueByDefault($optValue); // Restore values by default defined in PATH_check module |
---|
85 | } |
---|
86 | } |
---|
87 | return $flux; |
---|
88 | } |
---|
89 | |
---|
90 | // Generate the HTML code of the div including the iframe calling board.html with several options |
---|
91 | // @in: $pgnText the game in PGN format |
---|
92 | // $idTxtArea id of the pgnText |
---|
93 | // $isLive true if live broadcast otherwise false |
---|
94 | // $isNewPgnSource true if extendedoptions |
---|
95 | // $pgnSource special option(s) to indicate the (remote) source of PGN if extendedoptions |
---|
96 | // $height the height of the iframe module |
---|
97 | // @return the HTML iframe to run board.html |
---|
98 | function GenHtml($pgnText, $idTxtArea, $isLive, $isNewPgnSource, $pgnSource, $height) |
---|
99 | { |
---|
100 | global $cs, $optValue; |
---|
101 | |
---|
102 | if ($isLive) |
---|
103 | { |
---|
104 | $pathBoard = PATH_live; // Relative path of the HTML script boardLive.html |
---|
105 | $optValue['md'] = "f"; // (f)igurine in live mode |
---|
106 | } |
---|
107 | else |
---|
108 | $pathBoard = PATH_board; // Relative path of the HTML script board.html |
---|
109 | if (strlen($cs) == 0) |
---|
110 | $csDef = " "; |
---|
111 | else |
---|
112 | $csDef = " style='" . $cs . "' "; // containerStyle |
---|
113 | |
---|
114 | $strHtml = "<div" . $csDef . CLS_CHESSS . ">"; |
---|
115 | if (!$isNewPgnSource) |
---|
116 | { |
---|
117 | $pgnId = "pgn4web_" . $idTxtArea; |
---|
118 | $strHtml .= "<textarea id='" . $pgnId . "' style='display:none;' rows='40' cols='8'>" . $pgnText . "</textarea>\n"; |
---|
119 | } |
---|
120 | $strHtml .= "<iframe src='" . _DIR_PLUGINS . $pathBoard . "?"; |
---|
121 | $isFirstOption = true; |
---|
122 | foreach ($optValue as $optShort => $value) |
---|
123 | { |
---|
124 | if (($optShort != "l") && // (l)ayout already encoded by (h)orizontal(L)ayout |
---|
125 | (($optShort[0] != "r") || $isLive)) |
---|
126 | { |
---|
127 | if (!$isFirstOption) $strHtml .= "&"; // Separator between two parameters |
---|
128 | $strHtml .= $optShort . "=" . rawurlencode($value); |
---|
129 | $isFirstOption = false; |
---|
130 | } |
---|
131 | } |
---|
132 | if (!$isNewPgnSource) |
---|
133 | $strHtml .= "&pi=" . rawurlencode($pgnId); |
---|
134 | |
---|
135 | $strHtml .= $pgnSource . "'\n"; |
---|
136 | $strHtml .= "frameborder='0' width='100%' height='" . $height . "' "; |
---|
137 | $strHtml .= "scrolling='no' marginheight='0' marginwidth='0'>" . NO_iFrame . "</iframe>"; |
---|
138 | $strHtml .= "</div>"; |
---|
139 | |
---|
140 | return $strHtml; |
---|
141 | } |
---|
142 | |
---|
143 | // @return true if the option matches with the value or the first character of the value |
---|
144 | // @in: @option the value of the option |
---|
145 | // @value the value for comparison |
---|
146 | function IsMatchFirst($option, $value) |
---|
147 | { |
---|
148 | if ($option == $value) |
---|
149 | return true; |
---|
150 | if ($option == $value[0]) |
---|
151 | return true; |
---|
152 | return false; |
---|
153 | } |
---|
154 | |
---|
155 | // Parse the options inside the [pgn prm1=value1 prm2=value2 ...] tag |
---|
156 | // Update the values in $optValue, the array of options |
---|
157 | // @in: $pgnOptionsInput the specified options in the pgn tag |
---|
158 | // $optName array of long names of options, an alternative to the short names |
---|
159 | // @return the pgn options without HTML tags |
---|
160 | function OptionParser($pgnOptionsInput, $optName) |
---|
161 | { |
---|
162 | global $optValue; |
---|
163 | |
---|
164 | $pgnOptions = preg_replace('@(pgnData=|pd=)<a href="([^"]+)".*</a>@i', "$1$2", $pgnOptionsInput); // Fix Swiss Knife MailCrypt |
---|
165 | $pgnOptions = preg_replace("@<.*?>@", " ", $pgnOptions); // Remove HTML tags |
---|
166 | foreach ($optValue as $optShort => $value) |
---|
167 | { |
---|
168 | if (preg_match("#(^|\s)(" . $optName[$optShort] . "|" . $optShort . ")=(.*?)(\s|$)#si", $pgnOptions, $thisOption)) |
---|
169 | { |
---|
170 | $valueNew = $thisOption[3]; |
---|
171 | if (IsOptionValueOk($optShort, $valueNew, $optValue[$optShort])) |
---|
172 | $optValue[$optShort] = $valueNew; |
---|
173 | } |
---|
174 | } |
---|
175 | // Rules between options |
---|
176 | if (IsMatchFirst($optValue['l'], "vertical")) // vertical (l)ayout? |
---|
177 | $optValue['hl'] = "f"; // (f)alse: vertical |
---|
178 | elseif (IsMatchFirst($optValue['l'], "horizontal")) // horizontal (l)ayout? |
---|
179 | $optValue['hl'] = "t"; // (t)rue: horizontal |
---|
180 | if (preg_match("#(^|\s)(height|h)=(.*?)(\s|$)#si", $pgnOptions, $thisOption)) |
---|
181 | $height = $thisOption[3]; |
---|
182 | if (IsMatchFirst($optValue['md'], "puzzle")) |
---|
183 | $optValue['hd'] = "v"; // headerDisplay <- (v)ariations |
---|
184 | return $pgnOptions; |
---|
185 | } |
---|
186 | |
---|
187 | // Compatibility with the SPIP plugin Swiss Army Knife a.k.a PenKnife |
---|
188 | // @in: $pgnText eventually with left curly inverted commas ou French double quotes << guillemets >> |
---|
189 | // @return $pgnText with only '"' instead of « » “ ” generated by Swiss Knife |
---|
190 | // Tool "Curly inverted commas" |
---|
191 | // @see http://www.degraeve.com/reference/specialcharacters.php |
---|
192 | function CompatibleSwissKnife($pgnText) |
---|
193 | { |
---|
194 | $pgnText = str_replace('« ', '"', $pgnText); // Left angle quote + " " to double quote |
---|
195 | $pgnText = str_replace('“', '"', $pgnText); // Left double quote |
---|
196 | $pgnText = str_replace(' »', '"', $pgnText); // " " + Right angle quote |
---|
197 | $pgnText = str_replace('”', '"', $pgnText); // Right double quote |
---|
198 | |
---|
199 | $pgnText = str_replace('’', "'", $pgnText); // Left single quote to single quote |
---|
200 | $pgnText = str_replace('‘', "'", $pgnText); // Left single quote |
---|
201 | $pgnText = str_replace('’', "'", $pgnText); // Right single quote |
---|
202 | return $pgnText; |
---|
203 | } |
---|
204 | |
---|
205 | // PGN comments are not processed as italic by SPIP. No empty line and HTML tags |
---|
206 | // @in: $pgnText eventually with HTML tags, emtpy lines, PGN comments between {...}, quotes by Swiss Knife |
---|
207 | // @out: $idTxtArea hexa identifier based on crc of the pgn text before replacing {...} |
---|
208 | // @return $pgnText without HTML tags, empty lines, quotes by Swiss Knife but protected PGN comments |
---|
209 | function SpipSpecific($pgnText, &$idTxtArea) |
---|
210 | { |
---|
211 | $pgnText = trim(preg_replace("@<.*?>@", " ", $pgnText)); // Remove HTML tags in the PGN game |
---|
212 | // No empty line processed as <p>...</p> by other SPIP plugins |
---|
213 | $pgnText = preg_replace("@(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n\']+@", "\r\n", $pgnText); |
---|
214 | $pgnText = CompatibleSwissKnife($pgnText); |
---|
215 | $idTxtArea = dechex(crc32($pgnText)); |
---|
216 | $pgnText = str_replace("{", "{", $pgnText); // Replace "{PGN comment}" with |
---|
217 | $pgnText = str_replace("}", "}", $pgnText); // "{PGN comment}" to avoid SPIP italic |
---|
218 | return $pgnText; |
---|
219 | } |
---|
220 | |
---|
221 | // Is there a new source for the PGN? |
---|
222 | // @in: the pgn options without HTML tags |
---|
223 | // $optName array of long names of options, an alternative to the short names |
---|
224 | // $skipPrms other options (extendedoptions, height) to be skiped |
---|
225 | // $pgnPrms pgn options to indicate alternative (remote) source |
---|
226 | // @out: $isNewPgnSource true if extendedoptions |
---|
227 | // @return $pgnSource special option(s) to indicate the (remote) (URL) source of PGN if extendedoptions |
---|
228 | function ExtendedOptionParser($pgnOptions, $optName, $skipPrms, $pgnPrms, &$isNewPgnSource, &$pgnSource) |
---|
229 | { |
---|
230 | global $extOpt; |
---|
231 | |
---|
232 | $isNewPgnSource = false; |
---|
233 | $pgnSource = trim($extOpt); |
---|
234 | if ($pgnSource != '') |
---|
235 | { |
---|
236 | $pgnSource = preg_replace('/^\s+/', '', $pgnSource); |
---|
237 | $pgnSource = preg_replace('/\s+$/', '', $pgnSource); |
---|
238 | $pgnSource = preg_replace('/&/', ' ', $pgnSource); |
---|
239 | } |
---|
240 | foreach ($skipPrms as $optShort => $optLong) |
---|
241 | { |
---|
242 | if (preg_match("#(^|\s)(" . $optLong . "|" . $optShort . ")=(.*?)(\s|$)#si", $pgnOptions, $thisOption)) |
---|
243 | { |
---|
244 | $valueNew = $thisOption[3]; |
---|
245 | switch ($optShort) |
---|
246 | { |
---|
247 | case "eo": if (IsMatchFirst($valueNew, 'true')) // Extended options? |
---|
248 | $pgnSource .= ' ' . $pgnOptions; |
---|
249 | break; |
---|
250 | } |
---|
251 | } |
---|
252 | } |
---|
253 | if (strlen($pgnSource) > 0) |
---|
254 | { |
---|
255 | foreach ($optName as $optShort => $optLong) |
---|
256 | $pgnSource = preg_replace('#(^|\s+)' . $optShort . '|' . $optLong . '=\S*#si', '', $pgnSource); |
---|
257 | |
---|
258 | foreach ($skipPrms as $optShort => $optLong) |
---|
259 | $pgnSource = preg_replace('#(^|\s+)' . $optShort . '|' . $optLong . '=\S*#si', '', $pgnSource); |
---|
260 | |
---|
261 | foreach ($pgnPrms as $optShort => $optLong) |
---|
262 | { |
---|
263 | if (preg_match('#(^|\s+)' . $optShort . '|' . $optLong . '=\S*#si', $pgnSource)) |
---|
264 | { |
---|
265 | $isNewPgnSource = true; |
---|
266 | break; |
---|
267 | } |
---|
268 | } |
---|
269 | if (strlen($pgnSource) > 0) |
---|
270 | $pgnSource = ' ' . $pgnSource; |
---|
271 | |
---|
272 | $pgnSource = rtrim($pgnSource); // No need to add "&" at the right of the last parameter |
---|
273 | $pgnSource = preg_replace('#\s+#si', '&', $pgnSource); |
---|
274 | } |
---|
275 | return $pgnSource; |
---|
276 | } |
---|
277 | |
---|
278 | // Evaluate the frame height of the iframe module |
---|
279 | // @in: $pgnText PGN game |
---|
280 | // $isLive true if $md="live" |
---|
281 | // $hl horizontalLayout "t" or "f" |
---|
282 | // $isNewPgnSource true if extendedOptions then +34 for the height |
---|
283 | // $md movesDisplay not hidden -> +300 for the height |
---|
284 | // @return $height = fh, the (f)rame(H)eight of the module |
---|
285 | // "b" for (b)oard for vertical scrolling of the game if horizontalLayout |
---|
286 | function FrameHeightEval($pgnText, $isLive, $hl, $isNewPgnSource, $md, /* i/o */ &$height) |
---|
287 | { |
---|
288 | global $optValue; |
---|
289 | |
---|
290 | if (!is_numeric($height)) |
---|
291 | { |
---|
292 | $ss = $optValue['ss']; |
---|
293 | if (!is_numeric($ss)) |
---|
294 | $ss = DEFAULT_ss; // default size for squareSize |
---|
295 | $ss = intval($ss); |
---|
296 | $height = DEFAULT_HEIGHT + $ss * 8; |
---|
297 | if ($isLive) |
---|
298 | { // GameSelector + eventDetails + playersDetails + statusDetails + padding |
---|
299 | $height += 23 + 18 + 17 + 17 + 10; |
---|
300 | if ($hl == "f") // Vertical layout? |
---|
301 | $height += 150; // Room for the PGN moves under statusDetails |
---|
302 | } |
---|
303 | else |
---|
304 | { |
---|
305 | // guessing if one game or multiple games are supplied |
---|
306 | $multiGamesRegexp = '/\s*\[\s*\w+\s*"[^"]*"\s*\]\s*[^\s\[\]]+[\s\S]*\[\s*\w+\s*"[^"]*"\s*\]\s*/'; |
---|
307 | if ($isNewPgnSource || (preg_match($multiGamesRegexp, $pgnText) > 0)) { $height += 34; } |
---|
308 | if ($hl == "t") // Horizontal layout? |
---|
309 | return "b"; // (b)oard required for vertical scrolling bar |
---|
310 | |
---|
311 | $height += 75; // header |
---|
312 | if (!IsMatchFirst($md, 'hidden')) { $height += 300; } // moves |
---|
313 | } |
---|
314 | } |
---|
315 | return $height; |
---|
316 | } |
---|
317 | |
---|
318 | // Associate to each short name of options a long name that can be used as paramater of the [pgn prm=value] tag |
---|
319 | // @out: $optName array of long names of options in alphabetic order, an alternative to the short names |
---|
320 | // $skipPrms other options (extendedoptions, height) to be skiped |
---|
321 | // $pgnPrms pgn options to indicate alternative (remote) source |
---|
322 | // @return nothing |
---|
323 | function NameOptionPrm(&$optName, &$skipPrms, &$pgnPrms) |
---|
324 | { |
---|
325 | // the [value by default] is in square brackets in first position in the list after // |
---|
326 | $optName = array( // of long names of options, an alternative to the short names |
---|
327 | 'am' => 'autoplayMode', // [n]one |(g)ame | (l)oop |
---|
328 | 'bbch' => 'boardBorderColor', // [E0E0E0] was 000000 |
---|
329 | 'bd' => 'buttonsDisplay', // [c]ustom, (h)idden, (s)tandard |
---|
330 | 'bch' => 'backgroundColorHex', // [F6F6F6] was FFFFFF, (t)ransparent to use the parent's background color |
---|
331 | 'bsch' => 'boardShadowColor', // [t]ransparent no shadow | (b)order | nbr |
---|
332 | 'cbch' => 'controlBackgroundColor', // [F0F0F0] was standard buttons |
---|
333 | 'cd' => 'commentsDisplay', // [n]ewline, (i)nline,(h)idden |
---|
334 | 'ctch' => 'controlTextColor', // [696969] was 000000 |
---|
335 | 'd' => 'delay', // [3000] was 1000 in ms |
---|
336 | 'dch' => 'darkColor', // [E0E0E0] was C6CEC3 |
---|
337 | 'fcch' => 'fontCommentsColor', // [000080] |
---|
338 | 'fcs' => 'fontCommentsSize', // [m]oves <- fontMovesSize |
---|
339 | 'fh' => 'frameHeight', // [""], (p)age, (b)oard, nbr, overriding textHeight. See FrameHeightEval() |
---|
340 | 'fhch' => 'fontHeaderColor', // [000000] |
---|
341 | 'fhs' => 'fontHeaderSize', // [14] was 16 |
---|
342 | 'fmch' => 'fontMovesColor', // [000000] |
---|
343 | 'fms' => 'fontMovesSize', // [14] was 16 |
---|
344 | 'fp' => 'framePadding', // [13] was 0 |
---|
345 | 'fw' => 'frameWidth', // [p]age, (b)oard, nbr, overriding textHeight |
---|
346 | 'hch' => 'highlightColor', // [ABABAB] was DAF4D7 |
---|
347 | 'hd' => 'headerDisplay', // [j]ustified | (h)idden | (c)entered | (l)ive | (v)ariations |
---|
348 | 'hl' => 'horizontalLayout', // [t]rue layout=[h]orizontal, (f)alse layout=(v)ertical |
---|
349 | 'hm' => 'highlightMode', // [b]order, (s)quare, (n)one |
---|
350 | 'hmch' => 'highlightMovesColor', // [E0E0E0] was DAF4D7 | (b)ackground for no highligh |
---|
351 | 'ig' => 'initialGame', // [f]irst | (l)ast | (r)andom |
---|
352 | 'ih' => 'initialHalfmove', // [s]tart | (e)nd | (r)andom | (c)omment | (v)ariation | nbr |
---|
353 | 'iv' => 'initialVariation', // [0] |
---|
354 | 'l' => 'layout', // [h]orizontal, (v)ertical. See OptionParser() |
---|
355 | 'lch' => 'lightColor', // [F6F6F6] was EFF4EC. suffix 'h' stands for hex |
---|
356 | 'md' => 'movesDisplay', // [f]igurine | (t)ext | (p)uzzle | (h)idden | (l)ive |
---|
357 | 'pf' => 'pieceFont', // [d]efault based on pieceSize | (a)lpha | (m)erida | (u)scf | (s)vgchess |
---|
358 | 'ps' => 'pieceSize', // [d]efault <- squareSize |
---|
359 | 'rd' => 'refreshDemo', // [f]alse | (t)rue if md="t" |
---|
360 | 'rm' => 'refreshMinutes', // [1] minute if md="t" |
---|
361 | 'ss' => 'squareSize', // [26] was 28 |
---|
362 | // textHeight: nbr, optional if frameHeight |
---|
363 | 'tm' => 'textMargin' // [13] was 0. Set left/right margin width of the textual section, header and/or moves text |
---|
364 | // textWidth: nbr, optional if frameWidth |
---|
365 | ); |
---|
366 | $skipPrms = array( // of other options (extendedOptions, height) to be skiped |
---|
367 | 'eo' => 'extendedOptions', // [f]alse, (t)rue |
---|
368 | 'h' => 'height' // [auto] = DEFAULT_HEIGHT. Height of the module |
---|
369 | ); |
---|
370 | $pgnPrms = array( // of pgn options to indicate alternative (remote) source |
---|
371 | 'fs' => 'fenString', // Initial position in format FEN |
---|
372 | 'pd' => 'pgnData', // URL of the PGN file |
---|
373 | 'pe' => 'pgnEncoded', // PGN game encoded |
---|
374 | 'pi' => 'pgnId', // id of the pgnText textarea |
---|
375 | 'pt' => 'pgnText' // PGN game |
---|
376 | ); |
---|
377 | } |
---|
378 | ?> |
---|