kokopu-react 3.1.0 → 3.3.0

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 (236) hide show
  1. package/.eslintrc.yml +1 -1
  2. package/CHANGELOG.md +10 -0
  3. package/dist/lib/chessboard/BoardProperties.js.map +1 -1
  4. package/dist/lib/chessboard/Chessboard.css +13 -13
  5. package/dist/lib/chessboard/Chessboard.d.ts +27 -2
  6. package/dist/lib/chessboard/Chessboard.js +36 -17
  7. package/dist/lib/chessboard/Chessboard.js.map +1 -1
  8. package/dist/lib/chessboard/ChessboardImpl.js.map +1 -1
  9. package/dist/lib/chessboard/Draggable.js.map +1 -1
  10. package/dist/lib/chessboard/Motion.js.map +1 -1
  11. package/dist/lib/chessboard/colorsets.js.map +1 -1
  12. package/dist/lib/chessboard/piecesets.js.map +1 -1
  13. package/dist/lib/errorbox/ErrorBox.css +18 -18
  14. package/dist/lib/errorbox/ErrorBox.js.map +1 -1
  15. package/dist/lib/errorbox/parsing.js.map +1 -1
  16. package/dist/lib/exception.js.map +1 -1
  17. package/dist/lib/i18n.d.ts +1 -0
  18. package/dist/lib/i18n.js +1 -0
  19. package/dist/lib/i18n.js.map +1 -1
  20. package/dist/lib/icons/AnnotationSymbolShape.css +6 -6
  21. package/dist/lib/icons/AnnotationSymbolShape.js.map +1 -1
  22. package/dist/lib/icons/ArrowMarkerIcon.js.map +1 -1
  23. package/dist/lib/icons/ArrowTip.js.map +1 -1
  24. package/dist/lib/icons/ChessPieceIcon.js.map +1 -1
  25. package/dist/lib/icons/SquareMarkerIcon.js.map +1 -1
  26. package/dist/lib/icons/TextMarkerIcon.js.map +1 -1
  27. package/dist/lib/icons/arrow.css +1 -1
  28. package/dist/lib/index.d.ts +1 -0
  29. package/dist/lib/index.js +1 -0
  30. package/dist/lib/index.js.map +1 -1
  31. package/dist/lib/movetext/Movetext.css +27 -27
  32. package/dist/lib/movetext/Movetext.js.map +1 -1
  33. package/dist/lib/movetext/MovetextImpl.js.map +1 -1
  34. package/dist/lib/movetext/fonts.css +7 -7
  35. package/dist/lib/movetext/htmlFilter.js.map +1 -1
  36. package/dist/lib/movetext/moveFormatter.js.map +1 -1
  37. package/dist/lib/navigationboard/NavigationBoard.d.ts +68 -1
  38. package/dist/lib/navigationboard/NavigationBoard.js +141 -13
  39. package/dist/lib/navigationboard/NavigationBoard.js.map +1 -1
  40. package/dist/lib/navigationboard/NavigationButton.d.ts +66 -0
  41. package/dist/lib/navigationboard/NavigationButton.js +67 -0
  42. package/dist/lib/navigationboard/NavigationButton.js.map +1 -0
  43. package/dist/lib/navigationboard/NavigationField.css +7 -7
  44. package/dist/lib/navigationboard/NavigationField.js.map +1 -1
  45. package/dist/lib/navigationboard/NavigationToolbar.css +16 -13
  46. package/dist/lib/navigationboard/NavigationToolbar.d.ts +10 -10
  47. package/dist/lib/navigationboard/NavigationToolbar.js +61 -79
  48. package/dist/lib/navigationboard/NavigationToolbar.js.map +1 -1
  49. package/dist/lib/navigationboard/iconPaths.d.ts +29 -0
  50. package/dist/lib/navigationboard/iconPaths.js +91 -0
  51. package/dist/lib/navigationboard/iconPaths.js.map +1 -0
  52. package/dist/lib/sanitization.js.map +1 -1
  53. package/dist/lib/types.js.map +1 -1
  54. package/dist/lib/util.js.map +1 -1
  55. package/doc_src/demo/PageChessboardBase.tsx +115 -115
  56. package/doc_src/demo/PageChessboardInteraction.tsx +303 -303
  57. package/doc_src/demo/PageChessboardMove.tsx +173 -173
  58. package/doc_src/demo/PageChessboardSmallScreens.tsx +94 -94
  59. package/doc_src/demo/PageMovetextBase.tsx +180 -180
  60. package/doc_src/demo/PageMovetextInteraction.tsx +123 -123
  61. package/doc_src/demo/PageNavigationBoardBase.tsx +144 -137
  62. package/doc_src/demo/PageNavigationBoardUncontrolledVsControlled.tsx +69 -69
  63. package/doc_src/demo/decs.d.ts +4 -4
  64. package/doc_src/demo/demo.css +12 -12
  65. package/doc_src/demo/util.ts +1 -1
  66. package/doc_src/examples/NavigationBoard.md +1 -1
  67. package/doc_src/theming/LogoRenderer.js +13 -13
  68. package/doc_src/theming/theming.css +5 -5
  69. package/package.json +95 -95
  70. package/scripts/doc.styleguide.config.js +131 -131
  71. package/scripts/doc.tsconfig.json +5 -5
  72. package/scripts/docker-compose.yml +1 -1
  73. package/scripts/test-graphic.tsconfig.json +5 -5
  74. package/scripts/test-graphic.webpack.config.js +57 -57
  75. package/scripts/test-headless.webpack.config.js +29 -29
  76. package/src/chessboard/BoardProperties.ts +68 -68
  77. package/src/chessboard/Chessboard.css +13 -13
  78. package/src/chessboard/Chessboard.tsx +436 -380
  79. package/src/chessboard/ChessboardImpl.tsx +557 -557
  80. package/src/chessboard/Draggable.tsx +185 -185
  81. package/src/chessboard/Motion.tsx +67 -67
  82. package/src/chessboard/colorsets.ts +66 -66
  83. package/src/chessboard/piecesets.ts +84 -84
  84. package/src/decs.d.ts +2 -2
  85. package/src/errorbox/ErrorBox.css +18 -18
  86. package/src/errorbox/ErrorBox.tsx +75 -75
  87. package/src/errorbox/parsing.tsx +95 -95
  88. package/src/exception.ts +17 -17
  89. package/src/i18n.ts +1 -0
  90. package/src/icons/AnnotationSymbolShape.css +6 -6
  91. package/src/icons/AnnotationSymbolShape.tsx +31 -31
  92. package/src/icons/ArrowMarkerIcon.tsx +34 -34
  93. package/src/icons/ArrowTip.tsx +14 -14
  94. package/src/icons/ChessPieceIcon.tsx +45 -45
  95. package/src/icons/SquareMarkerIcon.tsx +20 -20
  96. package/src/icons/TextMarkerIcon.tsx +29 -29
  97. package/src/icons/arrow.css +1 -1
  98. package/src/index.ts +3 -2
  99. package/src/movetext/Movetext.css +27 -27
  100. package/src/movetext/Movetext.tsx +158 -158
  101. package/src/movetext/MovetextImpl.tsx +345 -345
  102. package/src/movetext/fonts.css +7 -7
  103. package/src/movetext/htmlFilter.tsx +129 -129
  104. package/src/movetext/moveFormatter.tsx +37 -37
  105. package/src/navigationboard/NavigationBoard.tsx +386 -180
  106. package/src/navigationboard/NavigationButton.ts +114 -0
  107. package/src/navigationboard/NavigationField.css +7 -7
  108. package/src/navigationboard/NavigationField.tsx +90 -90
  109. package/src/navigationboard/NavigationToolbar.css +16 -13
  110. package/src/navigationboard/NavigationToolbar.tsx +98 -112
  111. package/src/navigationboard/iconPaths.ts +98 -0
  112. package/src/sanitization.ts +17 -17
  113. package/src/types.ts +116 -116
  114. package/src/util.ts +11 -11
  115. package/test/01_types.js +167 -167
  116. package/test/02_chessboard_util.js +191 -115
  117. package/test/03_error_box_graphic.js +16 -16
  118. package/test/04_icons_graphic.js +32 -32
  119. package/test/05_chessboard_graphic.js +60 -60
  120. package/test/06_chessboard_animation.js +36 -36
  121. package/test/07_chessboard_interaction.js +101 -101
  122. package/test/08_chessboard_play_moves.js +62 -62
  123. package/test/09_chessboard_small_screens.js +32 -32
  124. package/test/10_movetext_graphic.js +32 -32
  125. package/test/11_movetext_interaction.js +100 -100
  126. package/test/12_navigation_board_base.js +151 -0
  127. package/test/13_navigation_board_graphic.js +68 -0
  128. package/test/14_navigation_board_autoplay.js +100 -0
  129. package/test/common/graphic.js +106 -97
  130. package/test/graphic_references/12_navigation_board_base/controlled/default.png +0 -0
  131. package/test/graphic_references/{12_navigation_board/controlled/forbidden_navigation_1.png → 12_navigation_board_base/controlled/forbidden_navigation_0.png} +0 -0
  132. package/test/graphic_references/{12_navigation_board/controlled/invalid_node_id.png → 12_navigation_board_base/controlled/forbidden_navigation_1.png} +0 -0
  133. package/test/graphic_references/{12_navigation_board/controlled/forbidden_navigation_0.png → 12_navigation_board_base/controlled/invalid_node_id.png} +0 -0
  134. package/test/graphic_references/12_navigation_board_base/controlled/no_callback_defined_0.png +0 -0
  135. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/controlled/no_callback_defined_1.png +0 -0
  136. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/controlled/overriding_initial_node_id.png +0 -0
  137. package/test/graphic_references/{12_navigation_board/controlled/regular_navigation_1.png → 12_navigation_board_base/controlled/regular_navigation_0.png} +0 -0
  138. package/test/graphic_references/{12_navigation_board/uncontrolled/initial_state_at_specific_id.png → 12_navigation_board_base/controlled/regular_navigation_1.png} +0 -0
  139. package/test/graphic_references/{12_navigation_board/controlled/regular_navigation_3.png → 12_navigation_board_base/controlled/regular_navigation_2.png} +0 -0
  140. package/test/graphic_references/{12_navigation_board/controlled/regular_navigation_0.png → 12_navigation_board_base/controlled/regular_navigation_3.png} +0 -0
  141. package/test/graphic_references/{12_navigation_board/uncontrolled/forbidden_navigation_at_beginning_2.png → 12_navigation_board_base/uncontrolled/forbidden_navigation_at_beginning_0.png} +0 -0
  142. package/test/graphic_references/{12_navigation_board/uncontrolled/forbidden_navigation_at_beginning_0.png → 12_navigation_board_base/uncontrolled/forbidden_navigation_at_beginning_1.png} +0 -0
  143. package/test/graphic_references/{12_navigation_board/uncontrolled/forbidden_navigation_at_beginning_1.png → 12_navigation_board_base/uncontrolled/forbidden_navigation_at_beginning_2.png} +0 -0
  144. package/test/graphic_references/{12_navigation_board/controlled/no_callback_defined_0.png → 12_navigation_board_base/uncontrolled/forbidden_navigation_at_end_0.png} +0 -0
  145. package/test/graphic_references/12_navigation_board_base/uncontrolled/forbidden_navigation_at_end_1.png +0 -0
  146. package/test/graphic_references/12_navigation_board_base/uncontrolled/forbidden_navigation_at_end_2.png +0 -0
  147. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/initial_state_at_end.png +0 -0
  148. package/test/graphic_references/12_navigation_board_base/uncontrolled/initial_state_at_specific_id.png +0 -0
  149. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/initial_state_default.png +0 -0
  150. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/initial_state_default_with_game_index.png +0 -0
  151. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/initial_state_on_null-move.png +0 -0
  152. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/navigation_with_keys_0.png +0 -0
  153. package/test/graphic_references/12_navigation_board_base/uncontrolled/navigation_with_keys_1.png +0 -0
  154. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/navigation_with_keys_2.png +0 -0
  155. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/navigation_with_keys_3.png +0 -0
  156. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/navigation_with_keys_4.png +0 -0
  157. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/regular_flipping_0.png +0 -0
  158. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/regular_flipping_1.png +0 -0
  159. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/regular_navigation_0.png +0 -0
  160. package/test/graphic_references/{12_navigation_board/controlled/default.png → 12_navigation_board_base/uncontrolled/regular_navigation_1.png} +0 -0
  161. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/regular_navigation_2.png +0 -0
  162. package/test/graphic_references/12_navigation_board_base/uncontrolled/regular_navigation_3.png +0 -0
  163. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/regular_navigation_4.png +0 -0
  164. package/test/graphic_references/{12_navigation_board → 12_navigation_board_base}/uncontrolled/regular_navigation_5.png +0 -0
  165. package/test/graphic_references/13_navigation_board_graphic/additional_buttons/error_1.png +0 -0
  166. package/test/graphic_references/13_navigation_board_graphic/additional_buttons/error_2.png +0 -0
  167. package/test/graphic_references/13_navigation_board_graphic/additional_buttons/multiple_buttons.png +0 -0
  168. package/test/graphic_references/{12_navigation_board/uncontrolled/regular_navigation_3.png → 13_navigation_board_graphic/additional_buttons/single_button.png} +0 -0
  169. package/test/graphic_references/{12_navigation_board → 13_navigation_board_graphic}/theme/custom_1.png +0 -0
  170. package/test/graphic_references/{12_navigation_board → 13_navigation_board_graphic}/theme/custom_2.png +0 -0
  171. package/test/graphic_references/{12_navigation_board → 13_navigation_board_graphic}/theme/large.png +0 -0
  172. package/test/graphic_references/13_navigation_board_graphic/theme/small.png +0 -0
  173. package/test/graphic_references/14_navigation_board_autoplay/controlled/after_1_move_acknowledging_is-playing_changes.png +0 -0
  174. package/test/graphic_references/14_navigation_board_autoplay/controlled/after_1_move_not_acknowledging_is-playing_changes.png +0 -0
  175. package/test/graphic_references/14_navigation_board_autoplay/controlled/after_2_moves_acknowledging_node_changes.png +0 -0
  176. package/test/graphic_references/14_navigation_board_autoplay/controlled/after_2_moves_not_acknowledging_node_changes.png +0 -0
  177. package/test/graphic_references/{12_navigation_board/uncontrolled/forbidden_navigation_at_end_1.png → 14_navigation_board_autoplay/controlled/after_reaching_the_end.png} +0 -0
  178. package/test/graphic_references/{12_navigation_board/uncontrolled/forbidden_navigation_at_end_2.png → 14_navigation_board_autoplay/controlled/already_at_the_end.png} +0 -0
  179. package/test/graphic_references/14_navigation_board_autoplay/controlled/initial_state_acknowledging_is-playing_changes.png +0 -0
  180. package/test/graphic_references/14_navigation_board_autoplay/controlled/initial_state_acknowledging_node_changes.png +0 -0
  181. package/test/graphic_references/{12_navigation_board/uncontrolled/forbidden_navigation_at_end_0.png → 14_navigation_board_autoplay/controlled/initial_state_at_the_end.png} +0 -0
  182. package/test/graphic_references/14_navigation_board_autoplay/controlled/initial_state_close_to_end.png +0 -0
  183. package/test/graphic_references/14_navigation_board_autoplay/controlled/initial_state_not_acknowledging_is-playing_changes.png +0 -0
  184. package/test/graphic_references/14_navigation_board_autoplay/controlled/initial_state_not_acknowledging_node_changes.png +0 -0
  185. package/test/graphic_references/14_navigation_board_autoplay/uncontrolled/after_2_moves_from_beginning_after_stop.png +0 -0
  186. package/test/graphic_references/14_navigation_board_autoplay/uncontrolled/after_2_moves_from_beginning_before_stop.png +0 -0
  187. package/test/graphic_references/14_navigation_board_autoplay/uncontrolled/after_reaching_the_end_after_stop.png +0 -0
  188. package/test/graphic_references/14_navigation_board_autoplay/uncontrolled/after_reaching_the_end_before_stop.png +0 -0
  189. package/test/graphic_references/14_navigation_board_autoplay/uncontrolled/initial_state_close_to_end.png +0 -0
  190. package/test/graphic_references/{12_navigation_board/uncontrolled/navigation_with_keys_1.png → 14_navigation_board_autoplay/uncontrolled/initial_state_default.png} +0 -0
  191. package/test/graphic_test_app/03_error_box_graphic/base.tsx +14 -14
  192. package/test/graphic_test_app/03_error_box_graphic/localization.tsx +1 -1
  193. package/test/graphic_test_app/04_icons_graphic/chess_piece.tsx +7 -7
  194. package/test/graphic_test_app/04_icons_graphic/marker.tsx +15 -15
  195. package/test/graphic_test_app/05_chessboard_graphic/annotations.tsx +7 -7
  196. package/test/graphic_test_app/05_chessboard_graphic/base.tsx +6 -6
  197. package/test/graphic_test_app/05_chessboard_graphic/chess_variants.tsx +7 -7
  198. package/test/graphic_test_app/05_chessboard_graphic/flipped.tsx +5 -5
  199. package/test/graphic_test_app/05_chessboard_graphic/move.tsx +12 -12
  200. package/test/graphic_test_app/05_chessboard_graphic/theme.tsx +12 -12
  201. package/test/graphic_test_app/06_chessboard_animation/base_1.tsx +6 -6
  202. package/test/graphic_test_app/06_chessboard_animation/base_2.tsx +6 -6
  203. package/test/graphic_test_app/06_chessboard_animation/base_3.tsx +6 -6
  204. package/test/graphic_test_app/06_chessboard_animation/on_redraw.tsx +16 -16
  205. package/test/graphic_test_app/07_chessboard_interaction/click_squares.tsx +5 -5
  206. package/test/graphic_test_app/07_chessboard_interaction/edit_arrows.tsx +4 -4
  207. package/test/graphic_test_app/07_chessboard_interaction/error.tsx +2 -2
  208. package/test/graphic_test_app/07_chessboard_interaction/move_pieces.tsx +5 -5
  209. package/test/graphic_test_app/08_chessboard_play_moves/base.tsx +8 -8
  210. package/test/graphic_test_app/08_chessboard_play_moves/promotions.tsx +4 -4
  211. package/test/graphic_test_app/09_chessboard_small_screens/base.tsx +9 -9
  212. package/test/graphic_test_app/09_chessboard_small_screens/top_bottom_components.tsx +9 -9
  213. package/test/graphic_test_app/10_movetext_graphic/base.tsx +4 -4
  214. package/test/graphic_test_app/10_movetext_graphic/error.tsx +6 -6
  215. package/test/graphic_test_app/10_movetext_graphic/format_move.tsx +1 -1
  216. package/test/graphic_test_app/10_movetext_graphic/html.tsx +5 -5
  217. package/test/graphic_test_app/10_movetext_graphic/options.tsx +10 -10
  218. package/test/graphic_test_app/11_movetext_interaction/error.tsx +1 -1
  219. package/test/graphic_test_app/11_movetext_interaction/select_moves.tsx +11 -11
  220. package/test/graphic_test_app/{12_navigation_board → 12_navigation_board_base}/controlled.tsx +5 -5
  221. package/test/graphic_test_app/{12_navigation_board → 12_navigation_board_base}/uncontrolled.tsx +7 -7
  222. package/test/graphic_test_app/13_navigation_board_graphic/additional_buttons.tsx +49 -0
  223. package/test/graphic_test_app/{12_navigation_board → 13_navigation_board_graphic}/error.tsx +2 -2
  224. package/test/graphic_test_app/{12_navigation_board → 13_navigation_board_graphic}/theme.tsx +4 -4
  225. package/test/graphic_test_app/14_navigation_board_autoplay/controlled.tsx +73 -0
  226. package/test/graphic_test_app/14_navigation_board_autoplay/uncontrolled.tsx +34 -0
  227. package/test/graphic_test_app/common/decs.d.ts +2 -2
  228. package/test/graphic_test_app/common/test_app.css +15 -15
  229. package/test/graphic_test_app/common/test_app.tsx +48 -48
  230. package/tsconfig.json +13 -13
  231. package/test/12_navigation_board.js +0 -168
  232. package/test/graphic_references/12_navigation_board/controlled/regular_navigation_2.png +0 -0
  233. package/test/graphic_references/12_navigation_board/theme/small.png +0 -0
  234. package/test/graphic_references/12_navigation_board/uncontrolled/regular_navigation_1.png +0 -0
  235. /package/test/graphic_references/{12_navigation_board → 13_navigation_board_graphic}/error/pgn_parsing_error.png +0 -0
  236. /package/test/graphic_references/{12_navigation_board → 13_navigation_board_graphic}/error/wrong_game_type.png +0 -0
