function renderChildrenOf(ctx, node, opts): MaybeVNodes { const cs = node.children, main = cs[0]; if (!main) return []; if (opts.isMainline) { const isWhite = main.ply % 2 === 1; if (!cs[1]) return [ isWhite ? renderIndex(main.ply, false) : null, ...renderMoveAndChildrenOf(ctx, main, { parentPath: opts.parentPath, isMainline: true }) ]; const mainChildren = renderChildrenOf(ctx, main, { parentPath: opts.parentPath + main.id, isMainline: true }), passOpts = { parentPath: opts.parentPath, isMainline: true }; return [ isWhite ? renderIndex(main.ply, false) : null, renderMoveOf(ctx, main, passOpts), isWhite ? emptyMove() : null, h('interrupt', renderLines(ctx, cs.slice(1), { parentPath: opts.parentPath, isMainline: true })), ...(isWhite && mainChildren ? [ renderIndex(main.ply, false), emptyMove() ] : []), ...mainChildren ]; } return cs[1] ? [renderLines(ctx, cs, opts)] : renderMoveAndChildrenOf(ctx, main, opts); }
export default function(ctrl: Controller): VNode { const showCeval = ctrl.vm.showComputer(); if (cevalShown !== showCeval) { if (!cevalShown) ctrl.vm.autoScrollNow = true; cevalShown = showCeval; } return h('div#puzzle.cg-512', [ h('div', { hook: { insert: _ => window.lichess.pubsub.emit('reset_zoom')() }, class: { gauge_displayed: ctrl.showEvalGauge() } }, [ h('div.lichess_game' + (ctrl.pref.blindfold ? '.blindfold' : ''), { hook: { insert: _ => window.lichess.pubsub.emit('content_loaded')() } }, [ visualBoard(ctrl), h('div.lichess_ground', [ // we need the wrapping div here // so the siblings are only updated when ceval is added h('div', showCeval ? [ cevalView.renderCeval(ctrl), cevalView.renderPvs(ctrl) ] : []), renderAnalyse(ctrl), feedbackView(ctrl), buttons(ctrl) ]) ]) ]), h('div.underboard', [ h('div.center', [ historyView(ctrl) ]) ]) ]); };
function renderTournament(ctrl, tour) { let width = tour.minutes * scale; const left = leftPos(tour.startsAt); // moves content into viewport, for long tourneys and marathons const paddingLeft = tour.minutes < 90 ? 0 : Math.max(0, Math.min(width - 250, // max padding, reserved text space leftPos(now) - left - 380)); // distance from Now // cut right overflow to fit viewport and not widen it, for marathons width = Math.min(width, leftPos(stopTime) - left); return h('a.tournament', { class: tournamentClass(tour), attrs: { href: '/tournament/' + tour.id, style: 'width: ' + width + 'px; left: ' + left + 'px; padding-left: ' + paddingLeft + 'px' }, }, [ h('span.icon', tour.perf ? { attrs: { 'data-icon': tour.perf.icon, title: tour.perf.name } } : {}), h('span.body', [ h('span.name', tour.fullName), h('span.infos', [ h('span.text', [ displayClock(tour.clock) + ' ', tour.variant.key === 'standard' ? null : tour.variant.name + ' ', tour.position ? 'Thematic ' : null, tour.rated ? ctrl.trans('rated') : ctrl.trans('casual') ]), tour.nbPlayers ? h('span.nb-players', { attrs: { 'data-icon': 'r' } }, tour.nbPlayers) : null ]) ]) ]); }
export function view(ctrl): VNode { const candidates = ctrl.candidates(); return dialog.form({ class: 'study_invite', onClose() { ctrl.open(false); ctrl.redraw(); }, content: [ h('h2', 'Invite to the study'), h('p.info', { attrs: { 'data-icon': '' } }, [ 'Please only invite people you know,', h('br'), 'and who actively want to join this study.' ]), candidates.length ? h('div.users', candidates.map(function(username) { return h('span.user_link.button', { key: username, attrs: { 'data-href': '/@/' + username }, hook: bind('click', _ => ctrl.invite(username)) }, username); })) : undefined, h('div.input-wrapper', [ // because typeahead messes up with snabbdom h('input', { attrs: { placeholder: 'Search by username' }, hook: { insert: vnode => { const el = vnode.elm as HTMLInputElement; window.lichess.userAutocomplete($(el), { tag: 'span', onSelect(v) { ctrl.invite(v.name); $(el).typeahead('close'); el.value = ''; ctrl.redraw(); } }); } } }) ]) ] }); }
ctrl.data.nowPlaying.map(function(pov) { return h('a.mini_board.is2d.' + pov.variant.key + (pov.isMyTurn ? '.my_turn' : ''), { key: pov.gameId, attrs: { href: '/' + pov.fullId } }, [ h('div', [ h('div.cg-board-wrap', { hook: { insert(vnode) { const lm = pov.lastMove; const config = { coordinates: false, drawable: { enabled: false, visible: false }, resizable: false, viewOnly: true, orientation: pov.variant.key === 'racingKings' ? 'white' : pov.color, fen: pov.fen, lastMove: lm && [lm[0] + lm[1], lm[2] + lm[3]] }; Chessground(vnode.elm as HTMLElement, config); } } }, [ h('div.cg-board') ]) ]), h('span.meta', [ pov.opponent.ai ? ctrl.trans('aiNameLevelAiLevel', 'Stockfish', pov.opponent.ai) : pov.opponent.username, h('span.indicator', pov.isMyTurn ? (pov.secondsLeft ? timer(pov) : ctrl.trans('yourTurn')) : h('span', { hook: { insert(vnode) { (vnode.elm as HTMLElement).innerHTML = ' '; } } })) ]) ]); }));
export default function(ctrl, color, position) { if (!ctrl.node.crazy) return; const pocket = ctrl.node.crazy.pockets[color === 'white' ? 0 : 1]; const dropped = ctrl.justDropped; let captured = ctrl.justCaptured; if (captured) { captured = captured.promoted ? 'pawn' : captured.role; } const activeColor = color === ctrl.turnColor(); const usable = !ctrl.embed && activeColor; return h('div.pocket.is2d.' + position, { class: { usable }, hook: { insert: vnode => { if (ctrl.embed) return; eventNames.forEach(name => { (vnode.elm as HTMLElement).addEventListener(name, e => drag(ctrl, color, e as MouchEvent)); }); } } }, oKeys.map(role => { let nb = pocket[role] || 0; if (activeColor) { if (dropped === role) nb--; if (captured === role) nb++; } return h('piece.' + role + '.' + color, { attrs: { 'data-role': role, 'data-color': color, 'data-nb': nb } }); }) ); }
function renderMoves(ctrl: RoundController): MaybeVNodes { const steps = ctrl.data.steps, firstPly = round.firstPly(ctrl.data), lastPly = round.lastPly(ctrl.data); if (typeof lastPly === 'undefined') return []; const pairs: Array<Array<any>> = []; let startAt = 1; if (firstPly % 2 === 1) { pairs.push([null, steps[1]]); startAt = 2; } for (let i = startAt; i < steps.length; i += 2) pairs.push([steps[i], steps[i + 1]]); 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)); els.push(renderMove(pairs[i][1], curPly)); } els.push(renderResult(ctrl)); return els; }
return (c: Challenge) => { return h('div.challenge.' + dir + '.c-' + c.id, { class: { declined: !!c.declined } }, [ h('div.content', [ h('span.head', renderUser(dir === 'in' ? c.challenger : c.destUser)), h('span.desc', [ ctrl.trans()(c.rated ? 'rated' : 'casual'), timeControl(c.timeControl), c.variant.name ].join(' • ')) ]), h('i', { attrs: {'data-icon': c.perf.icon} }), h('div.buttons', (dir === 'in' ? inButtons : outButtons)(ctrl, c)) ]); };
export function view(ctrl: BoardCtrl): VNode { return h('div.sub.board', [ header(ctrl.trans.noarg('boardGeometry'), ctrl.close), h('div.selector', [ h('a.text', { class: { active: !ctrl.data.is3d }, attrs: { 'data-icon': 'E' }, hook: bind('click', () => ctrl.setIs3d(false)) }, '2D'), h('a.text', { class: { active: ctrl.data.is3d }, attrs: { 'data-icon': 'E' }, hook: bind('click', () => ctrl.setIs3d(true)) }, '3D') ]), h('div.zoom', [ h('h2', ctrl.trans.noarg('boardSize')), h('div.slider', { hook: { insert: vnode => makeSlider(ctrl, vnode.elm as HTMLElement) } }) ]) ]); }
export function renderIndexAndMove(ctx: Ctx, node): VNode[] { return node.uci ? [renderIndex(node.ply, ctx.withDots)].concat(renderMove(ctx, node)) : [h('span.init', 'Initial position')]; }