kokopu-react 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/dist/lib/ArrowMarkerIcon.js +3 -3
  4. package/dist/lib/Chessboard.js +6 -8
  5. package/dist/lib/ErrorBox.js +85 -5
  6. package/dist/lib/Movetext.js +851 -0
  7. package/dist/lib/SquareMarkerIcon.js +1 -1
  8. package/dist/lib/TextMarkerIcon.js +1 -1
  9. package/dist/lib/css/arrow.css +1 -1
  10. package/dist/lib/css/chessboard.css +6 -1
  11. package/dist/lib/css/error_box.css +13 -2
  12. package/dist/lib/css/{label.css → fonts.css} +11 -6
  13. package/dist/lib/css/movetext.css +117 -0
  14. package/dist/lib/css/symbol.css +9 -1
  15. package/dist/lib/fonts/chessalpha.woff +0 -0
  16. package/dist/lib/fonts/chessalpha.woff2 +0 -0
  17. package/dist/lib/i18n.js +17 -2
  18. package/dist/lib/impl/ArrowTip.js +1 -1
  19. package/dist/lib/impl/HtmlSanitizer.js +171 -0
  20. package/dist/lib/impl/TextSymbol.js +2 -4
  21. package/dist/lib/impl/colorsets.js +1 -1
  22. package/dist/lib/impl/piecesets.js +1 -1
  23. package/dist/lib/impl/util.js +1 -1
  24. package/dist/lib/index.js +8 -0
  25. package/doc_src/demo/PageChessboardBase.js +1 -1
  26. package/doc_src/demo/{PageChessboardEdition.js → PageChessboardInteraction.js} +2 -4
  27. package/doc_src/demo/PageChessboardMove.js +1 -1
  28. package/doc_src/demo/PageChessboardSmallScreens.js +1 -1
  29. package/doc_src/demo/PageMovetextBase.js +214 -0
  30. package/doc_src/demo/PageMovetextInteraction.js +159 -0
  31. package/doc_src/demo/demo.css +8 -1
  32. package/doc_src/demo/game-1.pgn +23 -0
  33. package/doc_src/demo/game-2.pgn +22 -0
  34. package/doc_src/demo/game-invalid.pgn +10 -0
  35. package/doc_src/demo/util.js +1 -1
  36. package/doc_src/examples/Movetext.md +15 -0
  37. package/doc_src/home.md +2 -0
  38. package/doc_src/theming/LogoRenderer.js +1 -1
  39. package/doc_src/theming/theming.css +1 -1
  40. package/graphic_test_app/01_marker_icons.js +1 -1
  41. package/graphic_test_app/02_chessboard_simple.js +1 -1
  42. package/graphic_test_app/03_chessboard_flipped.js +1 -1
  43. package/graphic_test_app/04_chessboard_annotations.js +1 -1
  44. package/graphic_test_app/05_chessboard_move.js +1 -1
  45. package/graphic_test_app/06_chessboard_theme.js +1 -1
  46. package/graphic_test_app/07_chessboard_click_squares.js +1 -1
  47. package/graphic_test_app/08_chessboard_move_pieces.js +1 -1
  48. package/graphic_test_app/09_chessboard_edit_arrows.js +1 -1
  49. package/graphic_test_app/10_chessboard_play_moves.js +1 -1
  50. package/graphic_test_app/11_chessboard_play_promotions.js +1 -1
  51. package/graphic_test_app/12_movetext_simple.js +41 -0
  52. package/graphic_test_app/13_movetext_error.js +38 -0
  53. package/graphic_test_app/14_movetext_html.js +40 -0
  54. package/graphic_test_app/15_movetext_options.js +42 -0
  55. package/graphic_test_app/16_movetext_interaction.js +42 -0
  56. package/graphic_test_app/common/dummy.pgn +8 -0
  57. package/graphic_test_app/common/games.pgn +167 -0
  58. package/graphic_test_app/common/heartbeat.txt +1 -0
  59. package/graphic_test_app/common/test_app.css +20 -4
  60. package/graphic_test_app/common/test_app.js +6 -6
  61. package/package.json +26 -25
  62. package/scripts/test_graphic/clean-graphic-output.js +1 -1
  63. package/scripts/test_graphic/docker-compose.yml +1 -1
  64. package/scripts/test_graphic/webpack-config.js +7 -3
  65. package/scripts/test_headless.webpack-config.js +2 -2
  66. package/src/ArrowMarkerIcon.js +1 -1
  67. package/src/Chessboard.js +6 -7
  68. package/src/ErrorBox.js +80 -1
  69. package/src/Movetext.js +627 -0
  70. package/src/SquareMarkerIcon.js +1 -1
  71. package/src/TextMarkerIcon.js +1 -1
  72. package/src/css/arrow.css +1 -1
  73. package/src/css/chessboard.css +6 -1
  74. package/src/css/error_box.css +13 -2
  75. package/src/css/{label.css → fonts.css} +11 -6
  76. package/src/css/movetext.css +117 -0
  77. package/src/css/symbol.css +9 -1
  78. package/src/fonts/chessalpha.woff +0 -0
  79. package/src/fonts/chessalpha.woff2 +0 -0
  80. package/src/i18n.js +12 -1
  81. package/src/impl/ArrowTip.js +1 -1
  82. package/src/impl/HtmlSanitizer.js +120 -0
  83. package/src/impl/TextSymbol.js +2 -3
  84. package/src/impl/colorsets.js +1 -1
  85. package/src/impl/piecesets.js +1 -1
  86. package/src/impl/util.js +1 -1
  87. package/src/index.js +2 -1
  88. package/src/markers.js +1 -1
  89. package/test/1_markers.js +1 -1
  90. package/test/2_chessboard_util.js +1 -1
  91. package/test/3_marker_icons_graphic.js +1 -1
  92. package/test/4_chessboard_graphic.js +1 -1
  93. package/test/5_chessboard_interaction.js +1 -1
  94. package/test/6_chessboard_play_moves.js +1 -1
  95. package/test/7_movetext_graphic.js +43 -0
  96. package/test/8_movetext_interaction.js +103 -0
  97. package/test/common/graphic.js +2 -2
  98. package/test/references/01_marker_icons/0.png +0 -0
  99. package/test/references/01_marker_icons/1.png +0 -0
  100. package/test/references/01_marker_icons/10.png +0 -0
  101. package/test/references/01_marker_icons/2.png +0 -0
  102. package/test/references/01_marker_icons/3.png +0 -0
  103. package/test/references/01_marker_icons/4.png +0 -0
  104. package/test/references/01_marker_icons/5.png +0 -0
  105. package/test/references/01_marker_icons/6.png +0 -0
  106. package/test/references/01_marker_icons/7.png +0 -0
  107. package/test/references/01_marker_icons/8.png +0 -0
  108. package/test/references/01_marker_icons/9.png +0 -0
  109. package/test/references/02_chessboard_simple/0.png +0 -0
  110. package/test/references/02_chessboard_simple/1.png +0 -0
  111. package/test/references/02_chessboard_simple/2.png +0 -0
  112. package/test/references/02_chessboard_simple/3.png +0 -0
  113. package/test/references/02_chessboard_simple/4.png +0 -0
  114. package/test/references/02_chessboard_simple/5.png +0 -0
  115. package/test/references/03_chessboard_flipped/0.png +0 -0
  116. package/test/references/03_chessboard_flipped/1.png +0 -0
  117. package/test/references/03_chessboard_flipped/2.png +0 -0
  118. package/test/references/03_chessboard_flipped/3.png +0 -0
  119. package/test/references/03_chessboard_flipped/4.png +0 -0
  120. package/test/references/04_chessboard_annotations/0.png +0 -0
  121. package/test/references/04_chessboard_annotations/1.png +0 -0
  122. package/test/references/04_chessboard_annotations/2.png +0 -0
  123. package/test/references/04_chessboard_annotations/3.png +0 -0
  124. package/test/references/04_chessboard_annotations/4.png +0 -0
  125. package/test/references/04_chessboard_annotations/5.png +0 -0
  126. package/test/references/05_chessboard_move/0.png +0 -0
  127. package/test/references/05_chessboard_move/1.png +0 -0
  128. package/test/references/05_chessboard_move/2.png +0 -0
  129. package/test/references/05_chessboard_move/3.png +0 -0
  130. package/test/references/05_chessboard_move/4.png +0 -0
  131. package/test/references/05_chessboard_move/5.png +0 -0
  132. package/test/references/05_chessboard_move/6.png +0 -0
  133. package/test/references/05_chessboard_move/7.png +0 -0
  134. package/test/references/05_chessboard_move/8.png +0 -0
  135. package/test/references/06_chessboard_theme/0.png +0 -0
  136. package/test/references/06_chessboard_theme/1.png +0 -0
  137. package/test/references/06_chessboard_theme/2.png +0 -0
  138. package/test/references/06_chessboard_theme/3.png +0 -0
  139. package/test/references/06_chessboard_theme/4.png +0 -0
  140. package/test/references/06_chessboard_theme/5.png +0 -0
  141. package/test/references/06_chessboard_theme/6.png +0 -0
  142. package/test/references/06_chessboard_theme/7.png +0 -0
  143. package/test/references/06_chessboard_theme/8.png +0 -0
  144. package/test/references/08_chessboard_move_pieces/after_move.png +0 -0
  145. package/test/references/08_chessboard_move_pieces/empty_square.png +0 -0
  146. package/test/references/08_chessboard_move_pieces/null_vector.png +0 -0
  147. package/test/references/08_chessboard_move_pieces/out_of_board.png +0 -0
  148. package/test/references/08_chessboard_move_pieces/over_arrow_marker.png +0 -0
  149. package/test/references/08_chessboard_move_pieces/over_empty.png +0 -0
  150. package/test/references/08_chessboard_move_pieces/over_non_empty_1.png +0 -0
  151. package/test/references/08_chessboard_move_pieces/over_non_empty_2.png +0 -0
  152. package/test/references/08_chessboard_move_pieces/over_square_marker.png +0 -0
  153. package/test/references/08_chessboard_move_pieces/over_text_marker.png +0 -0
  154. package/test/references/09_chessboard_edit_arrows/base_1.png +0 -0
  155. package/test/references/09_chessboard_edit_arrows/base_2.png +0 -0
  156. package/test/references/09_chessboard_edit_arrows/edit_color_not_set.png +0 -0
  157. package/test/references/09_chessboard_edit_arrows/null_vector.png +0 -0
  158. package/test/references/09_chessboard_edit_arrows/out_of_board.png +0 -0
  159. package/test/references/09_chessboard_edit_arrows/over_arrow_marker.png +0 -0
  160. package/test/references/09_chessboard_edit_arrows/over_square_marker.png +0 -0
  161. package/test/references/10_chessboard_play_moves/castling_move.png +0 -0
  162. package/test/references/10_chessboard_play_moves/chess960_ambiguous_king_move.png +0 -0
  163. package/test/references/10_chessboard_play_moves/chess960_castling_move_1.png +0 -0
  164. package/test/references/10_chessboard_play_moves/chess960_castling_move_2.png +0 -0
  165. package/test/references/10_chessboard_play_moves/chess960_non_kxr_castling.png +0 -0
  166. package/test/references/10_chessboard_play_moves/illegal_move.png +0 -0
  167. package/test/references/10_chessboard_play_moves/illegal_position.png +0 -0
  168. package/test/references/10_chessboard_play_moves/null_vector.png +0 -0
  169. package/test/references/10_chessboard_play_moves/out_of_board.png +0 -0
  170. package/test/references/10_chessboard_play_moves/regular_move_1.png +0 -0
  171. package/test/references/10_chessboard_play_moves/regular_move_2.png +0 -0
  172. package/test/references/10_chessboard_play_moves/wrong_color.png +0 -0
  173. package/test/references/11_chessboard_play_promotions/antichess_promotion.png +0 -0
  174. package/test/references/11_chessboard_play_promotions/cancel_promotion.png +0 -0
  175. package/test/references/11_chessboard_play_promotions/regular_promotion_1.png +0 -0
  176. package/test/references/11_chessboard_play_promotions/regular_promotion_2.png +0 -0
  177. package/test/references/12_movetext_simple/0.png +0 -0
  178. package/test/references/12_movetext_simple/1.png +0 -0
  179. package/test/references/12_movetext_simple/2.png +0 -0
  180. package/test/references/12_movetext_simple/3.png +0 -0
  181. package/test/references/13_movetext_error/0.png +0 -0
  182. package/test/references/13_movetext_error/1.png +0 -0
  183. package/test/references/13_movetext_error/2.png +0 -0
  184. package/test/references/13_movetext_error/3.png +0 -0
  185. package/test/references/14_movetext_html/0.png +0 -0
  186. package/test/references/14_movetext_html/1.png +0 -0
  187. package/test/references/14_movetext_html/2.png +0 -0
  188. package/test/references/15_movetext_options/0.png +0 -0
  189. package/test/references/15_movetext_options/1.png +0 -0
  190. package/test/references/15_movetext_options/2.png +0 -0
  191. package/test/references/15_movetext_options/3.png +0 -0
  192. package/test/references/16_movetext_interaction/0.png +0 -0
  193. package/test/references/16_movetext_interaction/1.png +0 -0
  194. package/test/references/16_movetext_interaction/2.png +0 -0
  195. package/graphic_test_app/common/healthcheck.txt +0 -1