@@ -23,14 +23,14 @@
23
23
 
24
24
 
25
25
  @font-face {
26
- font-family: 'Chess Alpha';
27
- src:
28
- url('./fonts/chessalpha.woff2') format('woff2'),
29
- url('./fonts/chessalpha.woff') format('woff');
30
- font-weight: normal;
31
- font-style: normal;
26
+ font-family: 'Chess Alpha';
27
+ src:
28
+ url('./fonts/chessalpha.woff2') format('woff2'),
29
+ url('./fonts/chessalpha.woff') format('woff');
30
+ font-weight: normal;
31
+ font-style: normal;
32
32
  }
33
33
 
34
34
  .kokopu-font-alpha {
35
- font-family: 'Chess Alpha', sans-serif;
35
+ font-family: 'Chess Alpha', sans-serif;
36
36
  }
@@ -45,48 +45,48 @@ type ChildNode = ReturnType<typeof htmlparser2.parseDocument>['children'][0];
45
45
  */
46
46
  export function htmlFilter(type: 'header' | 'comment'): (text: string) => React.ReactNode {
47
47
 
48
- let allowedTags = [
49
- 'span', // general purpose
50
- 'a', // links
51
- 'b', 'strong', 'i', 'em', // bold, italic
52
- 'del', 'ins', 's', 'u', // underline, strikethrough
53
- 'sub', 'sup', // subscript, superscript
54
- 'abbr', // acronym (typically associated to a title=".." tooltip)
55
- 'q', 'cite', // quotation and title of a work
56
- 'mark', // highlighted text
57
- 'small', // smaller text
58
- ];
59
- const allowedAttributes: Record<string, string[]> = {
60
- '*': [ 'class', 'id', 'title' ],
61
- 'a': [ 'href', 'target' ],
62
- };
63
-
64
- if (type === 'comment') {
65
- allowedTags = allowedTags.concat([
66
- 'div', // general purpose
67
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', // headings
68
- 'p', 'br', // paragraph and line break
69
- 'blockquote', // long quotation
70
- 'img', // image
71
- 'pre', // preformatted text
72
- 'ul', 'ol', 'li', // lists
73
- ]);
74
- Object.assign(allowedAttributes, {
75
- 'img': [ 'alt', 'src', 'height', 'width' ],
76
- });
77
- }
78
-
79
- const sanitizer = new HtmlSanitizer({
80
- allowedTags: allowedTags,
81
- allowedAttributes: allowedAttributes,
82
- });
83
- return text => sanitizer.parse(text);
48
+ let allowedTags = [
49
+ 'span', // general purpose
50
+ 'a', // links
51
+ 'b', 'strong', 'i', 'em', // bold, italic
52
+ 'del', 'ins', 's', 'u', // underline, strikethrough
53
+ 'sub', 'sup', // subscript, superscript
54
+ 'abbr', // acronym (typically associated to a title=".." tooltip)
55
+ 'q', 'cite', // quotation and title of a work
56
+ 'mark', // highlighted text
57
+ 'small', // smaller text
58
+ ];
59
+ const allowedAttributes: Record<string, string[]> = {
60
+ '*': [ 'class', 'id', 'title' ],
61
+ 'a': [ 'href', 'target' ],
62
+ };
63
+
64
+ if (type === 'comment') {
65
+ allowedTags = allowedTags.concat([
66
+ 'div', // general purpose
67
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', // headings
68
+ 'p', 'br', // paragraph and line break
69
+ 'blockquote', // long quotation
70
+ 'img', // image
71
+ 'pre', // preformatted text
72
+ 'ul', 'ol', 'li', // lists
73
+ ]);
74
+ Object.assign(allowedAttributes, {
75
+ 'img': [ 'alt', 'src', 'height', 'width' ],
76
+ });
77
+ }
78
+
79
+ const sanitizer = new HtmlSanitizer({
80
+ allowedTags: allowedTags,
81
+ allowedAttributes: allowedAttributes,
82
+ });
83
+ return text => sanitizer.parse(text);
84
84
  }
