progress on chessground based rounds
This commit is contained in:
parent
b3232bc84f
commit
18e428c1e8
|
@ -56,11 +56,10 @@ object Round extends LilaController with TheftPrevention {
|
||||||
html = pov.game.started.fold(
|
html = pov.game.started.fold(
|
||||||
PreventTheft(pov) {
|
PreventTheft(pov) {
|
||||||
(pov.game.tournamentId ?? TournamentRepo.byId) zip
|
(pov.game.tournamentId ?? TournamentRepo.byId) zip
|
||||||
Env.game.crosstableApi(pov.game) zip
|
Env.game.crosstableApi(pov.game) flatMap {
|
||||||
(pov.game.playable ?? env.takebacker.isAllowedByPrefs(pov.game)) flatMap {
|
case (tour, crosstable) =>
|
||||||
case ((tour, crosstable), takebackable) =>
|
|
||||||
env.jsonView.playerJson(pov, ctx.pref, Env.api.version, ctx.me) map { data =>
|
env.jsonView.playerJson(pov, ctx.pref, Env.api.version, ctx.me) map { data =>
|
||||||
Ok(html.round.player(pov, data, tour = tour, cross = crosstable, takebackable = takebackable))
|
Ok(html.round.player(pov, data, tour = tour, cross = crosstable))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@(pov: Pov, data: play.api.libs.json.JsObject, tour: Option[lila.tournament.Tournament], cross: Option[lila.game.Crosstable], takebackable: Boolean)(implicit ctx: Context)
|
@(pov: Pov, data: play.api.libs.json.JsObject, tour: Option[lila.tournament.Tournament], cross: Option[lila.game.Crosstable])(implicit ctx: Context)
|
||||||
|
|
||||||
@import pov._
|
@import pov._
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@
|
||||||
*@
|
*@
|
||||||
@jsAt(s"compiled/lichess.round.js")
|
@jsAt(s"compiled/lichess.round.js")
|
||||||
@helper.javascriptRouter("roundRoutes")(
|
@helper.javascriptRouter("roundRoutes")(
|
||||||
routes.javascript.Auth.signup
|
routes.javascript.Auth.signup,
|
||||||
|
routes.javascript.User.show
|
||||||
)(ctx.req)
|
)(ctx.req)
|
||||||
@embedJs {
|
@embedJs {
|
||||||
lichess = lichess || {};
|
lichess = lichess || {};
|
||||||
|
@ -18,7 +19,29 @@ lichess.round = {
|
||||||
data: @Html(play.api.libs.json.Json.stringify(data)),
|
data: @Html(play.api.libs.json.Json.stringify(data)),
|
||||||
routes: roundRoutes.controllers,
|
routes: roundRoutes.controllers,
|
||||||
i18n: @Html(J.stringify(i18nJsObject(
|
i18n: @Html(J.stringify(i18nJsObject(
|
||||||
trans.premoveEnabledClickAnywhereToCancel)))
|
trans.premoveEnabledClickAnywhereToCancel,
|
||||||
|
trans.aiNameLevelAiLevel,
|
||||||
|
trans.waiting,
|
||||||
|
trans.yourTurn,
|
||||||
|
trans.abortGame,
|
||||||
|
trans.proposeATakeback,
|
||||||
|
trans.offerDraw,
|
||||||
|
trans.resign,
|
||||||
|
trans.theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim,
|
||||||
|
trans.forceResignation,
|
||||||
|
trans.forceDraw,
|
||||||
|
trans.threefoldRepetition,
|
||||||
|
trans.claimADraw,
|
||||||
|
trans.drawOfferSent,
|
||||||
|
trans.cancel,
|
||||||
|
trans.yourOpponentOffersADraw,
|
||||||
|
trans.accept,
|
||||||
|
trans.decline,
|
||||||
|
trans.takebackPropositionSent,
|
||||||
|
trans.yourOpponentProposesATakeback,
|
||||||
|
trans.youHaveNbSecondsToMakeYourFirstMove,
|
||||||
|
trans.thisPlayerUsesChessComputerAssistance
|
||||||
|
)))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,10 +93,6 @@ object Event {
|
||||||
override def owner = true
|
override def owner = true
|
||||||
}
|
}
|
||||||
|
|
||||||
object Reload extends Empty {
|
|
||||||
def typ = "resync"
|
|
||||||
}
|
|
||||||
|
|
||||||
case class Promotion(role: PromotableRole, pos: Pos) extends Event {
|
case class Promotion(role: PromotableRole, pos: Pos) extends Event {
|
||||||
def typ = "promotion"
|
def typ = "promotion"
|
||||||
def data = Json.obj(
|
def data = Json.obj(
|
||||||
|
@ -141,8 +137,12 @@ object Event {
|
||||||
case object ReloadTables extends Empty {
|
case object ReloadTables extends Empty {
|
||||||
def typ = "reloadTable"
|
def typ = "reloadTable"
|
||||||
}
|
}
|
||||||
case object ReloadTablesOwner extends Empty {
|
|
||||||
def typ = "reloadTable"
|
case object Reload extends Empty {
|
||||||
|
def typ = "reload"
|
||||||
|
}
|
||||||
|
case object ReloadOwner extends Empty {
|
||||||
|
def typ = "reload"
|
||||||
override def owner = true
|
override def owner = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ private[round] final class Drawer(messenger: Messenger, finisher: Finisher) {
|
||||||
case Pov(g, color) if (g playerCanOfferDraw color) => GameRepo save {
|
case Pov(g, color) if (g playerCanOfferDraw color) => GameRepo save {
|
||||||
messenger.system(g, _.drawOfferSent)
|
messenger.system(g, _.drawOfferSent)
|
||||||
Progress(g) map { g => g.updatePlayer(color, _ offerDraw g.turns) }
|
Progress(g) map { g => g.updatePlayer(color, _ offerDraw g.turns) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case _ => fufail("[drawer] invalid yes " + pov)
|
case _ => fufail("[drawer] invalid yes " + pov)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,11 +26,11 @@ private[round] final class Drawer(messenger: Messenger, finisher: Finisher) {
|
||||||
case Pov(g, color) if pov.player.isOfferingDraw => GameRepo save {
|
case Pov(g, color) if pov.player.isOfferingDraw => GameRepo save {
|
||||||
messenger.system(g, _.drawOfferCanceled)
|
messenger.system(g, _.drawOfferCanceled)
|
||||||
Progress(g) map { g => g.updatePlayer(color, _.removeDrawOffer) }
|
Progress(g) map { g => g.updatePlayer(color, _.removeDrawOffer) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case Pov(g, color) if pov.opponent.isOfferingDraw => GameRepo save {
|
case Pov(g, color) if pov.opponent.isOfferingDraw => GameRepo save {
|
||||||
messenger.system(g, _.drawOfferDeclined)
|
messenger.system(g, _.drawOfferDeclined)
|
||||||
Progress(g) map { g => g.updatePlayer(!color, _.removeDrawOffer) }
|
Progress(g) map { g => g.updatePlayer(!color, _.removeDrawOffer) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case _ => fufail("[drawer] invalid no " + pov)
|
case _ => fufail("[drawer] invalid no " + pov)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,7 @@ final class Env(
|
||||||
chatApi = chatApi,
|
chatApi = chatApi,
|
||||||
userJsonView = userJsonView,
|
userJsonView = userJsonView,
|
||||||
getVersion = version,
|
getVersion = version,
|
||||||
|
canTakeback = takebacker.isAllowedByPrefs,
|
||||||
baseAnimationDuration = AnimationDuration)
|
baseAnimationDuration = AnimationDuration)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,7 @@ final class JsonView(
|
||||||
chatApi: lila.chat.ChatApi,
|
chatApi: lila.chat.ChatApi,
|
||||||
userJsonView: lila.user.JsonView,
|
userJsonView: lila.user.JsonView,
|
||||||
getVersion: String => Fu[Int],
|
getVersion: String => Fu[Int],
|
||||||
|
canTakeback: Game => Fu[Boolean],
|
||||||
baseAnimationDuration: Duration) {
|
baseAnimationDuration: Duration) {
|
||||||
|
|
||||||
def playerJson(
|
def playerJson(
|
||||||
|
@ -26,8 +27,9 @@ final class JsonView(
|
||||||
playerUser: Option[User]): Fu[JsObject] =
|
playerUser: Option[User]): Fu[JsObject] =
|
||||||
getVersion(pov.game.id) zip
|
getVersion(pov.game.id) zip
|
||||||
(pov.opponent.userId ?? UserRepo.byId) zip
|
(pov.opponent.userId ?? UserRepo.byId) zip
|
||||||
|
canTakeback(pov.game) zip
|
||||||
getChat(pov.game, playerUser) map {
|
getChat(pov.game, playerUser) map {
|
||||||
case ((version, opponentUser), chat) =>
|
case (((version, opponentUser), takebackable), chat) =>
|
||||||
import pov._
|
import pov._
|
||||||
Json.obj(
|
Json.obj(
|
||||||
"game" -> Json.obj(
|
"game" -> Json.obj(
|
||||||
|
@ -66,7 +68,8 @@ final class JsonView(
|
||||||
"isProposingTakeback" -> opponent.isProposingTakeback.option(true)
|
"isProposingTakeback" -> opponent.isProposingTakeback.option(true)
|
||||||
).noNull,
|
).noNull,
|
||||||
"url" -> Json.obj(
|
"url" -> Json.obj(
|
||||||
"socket" -> s"/$fullId/socket/v$apiVersion"
|
"socket" -> s"/$fullId/socket/v$apiVersion",
|
||||||
|
"round" -> s"/$fullId"
|
||||||
),
|
),
|
||||||
"pref" -> Json.obj(
|
"pref" -> Json.obj(
|
||||||
"animationDuration" -> animationDuration(pov, pref),
|
"animationDuration" -> animationDuration(pov, pref),
|
||||||
|
@ -90,7 +93,8 @@ final class JsonView(
|
||||||
},
|
},
|
||||||
"possibleMoves" -> possibleMoves(pov),
|
"possibleMoves" -> possibleMoves(pov),
|
||||||
"tournamentId" -> game.tournamentId,
|
"tournamentId" -> game.tournamentId,
|
||||||
"poolId" -> game.poolId)
|
"poolId" -> game.poolId,
|
||||||
|
"takebackable" -> takebackable)
|
||||||
}
|
}
|
||||||
|
|
||||||
def watcherJson(pov: Pov, version: Int, tv: Boolean, pref: Pref) = {
|
def watcherJson(pov: Pov, version: Int, tv: Boolean, pref: Pref) = {
|
||||||
|
|
|
@ -31,11 +31,11 @@ private[round] final class Rematcher(
|
||||||
case Pov(game, color) if pov.player.isOfferingRematch => GameRepo save {
|
case Pov(game, color) if pov.player.isOfferingRematch => GameRepo save {
|
||||||
messenger.system(game, _.rematchOfferCanceled)
|
messenger.system(game, _.rematchOfferCanceled)
|
||||||
Progress(game) map { g => g.updatePlayer(color, _.removeRematchOffer) }
|
Progress(game) map { g => g.updatePlayer(color, _.removeRematchOffer) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case Pov(game, color) if pov.opponent.isOfferingRematch => GameRepo save {
|
case Pov(game, color) if pov.opponent.isOfferingRematch => GameRepo save {
|
||||||
messenger.system(game, _.rematchOfferDeclined)
|
messenger.system(game, _.rematchOfferDeclined)
|
||||||
Progress(game) map { g => g.updatePlayer(!color, _.removeRematchOffer) }
|
Progress(game) map { g => g.updatePlayer(!color, _.removeRematchOffer) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case _ => ClientErrorException.future("[rematcher] invalid no " + pov)
|
case _ => ClientErrorException.future("[rematcher] invalid no " + pov)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ private[round] final class Rematcher(
|
||||||
private def rematchCreate(pov: Pov): Fu[Events] = GameRepo save {
|
private def rematchCreate(pov: Pov): Fu[Events] = GameRepo save {
|
||||||
messenger.system(pov.game, _.rematchOfferSent)
|
messenger.system(pov.game, _.rematchOfferSent)
|
||||||
Progress(pov.game) map { g => g.updatePlayer(pov.color, _ offerRematch) }
|
Progress(pov.game) map { g => g.updatePlayer(pov.color, _ offerRematch) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
|
|
||||||
private def returnGame(pov: Pov): Fu[Game] = for {
|
private def returnGame(pov: Pov): Fu[Game] = for {
|
||||||
pieces ← pov.game.variant.standard.fold(
|
pieces ← pov.game.variant.standard.fold(
|
||||||
|
|
|
@ -15,7 +15,7 @@ private[round] final class Takebacker(
|
||||||
case Pov(game, color) if (game playerCanProposeTakeback color) => GameRepo save {
|
case Pov(game, color) if (game playerCanProposeTakeback color) => GameRepo save {
|
||||||
messenger.system(game, _.takebackPropositionSent)
|
messenger.system(game, _.takebackPropositionSent)
|
||||||
Progress(game) map { g => g.updatePlayer(color, _.proposeTakeback) }
|
Progress(game) map { g => g.updatePlayer(color, _.proposeTakeback) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case _ => ClientErrorException.future("[takebacker] invalid yes " + pov)
|
case _ => ClientErrorException.future("[takebacker] invalid yes " + pov)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,11 @@ private[round] final class Takebacker(
|
||||||
case Pov(game, color) if pov.player.isProposingTakeback => GameRepo save {
|
case Pov(game, color) if pov.player.isProposingTakeback => GameRepo save {
|
||||||
messenger.system(game, _.takebackPropositionCanceled)
|
messenger.system(game, _.takebackPropositionCanceled)
|
||||||
Progress(game) map { g => g.updatePlayer(color, _.removeTakebackProposition) }
|
Progress(game) map { g => g.updatePlayer(color, _.removeTakebackProposition) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case Pov(game, color) if pov.opponent.isProposingTakeback => GameRepo save {
|
case Pov(game, color) if pov.opponent.isProposingTakeback => GameRepo save {
|
||||||
messenger.system(game, _.takebackPropositionDeclined)
|
messenger.system(game, _.takebackPropositionDeclined)
|
||||||
Progress(game) map { g => g.updatePlayer(!color, _.removeTakebackProposition) }
|
Progress(game) map { g => g.updatePlayer(!color, _.removeTakebackProposition) }
|
||||||
} inject List(Event.ReloadTablesOwner)
|
} inject List(Event.ReloadOwner)
|
||||||
case _ => ClientErrorException.future("[takebacker] invalid no " + pov)
|
case _ => ClientErrorException.future("[takebacker] invalid no " + pov)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -709,14 +709,13 @@ div.lichess_control .lichess_rematch.disabled {
|
||||||
}
|
}
|
||||||
#claim_draw_zone,
|
#claim_draw_zone,
|
||||||
div.force_resign_zone,
|
div.force_resign_zone,
|
||||||
div.proposed_takeback,
|
div.negociation {
|
||||||
div.offered_draw {
|
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
border-top: 1px solid #ddd;
|
border-top: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
#claim_draw_zone .button,
|
#claim_draw_zone .button,
|
||||||
div.offered_draw .button,
|
div.negociation .button,
|
||||||
div.force_resign_zone .button {
|
div.force_resign_zone .button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -742,7 +741,9 @@ div.lichess_current_player div.lichess_player {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-left: -5px;
|
margin-left: -5px;
|
||||||
}
|
}
|
||||||
div.lichess_current_player div.lichess_player div.piece {
|
div.lichess_current_player div.lichess_player div.cg-piece {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,8 +126,7 @@ body.dark #hooks_list .pool_buttons > a,
|
||||||
body.dark #hooks_list .pool_title,
|
body.dark #hooks_list .pool_title,
|
||||||
body.dark div.training div.box,
|
body.dark div.training div.box,
|
||||||
body.dark div.force_resign_zone,
|
body.dark div.force_resign_zone,
|
||||||
body.dark div.proposed_takeback,
|
body.dark div.negotiation {
|
||||||
body.dark div.offered_draw {
|
|
||||||
border-color: #3d3d3d;
|
border-color: #3d3d3d;
|
||||||
}
|
}
|
||||||
body.dark #crosstable td.last {
|
body.dark #crosstable td.last {
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"watchify": "^1.0.2"
|
"watchify": "^1.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chessground": "git://github.com/ornicar/chessground.git",
|
"chessground": "^1.4.0",
|
||||||
"lodash-node": "^2.4.1",
|
"lodash-node": "^2.4.1",
|
||||||
"mithril": "^0.1.22"
|
"mithril": "^0.1.22"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module.exports = function(data) {
|
module.exports = function(data, onFlag) {
|
||||||
|
|
||||||
var lastUpdate;
|
var lastUpdate;
|
||||||
|
|
||||||
|
@ -22,7 +22,8 @@ module.exports = function(data) {
|
||||||
|
|
||||||
this.tick = function(color) {
|
this.tick = function(color) {
|
||||||
m.startComputation();
|
m.startComputation();
|
||||||
this.data[color] = lastUpdate[color] - (new Date() - lastUpdate.at) / 1000;
|
this.data[color] = Math.max(0, lastUpdate[color] - (new Date() - lastUpdate.at) / 1000);
|
||||||
|
if (this.data[color] === 0) onFlag();
|
||||||
m.endComputation();
|
m.endComputation();
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
var m = require('mithril');
|
var m = require('mithril');
|
||||||
var partial = require('lodash-node/modern/functions/partial');
|
var partial = require('lodash-node/modern/functions/partial');
|
||||||
|
var throttle = require('lodash-node/modern/functions/throttle');
|
||||||
var chessground = require('chessground');
|
var chessground = require('chessground');
|
||||||
var data = require('./data');
|
var data = require('./data');
|
||||||
var round = require('./round');
|
var round = require('./round');
|
||||||
|
var ground = require('./ground');
|
||||||
var socket = require('./socket');
|
var socket = require('./socket');
|
||||||
var clockCtrl = require('./clock/ctrl');
|
var clockCtrl = require('./clock/ctrl');
|
||||||
var util = require('./util');
|
|
||||||
|
|
||||||
module.exports = function(cfg, router, i18n, socketSend) {
|
module.exports = function(cfg, router, i18n, socketSend) {
|
||||||
|
|
||||||
|
@ -24,36 +25,17 @@ module.exports = function(cfg, router, i18n, socketSend) {
|
||||||
});
|
});
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
this.chessground = new chessground.controller({
|
this.chessground = ground.make(this.data, cfg.game.fen, this.userMove);
|
||||||
fen: cfg.game.fen,
|
|
||||||
orientation: this.data.player.color,
|
|
||||||
turnColor: this.data.game.player,
|
|
||||||
lastMove: util.str2move(this.data.game.lastMove),
|
|
||||||
highlight: {
|
|
||||||
lastMove: this.data.pref.highlight,
|
|
||||||
check: this.data.pref.highlight,
|
|
||||||
dragOver: true
|
|
||||||
},
|
|
||||||
movable: {
|
|
||||||
free: false,
|
|
||||||
color: round.isPlayerPlaying(this.data) ? this.data.player.color : null,
|
|
||||||
dests: round.parsePossibleMoves(this.data.possibleMoves),
|
|
||||||
showDests: this.data.pref.destination,
|
|
||||||
events: {
|
|
||||||
after: this.userMove
|
|
||||||
},
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
enabled: true,
|
|
||||||
duration: this.data.pref.animationDuration
|
|
||||||
},
|
|
||||||
premovable: {
|
|
||||||
enabled: this.data.pref.enablePremove,
|
|
||||||
showDests: this.data.pref.destination
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.clock = this.data.clock ? new clockCtrl(this.data.clock) : false;
|
this.reload = function(cfg) {
|
||||||
|
this.data = data(cfg);
|
||||||
|
ground.reload(this.chessground, this.data, cfg.game.fen);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.clock = this.data.clock ? new clockCtrl(
|
||||||
|
this.data.clock,
|
||||||
|
throttle(partial(this.socket.send, 'outoftime'), 500)
|
||||||
|
) : false;
|
||||||
|
|
||||||
this.isClockRunning = function() {
|
this.isClockRunning = function() {
|
||||||
return !this.data.game.finished && ((this.data.game.turns - this.data.game.startedAtTurn) > 1 || this.data.game.clockRunning);
|
return !this.data.game.finished && ((this.data.game.turns - this.data.game.startedAtTurn) > 1 || this.data.game.clockRunning);
|
||||||
|
|
53
ui/round/src/ground.js
Normal file
53
ui/round/src/ground.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
var chessground = require('chessground');
|
||||||
|
var round = require('./round');
|
||||||
|
var util = require('./util');
|
||||||
|
|
||||||
|
function makeConfig(data, fen) {
|
||||||
|
return {
|
||||||
|
fen: fen,
|
||||||
|
orientation: data.player.color,
|
||||||
|
turnColor: data.game.player,
|
||||||
|
lastMove: util.str2move(data.game.lastMove),
|
||||||
|
highlight: {
|
||||||
|
lastMove: data.pref.highlight,
|
||||||
|
check: data.pref.highlight,
|
||||||
|
dragOver: true
|
||||||
|
},
|
||||||
|
movable: {
|
||||||
|
free: false,
|
||||||
|
color: round.isPlayerPlaying(data) ? data.player.color : null,
|
||||||
|
dests: round.parsePossibleMoves(data.possibleMoves),
|
||||||
|
showDests: data.pref.destination
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
enabled: true,
|
||||||
|
duration: data.pref.animationDuration
|
||||||
|
},
|
||||||
|
premovable: {
|
||||||
|
enabled: data.pref.enablePremove,
|
||||||
|
showDests: data.pref.destination
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function make(data, fen, userMove) {
|
||||||
|
var config = makeConfig(data, fen);
|
||||||
|
config.movable.events = {
|
||||||
|
after: userMove
|
||||||
|
};
|
||||||
|
return new chessground.controller(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload(ground, data, fen) {
|
||||||
|
ground.set(makeConfig(data, fen));
|
||||||
|
}
|
||||||
|
|
||||||
|
function end(ground) {
|
||||||
|
ground.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
make: make,
|
||||||
|
reload: reload,
|
||||||
|
end: end
|
||||||
|
};
|
|
@ -14,8 +14,42 @@ function parsePossibleMoves(possibleMoves) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function playable(data) {
|
||||||
|
return data.game.started && !data.game.finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mandatory(data) {
|
||||||
|
return data.tournamentId || data.poolId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function abortable(data) {
|
||||||
|
return playable(data) && data.game.turns < 2 && !mandatory(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function takebackable(data) {
|
||||||
|
return playable(data) && data.takebackable && !data.tournamentId && data.game.turns > 1 && !data.player.isProposingTakeback && !data.opponent.isProposingTakeback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawable(data) {
|
||||||
|
return playable(data) && data.game.turns >= 2 && !data.player.isOfferingDraw && !data.opponent.ai;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resignable(data) {
|
||||||
|
return playable(data) && !abortable(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlayer(data, color) {
|
||||||
|
return data.player.color == color ? data.player : data.opponent;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isGamePlaying: isGamePlaying,
|
isGamePlaying: isGamePlaying,
|
||||||
isPlayerPlaying: isPlayerPlaying,
|
isPlayerPlaying: isPlayerPlaying,
|
||||||
|
playable: playable,
|
||||||
|
abortable: abortable,
|
||||||
|
takebackable: takebackable,
|
||||||
|
drawable: drawable,
|
||||||
|
resignable: resignable,
|
||||||
|
getPlayer: getPlayer,
|
||||||
parsePossibleMoves: parsePossibleMoves
|
parsePossibleMoves: parsePossibleMoves
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
var m = require('mithril');
|
var m = require('mithril');
|
||||||
var round = require('./round');
|
var round = require('./round');
|
||||||
var ground = require('chessground');
|
var ground = require('./ground');
|
||||||
|
var xhr = require('./xhr');
|
||||||
|
|
||||||
module.exports = function(send, ctrl) {
|
module.exports = function(send, ctrl) {
|
||||||
|
|
||||||
|
@ -8,14 +9,14 @@ module.exports = function(send, ctrl) {
|
||||||
|
|
||||||
var handlers = {
|
var handlers = {
|
||||||
possibleMoves: function(o) {
|
possibleMoves: function(o) {
|
||||||
ctrl.chessground.reconfigure({
|
ctrl.chessground.set({
|
||||||
movable: {
|
movable: {
|
||||||
dests: round.parsePossibleMoves(o)
|
dests: round.parsePossibleMoves(o)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
state: function(o) {
|
state: function(o) {
|
||||||
ctrl.chessground.reconfigure({
|
ctrl.chessground.set({
|
||||||
turnColor: o.color
|
turnColor: o.color
|
||||||
});
|
});
|
||||||
ctrl.data.game.player = o.color;
|
ctrl.data.game.player = o.color;
|
||||||
|
@ -42,7 +43,7 @@ module.exports = function(send, ctrl) {
|
||||||
ctrl.chessground.setPieces(pieces);
|
ctrl.chessground.setPieces(pieces);
|
||||||
},
|
},
|
||||||
check: function(o) {
|
check: function(o) {
|
||||||
ctrl.chessground.reconfigure({
|
ctrl.chessground.set({
|
||||||
check: o
|
check: o
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -58,11 +59,30 @@ module.exports = function(send, ctrl) {
|
||||||
$.redirect(o);
|
$.redirect(o);
|
||||||
}, 400);
|
}, 400);
|
||||||
},
|
},
|
||||||
|
reload: function(o) {
|
||||||
|
xhr.reload(ctrl.data).then(ctrl.reload);
|
||||||
|
},
|
||||||
threefoldRepetition: function() {
|
threefoldRepetition: function() {
|
||||||
// ???
|
// ???
|
||||||
},
|
},
|
||||||
clock: function(o) {
|
clock: function(o) {
|
||||||
if (ctrl.clock) ctrl.clock.update(o.white, o.black);
|
if (ctrl.clock) ctrl.clock.update(o.white, o.black);
|
||||||
|
},
|
||||||
|
crowd: function(o) {
|
||||||
|
m.startComputation();
|
||||||
|
['white', 'black'].forEach(function(c) {
|
||||||
|
round.getPlayer(ctrl.data, c).statused = true;
|
||||||
|
round.getPlayer(ctrl.data, c).connected = o[c];
|
||||||
|
});
|
||||||
|
ctrl.data.watchers = o.watchers;
|
||||||
|
m.endComputation();
|
||||||
|
},
|
||||||
|
end: function() {
|
||||||
|
m.startComputation();
|
||||||
|
ctrl.data.game.finished = true;
|
||||||
|
m.endComputation();
|
||||||
|
ground.end(ctrl.chessground);
|
||||||
|
xhr.reload(ctrl.data).then(ctrl.reload);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,112 @@
|
||||||
var map = require('lodash-node/modern/collections/map');
|
var map = require('lodash-node/modern/collections/map');
|
||||||
var chessground = require('chessground');
|
var chessground = require('chessground');
|
||||||
|
var round = require('./round');
|
||||||
var opposite = chessground.util.opposite;
|
var opposite = chessground.util.opposite;
|
||||||
var classSet = chessground.util.classSet;
|
var classSet = chessground.util.classSet;
|
||||||
var partial = chessground.util.partial;
|
var partial = chessground.util.partial;
|
||||||
var clockView = require('./clock/view');
|
var clockView = require('./clock/view');
|
||||||
var m = require('mithril');
|
var m = require('mithril');
|
||||||
|
|
||||||
|
function renderOpponent(ctrl) {
|
||||||
|
var op = ctrl.data.opponent;
|
||||||
|
return op.ai ? m('div.username.connected.statused',
|
||||||
|
ctrl.trans('aiNameLevelAiLevel', 'Stockfish', op.ai)
|
||||||
|
) : m('div', {
|
||||||
|
class: 'username ' + op.color + ' ' + classSet({
|
||||||
|
'statused': op.statused,
|
||||||
|
'connected': op.connected,
|
||||||
|
'offline': !op.connected
|
||||||
|
})
|
||||||
|
},
|
||||||
|
op.user ? [
|
||||||
|
m('a', {
|
||||||
|
class: 'user_link ulpt',
|
||||||
|
href: ctrl.router.User.show(op.user.username).url,
|
||||||
|
target: round.playable(ctrl.data) ? '_blank' : null,
|
||||||
|
'data-icon': 'r',
|
||||||
|
}, [
|
||||||
|
(op.user.title ? op.user.title + ' ' : '') + op.user.username,
|
||||||
|
op.engine ? m('span[data-icon=j]', {
|
||||||
|
title: ctrl.trans('thisPlayerUsesChessComputerAssistance')
|
||||||
|
}) : null
|
||||||
|
]),
|
||||||
|
m('span.status')
|
||||||
|
] : m('span.user_link', [
|
||||||
|
'Anonymous',
|
||||||
|
m('span.status')
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTableEnd(ctrl) {}
|
||||||
|
|
||||||
|
function renderButton(ctrl, condition, icon, hint, socketMsg) {
|
||||||
|
return condition(ctrl.data) ? m('button', {
|
||||||
|
class: 'button hint--bottom',
|
||||||
|
'data-hint': ctrl.trans(hint),
|
||||||
|
onclick: partial(ctrl.socket.send, socketMsg, null)
|
||||||
|
}, m('span[data-icon=' + icon + ']')) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTablePlay(ctrl) {
|
||||||
|
var d = ctrl.data;
|
||||||
|
return [
|
||||||
|
m('div.lichess_current_player',
|
||||||
|
m('div.lichess_player', [
|
||||||
|
m('div.cg-piece.king.' + d.game.player),
|
||||||
|
m('p', ctrl.trans(d.game.player == d.player.color ? 'yourTurn' : 'waiting'))
|
||||||
|
])
|
||||||
|
),
|
||||||
|
m('div.lichess_control.icons', [
|
||||||
|
renderButton(ctrl, round.abortable, 'L', 'abortGame', 'abort'),
|
||||||
|
renderButton(ctrl, round.takebackable, 'i', 'proposeATakeback', 'takeback-yes'),
|
||||||
|
renderButton(ctrl, round.drawable, '2', 'offerDraw', 'draw-yes'),
|
||||||
|
renderButton(ctrl, round.resignable, 'b', 'resign', 'resign')
|
||||||
|
]),
|
||||||
|
d.player.isOfferingDraw ? m('div.negociation', [
|
||||||
|
ctrl.trans('drawOfferSent') + ' ',
|
||||||
|
m('a', {
|
||||||
|
onclick: partial(ctrl.socket.send, 'draw-no', null)
|
||||||
|
}, ctrl.trans('cancel'))
|
||||||
|
]) : null,
|
||||||
|
d.opponent.isOfferingDraw ? m('div.negociation', [
|
||||||
|
ctrl.trans('yourOpponentOffersADraw'),
|
||||||
|
m('br'),
|
||||||
|
m('a.button[data-icon=E]', {
|
||||||
|
onclick: partial(ctrl.socket.send, 'draw-yes', null)
|
||||||
|
}, ctrl.trans('accept')),
|
||||||
|
m.trust(' '),
|
||||||
|
m('a.button[data-icon=L]', {
|
||||||
|
onclick: partial(ctrl.socket.send, 'draw-no', null)
|
||||||
|
}, ctrl.trans('decline')),
|
||||||
|
]) : null,
|
||||||
|
d.player.isProposingTakeback ? m('div.negociation', [
|
||||||
|
ctrl.trans('takebackPropositionSent') + ' ',
|
||||||
|
m('a', {
|
||||||
|
onclick: partial(ctrl.socket.send, 'takeback-no', null)
|
||||||
|
}, ctrl.trans('cancel'))
|
||||||
|
]) : null,
|
||||||
|
d.opponent.isProposingTakeback ? m('div.negociation', [
|
||||||
|
ctrl.trans('yourOpponentProposesATakeback'),
|
||||||
|
m('br'),
|
||||||
|
m('a.button[data-icon=E]', {
|
||||||
|
onclick: partial(ctrl.socket.send, 'takeback-yes', null)
|
||||||
|
}, ctrl.trans('accept')),
|
||||||
|
m.trust(' '),
|
||||||
|
m('a.button[data-icon=L]', {
|
||||||
|
onclick: partial(ctrl.socket.send, 'takeback-no', null)
|
||||||
|
}, ctrl.trans('decline')),
|
||||||
|
]) : null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = function(ctrl) {
|
module.exports = function(ctrl) {
|
||||||
var clockRunningColor = ctrl.isClockRunning() ? ctrl.data.game.player : null;
|
var clockRunningColor = ctrl.isClockRunning() ? ctrl.data.game.player : null;
|
||||||
return m('div', {
|
return m('div', {
|
||||||
|
config: function(el, isUpdate, context) {
|
||||||
|
if (isUpdate) return;
|
||||||
|
$('body').trigger('lichess.content_loaded');
|
||||||
|
},
|
||||||
class: 'lichess_game clearfix not_spectator pov_' + ctrl.data.player.color
|
class: 'lichess_game clearfix not_spectator pov_' + ctrl.data.player.color
|
||||||
}, [
|
}, [
|
||||||
ctrl.data.blindMode ? m('div#lichess_board_blind') : null,
|
ctrl.data.blindMode ? m('div#lichess_board_blind') : null,
|
||||||
|
@ -24,7 +122,13 @@ module.exports = function(ctrl) {
|
||||||
'table_with_clock': ctrl.clock,
|
'table_with_clock': ctrl.clock,
|
||||||
'finished': ctrl.data.game.finished
|
'finished': ctrl.data.game.finished
|
||||||
})
|
})
|
||||||
}), (ctrl.clock && !ctrl.data.blindMode) ? clockView(ctrl.clock, ctrl.data.player.color, "bottom", clockRunningColor) : null,
|
}, [
|
||||||
|
m('div.lichess_opponent', renderOpponent(ctrl)),
|
||||||
|
m('div.lichess_separator'),
|
||||||
|
m('div.table_inner',
|
||||||
|
round.playable(ctrl.data) ? renderTablePlay(ctrl) : renderTableEnd(ctrl)
|
||||||
|
)
|
||||||
|
]), (ctrl.clock && !ctrl.data.blindMode) ? clockView(ctrl.clock, ctrl.data.player.color, "bottom", clockRunningColor) : null,
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
|
18
ui/round/src/xhr.js
Normal file
18
ui/round/src/xhr.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
var m = require('mithril');
|
||||||
|
|
||||||
|
var xhrConfig = function(xhr) {
|
||||||
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||||
|
xhr.setRequestHeader('Accept', 'application/vnd.lichess.v1+json');
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload(data) {
|
||||||
|
return m.request({
|
||||||
|
method: 'GET',
|
||||||
|
url: data.url.round,
|
||||||
|
config: xhrConfig
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
reload: reload
|
||||||
|
};
|
Loading…
Reference in a new issue