@@ -0,0 +1,851 @@
1
+ "use strict";
2
+
3
+ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports["default"] = void 0;
9
+
10
+ var _propTypes = _interopRequireDefault(require("prop-types"));
11
+
12
+ var _react = _interopRequireDefault(require("react"));
13
+
14
+ var _kokopu = _interopRequireDefault(require("kokopu"));
15
+
16
+ var _HtmlSanitizer = _interopRequireDefault(require("./impl/HtmlSanitizer"));
17
+
18
+ var _Chessboard = _interopRequireDefault(require("./Chessboard"));
19
+
20
+ var _ErrorBox = _interopRequireDefault(require("./ErrorBox"));
21
+
22
+ var _i18n = _interopRequireDefault(require("./i18n"));
23
+
24
+ require("./css/fonts.css");
25
+
26
+ require("./css/movetext.css");
27
+
28
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
29
+
30
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
31
+
32
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
33
+
34
+ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
35
+
36
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
37
+
38
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
39
+
40
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
41
+
42
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
43
+
44
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
45
+
46
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
47
+
48
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
49
+
50
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
51
+
52
+ function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
53
+
54
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
55
+
56
+ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
57
+
58
+ function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
59
+
60
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
61
+
62
+ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
63
+
64
+ /**
65
+ * Display a chess game, i.e. the headers (name of the players, event, etc.), the moves, and all the related annotations if any (comments, variations, NAGs...).
66
+ */
67
+ var Movetext = /*#__PURE__*/function (_React$Component) {
68
+ _inherits(Movetext, _React$Component);
69
+
70
+ var _super = _createSuper(Movetext);
71
+
72
+ function Movetext(props) {
73
+ var _this;
74
+
75
+ _classCallCheck(this, Movetext);
76
+
77
+ _this = _super.call(this, props);
78
+ _this.focusFieldRef = /*#__PURE__*/_react["default"].createRef();
79
+ return _this;
80
+ }
81
+
82
+ _createClass(Movetext, [{
83
+ key: "render",
84
+ value: function render() {
85
+ var info = parseGame(this.props.game, this.props.gameIndex);
86
+
87
+ if (info.error) {
88
+ return /*#__PURE__*/_react["default"].createElement(_ErrorBox["default"], {
89
+ title: _i18n["default"].INVALID_PGN_ERROR_TITLE,
90
+ message: info.message,
91
+ text: info.text,
92
+ errorIndex: info.errorIndex,
93
+ lineNumber: info.lineNumber
94
+ });
95
+ }
96
+
97
+ return /*#__PURE__*/_react["default"].createElement("div", {
98
+ className: "kokopu-movetext"
99
+ }, this.renderHeaders(info.game), this.renderBody(info.game), this.renderFocusField());
100
+ }
101
+ }, {
102
+ key: "renderHeaders",
103
+ value: function renderHeaders(game) {
104
+ if (!this.props.headerVisible) {
105
+ return undefined;
106
+ }
107
+
108
+ var headers = [];
109
+ headers.push(this.renderPlayerHeaders(game, 'w'));
110
+ headers.push(this.renderPlayerHeaders(game, 'b'));
111
+ headers.push(this.renderEventRoundHeaders(game));
112
+ headers.push(this.renderDateSiteHeaders(game));
113
+ headers.push(this.renderAnnotatorHeader(game));
114
+ return headers.some(function (element) {
115
+ return element !== undefined;
116
+ }) ? /*#__PURE__*/_react["default"].createElement("div", {
117
+ className: "kokopu-headers"
118
+ }, headers) : undefined;
119
+ }
120
+ /**
121
+ * Header containing the player-related information (name, rating, title) corresponding to the given color.
122
+ */
123
+
124
+ }, {
125
+ key: "renderPlayerHeaders",
126
+ value: function renderPlayerHeaders(game, color) {
127
+ var playerName = game.playerName(color);
128
+
129
+ if (playerName === undefined) {
130
+ return undefined;
131
+ }
132
+
133
+ var title = game.playerTitle(color);
134
+ var rating = game.playerElo(color);
135
+ var classNames = ['kokopu-headerGroup-player', color === 'w' ? 'kokopu-headerGroup-whitePlayer' : 'kokopu-headerGroup-blackPlayer'];
136
+
137
+ var colorTag = /*#__PURE__*/_react["default"].createElement("span", {
138
+ className: "kokopu-colorTag"
139
+ });
140
+
141
+ var playerNameElement = /*#__PURE__*/_react["default"].createElement("span", {
142
+ className: "kokopu-header-playerName"
143
+ }, sanitizeHtml(playerName));
144
+
145
+ var titleElement = title === undefined ? undefined : /*#__PURE__*/_react["default"].createElement("span", {
146
+ className: "kokopu-header-playerTitle"
147
+ }, sanitizeHtml(title));
148
+ var ratingElement = rating === undefined ? undefined : /*#__PURE__*/_react["default"].createElement("span", {
149
+ className: "kokopu-header-playerRating"
150
+ }, sanitizeHtml(rating));
151
+ var separator = title === undefined || rating === undefined ? undefined : "\xA0"; // \u00a0 == &nbsp;
152
+
153
+ var titleRatingGroup = title === undefined && rating === undefined ? undefined : /*#__PURE__*/_react["default"].createElement("span", {
154
+ className: "kokopu-headerGroup-titleRating"
155
+ }, titleElement, separator, ratingElement);
156
+ return /*#__PURE__*/_react["default"].createElement("div", {
157
+ className: classNames.join(' '),
158
+ key: 'player-' + color
159
+ }, colorTag, playerNameElement, titleRatingGroup);
160
+ }
161
+ /**
162
+ * Header containing the event-related information: event + round.
163
+ */
164
+
165
+ }, {
166
+ key: "renderEventRoundHeaders",
167
+ value: function renderEventRoundHeaders(game) {
168
+ var evt = game.event();
169
+
170
+ if (evt === undefined) {
171
+ return undefined;
172
+ }
173
+
174
+ var round = game.round();
175
+ var roundElement = round === undefined ? undefined : /*#__PURE__*/_react["default"].createElement("span", {
176
+ className: "kokopu-header-round"
177
+ }, sanitizeHtml(round));
178
+
179
+ var evtElement = /*#__PURE__*/_react["default"].createElement("span", {
180
+ className: "kokopu-header-event"
181
+ }, sanitizeHtml(evt));
182
+
183
+ return /*#__PURE__*/_react["default"].createElement("div", {
184
+ className: "kokopu-headerGroup-eventRound",
185
+ key: "event-round"
186
+ }, evtElement, roundElement);
187
+ }
188
+ /**
189
+ * Header containing the date/place information.
190
+ */
191
+
192
+ }, {
193
+ key: "renderDateSiteHeaders",
194
+ value: function renderDateSiteHeaders(game) {
195
+ var date = game.date();
196
+ var site = game.site();
197
+
198
+ if (date === undefined && site === undefined) {
199
+ return undefined;
200
+ }
201
+
202
+ var dateElement = date === undefined ? undefined : /*#__PURE__*/_react["default"].createElement("span", {
203
+ className: "kokopu-header-date"
204
+ }, capitalizeFirstWord(game.dateAsString()));
205
+ var siteElement = site === undefined ? undefined : /*#__PURE__*/_react["default"].createElement("span", {
206
+ className: "kokopu-header-site"
207
+ }, sanitizeHtml(site));
208
+ var separator = date === undefined || site === undefined ? undefined : "\xA0\u2013\xA0"; // \u00a0 == &nbsp; \u2013 == &ndash;
209
+
210
+ return /*#__PURE__*/_react["default"].createElement("div", {
211
+ className: "kokopu-headerGroup-dateSite",
212
+ key: "date-site"
213
+ }, dateElement, separator, siteElement);
214
+ }
215
+ /**
216
+ * Header containing the annotator information.
217
+ */
218
+
219
+ }, {
220
+ key: "renderAnnotatorHeader",
221
+ value: function renderAnnotatorHeader(game) {
222
+ var annotator = game.annotator();
223
+
224
+ if (annotator === undefined) {
225
+ return undefined;
226
+ }
227
+
228
+ annotator = _i18n["default"].ANNOTATED_BY.replace(/\{0\}/g, annotator);
229
+ return /*#__PURE__*/_react["default"].createElement("div", {
230
+ className: "kokopu-header-annotator",
231
+ key: "annotator"
232
+ }, sanitizeHtml(annotator));
233
+ }
234
+ }, {
235
+ key: "renderFocusField",
236
+ value: function renderFocusField() {
237
+ var _this2 = this;
238
+
239
+ if (this.props.interactionMode !== 'selectMove') {
240
+ return undefined;
241
+ }
242
+
243
+ return /*#__PURE__*/_react["default"].createElement("div", {
244
+ className: "kokopu-focusFieldContainer"
245
+ }, /*#__PURE__*/_react["default"].createElement("a", {
246
+ className: "kokopu-focusField",
247
+ href: "#",
248
+ ref: this.focusFieldRef,
249
+ onKeyDown: function onKeyDown(evt) {
250
+ return _this2.handleKeyDownInFocusField(evt);
251
+ }
252
+ }));
253
+ }
254
+ }, {
255
+ key: "renderBody",
256
+ value: function renderBody(game) {
257
+ return this.renderVariation(this.getNotationTextBuilder(), game.mainVariation(), 'main-variation', true, game.result());
258
+ }
259
+ /**
260
+ * Render the given variation and its sub-variations, recursively.
261
+ */
262
+
263
+ }, {
264
+ key: "renderVariation",
265
+ value: function renderVariation(notationTextBuilder, variation, variationKey, isMainVariation, gameResult) {
266
+ var moveGroups = []; // ... and also long comments and long sub-variations
267
+
268
+ var currentMoveGroupElements = []; // Close the current move group, if any.
269
+
270
+ function closeMoveGroup() {
271
+ if (!variation.isLongVariation() || currentMoveGroupElements.length === 0) {
272
+ return;
273
+ }
274
+
275
+ moveGroups.push( /*#__PURE__*/_react["default"].createElement("div", {
276
+ className: "kokopu-moveGroup",
277
+ key: 'group-' + moveGroups.length
278
+ }, currentMoveGroupElements));
279
+ currentMoveGroupElements = [];
280
+ } // Write the initial comment, if any.
281
+
282
+
283
+ var variationComment = this.extractComment(variation);
284
+
285
+ if (variationComment !== undefined) {
286
+ if (variation.isLongComment()) {
287
+ moveGroups.push(this.renderComment(variation, variationComment, true));
288
+ } else {
289
+ currentMoveGroupElements.push(this.renderComment(variation, variationComment, true));
290
+ }
291
+ } // Visit all the PGN nodes (one node per move) within the variation.
292
+
293
+
294
+ var forcePrintMoveNumber = true;
295
+ var node = variation.first();
296
+
297
+ while (node !== undefined) {
298
+ // Write the move, including directly related information (i.e. move number + NAGs).
299
+ currentMoveGroupElements.push(this.renderMove(notationTextBuilder, node, forcePrintMoveNumber)); // Write the comment (if any).
300
+
301
+ var nodeComment = this.extractComment(node);
302
+
303
+ if (nodeComment !== undefined) {
304
+ if (node.isLongComment()) {
305
+ closeMoveGroup();
306
+ moveGroups.push(this.renderComment(node, nodeComment, false));
307
+ } else {
308
+ currentMoveGroupElements.push(this.renderComment(node, nodeComment, false));
309
+ }
310
+ } // Write the sub-variations.
311
+
312
+
313
+ var hasNonEmptySubVariations = false;
314
+
315
+ var _iterator = _createForOfIteratorHelper(node.variations().entries()),
316
+ _step;
317
+
318
+ try {
319
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
320
+ var _step$value = _slicedToArray(_step.value, 2),
321
+ index = _step$value[0],
322
+ subVariation = _step$value[1];
323
+
324
+ var subVariationElement = this.renderVariation(notationTextBuilder, subVariation, node.fullMoveNumber() + node.moveColor() + '-variation-' + index, false);
325
+
326
+ if (subVariationElement) {
327
+ if (subVariation.isLongVariation()) {
328
+ closeMoveGroup();
329
+ moveGroups.push(subVariationElement);
330
+ } else {
331
+ currentMoveGroupElements.push(subVariationElement);
332
+ }
333
+
334
+ hasNonEmptySubVariations = true;
335
+ }
336
+ } // Back to the current variation, go to the next move.
337
+
338
+ } catch (err) {
339
+ _iterator.e(err);
340
+ } finally {
341
+ _iterator.f();
342
+ }
343
+
344
+ forcePrintMoveNumber = nodeComment !== undefined || hasNonEmptySubVariations;
345
+ node = node.next();
346
+ } // Append the game result at the end of the main variation.
347
+
348
+
349
+ if (isMainVariation && gameResult !== '*') {
350
+ currentMoveGroupElements.push( /*#__PURE__*/_react["default"].createElement("span", {
351
+ className: "kokopu-gameResult",
352
+ key: "gameResult"
353
+ }, formatResult(gameResult)));
354
+ } // Close the last move group, and return the result.
355
+
356
+
357
+ closeMoveGroup();
358
+ var elements = variation.isLongVariation() ? moveGroups : currentMoveGroupElements;
359
+
360
+ if (elements.length === 0) {
361
+ return undefined;
362
+ }
363
+
364
+ if (isMainVariation) {
365
+ return /*#__PURE__*/_react["default"].createElement("div", {
366
+ className: "kokopu-variation kokopu-mainVariation",
367
+ key: variationKey
368
+ }, elements);
369
+ } else if (variation.isLongVariation()) {
370
+ return /*#__PURE__*/_react["default"].createElement("div", {
371
+ className: "kokopu-variation kokopu-subVariation",
372
+ key: variationKey
373
+ }, elements);
374
+ } else {
375
+ return /*#__PURE__*/_react["default"].createElement("span", {
376
+ className: "kokopu-variation kokopu-subVariation",
377
+ key: variationKey
378
+ }, elements);
379
+ }
380
+ }
381
+ /**
382
+ * Render the given move, move number, and NAG (if any).
383
+ */
384
+
385
+ }, {
386
+ key: "renderMove",
387
+ value: function renderMove(notationTextBuilder, node, forcePrintMoveNumber) {
388
+ var _this3 = this;
389
+
390
+ // Move number
391
+ var moveNumber = undefined;
392
+
393
+ if (forcePrintMoveNumber || node.moveColor() === 'w') {
394
+ var moveNumberText = node.fullMoveNumber() + (node.moveColor() === 'w' ? '.' : "\u2026");
395
+ moveNumber = /*#__PURE__*/_react["default"].createElement("span", {
396
+ className: "kokopu-moveNumber"
397
+ }, moveNumberText);
398
+ } // SAN notation.
399
+
400
+
401
+ var notationText = notationTextBuilder(node.notation()); // NAGs
402
+
403
+ var nagElements = node.nags().map(function (nag) {
404
+ return /*#__PURE__*/_react["default"].createElement("span", {
405
+ className: "kokopu-nag",
406
+ key: nag
407
+ }, _kokopu["default"].nagSymbol(nag));
408
+ }); // Class
409
+
410
+ var nodeId = node.id();
411
+ var moveClassNames = ['kokopu-move'];
412
+ var onClick = undefined;
413
+
414
+ if (this.props.selection && this.props.selection === nodeId) {
415
+ moveClassNames.push('kokopu-selectedMove');
416
+ }
417
+
418
+ if (this.props.interactionMode === 'selectMove') {
419
+ moveClassNames.push('kokopu-clickableMove');
420
+
421
+ onClick = function onClick() {
422
+ return _this3.handleNodeClicked(nodeId);
423
+ };
424
+ }
425
+
426
+ return /*#__PURE__*/_react["default"].createElement("span", {
427
+ className: moveClassNames.join(' '),
428
+ key: node.fullMoveNumber() + node.moveColor()
429
+ }, /*#__PURE__*/_react["default"].createElement("span", {
430
+ className: "kokopu-moveContent",
431
+ onClick: onClick
432
+ }, moveNumber, /*#__PURE__*/_react["default"].createElement("span", {
433
+ className: "kokopu-moveNotation"
434
+ }, notationText), nagElements.length === 0 ? undefined : nagElements));
435
+ }
436
+ /**
437
+ * Render the given text comment with its diagrams, if any.
438
+ */
439
+
440
+ }, {
441
+ key: "renderComment",
442
+ value: function renderComment(node, comment, isVariation) {
443
+ var content;
444
+
445
+ if (this.props.diagramVisible && comment.includes('[#]')) {
446
+ content = [];
447
+ var sanitizer = createSanitizer();
448
+ var isFirstPart = true;
449
+
450
+ var _iterator2 = _createForOfIteratorHelper(comment.split('[#]').entries()),
451
+ _step2;
452
+
453
+ try {
454
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
455
+ var _step2$value = _slicedToArray(_step2.value, 2),
456
+ index = _step2$value[0],
457
+ part = _step2$value[1];
458
+
459
+ if (!isFirstPart) {
460
+ var position = isVariation ? node.initialPosition() : node.position();
461
+
462
+ var diagram = /*#__PURE__*/_react["default"].createElement(_Chessboard["default"], {
463
+ position: position,
464
+ squareMarkers: node.tag('csl'),
465
+ arrowMarkers: node.tag('cal'),
466
+ textMarkers: node.tag('ctl'),
467
+ flipped: this.props.diagramOptions.flipped,
468
+ squareSize: this.props.diagramOptions.squareSize,
469
+ coordinateVisible: this.props.diagramOptions.coordinateVisible,
470
+ smallScreenLimits: this.props.diagramOptions.smallScreenLimits,
471
+ colorset: this.props.diagramOptions.colorset,
472
+ pieceset: this.props.diagramOptions.pieceset
473
+ });
474
+
475
+ content.push( /*#__PURE__*/_react["default"].createElement("div", {
476
+ className: "kokopu-diagram",
477
+ key: 'diagram-' + index
478
+ }, diagram));
479
+ }
480
+
481
+ isFirstPart = false;
482
+ part = part.trim();
483
+
484
+ if (part.length !== 0) {
485
+ content.push(sanitizeHtml(part, sanitizer));
486
+ }
487
+ }
488
+ } catch (err) {
489
+ _iterator2.e(err);
490
+ } finally {
491
+ _iterator2.f();
492
+ }
493
+ } else {
494
+ content = sanitizeHtml(comment);
495
+ }
496
+
497
+ var key = isVariation ? 'initial-comment' : node.fullMoveNumber() + node.moveColor() + '-comment';
498
+ return node.isLongComment() ? /*#__PURE__*/_react["default"].createElement("div", {
499
+ className: "kokopu-comment",
500
+ key: key
501
+ }, content) : /*#__PURE__*/_react["default"].createElement("span", {
502
+ className: "kokopu-comment",
503
+ key: key
504
+ }, content);
505
+ }
506
+ }, {
507
+ key: "extractComment",
508
+ value: function extractComment(node) {
509
+ var comment = node.comment();
510
+
511
+ if (comment) {
512
+ // Remove the diagrams if necessary.
513
+ if (!this.props.diagramVisible) {
514
+ comment = comment.replace(/\[#\]/g, ' ');
515
+ } // Trim and sanitize the space characters.
516
+
517
+
518
+ comment = comment.replace(/\s+/g, ' ').trim();
519
+ }
520
+
521
+ return comment ? comment : undefined;
522
+ }
523
+ }, {
524
+ key: "handleKeyDownInFocusField",
525
+ value: function handleKeyDownInFocusField(evt) {
526
+ if (!this.props.selection) {
527
+ return;
528
+ }
529
+
530
+ var game = parseGame(this.props.game, this.props.gameIndex).game;
531
+ var nodeId = false;
532
+ var evtOrigin = '';
533
+
534
+ if (this.props.selection === 'start') {
535
+ if (evt.key === 'ArrowRight') {
536
+ nodeId = getNextNodeId(game.mainVariation(), true);
537
+ evtOrigin = 'key-next';
538
+ } else if (evt.key === 'End') {
539
+ nodeId = getLastNodeId(game.mainVariation(), true);
540
+ evtOrigin = 'key-last';
541
+ }
542
+ } else {
543
+ var currentNode = game.findById(this.props.selection);
544
+
545
+ if (!currentNode) {
546
+ return;
547
+ }
548
+
549
+ if (evt.key === 'Home') {
550
+ nodeId = 'start';
551
+ evtOrigin = 'key-first';
552
+ } else if (evt.key === 'ArrowLeft') {
553
+ nodeId = getPreviousNodeId(currentNode);
554
+ evtOrigin = 'key-previous';
555
+ } else if (evt.key === 'ArrowRight') {
556
+ nodeId = getNextNodeId(currentNode, false);
557
+ evtOrigin = 'key-next';
558
+ } else if (evt.key === 'End') {
559
+ nodeId = getLastNodeId(currentNode, false);
560
+ evtOrigin = 'key-last';
561
+ }
562
+ }
563
+
564
+ if (nodeId && this.props.onMoveSelected) {
565
+ this.props.onMoveSelected(nodeId, evtOrigin);
566
+ }
567
+ }
568
+ }, {
569
+ key: "handleNodeClicked",
570
+ value: function handleNodeClicked(nodeId) {
571
+ this.focusFieldRef.current.focus();
572
+
573
+ if (this.props.onMoveSelected) {
574
+ this.props.onMoveSelected(nodeId === this.props.selection ? undefined : nodeId, 'click');
575
+ }
576
+ }
577
+ /**
578
+ * Return the square at the given location.
579
+ */
580
+
581
+ }, {
582
+ key: "getNotationTextBuilder",
583
+ value: function getNotationTextBuilder() {
584
+ var pieceSymbols = this.props.pieceSymbols;
585
+
586
+ if (pieceSymbols === 'localized') {
587
+ var mapping = _i18n["default"].PIECE_SYMBOLS;
588
+ return function (notation) {
589
+ return notation.replace(/[KQRBNP]/g, function (match) {
590
+ return mapping[match];
591
+ });
592
+ };
593
+ } else if (pieceSymbols === 'figurines') {
594
+ return function (notation) {
595
+ return figurineNotation(notation, 'alpha');
596
+ };
597
+ } else if (pieceSymbols !== 'native' && pieceSymbols && ['K', 'Q', 'R', 'B', 'N', 'P'].every(function (p) {
598
+ return typeof pieceSymbols[p] === 'string';
599
+ })) {
600
+ return function (notation) {
601
+ return notation.replace(/[KQRBNP]/g, function (match) {
602
+ return pieceSymbols[match];
603
+ });
604
+ };
605
+ } else {
606
+ return function (notation) {
607
+ return notation;
608
+ };
609
+ }
610
+ }
611
+ }]);
612
+
613
+ return Movetext;
614
+ }(_react["default"].Component);
615
+
616
+ exports["default"] = Movetext;
617
+ Movetext.propTypes = {
618
+ /**
619
+ * Displayed position. Can be a [kokopu.Game](https://kokopu.yo35.org/docs/Game.html) object,
620
+ * a [kokopu.Database](https://kokopu.yo35.org/docs/Database.html) object,
621
+ * or a [PGN string](https://en.wikipedia.org/wiki/Portable_Game_Notation).
622
+ */
623
+ game: _propTypes["default"].oneOfType([_propTypes["default"].instanceOf(_kokopu["default"].Game), _propTypes["default"].instanceOf(_kokopu["default"].Database), _propTypes["default"].string]),
624
+
625
+ /**
626
+ * Index of the game to display (only if attribute `game` is a [kokopu.Database](https://kokopu.yo35.org/docs/Database.html)
627
+ * or a [PGN string](https://en.wikipedia.org/wiki/Portable_Game_Notation)): `0` for the first game of the database/PGN, `1` for the second one, etc.
628
+ * If omitted, the first game of the database/PGN is displayed.
629
+ */
630
+ gameIndex: _propTypes["default"].number,
631
+
632
+ /**
633
+ * Options applicable to the diagrams in the comments. See [Chessboard](#/Components/Chessboard) for more details about each option.
634
+ */
635
+ diagramOptions: _propTypes["default"].shape({
636
+ flipped: _Chessboard["default"].propTypes.flipped,
637
+ squareSize: _Chessboard["default"].propTypes.squareSize,
638
+ coordinateVisible: _Chessboard["default"].propTypes.coordinateVisible,
639
+ smallScreenLimits: _Chessboard["default"].propTypes.smallScreenLimits,
640
+ colorset: _Chessboard["default"].propTypes.colorset,
641
+ pieceset: _Chessboard["default"].propTypes.pieceset
642
+ }),
643
+
644
+ /**
645
+ * Symbols to use for the chess pieces. Can be:
646
+ * - `'native'`: use the first letter of the piece names (in English),
647
+ * - `'localized'`: use the symbols defined by `i18n.PIECE_SYMBOLS`,
648
+ * - `'figurines'`: use the figurines,
649
+ * - or an object defining 6 string-valued properties named `K`, `Q`, `R`, `B`, `N` and `P`.
650
+ */
651
+ pieceSymbols: _propTypes["default"].oneOfType([_propTypes["default"].oneOf(['native', 'localized', 'figurines']), _propTypes["default"].shape({
652
+ K: _propTypes["default"].string.isRequired,
653
+ Q: _propTypes["default"].string.isRequired,
654
+ R: _propTypes["default"].string.isRequired,
655
+ B: _propTypes["default"].string.isRequired,
656
+ N: _propTypes["default"].string.isRequired,
657
+ P: _propTypes["default"].string.isRequired
658
+ })]),
659
+
660
+ /**
661
+ * Whether the diagrams within the comments (if any) are displayed or not.
662
+ */
663
+ diagramVisible: _propTypes["default"].bool,
664
+
665
+ /**
666
+ * Whether the game headers (if any) are displayed or not.
667
+ */
668
+ headerVisible: _propTypes["default"].bool,
669
+
670
+ /**
671
+ * ID of the selected move (or `'start'` for the beginning of the main variation).
672
+ * Use [kokopu.Node#id](https://kokopu.yo35.org/docs/Node.html#id) to get the ID of a game move.
673
+ */
674
+ selection: _propTypes["default"].string,
675
+
676
+ /**
677
+ * Type of action allowed with the mouse/keys on the component. If undefined, then the user cannot interact with the component.
678
+ *
679
+ * - `'selectMove'` allows the user to select a move.
680
+ */
681
+ interactionMode: _propTypes["default"].oneOf(['selectMove']),
682
+
683
+ /**
684
+ * Callback invoked when the user selects a move (only if `interactionMode` is set to `'selectMove'`).
685
+ *
686
+ * @param {string?} nodeId ID of the selected move (as returned by [kokopu.Node#id](https://kokopu.yo35.org/docs/Node.html#id)),
687
+ * `'start'` for the beginning of the main variation, or `undefined` if the user unselects the previously selected move.
688
+ * @param {string} evtOrigin Origin of the event. Can be:
689
+ * - `'key-first'`: the event has been triggered by the "go-to-first-move" key (aka. the home key),
690
+ * - `'key-previous'`: the event has been triggered by the "go-to-previous-move" key (aka. the arrow left key),
691
+ * - `'key-next'`: the event has been triggered by the "go-to-next-move" key (aka. the arrow right key),
692
+ * - `'key-last'`: the event has been triggered by the "go-to-last-move" key (aka. the end key),
693
+ * - `'click'`: the event has been triggered by a mouse click on a move.
694
+ */
695
+ onMoveSelected: _propTypes["default"].func
696
+ };
697
+ Movetext.defaultProps = {
698
+ game: new _kokopu["default"].Game(),
699
+ gameIndex: 0,
700
+ diagramOptions: {},
701
+ pieceSymbols: 'native',
702
+ diagramVisible: true,
703
+ headerVisible: true
704
+ };
705
+ /**
706
+ * Example: `'hello world'` is turned into `'Hello world'`.
707
+ */
708
+
709
+ function capitalizeFirstWord(text) {
710
+ return text.length === 0 ? '' : text.charAt(0).toUpperCase() + text.slice(1);
711
+ }
712
+
713
+ function formatResult(result) {
714
+ switch (result) {
715
+ case '1/2-1/2':
716
+ return "\xBD\u2013\xBD";
717
+
718
+ case '1-0':
719
+ return "1\u20130";
720
+
721
+ case '0-1':
722
+ return "0\u20131";
723
+ }
724
+ }
725
+
726
+ function createSanitizer() {
727
+ return new _HtmlSanitizer["default"]({
728
+ allowedTags: ['a', 'span', 'b', 'strong', 'i', 'em', 'mark', 'small', 'del', 'ins', 'sub', 'sup'],
729
+ allowedAttributes: {
730
+ '*': ['class', 'id', 'title'],
731
+ 'a': ['href', 'target']
732
+ }
733
+ });
734
+ }
735
+
736
+ function sanitizeHtml(text, sanitizer) {
737
+ if (!sanitizer) {
738
+ sanitizer = createSanitizer();
739
+ }
740
+
741
+ var result = sanitizer.parse(text);
742
+ return result !== null && result !== void 0 ? result : text;
743
+ }
744
+ /**
745
+ * Decompose the given string into piece symbol characters and sections of non piece symbol characters, and transform the piece symbols into
746
+ * React objects represented with the given chess font.
747
+ */
748
+
749
+
750
+ function figurineNotation(text, fontName) {
751
+ var result = [];
752
+ var beginOfText = 0;
753
+ var pieceSymbolIndex = 0;
754
+
755
+ for (var pos = 0; pos < text.length; ++pos) {
756
+ var currentChar = text.charAt(pos);
757
+
758
+ if (currentChar === 'K' || currentChar === 'Q' || currentChar === 'R' || currentChar === 'B' || currentChar === 'N' || currentChar === 'P') {
759
+ if (pos > beginOfText) {
760
+ result.push(text.substring(beginOfText, pos));
761
+ }
762
+
763
+ beginOfText = pos + 1;
764
+ var key = 'symbol-' + pieceSymbolIndex++;
765
+ result.push( /*#__PURE__*/_react["default"].createElement("span", {
766
+ className: 'kokopu-font-' + fontName,
767
+ key: key
768
+ }, currentChar));
769
+ }
770
+ }
771
+
772
+ if (beginOfText < text.length) {
773
+ result.push(text.substring(beginOfText));
774
+ }
775
+
776
+ return result;
777
+ }
778
+
779
+ function getPreviousNodeId(currentNode) {
780
+ var previousNode = currentNode.previous();
781
+
782
+ if (previousNode) {
783
+ return previousNode.id();
784
+ } else {
785
+ var parentNode = currentNode.parentVariation().parentNode();
786
+ return parentNode ? getPreviousNodeId(parentNode) : 'start';
787
+ }
788
+ }
789
+
790
+ function getNextNodeId(currentNode, isVariation) {
791
+ var nextNode = isVariation ? currentNode.first() : currentNode.next();
792
+ return nextNode ? nextNode.id() : false;
793
+ }
794
+
795
+ function getLastNodeId(currentNode, isVariation) {
796
+ currentNode = isVariation ? currentNode.first() : currentNode.next();
797
+
798
+ if (!currentNode) {
799
+ // Ensure that the input node is not already the last one.
800
+ return false;
801
+ }
802
+
803
+ while (true) {
804
+ var nextNode = currentNode.next();
805
+
806
+ if (!nextNode) {
807
+ return currentNode.id();
808
+ }
809
+
810
+ currentNode = nextNode;
811
+ }
812
+ }
813
+ /**
814
+ * Try to interpret the given object as a chess game.
815
+ */
816
+
817
+
818
+ function parseGame(game, gameIndex) {
819
+ if (game instanceof _kokopu["default"].Game) {
820
+ return {
821
+ error: false,
822
+ game: game
823
+ };
824
+ } else if (game instanceof _kokopu["default"].Database || typeof game === 'string') {
825
+ try {
826
+ var result = game instanceof _kokopu["default"].Database ? game.game(gameIndex) : _kokopu["default"].pgnRead(game, gameIndex);
827
+ return {
828
+ error: false,
829
+ game: result
830
+ };
831
+ } catch (e) {
832
+ // istanbul ignore else
833
+ if (e instanceof _kokopu["default"].exception.InvalidPGN) {
834
+ return {
835
+ error: true,
836
+ message: e.message,
837
+ text: e.pgn,
838
+ errorIndex: e.index,
839
+ lineNumber: e.lineNumber
840
+ };
841
+ } else {
842
+ throw e;
843
+ }
844
+ }
845
+ } else {
846
+ return {
847
+ error: true,
848
+ message: _i18n["default"].INVALID_GAME_ATTRIBUTE_ERROR_MESSAGE
849
+ };
850
+ }
851
+ }