<commit_before>import { h, VNode } from 'snabbdom';
import { numberFormat } from 'common/number';
import { perf } from 'game/perf';
import { bind, dataIcon, MaybeVNode } from 'common/snabbdom';
import { defined } from 'common';
import { view as renderConfig } from './explorerConfig';
import { moveArrowAttributes, ucfirst } from './explorerUtil';
import AnalyseCtrl from '../ctrl';
import { isOpening, isTablebase, TablebaseCategory, OpeningData, OpeningMoveStats, OpeningGame } from './interfaces';
import ExplorerCtrl from './explorerCtrl';
import { showTablebase } from './tablebaseView';

function resultBar(move: OpeningMoveStats): VNode {
  const sum = move.white + move.draws + move.black;
  function section(key: 'white' | 'black' | 'draws') {
    const percent = (move[key] * 100) / sum;
    return h(
      'span.' + key,
      {
        attrs: { style: 'width: ' + Math.round((move[key] * 1000) / sum) / 10 + '%' },
      },
      percent > 12 ? Math.round(percent) + (percent > 20 ? '%' : '') : ''
    );
  }
  return h('div.bar', ['white', 'draws', 'black'].map(section));
}

function showMoveTable(ctrl: AnalyseCtrl, data: OpeningData): VNode | null {
  if (!data.moves.length) return null;
  const trans = ctrl.trans.noarg;
  const movesWithCurrent =
    data.moves.length > 1 && [data.white, data.black, data.draws].every(defined)
      ? [
          ...data.moves,
          {
            ...data,
            uci: '',
            san: 'Σ',
          } as OpeningMoveStats,
        ]
      : data.moves;

  return h('table.moves', [
    h('thead', [
      h('tr', [h('th.title', trans('move')), h('th.title', trans('games')), h('th.title', trans('whiteDrawBlack'))]),
    ]),
    h(
      'tbody',
      moveArrowAttributes(ctrl, { fen: data.fen, onClick: (_, uci) => uci && ctrl.explorerMove(uci) }),
      movesWithCurrent.map(move =>
        h(
          `tr.expl-uci-${move.uci}`,
          {
            attrs: {
              'data-uci': move.uci,
              title: move.uci ? (move.averageRating ? ctrl.trans('averageRatingX', move.averageRating) : '') : 'Total',
            },
          },
          [
            h('td', move.san[0] === 'P' ? move.san.slice(1) : move.san),
            h('td', numberFormat(move.white + move.draws + move.black)),
            h('td', resultBar(move)),
          ]
        )
      )
    ),
  ]);
}

function showResult(winner?: Color): VNode {
  if (winner === 'white') return h('result.white', '1-0');
  if (winner === 'black') return h('result.black', '0-1');
  return h('result.draws', '½-½');
}

function showGameTable(ctrl: AnalyseCtrl, fen: Fen, title: string, games: OpeningGame[]): VNode | null {
  if (!ctrl.explorer.withGames || !games.length) return null;
  const openedId = ctrl.explorer.gameMenu();
  return h('table.games', [
    h('thead', [h('tr', [h('th.title', { attrs: { colspan: 5 } }, title)])]),
    h(
      'tbody',
      moveArrowAttributes(ctrl, {
        fen,
        onClick: (e, _) => {
          const $tr = $(e.target as HTMLElement).parents('tr');
          if (!$tr.length) return;
          const id = $tr.data('id');
          if (ctrl.study && ctrl.study.members.canContribute()) {
            ctrl.explorer.gameMenu(id);
            ctrl.redraw();
          } else openGame(ctrl, id);
        },
      }),
      games.map(game => {
        return openedId === game.id
          ? gameActions(ctrl, game)
          : h(
              'tr',
              {
                key: game.id,
                attrs: { 'data-id': game.id, 'data-uci': game.uci || '' },
              },
              [
                h(
                  'td',
                  [game.white, game.black].map(p => h('span', '' + p.rating))
                ),
                h(
                  'td',
                  [game.white, game.black].map(p => h('span', p.name))
                ),
                h('td', showResult(game.winner)),
                h('td', game.year || game.month),
                h(
                  'td',
                  game.speed &&
                    h('i', {
                      attrs: {
                        title: ucfirst(game.speed),
                        ...dataIcon(perf.icons[game.speed]),
                      },
                    })
                ),
              ]
            );
      })
    ),
  ]);
}

