fully type ui/round
parent
85538b2003
commit
93c9be0989
|
@ -83,9 +83,8 @@ object JsonView {
|
|||
"games" -> o.nb,
|
||||
"rating" -> o.glicko.rating.toInt,
|
||||
"rd" -> o.glicko.deviation.toInt,
|
||||
"prov" -> o.glicko.provisional,
|
||||
"prog" -> o.progress
|
||||
)
|
||||
).add("prov" -> o.glicko.provisional)
|
||||
}
|
||||
|
||||
private val standardPerfKeys: Set[Perf.Key] = PerfType.standard.map(_.key)(scala.collection.breakOut)
|
||||
|
|
|
@ -71,10 +71,10 @@ export function replayable(data: GameData): boolean {
|
|||
(status.aborted(data) && bothPlayersHavePlayed(data));
|
||||
}
|
||||
|
||||
export function getPlayer(data: GameData, color: Color): Player;
|
||||
export function getPlayer(data: GameData, color: Color | undefined): Player;
|
||||
export function getPlayer(data: GameData, color?: Color): Player | null {
|
||||
if (data.player.color == color) return data.player;
|
||||
if (data.opponent.color == color) return data.opponent;
|
||||
if (data.player.color === color) return data.player;
|
||||
if (data.opponent.color === color) return data.opponent;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ export interface Game {
|
|||
boosted?: boolean;
|
||||
rematch?: string;
|
||||
rated?: boolean;
|
||||
perf: string;
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
|
@ -42,7 +43,7 @@ export type StatusId = number;
|
|||
export interface Player {
|
||||
id: string;
|
||||
name: string;
|
||||
user: User;
|
||||
user?: PlayerUser;
|
||||
spectator?: boolean;
|
||||
color: Color;
|
||||
proposingTakeback?: boolean;
|
||||
|
@ -55,6 +56,10 @@ export interface Player {
|
|||
hold?: Hold;
|
||||
ratingDiff?: number;
|
||||
checks?: number;
|
||||
rating?: number;
|
||||
provisional?: string;
|
||||
engine?: boolean;
|
||||
berserk?: boolean;
|
||||
}
|
||||
|
||||
export interface TournamentRanks {
|
||||
|
@ -63,8 +68,11 @@ export interface TournamentRanks {
|
|||
}
|
||||
|
||||
export interface Tournament {
|
||||
id: string;
|
||||
berserkable: boolean;
|
||||
ranks?: TournamentRanks;
|
||||
running?: boolean;
|
||||
nbSecondsForFirstMove?: number;
|
||||
}
|
||||
|
||||
export interface Simul {
|
||||
|
@ -79,9 +87,22 @@ export interface Clock {
|
|||
|
||||
export type Source = 'import' | 'lobby' | 'pool';
|
||||
|
||||
export interface User {
|
||||
export interface PlayerUser {
|
||||
online: boolean;
|
||||
username: string;
|
||||
patron?: boolean;
|
||||
title?: string;
|
||||
perfs: {
|
||||
[key: string]: Perf;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Perf {
|
||||
games: number;
|
||||
rating: number;
|
||||
rd: number;
|
||||
prog: number;
|
||||
prov?: boolean;
|
||||
}
|
||||
|
||||
export interface Ctrl {
|
||||
|
|
|
@ -19,6 +19,7 @@ export interface ClockData {
|
|||
emerg: Seconds;
|
||||
showTenths: 0 | 1 | 2;
|
||||
showBar: boolean;
|
||||
moretime: number;
|
||||
}
|
||||
|
||||
interface Times {
|
||||
|
|
|
@ -5,8 +5,7 @@ import { game } from 'game';
|
|||
import RoundController from '../ctrl';
|
||||
import { ClockController, ClockData, Millis } from './clockCtrl';
|
||||
import { Player } from 'game';
|
||||
|
||||
type Position = 'top' | 'bottom';
|
||||
import { Position } from '../interfaces';
|
||||
|
||||
export function renderClock(ctrl: RoundController, player: Player, position: Position) {
|
||||
const clock = ctrl.clock!,
|
||||
|
|
|
@ -6,6 +6,7 @@ export interface CorresClockData {
|
|||
white: Seconds;
|
||||
black: Seconds;
|
||||
emerg: Seconds;
|
||||
showBar: boolean;
|
||||
}
|
||||
|
||||
export interface CorresClockController {
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import { h } from 'snabbdom'
|
||||
import { Millis } from '../clock/clockCtrl';
|
||||
import { Position } from '../interfaces';
|
||||
import { CorresClockController } from './corresClockCtrl';
|
||||
|
||||
function prefixInteger(num, length) {
|
||||
function prefixInteger(num: number, length: number): string {
|
||||
return (num / Math.pow(10, length)).toFixed(length).substr(2);
|
||||
}
|
||||
|
||||
function bold(x) {
|
||||
function bold(x: string) {
|
||||
return '<b>' + x + '</b>';
|
||||
}
|
||||
|
||||
function formatClockTime(trans, time) {
|
||||
var date = new Date(time);
|
||||
var minutes = prefixInteger(date.getUTCMinutes(), 2);
|
||||
var seconds = prefixInteger(date.getSeconds(), 2);
|
||||
var hours, str = '';
|
||||
function formatClockTime(trans: Trans, time: Millis) {
|
||||
const date = new Date(time),
|
||||
minutes = prefixInteger(date.getUTCMinutes(), 2),
|
||||
seconds = prefixInteger(date.getSeconds(), 2);
|
||||
let hours: number, str = '';
|
||||
if (time >= 86400 * 1000) {
|
||||
// days : hours
|
||||
var days = date.getUTCDate() - 1;
|
||||
const days = date.getUTCDate() - 1;
|
||||
hours = date.getUTCHours();
|
||||
str += (days === 1 ? trans('oneDay') : trans('nbDays', days)) + ' ';
|
||||
if (hours !== 0) str += trans('nbHours', hours);
|
||||
|
@ -30,7 +33,7 @@ function formatClockTime(trans, time) {
|
|||
return str;
|
||||
}
|
||||
|
||||
export default function(ctrl, trans, color, position, runningColor) {
|
||||
export default function(ctrl: CorresClockController, trans: Trans, color: Color, position: Position, runningColor: Color) {
|
||||
const millis = ctrl.millisOf(color);
|
||||
const update = (el: HTMLElement) => {
|
||||
el.innerHTML = formatClockTime(trans, millis);
|
|
@ -2,21 +2,22 @@ import { game } from 'game';
|
|||
import { dragNewPiece } from 'chessground/drag';
|
||||
import RoundController from '../ctrl';
|
||||
import * as cg from 'chessground/types';
|
||||
import { RoundData } from '../interfaces';
|
||||
|
||||
export function drag(ctrl: RoundController, e: cg.MouchEvent) {
|
||||
export function drag(ctrl: RoundController, e: cg.MouchEvent): void {
|
||||
if (e.button !== undefined && e.button !== 0) return; // only touch or left click
|
||||
if (ctrl.replaying() || !game.isPlayerPlaying(ctrl.data)) return;
|
||||
const el = e.target as HTMLElement,
|
||||
role = el.getAttribute('data-role'),
|
||||
color = el.getAttribute('data-color'),
|
||||
number = el.getAttribute('data-nb');
|
||||
role = el.getAttribute('data-role') as cg.Role,
|
||||
color = el.getAttribute('data-color') as cg.Color,
|
||||
number = el.getAttribute('data-nb');
|
||||
if (!role || !color || number === '0') return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
dragNewPiece(ctrl.chessground.state, { color, role }, e);
|
||||
}
|
||||
|
||||
export function valid(data, role, key) {
|
||||
export function valid(data: RoundData, role: cg.Role, key: cg.Key): boolean {
|
||||
|
||||
if (!game.isPlayerTurn(data)) return false;
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ import {
|
|||
|
||||
constructor(opts: RoundOpts, redraw: Redraw) {
|
||||
|
||||
const d = round.merge({}, opts.data).data;
|
||||
const d = round.merge({} as RoundData, opts.data).data;
|
||||
|
||||
this.opts = opts;
|
||||
this.data = d;
|
||||
|
@ -427,7 +427,7 @@ import {
|
|||
d = merged.data;
|
||||
this.data = d;
|
||||
this.clearVmJust();
|
||||
if (this.clock) this.clock.update(d.clock.white, d.clock.black);
|
||||
if (this.clock) this.clock.update(d.clock!.white, d.clock!.black);
|
||||
if (this.corresClock) this.corresClock.update(d.correspondence.white, d.correspondence.black);
|
||||
if (!this.replaying()) ground.reload(this);
|
||||
this.setTitle();
|
||||
|
@ -529,7 +529,7 @@ import {
|
|||
if (this.resignConfirm) {
|
||||
if (v) this.socket.sendLoading('resign');
|
||||
else this.resignConfirm = false;
|
||||
} else if (v !== false) {
|
||||
} else if (v) {
|
||||
if (this.data.pref.confirmResign) this.resignConfirm = true;
|
||||
else this.socket.sendLoading('resign');
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { VNode } from 'snabbdom/vnode';
|
||||
import { GameData, Status } from 'game';
|
||||
import { ClockData } from './clock/clockCtrl';
|
||||
import { CorresClockData } from './corresClock/corresClockCtrl';
|
||||
import * as cg from 'chessground/types';
|
||||
|
||||
|
@ -28,6 +29,7 @@ export interface SocketDrop {
|
|||
}
|
||||
|
||||
export interface RoundData extends GameData {
|
||||
clock?: ClockData;
|
||||
pref: Pref;
|
||||
steps: Step[];
|
||||
possibleMoves?: { [key: string]: string };
|
||||
|
@ -40,6 +42,12 @@ export interface RoundData extends GameData {
|
|||
round: string;
|
||||
},
|
||||
blind?: boolean;
|
||||
tv?: Tv;
|
||||
}
|
||||
|
||||
export interface Tv {
|
||||
channel: string;
|
||||
flip: boolean;
|
||||
}
|
||||
|
||||
interface CrazyData {
|
||||
|
@ -144,3 +152,5 @@ export interface MoveMetadata {
|
|||
justDropped?: cg.Role;
|
||||
justCaptured?: cg.Piece;
|
||||
}
|
||||
|
||||
export type Position = 'top' | 'bottom';
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
import { h } from 'snabbdom'
|
||||
import { Api as ChessgroundApi } from 'chessground/api';
|
||||
import * as cg from 'chessground/types';
|
||||
import { Step, Untyped, Redraw } from './interfaces';
|
||||
import RoundController from './ctrl';
|
||||
import { Step, Redraw } from './interfaces';
|
||||
|
||||
export interface KeyboardMove extends Untyped {
|
||||
export interface KeyboardMove {
|
||||
update(step: Step): void;
|
||||
registerHandler(h): void
|
||||
hasFocus(): boolean;
|
||||
setFocus(v: boolean): void;
|
||||
san(orig: cg.Key, dest: cg.Key): void;
|
||||
select(key: cg.Key): void;
|
||||
hasSelected(): cg.Key | undefined;
|
||||
usedSan: boolean;
|
||||
}
|
||||
|
||||
export function ctrl(cg: ChessgroundApi, step: Step, redraw: Redraw) {
|
||||
export function ctrl(cg: ChessgroundApi, step: Step, redraw: Redraw): KeyboardMove {
|
||||
let focus = false;
|
||||
let handler;
|
||||
let preHandlerBuffer = step.fen;
|
||||
const select = function(key: cg.Key) {
|
||||
const select = function(key: cg.Key): void {
|
||||
if (cg.state.selected === key) cg.cancelMove();
|
||||
else cg.selectSquare(key, true);
|
||||
};
|
||||
|
@ -42,7 +49,7 @@ export function ctrl(cg: ChessgroundApi, step: Step, redraw: Redraw) {
|
|||
};
|
||||
};
|
||||
|
||||
export function render(ctrl: RoundController) {
|
||||
export function render(ctrl: KeyboardMove) {
|
||||
return h('div.keyboard-move', [
|
||||
h('input', {
|
||||
attrs: {
|
||||
|
|
|
@ -1,26 +1,34 @@
|
|||
import { h } from 'snabbdom'
|
||||
import * as ground from './ground';
|
||||
import * as cg from 'chessground/types';
|
||||
import { DrawShape } from 'chessground/draw';
|
||||
import xhr = require('./xhr');
|
||||
import { key2pos } from 'chessground/util';
|
||||
import { bind } from './util';
|
||||
import { h } from 'snabbdom'
|
||||
import RoundController from './ctrl';
|
||||
|
||||
let promoting: any | undefined;
|
||||
interface Promoting {
|
||||
move: [cg.Key, cg.Key];
|
||||
pre: boolean;
|
||||
meta: cg.MoveMetadata
|
||||
}
|
||||
|
||||
let promoting: Promoting | undefined;
|
||||
let prePromotionRole: cg.Role | undefined;
|
||||
|
||||
function sendPromotion(ctrl, orig, dest, role, meta) {
|
||||
function sendPromotion(ctrl: RoundController, orig: cg.Key, dest: cg.Key, role: cg.Role, meta: cg.MoveMetadata): boolean {
|
||||
ground.promote(ctrl.chessground, dest, role);
|
||||
ctrl.sendMove(orig, dest, role, meta);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function start(ctrl, orig, dest, meta) {
|
||||
var d = ctrl.data;
|
||||
var piece = ctrl.chessground.state.pieces[dest];
|
||||
var premovePiece = ctrl.chessground.state.pieces[orig];
|
||||
export function start(ctrl: RoundController, orig: cg.Key, dest: cg.Key, meta: cg.MoveMetadata): boolean {
|
||||
const d = ctrl.data,
|
||||
piece = ctrl.chessground.state.pieces[dest],
|
||||
premovePiece = ctrl.chessground.state.pieces[orig];
|
||||
if (((piece && piece.role === 'pawn') || (premovePiece && premovePiece.role === 'pawn')) && (
|
||||
(dest[1] == 8 && d.player.color === 'white') ||
|
||||
(dest[1] == 1 && d.player.color === 'black'))) {
|
||||
(dest[1] === '8' && d.player.color === 'white') ||
|
||||
(dest[1] === '1' && d.player.color === 'black'))) {
|
||||
if (prePromotionRole && meta.premove) return sendPromotion(ctrl, orig, dest, prePromotionRole, meta);
|
||||
if (!meta.ctrlKey && !promoting && (d.pref.autoQueen === 3 || (d.pref.autoQueen === 2 && premovePiece))) {
|
||||
if (premovePiece) setPrePromotion(ctrl, dest, 'queen');
|
||||
|
@ -38,7 +46,7 @@ export function start(ctrl, orig, dest, meta) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function setPrePromotion(ctrl, dest, role) {
|
||||
function setPrePromotion(ctrl: RoundController, dest: cg.Key, role: cg.Role): void {
|
||||
prePromotionRole = role;
|
||||
ctrl.chessground.setAutoShapes([{
|
||||
orig: dest,
|
||||
|
@ -46,11 +54,12 @@ function setPrePromotion(ctrl, dest, role) {
|
|||
color: ctrl.data.player.color,
|
||||
role,
|
||||
opacity: 0.8
|
||||
}
|
||||
}]);
|
||||
},
|
||||
brush: ''
|
||||
} as DrawShape]);
|
||||
}
|
||||
|
||||
export function cancelPrePromotion(ctrl) {
|
||||
export function cancelPrePromotion(ctrl: RoundController) {
|
||||
if (prePromotionRole) {
|
||||
ctrl.chessground.setAutoShapes([]);
|
||||
prePromotionRole = undefined;
|
||||
|
@ -58,7 +67,7 @@ export function cancelPrePromotion(ctrl) {
|
|||
}
|
||||
}
|
||||
|
||||
function finish(ctrl, role) {
|
||||
function finish(ctrl: RoundController, role: cg.Role) {
|
||||
if (promoting) {
|
||||
const info = promoting;
|
||||
promoting = undefined;
|
||||
|
@ -67,14 +76,14 @@ function finish(ctrl, role) {
|
|||
}
|
||||
}
|
||||
|
||||
export function cancel(ctrl) {
|
||||
export function cancel(ctrl: RoundController) {
|
||||
cancelPrePromotion(ctrl);
|
||||
ctrl.chessground.cancelPremove();
|
||||
if (promoting) xhr.reload(ctrl).then(ctrl.reload);
|
||||
promoting = undefined;
|
||||
}
|
||||
|
||||
function renderPromotion(ctrl, dest, pieces, color, orientation) {
|
||||
function renderPromotion(ctrl: RoundController, dest: cg.Key, roles: cg.Role[], color: Color, orientation: Color) {
|
||||
var left = (8 - key2pos(dest)[0]) * 12.5;
|
||||
if (orientation === 'white') left = 87.5 - left;
|
||||
var vertical = color === orientation ? 'top' : 'bottom';
|
||||
|
@ -90,7 +99,7 @@ function renderPromotion(ctrl, dest, pieces, color, orientation) {
|
|||
});
|
||||
}
|
||||
}
|
||||
}, pieces.map((serverRole, i) => {
|
||||
}, roles.map((serverRole, i) => {
|
||||
var top = (color === orientation ? i : 7 - i) * 12.5;
|
||||
return h('square', {
|
||||
attrs: {style: 'top: ' + top + '%;left: ' + left + '%'},
|
||||
|
@ -104,12 +113,13 @@ function renderPromotion(ctrl, dest, pieces, color, orientation) {
|
|||
}));
|
||||
};
|
||||
|
||||
export function view(ctrl) {
|
||||
if (!promoting) return;
|
||||
var pieces = ['queen', 'knight', 'rook', 'bishop'];
|
||||
if (ctrl.data.game.variant.key === 'antichess') pieces.push('king');
|
||||
const roles: cg.Role[] = ['queen', 'knight', 'rook', 'bishop'];
|
||||
|
||||
return renderPromotion(ctrl, promoting.move[1], pieces,
|
||||
export function view(ctrl: RoundController) {
|
||||
if (!promoting) return;
|
||||
|
||||
return renderPromotion(ctrl, promoting.move[1],
|
||||
ctrl.data.game.variant.key === 'antichess' ? roles.concat('king') : roles,
|
||||
ctrl.data.player.color,
|
||||
ctrl.chessground.state.orientation);
|
||||
};
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
export function firstPly(d) {
|
||||
import { RoundData, Step } from './interfaces';
|
||||
|
||||
export function firstPly(d: RoundData): number {
|
||||
return d.steps[0].ply;
|
||||
}
|
||||
|
||||
export function lastPly(d) {
|
||||
export function lastPly(d: RoundData): number {
|
||||
return d.steps[d.steps.length - 1].ply;
|
||||
}
|
||||
|
||||
export function plyStep(d, ply) {
|
||||
export function plyStep(d: RoundData, ply): Step {
|
||||
return d.steps[ply - firstPly(d)];
|
||||
}
|
||||
|
||||
export function merge(old, cfg) {
|
||||
export function merge(old: RoundData, cfg: RoundData): {
|
||||
data: RoundData;
|
||||
changes: any;
|
||||
} {
|
||||
var data = cfg;
|
||||
|
||||
if (data.clock) {
|
||||
|
@ -24,7 +29,7 @@ export function merge(old, cfg) {
|
|||
if (['horde', 'crazyhouse'].indexOf(data.game.variant.key) !== -1)
|
||||
data.pref.showCaptured = false;
|
||||
|
||||
var changes: any = {};
|
||||
const changes: any = {};
|
||||
if (old.opponent) {
|
||||
if (!old.opponent.offeringDraw && cfg.opponent.offeringDraw)
|
||||
changes.drawOffer = true;
|
||||
|
|
|
@ -17,9 +17,10 @@ const F = [
|
|||
};
|
||||
});
|
||||
|
||||
var tickerTimer;
|
||||
let tickerTimer: number | undefined;
|
||||
function resetTicker() {
|
||||
tickerTimer = clearTimeout(tickerTimer);
|
||||
if (tickerTimer) clearTimeout(tickerTimer);
|
||||
tickerTimer = undefined;
|
||||
F[0]();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import * as cg from 'chessground/types'
|
||||
|
||||
import { h } from 'snabbdom'
|
||||
|
||||
type Redraw = () => void
|
||||
import * as cg from 'chessground/types'
|
||||
import { Redraw } from './interfaces';
|
||||
|
||||
const pieceScores = {
|
||||
pawn: 1,
|
||||
|
@ -37,27 +35,27 @@ export function bind(eventName: string, f: (e: Event) => void, redraw: Redraw |
|
|||
}
|
||||
export function parsePossibleMoves(possibleMoves) {
|
||||
if (!possibleMoves) return {};
|
||||
for (var k in possibleMoves) {
|
||||
for (let k in possibleMoves) {
|
||||
if (typeof possibleMoves[k] === 'object') break;
|
||||
possibleMoves[k] = possibleMoves[k].match(/.{2}/g);
|
||||
}
|
||||
return possibleMoves;
|
||||
};
|
||||
// {white: {pawn: 3 queen: 1}, black: {bishop: 2}}
|
||||
export function getMaterialDiff(pieces) {
|
||||
var counts = {
|
||||
export function getMaterialDiff(pieces: cg.Pieces): cg.MaterialDiff {
|
||||
let counts = {
|
||||
king: 0,
|
||||
queen: 0,
|
||||
rook: 0,
|
||||
bishop: 0,
|
||||
knight: 0,
|
||||
pawn: 0
|
||||
}, p, role, c;
|
||||
for (var k in pieces) {
|
||||
}, p, role, c, k;
|
||||
for (k in pieces) {
|
||||
p = pieces[k];
|
||||
counts[p.role] += (p.color === 'white' ? 1 : -1);
|
||||
}
|
||||
var diff = {
|
||||
const diff = {
|
||||
white: {},
|
||||
black: {}
|
||||
};
|
||||
|
@ -68,9 +66,9 @@ export function getMaterialDiff(pieces) {
|
|||
}
|
||||
return diff;
|
||||
};
|
||||
export function getScore(pieces) {
|
||||
var score = 0;
|
||||
for (var k in pieces) {
|
||||
export function getScore(pieces: cg.Pieces): number {
|
||||
let score = 0, k;
|
||||
for (k in pieces) {
|
||||
score += pieceScores[pieces[k].role] * (pieces[k].color === 'white' ? 1 : -1);
|
||||
}
|
||||
return score;
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
import * as util from '../util';
|
||||
import { game, status, router } from 'game';
|
||||
|
||||
import { h } from 'snabbdom'
|
||||
import { VNode } from 'snabbdom/vnode'
|
||||
import * as util from '../util';
|
||||
import { game, status, router } from 'game';
|
||||
import { RoundData, MaybeVNodes } from '../interfaces';
|
||||
import { ClockData } from '../clock/clockCtrl';
|
||||
import RoundController from '../ctrl';
|
||||
|
||||
function analysisBoardOrientation(data) {
|
||||
function analysisBoardOrientation(data: RoundData) {
|
||||
return data.game.variant.key === 'racingKings' ? 'white' : data.player.color;
|
||||
}
|
||||
|
||||
function poolUrl(clock) {
|
||||
function poolUrl(clock: ClockData) {
|
||||
return '/#pool/' + (clock.initial / 60) + '+' + clock.increment;
|
||||
}
|
||||
|
||||
function analysisButton(ctrl) {
|
||||
var d = ctrl.data;
|
||||
var url = router.game(d, analysisBoardOrientation(d)) + '#' + ctrl.vm.ply;
|
||||
function analysisButton(ctrl: RoundController): VNode | null {
|
||||
const d = ctrl.data,
|
||||
url = router.game(d, analysisBoardOrientation(d)) + '#' + ctrl.ply;
|
||||
return game.replayable(d) ? h('a.button', {
|
||||
attrs: { href: url },
|
||||
hook: util.bind('click', () => {
|
||||
hook: util.bind('click', _ => {
|
||||
// force page load in case the URL is the same
|
||||
if (location.pathname === url.split('#')[0]) location.reload();
|
||||
})
|
||||
}, ctrl.trans.noarg('analysis')) : null;
|
||||
}
|
||||
|
||||
function rematchButtons(ctrl): Array<VNode | null> {
|
||||
var d = ctrl.data;
|
||||
var me = d.player.offeringRematch, them = d.opponent.offeringRematch;
|
||||
function rematchButtons(ctrl): MaybeVNodes {
|
||||
const d = ctrl.data,
|
||||
me = d.player.offeringRematch, them = d.opponent.offeringRematch;
|
||||
return [
|
||||
them ? h('a.rematch-decline', {
|
||||
attrs: {
|
||||
|
@ -62,9 +64,16 @@ function rematchButtons(ctrl): Array<VNode | null> {
|
|||
];
|
||||
}
|
||||
|
||||
export function standard(ctrl, condition, icon, hint, socketMsg, onclick): VNode {
|
||||
export function standard(
|
||||
ctrl: RoundController,
|
||||
condition: ((d: RoundData) => boolean) | undefined,
|
||||
icon: string,
|
||||
hint: string,
|
||||
socketMsg: string,
|
||||
onclick?: () => void
|
||||
): VNode {
|
||||
// disabled if condition callback is provided and is falsy
|
||||
var enabled = function() {
|
||||
const enabled = function() {
|
||||
return !condition || condition(ctrl.data);
|
||||
};
|
||||
return h('button.fbt.hint--bottom.' + socketMsg, {
|
||||
|
@ -72,18 +81,15 @@ export function standard(ctrl, condition, icon, hint, socketMsg, onclick): VNode
|
|||
disabled: !enabled(),
|
||||
'data-hint': ctrl.trans.noarg(hint)
|
||||
},
|
||||
hook: {
|
||||
insert: vnode => {
|
||||
(vnode.elm as HTMLElement).addEventListener('click', () => {
|
||||
if (enabled()) onclick ? onclick() : ctrl.socket.sendLoading(socketMsg, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
hook: util.bind('click', _ => {
|
||||
if (enabled()) onclick ? onclick() : ctrl.socket.sendLoading(socketMsg);
|
||||
})
|
||||
}, [
|
||||
h('span', { attrs: util.dataIcon(icon) })
|
||||
]);
|
||||
};
|
||||
export function forceResign(ctrl) {
|
||||
}
|
||||
|
||||
export function forceResign(ctrl: RoundController) {
|
||||
return ctrl.forceResignable() ? h('div.suggestion', [
|
||||
h('p', ctrl.trans.noarg('opponentLeftChoices')),
|
||||
h('a.button', {
|
||||
|
@ -93,8 +99,9 @@ export function forceResign(ctrl) {
|
|||
hook: util.bind('click', () => ctrl.socket.sendLoading('draw-force'))
|
||||
}, ctrl.trans.noarg('forceDraw'))
|
||||
]) : null;
|
||||
};
|
||||
export function resignConfirm(ctrl): VNode {
|
||||
}
|
||||
|
||||
export function resignConfirm(ctrl: RoundController): VNode {
|
||||
return h('div.resign_confirm', [
|
||||
h('button.fbt.no.hint--bottom', {
|
||||
attrs: { 'data-hint': ctrl.trans.noarg('cancel') },
|
||||
|
@ -105,21 +112,24 @@ export function resignConfirm(ctrl): VNode {
|
|||
hook: util.bind('click', () => ctrl.resign(true))
|
||||
}, [h('span', { attrs: util.dataIcon('b') })])
|
||||
]);
|
||||
};
|
||||
export function threefoldClaimDraw(ctrl) {
|
||||
}
|
||||
|
||||
export function threefoldClaimDraw(ctrl: RoundController) {
|
||||
return ctrl.data.game.threefold ? h('div.suggestion', [
|
||||
h('p', ctrl.trans('threefoldRepetition')),
|
||||
h('a.button', {
|
||||
hook: util.bind('click', () => ctrl.socket.sendLoading('draw-claim'))
|
||||
}, ctrl.trans.noarg('claimADraw'))
|
||||
]) : null;
|
||||
};
|
||||
export function cancelDrawOffer(ctrl) {
|
||||
}
|
||||
|
||||
export function cancelDrawOffer(ctrl: RoundController) {
|
||||
return ctrl.data.player.offeringDraw ? h('div.pending', [
|
||||
h('p', ctrl.trans.noarg('drawOfferSent'))
|
||||
]) : null;
|
||||
};
|
||||
export function answerOpponentDrawOffer(ctrl) {
|
||||
}
|
||||
|
||||
export function answerOpponentDrawOffer(ctrl: RoundController) {
|
||||
return ctrl.data.opponent.offeringDraw ? h('div.negotiation.draw', [
|
||||
h('p', ctrl.trans.noarg('yourOpponentOffersADraw')),
|
||||
h('a.accept', {
|
||||
|
@ -137,16 +147,18 @@ export function answerOpponentDrawOffer(ctrl) {
|
|||
hook: util.bind('click', () => ctrl.socket.sendLoading('draw-no'))
|
||||
})
|
||||
]) : null;
|
||||
};
|
||||
export function cancelTakebackProposition(ctrl) {
|
||||
}
|
||||
|
||||
export function cancelTakebackProposition(ctrl: RoundController) {
|
||||
return ctrl.data.player.proposingTakeback ? h('div.pending', [
|
||||
h('p', ctrl.trans.noarg('takebackPropositionSent')),
|
||||
h('a.button', {
|
||||
hook: util.bind('click', () => ctrl.socket.sendLoading('takeback-no'))
|
||||
}, ctrl.trans.noarg('cancel'))
|
||||
]) : null;
|
||||
};
|
||||
export function answerOpponentTakebackProposition(ctrl) {
|
||||
}
|
||||
|
||||
export function answerOpponentTakebackProposition(ctrl: RoundController) {
|
||||
return ctrl.data.opponent.proposingTakeback ? h('div.negotiation.takeback', [
|
||||
h('p', ctrl.trans.noarg('yourOpponentProposesATakeback')),
|
||||
h('a.accept', {
|
||||
|
@ -164,9 +176,10 @@ export function answerOpponentTakebackProposition(ctrl) {
|
|||
hook: util.bind('click', () => ctrl.socket.sendLoading('takeback-no'))
|
||||
})
|
||||
]) : null;
|
||||
};
|
||||
export function submitMove(ctrl): VNode | undefined {
|
||||
return (ctrl.vm.moveToSubmit || ctrl.vm.dropToSubmit) ? h('div.negotiation.move-confirm', [
|
||||
}
|
||||
|
||||
export function submitMove(ctrl: RoundController): VNode | undefined {
|
||||
return (ctrl.moveToSubmit || ctrl.dropToSubmit) ? h('div.negotiation.move-confirm', [
|
||||
h('p', ctrl.trans.noarg('moveConfirmation')),
|
||||
h('a.accept', {
|
||||
attrs: {
|
||||
|
@ -183,9 +196,10 @@ export function submitMove(ctrl): VNode | undefined {
|
|||
hook: util.bind('click', () => ctrl.submitMove(false))
|
||||
})
|
||||
]) : undefined;
|
||||
};
|
||||
export function backToTournament(ctrl): VNode | undefined {
|
||||
var d = ctrl.data;
|
||||
}
|
||||
|
||||
export function backToTournament(ctrl: RoundController): VNode | undefined {
|
||||
const d = ctrl.data;
|
||||
return (d.tournament && d.tournament.running) ? h('div.follow_up', [
|
||||
h('a.text.fbt.strong.glowed', {
|
||||
attrs: {
|
||||
|
@ -204,22 +218,24 @@ export function backToTournament(ctrl): VNode | undefined {
|
|||
]),
|
||||
analysisButton(ctrl)
|
||||
]) : undefined;
|
||||
};
|
||||
export function moretime(ctrl) {
|
||||
}
|
||||
|
||||
export function moretime(ctrl: RoundController) {
|
||||
return game.moretimeable(ctrl.data) ? h('a.moretime.hint--bottom-left', {
|
||||
attrs: { 'data-hint': ctrl.trans('giveNbSeconds', ctrl.data.clock.moretime) },
|
||||
attrs: { 'data-hint': ctrl.trans('giveNbSeconds', ctrl.data.clock!.moretime) },
|
||||
hook: util.bind('click', ctrl.socket.moreTime)
|
||||
}, [
|
||||
h('span', { attrs: util.dataIcon('O')})
|
||||
]) : null;
|
||||
};
|
||||
export function followUp(ctrl): VNode {
|
||||
var d = ctrl.data;
|
||||
var rematchable = !d.game.rematch && (status.finished(d) || status.aborted(d)) && !d.tournament && !d.simul && !d.game.boosted && (d.opponent.onGame || (!d.clock && d.player.user && d.opponent.user));
|
||||
var newable = (status.finished(d) || status.aborted(d)) && (
|
||||
}
|
||||
|
||||
export function followUp(ctrl: RoundController): VNode {
|
||||
const d = ctrl.data,
|
||||
rematchable = !d.game.rematch && (status.finished(d) || status.aborted(d)) && !d.tournament && !d.simul && !d.game.boosted && (d.opponent.onGame || (!d.clock && d.player.user && d.opponent.user)),
|
||||
newable = (status.finished(d) || status.aborted(d)) && (
|
||||
d.game.source === 'lobby' ||
|
||||
d.game.source === 'pool');
|
||||
const rematchZone = ctrl.vm.challengeRematched ? [h('div.suggestion.text', {
|
||||
d.game.source === 'pool'),
|
||||
rematchZone = ctrl.challengeRematched ? [h('div.suggestion.text', {
|
||||
attrs: util.dataIcon('j')
|
||||
}, ctrl.trans.noarg('rematchOfferSent')
|
||||
)] : (rematchable || d.game.rematch ? rematchButtons(ctrl) : []);
|
||||
|
@ -229,18 +245,19 @@ export function followUp(ctrl): VNode {
|
|||
attrs: {href: '/tournament/' + d.tournament.id}
|
||||
}, ctrl.trans.noarg('viewTournament')) : null,
|
||||
newable ? h('a.button', {
|
||||
attrs: {href: d.game.source === 'pool' ? poolUrl(d.clock) : '/?hook_like=' + d.game.id },
|
||||
attrs: {href: d.game.source === 'pool' ? poolUrl(d.clock!) : '/?hook_like=' + d.game.id },
|
||||
}, ctrl.trans.noarg('newOpponent')) : null,
|
||||
analysisButton(ctrl)
|
||||
]);
|
||||
};
|
||||
export function watcherFollowUp(ctrl): VNode {
|
||||
var d = ctrl.data;
|
||||
}
|
||||
|
||||
export function watcherFollowUp(ctrl: RoundController): VNode {
|
||||
const d = ctrl.data;
|
||||
return h('div.follow_up', [
|
||||
d.game.rematch ? h('a.button.text', {
|
||||
attrs: {
|
||||
'data-icon': 'v',
|
||||
href: router.game(d.game.rematch, d.opponent.color)
|
||||
href: `${d.game.rematch}/${d.opponent.color}`
|
||||
}
|
||||
}, ctrl.trans.noarg('viewRematch')) : null,
|
||||
d.tournament ? h('a.button', {
|
||||
|
@ -248,4 +265,4 @@ export function watcherFollowUp(ctrl): VNode {
|
|||
}, ctrl.trans.noarg('viewTournament')) : null,
|
||||
analysisButton(ctrl)
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,34 +1,35 @@
|
|||
import { game } from 'game';
|
||||
import round = require('../round');
|
||||
import table = require('./table');
|
||||
import promotion = require('../promotion');
|
||||
import ground = require('../ground');
|
||||
import { read as fenRead } from 'chessground/fen';
|
||||
import util = require('../util');
|
||||
import blind = require('../blind');
|
||||
import keyboard = require('../keyboard');
|
||||
import crazyView from '../crazy/crazyView';
|
||||
import { render as keyboardMove } from '../keyboardMove';
|
||||
|
||||
import { h } from 'snabbdom'
|
||||
import { VNode } from 'snabbdom/vnode'
|
||||
import { game } from 'game';
|
||||
import { plyStep } from '../round';
|
||||
import renderTable from './table';
|
||||
import * as promotion from '../promotion';
|
||||
import { render as renderGround } from '../ground';
|
||||
import { read as fenRead } from 'chessground/fen';
|
||||
import * as util from '../util';
|
||||
import * as blind from '../blind';
|
||||
import * as keyboard from '../keyboard';
|
||||
import crazyView from '../crazy/crazyView';
|
||||
import { render as keyboardMove } from '../keyboardMove';
|
||||
import RoundController from '../ctrl';
|
||||
import * as cg from 'chessground/types';
|
||||
|
||||
function renderMaterial(material, checks, score) {
|
||||
function renderMaterial(material: cg.MaterialDiff, checks?: number, score?: number) {
|
||||
var children: VNode[] = [];
|
||||
if (score || score === 0)
|
||||
children.push(h('score', score > 0 ? '+' + score : score));
|
||||
for (var role in material) {
|
||||
children.push(h('score', (score > 0 ? '+' : '') + score));
|
||||
for (let role in material) {
|
||||
const content: VNode[] = [];
|
||||
for (var i = 0; i < material[role]; i++) content.push(h('mono-piece.' + role));
|
||||
for (let i = 0; i < material[role]; i++) content.push(h('mono-piece.' + role));
|
||||
children.push(h('tomb', content));
|
||||
}
|
||||
for (var i = 0; i < checks; i++) {
|
||||
if (checks) for (let i = 0; i < checks; i++) {
|
||||
children.push(h('tomb', h('mono-piece.king')));
|
||||
}
|
||||
return h('div.cemetery', children);
|
||||
}
|
||||
|
||||
function wheel(ctrl, e) {
|
||||
function wheel(ctrl: RoundController, e: WheelEvent): boolean {
|
||||
if (game.isPlayerPlaying(ctrl.data)) return true;
|
||||
e.preventDefault();
|
||||
if (e.deltaY > 0) keyboard.next(ctrl);
|
||||
|
@ -37,38 +38,38 @@ function wheel(ctrl, e) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function visualBoard(ctrl) {
|
||||
function visualBoard(ctrl: RoundController) {
|
||||
return h('div.lichess_board_wrap', [
|
||||
h('div.lichess_board.' + ctrl.data.game.variant.key + (ctrl.data.pref.blindfold ? '.blindfold' : ''), {
|
||||
hook: util.bind('wheel', e => wheel(ctrl, e))
|
||||
}, [ground.render(ctrl)]),
|
||||
hook: util.bind('wheel', (e: WheelEvent) => wheel(ctrl, e))
|
||||
}, [renderGround(ctrl)]),
|
||||
promotion.view(ctrl)
|
||||
]);
|
||||
}
|
||||
|
||||
function blindBoard(ctrl) {
|
||||
function blindBoard(ctrl: RoundController) {
|
||||
return h('div.lichess_board_blind', [
|
||||
h('div.textual', {
|
||||
hook: {
|
||||
insert: vnode => blind.init(vnode.elm, ctrl)
|
||||
insert: vnode => blind.init(vnode.elm as HTMLElement, ctrl)
|
||||
}
|
||||
}, [ ground.render(ctrl) ])
|
||||
}, [ renderGround(ctrl) ])
|
||||
]);
|
||||
}
|
||||
|
||||
var emptyMaterialDiff = {
|
||||
white: [],
|
||||
black: []
|
||||
const emptyMaterialDiff: cg.MaterialDiff = {
|
||||
white: {},
|
||||
black: {}
|
||||
};
|
||||
|
||||
export function main(ctrl: any): VNode {
|
||||
export function main(ctrl: RoundController): VNode {
|
||||
const d = ctrl.data,
|
||||
cgState = ctrl.chessground && ctrl.chessground.state,
|
||||
topColor = d[ctrl.vm.flip ? 'player' : 'opponent'].color,
|
||||
bottomColor = d[ctrl.vm.flip ? 'opponent' : 'player'].color;
|
||||
topColor = d[ctrl.flip ? 'player' : 'opponent'].color,
|
||||
bottomColor = d[ctrl.flip ? 'opponent' : 'player'].color;
|
||||
let material, score;
|
||||
if (d.pref.showCaptured) {
|
||||
var pieces = cgState ? cgState.pieces : fenRead(round.plyStep(ctrl.data, ctrl.vm.ply).fen);
|
||||
var pieces = cgState ? cgState.pieces : fenRead(plyStep(ctrl.data, ctrl.ply).fen);
|
||||
material = util.getMaterialDiff(pieces);
|
||||
score = util.getScore(pieces) * (bottomColor === 'white' ? 1 : -1);
|
||||
} else material = emptyMaterialDiff;
|
||||
|
@ -81,7 +82,7 @@ export function main(ctrl: any): VNode {
|
|||
d.blind ? blindBoard(ctrl) : visualBoard(ctrl),
|
||||
h('div.lichess_ground', [
|
||||
crazyView(ctrl, topColor, 'top') || renderMaterial(material[topColor], d.player.checks, undefined),
|
||||
table.render(ctrl),
|
||||
renderTable(ctrl),
|
||||
crazyView(ctrl, bottomColor, 'bottom') || renderMaterial(material[bottomColor], d.opponent.checks, score)
|
||||
])
|
||||
]),
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { h } from 'snabbdom'
|
||||
import { VNode, VNodeData } from 'snabbdom/vnode'
|
||||
import * as round from '../round';
|
||||
import { dropThrottle } from 'common';
|
||||
import { game, status, router, view as gameView } from 'game';
|
||||
import * as util from '../util';
|
||||
|
||||
import { h } from 'snabbdom'
|
||||
import { VNode } from 'snabbdom/vnode'
|
||||
import RoundController from '../ctrl';
|
||||
import { Step, MaybeVNodes, RoundData } from '../interfaces';
|
||||
|
||||
function emptyMove() {
|
||||
return h('move.empty', '...');
|
||||
|
@ -15,30 +16,30 @@ function nullMove() {
|
|||
|
||||
const scrollThrottle = dropThrottle(100);
|
||||
|
||||
function autoScroll(el, ctrl) {
|
||||
function autoScroll(el: HTMLElement, ctrl: RoundController) {
|
||||
scrollThrottle(function() {
|
||||
if (ctrl.data.steps.length < 7) return;
|
||||
let st;
|
||||
if (ctrl.vm.ply < 3) st = 0;
|
||||
else if (ctrl.vm.ply >= round.lastPly(ctrl.data) - 1) st = 9999;
|
||||
let st: number | undefined = undefined;
|
||||
if (ctrl.ply < 3) st = 0;
|
||||
else if (ctrl.ply >= round.lastPly(ctrl.data) - 1) st = 9999;
|
||||
else {
|
||||
const plyEl = el.querySelector('.active');
|
||||
const plyEl = el.querySelector('.active') as HTMLElement | undefined;
|
||||
if (plyEl) st = plyEl.offsetTop - el.offsetHeight / 2 + plyEl.offsetHeight / 2;
|
||||
}
|
||||
if (st !== undefined) el.scrollTop = st;
|
||||
});
|
||||
}
|
||||
|
||||
function renderMove(step, curPly, orEmpty) {
|
||||
function renderMove(step: Step, curPly: number, orEmpty?: boolean) {
|
||||
if (!step) return orEmpty ? emptyMove() : nullMove();
|
||||
var san = step.san[0] === 'P' ? step.san.slice(1) : step.san.replace('x', 'х');
|
||||
const san = step.san[0] === 'P' ? step.san.slice(1) : step.san.replace('x', 'х');
|
||||
return h('move', {
|
||||
class: { active: step.ply === curPly }
|
||||
}, san);
|
||||
}
|
||||
|
||||
function renderResult(ctrl) {
|
||||
var result;
|
||||
function renderResult(ctrl: RoundController) {
|
||||
let result;
|
||||
if (status.finished(ctrl.data)) switch (ctrl.data.game.winner) {
|
||||
case 'white':
|
||||
result = '1-0';
|
||||
|
@ -50,14 +51,14 @@ function renderResult(ctrl) {
|
|||
result = '½-½';
|
||||
}
|
||||
if (result || status.aborted(ctrl.data)) {
|
||||
var winner = game.getPlayer(ctrl.data, ctrl.data.game.winner);
|
||||
const winner = game.getPlayer(ctrl.data, ctrl.data.game.winner);
|
||||
return h('div.result_wrap', [
|
||||
h('p.result', result),
|
||||
h('p.status', {
|
||||
hook: {
|
||||
insert: _ => {
|
||||
if (ctrl.vm.autoScroll) ctrl.vm.autoScroll();
|
||||
else setTimeout(() => { ctrl.vm.autoScroll() }, 200);
|
||||
if (ctrl.autoScroll) ctrl.autoScroll();
|
||||
else setTimeout(() => ctrl.autoScroll(), 200);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
|
@ -69,7 +70,7 @@ function renderResult(ctrl) {
|
|||
return;
|
||||
}
|
||||
|
||||
function renderMoves(ctrl) {
|
||||
function renderMoves(ctrl: RoundController): MaybeVNodes {
|
||||
const steps = ctrl.data.steps,
|
||||
firstPly = round.firstPly(ctrl.data),
|
||||
lastPly = round.lastPly(ctrl.data);
|
||||
|
@ -81,10 +82,10 @@ function renderMoves(ctrl) {
|
|||
pairs.push([null, steps[1]]);
|
||||
startAt = 2;
|
||||
}
|
||||
for (var i = startAt; i < steps.length; i += 2) pairs.push([steps[i], steps[i + 1]]);
|
||||
for (let i = startAt; i < steps.length; i += 2) pairs.push([steps[i], steps[i + 1]]);
|
||||
|
||||
const els: Array<VNode | undefined> = [], curPly = ctrl.vm.ply;
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
const els: MaybeVNodes = [], curPly = ctrl.ply;
|
||||
for (let i = 0; i < pairs.length; i++) {
|
||||
els.push(h('index', i + 1 + ''));
|
||||
els.push(renderMove(pairs[i][0], curPly, true));
|
||||
els.push(renderMove(pairs[i][1], curPly, false));
|
||||
|
@ -94,27 +95,27 @@ function renderMoves(ctrl) {
|
|||
return els;
|
||||
}
|
||||
|
||||
function analyseButton(ctrl) {
|
||||
var showInfo = ctrl.forecastInfo();
|
||||
var data: any = {
|
||||
function analyseButton(ctrl: RoundController) {
|
||||
const showInfo = ctrl.forecastInfo();
|
||||
const data: VNodeData = {
|
||||
class: {
|
||||
'hint--top': !showInfo,
|
||||
'hint--bottom': showInfo,
|
||||
'glowed': showInfo,
|
||||
'text': ctrl.data.forecastCount
|
||||
'text': !!ctrl.data.forecastCount
|
||||
},
|
||||
attrs: {
|
||||
'data-hint': ctrl.trans.noarg('analysis'),
|
||||
href: router.game(ctrl.data, ctrl.data.player.color) + '/analysis#' + ctrl.vm.ply
|
||||
href: router.game(ctrl.data, ctrl.data.player.color) + '/analysis#' + ctrl.ply
|
||||
}
|
||||
};
|
||||
if (showInfo) data.hook = {
|
||||
insert: vnode => {
|
||||
insert(vnode) {
|
||||
setTimeout(() => {
|
||||
$(vnode.elm).powerTip({
|
||||
$(vnode.elm as HTMLElement).powerTip({
|
||||
closeDelay: 200,
|
||||
placement: 'n'
|
||||
}).data('powertipjq', $(vnode.elm).siblings('.forecast-info').clone().removeClass('none')).powerTip('show');
|
||||
}).data('powertipjq', $(vnode.elm as HTMLElement).siblings('.forecast-info').clone().removeClass('none')).powerTip('show');
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
@ -122,9 +123,9 @@ function analyseButton(ctrl) {
|
|||
h('a.fbt.analysis', data, [
|
||||
h('span', {
|
||||
attrs: util.dataIcon('A'),
|
||||
class: {text: ctrl.data.forecastCount}
|
||||
class: {text: !!ctrl.data.forecastCount}
|
||||
}),
|
||||
ctrl.data.forecastCount
|
||||
'' + ctrl.data.forecastCount
|
||||
]),
|
||||
showInfo ? h('div.forecast-info.info.none', [
|
||||
h('strong.title.text', { attrs: util.dataIcon('') }, 'Speed up your game!'),
|
||||
|
@ -133,10 +134,10 @@ function analyseButton(ctrl) {
|
|||
];
|
||||
}
|
||||
|
||||
function renderButtons(ctrl) {
|
||||
var d = ctrl.data;
|
||||
var firstPly = round.firstPly(d);
|
||||
var lastPly = round.lastPly(d);
|
||||
function renderButtons(ctrl: RoundController) {
|
||||
const d = ctrl.data,
|
||||
firstPly = round.firstPly(d),
|
||||
lastPly = round.lastPly(d);
|
||||
return h('div.buttons', {
|
||||
hook: util.bind('mousedown', e => {
|
||||
const target = e.target as HTMLElement;
|
||||
|
@ -147,13 +148,13 @@ function renderButtons(ctrl) {
|
|||
if (action === 'flip') {
|
||||
if (d.tv) location.href = '/tv/' + d.tv.channel + (d.tv.flip ? '' : '?flip=1');
|
||||
else if (d.player.spectator) location.href = router.game(d, d.opponent.color);
|
||||
else ctrl.flip();
|
||||
else ctrl.flipNow();
|
||||
}
|
||||
}
|
||||
}, ctrl.redraw)
|
||||
}, [
|
||||
h('button.fbt.flip.hint--top', {
|
||||
class: { active: ctrl.vm.flip },
|
||||
class: { active: ctrl.flip },
|
||||
attrs: {
|
||||
'data-hint': ctrl.trans('flipBoard'),
|
||||
'data-act': 'flip'
|
||||
|
@ -163,11 +164,11 @@ function renderButtons(ctrl) {
|
|||
]),
|
||||
h('nav', [
|
||||
['W', firstPly],
|
||||
['Y', ctrl.vm.ply - 1],
|
||||
['X', ctrl.vm.ply + 1],
|
||||
['Y', ctrl.ply - 1],
|
||||
['X', ctrl.ply + 1],
|
||||
['V', lastPly]
|
||||
].map((b, i) => {
|
||||
const enabled = ctrl.vm.ply !== b[1] && b[1] >= firstPly && b[1] <= lastPly;
|
||||
const enabled = ctrl.ply !== b[1] && b[1] >= firstPly && b[1] <= lastPly;
|
||||
return h('button.fbt', {
|
||||
class: { glowed: i === 3 && ctrl.isLate() },
|
||||
attrs: {
|
||||
|
@ -181,7 +182,7 @@ function renderButtons(ctrl) {
|
|||
]);
|
||||
}
|
||||
|
||||
function racingKingsInit(d) {
|
||||
function racingKingsInit(d: RoundData) {
|
||||
if (d.game.variant.key === 'racingKings' && d.game.turns === 0 && !d.player.spectator) {
|
||||
const yourTurn = d.player.color === 'white' ? [h('br'), h('strong', "it's your turn!")] : [];
|
||||
return h('div.message', {
|
||||
|
@ -194,12 +195,12 @@ function racingKingsInit(d) {
|
|||
return;
|
||||
}
|
||||
|
||||
export function render(ctrl: any): VNode {
|
||||
export default function(ctrl: RoundController): VNode {
|
||||
return h('div.replay', [
|
||||
renderButtons(ctrl),
|
||||
racingKingsInit(ctrl.data) || (ctrl.replayEnabledByPref() ? h('div.moves', {
|
||||
hook: {
|
||||
insert: vnode => {
|
||||
insert(vnode) {
|
||||
(vnode.elm as HTMLElement).addEventListener('mousedown', e => {
|
||||
let node = e.target as HTMLElement, offset = -2;
|
||||
if (node.tagName !== 'MOVE') return;
|
||||
|
@ -212,11 +213,11 @@ export function render(ctrl: any): VNode {
|
|||
}
|
||||
}
|
||||
});
|
||||
ctrl.vm.autoScroll = () => { autoScroll(vnode.elm, ctrl); };
|
||||
ctrl.vm.autoScroll();
|
||||
window.addEventListener('load', ctrl.vm.autoScroll);
|
||||
ctrl.autoScroll = () => autoScroll(vnode.elm as HTMLElement, ctrl); ;
|
||||
ctrl.autoScroll();
|
||||
window.addEventListener('load', ctrl.autoScroll);
|
||||
}
|
||||
}
|
||||
}, renderMoves(ctrl)) : renderResult(ctrl))
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import { game, status } from 'game';
|
||||
import clockView = require('../clock/view');
|
||||
import corresClockView from '../corresClock/view';
|
||||
import replay = require('./replay');
|
||||
import renderUser = require('./user');
|
||||
import button = require('./button');
|
||||
|
||||
import { h } from 'snabbdom'
|
||||
import { VNode } from 'snabbdom/vnode'
|
||||
import { MaybeVNodes } from '../interfaces';
|
||||
import { Position, MaybeVNodes } from '../interfaces';
|
||||
import { game, status, Player } from 'game';
|
||||
import { renderClock } from '../clock/clockView';
|
||||
import renderCorresClock from '../corresClock/corresClockView';
|
||||
import renderReplay from './replay';
|
||||
import * as renderUser from './user';
|
||||
import * as button from './button';
|
||||
import RoundController from '../ctrl';
|
||||
|
||||
function playerAt(ctrl, position) {
|
||||
return ctrl.vm.flip ^ ((position === 'top') as any) ? ctrl.data.opponent : ctrl.data.player;
|
||||
function playerAt(ctrl: RoundController, position: Position) {
|
||||
return (ctrl.flip as any) ^ ((position === 'top') as any) ? ctrl.data.opponent : ctrl.data.player;
|
||||
}
|
||||
|
||||
function topPlayer(ctrl) {
|
||||
function topPlayer(ctrl: RoundController) {
|
||||
return playerAt(ctrl, 'top');
|
||||
}
|
||||
|
||||
function bottomPlayer(ctrl) {
|
||||
function bottomPlayer(ctrl: RoundController) {
|
||||
return playerAt(ctrl, 'bottom');
|
||||
}
|
||||
|
||||
function renderPlayer(ctrl, player) {
|
||||
function renderPlayer(ctrl: RoundController, player: Player) {
|
||||
return player.ai ? h('div.username.user_link.online', [
|
||||
h('i.line'),
|
||||
h('name', renderUser.aiName(ctrl, player))
|
||||
|
@ -29,48 +29,48 @@ function renderPlayer(ctrl, player) {
|
|||
renderUser.userHtml(ctrl, player);
|
||||
}
|
||||
|
||||
function isLoading(ctrl) {
|
||||
return ctrl.vm.loading || ctrl.vm.redirecting;
|
||||
function isLoading(ctrl: RoundController): boolean {
|
||||
return ctrl.loading || ctrl.redirecting;
|
||||
}
|
||||
|
||||
function loader() { return h('span.ddloader'); }
|
||||
|
||||
function renderTableWith(ctrl, buttons: MaybeVNodes) {
|
||||
function renderTableWith(ctrl: RoundController, buttons: MaybeVNodes) {
|
||||
return [
|
||||
replay.render(ctrl),
|
||||
renderReplay(ctrl),
|
||||
h('div.control.buttons', buttons),
|
||||
renderPlayer(ctrl, bottomPlayer(ctrl))
|
||||
];
|
||||
}
|
||||
|
||||
function renderTableEnd(ctrl) {
|
||||
function renderTableEnd(ctrl: RoundController) {
|
||||
return renderTableWith(ctrl, [
|
||||
isLoading(ctrl) ? loader() : (button.backToTournament(ctrl) || button.followUp(ctrl))
|
||||
]);
|
||||
}
|
||||
|
||||
function renderTableWatch(ctrl) {
|
||||
function renderTableWatch(ctrl: RoundController) {
|
||||
return renderTableWith(ctrl, [
|
||||
isLoading(ctrl) ? loader() : button.watcherFollowUp(ctrl)
|
||||
]);
|
||||
}
|
||||
|
||||
function tournamentStartWarning(ctrl) {
|
||||
function tournamentStartWarning(ctrl: RoundController) {
|
||||
return h('div.suggestion', [
|
||||
h('div.text', { attrs: {'data-icon': 'j'} },
|
||||
ctrl.trans('youHaveNbSecondsToMakeYourFirstMove', ctrl.data.tournament.nbSecondsForFirstMove))
|
||||
ctrl.trans('youHaveNbSecondsToMakeYourFirstMove', ctrl.data.tournament!.nbSecondsForFirstMove))
|
||||
]);
|
||||
}
|
||||
|
||||
function renderTablePlay(ctrl) {
|
||||
function renderTablePlay(ctrl: RoundController) {
|
||||
const d = ctrl.data;
|
||||
const loading = isLoading(ctrl);
|
||||
let submit = button.submitMove(ctrl);
|
||||
let icons = (loading || submit) ? [] : [
|
||||
game.abortable(d) ? button.standard(ctrl, null, 'L', 'abortGame', 'abort', null) :
|
||||
game.abortable(d) ? button.standard(ctrl, undefined, 'L', 'abortGame', 'abort') :
|
||||
button.standard(ctrl, game.takebackable, 'i', 'proposeATakeback', 'takeback-yes', ctrl.takebackYes),
|
||||
button.standard(ctrl, ctrl.canOfferDraw, '2', 'offerDraw', 'draw-yes', ctrl.offerDraw),
|
||||
ctrl.vm.resignConfirm ? button.resignConfirm(ctrl) : button.standard(ctrl, game.resignable, 'b', 'resign', 'resign-confirm', ctrl.resign)
|
||||
ctrl.resignConfirm ? button.resignConfirm(ctrl) : button.standard(ctrl, game.resignable, 'b', 'resign', 'resign-confirm', () => ctrl.resign(true))
|
||||
];
|
||||
let buttons: MaybeVNodes = loading ? [loader()] : (submit ? [submit] : [
|
||||
button.forceResign(ctrl),
|
||||
|
@ -82,14 +82,14 @@ function renderTablePlay(ctrl) {
|
|||
(d.tournament && game.nbMoves(d, d.player.color) === 0) ? tournamentStartWarning(ctrl) : null
|
||||
]);
|
||||
return [
|
||||
replay.render(ctrl),
|
||||
renderReplay(ctrl),
|
||||
h('div.control.icons', icons),
|
||||
h('div.control.buttons', buttons),
|
||||
renderPlayer(ctrl, bottomPlayer(ctrl))
|
||||
];
|
||||
}
|
||||
|
||||
function whosTurn(ctrl, color) {
|
||||
function whosTurn(ctrl: RoundController, color: Color) {
|
||||
var d = ctrl.data;
|
||||
if (status.finished(d) || status.aborted(d)) return;
|
||||
return h('div.whos_turn',
|
||||
|
@ -101,18 +101,18 @@ function whosTurn(ctrl, color) {
|
|||
);
|
||||
}
|
||||
|
||||
function anyClock(ctrl, position) {
|
||||
function anyClock(ctrl: RoundController, position: Position) {
|
||||
var player = playerAt(ctrl, position);
|
||||
if (ctrl.clock) return clockView.renderClock(ctrl, player, position);
|
||||
if (ctrl.clock) return renderClock(ctrl, player, position);
|
||||
else if (ctrl.data.correspondence && ctrl.data.game.turns > 1)
|
||||
return corresClockView(
|
||||
ctrl.corresClock, ctrl.trans, player.color, position, ctrl.data.game.player
|
||||
return renderCorresClock(
|
||||
ctrl.corresClock!, ctrl.trans, player.color, position, ctrl.data.game.player
|
||||
);
|
||||
else return whosTurn(ctrl, player.color);
|
||||
}
|
||||
|
||||
export function render(ctrl: any): VNode {
|
||||
const contents: Array<VNode | string> = [
|
||||
export default function(ctrl: RoundController): VNode {
|
||||
const contents: MaybeVNodes = [
|
||||
renderPlayer(ctrl, topPlayer(ctrl)),
|
||||
h('div.table_inner',
|
||||
ctrl.data.player.spectator ? renderTableWatch(ctrl) : (
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
import { game } from 'game';
|
||||
|
||||
import { h } from 'snabbdom'
|
||||
import { VNode } from 'snabbdom/vnode'
|
||||
import { game, Player } from 'game';
|
||||
import RoundController from '../ctrl';
|
||||
|
||||
function ratingDiff(player) {
|
||||
function ratingDiff(player: Player): VNode | undefined {
|
||||
if (!player.ratingDiff) return;
|
||||
if (player.ratingDiff === 0) return h('span.rp.null', '±0');
|
||||
if (player.ratingDiff > 0) return h('span.rp.up', '+' + player.ratingDiff);
|
||||
if (player.ratingDiff < 0) return h('span.rp.down', player.ratingDiff);
|
||||
return;
|
||||
if (player.ratingDiff < 0) return h('span.rp.down', '' + player.ratingDiff);
|
||||
}
|
||||
|
||||
export function aiName(ctrl, player) {
|
||||
export function aiName(ctrl: RoundController, player: Player) {
|
||||
return ctrl.trans('aiNameLevelAiLevel', 'Stockfish', player.ai);
|
||||
}
|
||||
|
||||
export function userHtml(ctrl, player) {
|
||||
var d = ctrl.data;
|
||||
var user = player.user;
|
||||
var perf = user ? user.perfs[d.game.perf] : null;
|
||||
var rating = player.rating ? player.rating : (perf && perf.rating);
|
||||
export function userHtml(ctrl: RoundController, player: Player) {
|
||||
const d = ctrl.data,
|
||||
user = player.user,
|
||||
perf = user ? user.perfs[d.game.perf] : null,
|
||||
rating = player.rating ? player.rating : (perf && perf.rating);
|
||||
if (user) {
|
||||
var connecting = !player.onGame && ctrl.vm.firstSeconds && user.online;
|
||||
const connecting = !player.onGame && ctrl.firstSeconds && user.online;
|
||||
return h('div.username.user_link.' + player.color, {
|
||||
class: {
|
||||
online: player.onGame,
|
||||
|
@ -39,7 +40,7 @@ export function userHtml(ctrl, player) {
|
|||
href: '/@/' + user.username,
|
||||
target: game.isPlayerPlaying(d) ? '_blank' : '_self'
|
||||
}
|
||||
}, user.title ? [h('span.title', user.title), ' ', user.username] : user.username),
|
||||
}, user.title ? [h('span.title', user.title), ' ', user.username] : [user.username]),
|
||||
rating ? h('rating', rating + (player.provisional ? '?' : '')) : null,
|
||||
ratingDiff(player),
|
||||
player.engine ? h('span', {
|
||||
|
@ -50,7 +51,7 @@ export function userHtml(ctrl, player) {
|
|||
}) : null
|
||||
]);
|
||||
}
|
||||
var connecting = !player.onGame && ctrl.vm.firstSeconds;
|
||||
const connecting = !player.onGame && ctrl.firstSeconds;
|
||||
return h('div.username.user_link', {
|
||||
class: {
|
||||
online: player.onGame,
|
||||
|
@ -65,14 +66,15 @@ export function userHtml(ctrl, player) {
|
|||
}),
|
||||
h('name', player.name || 'Anonymous')
|
||||
]);
|
||||
};
|
||||
export function userTxt(ctrl, player) {
|
||||
}
|
||||
|
||||
export function userTxt(ctrl: RoundController, player: Player) {
|
||||
if (player.user) {
|
||||
var perf = player.user.perfs[ctrl.data.game.perf];
|
||||
var name = (player.user.title ? player.user.title + ' ' : '') + player.user.username;
|
||||
var rating = player.rating ? player.rating : (perf ? perf.rating : null);
|
||||
rating = rating ? ' (' + rating + (player.provisional ? '?' : '') + ')' : '';
|
||||
return name + rating;
|
||||
const perf = player.user.perfs[ctrl.data.game.perf],
|
||||
name = (player.user.title ? player.user.title + ' ' : '') + player.user.username,
|
||||
rating = player.rating ? player.rating : (perf ? perf.rating : null),
|
||||
showRating = rating ? ' (' + rating + (player.provisional ? '?' : '') + ')' : '';
|
||||
return name + showRating;
|
||||
} else if (player.ai) return aiName(ctrl, player)
|
||||
else return 'Anonymous';
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue