ssb-chess-mithril 2.1.0 → 2.1.2

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 (35) hide show
  1. package/css/chessground-examples/README.md +14 -4
  2. package/css/chessground-examples/assets/3d.css +24 -23
  3. package/css/chessground-examples/assets/chessground.css +73 -15
  4. package/css/chessground-examples/assets/examples.css +1 -1
  5. package/css/chessground-examples/assets/theme.css +37 -26
  6. package/css/chessground-examples/index.html +5 -5
  7. package/css/chessground-examples/package-lock.json +1747 -8086
  8. package/css/chessground-examples/package.json +32 -17
  9. package/css/chessground-examples/pnpm-lock.yaml +874 -0
  10. package/css/chessground-examples/rollup.config.mjs +20 -0
  11. package/css/chessground-examples/src/main.ts +81 -48
  12. package/css/chessground-examples/src/units/anim.ts +63 -42
  13. package/css/chessground-examples/src/units/basics.ts +73 -13
  14. package/css/chessground-examples/src/units/fen.ts +21 -18
  15. package/css/chessground-examples/src/units/in3d.ts +26 -26
  16. package/css/chessground-examples/src/units/perf.ts +29 -15
  17. package/css/chessground-examples/src/units/play.ts +71 -52
  18. package/css/chessground-examples/src/units/svg.ts +230 -102
  19. package/css/chessground-examples/src/units/unit.ts +47 -18
  20. package/css/chessground-examples/src/units/viewOnly.ts +11 -11
  21. package/css/chessground-examples/src/units/zh.ts +25 -23
  22. package/css/chessground-examples/src/util.ts +30 -18
  23. package/css/chessground-examples/tsconfig.json +6 -4
  24. package/css/miniboards.css +1 -0
  25. package/index.js +87 -78
  26. package/package.json +4 -4
  27. package/ui/game/PieceGraveyard.js +1 -1
  28. package/ui/game/chat.js +3 -3
  29. package/ui/game/gameHistory.js +5 -1
  30. package/ui/game/gameView.js +30 -11
  31. package/ui/miniboard/miniboard.js +8 -5
  32. package/ui/pageLayout/navigation.js +11 -7
  33. package/css/chessground-examples/gulpfile.js +0 -52
  34. package/css/chessground-examples/index.standalone.html +0 -17
  35. package/css/chessground-examples/yarn.lock +0 -3529
@@ -1,46 +1,58 @@
1
- import { Api } from 'chessground/api';
1
+ import { Api } from "chessground/api";
2
+ import { Color, Key } from "chessground/types";
3
+ import { ChessInstance, SQUARES } from "chess.js";
2
4
 
