diff --git a/README.md b/README.md index 92df2f1ba9..8005a0684a 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,9 @@ name | type | default | description --- | --- | --- | --- **username** | string | - | filter games by user **rated** | 1 or 0 | - | filter rated or casual games +**analysed** | 1 or 0 | - | filter only analysed (or not analysed) games **nb** | int | 10 | maximum number of games to return +**with_analysis** | 1 or 0 | 0 | include deep analysis data in the result **token** | string | - | security token (unlocks secret game data) ``` @@ -143,7 +145,7 @@ name | type | default | description { "id": "x2kpaixn", "rated": false, - "status": "mate", + "status": "mate", // (1) "clock":{ // all clock values are expressed in seconds "limit": 300, "increment": 8, @@ -165,36 +167,7 @@ name | type | default | description }, "black": ... // other player } - }, - { - ... // other game - } - ] -} -``` - -(1) All game statuses: https://github.com/ornicar/scalachess/blob/master/src/main/scala/Status.scala#L16-L25 - -### `GET /api/analysis` fetch many analysis - -This API requires a secret token to work. -Analysis are returned by descendant chronological order. -All parameters are optional. - -name | type | default | description ---- | --- | --- | --- -**token** | string | - | security token -**nb** | int | 10 | maximum number of analysis to return - -``` -> curl http://en.lichess.org/api/analysis?nb=10 -``` - -```javascript -{ - "list": [ - { - "analysis": [ + "analysis": [ // only if the with_analysis flag is set { "eval": -26, // board evaluation in centipawns "move": "e4", @@ -214,15 +187,81 @@ name | type | default | description }, // ... more moves ], - "game": { - // similar to the game API format, see above - }, - "uci": "e2e4 e7e5 d2d4 e5d4 g1f3 g8f6" // UCI compatible game moves + }, + { + ... // other game } ] } ``` +(1) All game statuses: https://github.com/ornicar/scalachess/blob/master/src/main/scala/Status.scala#L16-L25 + +### `GET /api/game/{id}` fetch one game by ID + +A single game is returned. +All parameters are optional. + +name | type | default | description +--- | --- | --- | --- +**with_analysis** | 1 or 0 | 0 | include deep analysis data in the result +**token** | string | - | security token (unlocks secret game data) + +``` +> curl http://en.lichess.org/api/game/x2kpaixn +``` + +```javascript +{ + "id": "x2kpaixn", + "rated": false, + "status": "mate", // (1) + "clock":{ // all clock values are expressed in seconds + "limit": 300, + "increment": 8, + "totalTime": 540 // evaluation of the game duration = limit + 30 * increment + }, + "timestamp": 1389100907239, + "turns": 44, + "url": "http://lichess.org/x2kpaixn", + "winner": "black", + "players": { + "white": { + "userId": "thibault" + "rating": 1642, + "analysis": { + "blunder": 1, + "inaccuracy": 0, + "mistake": 2 + } + }, + "black": ... // other player + }, + "analysis": [ // only if the with_analysis flag is set + { + "eval": -26, // board evaluation in centipawns + "move": "e4", + "ply": 1 + }, + { + "eval": -8, + "move": "b5", + "ply": 2 + }, + { + "comment": "(-0.08 → -0.66) Inaccuracy. The best move was c4.", + "eval": -66, + "move": "Nfe3", + "ply": 3, + "variation": "c4 bxc4 Nfe3 c5 Qf1 f6 Rxc4 Bb7 b4 Ba6" + }, + // ... more moves + ] +} +``` + +(1) All game statuses: https://github.com/ornicar/scalachess/blob/master/src/main/scala/Status.scala#L16-L25 + ### `GET /api/puzzle/` fetch one puzzle ``` diff --git a/app/controllers/Api.scala b/app/controllers/Api.scala index 03d547ec0e..69bd9ed3ca 100644 --- a/app/controllers/Api.scala +++ b/app/controllers/Api.scala @@ -9,7 +9,6 @@ object Api extends LilaController { private val userApi = Env.api.userApi private val gameApi = Env.api.gameApi - private val analysisApi = Env.api.analysisApi private val puzzleApi = Env.api.puzzleApi def user(username: String) = ApiResult { req => @@ -21,7 +20,7 @@ object Api extends LilaController { def users = ApiResult { req => userApi.list( team = get("team", req), - engine = get("engine", req) map ("1"==), + engine = getBoolOpt("engine", req), token = get("token", req), nb = getInt("nb", req) ) map (_.some) @@ -30,17 +29,19 @@ object Api extends LilaController { def games = ApiResult { req => gameApi.list( username = get("username", req), - rated = get("rated", req) map ("1"==), + rated = getBoolOpt("rated", req), + analysed = getBoolOpt("analysed", req), + withAnalysis = getBool("with_analysis", req), token = get("token", req), nb = getInt("nb", req) ) map (_.some) } - def analysis = ApiResult { req => - analysisApi.list( - nb = getInt("nb", req), - token = get("token", req) - ) map (_.some) + def game(id: String) = ApiResult { req => + gameApi.one( + id = id take lila.game.Game.gameIdSize, + withAnalysis = getBool("with_analysis", req), + token = get("token", req)) } def puzzle(id: String) = ApiResult { req => diff --git a/app/controllers/Auth.scala b/app/controllers/Auth.scala index 37e035b55e..85835ce94f 100644 --- a/app/controllers/Auth.scala +++ b/app/controllers/Auth.scala @@ -64,7 +64,7 @@ object Auth extends LilaController { BadRequest(html.auth.signup(err, captcha)) }, data => Firewall { - UserRepo.create(data.username, data.password) flatMap { userOption => + UserRepo.create(data.username, data.password, ctx.blindMode) flatMap { userOption => val user = userOption err "No user could be created for %s".format(data.username) HistoryRepo.create(user) >> gotoSignupSucceeded(user.username) } diff --git a/app/controllers/LilaController.scala b/app/controllers/LilaController.scala index 60cdcbba28..bbe0c50350 100644 --- a/app/controllers/LilaController.scala +++ b/app/controllers/LilaController.scala @@ -188,7 +188,7 @@ private[controllers] trait LilaController } private def pageDataBuilder(ctx: lila.user.UserContext): Fu[PageData] = - ctx.me.fold(fuccess(PageData.default)) { me => + ctx.me.fold(fuccess(PageData anon blindMode(ctx.req))) { me => val isPage = HTTPRequest.isSynchronousHttp(ctx.req) (Env.pref.api getPref me) zip { isPage ?? { @@ -203,11 +203,13 @@ private[controllers] trait LilaController } } map { case (pref, ((friends, teamNbRequests), messageIds)) => - val blindMode = ctx.req.cookies.get(Env.api.accessibilityBlindCookieName).map(_.value) == "1".some - PageData(friends, teamNbRequests, messageIds.size, pref, blindMode) + PageData(friends, teamNbRequests, messageIds.size, pref, blindMode(ctx.req)) } } + private def blindMode(req: RequestHeader) = + req.cookies.get(Env.api.accessibilityBlindCookieName).map(_.value) == "1".some + private def restoreUser(req: RequestHeader): Fu[Option[UserModel]] = Env.security.api restoreUser req addEffect { _ ifTrue (HTTPRequest isSynchronousHttp req) foreach { user => diff --git a/app/controllers/RequestGetter.scala b/app/controllers/RequestGetter.scala index 5413042f87..85ca30515a 100644 --- a/app/controllers/RequestGetter.scala +++ b/app/controllers/RequestGetter.scala @@ -23,4 +23,7 @@ trait RequestGetter { protected def getBool(name: String, req: RequestHeader) = getInt(name, req) exists (1==) + + protected def getBoolOpt(name: String, req: RequestHeader) = + getInt(name, req) map (1==) } diff --git a/app/templating/AssetHelper.scala b/app/templating/AssetHelper.scala index afc5633684..fb84bf2071 100644 --- a/app/templating/AssetHelper.scala +++ b/app/templating/AssetHelper.scala @@ -10,9 +10,11 @@ trait AssetHelper { def isProd: Boolean - private val domain = lila.api.Env.current.Net.AssetDomain + val assetDomain = lila.api.Env.current.Net.AssetDomain - def staticUrl(path: String) = s"http://$domain${routes.Assets.at(path)}" + val assetBaseUrl = s"http://$assetDomain" + + def staticUrl(path: String) = s"$assetBaseUrl${routes.Assets.at(path)}" def cssTag(name: String, staticDomain: Boolean = true) = cssAt("stylesheets/" + name, staticDomain) diff --git a/app/views/base/captcha.scala.html b/app/views/base/captcha.scala.html index 357791bb6f..6836272cc8 100644 --- a/app/views/base/captcha.scala.html +++ b/app/views/base/captcha.scala.html @@ -1,5 +1,9 @@ @(move: Field, gameId: Field, captcha: lila.common.Captcha)(implicit ctx: Context) + +@if(ctx.blindMode) { + +} else {
+} diff --git a/app/views/base/layout.scala.html b/app/views/base/layout.scala.html index 1d2cddb40a..58da9827d5 100644 --- a/app/views/base/layout.scala.html +++ b/app/views/base/layout.scala.html @@ -50,10 +50,10 @@ openGraph: Map[Symbol, String] = Map.empty)(body: Html)(implicit ctx: Context)
@trans.freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents()
@if(!zen) { - + } @if(!zen) { @siteMenu.all(ctx.me).map { elem => diff --git a/app/views/board/editor.scala.html b/app/views/board/editor.scala.html index 9c6052e186..c52070d84a 100644 --- a/app/views/board/editor.scala.html +++ b/app/views/board/editor.scala.html @@ -16,7 +16,7 @@ title = trans.boardEditor.str(), menu = game.sideMenu(listMenu, "edit").some, moreJs = moreJs, moreCss = moreCss) { -
+
@trans.startPosition() diff --git a/app/views/coordinate/home.scala.html b/app/views/coordinate/home.scala.html index 4f3c15e85d..b4cd8418c1 100644 --- a/app/views/coordinate/home.scala.html +++ b/app/views/coordinate/home.scala.html @@ -1,5 +1,7 @@ @(scoreOption: Option[lila.coordinate.Score])(implicit ctx: Context) +@import lila.pref.Pref.Color + @moreCss = { @cssTag("training.css") @cssTag("coordinate.css") @@ -50,11 +52,11 @@ active = siteMenu.puzzle.some) { }
- @lila.pref.Pref.Color.choices.map { + @List(Color.BLACK -> trans.black(), Color.RANDOM -> trans.randomColor(), Color.WHITE -> trans.white()).map { case (id, name) => { - + } }
diff --git a/app/views/puzzle/playMode.scala.html b/app/views/puzzle/playMode.scala.html index 73f7a45fb0..7ff87ca373 100644 --- a/app/views/puzzle/playMode.scala.html +++ b/app/views/puzzle/playMode.scala.html @@ -6,7 +6,8 @@ data-color="@puzzle.color" data-move="@puzzle.initialMove" data-lines="@lila.puzzle.Line.toJsonString(puzzle.lines)" - data-post-url="@routes.Puzzle.attempt(puzzle.id)"> + data-post-url="@routes.Puzzle.attempt(puzzle.id)" + data-asset-url="@assetBaseUrl">
@trainingBox(puzzle, userInfos, true) diff --git a/app/views/puzzle/viewMode.scala.html b/app/views/puzzle/viewMode.scala.html index 38ca90fb19..d5be48ed31 100644 --- a/app/views/puzzle/viewMode.scala.html +++ b/app/views/puzzle/viewMode.scala.html @@ -6,7 +6,8 @@ data-color="@puzzle.color" data-move="@puzzle.initialMove" data-lines="@lila.puzzle.Line.toJsonString(puzzle.lines)" - data-new-url="@routes.Puzzle.newPuzzle"> + data-new-url="@routes.Puzzle.newPuzzle" + data-asset-url="@assetBaseUrl">
@trainingBox(puzzle, userInfos, false) diff --git a/app/views/round/table/playing.scala.html b/app/views/round/table/playing.scala.html index cc24e30380..884bb17aa6 100644 --- a/app/views/round/table/playing.scala.html +++ b/app/views/round/table/playing.scala.html @@ -14,23 +14,23 @@
@if(game.abortable) { - + } else { @if(takebackable && game.playerCanProposeTakeback(color)) { - + } @if(game.playerCanOfferDraw(color)) { - + } - + }
@if(game.resignable && !game.hasAi) { diff --git a/app/views/round/textualRepresentation.scala.html b/app/views/round/textualRepresentation.scala.html index d37cb22a7a..7a25f75605 100644 --- a/app/views/round/textualRepresentation.scala.html +++ b/app/views/round/textualRepresentation.scala.html @@ -5,7 +5,7 @@
Turn
@pov.game.turns
PGN
-
@pov.game.pgnMoves.mkString(" ")
+
@Html(pov.game.pgnMoves.mkString("
"))
FEN
@{chess.format.Forsyth.>>(pov.game.toChess)}
Your color
@@ -25,8 +25,8 @@ + } diff --git a/bin/mongodb/game-analysed.js b/bin/mongodb/game-analysed.js new file mode 100644 index 0000000000..f28cdea443 --- /dev/null +++ b/bin/mongodb/game-analysed.js @@ -0,0 +1,4 @@ +var games = db.game5; +db.analysis2.find({},{_id:true}).forEach(function(analysis) { + games.update({_id: analysis._id},{$set:{an:true}}); +}); diff --git a/cljs/puzzle/externs/misc.js b/cljs/puzzle/externs/misc.js index 0a1eb593cb..60366aef20 100644 --- a/cljs/puzzle/externs/misc.js +++ b/cljs/puzzle/externs/misc.js @@ -5,7 +5,8 @@ var Chess = { load: function(fen) {}, fen: function() {}, turn: function() {}, - in_check: function() {} + in_check: function() {}, + undo: function() {} }; jQuery.prototype.sparkline = function(points, options) {}; jQuery.displayBoardMarks = function(board, white) {}; diff --git a/cljs/puzzle/src/core.cljs b/cljs/puzzle/src/core.cljs index a8307579b4..5d50396064 100644 --- a/cljs/puzzle/src/core.cljs +++ b/cljs/puzzle/src/core.cljs @@ -22,13 +22,12 @@ (jq/add-class ($ squares $puzzle) :last) (when $check (jq/add-class $check :check))))) -(defn make-chessboard [config] - (let [static-domain (str "http://" (clojure.string/replace (.-domain js/document) #"^\w+" "static")) - piece-set (jq/data ($ :body) :piece-set)] +(defn make-chessboard [config asset-url] + (let [piece-set (jq/data ($ :body) :piece-set)] (new js/ChessBoard "chessboard" (clj->js (merge {:sparePieces false :showNotation false - :pieceTheme (str static-domain "/assets/piece/" piece-set "/{piece}.svg")} + :pieceTheme (str asset-url "/assets/piece/" piece-set "/{piece}.svg")} config))))) (defn board-marks! [$puzzle] diff --git a/cljs/puzzle/src/play.cljs b/cljs/puzzle/src/play.cljs index 4a5a22d737..0b8e944b69 100644 --- a/cljs/puzzle/src/play.cljs +++ b/cljs/puzzle/src/play.cljs @@ -13,13 +13,14 @@ (defn on-drop! [orig, dest] (if (core/apply-move chess orig dest) (put! drop-chan (str orig dest)) "snapback")) -(defn make-chessboard [$puzzle fen] +(defn make-chessboard [$puzzle asset-url fen] (core/make-chessboard {:orientation (jq/data $puzzle :color) :position fen :moveSpeed animation-delay :draggable true :dropOffBoard "snapback" - :onDrop on-drop!})) + :onDrop on-drop!} + asset-url)) (defn show-turn! [$puzzle] (let [color (if (= (.turn chess) "w") "white" "black") @@ -106,9 +107,10 @@ (defn run! [] (let [$puzzle ($ :#puzzle) + assets (jq/data $puzzle :asset-url) lines (js->clj (jq/data $puzzle :lines)) initial-fen (jq/data $puzzle :fen) - chessboard (make-chessboard $puzzle initial-fen) + chessboard (make-chessboard $puzzle assets initial-fen) started-at (new js/Date)] (core/center-right! ($ :.right $puzzle)) (core/board-marks! $puzzle) diff --git a/cljs/puzzle/src/view.cljs b/cljs/puzzle/src/view.cljs index c823c2e460..35dd752ea0 100644 --- a/cljs/puzzle/src/view.cljs +++ b/cljs/puzzle/src/view.cljs @@ -9,10 +9,11 @@ (def continue-chan (async/chan)) (def animation-delay 200) -(defn make-chessboard [$puzzle] +(defn make-chessboard [$puzzle asset-url] (core/make-chessboard {:orientation (jq/data $puzzle :color) :moveSpeed animation-delay - :draggable false})) + :draggable false} + asset-url)) (defn bind-vote! [$vote] (jq/on $vote :click ".enabled a:not(.active)" @@ -72,6 +73,7 @@ (defn run! [progress] (let [$puzzle ($ :#puzzle) + assets (jq/data $puzzle :asset-url) $browse ($ :#GameButtons $puzzle) $first ($ :.first $browse) $prev ($ :.prev $browse) @@ -80,7 +82,7 @@ lines (js->clj (jq/data $puzzle :lines)) line (find-best-line-from-progress lines progress) history (vec (make-history (jq/data $puzzle :fen) (conj (seq line) (jq/data $puzzle :move)))) - chessboard (make-chessboard $puzzle)] + chessboard (make-chessboard $puzzle assets)] (core/center-right! ($ :.right $puzzle)) (core/board-marks! $puzzle) (core/buttons! $puzzle) diff --git a/conf/messages.de b/conf/messages.de index a1eab18c0d..a951c22049 100644 --- a/conf/messages.de +++ b/conf/messages.de @@ -28,7 +28,7 @@ yourOpponentWantsToPlayANewGameWithYou=Dein Gegner möchte eine neue Partie mit joinTheGame=Der Partie beitreten whitePlays=Weiß am Zug blackPlays=Schwarz am Zug -theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim=Dein Gegner hat die Partie verlassen. Du kannst den Sieg reklamieren oder auf deinen Gegner warten. +theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim=Dein Gegner hat die Partie verlassen. Du kannst den Sieg reklamieren, dass Spiel remis erklären oder auf deinen Gegner warten. makeYourOpponentResign=Bringe deinen Gegner zur Aufgabe forceResignation=Sieg reklamieren forceDraw=Remis erzwingen @@ -135,7 +135,7 @@ nbWins=%s Siege nbLosses=%s Niederlagen nbDraws=%s Remis exportGames=Spiele exportieren -ratingRange=Elo-Bereich +ratingRange=Rating-Bereich giveNbSeconds=%s Sekunden geben premoveEnabledClickAnywhereToCancel=Vorauszug aktiviert - Zum Abbrechen irgendwo klicken thisPlayerUsesChessComputerAssistance=Dieser Spieler verwendet einen Schachcomputer als Hilfe diff --git a/conf/messages.sk b/conf/messages.sk index c036b84dc3..3cc117947a 100644 --- a/conf/messages.sk +++ b/conf/messages.sk @@ -50,7 +50,7 @@ theComputerAnalysisHasFailed=Počítačová analýza zlyhala viewTheComputerAnalysis=Zobraziť počítačovú analýzu requestAComputerAnalysis=Požiadať o počítačovú analýzu computerAnalysis=Počítačová anylýza -blunders=Hrubyé chyby +blunders=Hrubé chyby mistakes=Chyby inaccuracies=Nepresnosti moveTimes=Trvanie ťahu @@ -65,7 +65,7 @@ viewAllNbGames=Zobraziť všetkých %s hier viewNbCheckmates=Zobraziť %s šach-maty nbBookmarks=%s Záložky nbPopularGames=%s Populárne hry -nbAnalysedGames=%s Analyzovaných Hier +nbAnalysedGames=%s Analyzovaných hier bookmarkedByNbPlayers=Záložku uložilo %s hráčov viewInFullSize=Zobraziť v plnej veľkosti logOut=Odhlásiť sa @@ -73,7 +73,7 @@ signIn=Prihlásiť sa newToLichess=Nováčik na Lichess? youNeedAnAccountToDoThat=Pre túto požiadavku musíš byť prihlásený signUp=Zaregistrovať sa -computersAreNotAllowedToPlay=I programmi scacchistici o l'ausilio di programmi scacchistici nono sono autorizzati,per cortesia evitate l'uso di programmi,databases,o i suggerimenti di altri giocatori durante una partita +computersAreNotAllowedToPlay=Hráči využívajúci pomoc počítačov nie sú vítaní. Počas partie nepoužívajte šachové programy, databázy ani rady iných hráčov. games=Hry forum=Forum xPostedInForumY=%s príspevkov na fóre %s @@ -124,7 +124,7 @@ inbox=Správy chatRoom=Chatovacia miestnosť spectatorRoom=Divácka miestnosť composeMessage=Napísať správu -noNewMessages=Žiadne správy momentálne +noNewMessages=Žiadne nové správy subject=Predmet recipient=Príjemca send=Poslať @@ -156,7 +156,7 @@ tournamentPoints=Turnajové body viewTournament=Zobraz turnaj backToTournament=Návrat do turnaja xTournament=%s turnaj -freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents=Šach online zadarmo. Hrajte šach teraz v novom rozhraní. Bez registrácie, reklám a zásuvných modulov. Hraj šach s počítačom, priateľmi alebo náhodnými protivníkmi. +freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents=Šach online zadarmo. Hrajte šach teraz v novom rozhraní. Bez registrácie, reklám a zásuvných modulov. Hrajte šach s počítačom, priateľmi alebo náhodnými protivníkmi. teams=Tímy nbMembers=%s členovia allTeams=Všetky tímy @@ -170,7 +170,7 @@ aConfirmationIsRequiredToJoin=Potvrdenie je potrebné na vstup joiningPolicy=Pravidlá vstupu teamLeader=Vecúci tímu teamBestPlayers=Najlepší hráči -teamRecentMembers=Nový členovia tímu +teamRecentMembers=Noví členovia tímu xJoinedTeamY=%s sa pripojil/la k tímu %s xCreatedTeamY=%s vytvoril/la tím %s averageElo=Priemerné Elo @@ -181,15 +181,15 @@ reset=Reset apply=Použiť leaderboard=Rebríček pasteTheFenStringHere=Vlož sem hodnotu FEN -pasteThePgnStringHere=Vlož sem PGN hodnotu +pasteThePgnStringHere=Vlož sem PGN text fromPosition=Od pozície continueFromHere=Pokračuj odtiaľto importGame=Import hry nbImportedGames=%s importovaných hier thisIsAChessCaptcha=Šachová CAPTCHA clickOnTheBoardToMakeYourMove=Kliknite na šachovnicu a spravte ťah, aby ste dokázali, že ste človek. -notACheckmate=Nie je šach mat -colorPlaysCheckmateInOne=%s hrá; šach mat na jeden ťah +notACheckmate=Nie je to mat +colorPlaysCheckmateInOne=%s na ťahu; mat prvým ťahom retry=Znova reconnecting=Pripájanie onlineFriends=Online pritelia @@ -221,14 +221,14 @@ required=Vyžaduje sa addToChrome=Pridať do Chrome openTournaments=Otvorené turnaje duration=Dĺžka -winner=Výťaz +winner=Víťaz standing=Pozícia createANewTournament=Vytvor nový turnaj join=Pripojiť withdraw=Odstúpiť points=Bodov -wins=Výťazi -losses=Porazený +wins=Víťazi +losses=Prehry winStreak=Séria výhier createdBy=Vytvorené waitingForNbPlayers=Čakanie na %s hráčov @@ -250,7 +250,7 @@ biography=Životopis country=Krajina preferences=Nastavenia watchLichessTV=Sleduj Lichess TV -previouslyOnLichessTV=Bolo na Liechess TV +previouslyOnLichessTV=Bolo na Lichess TV todaysLeaders=Dnešní lídri onlinePlayers=Pripojený hráči progressToday=Dnešný pokrok @@ -267,10 +267,10 @@ bestSlowPlayers=Najlepší hráči pomalých hier bewareTheGameIsRatedButHasNoClock=Pozor, hra je bodovaná, ale bez času! training=Tréning yourPuzzleRatingX=Hodnotenie z : %s -findTheBestMoveForWhite=Trova la miglior mossa del bianco -findTheBestMoveForBlack=Trova la miglior mossa del nero +findTheBestMoveForWhite=Nájdite za bieleho najlepší ťah. +findTheBestMoveForBlack=Nájdite za čierneho najlepší ťah. toTrackYourProgress=Sledovať svoj pokrok: -trainingSignupExplanation=Lichess ponúka šachové rébusy, ktoré zlepšujú vašu šikovnosť pre ďalšie šachové partie . +trainingSignupExplanation=Lichess ponúka šachové rébusy, ktoré zlepšujú vašu šikovnosť pre ďalšie šachové partie. recentlyPlayedPuzzles=Nedávno hrané rébusy puzzleId=Rébus %s puzzleOfTheDay=Rébus dňa @@ -280,16 +280,16 @@ butYouCanDoBetter=Ale môžete to spraviť lepšie. bestMove=Najlepší ťah! keepGoing=Pokračujte... puzzleFailed=Nesprávne vyriešený rébus -butYouCanKeepTrying=Napriek tomu môžte pokračovať. +butYouCanKeepTrying=Napriek tomu môžete pokračovať. victory=Výhra! giveUp=Vzdať sa puzzleSolvedInXSeconds=Rébus vyriešený za %s sekúnd. -wasThisPuzzleAnyGood=Bol rébus správny? +wasThisPuzzleAnyGood=Bol tento rébus dobrý? pleaseVotePuzzle=Pomôžte zlepšiť lichess hlasovaním, kliknutím na šípku hore alebo dole: thankYou=Ďakujem! ratingX=Hodnotenie: %s playedXTimes=Hraný %s krát -fromGameLink=Pre hru %s +fromGameLink=Z hry %s startTraining=Začať tréning continueTraining=Pokračovanie v tréningu retryThisPuzzle=Opakovať tento rébus diff --git a/conf/messages.zh b/conf/messages.zh index 3940ab1911..311d0a56a9 100644 --- a/conf/messages.zh +++ b/conf/messages.zh @@ -73,6 +73,7 @@ signIn=登录 newToLichess=新来的吗 youNeedAnAccountToDoThat=你需要先登入帐号 signUp=注册 +computersAreNotAllowedToPlay=电脑及电脑辅助队员不准玩。请不要从国际象棋引擎,数据库,或者从其他玩家边玩得到帮助 games=棋 forum=论坛 xPostedInForumY=%s 张贴在论坛 %s @@ -266,12 +267,27 @@ bestSlowPlayers=最佳慢棋棋手 bewareTheGameIsRatedButHasNoClock=注意,这局是评分的但不计时 training=训练 yourPuzzleRatingX=您的难题评级:%s +findTheBestMoveForWhite=找到白色的最好的移动 +findTheBestMoveForBlack=找到黑色的最好的移动 toTrackYourProgress=跟踪你的进度: trainingSignupExplanation=Lichess将提供符合您的能力,从而为更好地训练难题。 recentlyPlayedPuzzles=最近在玩拼图 puzzleId=谜 %s +puzzleOfTheDay=一天之谜 +clickToSolve=点击解决 goodMove=好着 butYouCanDoBetter=你可以做的更好 bestMove=最理想的做法 +keepGoing=继续下去... +puzzleFailed=失败之谜 +butYouCanKeepTrying=但是你可以不断尝试 victory=胜利! +giveUp=放弃 +wasThisPuzzleAnyGood=为这个难题有什么好处? +pleaseVotePuzzle=通过投票帮助lichess提高,使用向上键或向下键: thankYou=谢谢! +startTraining=开始训练 +continueTraining=继续训练 +retryThisPuzzle=重试这个难题 +thisPuzzleIsCorrect=这个难题是正确的,有趣的 +thisPuzzleIsWrong=这个难题是错误的或无聊 diff --git a/conf/routes b/conf/routes index 710d626139..300cff3bb7 100644 --- a/conf/routes +++ b/conf/routes @@ -245,7 +245,7 @@ POST /report/:id/process controllers.Report.process(id: String) GET /api/user controllers.Api.users GET /api/user/:id controllers.Api.user(id: String) GET /api/game controllers.Api.games -GET /api/analysis controllers.Api.analysis +GET /api/game/:id controllers.Api.game(id: String) GET /api/puzzle/:id controllers.Api.puzzle(id: String) POST /api/puzzle controllers.Puzzle.importBatch diff --git a/modules/analyse/src/main/Analyser.scala b/modules/analyse/src/main/Analyser.scala index 628b1bbb99..01af251195 100644 --- a/modules/analyse/src/main/Analyser.scala +++ b/modules/analyse/src/main/Analyser.scala @@ -31,11 +31,6 @@ final class Analyser( if (a.stalled) (AnalysisRepo remove a.id) inject none[Analysis] else fuccess(a.some) } - def hasDone(id: String): Fu[Boolean] = getDone(id) map (_.isDefined) - - def hasMany(ids: Seq[String]): Fu[Set[String]] = - $primitive[Analysis, String]($select byIds ids, "_id")(_.asOpt[String]) map (_.toSet) - def getOrGenerate( id: String, userId: String, @@ -84,7 +79,7 @@ final class Analyser( game.userIds foreach { userId => evaluator ! lila.hub.actorApi.evaluation.Refresh(userId) } - } inject analysis + } >>- GameRepo.setAnalysed(game.id) inject analysis } else fufail(s"[analysis] invalid $id") }) diff --git a/modules/api/src/main/AnalysisApi.scala b/modules/api/src/main/AnalysisApi.scala deleted file mode 100644 index 215bc7549b..0000000000 --- a/modules/api/src/main/AnalysisApi.scala +++ /dev/null @@ -1,71 +0,0 @@ -package lila.api - -import chess.format.pgn.Pgn -import chess.format.UciDump -import chess.Replay -import play.api.libs.json._ - -import lila.analyse.{ Analysis, AnalysisRepo } -import lila.common.PimpedJson._ -import lila.db.api._ -import lila.db.Implicits._ -import lila.game.{ Game, GameRepo, PgnDump } -import lila.hub.actorApi.{ router => R } - -private[api] final class AnalysisApi( - apiToken: String, - makeUrl: Any => Fu[String], - nbAnalysis: () => Fu[Int], - pgnDump: PgnDump) { - - private def makeNb(nb: Option[Int]) = math.min(100, nb | 10) - - private def makeSkip = nbAnalysis() map { nb => scala.util.Random.nextInt(nb) } - - def list(nb: Option[Int], token: Option[String]): Fu[JsObject] = - if (~token != apiToken) fuccess(Json.obj("oh" -> "bummer")) - else makeSkip flatMap { skip => - AnalysisRepo.skipping(skip, makeNb(nb)) flatMap { as => - GameRepo games as.map(_.id) flatMap { games => - games.map { g => - as find (_.id == g.id) map { _ -> g } - }.flatten.map { - case (a, g) => GameRepo initialFen g.id flatMap { initialFen => - pgnDump(g) zip makeUrl(R.Watcher(g.id, g.firstPlayer.color.name)) map { - case (pgn, url) => (g, a, url, pgn, initialFen) - } - } - }.sequenceFu map { tuples => - Json.obj( - "list" -> JsArray(tuples map { - case (game, analysis, url, pgn, fen) => Json.obj( - "game" -> (GameApi.gameToJson(game, url, analysis.some) ++ { - fen ?? { f => Json.obj("initialFen" -> f) } - }), - "analysis" -> AnalysisApi.analysisToJson(analysis, pgn), - "uci" -> uciMovesOf(game, fen).map(_.mkString(" ")) - ).noNull - }) - ) - } - } - } - } - - private def uciMovesOf(game: Game, initialFen: Option[String]): Option[List[String]] = - Replay(game.pgnMoves mkString " ", initialFen, game.variant).toOption map UciDump.apply -} - -private[api] object AnalysisApi { - - def analysisToJson(analysis: Analysis, pgn: Pgn) = JsArray(analysis.infoAdvices zip pgn.moves map { - case ((info, adviceOption), move) => Json.obj( - "ply" -> info.ply, - "move" -> move.san, - "eval" -> info.score.map(_.centipawns), - "mate" -> info.mate, - "variation" -> info.variation.isEmpty.fold(JsNull, info.variation mkString " "), - "comment" -> adviceOption.map(_.makeComment(true, true)) - ).noNull - }) -} diff --git a/modules/api/src/main/Context.scala b/modules/api/src/main/Context.scala index 7fbb96b229..e7b43dcf8f 100644 --- a/modules/api/src/main/Context.scala +++ b/modules/api/src/main/Context.scala @@ -14,7 +14,10 @@ case class PageData( blindMode: Boolean) object PageData { + val default = PageData(Nil, 0, 0, Pref.default, false) + + def anon(blindMode: Boolean) = default.copy(blindMode = blindMode) } sealed trait Context extends lila.user.UserContextWrapper { diff --git a/modules/api/src/main/Env.scala b/modules/api/src/main/Env.scala index 599f22a9ba..c80e707110 100644 --- a/modules/api/src/main/Env.scala +++ b/modules/api/src/main/Env.scala @@ -44,12 +44,6 @@ final class Env( val gameApi = new GameApi( makeUrl = apiUrl, apiToken = apiToken, - isOnline = userEnv.isOnline) - - val analysisApi = new AnalysisApi( - apiToken = apiToken, - makeUrl = apiUrl, - nbAnalysis = () => analyseEnv.cached.nbAnalysis, pgnDump = pgnDump) val puzzleApi = new PuzzleApi( diff --git a/modules/api/src/main/GameApi.scala b/modules/api/src/main/GameApi.scala index 026ded71e9..40fc76507c 100644 --- a/modules/api/src/main/GameApi.scala +++ b/modules/api/src/main/GameApi.scala @@ -2,58 +2,70 @@ package lila.api import play.api.libs.json._ +import chess.format.pgn.Pgn import lila.analyse.{ AnalysisRepo, Analysis } import lila.common.PimpedJson._ import lila.db.api._ import lila.db.Implicits._ -import lila.game.Game import lila.game.Game.{ BSONFields => G } import lila.game.tube.gameTube +import lila.game.{ Game, PgnDump } import lila.hub.actorApi.{ router => R } import makeTimeout.short private[api] final class GameApi( makeUrl: Any => Fu[String], apiToken: String, - isOnline: String => Boolean) { + pgnDump: PgnDump) { private def makeNb(nb: Option[Int]) = math.min(100, nb | 10) def list( username: Option[String], rated: Option[Boolean], + analysed: Option[Boolean], + withAnalysis: Boolean, token: Option[String], nb: Option[Int]): Fu[JsObject] = $find($query(Json.obj( G.status -> $gte(chess.Status.Mate.id), G.playerUids -> username, - G.rated -> rated.map(_.fold(JsBoolean(true), $exists(false))) - ).noNull) sort lila.game.Query.sortCreated, makeNb(nb)) flatMap { games => + G.rated -> rated.map(_.fold(JsBoolean(true), $exists(false))), + G.analysed -> analysed.map(_.fold(JsBoolean(true), $exists(false))) + ).noNull) sort lila.game.Query.sortCreated, makeNb(nb)) flatMap + gamesJson(withAnalysis, token) map { games => + Json.obj("list" -> games) + } + + def one( + id: String, + withAnalysis: Boolean, + token: Option[String]): Fu[Option[JsObject]] = + $find byId id map (_.toList) flatMap gamesJson(withAnalysis, token) map (_.headOption) + + private def gamesJson(withAnalysis: Boolean, token: Option[String])(games: List[Game]): Fu[List[JsObject]] = AnalysisRepo doneByIds games.map(_.id) flatMap { analysisOptions => - (games map { g => makeUrl(R.Watcher(g.id, g.firstPlayer.color.name)) }).sequenceFu map { urls => - val validToken = check(token) - Json.obj( - "list" -> JsArray( - games zip urls zip analysisOptions map { - case ((g, url), analysisOption) => - GameApi.gameToJson(g, url, analysisOption, - withBlurs = validToken, - withMoveTimes = validToken) - } - ) - ) + (games map { g => withAnalysis ?? (pgnDump(g) map (_.some)) }).sequenceFu flatMap { pgns => + (games map { g => makeUrl(R.Watcher(g.id, g.firstPlayer.color.name)) }).sequenceFu map { urls => + val validToken = check(token) + games zip urls zip analysisOptions zip pgns map { + case (((g, url), analysisOption), pgnOption) => + gameToJson(g, url, analysisOption, pgnOption, + withAnalysis = withAnalysis, + withBlurs = validToken, + withMoveTimes = validToken) + } + } } } - } private def check(token: Option[String]) = token ?? (apiToken==) -} -private[api] object GameApi { - - def gameToJson( + private def gameToJson( g: Game, url: String, analysisOption: Option[Analysis], + pgnOption: Option[Pgn], + withAnalysis: Boolean, withBlurs: Boolean = false, withMoveTimes: Boolean = false) = Json.obj( "id" -> g.id, @@ -84,6 +96,18 @@ private[api] object GameApi { ) ).noNull }), + "analysis" -> analysisOption.ifTrue(withAnalysis).|@|(pgnOption).apply { + case (analysis, pgn) => JsArray(analysis.infoAdvices zip pgn.moves map { + case ((info, adviceOption), move) => Json.obj( + "ply" -> info.ply, + "move" -> move.san, + "eval" -> info.score.map(_.centipawns), + "mate" -> info.mate, + "variation" -> info.variation.isEmpty.fold(JsNull, info.variation mkString " "), + "comment" -> adviceOption.map(_.makeComment(true, true)) + ).noNull + }) + }, "winner" -> g.winnerColor.map(_.name), "url" -> url ).noNull diff --git a/modules/chess b/modules/chess index 87a28a41a6..7b015665cd 160000 --- a/modules/chess +++ b/modules/chess @@ -1 +1 @@ -Subproject commit 87a28a41a6a9087a3af7af597cf9fedbe24c81ce +Subproject commit 7b015665cd7d97c61c38800e10202a33d1d0beb0 diff --git a/modules/game/src/main/Game.scala b/modules/game/src/main/Game.scala index 0e38f06946..42a52b8cca 100644 --- a/modules/game/src/main/Game.scala +++ b/modules/game/src/main/Game.scala @@ -423,6 +423,7 @@ object Game { val castleLastMoveTime = "cl" val moveTimes = "mt" val rated = "ra" + val analysed = "an" val variant = "v" val next = "ne" val bookmarks = "bm" diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala index a49d346a61..724cd1c16b 100644 --- a/modules/game/src/main/GameRepo.scala +++ b/modules/game/src/main/GameRepo.scala @@ -109,6 +109,18 @@ trait GameRepo { def onTv(nb: Int): Fu[List[Game]] = $find($query(Json.obj(F.tvAt -> $exists(true))) sort $sort.desc(F.tvAt), nb) + def setAnalysed(id: ID) { + $update.fieldUnchecked(id, F.analysed, true) + } + + private def selectAnalysed = Json.obj(F.analysed -> true) + + def isAnalysed(id: ID): Fu[Boolean] = + $count.exists($select(id) ++ selectAnalysed) + + def filterAnalysed(ids: Seq[String]): Fu[Set[String]] = + $primitive(($select byIds ids) ++ selectAnalysed, "_id")(_.asOpt[String]) map (_.toSet) + def incBookmarks(id: ID, value: Int) = $update($select(id), $incBson("bm" -> value)) diff --git a/modules/gameSearch/src/main/Env.scala b/modules/gameSearch/src/main/Env.scala index a3ca30ea6a..48c36f06a1 100644 --- a/modules/gameSearch/src/main/Env.scala +++ b/modules/gameSearch/src/main/Env.scala @@ -10,8 +10,7 @@ import lila.game.tube.gameTube final class Env( config: Config, system: ActorSystem, - client: ElasticClient, - analyser: lila.analyse.Analyser) { + client: ElasticClient) { private val IndexName = config getString "index" private val TypeName = config getString "type" @@ -21,8 +20,7 @@ final class Env( private val indexer: ActorRef = system.actorOf(Props(new Indexer( client = client, indexName = IndexName, - typeName = TypeName, - analyser = analyser + typeName = TypeName )), name = IndexerName) lazy val paginator = new lila.search.PaginatorBuilder( @@ -49,6 +47,5 @@ object Env { lazy val current = "[boot] gameSearch" describes new Env( config = lila.common.PlayApp loadConfig "gameSearch", system = lila.common.PlayApp.system, - client = lila.search.Env.current.client, - analyser = lila.analyse.Env.current.analyser) + client = lila.search.Env.current.client) } diff --git a/modules/gameSearch/src/main/Indexer.scala b/modules/gameSearch/src/main/Indexer.scala index c5d6258c76..91ddb76229 100644 --- a/modules/gameSearch/src/main/Indexer.scala +++ b/modules/gameSearch/src/main/Indexer.scala @@ -6,6 +6,7 @@ import com.sksamuel.elastic4s.ElasticClient import com.sksamuel.elastic4s.ElasticDsl._ import com.sksamuel.elastic4s.mapping.FieldType._ +import lila.game.GameRepo import lila.game.actorApi.{ InsertGame, FinishGame } import lila.search.actorApi._ import lila.search.ElasticSearch @@ -13,8 +14,7 @@ import lila.search.ElasticSearch private[gameSearch] final class Indexer( client: ElasticClient, indexName: String, - typeName: String, - analyser: lila.analyse.Analyser) extends Actor { + typeName: String) extends Actor { context.system.lilaBus.subscribe(self, 'finishGame) @@ -28,7 +28,7 @@ private[gameSearch] final class Indexer( case FinishGame(game, _, _) => self ! InsertGame(game) case InsertGame(game) => if (storable(game)) { - analyser hasDone game.id foreach { analysed => + GameRepo isAnalysed game.id foreach { analysed => client execute store(game, analysed) } } @@ -68,7 +68,7 @@ private[gameSearch] final class Indexer( $enumerate.bulk[Option[lila.game.Game]]($query.all, batchSize) { gameOptions => val games = gameOptions.flatten filter storable val nbGames = games.size - (analyser hasMany games.map(_.id).toSeq flatMap { analysedIds => + (GameRepo filterAnalysed games.map(_.id).toSeq flatMap { analysedIds => client bulk { games.map { g => store(g, analysedIds(g.id)) }: _* } diff --git a/modules/mod/src/main/Modlog.scala b/modules/mod/src/main/Modlog.scala index fa0b66505e..4001ea97cf 100644 --- a/modules/mod/src/main/Modlog.scala +++ b/modules/mod/src/main/Modlog.scala @@ -28,7 +28,7 @@ case class Modlog( case a => a } - override def toString = s"$mod $showAction $user" + override def toString = s"$mod $showAction ${~user}" } object Modlog { diff --git a/modules/round/src/main/Round.scala b/modules/round/src/main/Round.scala index bd594b801e..a146bff61a 100644 --- a/modules/round/src/main/Round.scala +++ b/modules/round/src/main/Round.scala @@ -128,14 +128,14 @@ private[round] final class Round( case Deploy(RemindDeployPost, _) => handle { game => game.clock.filter(_ => game.playable) ?? { clock => import chess.Color - val freeSeconds = 12 + val freeSeconds = 15 val newClock = clock.giveTime(Color.White, freeSeconds).giveTime(Color.Black, freeSeconds) val progress = (game withClock newClock) + Event.Clock(newClock) messenger.system(game, (_.untranslated("Deploy in progress"))) + messenger.system(game, (_.untranslated("Sorry for the inconvenience!"))) Color.all.foreach { c => messenger.system(game, (_.untranslated(s"$c + $freeSeconds seconds"))) } - messenger.system(game, (_.untranslated("Sorry for the inconvenience!"))) GameRepo save progress inject progress.events } } diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala index b212ee58ba..dccede8336 100644 --- a/modules/user/src/main/UserRepo.scala +++ b/modules/user/src/main/UserRepo.scala @@ -180,10 +180,10 @@ trait UserRepo { _ ?? (data => data.enabled && data.compare(password)) } - def create(username: String, password: String): Fu[Option[User]] = + def create(username: String, password: String, blind: Boolean): Fu[Option[User]] = !nameExists(username) flatMap { _ ?? { - $insert.bson(newUser(username, password)) >> named(normalize(username)) + $insert.bson(newUser(username, password, blind)) >> named(normalize(username)) } } @@ -276,7 +276,7 @@ trait UserRepo { def filterByEngine(userIds: List[String]): Fu[List[String]] = $primitive(Json.obj("_id" -> $in(userIds)) ++ engineSelect(true), F.username)(_.asOpt[String]) - private def newUser(username: String, password: String) = { + private def newUser(username: String, password: String, blind: Boolean) = { val salt = ornicar.scalalib.Random nextStringUppercase 32 val perfs = Perfs.default @@ -295,7 +295,9 @@ trait UserRepo { F.count -> Count.default, F.enabled -> true, F.createdAt -> DateTime.now, - F.seenAt -> DateTime.now) + F.seenAt -> DateTime.now) ++ { + if (blind) BSONDocument("blind" -> true) else BSONDocument() + } } def artificialSetPassword(id: String, password: String) = diff --git a/project/Build.scala b/project/Build.scala index 9843cbe0c3..a2c7f89e71 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -118,7 +118,7 @@ object ApplicationBuild extends Build { play.api, RM, PRM) ) - lazy val gameSearch = project("gameSearch", Seq(common, hub, chess, search, game, analyse)).settings( + lazy val gameSearch = project("gameSearch", Seq(common, hub, chess, search, game)).settings( libraryDependencies ++= provided( play.api, RM, PRM, elastic4s) ) diff --git a/public/javascripts/big.js b/public/javascripts/big.js index 79973a48f9..9c0b4a7ec4 100644 --- a/public/javascripts/big.js +++ b/public/javascripts/big.js @@ -506,7 +506,7 @@ var storage = { }); } - $('#lichess').on('click', 'a.socket-link:not(.disabled)', function() { + $('#lichess').on('click', '.socket-link:not(.disabled)', function() { lichess.socket.send($(this).data('msg'), $(this).data('data')); }); @@ -2517,7 +2517,7 @@ var storage = { return $plot.data('hook', hook).powerTip({ fadeInTime: 0, fadeOutTime: 0, - placement: 'ne', + placement: hook.rating > 2200 ? 'se' : 'ne', mouseOnToPopup: true, closeDelay: 200, intentPollInterval: 50, diff --git a/public/javascripts/boardEditor.js b/public/javascripts/boardEditor.js index 91b0487186..4eb16d1ed7 100644 --- a/public/javascripts/boardEditor.js +++ b/public/javascripts/boardEditor.js @@ -11,6 +11,7 @@ $(function() { bq: 'q' }; $wrap.find('.castling input').on('change', onChange); + var assetUrl = $wrap.data('asset-url'); function getRich() { return toRich(board.fen()); @@ -38,7 +39,7 @@ $(function() { }); } - var pieceTheme = 'http://' + document.domain.replace(/^\w+/, 'static') + '/assets/piece/' + $('body').data('piece-set') + '/{piece}.svg'; + var pieceTheme = assetUrl + '/assets/piece/' + $('body').data('piece-set') + '/{piece}.svg'; board = new ChessBoard('chessboard', { position: toBase($('#chessboard').data('fen')) || 'start', draggable: true, diff --git a/public/stylesheets/board.css b/public/stylesheets/board.css index d1cb94fc2e..09d7bdb56a 100644 --- a/public/stylesheets/board.css +++ b/public/stylesheets/board.css @@ -513,7 +513,7 @@ div.lichess_table div.username.connected span.status:before { div.lichess_table div.username a:hover { text-decoration: none; } -div.lichess_table a.button.strong { +div.lichess_table .button.strong { padding-top: 1em; padding-bottom: 1em; font-weight: bold; @@ -622,23 +622,26 @@ div.lichess_control div.rematch_alert .button { div.lichess_control div.rematch_wait { margin-bottom: 1em; } -div.lichess_control.icons a.button { +div.lichess_control.icons .button { font-size: 16px; - height: 22px; + height: 34px; + padding: 0 10px; + margin: 0 3px; } -div.lichess_control.icons a.takeback span:before { - vertical-align: -7px; -} -div.lichess_control.icons a.draw span:before { +div.lichess_control.icons .takeback span:before { display: inline-block; - transform: rotate(-90deg); + transform: translateY(1px); +} +div.lichess_control.icons .draw span:before { + display: inline-block; + transform: translateY(-1px) rotate(-90deg); -webkit-transform: rotate(-90deg); - vertical-align: -4px; } -div.lichess_control.icons a.resign span:before { - vertical-align: -6px; +div.lichess_control.icons .resign span:before { + display: inline-block; + transform: translateY(1px); } -div.lichess_control.buttons a.button { +div.lichess_control.buttons .button { display: block; margin-bottom: 0.7em; text-align: center; diff --git a/public/stylesheets/common.css b/public/stylesheets/common.css index 09d825277f..c26dc2651e 100644 --- a/public/stylesheets/common.css +++ b/public/stylesheets/common.css @@ -513,6 +513,10 @@ a.revert-underline { a.revert-underline:hover { text-decoration: underline; } +button.a { + border: none; + background: none; +} strong { font-weight: bold; } @@ -841,7 +845,7 @@ body.tight div.side_menu a { #reconnecting, a.goto_nav, #top a.toggle, -a#sound_state { +#sound_state { text-decoration: none; font-size: 13px; height: 24px; @@ -877,7 +881,7 @@ body.offline #nb_connected_players { } #top a.goto_nav:hover, #top a.toggle:hover, -a#sound_state:hover { +#sound_state:hover { color: #303030; border-color: #808080; } @@ -1723,7 +1727,7 @@ div.game_config div.color_submits button.random span { } #hooks_wrap table.list thead th { cursor: pointer; - padding: 13px 12px; + padding: 12.5px 12px; font-weight: lighter; border-bottom: 1px solid #ccc; } diff --git a/public/stylesheets/coordinate.css b/public/stylesheets/coordinate.css index 724d603595..2eeb2346d9 100644 --- a/public/stylesheets/coordinate.css +++ b/public/stylesheets/coordinate.css @@ -1,3 +1,21 @@ +form.color .color span { + display: block; + width: 30px; + height: 30px; + background-size: 30px 30px; + background-repeat: no-repeat; + padding: 0; + margin: 10px 15px; +} +form.color .color_1 span { + background-image: url(../piece/cburnett/wK.svg); +} +form.color .color_2 span { + background-image: url(../piece/cburnett/wbK.svg); +} +form.color .color_3 span { + background-image: url(../piece/cburnett/bK.svg); +} #trainer .lichess_board { display: none; border: 1px solid #c0c0c0; diff --git a/public/stylesheets/dark.css b/public/stylesheets/dark.css index ca155df707..1a06484bc8 100644 --- a/public/stylesheets/dark.css +++ b/public/stylesheets/dark.css @@ -140,7 +140,7 @@ body.dark #top a.toggle:hover, body.dark #top .dropdown, body.dark #top a.goto_nav.current, body.dark #top a.goto_nav:hover, -body.dark a#sound_state:hover { +body.dark #sound_state:hover { color: #c0c0c0; } body.dark #site_title,