kokopu-react 1.5.5 → 1.7.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.
- package/.nycrc.yml +1 -1
- package/CHANGELOG.md +18 -0
- package/README.md +2 -2
- package/dist/lib/ArrowMarkerIcon.js +2 -2
- package/dist/lib/Chessboard.js +26 -18
- package/dist/lib/Movetext.js +41 -82
- package/dist/lib/formatmove.js +119 -0
- package/dist/lib/impl/HtmlSanitizer.js +8 -3
- package/dist/lib/impl/colorsets.js +44 -44
- package/dist/lib/impl/util.js +1 -1
- package/dist/lib/index.js +14 -0
- package/dist/lib/markers.js +7 -7
- package/doc_src/demo/PageChessboardInteraction.js +4 -3
- package/doc_src/demo/PageChessboardMove.js +36 -5
- package/doc_src/demo/demo.css +4 -0
- package/{graphic_test_app → graphic_test_src}/01_marker_icons.js +0 -0
- package/{graphic_test_app → graphic_test_src}/02_chessboard_simple.js +0 -0
- package/{graphic_test_app → graphic_test_src}/03_chessboard_flipped.js +0 -0
- package/{graphic_test_app → graphic_test_src}/04_chessboard_annotations.js +8 -8
- package/{graphic_test_app → graphic_test_src}/05_chessboard_move.js +4 -4
- package/{graphic_test_app → graphic_test_src}/06_chessboard_theme.js +2 -2
- package/{graphic_test_app → graphic_test_src}/07_chessboard_click_squares.js +0 -0
- package/{graphic_test_app → graphic_test_src}/08_chessboard_move_pieces.js +1 -1
- package/{graphic_test_app → graphic_test_src}/09_chessboard_edit_arrows.js +0 -0
- package/{graphic_test_app → graphic_test_src}/10_chessboard_play_moves.js +1 -1
- package/{graphic_test_app → graphic_test_src}/11_chessboard_play_promotions.js +0 -0
- package/{graphic_test_app → graphic_test_src}/12_movetext_simple.js +0 -0
- package/{graphic_test_app → graphic_test_src}/13_movetext_error.js +0 -0
- package/{graphic_test_app → graphic_test_src}/14_movetext_html.js +0 -0
- package/{graphic_test_app → graphic_test_src}/15_movetext_options.js +0 -4
- package/{graphic_test_app → graphic_test_src}/16_movetext_interaction.js +0 -0
- package/{graphic_test_app → graphic_test_src}/common/dummy.pgn +0 -0
- package/{graphic_test_app → graphic_test_src}/common/games.pgn +11 -5
- package/{graphic_test_app → graphic_test_src}/common/heartbeat.txt +0 -0
- package/graphic_test_src/common/smiley.png +0 -0
- package/{graphic_test_app → graphic_test_src}/common/test_app.css +0 -0
- package/{graphic_test_app → graphic_test_src}/common/test_app.js +3 -2
- package/graphics/chess_sprites/cburnett/bb.svg +45 -0
- package/graphics/chess_sprites/cburnett/bk.svg +47 -0
- package/graphics/chess_sprites/cburnett/bn.svg +37 -0
- package/graphics/chess_sprites/cburnett/bp.svg +19 -0
- package/graphics/chess_sprites/cburnett/bq.svg +58 -0
- package/graphics/chess_sprites/cburnett/br.svg +60 -0
- package/graphics/chess_sprites/cburnett/bx.svg +56 -0
- package/graphics/chess_sprites/cburnett/wb.svg +48 -0
- package/graphics/chess_sprites/cburnett/wk.svg +26 -0
- package/graphics/chess_sprites/cburnett/wn.svg +37 -0
- package/graphics/chess_sprites/cburnett/wp.svg +19 -0
- package/graphics/chess_sprites/cburnett/wq.svg +60 -0
- package/graphics/chess_sprites/cburnett/wr.svg +44 -0
- package/graphics/chess_sprites/cburnett/wx.svg +56 -0
- package/graphics/chess_sprites/generate_sprites.sh +135 -0
- package/graphics/chess_sprites/mmonge/celtic-bx.svg +112 -0
- package/graphics/chess_sprites/mmonge/celtic-wx.svg +111 -0
- package/graphics/chess_sprites/mmonge/celtic.svg +1576 -0
- package/graphics/chess_sprites/mmonge/eyes-spatial-bx.svg +101 -0
- package/graphics/chess_sprites/mmonge/eyes-spatial-wx.svg +100 -0
- package/graphics/chess_sprites/mmonge/eyes.svg +2538 -0
- package/graphics/chess_sprites/mmonge/fantasy-bx.svg +112 -0
- package/graphics/chess_sprites/mmonge/fantasy-wx.svg +111 -0
- package/graphics/chess_sprites/mmonge/fantasy.svg +5497 -0
- package/graphics/chess_sprites/mmonge/fantasy_alt.svg +3742 -0
- package/graphics/chess_sprites/mmonge/freak.svg +3479 -0
- package/graphics/chess_sprites/mmonge/prmi.svg +6776 -0
- package/graphics/chess_sprites/mmonge/skulls-bx.svg +124 -0
- package/graphics/chess_sprites/mmonge/skulls-wx.svg +111 -0
- package/graphics/chess_sprites/mmonge/skulls.svg +12978 -0
- package/graphics/chess_sprites/mmonge/spatial.svg +1223 -0
- package/package.json +28 -22
- package/scripts/{test_graphic/clean-graphic-output.js → clean-graphic-output.js} +1 -1
- package/scripts/docker-compose.yml +14 -0
- package/scripts/{test_graphic/webpack-config.js → test-graphic.webpack.config.js} +9 -5
- package/scripts/{test_headless.webpack-config.js → test-headless.webpack.config.js} +2 -1
- package/src/Chessboard.js +24 -16
- package/src/Movetext.js +37 -59
- package/src/formatmove.js +91 -0
- package/src/impl/HtmlSanitizer.js +8 -2
- package/src/impl/colorsets.js +44 -44
- package/src/impl/util.js +1 -1
- package/src/index.js +1 -0
- package/src/markers.js +7 -7
- package/test/1_markers.js +14 -14
- package/test/common/graphic.js +2 -2
- package/test/references/04_chessboard_annotations/0.png +0 -0
- package/test/references/04_chessboard_annotations/1.png +0 -0
- package/test/references/04_chessboard_annotations/2.png +0 -0
- package/test/references/04_chessboard_annotations/3.png +0 -0
- package/test/references/04_chessboard_annotations/4.png +0 -0
- package/test/references/04_chessboard_annotations/5.png +0 -0
- package/test/references/05_chessboard_move/3.png +0 -0
- package/test/references/05_chessboard_move/4.png +0 -0
- package/test/references/05_chessboard_move/5.png +0 -0
- package/test/references/05_chessboard_move/6.png +0 -0
- package/test/references/06_chessboard_theme/0.png +0 -0
- package/test/references/06_chessboard_theme/1.png +0 -0
- package/test/references/06_chessboard_theme/2.png +0 -0
- package/test/references/06_chessboard_theme/3.png +0 -0
- package/test/references/06_chessboard_theme/4.png +0 -0
- package/test/references/06_chessboard_theme/5.png +0 -0
- package/test/references/06_chessboard_theme/6.png +0 -0
- package/test/references/06_chessboard_theme/7.png +0 -0
- package/test/references/06_chessboard_theme/8.png +0 -0
- package/test/references/08_chessboard_move_pieces/over_arrow_marker.png +0 -0
- package/test/references/08_chessboard_move_pieces/over_square_marker.png +0 -0
- package/test/references/08_chessboard_move_pieces/over_text_marker.png +0 -0
- package/test/references/09_chessboard_edit_arrows/base_1.png +0 -0
- package/test/references/09_chessboard_edit_arrows/base_2.png +0 -0
- package/test/references/09_chessboard_edit_arrows/null_vector.png +0 -0
- package/test/references/09_chessboard_edit_arrows/over_arrow_marker.png +0 -0
- package/test/references/09_chessboard_edit_arrows/over_square_marker.png +0 -0
- package/test/references/10_chessboard_play_moves/castling_move.png +0 -0
- package/test/references/10_chessboard_play_moves/regular_move_2.png +0 -0
- package/test/references/14_movetext_html/0.png +0 -0
- package/test/references/14_movetext_html/1.png +0 -0
- package/test/references/14_movetext_html/2.png +0 -0
- package/scripts/test_graphic/docker-compose.yml +0 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kokopu-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "A React-based library to create and display chessboard and chess-related components.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chess",
|
|
@@ -18,61 +18,67 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist/lib",
|
|
20
20
|
"doc_src",
|
|
21
|
-
"
|
|
21
|
+
"graphic_test_src",
|
|
22
22
|
"graphics",
|
|
23
|
-
"scripts/
|
|
24
|
-
"scripts/
|
|
23
|
+
"scripts/clean-graphic-output.js",
|
|
24
|
+
"scripts/docker-compose.yml",
|
|
25
|
+
"scripts/test-graphic.webpack.config.js",
|
|
26
|
+
"scripts/test-headless.webpack.config.js",
|
|
25
27
|
"src",
|
|
26
28
|
"test",
|
|
27
29
|
".babelrc",
|
|
28
30
|
".eslint.yml",
|
|
29
31
|
".nycrc.yml"
|
|
30
32
|
],
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"kokopu": "^2.6.0",
|
|
35
|
+
"react": "17.x.x || 18.x.x"
|
|
36
|
+
},
|
|
31
37
|
"dependencies": {
|
|
32
38
|
"htmlparser2": "^8.0.1",
|
|
33
|
-
"kokopu": "^2.8.0",
|
|
34
39
|
"prop-types": "^15.8.1",
|
|
35
|
-
"react": "^17.0.2",
|
|
36
40
|
"react-draggable": "^4.4.5",
|
|
37
41
|
"react-motion": "^0.5.2"
|
|
38
42
|
},
|
|
39
43
|
"devDependencies": {
|
|
40
44
|
"@babel/cli": "^7.17.10",
|
|
41
|
-
"@babel/core": "^7.
|
|
42
|
-
"@babel/preset-env": "^7.
|
|
45
|
+
"@babel/core": "^7.18.5",
|
|
46
|
+
"@babel/preset-env": "^7.18.2",
|
|
43
47
|
"@babel/preset-react": "^7.17.12",
|
|
44
|
-
"@emotion/react": "^11.9.
|
|
45
|
-
"@emotion/styled": "^11.
|
|
46
|
-
"@mui/material": "^5.8.
|
|
48
|
+
"@emotion/react": "^11.9.3",
|
|
49
|
+
"@emotion/styled": "^11.9.3",
|
|
50
|
+
"@mui/material": "^5.8.5",
|
|
47
51
|
"babel-loader": "^8.2.5",
|
|
48
52
|
"babel-plugin-istanbul": "^6.1.1",
|
|
49
53
|
"copy-webpack-plugin": "^11.0.0",
|
|
50
54
|
"coveralls": "^3.1.1",
|
|
51
55
|
"css-loader": "^6.7.1",
|
|
52
|
-
"eslint": "^8.
|
|
53
|
-
"eslint-plugin-react": "^7.
|
|
56
|
+
"eslint": "^8.18.0",
|
|
57
|
+
"eslint-plugin-react": "^7.30.0",
|
|
54
58
|
"html-webpack-plugin": "^5.5.0",
|
|
55
59
|
"img-diff-js": "^0.5.2",
|
|
60
|
+
"kokopu": "^2.9.0",
|
|
56
61
|
"mocha": "^10.0.0",
|
|
57
62
|
"null-loader": "^4.0.1",
|
|
58
63
|
"nyc": "^15.1.0",
|
|
59
|
-
"react
|
|
64
|
+
"react": "^18.2.0",
|
|
65
|
+
"react-dom": "^18.2.0",
|
|
60
66
|
"react-styleguidist": "^11.2.0",
|
|
61
|
-
"selenium-webdriver": "^4.
|
|
62
|
-
"ssh2-sftp-client": "^
|
|
67
|
+
"selenium-webdriver": "^4.2.0",
|
|
68
|
+
"ssh2-sftp-client": "^9.0.1",
|
|
63
69
|
"style-loader": "^3.3.1",
|
|
64
70
|
"unit.js": "^2.1.1",
|
|
65
|
-
"webpack": "^5.
|
|
66
|
-
"webpack-cli": "^4.
|
|
71
|
+
"webpack": "^5.73.0",
|
|
72
|
+
"webpack-cli": "^4.10.0"
|
|
67
73
|
},
|
|
68
74
|
"scripts": {
|
|
69
|
-
"lint": "eslint doc_src
|
|
70
|
-
"preunit": "webpack --config scripts/
|
|
75
|
+
"lint": "eslint doc_src graphic_test_src src test",
|
|
76
|
+
"preunit": "webpack --config scripts/test-headless.webpack.config.js && webpack --config scripts/test-graphic.webpack.config.js && node scripts/clean-graphic-output.js",
|
|
71
77
|
"unit": "nyc mocha --bail --sort",
|
|
72
78
|
"test": "npm run lint && npm run unit",
|
|
73
79
|
"publish_coverage": "nyc report --reporter=text-lcov | coveralls",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
80
|
+
"test_env:start": "docker-compose -p kokopu-react-test-env -f scripts/docker-compose.yml up -d",
|
|
81
|
+
"test_env:stop": "docker-compose -p kokopu-react-test-env -f scripts/docker-compose.yml down",
|
|
76
82
|
"build": "make -f scripts/build.mk",
|
|
77
83
|
"clean": "make -f scripts/build.mk clean",
|
|
78
84
|
"start": "styleguidist server --open --config scripts/styleguide.config.js",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
version: "3.7"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
web-client:
|
|
5
|
+
image: selenium/standalone-firefox:4.2.2-20220622
|
|
6
|
+
shm_size: '2gb'
|
|
7
|
+
ports:
|
|
8
|
+
- 4444:4444 # http://localhost:4444 (driver)
|
|
9
|
+
- 7900:7900 # http://localhost:7900 (VNC, pass: secret)
|
|
10
|
+
volumes:
|
|
11
|
+
- type: bind
|
|
12
|
+
source: ..
|
|
13
|
+
target: /app
|
|
14
|
+
read_only: true
|
|
@@ -25,11 +25,11 @@ const path = require('path');
|
|
|
25
25
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
|
26
26
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
27
27
|
|
|
28
|
-
// List all the JS files in /
|
|
29
|
-
var items = fs.readdirSync('./
|
|
28
|
+
// List all the JS files in /graphic_test_src, each of them corresponding to a entry.
|
|
29
|
+
var items = fs.readdirSync('./graphic_test_src').filter(filename => path.extname(filename) === '.js').map(filename => path.basename(filename, '.js')).sort();
|
|
30
30
|
var entries = {};
|
|
31
31
|
items.forEach(item => {
|
|
32
|
-
entries[item] = `./
|
|
32
|
+
entries[item] = `./graphic_test_src/${item}`;
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
// Define the outputs.
|
|
@@ -39,14 +39,18 @@ var plugins = items.map(item => new HtmlWebpackPlugin({
|
|
|
39
39
|
filename: item + '/index.html',
|
|
40
40
|
}));
|
|
41
41
|
plugins.push(new CopyWebpackPlugin({
|
|
42
|
-
patterns: [
|
|
42
|
+
patterns: [
|
|
43
|
+
{ from: './graphic_test_src/common/heartbeat.txt', to: 'heartbeat.txt' },
|
|
44
|
+
{ from: './graphic_test_src/common/smiley.png', to: 'smiley.png' },
|
|
45
|
+
],
|
|
43
46
|
}));
|
|
44
47
|
|
|
45
48
|
module.exports = {
|
|
46
49
|
mode: 'development',
|
|
50
|
+
devtool: 'inline-source-map',
|
|
47
51
|
entry: entries,
|
|
48
52
|
output: {
|
|
49
|
-
path: path.resolve(__dirname, '
|
|
53
|
+
path: path.resolve(__dirname, '../build/test_graphic'),
|
|
50
54
|
hashFunction: "xxhash64", // FIXME The default hash function used by Webpack has been removed from OpenSSL.
|
|
51
55
|
},
|
|
52
56
|
plugins: plugins,
|
|
@@ -24,8 +24,9 @@ const path = require('path');
|
|
|
24
24
|
|
|
25
25
|
module.exports = {
|
|
26
26
|
mode: 'development',
|
|
27
|
+
devtool: 'inline-source-map',
|
|
27
28
|
entry: {
|
|
28
|
-
index: './src/index
|
|
29
|
+
index: './src/index',
|
|
29
30
|
},
|
|
30
31
|
output: {
|
|
31
32
|
path: path.resolve(__dirname, '../build/test_headless'),
|
package/src/Chessboard.js
CHANGED
|
@@ -123,10 +123,10 @@ export default class Chessboard extends React.Component {
|
|
|
123
123
|
return (
|
|
124
124
|
<svg className="kokopu-chessboard" viewBox={viewBox} width={xmax - xmin} height={ymax - ymin}>
|
|
125
125
|
<defs>
|
|
126
|
+
{this.renderArrowTip(colorset, 'b')}
|
|
126
127
|
{this.renderArrowTip(colorset, 'g')}
|
|
127
128
|
{this.renderArrowTip(colorset, 'r')}
|
|
128
129
|
{this.renderArrowTip(colorset, 'y')}
|
|
129
|
-
{this.renderArrowTip(colorset, 'highlight')}
|
|
130
130
|
</defs>
|
|
131
131
|
{squares}
|
|
132
132
|
{rankCoordinates}
|
|
@@ -213,7 +213,8 @@ export default class Chessboard extends React.Component {
|
|
|
213
213
|
let { x, y } = this.getSquareCoordinates(squareSize, this.state.hoveredSquare);
|
|
214
214
|
let thickness = Math.max(2, Math.round(HOVER_MARKER_THICKNESS_FACTOR * squareSize));
|
|
215
215
|
let size = squareSize - thickness;
|
|
216
|
-
|
|
216
|
+
let color = this.isEditArrowModeEnabled() ? this.props.editedArrowColor : this.props.moveArrowColor;
|
|
217
|
+
return <rect className="kokopu-hoveredSquare" x={x + thickness/2} y={y + thickness/2} width={size} height={size} stroke={colorset['c' + color]} strokeWidth={thickness} />;
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
renderPiece(position, squareSize, pieceset, sq) {
|
|
@@ -272,7 +273,7 @@ export default class Chessboard extends React.Component {
|
|
|
272
273
|
return (
|
|
273
274
|
<line
|
|
274
275
|
className="kokopu-annotation kokopu-arrow kokopu-arrowDraggable kokopu-dragging" x1={xFrom} y1={yFrom} x2={xTo} y2={yTo}
|
|
275
|
-
stroke={colorset[this.props.editedArrowColor]} strokeWidth={strokeWidth} markerEnd={`url(#${arrowTipId})`}
|
|
276
|
+
stroke={colorset['c' + this.props.editedArrowColor]} strokeWidth={strokeWidth} markerEnd={`url(#${arrowTipId})`}
|
|
276
277
|
/>
|
|
277
278
|
);
|
|
278
279
|
}
|
|
@@ -335,7 +336,7 @@ export default class Chessboard extends React.Component {
|
|
|
335
336
|
let result = [];
|
|
336
337
|
Object.entries(sqm).forEach(([ sq, color ]) => {
|
|
337
338
|
let { x, y } = this.getSquareCoordinates(squareSize, sq);
|
|
338
|
-
result.push(<rect key={'sqm-' + sq} className="kokopu-annotation" x={x} y={y} width={squareSize} height={squareSize} fill={colorset[color]} />);
|
|
339
|
+
result.push(<rect key={'sqm-' + sq} className="kokopu-annotation" x={x} y={y} width={squareSize} height={squareSize} fill={colorset['c' + color]} />);
|
|
339
340
|
});
|
|
340
341
|
return result;
|
|
341
342
|
}
|
|
@@ -348,7 +349,7 @@ export default class Chessboard extends React.Component {
|
|
|
348
349
|
y += squareSize / 2;
|
|
349
350
|
result.push(
|
|
350
351
|
<g key={'txtm-' + sq} className="kokopu-annotation">
|
|
351
|
-
<TextSymbol x={x} y={y} size={squareSize} symbol={value.symbol} color={colorset[value.color]} />
|
|
352
|
+
<TextSymbol x={x} y={y} size={squareSize} symbol={value.symbol} color={colorset['c' + value.color]} />
|
|
352
353
|
</g>
|
|
353
354
|
);
|
|
354
355
|
});
|
|
@@ -376,7 +377,7 @@ export default class Chessboard extends React.Component {
|
|
|
376
377
|
result.push(
|
|
377
378
|
<line
|
|
378
379
|
key={'arm-' + vect} className="kokopu-annotation kokopu-arrow" x1={xFrom} y1={yFrom} x2={xTo} y2={yTo}
|
|
379
|
-
stroke={colorset[color]} strokeWidth={strokeWidth} markerEnd={`url(#${arrowTipId})`}
|
|
380
|
+
stroke={colorset['c' + color]} strokeWidth={strokeWidth} markerEnd={`url(#${arrowTipId})`}
|
|
380
381
|
/>
|
|
381
382
|
);
|
|
382
383
|
});
|
|
@@ -397,16 +398,17 @@ export default class Chessboard extends React.Component {
|
|
|
397
398
|
yTo += Math.sign(yFrom - yTo) * ARROW_TIP_OFFSET_FACTOR * squareSize;
|
|
398
399
|
let x = xTo * alpha + xFrom * (1 - alpha);
|
|
399
400
|
let y = yTo * alpha + yFrom * (1 - alpha);
|
|
401
|
+
let color = this.props.moveArrowColor;
|
|
400
402
|
return (
|
|
401
403
|
<line
|
|
402
|
-
className="kokopu-annotation kokopu-arrow" x1={xFrom} y1={yFrom} x2={x} y2={y} stroke={colorset['
|
|
403
|
-
strokeWidth={squareSize * STROKE_THICKNESS_FACTOR} markerEnd={`url(#${this.getArrowTipId(
|
|
404
|
+
className="kokopu-annotation kokopu-arrow" x1={xFrom} y1={yFrom} x2={x} y2={y} stroke={colorset['c' + color]}
|
|
405
|
+
strokeWidth={squareSize * STROKE_THICKNESS_FACTOR} markerEnd={`url(#${this.getArrowTipId(color)})`}
|
|
404
406
|
/>
|
|
405
407
|
);
|
|
406
408
|
}
|
|
407
409
|
|
|
408
410
|
renderArrowTip(colorset, color) {
|
|
409
|
-
return <ArrowTip id={this.getArrowTipId(color)} color={colorset[color]} />;
|
|
411
|
+
return <ArrowTip id={this.getArrowTipId(color)} color={colorset['c' + color]} />;
|
|
410
412
|
}
|
|
411
413
|
|
|
412
414
|
renderTurnFlag(turn, squareSize, pieceset) {
|
|
@@ -735,7 +737,7 @@ export default class Chessboard extends React.Component {
|
|
|
735
737
|
/**
|
|
736
738
|
* Available colorsets for theming.
|
|
737
739
|
*
|
|
738
|
-
* @returns {Object.<string, {w: string, b: string,
|
|
740
|
+
* @returns {Object.<string, { w: string, b: string, cb: string, cg: string, cr: string, cy: string }>}
|
|
739
741
|
* @public
|
|
740
742
|
*/
|
|
741
743
|
static colorsets() {
|
|
@@ -745,8 +747,8 @@ export default class Chessboard extends React.Component {
|
|
|
745
747
|
/**
|
|
746
748
|
* Available piecesets for theming.
|
|
747
749
|
*
|
|
748
|
-
* @returns {Object.<string, {bb: string, bk: string, bn: string, bp: string, bq: string, br: string, bx: string,
|
|
749
|
-
*
|
|
750
|
+
* @returns {Object.<string, { bb: string, bk: string, bn: string, bp: string, bq: string, br: string, bx: string,
|
|
751
|
+
* wb: string, wk: string, wn: string, wp: string, wq: string, wr: string, wx: string }>}
|
|
750
752
|
* @public
|
|
751
753
|
*/
|
|
752
754
|
static piecesets() {
|
|
@@ -789,7 +791,7 @@ Chessboard.propTypes = {
|
|
|
789
791
|
* Square markers, defined as a "square -> color" struct (e.g. `{ e4: 'g', d5: 'r' }`) or as a comma-separated CSL string (e.g. `'Rd5,Ge4'`).
|
|
790
792
|
*/
|
|
791
793
|
squareMarkers: PropTypes.oneOfType([
|
|
792
|
-
PropTypes.objectOf(PropTypes.oneOf([ 'g', 'r', 'y' ])),
|
|
794
|
+
PropTypes.objectOf(PropTypes.oneOf([ 'b', 'g', 'r', 'y' ])),
|
|
793
795
|
PropTypes.string
|
|
794
796
|
]),
|
|
795
797
|
|
|
@@ -800,7 +802,7 @@ Chessboard.propTypes = {
|
|
|
800
802
|
textMarkers: PropTypes.oneOfType([
|
|
801
803
|
PropTypes.objectOf(PropTypes.shape({
|
|
802
804
|
symbol: PropTypes.string.isRequired,
|
|
803
|
-
color: PropTypes.oneOf([ 'g', 'r', 'y' ]).isRequired
|
|
805
|
+
color: PropTypes.oneOf([ 'b', 'g', 'r', 'y' ]).isRequired
|
|
804
806
|
})),
|
|
805
807
|
PropTypes.string
|
|
806
808
|
]),
|
|
@@ -810,7 +812,7 @@ Chessboard.propTypes = {
|
|
|
810
812
|
* or as a comma-separated CAL string (e.g. `'Ge2e4,Rg8f6,Yg8h6'`).
|
|
811
813
|
*/
|
|
812
814
|
arrowMarkers: PropTypes.oneOfType([
|
|
813
|
-
PropTypes.objectOf(PropTypes.oneOf([ 'g', 'r', 'y' ])),
|
|
815
|
+
PropTypes.objectOf(PropTypes.oneOf([ 'b', 'g', 'r', 'y' ])),
|
|
814
816
|
PropTypes.string
|
|
815
817
|
]),
|
|
816
818
|
|
|
@@ -834,6 +836,11 @@ Chessboard.propTypes = {
|
|
|
834
836
|
*/
|
|
835
837
|
moveArrowVisible: PropTypes.bool,
|
|
836
838
|
|
|
839
|
+
/**
|
|
840
|
+
* Color of the move arrow.
|
|
841
|
+
*/
|
|
842
|
+
moveArrowColor: PropTypes.oneOf([ 'b', 'g', 'r', 'y' ]),
|
|
843
|
+
|
|
837
844
|
/**
|
|
838
845
|
* Whether moves are animated or not.
|
|
839
846
|
*/
|
|
@@ -881,7 +888,7 @@ Chessboard.propTypes = {
|
|
|
881
888
|
/**
|
|
882
889
|
* Color of the edited arrow (only used if `interactionMode` is set to `'editArrows'`).
|
|
883
890
|
*/
|
|
884
|
-
editedArrowColor: PropTypes.oneOf([ 'g', 'r', 'y' ]),
|
|
891
|
+
editedArrowColor: PropTypes.oneOf([ 'b', 'g', 'r', 'y' ]),
|
|
885
892
|
|
|
886
893
|
/**
|
|
887
894
|
* Callback invoked when a piece is moved through drag&drop (only if `interactionMode` is set to `'movePieces'`).
|
|
@@ -921,6 +928,7 @@ Chessboard.defaultProps = {
|
|
|
921
928
|
squareSize: 40,
|
|
922
929
|
coordinateVisible: true,
|
|
923
930
|
moveArrowVisible: true,
|
|
931
|
+
moveArrowColor: 'b',
|
|
924
932
|
animated: false,
|
|
925
933
|
colorset: 'original',
|
|
926
934
|
pieceset: 'cburnett',
|
package/src/Movetext.js
CHANGED
|
@@ -27,9 +27,9 @@ import kokopu from 'kokopu';
|
|
|
27
27
|
import HtmlSanitizer from './impl/HtmlSanitizer';
|
|
28
28
|
import Chessboard from './Chessboard';
|
|
29
29
|
import ErrorBox from './ErrorBox';
|
|
30
|
+
import { moveFormatter } from './formatmove';
|
|
30
31
|
import i18n from './i18n';
|
|
31
32
|
|
|
32
|
-
import './css/fonts.css';
|
|
33
33
|
import './css/movetext.css';
|
|
34
34
|
|
|
35
35
|
|
|
@@ -144,7 +144,8 @@ export default class Movetext extends React.Component {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
renderBody(game) {
|
|
147
|
-
|
|
147
|
+
let notationTextBuilder = moveFormatter(this.props.pieceSymbols);
|
|
148
|
+
return this.renderVariation(notationTextBuilder, game.mainVariation(), 'main-variation', true, game.result());
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
/**
|
|
@@ -284,9 +285,9 @@ export default class Movetext extends React.Component {
|
|
|
284
285
|
*/
|
|
285
286
|
renderComment(node, comment, isVariation) {
|
|
286
287
|
let content;
|
|
288
|
+
let sanitizer = createSanitizer(true);
|
|
287
289
|
if (this.props.diagramVisible && comment.includes('[#]')) {
|
|
288
290
|
content = [];
|
|
289
|
-
let sanitizer = createSanitizer();
|
|
290
291
|
let isFirstPart = true;
|
|
291
292
|
for (let [index, part] of comment.split('[#]').entries()) {
|
|
292
293
|
if (!isFirstPart) {
|
|
@@ -309,7 +310,7 @@ export default class Movetext extends React.Component {
|
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
312
|
else {
|
|
312
|
-
content = sanitizeHtml(comment);
|
|
313
|
+
content = sanitizeHtml(comment, sanitizer);
|
|
313
314
|
}
|
|
314
315
|
let key = isVariation ? 'initial-comment' : node.fullMoveNumber() + node.moveColor() + '-comment';
|
|
315
316
|
return node.isLongComment() ? <div className="kokopu-comment" key={key}>{content}</div> : <span className="kokopu-comment" key={key}>{content}</span>;
|
|
@@ -368,26 +369,6 @@ export default class Movetext extends React.Component {
|
|
|
368
369
|
}
|
|
369
370
|
}
|
|
370
371
|
|
|
371
|
-
/**
|
|
372
|
-
* Return the square at the given location.
|
|
373
|
-
*/
|
|
374
|
-
getNotationTextBuilder() {
|
|
375
|
-
let pieceSymbols = this.props.pieceSymbols;
|
|
376
|
-
if (pieceSymbols === 'localized') {
|
|
377
|
-
let mapping = i18n.PIECE_SYMBOLS;
|
|
378
|
-
return notation => notation.replace(/[KQRBNP]/g, match => mapping[match]);
|
|
379
|
-
}
|
|
380
|
-
else if (pieceSymbols === 'figurines') {
|
|
381
|
-
return notation => figurineNotation(notation, 'alpha');
|
|
382
|
-
}
|
|
383
|
-
else if (pieceSymbols !== 'native' && pieceSymbols && ['K', 'Q', 'R', 'B', 'N', 'P'].every(p => typeof pieceSymbols[p] === 'string')) {
|
|
384
|
-
return notation => notation.replace(/[KQRBNP]/g, match => pieceSymbols[match]);
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
return notation => notation;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
372
|
/**
|
|
392
373
|
* Set the focus to the current component.
|
|
393
374
|
*
|
|
@@ -615,52 +596,49 @@ function formatResult(result) {
|
|
|
615
596
|
}
|
|
616
597
|
|
|
617
598
|
|
|
618
|
-
function createSanitizer() {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
599
|
+
function createSanitizer(withBlockTags) {
|
|
600
|
+
let allowedTags = [
|
|
601
|
+
'span', // general purpose
|
|
602
|
+
'a', // links
|
|
603
|
+
'b', 'strong', 'i', 'em', // bold, italic
|
|
604
|
+
'del', 'ins', 's', 'u', // underline, strikethrough
|
|
605
|
+
'sub', 'sup', // subscript, superscript
|
|
606
|
+
'abbr', // acronym (typically associated to a title=".." tooltip)
|
|
607
|
+
'q', 'cite', // quotation and title of a work
|
|
608
|
+
'mark', // highlighted text
|
|
609
|
+
'small', // smaller text
|
|
610
|
+
];
|
|
611
|
+
let allowedAttributes = {
|
|
612
|
+
'*': [ 'class', 'id', 'title' ],
|
|
613
|
+
'a': [ 'href', 'target' ],
|
|
614
|
+
};
|
|
615
|
+
if (withBlockTags) {
|
|
616
|
+
allowedTags = allowedTags.concat([
|
|
617
|
+
'div', // general purpose
|
|
618
|
+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', // headings
|
|
619
|
+
'p', 'br', // paragraph and line break
|
|
620
|
+
'blockquote', // long quotation
|
|
621
|
+
'img', // image
|
|
622
|
+
'pre', // preformatted text
|
|
623
|
+
'ul', 'ol', 'li', // lists
|
|
624
|
+
]);
|
|
625
|
+
Object.assign(allowedAttributes, {
|
|
626
|
+
'img': [ 'alt', 'src', 'height', 'width' ],
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
return new HtmlSanitizer({ allowedTags: allowedTags, allowedAttributes: allowedAttributes });
|
|
626
630
|
}
|
|
627
631
|
|
|
628
632
|
|
|
629
633
|
function sanitizeHtml(text, sanitizer) {
|
|
630
634
|
if (!sanitizer) {
|
|
631
|
-
sanitizer = createSanitizer();
|
|
635
|
+
sanitizer = createSanitizer(false);
|
|
632
636
|
}
|
|
633
637
|
let result = sanitizer.parse(text);
|
|
634
638
|
return result ?? text;
|
|
635
639
|
}
|
|
636
640
|
|
|
637
641
|
|
|
638
|
-
/**
|
|
639
|
-
* Decompose the given string into piece symbol characters and sections of non piece symbol characters, and transform the piece symbols into
|
|
640
|
-
* React objects represented with the given chess font.
|
|
641
|
-
*/
|
|
642
|
-
function figurineNotation(text, fontName) {
|
|
643
|
-
let result = [];
|
|
644
|
-
let beginOfText = 0;
|
|
645
|
-
let pieceSymbolIndex = 0;
|
|
646
|
-
for (let pos = 0; pos < text.length; ++pos) {
|
|
647
|
-
let currentChar = text.charAt(pos);
|
|
648
|
-
if (currentChar === 'K' || currentChar === 'Q' || currentChar === 'R' || currentChar === 'B' || currentChar === 'N' || currentChar === 'P') {
|
|
649
|
-
if (pos > beginOfText) {
|
|
650
|
-
result.push(text.substring(beginOfText, pos));
|
|
651
|
-
}
|
|
652
|
-
beginOfText = pos + 1;
|
|
653
|
-
let key = 'symbol-' + (pieceSymbolIndex++);
|
|
654
|
-
result.push(<span className={'kokopu-font-' + fontName} key={key}>{currentChar}</span>);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
if (beginOfText < text.length) {
|
|
658
|
-
result.push(text.substring(beginOfText));
|
|
659
|
-
}
|
|
660
|
-
return result;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
|
|
664
642
|
/**
|
|
665
643
|
* Try to interpret the given object as a chess game.
|
|
666
644
|
*/
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/******************************************************************************
|
|
2
|
+
* *
|
|
3
|
+
* This file is part of Kokopu-React, a JavaScript chess library. *
|
|
4
|
+
* Copyright (C) 2021-2022 Yoann Le Montagner <yo35 -at- melix.net> *
|
|
5
|
+
* *
|
|
6
|
+
* This program is free software: you can redistribute it and/or *
|
|
7
|
+
* modify it under the terms of the GNU Lesser General Public License *
|
|
8
|
+
* as published by the Free Software Foundation, either version 3 of *
|
|
9
|
+
* the License, or (at your option) any later version. *
|
|
10
|
+
* *
|
|
11
|
+
* This program is distributed in the hope that it will be useful, *
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
14
|
+
* GNU Lesser General Public License for more details. *
|
|
15
|
+
* *
|
|
16
|
+
* You should have received a copy of the GNU Lesser General *
|
|
17
|
+
* Public License along with this program. If not, see *
|
|
18
|
+
* <http://www.gnu.org/licenses/>. *
|
|
19
|
+
* *
|
|
20
|
+
******************************************************************************/
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
import React from 'react';
|
|
24
|
+
import i18n from './i18n';
|
|
25
|
+
|
|
26
|
+
import './css/fonts.css';
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Render the given SAN notation according to the given piece symbol style.
|
|
31
|
+
*
|
|
32
|
+
* @param {string|object} pieceSymbols See {@link moveFormatter}.
|
|
33
|
+
* @param {string} notation SAN notation.
|
|
34
|
+
* @returns {string|React.ReactFragment}
|
|
35
|
+
*/
|
|
36
|
+
export function formatMove(pieceSymbols, notation) {
|
|
37
|
+
let formatter = moveFormatter(pieceSymbols);
|
|
38
|
+
return formatter(notation);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Return a callback capable of rendering a SAN notation using the given piece symbol style.
|
|
44
|
+
*
|
|
45
|
+
* @param {string|object} pieceSymbols `'native'`, `'localized'`, `'figurines'`, or an object defining a string-valued properties
|
|
46
|
+
* for each English piece symbol.
|
|
47
|
+
* @returns {function(string)} Callback returning either a string or a React fragment.
|
|
48
|
+
*/
|
|
49
|
+
export function moveFormatter(pieceSymbols) {
|
|
50
|
+
if (pieceSymbols === 'localized') {
|
|
51
|
+
let mapping = i18n.PIECE_SYMBOLS;
|
|
52
|
+
return notation => notation.replace(/[KQRBNP]/g, match => mapping[match]);
|
|
53
|
+
}
|
|
54
|
+
else if (pieceSymbols === 'figurines') {
|
|
55
|
+
return notation => figurineNotation('alpha', notation);
|
|
56
|
+
}
|
|
57
|
+
else if (pieceSymbols !== 'native' && pieceSymbols && ['K', 'Q', 'R', 'B', 'N', 'P'].every(p => typeof pieceSymbols[p] === 'string')) {
|
|
58
|
+
return notation => notation.replace(/[KQRBNP]/g, match => pieceSymbols[match]);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return notation => notation;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Decompose the given string into piece symbol characters and sections of non piece symbol characters, and transform the piece symbols into
|
|
68
|
+
* React objects represented with the given chess font.
|
|
69
|
+
*
|
|
70
|
+
* @ignore
|
|
71
|
+
*/
|
|
72
|
+
function figurineNotation(fontName, text) {
|
|
73
|
+
let result = [];
|
|
74
|
+
let beginOfText = 0;
|
|
75
|
+
let pieceSymbolIndex = 0;
|
|
76
|
+
for (let pos = 0; pos < text.length; ++pos) {
|
|
77
|
+
let currentChar = text.charAt(pos);
|
|
78
|
+
if (currentChar === 'K' || currentChar === 'Q' || currentChar === 'R' || currentChar === 'B' || currentChar === 'N' || currentChar === 'P') {
|
|
79
|
+
if (pos > beginOfText) {
|
|
80
|
+
result.push(text.substring(beginOfText, pos));
|
|
81
|
+
}
|
|
82
|
+
beginOfText = pos + 1;
|
|
83
|
+
let key = 'symbol-' + (pieceSymbolIndex++);
|
|
84
|
+
result.push(<span className={'kokopu-font-' + fontName} key={key}>{currentChar}</span>);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (beginOfText < text.length) {
|
|
88
|
+
result.push(text.substring(beginOfText));
|
|
89
|
+
}
|
|
90
|
+
return <>{result}</>;
|
|
91
|
+
}
|
|
@@ -24,6 +24,12 @@ import React from 'react';
|
|
|
24
24
|
import * as htmlparser2 from 'htmlparser2';
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Those tags cannot have any children.
|
|
29
|
+
*/
|
|
30
|
+
const NO_CHILDREN_TAGS = new Set([ 'br', 'hr', 'img' ]);
|
|
31
|
+
|
|
32
|
+
|
|
27
33
|
/**
|
|
28
34
|
* HTML sanitizer: parse HTML string, keeping only the allowed HTML nodes, and the corresponding React object.
|
|
29
35
|
*/
|
|
@@ -74,14 +80,14 @@ export default class HtmlSanitizer {
|
|
|
74
80
|
return node.data;
|
|
75
81
|
}
|
|
76
82
|
else if (node.type === 'tag') {
|
|
77
|
-
let children = node.children.map(child => this._processNode(child));
|
|
83
|
+
let children = NO_CHILDREN_TAGS.has(node.name) ? undefined : node.children.map(child => this._processNode(child));
|
|
78
84
|
if (this._allowedTags.has(node.name)) {
|
|
79
85
|
let attributes = this._filterAttributes(node.name, node.attribs);
|
|
80
86
|
attributes['key'] = this._allocateKey();
|
|
81
87
|
return React.createElement(node.name, attributes, children);
|
|
82
88
|
}
|
|
83
89
|
else {
|
|
84
|
-
return <React.Fragment key={this._allocateKey()}>{children}</React.Fragment
|
|
90
|
+
return children ? <React.Fragment key={this._allocateKey()}>{children}</React.Fragment> : undefined;
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
else {
|