@lichess-org/pgn-viewer 0.0.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 (90) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +127 -0
  3. package/demo/demo.js +241 -0
  4. package/demo/frame.html +11 -0
  5. package/demo/full-screen.html +19 -0
  6. package/demo/index.html +30 -0
  7. package/demo/lichess-pgn-viewer.css +734 -0
  8. package/demo/lichess-pgn-viewer.demo.css +17 -0
  9. package/demo/lichess-pgn-viewer.js +5916 -0
  10. package/demo/one.html +25 -0
  11. package/demo/one.js +32 -0
  12. package/dist/config.d.ts +30 -0
  13. package/dist/config.js +54 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/events.d.ts +4 -0
  16. package/dist/events.js +42 -0
  17. package/dist/events.js.map +1 -0
  18. package/dist/game.d.ts +20 -0
  19. package/dist/game.js +45 -0
  20. package/dist/game.js.map +1 -0
  21. package/dist/interfaces.d.ts +95 -0
  22. package/dist/interfaces.js +2 -0
  23. package/dist/interfaces.js.map +1 -0
  24. package/dist/lichess-pgn-viewer.css +1 -0
  25. package/dist/lichess-pgn-viewer.min.js +4 -0
  26. package/dist/main.d.ts +3 -0
  27. package/dist/main.js +18 -0
  28. package/dist/main.js.map +1 -0
  29. package/dist/path.d.ts +16 -0
  30. package/dist/path.js +18 -0
  31. package/dist/path.js.map +1 -0
  32. package/dist/pgn.d.ts +4 -0
  33. package/dist/pgn.js +128 -0
  34. package/dist/pgn.js.map +1 -0
  35. package/dist/pgnViewer.d.ts +34 -0
  36. package/dist/pgnViewer.js +90 -0
  37. package/dist/pgnViewer.js.map +1 -0
  38. package/dist/translation.d.ts +2 -0
  39. package/dist/translation.js +14 -0
  40. package/dist/translation.js.map +1 -0
  41. package/dist/tsconfig.tsbuildinfo +1 -0
  42. package/dist/view/glyph.d.ts +1 -0
  43. package/dist/view/glyph.js +104 -0
  44. package/dist/view/glyph.js.map +1 -0
  45. package/dist/view/main.d.ts +5 -0
  46. package/dist/view/main.js +78 -0
  47. package/dist/view/main.js.map +1 -0
  48. package/dist/view/menu.d.ts +3 -0
  49. package/dist/view/menu.js +61 -0
  50. package/dist/view/menu.js.map +1 -0
  51. package/dist/view/player.d.ts +3 -0
  52. package/dist/view/player.js +32 -0
  53. package/dist/view/player.js.map +1 -0
  54. package/dist/view/side.d.ts +3 -0
  55. package/dist/view/side.js +102 -0
  56. package/dist/view/side.js.map +1 -0
  57. package/dist/view/util.d.ts +4 -0
  58. package/dist/view/util.js +23 -0
  59. package/dist/view/util.js.map +1 -0
  60. package/package.json +73 -0
  61. package/scss/_chessground.base.scss +164 -0
  62. package/scss/_chessground.cburnett.css +37 -0
  63. package/scss/_chessground.transp.css +57 -0
  64. package/scss/_controls.scss +30 -0
  65. package/scss/_fbt.scss +32 -0
  66. package/scss/_font-embed.scss +30 -0
  67. package/scss/_font.scss +33 -0
  68. package/scss/_layout.scss +147 -0
  69. package/scss/_lichess-pgn-viewer.lib.scss +78 -0
  70. package/scss/_pane.scss +31 -0
  71. package/scss/_player.scss +39 -0
  72. package/scss/_scrollbar.scss +16 -0
  73. package/scss/_side.scss +155 -0
  74. package/scss/_util.scss +7 -0
  75. package/scss/lichess-pgn-viewer.scss +4 -0
  76. package/src/config.ts +53 -0
  77. package/src/events.ts +42 -0
  78. package/src/game.ts +61 -0
  79. package/src/interfaces.ts +108 -0
  80. package/src/main.ts +24 -0
  81. package/src/path.ts +28 -0
  82. package/src/pgn.ts +141 -0
  83. package/src/pgnViewer.ts +114 -0
  84. package/src/translation.ts +17 -0
  85. package/src/view/glyph.ts +113 -0
  86. package/src/view/main.ts +99 -0
  87. package/src/view/menu.ts +92 -0
  88. package/src/view/player.ts +41 -0
  89. package/src/view/side.ts +123 -0
  90. package/src/view/util.ts +40 -0