function openGame(ctrl: AnalyseCtrl, gameId: string) {
  const orientation = ctrl.chessground.state.orientation,
    fenParam = ctrl.node.ply > 0 ? '?fen=' + ctrl.node.fen : '';
  let url = '/' + gameId + '/' + orientation + fenParam;
  if (ctrl.explorer.db() === 'masters') url = '/import/master' + url;
  window.open(url, '_blank', 'noopener');
}

function gameActions(ctrl: AnalyseCtrl, game: OpeningGame): VNode {
  function send(insert: boolean) {
    ctrl.study!.explorerGame(game.id, insert);
    ctrl.explorer.gameMenu(null);
    ctrl.redraw();
  }
  return h(
    'tr',
    {
      key: game.id + '-m',
    },
    [
      h(
        'td.game_menu',
        {
          attrs: { colspan: 4 },
        },
        [
          h('div.game_title', `${game.white.name} - ${game.black.name}, ${showResult(game.winner).text}, ${game.year}`),
          h('div.menu', [
            h(
              'a.text',
              {
                attrs: dataIcon(''),
                hook: bind('click', _ => openGame(ctrl, game.id)),
              },
              'View'
            ),
            ...(ctrl.study
              ? [
                  h(
                    'a.text',
                    {
                      attrs: dataIcon(''),
                      hook: bind('click', _ => send(false), ctrl.redraw),<commit_msg>update colspan of explorer game menu in studies<commit_after>import { h, VNode } from 'snabbdom';
import { numberFormat } from 'common/number';
import { perf } from 'game/perf';
import { bind, dataIcon, MaybeVNode } from 'common/snabbdom';
import { defined } from 'common';
import { view as renderConfig } from './explorerConfig';
import { moveArrowAttributes, ucfirst } from './explorerUtil';
import AnalyseCtrl from '../ctrl';
import { isOpening, isTablebase, TablebaseCategory, OpeningData, OpeningMoveStats, OpeningGame } from './interfaces';
import ExplorerCtrl from './explorerCtrl';
import { showTablebase } from './tablebaseView';

function resultBar(move: OpeningMoveStats): VNode {
  const sum = move.white + move.draws + move.black;
  function section(key: 'white' | 'black' | 'draws') {
    const percent = (move[key] * 100) / sum;
    return h(
      'span.' + key,
      {
        attrs: { style: 'width: ' + Math.round((move[key] * 1000) / sum) / 10 + '%' },
      },
      percent > 12 ? Math.round(percent) + (percent > 20 ? '%' : '') : ''
    );
  }
  return h('div.bar', ['white', 'draws', 'black'].map(section));
}

function showMoveTable(ctrl: AnalyseCtrl, data: OpeningData): VNode | null {
  if (!data.moves.length) return null;
  const trans = ctrl.trans.noarg;
  const movesWithCurrent =
    data.moves.length > 1 && [data.white, data.black, data.draws].every(defined)
      ? [
          ...data.moves,
          {
            ...data,
            uci: '',
            san: 'Σ',
          } as OpeningMoveStats,
        ]
      : data.moves;

  return h('table.moves', [
    h('thead', [
      h('tr', [h('th.title', trans('move')), h('th.title', trans('games')), h('th.title', trans('whiteDrawBlack'))]),
    ]),
    h(
      'tbody',
      moveArrowAttributes(ctrl, { fen: data.fen, onClick: (_, uci) => uci && ctrl.explorerMove(uci) }),
      movesWithCurrent.map(move =>
        h(
          `tr.expl-uci-${move.uci}`,
          {
            attrs: {
              'data-uci': move.uci,
              title: move.uci ? (move.averageRating ? ctrl.trans('averageRatingX', move.averageRating) : '') : 'Total',
            },
          },
          [
            h('td', move.san[0] === 'P' ? move.san.slice(1) : move.san),
            h('td', numberFormat(move.white + move.draws + move.black)),
            h('td', resultBar(move)),
          ]
        )
      )
    ),
  ]);
}

function showResult(winner?: Color): VNode {
  if (winner === 'white') return h('result.white', '1-0');
  if (winner === 'black') return h('result.black', '0-1');
  return h('result.draws', '½-½');
}

function showGameTable(ctrl: AnalyseCtrl, fen: Fen, title: string, games: OpeningGame[]): VNode | null {
  if (!ctrl.explorer.withGames || !games.length) return null;
  const openedId = ctrl.explorer.gameMenu();
  return h('table.games', [
    h('thead', [h('tr', [h('th.title', { attrs: { colspan: 5 } }, title)])]),
    h(
      'tbody',
      moveArrowAttributes(ctrl, {
        fen,
        onClick: (e, _) => {
          const $tr = $(e.target as HTMLElement).parents('tr');
          if (!$tr.length) return;
          const id = $tr.data('id');
          if (ctrl.study && ctrl.study.members.canContribute()) {
            ctrl.explorer.gameMenu(id);
            ctrl.redraw();
          } else openGame(ctrl, id);
        },
      }),
      games.map(game => {
        return openedId === game.id
          ? gameActions(ctrl, game)
          : h(
              'tr',
              {
                key: game.id,
                attrs: { 'data-id': game.id, 'data-uci': game.uci || '' },
              },
              [
                h(
                  'td',
                  [game.white, game.black].map(p => h('span', '' + p.rating))
                ),
                h(
                  'td',
                  [game.white, game.black].map(p => h('span', p.name))
                ),
                h('td', showResult(game.winner)),
                h('td', game.year || game.month),
                h(
                  'td',
                  game.speed &&
                    h('i', {
                      attrs: {
                        title: ucfirst(game.speed),
                        ...dataIcon(perf.icons[game.speed]),
                      },
                    })
                ),
              ]
            );
      })
    ),
  ]);
}

function openGame(ctrl: AnalyseCtrl, gameId: string) {
  const orientation = ctrl.chessground.state.orientation,
    fenParam = ctrl.node.ply > 0 ? '?fen=' + ctrl.node.fen : '';
  let url = '/' + gameId + '/' + orientation + fenParam;
  if (ctrl.explorer.db() === 'masters') url = '/import/master' + url;
  window.open(url, '_blank', 'noopener');
}

function gameActions(ctrl: AnalyseCtrl, game: OpeningGame): VNode {
  function send(insert: boolean) {
    ctrl.study!.explorerGame(game.id, insert);
    ctrl.explorer.gameMenu(null);
    ctrl.redraw();
  }
  return h(
    'tr',
    {
      key: game.id + '-m',
    },
    [
      h(
        'td.game_menu',
        {
          attrs: { colspan: 5 },
        },
        [
          h('div.game_title', `${game.white.name} - ${game.black.name}, ${showResult(game.winner).text}, ${game.year}`),
          h('div.menu', [
            h(
              'a.text',
              {
                attrs: dataIcon(''),
                hook: bind('click', _ => openGame(ctrl, game.id)),
              },
              'View'
            ),
            ...(ctrl.study
              ? [
                  h(
                    'a.text',
                    {
                      attrs: dataIcon(''),
                      hook: bind('click', _ => send(false), ctrl.redraw),
{'fraction_non_alphanumeric': 0.17543550941404187, 'fraction_numerical': 0.004047158191096252, 'mean_word_length': 1.7167782026768643, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6632829', 'n_tokens_mistral': 3662, 'n_tokens_neox': 3515, 'n_words': 712}