85
85
 
86
86
 
87
87
  interface HtmlSanitizerProps {
88
- allowedTags?: string[];
89
- allowedAttributes?: Record<string, string[]>;
88
+ allowedTags?: string[];
89
+ allowedAttributes?: Record<string, string[]>;
90
90
  }
91
91
 
92
92
 
@@ -95,96 +95,96 @@ interface HtmlSanitizerProps {
95
95
  */
96
96
  class HtmlSanitizer {
97
97
 
98
- #allowedTags: Set<string>;
99
- #allowedAttributes: Map<string, Set<string>>;
100
- #keyCounter = 0;
101
-
102
- constructor({ allowedTags, allowedAttributes }: HtmlSanitizerProps) {
103
- this.#allowedTags = allowedTags ? new Set(allowedTags) : new Set();
104
- this.#allowedAttributes = allowedAttributes ? new Map(Object.entries(allowedAttributes).map(([k, v]) => [k, new Set(v)])) : new Map();
105
- }
106
-
107
- /**
108
- * @param text - HTML string to be parsed.
109
- * @returns React object corresponding to the given HTML string. Can also be a fragment, or just a simple string if the given HTML string
110
- * does not contain any HTML tag.
111
- */
112
- parse(text: string): React.ReactNode {
113
- const dom = htmlparser2.parseDocument(text);
114
-
115
- // Ensure that the returned object is a non-empty node type.
116
- if (dom.type !== 'root') {
117
- return undefined;
118
- }
119
-
120
- // Process the children of the root node.
121
- const result: React.ReactNode[] = [];
122
- dom.children.forEach(child => {
123
- const childElement = this.processNode(child);
124
- if (childElement) {
125
- result.push(childElement);
126
- }
127
- });
128
-
129
- // Return the result.
130
- if (result.length === 0) {
131
- return undefined;
132
- }
133
- else if (result.length === 1) {
134
- return result[0];
135
- }
136
- else {
137
- return <React.Fragment key={this.allocateKey()}>{result}</React.Fragment>;
138
- }
139
- }
140
-
141
- private processNode(node: ChildNode): React.ReactNode {
142
- if (node.type === 'text') {
143
- return node.data;
144
- }
145
- else if (node.type === 'tag') {
146
- const children = NO_CHILDREN_TAGS.has(node.name) ? undefined : node.children.map(child => this.processNode(child));
147
- if (this.#allowedTags.has(node.name)) {
148
- const attributes = this.filterAttributes(node.name, node.attribs);
149
- attributes['key'] = this.allocateKey();
150
- return React.createElement(node.name, attributes, children);
151
- }
152
- else {
153
- return children ? <React.Fragment key={this.allocateKey()}>{children}</React.Fragment> : undefined;
154
- }
155
- }
156
- else {
157
- return undefined;
158
- }
159
- }
160
-
161
- private filterAttributes(nodeName: string, attributes: Record<string, string>) {
162
- const result: Record<string, string> = {};
163
- for (const [ attribute, value ] of Object.entries(attributes)) {
164
- if (this.isAttributeAllowed(nodeName, attribute)) {
165
- result[attribute === 'class' ? 'className' : attribute] = value;
166
- }
167
- }
168
- return result;
169
- }
170
-
171
- private isAttributeAllowed(nodeName: string, attribute: string) {
172
- if (attribute === 'key' || attribute === 'ref' || attribute === 'className') { // Those attributes are always forbidden.
173
- return false;
174
- }
175
- const allowedAttributesForCurrent = this.#allowedAttributes.get(nodeName);
176
- if (allowedAttributesForCurrent && allowedAttributesForCurrent.has(attribute)) {
177
- return true;
178
- }
179
- const allowedAttributesForAll = this.#allowedAttributes.get('*');
180
- if (allowedAttributesForAll && allowedAttributesForAll.has(attribute)) {
181
- return true;
182
- }
183
- return false;
184
- }
185
-
186
- private allocateKey() {
187
- return 'auto-' + (this.#keyCounter++);
188
- }
98
+ #allowedTags: Set<string>;
99
+ #allowedAttributes: Map<string, Set<string>>;
100
+ #keyCounter = 0;
101
+
102
+ constructor({ allowedTags, allowedAttributes }: HtmlSanitizerProps) {
103
+ this.#allowedTags = allowedTags ? new Set(allowedTags) : new Set();
104
+ this.#allowedAttributes = allowedAttributes ? new Map(Object.entries(allowedAttributes).map(([k, v]) => [k, new Set(v)])) : new Map();
105
+ }
106
+
107
+ /**
108
+ * @param text - HTML string to be parsed.
109
+ * @returns React object corresponding to the given HTML string. Can also be a fragment, or just a simple string if the given HTML string
110
+ * does not contain any HTML tag.
111
+ */
112
+ parse(text: string): React.ReactNode {
113
+ const dom = htmlparser2.parseDocument(text);
114
+
115
+ // Ensure that the returned object is a non-empty node type.
116
+ if (dom.type !== 'root') {
117
+ return undefined;
118
+ }
119
+
120
+ // Process the children of the root node.
121
+ const result: React.ReactNode[] = [];
122
+ dom.children.forEach(child => {
123
+ const childElement = this.processNode(child);
124
+ if (childElement) {
125
+ result.push(childElement);
126
+ }
127
+ });
128
+
129
+ // Return the result.
130
+ if (result.length === 0) {
131
+ return undefined;
132
+ }
133
+ else if (result.length === 1) {
134
+ return result[0];
135
+ }
136
+ else {
137
+ return <React.Fragment key={this.allocateKey()}>{result}</React.Fragment>;
138
+ }
139
+ }
140
+
141
+ private processNode(node: ChildNode): React.ReactNode {
142
+ if (node.type === 'text') {
143
+ return node.data;
144
+ }
145
+ else if (node.type === 'tag') {
146
+ const children = NO_CHILDREN_TAGS.has(node.name) ? undefined : node.children.map(child => this.processNode(child));
147
+ if (this.#allowedTags.has(node.name)) {
148
+ const attributes = this.filterAttributes(node.name, node.attribs);
149
+ attributes['key'] = this.allocateKey();
150
+ return React.createElement(node.name, attributes, children);
151
+ }
152
+ else {
153
+ return children ? <React.Fragment key={this.allocateKey()}>{children}</React.Fragment> : undefined;
154
+ }
155
+ }
156
+ else {
157
+ return undefined;
158
+ }
159
+ }
160
+
161
+ private filterAttributes(nodeName: string, attributes: Record<string, string>) {
162
+ const result: Record<string, string> = {};
163
+ for (const [ attribute, value ] of Object.entries(attributes)) {
164
+ if (this.isAttributeAllowed(nodeName, attribute)) {
165
+ result[attribute === 'class' ? 'className' : attribute] = value;
166
+ }
167
+ }
168
+ return result;
169
+ }
170
+
171
+ private isAttributeAllowed(nodeName: string, attribute: string) {
172
+ if (attribute === 'key' || attribute === 'ref' || attribute === 'className') { // Those attributes are always forbidden.
173
+ return false;
174
+ }
175
+ const allowedAttributesForCurrent = this.#allowedAttributes.get(nodeName);
176
+ if (allowedAttributesForCurrent && allowedAttributesForCurrent.has(attribute)) {
177
+ return true;
178
+ }
179
+ const allowedAttributesForAll = this.#allowedAttributes.get('*');
180
+ if (allowedAttributesForAll && allowedAttributesForAll.has(attribute)) {
181
+ return true;
182
+ }
183
+ return false;
184
+ }
185
+
186
+ private allocateKey() {
187
+ return 'auto-' + (this.#keyCounter++);
188
+ }
189
189
 
190
190
  }
@@ -38,8 +38,8 @@ import './fonts.css';
38
38
  * @param notation - SAN notation.
39
39
  */
40
40
  export function formatMove(pieceSymbols: 'native' | 'localized' | 'figurines' | PieceSymbolMapping, notation: string): React.ReactNode {
41
- const formatter = moveFormatter(pieceSymbols);
42
- return formatter(notation);
41
+ const formatter = moveFormatter(pieceSymbols);
42
+ return formatter(notation);
43
43
  }
44
44
 
45
45
 
@@ -53,22 +53,22 @@ export function formatMove(pieceSymbols: 'native' | 'localized' | 'figurines' |
53
53
  * - {@link PieceSymbolMapping}: use the given custom set of chess piece symbols.
54
54
  */
55
55
  export function moveFormatter(pieceSymbols: 'native' | 'localized' | 'figurines' | PieceSymbolMapping): (notation: string) => React.ReactNode {
56
- if (pieceSymbols === 'native') {
57
- return notation => notation;
58
- }
59
- else if (pieceSymbols === 'localized') {
60
- const mapping = i18n.PIECE_SYMBOLS;
61
- return notation => pieceMappingNotation(mapping, notation);
62
- }
63
- else if (pieceSymbols === 'figurines') {
64
- return notation => figurineNotation('alpha', notation);
65
- }
66
- else if (isPieceSymbolMapping(pieceSymbols)) {
67
- return notation => pieceMappingNotation(pieceSymbols, notation);
68
- }
69
- else {
70
- throw new IllegalArgument('moveFormatter', 'pieceSymbols');
71
- }
56
+ if (pieceSymbols === 'native') {
57
+ return notation => notation;
58
+ }
59
+ else if (pieceSymbols === 'localized') {
60
+ const mapping = i18n.PIECE_SYMBOLS;
61
+ return notation => pieceMappingNotation(mapping, notation);
62
+ }
63
+ else if (pieceSymbols === 'figurines') {
64
+ return notation => figurineNotation('alpha', notation);
65
+ }
66
+ else if (isPieceSymbolMapping(pieceSymbols)) {
67
+ return notation => pieceMappingNotation(pieceSymbols, notation);
68
+ }
69
+ else {
70
+ throw new IllegalArgument('moveFormatter', 'pieceSymbols');
71
+ }
72
72
  }
73
73
 
74
74
 
@@ -76,7 +76,7 @@ export function moveFormatter(pieceSymbols: 'native' | 'localized' | 'figurines'
76
76
  * Replace the native piece symbols by those defined by the given {@link PieceSymbolMapping}.
77
77
  */
78
78
  function pieceMappingNotation(mapping: PieceSymbolMapping, text: string) {
79
- return text.replace(/[KQRBNP]/g, match => mapping[match as keyof PieceSymbolMapping]);
79
+ return text.replace(/[KQRBNP]/g, match => mapping[match as keyof PieceSymbolMapping]);
80
80
  }
81
81
 
82
82
 
@@ -85,22 +85,22 @@ function pieceMappingNotation(mapping: PieceSymbolMapping, text: string) {
85
85
  * React objects represented with the given chess font.
86
86
  */
87
87
  function figurineNotation(fontName: string, text: string): React.ReactNode {
88
- const result: React.ReactNode[] = [];
89
- let beginOfText = 0;
90
- let pieceSymbolIndex = 0;
91
- for (let pos = 0; pos < text.length; ++pos) {
92
- const currentChar = text.charAt(pos);
93
- if (currentChar === 'K' || currentChar === 'Q' || currentChar === 'R' || currentChar === 'B' || currentChar === 'N' || currentChar === 'P') {
94
- if (pos > beginOfText) {
95
- result.push(text.substring(beginOfText, pos));
96
- }
97
- beginOfText = pos + 1;
98
- const key = 'symbol-' + (pieceSymbolIndex++);
99
- result.push(<span className={'kokopu-font-' + fontName} key={key}>{currentChar}</span>);
100
- }
101
- }
102
- if (beginOfText < text.length) {
103
- result.push(text.substring(beginOfText));
104
- }
105
- return <>{result}</>;
88
+ const result: React.ReactNode[] = [];
89
+ let beginOfText = 0;
90
+ let pieceSymbolIndex = 0;
91
+ for (let pos = 0; pos < text.length; ++pos) {
92
+ const currentChar = text.charAt(pos);
93
+ if (currentChar === 'K' || currentChar === 'Q' || currentChar === 'R' || currentChar === 'B' || currentChar === 'N' || currentChar === 'P') {
94
+ if (pos > beginOfText) {
95
+ result.push(text.substring(beginOfText, pos));
96
+ }
97
+ beginOfText = pos + 1;
98
+ const key = 'symbol-' + (pieceSymbolIndex++);
99
+ result.push(<span className={'kokopu-font-' + fontName} key={key}>{currentChar}</span>);
100
+ }
101
+ }
102
+ if (beginOfText < text.length) {
103
+ result.push(text.substring(beginOfText));
104
+ }
105
+ return <>{result}</>;
106
106
  }