@@ -0,0 +1,78 @@
1
+ // in addition to scss overrides, all colors below may be overridden with css
2
+ // variables defined as --c-lpv-accent, --c-lpv-accent-over, etc.
3
+
4
+ $lpv-site-hue: 37 !default;
5
+ $lpv-accent: hsl(88, 62%, 37%) !default;
6
+ $lpv-accent-over: white !default;
7
+ $lpv-bg: hsl($lpv-site-hue, 5%, 18%) !default;
8
+ $lpv-fbt-hover: mix($lpv-accent, $lpv-bg, 75%) !default;
9
+ $lpv-bg-player: $lpv-bg !default;
10
+ $lpv-bg-controls: $lpv-bg !default;
11
+ $lpv-bg-movelist: $lpv-bg !default;
12
+ $lpv-bg-variation: hsl($lpv-site-hue, 5%, 15%) !default;
13
+ $lpv-bg-pane: mix($lpv-accent, $lpv-bg, 15%) !default;
14
+ $lpv-pgn-text: mix($lpv-bg-pane, $lpv-bg, 30%) !default;
15
+ $lpv-font: #aaa !default;
16
+ $lpv-font-shy: mix($lpv-font, $lpv-bg, 50%) !default;
17
+ $lpv-past-moves: $lpv-font !default;
18
+ $lpv-font-bg: mix($lpv-font, $lpv-bg, 20%) !default;
19
+ $lpv-current-move: mix($lpv-accent, $lpv-bg, 70%) !default;
20
+ $lpv-move-hover: mix($lpv-accent, $lpv-bg, 30%) !default;
21
+ $lpv-border: hsl(0, 0%, 25%) !default;
22
+ $lpv-side-border: hsl(37deg, 5%, 13%) !default;
23
+
24
+ /* move highlights */
25
+ $lpv-inaccuracy: hsl(202, 78%, 62%) !default;
26
+ $lpv-mistake: hsl(41, 100%, 45%) !default;
27
+ $lpv-blunder: hsl(0, 69%, 60%) !default;
28
+ $lpv-good-move: hsl(130, 67%, 62%) !default;
29
+ $lpv-brilliant: hsl(129, 71%, 45%) !default;
30
+ $lpv-interesting: hsl(307, 80%, 70%) !default;
31
+ $lpv-bg-inaccuracy-hover: mix($lpv-inaccuracy, $lpv-bg, 30%) !default;
32
+ $lpv-bg-mistake-hover: mix($lpv-mistake, $lpv-bg, 30%) !default;
33
+ $lpv-bg-blunder-hover: mix($lpv-blunder, $lpv-bg, 30%) !default;
34
+ $lpv-bg-good-hover: mix($lpv-good-move, $lpv-bg, 30%) !default;
35
+ $lpv-bg-brilliant-hover: mix($lpv-brilliant, $lpv-bg, 30%) !default;
36
+ $lpv-bg-interesting-hover: mix($lpv-interesting, $lpv-bg, 30%) !default;
37
+
38
+ @import './fbt';
39
+ @import './util';
40
+ @import './layout';
41
+ @import './side';
42
+ @import './player';
43
+ @import './pane';
44
+ @import './controls';
45
+ @import './scrollbar';
46
+ @import './font';
47
+
48
+ .lpv {
49
+ border-radius: 5px;
50
+ box-shadow:
51
+ 0 2px 2px 0 rgba(0, 0, 0, 0.14),
52
+ 0 3px 1px -2px rgba(0, 0, 0, 0.2),
53
+ 0 1px 5px 0 rgba(0, 0, 0, 0.12);
54
+ overflow: hidden;
55
+ background: var(--c-lpv-bg, $lpv-bg);
56
+ color: var(--c-lpv-font, $lpv-font);
57
+ box-sizing: border-box;
58
+
59
+ *,
60
+ *::before,
61
+ *::after {
62
+ box-sizing: inherit;
63
+ }
64
+
65
+ &__board {
66
+ user-select: none;
67
+ .cg-wrap {
68
+ @extend %square;
69
+ }
70
+ cg-board {
71
+ box-shadow: none;
72
+ }
73
+ }
74
+
75
+ &:focus {
76
+ outline: auto 2px var(--c-lpv-accent, $lpv-accent);
77
+ }
78
+ }
@@ -0,0 +1,31 @@
1
+ .lpv {
2
+ &__pane {
3
+ z-index: 2;
4
+ border-bottom: 2px solid var(--c-lpv-accent, $lpv-accent);
5
+ background: var(--c-lpv-bg-pane, $lpv-bg-pane);
6
+ display: flex;
7
+ flex-flow: column;
8
+ justify-content: center;
9
+ .lpv__fbt {
10
+ text-align: left;
11
+ padding: 0.8em 2.5em;
12
+ transition: none;
13
+ &::before {
14
+ color: var(--c-lpv-accent, $lpv-accent);
15
+ font-size: 2em;
16
+ }
17
+ &:hover::before {
18
+ color: var(--c-lpv-accent-over, $lpv-accent-over);
19
+ }
20
+ }
21
+ }
22
+
23
+ &__pgn {
24
+ &__text {
25
+ flex: 1 1 auto;
26
+ background: var(--c-lpv-pgn-text, $lpv-pgn-text);
27
+ color: var(--c-lpv-font, $lpv-font);
28
+ padding: 0.8em 1.3em;
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,39 @@
1
+ .lpv {
2
+ &__player {
3
+ font-size: 0.8em;
4
+ background: var(--c-lpv-bg-player, $lpv-bg-player);
5
+ display: flex;
6
+ flex-flow: row nowrap;
7
+ padding: 0 1em;
8
+ // justify-content: stretch;
9
+ &--bottom {
10
+ border-bottom: 1px solid var(--c-lpv-border, $lpv-border);
11
+ .lpv--controls-false & {
12
+ border-bottom: none;
13
+ }
14
+ }
15
+ &__person {
16
+ flex: 1 1 auto;
17
+ display: flex;
18
+ flex-flow: row nowrap;
19
+ align-items: center;
20
+ gap: 1ch;
21
+ color: var(--c-lpv-font, $lpv-font);
22
+ text-decoration: none;
23
+ }
24
+ &__title {
25
+ font-weight: bold;
26
+ }
27
+ &__clock {
28
+ display: flex;
29
+ flex-flow: row nowrap;
30
+ align-items: center;
31
+ font-family: monospace;
32
+ font-size: 1.4em;
33
+ font-weight: bold;
34
+ &.active {
35
+ color: var(--c-lpv-accent, $lpv-accent);
36
+ }
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,16 @@
1
+ .lpv {
2
+ *::-webkit-scrollbar,
3
+ *::-webkit-scrollbar-corner {
4
+ width: 0.5rem;
5
+ background: var(--c-lpv-bg, $lpv-bg);
6
+ }
7
+
8
+ *::-webkit-scrollbar-thumb {
9
+ background: var(--c-lpv-font-bg, $lpv-font-bg);
10
+ }
11
+
12
+ *::-webkit-scrollbar-thumb:hover,
13
+ *::-webkit-scrollbar-thumb:active {
14
+ background: var(--c-lpv-font-shy, $lpv-font-shy);
15
+ }
16
+ }
@@ -0,0 +1,155 @@
1
+ .lpv {
2
+ &__side {
3
+ overflow: hidden;
4
+ display: flex;
5
+ flex-flow: column;
6
+ }
7
+ &__moves {
8
+ position: relative; // required for autoscroll
9
+ flex: 1 1 0;
10
+ display: flex;
11
+ flex-flow: row wrap;
12
+ overflow-y: auto;
13
+ background: var(--c-lpv-bg-movelist, $lpv-bg-movelist);
14
+ align-items: center;
15
+ align-content: flex-start;
16
+ will-change: scroll-position;
17
+ user-select: none;
18
+ line-height: 1.7;
19
+ min-width: 20ch;
20
+ index {
21
+ color: var(--c-lpv-font-shy, $lpv-font-shy);
22
+ }
23
+ > index {
24
+ flex: 0 0 15%;
25
+ margin-right: 3%;
26
+ display: flex;
27
+ justify-content: flex-end;
28
+ }
29
+ move {
30
+ border-radius: 3px;
31
+ padding-left: 3%;
32
+ font-weight: bold;
33
+ white-space: nowrap;
34
+ &.empty {
35
+ color: var(--c-lpv-font-shy, $lpv-font-shy);
36
+ }
37
+ &:not(.empty):hover {
38
+ background: var(--c-lpv-move-hover, $lpv-move-hover);
39
+ color: var(--c-lpv-accent-over, $lpv-accent-over);
40
+ cursor: pointer;
41
+ }
42
+ &.ancestor {
43
+ color: var(--c-lpv-past-moves, $lpv-past-moves);
44
+ }
45
+ &.current {
46
+ background: var(--c-lpv-current-move, $lpv-current-move) !important;
47
+ color: var(--c-lpv-accent-over, $lpv-accent-over);
48
+ }
49
+ &.inaccuracy {
50
+ color: var(--c-lpv-inaccuracy, $lpv-inaccuracy);
51
+ &:hover {
52
+ background: var(--c-lpv-bg-inaccuracy-hover, $lpv-bg-inaccuracy-hover);
53
+ }
54
+ }
55
+ &.mistake {
56
+ color: var(--c-lpv-mistake, $lpv-mistake);
57
+ &:hover {
58
+ background: var(--c-lpv-bg-mistake-hover, $lpv-bg-mistake-hover);
59
+ }
60
+ }
61
+ &.blunder {
62
+ color: var(--c-lpv-blunder, $lpv-blunder);
63
+ &:hover {
64
+ background: var(--c-lpv-bg-blunder-hover, $lpv-bg-blunder-hover);
65
+ }
66
+ }
67
+ &.good {
68
+ color: var(--c-lpv-good-move, $lpv-good-move);
69
+ &:hover {
70
+ background: var(--c-lpv-bg-good-hover, $lpv-bg-good-hover);
71
+ }
72
+ }
73
+ &.brilliant {
74
+ color: var(--c-lpv-brilliant, $lpv-brilliant);
75
+ &:hover {
76
+ background: var(--c-lpv-bg-brilliant-hover, $lpv-bg-brilliant-hover);
77
+ }
78
+ }
79
+ &.interesting {
80
+ color: var(--c-lpv-interesting, $lpv-interesting);
81
+ &:hover {
82
+ background: var(--c-lpv-bg-interesting-hover, $lpv-bg-interesting-hover);
83
+ }
84
+ }
85
+ }
86
+ > move {
87
+ flex: 0 0 41%;
88
+ font-size: 1.1em;
89
+ }
90
+ comment {
91
+ user-select: text;
92
+ font-size: 0.9em;
93
+ }
94
+ comment.result {
95
+ text-align: center;
96
+ font-weight: bold;
97
+ }
98
+ > comment {
99
+ flex: 1 1 100%;
100
+ background: var(--c-lpv-bg-variation, $lpv-bg-variation);
101
+ border: 1px solid var(--c-lpv-side-border, $lpv-side-border);
102
+ border-width: 1px 0;
103
+ padding: 0.4em 1em;
104
+ line-height: 1.4;
105
+ overflow-wrap: break-word;
106
+ word-break: break-word;
107
+ + variation,
108
+ + comment {
109
+ border-top: none;
110
+ }
111
+ }
112
+ > variation {
113
+ flex: 1 1 100%;
114
+ display: block;
115
+ overflow: hidden;
116
+ font-size: 0.8em;
117
+ background: var(--c-lpv-bg-variation, $lpv-bg-variation);
118
+ border: 1px solid var(--c-lpv-side-border, $lpv-side-border);
119
+ border-width: 1px 0;
120
+ padding: 0em 0.6em;
121
+ + variation {
122
+ border-top: none;
123
+ }
124
+ move {
125
+ display: inline-block;
126
+ padding: 0.1em 0.2em;
127
+ min-width: 2.5ch;
128
+ text-align: center;
129
+ + index {
130
+ margin-left: 0.2em;
131
+ }
132
+ }
133
+ index {
134
+ margin: 0;
135
+ padding: 0.1em 0;
136
+ + move {
137
+ margin-left: 0.1em;
138
+ }
139
+ }
140
+ comment {
141
+ align-self: center;
142
+ margin: 0 0.3em;
143
+ }
144
+ paren {
145
+ color: var(--c-lpv-font-shy, $lpv-font-shy);
146
+ &.open {
147
+ margin: 0 0.1em 0 0.2em;
148
+ }
149
+ &.close {
150
+ margin: 0 0.2em 0 0.1em;
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,7 @@
1
+ %square {
2
+ position: relative;
3
+ display: block;
4
+ height: 0;
5
+ padding-bottom: 100%;
6
+ width: 100%;
7
+ }
@@ -0,0 +1,4 @@
1
+ @import './chessground.base';
2
+ @import './chessground.transp';
3
+ @import './chessground.cburnett';
4
+ @import 'lichess-pgn-viewer.lib';
package/src/config.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { Opts } from './interfaces';
2
+
3
+ const defaults: Opts = {
4
+ pgn: '*', // the PGN to render
5
+ fen: undefined, // initial FEN, will append [FEN "initial FEN"] to the PGN
6
+ showPlayers: 'auto', // show the players above and under the board
7
+ showClocks: true, // show the clocks alongside the players
8
+ showMoves: 'auto', // false | "right" | "bottom" | auto. "auto" uses media queries
9
+ showControls: true, // show the [prev, menu, next] buttons
10
+ scrollToMove: true, // enable scrolling through moves with a mouse wheel
11
+ keyboardToMove: true, // enable keyboard navigation through moves
12
+ orientation: undefined, // orientation of the board. Undefined to use the Orientation PGN tag.
13
+ initialPly: 0, // current position to display. Can be a number, or "last"
14
+ chessground: {}, // chessground configuration https://github.com/lichess-org/chessground/blob/master/src/config.ts#L7
15
+ drawArrows: true, // allow mouse users to draw volatile arrows on the board. Disable for little perf boost
16
+ menu: {
17
+ getPgn: {
18
+ enabled: true, // enable the "Get PGN" menu entry
19
+ fileName: undefined, // name of the file when user clicks "Download PGN". Leave empty for automatic name.
20
+ },
21
+ practiceWithComputer: {
22
+ enabled: true,
23
+ },
24
+ analysisBoard: {
25
+ enabled: true,
26
+ },
27
+ },
28
+ lichess: 'https://lichess.org', // support for Lichess games, with links to the game and players. Set to false to disable.
29
+ classes: undefined, // CSS classes to set on the root element. Defaults to the element classes before being replaced by LPV.
30
+ };
31
+
32
+ export default function (element: HTMLElement, cfg: Partial<Opts>) {
33
+ const opts = { ...defaults };
34
+ deepMerge(opts, cfg);
35
+ if (opts.fen) opts.pgn = `[FEN "${opts.fen}"]\n${opts.pgn}`;
36
+ if (!opts.classes) opts.classes = element.className;
37
+ return opts;
38
+ }
39
+
40
+ function deepMerge(base: any, extend: any): void {
41
+ for (const key in extend) {
42
+ if (typeof extend[key] !== 'undefined') {
43
+ if (isPlainObject(base[key]) && isPlainObject(extend[key])) deepMerge(base[key], extend[key]);
44
+ else base[key] = extend[key];
45
+ }
46
+ }
47
+ }
48
+
49
+ function isPlainObject(o: unknown): boolean {
50
+ if (typeof o !== 'object' || o === null) return false;
51
+ const proto = Object.getPrototypeOf(o);
52
+ return proto === Object.prototype || proto === null;
53
+ }
package/src/events.ts ADDED
@@ -0,0 +1,42 @@
1
+ import PgnViewer from './pgnViewer';
2
+
3
+ export function stepwiseScroll(inner: (e: WheelEvent, scroll: boolean) => void): (e: WheelEvent) => void {
4
+ let scrollTotal = 0;
5
+ return (e: WheelEvent) => {
6
+ scrollTotal += e.deltaY * (e.deltaMode ? 40 : 1);
7
+ if (Math.abs(scrollTotal) >= 4) {
8
+ inner(e, true);
9
+ scrollTotal = 0;
10
+ } else {
11
+ inner(e, false);
12
+ }
13
+ };
14
+ }
15
+
16
+ export function eventRepeater(action: () => void, e: Event) {
17
+ const repeat = () => {
18
+ action();
19
+ delay = Math.max(100, delay - delay / 15);
20
+ timeout = setTimeout(repeat, delay);
21
+ };
22
+ let delay = 350;
23
+ let timeout = setTimeout(repeat, 500);
24
+ action();
25
+ const eventName = e.type == 'touchstart' ? 'touchend' : 'mouseup';
26
+ document.addEventListener(eventName, () => clearTimeout(timeout), { once: true });
27
+ }
28
+
29
+ const suppressKeyNavOn = (e: KeyboardEvent): boolean =>
30
+ e.altKey ||
31
+ e.ctrlKey ||
32
+ e.shiftKey ||
33
+ e.metaKey ||
34
+ document.activeElement instanceof HTMLInputElement ||
35
+ document.activeElement instanceof HTMLTextAreaElement;
36
+
37
+ export const onKeyDown = (ctrl: PgnViewer) => (e: KeyboardEvent) => {
38
+ if (suppressKeyNavOn(e)) return;
39
+ else if (e.key == 'ArrowLeft') ctrl.goTo('prev');
40
+ else if (e.key == 'ArrowRight') ctrl.goTo('next');
41
+ else if (e.key == 'f') ctrl.flip();
42
+ };
package/src/game.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { Node, ChildNode } from 'chessops/pgn';
2
+ import { Id, Initial, InitialOrMove, Metadata, MoveData, Players, Ply } from './interfaces';
3
+ import { Path } from './path';
4
+
5
+ export type AnyNode = Node<MoveData>;
6
+ export type MoveNode = ChildNode<MoveData>;
7
+
8
+ // immutable
9
+ export class Game {
10
+ mainline: MoveData[];
11
+
12
+ constructor(
13
+ readonly initial: Initial,
14
+ readonly moves: AnyNode,
15
+ readonly players: Players,
16
+ readonly metadata: Metadata,
17
+ ) {
18
+ this.mainline = Array.from(this.moves.mainline());
19
+ }
20
+
21
+ nodeAt = (path: Path): AnyNode | undefined => nodeAtPathFrom(this.moves, path);
22
+
23
+ dataAt = (path: Path): MoveData | Initial | undefined => {
24
+ const node = this.nodeAt(path);
25
+ return node ? (isMoveNode(node) ? node.data : this.initial) : undefined;
26
+ };
27
+
28
+ title = () =>
29
+ this.players.white.name
30
+ ? [
31
+ this.players.white.title,
32
+ this.players.white.name,
33
+ 'vs',
34
+ this.players.black.title,
35
+ this.players.black.name,
36
+ ]
37
+ .filter(x => x && !!x.trim())
38
+ .join('_')
39
+ .replace(' ', '-')
40
+ : 'lichess-pgn-viewer';
41
+
42
+ pathAtMainlinePly = (ply: Ply | 'last') =>
43
+ ply == 0
44
+ ? Path.root
45
+ : this.mainline[Math.max(0, Math.min(this.mainline.length - 1, ply == 'last' ? 9999 : ply - 1))]
46
+ ?.path || Path.root;
47
+
48
+ hasPlayerName = () => !!(this.players.white?.name || this.players.black?.name);
49
+ }
50
+
51
+ const childById = (node: AnyNode, id: Id): MoveNode | undefined =>
52
+ node.children.find(c => c.data.path.last() == id);
53
+
54
+ const nodeAtPathFrom = (node: AnyNode, path: Path): AnyNode | undefined => {
55
+ if (path.empty()) return node;
56
+ const child = childById(node, path.head());
57
+ return child ? nodeAtPathFrom(child, path.tail()) : undefined;
58
+ };
59
+
60
+ export const isMoveNode = (n: AnyNode): n is MoveNode => 'data' in n;
61
+ export const isMoveData = (d: InitialOrMove): d is MoveData => 'uci' in d;
@@ -0,0 +1,108 @@
1
+ import { Color, Move, Position } from 'chessops';
2
+ import { Config as CgConfig } from 'chessground/config';
3
+ import { FEN } from 'chessground/types';
4
+ import { Path } from './path';
5
+ import { CommentShape } from 'chessops/pgn';
6
+
7
+ export type Id = string;
8
+ export type San = string;
9
+ export type Uci = string;
10
+ export type Ply = number;
11
+
12
+ export type Translate = (key: string) => string | undefined;
13
+
14
+ export type Clocks = {
15
+ white?: number;
16
+ black?: number;
17
+ };
18
+
19
+ export interface InitialOrMove {
20
+ fen: FEN;
21
+ turn: Color;
22
+ check: boolean;
23
+ comments: string[];
24
+ shapes: CommentShape[];
25
+ clocks: Clocks;
26
+ }
27
+
28
+ export interface Initial extends InitialOrMove {
29
+ pos: Position;
30
+ }
31
+
32
+ export interface MoveData extends InitialOrMove {
33
+ path: Path;
34
+ ply: number;
35
+ move: Move;
36
+ san: San;
37
+ uci: Uci;
38
+ startingComments: string[];
39
+ nags: number[];
40
+ emt?: number;
41
+ }
42
+
43
+ export interface Metadata {
44
+ externalLink?: string;
45
+ isLichess: boolean;
46
+ timeControl?: {
47
+ initial: number;
48
+ increment: number;
49
+ };
50
+ orientation?: Color;
51
+ result?: string;
52
+ }
53
+
54
+ export interface Player {
55
+ name?: string;
56
+ title?: string;
57
+ rating?: number;
58
+ isLichessUser: boolean;
59
+ }
60
+ export interface Players {
61
+ white: Player;
62
+ black: Player;
63
+ }
64
+
65
+ export type Pane = 'board' | 'menu' | 'pgn';
66
+
67
+ export interface Comments {
68
+ texts: string[];
69
+ shapes: CommentShape[];
70
+ clock?: number;
71
+ emt?: number;
72
+ }
73
+
74
+ export type GoTo = 'first' | 'prev' | 'next' | 'last';
75
+
76
+ export type ShowMoves = false | 'right' | 'bottom' | 'auto';
77
+ export type ShowPlayers = true | false | 'auto';
78
+ export type Lichess = string | false;
79
+
80
+ export interface Opts {
81
+ pgn: string;
82
+ fen?: string;
83
+ chessground: CgConfig;
84
+ orientation?: Color;
85
+ showPlayers: ShowPlayers;
86
+ showMoves: ShowMoves;
87
+ showClocks: boolean;
88
+ showControls: boolean;
89
+ initialPly: Ply | 'last';
90
+ scrollToMove: boolean;
91
+ keyboardToMove: boolean;
92
+ drawArrows: boolean;
93
+ menu: {
94
+ getPgn: {
95
+ enabled?: boolean;
96
+ fileName?: string;
97
+ };
98
+ practiceWithComputer?: {
99
+ enabled?: boolean;
100
+ };
101
+ analysisBoard?: {
102
+ enabled?: boolean;
103
+ };
104
+ };
105
+ lichess: Lichess;
106
+ classes?: string;
107
+ translate?: Translate;
108
+ }
package/src/main.ts ADDED
@@ -0,0 +1,24 @@
1
+ import PgnViewer from './pgnViewer';
2
+ import view from './view/main';
3
+ import { init, attributesModule, classModule } from 'snabbdom';
4
+ import { Opts } from './interfaces';
5
+ import config from './config';
6
+
7
+ export default function start(element: HTMLElement, cfg: Partial<Opts>): PgnViewer {
8
+ const patch = init([classModule, attributesModule]);
9
+
10
+ const opts = config(element, cfg);
11
+
12
+ const ctrl = new PgnViewer(opts, redraw);
13
+
14
+ const blueprint = view(ctrl);
15
+ element.innerHTML = '';
16
+ let vnode = patch(element, blueprint);
17
+ ctrl.div = vnode.elm as HTMLElement;
18
+
19
+ function redraw() {
20
+ vnode = patch(vnode, view(ctrl));
21
+ }
22
+
23
+ return ctrl;
24
+ }
package/src/path.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { Id } from './interfaces';
2
+
3
+ export class Path {
4
+ constructor(readonly path: string) {}
5
+
6
+ size = () => this.path.length / 2;
7
+
8
+ head = (): Id => this.path.slice(0, 2);
9
+
10
+ // returns an invalid path doesn't starting from root
11
+ tail = (): Path => new Path(this.path.slice(2));
12
+
13
+ init = (): Path => new Path(this.path.slice(0, -2));
14
+
15
+ last = (): Id => this.path.slice(-2);
16
+
17
+ empty = () => this.path == '';
18
+
19
+ contains = (other: Path): boolean => this.path.startsWith(other.path);
20
+
21
+ isChildOf = (parent: Path): boolean => this.init() === parent;
22
+
23
+ append = (id: Id) => new Path(this.path + id);
24
+
25
+ equals = (other: Path) => this.path == other.path;
26
+
27
+ static root = new Path('');
28
+ }