3
- export function toDests(chess: any) {
4
- const dests = {};
5
- chess.SQUARES.forEach(s => {
6
- const ms = chess.moves({square: s, verbose: true});
7
- if (ms.length) dests[s] = ms.map(m => m.to);
5
+ export function toDests(chess: ChessInstance): Map<Key, Key[]> {
6
+ const dests = new Map();
7
+ SQUARES.forEach((s) => {
8
+ const ms = chess.moves({ square: s, verbose: true });
9
+ if (ms.length)
10
+ dests.set(
11
+ s,
12
+ ms.map((m) => m.to),
13
+ );
8
14
  });
9
15
  return dests;
10
16
  }
11
17
 
12
- export function toColor(chess: any) {
13
- return (chess.turn() === 'w') ? 'white' : 'black';
14
-
18
+ export function toColor(chess: ChessInstance): Color {
19
+ return chess.turn() === "w" ? "white" : "black";
15
20
  }
16
21
 
17
22
  export function playOtherSide(cg: Api, chess) {
18
23
  return (orig, dest) => {
19
- chess.move({from: orig, to: dest});
24
+ chess.move({ from: orig, to: dest });
20
25
  cg.set({
21
26
  turnColor: toColor(chess),
22
27
  movable: {
23
28
  color: toColor(chess),
24
- dests: toDests(chess)
25
- }
29
+ dests: toDests(chess),
30
+ },
26
31
  });
27
32
  };
28
33
  }
29
34
 
30
- export function aiPlay(cg: Api, chess, delay: number, firstMove: boolean) {
35
+ export function aiPlay(
36
+ cg: Api,
37
+ chess: ChessInstance,
38
+ delay: number,
39
+ firstMove: boolean,
40
+ ) {
31
41
  return (orig, dest) => {
32
- chess.move({from: orig, to: dest});
42
+ chess.move({ from: orig, to: dest });
33
43
  setTimeout(() => {
34
- const moves = chess.moves({verbose:true});
35
- const move = firstMove ? moves[0] : moves[Math.floor(Math.random() * moves.length)];
44
+ const moves = chess.moves({ verbose: true });
45
+ const move = firstMove
46
+ ? moves[0]
47
+ : moves[Math.floor(Math.random() * moves.length)];
36
48
  chess.move(move.san);
37
49
  cg.move(move.from, move.to);
38
50
  cg.set({
39
51
  turnColor: toColor(chess),
40
52
  movable: {
41
53
  color: toColor(chess),
42
- dests: toDests(chess)
43
- }
54
+ dests: toDests(chess),
55
+ },
44
56
  });
45
57
  cg.playPremove();
46
58
  }, delay);
@@ -1,9 +1,8 @@
1
1
  {
2
- "include": [
3
- "src/*.ts"
4
- ],
2
+ "include": ["src/*.ts"],
5
3
  "compilerOptions": {
6
4
  "outDir": "./dist",
5
+ "rootDir": "./src",
7
6
  "noImplicitAny": false,
8
7
  "strictNullChecks": true,
9
8
  "noUnusedLocals": true,
@@ -12,6 +11,9 @@
12
11
  "noImplicitReturns": true,
13
12
  "noImplicitThis": true,
14
13
  "noUnusedParameters": true,
15
- "target": "es6"
14
+ "moduleResolution": "node",
15
+ "target": "ES2018",
16
+ "module": "esnext",
17
+ "lib": ["ES2018", "DOM"]
16
18
  }
17
19
  }
@@ -33,6 +33,7 @@
33
33
  .ssb-chess-miniboard-controls {
34
34
  display: flex;
35
35
  justify-content: center;
36
+ position: relative;
36
37
  }
37
38
 
38
39
  .ssb-chess-miniboard-control {
package/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const m = require('mithril');
2
- const onceTrue = require('mutant/once-true');
3
-
4
2
  const MainCtrl = require('ssb-chess');
3
+ const util = require('util');
5
4
 
6
5
  const MiniboardListComponent = require('./ui/miniboard/miniboard_list');
7
6
  const NavigationBar = require('./ui/pageLayout/navigation');
@@ -15,66 +14,71 @@ const Notifier = require('./ui/notify/notifier');
15
14
  module.exports = (attachToElement, dataAccess, opts = {}) => {
16
15
  const { initialView } = opts;
17
16
 
18
- const cssFiles = [
19
- './css/global.css',
20
- './css/chessground-examples/assets/chessground.css',
21
- './css/chessground-examples/assets/theme.css',
22
- './css/board-theme.css',
23
- './css/miniboards.css',
24
- './css/largeBoard.css',
25
- './css/invites.css',
26
- './css/loading.css',
27
- './css/promote.css',
28
- './css/game.css',
29
- './css/historyArea.css',
30
- './css/playerProfiles.css',
31
- './css/actionButtons.css',
32
- './css/activity.css',
33
- './css/pgnExport.css',
34
- './css/nextPrevious.css'
35
- ];
36
-
37
- // h4cky0 strikes again? mebbe there's a better way? ;x
38
- function cssFilesToStyleTag(dom) {
17
+ // function cssFileElements() {
18
+ // const styles = m('div', {}, cssPaths().map(file => m('link', { rel: 'stylesheet', href: file })));
19
+ // return styles
20
+ // }
21
+
22
+ function cssPaths() {
23
+ const cssFiles = [
24
+ './css/global.css',
25
+ './css/chessground-examples/assets/chessground.css',
26
+ './css/chessground-examples/assets/theme.css',
27
+ './css/board-theme.css',
28
+ './css/miniboards.css',
29
+ './css/largeBoard.css',
30
+ './css/invites.css',
31
+ './css/loading.css',
32
+ './css/promote.css',
33
+ './css/game.css',
34
+ './css/historyArea.css',
35
+ './css/playerProfiles.css',
36
+ './css/actionButtons.css',
37
+ './css/activity.css',
38
+ './css/pgnExport.css',
39
+ './css/nextPrevious.css'
40
+ ];
39
41
 
40
42
  const rootDir = `${__dirname}/`;
41
-
42
- const styles = m('div', {}, cssFiles.map(file => m('link', { rel: 'stylesheet', href: rootDir + file })));
43
-
44
- m.render(dom, styles);
43
+ return cssFiles.map(file => rootDir + file)
45
44
  }
46
45
 
47
- function renderPageTop(parent, mainCtrl, settingsCtrl) {
48
- const navBar = NavigationBar(mainCtrl, settingsCtrl);
46
+ function appendCss() {
49
47
 
50
- const TopComponent = {
51
- view: () => m('div', [
52
- m(navBar),
53
- ]),
54
- };
48
+ cssPaths().forEach((filepath) => {
49
+ const link = document.createElement('link');
55
50
 
56
- m.mount(parent, TopComponent);
51
+ link.setAttribute('rel', 'stylesheet');
52
+ link.setAttribute('href', filepath);
53
+ document.head.appendChild(link);
54
+ })
57
55
  }
58
56
 
59
- function appRouter(mainBody, mainCtrl, settingsCtrl) {
57
+ async function appRouter(mountElement, mainCtrl, settingsCtrl) {
58
+ appendCss();
59
+
60
60
  const gamesInProgressObs = mainCtrl.getGameCtrl().getMyGamesInProgress();
61
61
  const gamesMyMoveObs = mainCtrl.getGameCtrl().getGamesWhereMyMove();
62
62
  const observableGamesObs = mainCtrl.getGameCtrl().getFriendsObservableGames();
63
63
  const userRecentActivity = mainCtrl.getRecentActivityCtrl().getRecentActivityForUserGames();
64
64
 
65
65
  // Hack: keep observables loaded with the latest value.
66
- gamesInProgressObs(e => e);
67
- gamesMyMoveObs(e => e);
68
- observableGamesObs(e => e);
69
- userRecentActivity(e => e);
66
+ const watches = [
67
+ gamesInProgressObs(e => e),
68
+ gamesMyMoveObs(e => e),
69
+ observableGamesObs(e => e),
70
+ userRecentActivity(e => e)
71
+ ];
70
72
 
71
73
  const defaultView = initialView || '/my_games';
72
74
 
73
- m.route.prefix('?');
75
+ m.route.prefix = '?';
76
+
77
+ const navigationBar = NavigationBar(mainCtrl, settingsCtrl)
74
78
 
75
- m.route(mainBody, defaultView, {
76
- '/my_games': MiniboardListComponent(mainCtrl, gamesInProgressObs, mainCtrl.getMyIdent()),
77
- '/games_my_move': MiniboardListComponent(mainCtrl, gamesMyMoveObs, mainCtrl.getMyIdent()),
79
+ m.route(mountElement, defaultView, {
80
+ '/my_games': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, gamesInProgressObs, mainCtrl.getMyIdent())),
81
+ '/games_my_move': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, gamesMyMoveObs, mainCtrl.getMyIdent())),
78
82
  '/games/:gameId': {
79
83
  onmatch(args) {
80
84
  const gameId = atob(args.gameId);
@@ -86,67 +90,72 @@ module.exports = (attachToElement, dataAccess, opts = {}) => {
86
90
  observableGames: observableGamesObs
87
91
  }
88
92
 
89
- // Only load the game page once we have the initial game situation state.
90
- // The mithril router allows us to return a component in a promise.
91
- return new Promise((resolve) => {
92
- onceTrue(gameSituationObs, () => {
93
- const gameComponent = GameComponent(dataAccess, mainCtrl, observables, settingsCtrl);
94
- resolve(gameComponent);
95
- });
96
- });
93
+ return GameComponent(gameId, dataAccess, mainCtrl, observables, settingsCtrl).then((gameComponent) => {
94
+ return layoutTemplate(navigationBar, gameComponent);
95
+ })
96
+
97
97
  },
98
98
  },
99
- '/invitations': InvitationsComponent(mainCtrl),
100
- '/activity': RecentActivityComponent(mainCtrl, userRecentActivity),
101
- '/observable': MiniboardListComponent(mainCtrl, observableGamesObs, mainCtrl.getMyIdent()),
102
- '/player/:playerId': PlayerProfileComponent(mainCtrl),
99
+ '/invitations': layoutTemplate(navigationBar, InvitationsComponent(mainCtrl)),
100
+ '/activity': layoutTemplate(navigationBar, RecentActivityComponent(mainCtrl, userRecentActivity)),
101
+ '/observable': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, observableGamesObs, mainCtrl.getMyIdent())),
102
+ '/player/:playerId': layoutTemplate(navigationBar, PlayerProfileComponent(mainCtrl)),
103
103
  '/games/:gameId/pgn': {
104
104
  onmatch(args) {
105
105
  const gameId = atob(args.gameId);
106
106
  return mainCtrl.getPgnCtrl()
107
107
  .getPgnExport(gameId)
108
- .then(pgnText => PgnExportComponent(gameId, pgnText));
108
+ .then(pgnText => layoutTemplate(navigationBar, PgnExportComponent(gameId, pgnText)));
109
109
  },
110
110
  },
111
111
  });
112
112
 
113
+ return watches;
113
114
  }
114
115
 
115
- dataAccess.whoAmI((err, ident) => {
116
- const mainCtrl = MainCtrl(dataAccess, ident.id);
117
-
118
- const settingsCtrl = mainCtrl.getSettingsCtrl();
119
-
120
- const mainBody = document.createElement('div');
121
- mainBody.className = 'ssb-chess-container'
122
-
123
- attachToElement.appendChild(mainBody);
124
-
125
- const navDiv = document.createElement('div');
126
- navDiv.id = 'ssb-nav';
127
- const bodyDiv = document.createElement('div');
116
+ function layoutTemplate(navigationBarComponent, component) {
117
+
118
+ // TODO: the nav bar should accept observables by injection and we should give it
119
+ // the same instances on each render so there's not jitter on the numbers in the nav items
120
+ return {
121
+ view: function() {
122
+ return m('div', {className: 'ssb-chess-container'}, [
123
+ m(navigationBarComponent),
124
+ m('div', m(component))
125
+ ])
126
+
127
+
128
+ }
129
+ }
128
130
 
129
- const cssDiv = document.createElement('div');
130
- cssFilesToStyleTag(cssDiv);
131
+ }
131
132
 
132
- mainBody.appendChild(cssDiv);
133
- mainBody.appendChild(navDiv);
134
- mainBody.appendChild(bodyDiv);
133
+ const whoAmIAsync = util.promisify(dataAccess.whoAmI.bind(dataAccess))
135
134
 
136
- renderPageTop(navDiv, mainCtrl, settingsCtrl);
135
+ const globalWatches = whoAmIAsync().then((ident) => {
136
+ const mainCtrl = MainCtrl(dataAccess, ident.id);
137
+ const settingsCtrl = mainCtrl.getSettingsCtrl();
137
138
 
138
139
  // Display HTML5 notifications if the user is not viewing the chess app
139
140
  // and one of their games has an update.
140
141
  const notifier = Notifier(mainCtrl);
141
142
  notifier.startNotifying();
142
143
 
143
- appRouter(bodyDiv, mainCtrl, settingsCtrl);
144
- });
144
+ return appRouter(attachToElement, mainCtrl, settingsCtrl);
145
+ })
145
146
 
146
147
  return {
147
148
  goToGame: (gameId) => {
148
149
  const gameRoute = `/games/${btoa(gameId)}`;
149
150
  m.route.set(gameRoute);
150
151
  },
152
+ destroy: () => {
153
+ // unmount the app
154
+ m.mount(attachToElement, null);
155
+ globalWatches.then((watches) => watches.forEach(w => w()))
156
+ }
151
157
  };
152
158
  };
159
+
160
+
161
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssb-chess-mithril",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "An ssb-chess client",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,17 +14,17 @@
14
14
  "author": "Gordon Martin",
15
15
  "license": "GPL-3.0",
16
16
  "dependencies": {
17
- "chessground": "7.6.8",
17
+ "@lichess-org/chessground": "^10.4.2",
18
18
  "howler": "^2.0.15",
19
19
  "human-time": "0.0.2",
20
20
  "hyperscript": "^2.0.2",
21
- "mithril": "1.1.7",
21
+ "mithril": "2.3.6",
22
22
  "mutant": "3.29.0",
23
23
  "pubsub-js": "^1.7.0",
24
24
  "pull-scroll": "^1.0.9",
25
25
  "pull-stream": "^3.6.14",
26
26
  "ramda": "^0.25.0",
27
- "ssb-chess": "4.0.5",
27
+ "ssb-chess": "4.0.7",
28
28
  "ssb-embedded-chat": "2.0.2"
29
29
  }
30
30
  }
@@ -1,7 +1,7 @@
1
1
  const m = require('mithril');
2
2
  const watchAll = require('mutant/watch-all');
3
3
 
4
- const { opposite } = require('chessground/util');
4
+ const { opposite } = require('@lichess-org/chessground/util');
5
5
 
6
6
  const R = require('ramda');
7
7
 
package/ui/game/chat.js CHANGED
@@ -1,15 +1,15 @@
1
1
 
2
2
  const m = require('mithril')
3
3
  const EmbeddedChat = require('ssb-embedded-chat');
4
- const pull = require('pull-stream')
4
+ const pull = require('pull-stream');
5
+
6
+ module.exports = function ChatBox(myIdent, dataAccess, originalSituation) {
5
7
 
6
- module.exports = function ChatBox(myIdent, dataAccess, situationObservable) {
7
8
  return {
8
9
  view: () => {
9
10
  return m('div', {className: 'ssb-chess-chat'});
10
11
  },
11
12
  oncreate: function (vNode) {
12
- const originalSituation = situationObservable();
13
13
  this.embeddedChat = makeEmbeddedChat(myIdent, dataAccess, originalSituation);
14
14
  vNode.dom.appendChild(this.embeddedChat.getChatboxElement());
15
15
  },
@@ -7,7 +7,11 @@ const R = require('ramda');
7
7
  const UserLocationUtils = require('../viewer_perspective/user_location')();
8
8
 
9
9
  module.exports = (gameObservable) => {
10
- let watchesToClear = [];
10
+ // TODO(hack): the router calls this function inside a 'onceTrue' which releases the observable once it exits the function body,
11
+ // at that point the observable would have no subscribers left and would have an initial value of null in the places it is used,
12
+ // so subscribing immediately stops that happening
13
+ const unwatch = gameObservable(e => e);
14
+ let watchesToClear = [unwatch];
11
15
 
12
16
  let moveNumberSelected = 'live';
13
17
  const moveSelectedObservable = Value(moveNumberSelected);
@@ -1,11 +1,12 @@
1
1
  const m = require('mithril');
2
- const { Chessground } = require('chessground');
2
+ const { Chessground } = require('@lichess-org/chessground');
3
3
  const PubSub = require('pubsub-js');
4
4
  const Value = require('mutant/value');
5
5
  const watchAll = require('mutant/watch-all');
6
6
  const computed = require('mutant/computed');
7
7
  const when = require('mutant/when');
8
8
  const { Howl } = require('howler');
9
+ const watch = require('mutant/watch');
9
10
 
10
11
  const ActionButtons = require('./gameActions');
11
12
  const GameHistory = require('./gameHistory');
@@ -15,10 +16,28 @@ const ChatBox = require('./chat')
15
16
  const PieceGraveyard = require('./PieceGraveyard');
16
17
  const NextPreviousButtons = require('./nextGameControl');
17
18
 
18
- module.exports = (dataAccess, gameCtrl, observables, settings) => {
19
- const myIdent = gameCtrl.getMyIdent();
19
+ function getInitialSituation(situationObservable) {
20
+ return new Promise((resolve, reject) => {
21
+ const w = watch(situationObservable, (situation) => {
22
+ if (situation) {
23
+ resolve(situation)
24
+
25
+ // hack: Keep the observable warm - the ssb-chess 'getSituationObservable' doesn't handle
26
+ // the observable having 0 listeners then 1 again - it aborts the underyling stream when it
27
+ // reaches 0 listeners but doesn't start it again if it reaches >= 1 again.
28
+ // TODO: fix this properly
29
+ setTimeout(w, 50000);
30
+ }
31
+ })
32
+ })
33
+
34
+ }
35
+
36
+ module.exports = async (gameId, dataAccess, gameCtrl, observables, settings) => {
20
37
 
38
+ const myIdent = gameCtrl.getMyIdent();
21
39
  const { situationObservable, gamesWhereMyMove, observableGames } = observables;
40
+ const originalSituation = await getInitialSituation(situationObservable);
22
41
 
23
42
  let chessGround = null;
24
43
  const chessGroundObservable = Value();
@@ -75,8 +94,8 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
75
94
 
76
95
  function isPromotionMove(cg, dest) {
77
96
  return (dest[1] === '8' || dest[1] === '1')
78
- && cg.state.pieces[dest]
79
- && (cg.state.pieces[dest].role === 'pawn');
97
+ && cg.state.pieces.get(dest)
98
+ && (cg.state.pieces.get(dest).role === 'pawn');
80
99
  }
81
100
 
82
101
  function renderBoard(gameId) {
@@ -131,18 +150,21 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
131
150
  }
132
151
 
133
152
  function situationToChessgroundConfig(situation, moveSelected, validMoves) {
153
+ const ended = ["mate", "draw", "resigned"].indexOf(situation.status.status) !== -1;
154
+
134
155
  const playerColour = situation.players[myIdent] ? situation.players[myIdent].colour : 'white';
135
156
 
136
157
  const colourToPlay = plyToColourToPlay(situation.ply);
137
158
 
138
159
  const config = {
160
+ viewOnly: ended,
139
161
  fen: situation.fen,
140
162
  orientation: playerColour,
141
163
  turnColor: colourToPlay,
142
164
  ply: situation.ply,
143
165
  check: situation.check,
144
166
  movable: {
145
- dests: validMoves,
167
+ dests: new Map(Object.entries(validMoves)),
146
168
  free: false,
147
169
  color: situation.toMove === myIdent ? playerColour : null,
148
170
  events: {
@@ -225,12 +247,11 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
225
247
  }
226
248
  }
227
249
 
228
- const Chat = ChatBox(myIdent, dataAccess, situationObservable);
250
+ const Chat = ChatBox(myIdent, dataAccess, originalSituation);
229
251
 
230
252
  return {
231
253
 
232
254
  view(ctrl) {
233
- const gameId = atob(ctrl.attrs.gameId);
234
255
 
235
256
  return m('div', {
236
257
  class: 'ssb-chess-board-background-blue3 merida ssb-chess-game-layout',
@@ -243,13 +264,11 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
243
264
  ])]);
244
265
  },
245
266
  oncreate(vNode) {
246
- const gameId = atob(vNode.attrs.gameId);
247
267
  const boardDom = document.getElementById(gameId);
248
268
 
249
- const originalSituation = situationObservable();
250
-
251
269
  const config = situationToChessgroundConfig(originalSituation, 'live', {});
252
270
  chessGround = Chessground(boardDom, config);
271
+
253
272
  chessGroundObservable.set(chessGround);
254
273
 
255
274
  const validMovesObservable = gameCtrl.getMovesFinderCtrl()
@@ -1,5 +1,5 @@
1
1
  const m = require('mithril');
2
- const { Chessground } = require('chessground');
2
+ const { Chessground } = require('@lichess-org/chessground');
3
3
  const timeAgo = require('./timeAgo');
4
4
 
5
5
  module.exports = (gameSummaryObservable, summary, identPerspective, opts) => {
@@ -13,9 +13,10 @@ module.exports = (gameSummaryObservable, summary, identPerspective, opts) => {
13
13
  && summary.players[identPerspective].colour) ? summary.players[identPerspective].colour : 'white';
14
14
 
15
15
  function renderPlayerName(player) {
16
- return m(`a[href=/player/${btoa(player.id)}]`, {
16
+ const link = `/player/${btoa(player.id)}`;
17
+ return m(m.route.Link, {
17
18
  class: 'ssb-chess-miniboard-name',
18
- oncreate: m.route.link,
19
+ href: link,
19
20
  },
20
21
  player.name.substring(0, 10));
21
22
  }
@@ -48,14 +49,16 @@ module.exports = (gameSummaryObservable, summary, identPerspective, opts) => {
48
49
  const observing = Object.keys(summary.players).indexOf(identPerspective) === -1;
49
50
  const boardSizeClass = opts && opts.small ? 'ssb-chess-board-small' : 'ssb-chess-board-medium';
50
51
 
52
+ const link = `/games/${btoa(summary.gameId)}?observing=${observing}`;
53
+
51
54
  return m('div', {
52
55
  class: 'ssb-chess-miniboard ssb-chess-board-background-blue3 merida',
53
56
  }, [
54
- m(`${'a[href=/games/'}${btoa(summary.gameId)}?observing=${observing}]`, {
57
+ m(m.route.Link, {
55
58
  class: `ssb-chessground-container cg-wrap ${boardSizeClass}`,
56
59
  title: summary.gameId,
57
60
  id: summary.gameId,
58
- oncreate: m.route.link,
61
+ href: link,
59
62
  }), renderSummaryBottom(),
60
63
  ]);
61
64
  }
@@ -2,7 +2,7 @@ const m = require('mithril');
2
2
  const SettingsDialog = require('../settings/settings-dialog');
3
3
 
4
4
  module.exports = (mainCtrl, settings) => {
5
- m.route.prefix('?');
5
+ m.route.prefix = '?';
6
6
 
7
7
  const gamesInProgress = {
8
8
  name: 'Games',
@@ -45,8 +45,8 @@ module.exports = (mainCtrl, settings) => {
45
45
  function renderNavItem(navItem) {
46
46
  return m('span', {
47
47
  class: 'ssb-chess-nav-item',
48
- }, m(`a[href=${navItem.link}]`, {
49
- oncreate: m.route.link,
48
+ }, m(m.route.Link, {
49
+ href: navItem.link,
50
50
  title: navItem.countHoverText(),
51
51
  },
52
52
 
@@ -89,24 +89,28 @@ module.exports = (mainCtrl, settings) => {
89
89
  }
90
90
 
91
91
  function keepCountsUpdated() {
92
- navItems.forEach((navItem) => {
92
+ const watches = navItems.map((navItem) => {
93
93
  if (navItem.countUpdateFn) {
94
- navItem.countUpdateFn()((items) => {
94
+ return navItem.countUpdateFn()((items) => {
95
95
  const numItems = items.length;
96
96
  navItem.count = numItems;
97
97
  m.redraw();
98
98
  });
99
+ } else {
100
+ return () => {}
99
101
  }
100
102
  });
103
+
104
+ return watches;
101
105
  }
102
106
 
103
107
  return {
104
108
  view: () => renderNavigation(),
105
109
  oncreate: () => {
106
- keepCountsUpdated();
110
+ this.watches = keepCountsUpdated();
107
111
  },
108
112
  onremove: () => {
109
-
113
+ this.watches.forEach(watch => watch());
110
114
  },
111
115
  };
112
116
  };
@@ -1,52 +0,0 @@
1
- const gulp = require('gulp');
2
- const source = require('vinyl-source-stream');
3
- const buffer = require('vinyl-buffer');
4
- const colors = require('ansi-colors');
5
- const logger = require('fancy-log');
6
- const watchify = require('watchify');
7
- const browserify = require('browserify');
8
- const uglify = require('gulp-uglify');
9
- const tsify = require('tsify');
10
-
11
- const destination = gulp.dest('./dist');
12
- const fileBaseName = 'chessground-examples';
13
-
14
- const browserifyOpts = (debug) => ({
15
- entries: ['src/main.ts'],
16
- standalone: 'ChessgroundExamples',
17
- debug: debug
18
- });
19
-
20
- const prod = () => browserify(browserifyOpts(false))
21
- .plugin(tsify)
22
- .bundle()
23
- .pipe(source(`${fileBaseName}.min.js`))
24
- .pipe(buffer())
25
- .pipe(uglify())
26
- .pipe(destination);
27
-
28
- const dev = () => browserify(browserifyOpts(true))
29
- .plugin(tsify)
30
- .bundle()
31
- .pipe(source(`${fileBaseName}.js`))
32
- .pipe(destination);
33
-
34
- const watch = () => {
35
-
36
- const bundle = () => bundler
37
- .bundle()
38
- .on('error', error => logger.error(colors.red(error.message)))
39
- .pipe(source(`${fileBaseName}.js`))
40
- .pipe(destination);
41
-
42
- const bundler = watchify(
43
- browserify(Object.assign({}, watchify.args, browserifyOpts(true)))
44
- .plugin(tsify)
45
- ).on('update', bundle).on('log', logger.info);
46
-
47
- return bundle();
48
- };
49
-
50
- gulp.task('prod', prod);
51
- gulp.task('dev', dev);
52
- gulp.task('default', watch);