<commit_before>import chessground from 'chessground-mobile';
import settings from '../../../settings';
import layout from '../../layout';
import { menuButton, loader, headerBtns } from '../../shared/common';
import popupWidget from '../../shared/popup';
import formWidgets from '../../shared/form';
import { view as renderClock } from '../clock/clockView';
import { view as renderPromotion } from '../promotion';
import * as utils from '../../../utils';
import helper from '../../helper';
import i18n from '../../../i18n';
import button from './button';
import gameApi from '../../../lichess/game';
import { perfTypes } from '../../../lichess/perfs';
import gameStatusApi from '../../../lichess/status';
import { view as renderChat } from '../chat';
import { view as renderCorrespondenceClock } from '../correspondenceClock/correspondenceView';
import variantApi from '../../../lichess/variant';
import { renderTable as renderReplayTable } from './replay';
import socket from '../../../socket';
import m from 'mithril';

export default function view(ctrl) {
  function overlay() {
    return [
      ctrl.chat ? renderChat(ctrl.chat) : null,
      renderPromotion(ctrl),
      renderGamePopup(ctrl),
      renderSubmitMovePopup(ctrl)
    ];
  }

  return layout.board(
    renderHeader.bind(undefined, ctrl),
    renderContent.bind(undefined, ctrl),
    overlay
  );
}

export function renderMaterial(material) {
  const children = [];
  for (let role in material) {
    let piece =
;
    let count = material[role];
    let content;
    if (count === 1) content = piece;
    else {
      content = [];
      for (let i = 0; i < count; i++) content.push(piece);
    }
    children.push(
{content}
);
  }
  return children;
}

export function renderBoard(ctrl, moreWrapperClasses, withStyle = true) {
  const { vh, vw } = helper.viewportDim();
  // ios 7.1 still doesn't support vh unit in calc
  // see board-content.styl section '.board_wrapper' for corresponding calc() rules
  const landscapeDim = (vh > 700 && vw < 1050) ? vh - 50 - vh * 0.12 : vh - 50;
  const boardStyle = helper.isLandscape() ? {
    width: landscapeDim + 'px',
    height: landscapeDim + 'px'
  } : {
    width: vw + 'px',
    height: vw + 'px'
  };
  const boardKey = helper.isLandscape() ? 'landscape' : 'portrait';
  const boardClass = [
    'board',
    settings.general.theme.board(),
    settings.general.theme.piece(),
    ctrl.data.game.variant.key
  ].join(' ');
  let wrapperClass = 'board_wrapper';

  if (moreWrapperClasses) {
    wrapperClass += ' ';
    wrapperClass += moreWrapperClasses;
  }

  function boardConfig(el, isUpdate) {
    if (!isUpdate || !ctrl.vm.renderedBoard) {
      chessground.render(el, ctrl.chessground);
      ctrl.vm.renderedBoard = true;
    }
  }

  return (

  );
}

function renderHeader(ctrl) {
  return (

      {menuButton()}
      {socket.isConnected() ?

        {ctrl.data.userTV ?  : null}
        {ctrl.title}

 : utils.hasNetwork() ?

        {i18n('reconnecting')}
        {loader}

 :
Offline

      }
      {headerBtns()}

  );
}

function renderContent(ctrl) {
  const material = chessground.board.getMaterialDiff(ctrl.chessground.data);
  const replayTable = renderReplayTable(ctrl);
  const player = renderPlayTable(ctrl, ctrl.data.player, material[ctrl.data.player.color], 'player');
  const opponent = renderPlayTable(ctrl, ctrl.data.opponent, material[ctrl.data.opponent.color], 'opponent');

  if (helper.isPortrait())
    return [
      opponent,
      renderBoard(ctrl),
      player,
      renderGameActionsBar(ctrl)
    ];
  else
    return [
      renderBoard(ctrl),

          {gameInfos(ctrl.data)}

          {opponent}
          {replayTable}
          {player}

        {renderGameActionsBar(ctrl)}

    ];
}

function compact(x) {
  if (Object.prototype.toString.call(x) === '[object Array]') {
    var elems = x.filter(function(n) {
      return n !== undefined;
    });
    return elems.length > 0 ? elems : null;
  }
  return x;
}

function ratingDiff(player) {
  if (typeof player.ratingDiff === 'undefined') return null;
  if (player.ratingDiff === 0) return m('span.rp.null', ' +0');
  if (player.ratingDiff > 0) return m('span.rp.up', ' +' + player.ratingDiff);
  if (player.ratingDiff < 0) return m('span.rp.down', ' ' + player.ratingDiff);
}

function renderCheckCount(ctrl, color) {
  const player = color === ctrl.data.player.color ? ctrl.data.opponent : ctrl.data.player;
  if (typeof player.checks !== 'undefined')
    return
{player.checks}
;
}

function renderSubmitMovePopup(ctrl) {
  if (!ctrl.vm.moveToSubmit)
    return
;

  return (

        {button.submitMove(ctrl)}

  );
}

function userInfos(user, player, playerName) {
  let title;
  if (user) {
    let onlineStatus = user.online ? 'connected to lichess' : 'offline';
    let onGameStatus = player.onGame ? 'currently on this game' : 'currently not on this game';
    title = `${playerName}: ${onlineStatus}; ${onGameStatus}`;
  } else
    title = playerName;
  window.plugins.toast.show(title, 'short', 'center');
}

function renderAntagonistInfo(ctrl, player, material, position) {
  const vmKey = position + 'Hash';
  const user = player.user;
  const playerName = utils.playerName(player, helper.isLandscape());
  const vConf = user ?
    helper.ontouch(utils.f(m.route, '/@/' + user.id), () => userInfos(user, player, playerName)) :
    utils.noop;

  const username = user ? user.username : 'anon';
  const onlineStatus = user && user.online ? 'online' : 'offline';
  const diff = ratingDiff(player);

  const hash = username + onlineStatus + player.onGame + player.rating + player.provisional + diff + player.checks + Object.keys(material).map(k => k + material[k]);

  if (ctrl.vm[vmKey] === hash) return {
    subtree: 'retain'
  };
  ctrl.vm[vmKey] = hash;

  return (

        {user ?
           :
          null
        }
        {playerName}
        {player.onGame ?
           :

        }

        {user && helper.isPortrait() ?

            {player.rating}
            {player.provisional ? '?' : ''}
            {diff}

 : null
        }
        {renderCheckCount(ctrl, player.color)}
        {ctrl.data.game.variant.key === 'horde' ? null : renderMaterial(material)}

  );
}

function renderPlayTable(ctrl, player, material, position) {
  const {vh, vw} = helper.viewportDim();
  const headerHeight = vh > 480 ? 50 : 40;
  const contentHeight = vh - headerHeight;
  // must do this here because of the lack of `calc` support
  // 45 refers to game actions bar height
  const style = helper.isLandscape() ? {} : { height: ((contentHeight - vw - 45) / 2) + 'px' };
  const key = helper.isLandscape() ? position + '-landscape' : position + '-portrait';
  const runningColor = ctrl.isClockRunning() ? ctrl.data.game.player : null;

  return (

      {renderAntagonistInfo(ctrl, player, material, position)}
      {ctrl.clock ?
        renderClock(ctrl.clock, player.color, runningColor) : (
        ctrl.data.correspondence ?
          renderCorrespondenceClock(
            ctrl.correspondenceClock, player.color, ctrl.data.game.player
          ) : null
        )
      }

  );
}

function tvChannelSelector(ctrl) {
  let channels = perfTypes.filter(e => e[0] !== 'correspondence').map(e => [e[1], e[0]]);
  channels.unshift(['Top rated', 'best']);
  channels.push(['Computer', 'computer']);

  return m('div.action', m('div.select_input',
    formWidgets.renderSelect('TV channel', 'tvChannel', channels, settings.tv.channel,
      false, ctrl.onTVChannelChange)
  ));
}

function renderGameRunningActions(ctrl) {
  if (ctrl.data.player.spectator) {
    let controls = [
      button.shareLink(ctrl),
      ctrl.data.tv ? tvChannelSelector(ctrl) : null
    ];

    return
{controls}
;
  }

  var d = ctrl.data;
  var answerButtons = compact([
    button.cancelDrawOffer(ctrl),
    button.answerOpponentDrawOffer(ctrl),
    button.cancelTakebackProposition(ctrl),
    button.answerOpponentTakebackProposition(ctrl),
    (gameApi.mandatory(d) && gameApi.nbMoves(d, d.player.color) === 0) ? m('div.text[data-icon=j]',
      i18n('youHaveNbSecondsToMakeYourFirstMove', 30)
    ) : undefined
  ]);

  return [
    m('div.game_controls', [
      button.shareLink(ctrl),
      button.moretime(ctrl),
      button.standard(ctrl, gameApi.abortable, 'L', 'abortGame', 'abort'),
      button.forceResign(ctrl) || [
        button.standard(ctrl, gameApi.takebackable, 'i', 'proposeATakeback', 'takeback-yes'),
        button.standard(ctrl, gameApi.drawable, '2', 'offerDraw', 'draw-yes'),
        button.standard(ctrl, gameApi.resignable, 'b', 'resign', 'resign'),
        button.threefoldClaimDraw(ctrl)
      ]
    ]),
    answerButtons ? m('div.answers', answerButtons) : null
  ];
}

function renderGameEndedActions(ctrl) {
  var result = gameApi.result(ctrl.data);
  var winner = gameApi.getPlayer(ctrl.data, ctrl.data.game.winner);
  var status = gameStatusApi.toLabel(ctrl.data.game.status.name, ctrl.data.game.winner, ctrl.data.game.variant.key) +
    (winner ? '. ' + i18n(winner.color === 'white' ? 'whiteIsVictorious' : 'blackIsVictorious') + '.' : '');
  const resultDom = gameStatusApi.aborted(ctrl.data) ? [] : [
    result, m('br'), m('br')
  ];
  resultDom.push(m('div.resultStatus', status));
  const buttons = ctrl.data.player.spectator ? [
    button.shareLink(ctrl),
    button.sharePGN(ctrl),
    ctrl.data.tv ? tvChannelSelector(ctrl) : null
  ] : [
    button.shareLink(ctrl),
    button.sharePGN(ctrl),
    button.newOpponent(ctrl),
    button.answerOpponentRematch(ctrl),
    button.cancelRematch(ctrl),
    button.rematch(ctrl)
  ];
  return [
    m('div.result', resultDom),
    m('div.control.buttons', buttons)
  ];
}

function gameInfos(data) {
  var time = gameApi.time(data);
  var mode = data.game.rated ? i18n('rated') : i18n('casual');
  var icon = data.opponent.ai ? ':' : utils.gameIcon(data.game.perf);
  var variant = m('span.variant', {
    config: helper.ontouch(
      () => {
        var link = variantApi(data.game.variant.key).link;
        if (link)
          window.open(link, '_blank');
      },
      () => window.plugins.toast.show(data.game.variant.title, 'short', 'center')
    )
  }, data.game.variant.name);
  var infos = [time + ' • ', variant, m('br'), mode];
  return [
    m('div.icon-game', {
      'data-icon': icon ? icon : ''
    }),
    m('div.game-title.no_select', infos)
  ];
}

function renderGamePopup(ctrl) {
  return popupWidget(
    'player_controls',
    helper.isPortrait() ? gameInfos(ctrl.data) : null,
    gameApi.playable(ctrl.data) ?
      renderGameRunningActions.bind(undefined, ctrl) : renderGameEndedActions.bind(undefined, ctrl),
    ctrl.vm.showingActions,
    ctrl.hideActions
  );
}

function renderGameActionsBar(ctrl) {
  const answerRequired = ctrl.data.opponent.proposingTakeback ||
    ctrl.data.opponent.offeringDraw ||
    gameApi.forceResignable(ctrl.data) ||
    ctrl.data.opponent.offeringRematch;

  const prevPly = ctrl.vm.ply - 1;
  const nextPly = ctrl.vm.ply + 1;
  const bwdOn = ctrl.vm.ply !== prevPly && prevPly >= ctrl.firstPly();
  const fwdOn = ctrl.vm.ply !== nextPly && nextPly <= ctrl.lastPly();
  const hash = answerRequired + (!ctrl.chat || ctrl.chat.unread) + ctrl.vm.flip + bwdOn + fwdOn;

  if (ctrl.vm.buttonsHash === hash) return {
    subtree: 'retain'
  };
  ctrl.vm.buttonsHash = hash;

  const gmClass = [
    'action_bar_button',
    'fa',
    'fa-ellipsis-h',
    answerRequired ? 'glow' : ''
  ].join(' ');

  const chatClass = [
    'action_bar_button',
    ctrl.chat && ctrl.chat.unread ? 'glow' : ''
  ].join(' ');

  return (

      <button className={gmClass} key="gameMenu" config={helper.ontouch(ctrl.showActions)} />
      {ctrl.chat ?
      <button className={chatClass} data-icon="c" key="chat" config={helper.ontouch(ctrl.chat.open || utils.noop)} /> : <button className="action_bar_button empty" />
      }
      {button.flipBoard(ctrl)}
      {button.backward(ctrl)}
      {button.forward(ctrl)}

  );
}
<commit_msg>Tweak<commit_after>import chessground from 'chessground-mobile';
import settings from '../../../settings';
import layout from '../../layout';
import { menuButton, loader, headerBtns } from '../../shared/common';
import popupWidget from '../../shared/popup';
import formWidgets from '../../shared/form';
import { view as renderClock } from '../clock/clockView';
import { view as renderPromotion } from '../promotion';
import * as utils from '../../../utils';
import helper from '../../helper';
import i18n from '../../../i18n';
import button from './button';
import gameApi from '../../../lichess/game';
import { perfTypes } from '../../../lichess/perfs';
import gameStatusApi from '../../../lichess/status';
import { view as renderChat } from '../chat';
import { view as renderCorrespondenceClock } from '../correspondenceClock/correspondenceView';
import variantApi from '../../../lichess/variant';
import { renderTable as renderReplayTable } from './replay';
import socket from '../../../socket';
import m from 'mithril';

export default function view(ctrl) {
  function overlay() {
    return [
      ctrl.chat ? renderChat(ctrl.chat) : null,
      renderPromotion(ctrl),
      renderGamePopup(ctrl),
      renderSubmitMovePopup(ctrl)
    ];
  }

  return layout.board(
    renderHeader.bind(undefined, ctrl),
    renderContent.bind(undefined, ctrl),
    overlay
  );
}

export function renderMaterial(material) {
  const children = [];
  for (let role in material) {
    let piece =
;
    let count = material[role];
    let content;
    if (count === 1) content = piece;
    else {
      content = [];
      for (let i = 0; i < count; i++) content.push(piece);
    }
    children.push(
{content}
);
  }
  return children;
}

export function renderBoard(ctrl, moreWrapperClasses, withStyle = true) {
  const { vh, vw } = helper.viewportDim();
  // ios 7.1 still doesn't support vh unit in calc
  // see board-content.styl section '.board_wrapper' for corresponding calc() rules
  const landscapeDim = (vh > 700 && vw < 1050) ? vh - 50 - vh * 0.12 : vh - 50;
  const boardStyle = helper.isLandscape() ? {
    width: landscapeDim + 'px',
    height: landscapeDim + 'px'
  } : {
    width: vw + 'px',
    height: vw + 'px'
  };
  const boardKey = helper.isLandscape() ? 'landscape' : 'portrait';
  const boardClass = [
    'board',
    settings.general.theme.board(),
    settings.general.theme.piece(),
    ctrl.data.game.variant.key
  ].join(' ');
  let wrapperClass = 'board_wrapper';

  if (moreWrapperClasses) {
    wrapperClass += ' ';
    wrapperClass += moreWrapperClasses;
  }

  function boardConfig(el, isUpdate) {
    if (!isUpdate || !ctrl.vm.renderedBoard) {
      chessground.render(el, ctrl.chessground);
      ctrl.vm.renderedBoard = true;
    }
  }

  return (

  );
}

function renderHeader(ctrl) {
  return (

      {menuButton()}
      {socket.isConnected() ?

        {ctrl.data.userTV ?  : null}
        {ctrl.title}

 : utils.hasNetwork() ?

        {i18n('reconnecting')}
        {loader}

 :
Offline

      }
      {headerBtns()}

  );
}

function renderContent(ctrl) {
  const material = chessground.board.getMaterialDiff(ctrl.chessground.data);
  const replayTable = renderReplayTable(ctrl);
  const player = renderPlayTable(ctrl, ctrl.data.player, material[ctrl.data.player.color], 'player');
  const opponent = renderPlayTable(ctrl, ctrl.data.opponent, material[ctrl.data.opponent.color], 'opponent');

  if (helper.isPortrait())
    return [
      opponent,
      renderBoard(ctrl),
      player,
      renderGameActionsBar(ctrl)
    ];
  else
    return [
      renderBoard(ctrl),

          {gameInfos(ctrl.data)}

          {opponent}
          {replayTable}
          {player}

        {renderGameActionsBar(ctrl)}

    ];
}

function compact(x) {
  if (Object.prototype.toString.call(x) === '[object Array]') {
    var elems = x.filter(function(n) {
      return n !== undefined;
    });
    return elems.length > 0 ? elems : null;
  }
  return x;
}

function ratingDiff(player) {
  if (typeof player.ratingDiff === 'undefined') return null;
  if (player.ratingDiff === 0) return m('span.rp.null', ' +0');
  if (player.ratingDiff > 0) return m('span.rp.up', ' +' + player.ratingDiff);
  if (player.ratingDiff < 0) return m('span.rp.down', ' ' + player.ratingDiff);
}

function renderCheckCount(ctrl, color) {
  const player = color === ctrl.data.player.color ? ctrl.data.opponent : ctrl.data.player;
  if (typeof player.checks !== 'undefined')
    return
{player.checks}
;
}

function renderSubmitMovePopup(ctrl) {
  if (!ctrl.vm.moveToSubmit)
    return
;

  return (

        {button.submitMove(ctrl)}

  );
}

function userInfos(user, player, playerName) {
  let title;
  if (user) {
    let onlineStatus = user.online ? 'connected to lichess' : 'offline';
    let onGameStatus = player.onGame ? 'currently on this game' : 'currently not on this game';
    title = `${playerName}: ${onlineStatus}; ${onGameStatus}`;
  } else
    title = playerName;
  window.plugins.toast.show(title, 'short', 'center');
}

function renderAntagonistInfo(ctrl, player, material, position) {
  const vmKey = position + 'Hash';
  const user = player.user;
  const playerName = utils.playerName(player, helper.isLandscape());
  const vConf = user ?
    helper.ontouch(utils.f(m.route, '/@/' + user.id), () => userInfos(user, player, playerName)) :
    utils.noop;

  const username = user ? user.username : 'anon';
  const onlineStatus = user && user.online ? 'online' : 'offline';
  const diff = ratingDiff(player);

  const hash = username + onlineStatus + player.onGame + player.rating + player.provisional + diff + player.checks + Object.keys(material).map(k => k + material[k]);

  if (ctrl.vm[vmKey] === hash) return {
    subtree: 'retain'
  };
  ctrl.vm[vmKey] = hash;

  return (

        {user ?
           :
          null
        }
        {playerName}
        {player.onGame ?
           :

        }

        {user && helper.isPortrait() ?

            {player.rating}
            {player.provisional ? '?' : ''}
            {diff}

 : null
        }
        {renderCheckCount(ctrl, player.color)}
        {ctrl.data.game.variant.key === 'horde' ? null : renderMaterial(material)}

  );
}

function renderPlayTable(ctrl, player, material, position) {
  const {vh, vw} = helper.viewportDim();
  const headerHeight = vh > 480 ? 50 : 40;
  const contentHeight = vh - headerHeight;
  // must do this here because of the lack of `calc` support
  // 45 refers to game actions bar height
  const style = helper.isLandscape() ? {} : { height: ((contentHeight - vw - 45) / 2) + 'px' };
  const key = helper.isLandscape() ? position + '-landscape' : position + '-portrait';
  const runningColor = ctrl.isClockRunning() ? ctrl.data.game.player : null;

  return (

      {renderAntagonistInfo(ctrl, player, material, position)}
      {ctrl.clock ?
        renderClock(ctrl.clock, player.color, runningColor) : (
        ctrl.correspondenceClock ?
          renderCorrespondenceClock(
            ctrl.correspondenceClock, player.color, ctrl.data.game.player
          ) : null
        )
      }

  );
}

function tvChannelSelector(ctrl) {
  let channels = perfTypes.filter(e => e[0] !== 'correspondence').map(e => [e[1], e[0]]);
  channels.unshift(['Top rated', 'best']);
  channels.push(['Computer', 'computer']);

  return m('div.action', m('div.select_input',
    formWidgets.renderSelect('TV channel', 'tvChannel', channels, settings.tv.channel,
      false, ctrl.onTVChannelChange)
  ));
}

function renderGameRunningActions(ctrl) {
  if (ctrl.data.player.spectator) {
    let controls = [
      button.shareLink(ctrl),
      ctrl.data.tv ? tvChannelSelector(ctrl) : null
    ];

    return
{controls}
;
  }

  var d = ctrl.data;
  var answerButtons = compact([
    button.cancelDrawOffer(ctrl),
    button.answerOpponentDrawOffer(ctrl),
    button.cancelTakebackProposition(ctrl),
    button.answerOpponentTakebackProposition(ctrl),
    (gameApi.mandatory(d) && gameApi.nbMoves(d, d.player.color) === 0) ? m('div.text[data-icon=j]',
      i18n('youHaveNbSecondsToMakeYourFirstMove', 30)
    ) : undefined
  ]);

  return [
    m('div.game_controls', [
      button.shareLink(ctrl),
      button.moretime(ctrl),
      button.standard(ctrl, gameApi.abortable, 'L', 'abortGame', 'abort'),
      button.forceResign(ctrl) || [
        button.standard(ctrl, gameApi.takebackable, 'i', 'proposeATakeback', 'takeback-yes'),
        button.standard(ctrl, gameApi.drawable, '2', 'offerDraw', 'draw-yes'),
        button.standard(ctrl, gameApi.resignable, 'b', 'resign', 'resign'),
        button.threefoldClaimDraw(ctrl)
      ]
    ]),
    answerButtons ? m('div.answers', answerButtons) : null
  ];
}

function renderGameEndedActions(ctrl) {
  var result = gameApi.result(ctrl.data);
  var winner = gameApi.getPlayer(ctrl.data, ctrl.data.game.winner);
  var status = gameStatusApi.toLabel(ctrl.data.game.status.name, ctrl.data.game.winner, ctrl.data.game.variant.key) +
    (winner ? '. ' + i18n(winner.color === 'white' ? 'whiteIsVictorious' : 'blackIsVictorious') + '.' : '');
  const resultDom = gameStatusApi.aborted(ctrl.data) ? [] : [
    result, m('br'), m('br')
  ];
  resultDom.push(m('div.resultStatus', status));
  const buttons = ctrl.data.player.spectator ? [
    button.shareLink(ctrl),
    button.sharePGN(ctrl),
    ctrl.data.tv ? tvChannelSelector(ctrl) : null
  ] : [
    button.shareLink(ctrl),
    button.sharePGN(ctrl),
    button.newOpponent(ctrl),
    button.answerOpponentRematch(ctrl),
    button.cancelRematch(ctrl),
    button.rematch(ctrl)
  ];
  return [
    m('div.result', resultDom),
    m('div.control.buttons', buttons)
  ];
}

function gameInfos(data) {
  var time = gameApi.time(data);
  var mode = data.game.rated ? i18n('rated') : i18n('casual');
  var icon = data.opponent.ai ? ':' : utils.gameIcon(data.game.perf);
  var variant = m('span.variant', {
    config: helper.ontouch(
      () => {
        var link = variantApi(data.game.variant.key).link;
        if (link)
          window.open(link, '_blank');
      },
      () => window.plugins.toast.show(data.game.variant.title, 'short', 'center')
    )
  }, data.game.variant.name);
  var infos = [time + ' • ', variant, m('br'), mode];
  return [
    m('div.icon-game', {
      'data-icon': icon ? icon : ''
    }),
    m('div.game-title.no_select', infos)
  ];
}

function renderGamePopup(ctrl) {
  return popupWidget(
    'player_controls',
    helper.isPortrait() ? gameInfos(ctrl.data) : null,
    gameApi.playable(ctrl.data) ?
      renderGameRunningActions.bind(undefined, ctrl) : renderGameEndedActions.bind(undefined, ctrl),
    ctrl.vm.showingActions,
    ctrl.hideActions
  );
}

function renderGameActionsBar(ctrl) {
  const answerRequired = ctrl.data.opponent.proposingTakeback ||
    ctrl.data.opponent.offeringDraw ||
    gameApi.forceResignable(ctrl.data) ||
    ctrl.data.opponent.offeringRematch;

  const prevPly = ctrl.vm.ply - 1;
  const nextPly = ctrl.vm.ply + 1;
  const bwdOn = ctrl.vm.ply !== prevPly && prevPly >= ctrl.firstPly();
  const fwdOn = ctrl.vm.ply !== nextPly && nextPly <= ctrl.lastPly();
  const hash = answerRequired + (!ctrl.chat || ctrl.chat.unread) + ctrl.vm.flip + bwdOn + fwdOn;

  if (ctrl.vm.buttonsHash === hash) return {
    subtree: 'retain'
  };
  ctrl.vm.buttonsHash = hash;

  const gmClass = [
    'action_bar_button',
    'fa',
    'fa-ellipsis-h',
    answerRequired ? 'glow' : ''
  ].join(' ');

  const chatClass = [
    'action_bar_button',
    ctrl.chat && ctrl.chat.unread ? 'glow' : ''
  ].join(' ');

  return (

      <button className={gmClass} key="gameMenu" config={helper.ontouch(ctrl.showActions)} />
      {ctrl.chat ?
      <button className={chatClass} data-icon="c" key="chat" config={helper.ontouch(ctrl.chat.open || utils.noop)} /> : <button className="action_bar_button empty" />
      }
      {button.flipBoard(ctrl)}
      {button.backward(ctrl)}
      {button.forward(ctrl)}

  );
}
<|endoftext|>


<commit_before>var events = require('events'),
    http = require('http'),
    cli = require('cli'),
    _ = require('lodash'),
    request = require('superagent'),
    util = require('util');

var check = function (config) {
    this.options = config;
    this.server = {};
    this.lastStatus = null;
    this.lastValue = null;
};

check.prototype = new events.EventEmitter();

check.prototype.setServer = function (conf) {
    this.server = conf;
};

check.prototype.exec = function () {
    if(!this.server.host || !this.server.port) {
        cli.debug('Server data not set for check');
        return false;
    }

    var self = this,
        options,
        responseBody = '',
        req;

    options = {
        host: this.server.host,
        port: this.server.port,
        path: '/render?target='+this.options.stat+'&rawData=true&from=-'+this.options.timeAgo,
        method: 'GET'
    };

    // Auth currently not supported
    if(this.server.username && this.server.password) {
        options.auth = this.server.username+':'+this.server.password;
    }

    cli.debug("Sending stat retrieval request for: " + this.options.stat);

    request
        .get(options.host + options.path)
        .set('port', options.port)
        .end(function(error, result){
            if(error) {
                cli.debug("Error");
                return;
            }

            var responseBody = result.text;

            cli.debug('Stat retrieval response: ' + responseBody.trim());
            self.processResponse(responseBody);
        });
};

check.prototype.processResponse = function(body) {
    var values = null,
        value = null,
        i,
        condition;

    try {
        values = body.split('|')[1].split(',');

        // parse every value to a float
        values = _.map(values, parseFloat);

        value = _.max(values);
    }
    catch(e) {
        cli.debug('Could not parse response, unexpected format');
    }

    this.lastValue = value;

    if(value === null || !_.isNumber(value)) {
        cli.error('No valid value for stat: '+this.options.stat);
        return;
    }

    if(this.options.condition) {
        cli.debug(util.format("Checking stat: %s with condition: %s and value of: %d", this.options.stat, this.options.condition, value));
        condition = this.options.condition.replace(/x/, value);

        try {
            if(eval(condition)) {
                cli.error("ERROR condition detected for " + this.options.stat + " with a value of " + value);

                this.lastStatus = 'ERROR';
                this.emit('notify', value);
            }
            else {
                this.lastStatus = 'NORMAL';
            }
        }
        catch (e) {
            cli.error("Unable to evaluate condition for check: "+e.message);
            this.lastStatus = null;
        }
    }
    else {
        cli.error('No condition given for stat');
        this.lastStatus = null;
    }
};

module.exports = check;<commit_msg>Ensure that values being evaluated are numbers<commit_after>var events = require('events'),
    http = require('http'),
    cli = require('cli'),
    _ = require('lodash'),
    request = require('superagent'),
    util = require('util');

var check = function (config) {
    this.options = config;
    this.server = {};
    this.lastStatus = null;
    this.lastValue = null;
};

check.prototype = new events.EventEmitter();

check.prototype.setServer = function (conf) {
    this.server = conf;
};

check.prototype.exec = function () {
    if(!this.server.host || !this.server.port) {
        cli.debug('Server data not set for check');
        return false;
    }

    var self = this,
        options,
        responseBody = '',
        req;

    options = {
        host: this.server.host,
        port: this.server.port,
        path: '/render?target='+this.options.stat+'&rawData=true&from=-'+this.options.timeAgo,
        method: 'GET'
    };

    // Auth currently not supported
    if(this.server.username && this.server.password) {
        options.auth = this.server.username+':'+this.server.password;
    }

    cli.debug("Sending stat retrieval request for: " + this.options.stat);

    request
        .get(options.host + options.path)
        .set('port', options.port)
        .end(function(error, result){
            if(error) {
                cli.debug("Error");
                return;
            }

            var responseBody = result.text;

            cli.debug('Stat retrieval response: ' + responseBody.trim());
            self.processResponse(responseBody);
        });
};

check.prototype.processResponse = function(body) {
    var values = null,
        value = null,
        i,
        condition;

    try {
        values = body.split('|')[1].split(',');

        // parse every value to a float
        values = _.map(values, parseFloat);
        values = _.filter(values, function(value) { return !isNaN(value); });

        if(_.isEmpty(values)) {
            cli.debug("No valid values for stat: "+this.options.stat);
            return;
        }

        value = _.max(values);
    }
    catch(e) {
        cli.debug('Could not parse response, unexpected format');
    }

    this.lastValue = value;

    if(value === null || !_.isNumber(value)) {
        cli.error('No valid value for stat: '+this.options.stat);
        return;
    }

    if(this.options.condition) {
        cli.debug(util.format("Checking stat: %s with condition: %s and value of: %d", this.options.stat, this.options.condition, value));
        condition = this.options.condition.replace(/x/, value);

        try {
            if(eval(condition)) {
                cli.error("ERROR condition detected for " + this.options.stat + " with a value of " + value);

                this.lastStatus = 'ERROR';
                this.emit('notify', value);
            }
            else {
                this.lastStatus = 'NORMAL';
            }
        }
        catch (e) {
            cli.error("Unable to evaluate condition for check: "+e.message);
            this.lastStatus = null;
        }
    }
    else {
        cli.error('No condition given for stat');
        this.lastStatus = null;
    }
};

module.exports = check;<|endoftext|>


<commit_before>/**
 * Created by twagner on 10/10/15.
 */
"use strict";
const AuthenticationRequest = require('./model/authentication'),
    TokenRequest = require('../lib/model/tokenRequest'),
    UserInfoRequest = require('../lib/model/userInfoRequest'),
    TokenError = require('../lib/model/tokenError'),
    auth = require('basic-auth'),
    validator = require('validator');

/**
 *
 * @param config
 * @returns {Function}
 */
const openIDConnect = function(config) {

    if (!config.privkey || !config.pubkey || !config.iss) {
        throw new Error("Invalid configuration for OpenIdConnect middleware!");
    }

    const iss = config.iss;
    const privkey = config.privkey;
    const pubkey = config.pubkey;

    const o = {};

    o.authorizationEndpoint = function(req, res, next) {
        console.log('OpenIDConnect: request method ' + req.method);
        console.log('OpenIDConnect: request path ' + req.path);

        const allowedMethod = req.method === 'GET' || req.method === 'POST';

        if (!allowedMethod) {
            console.log('OpenIDConnect: Unsupported request method!');
            return next();
        }

        // TODO: Passing Request Parameters as JWTs
        console.log('OpenIDConnect: request method data');
        let data;
        if (req.method === 'GET') {
            data = req.query;
        } else if (req.method === 'POST') {

            // our jwt original request:
            if (req.body.originalRequest) {
                // verify token
                data = req.body.originalRequest;
            }
            data = req.body;
        }

        if (req.session && req.session.user) {
            data.user = req.session.user;
        }

        if (req.session && req.session.authTime) {
            data.authTime = req.session.authTime;
        }

        console.log('OpenIDConnect: create authentication request with data: ' + JSON.stringify(data));
        req.aq = new AuthenticationRequest(data, config);

        req.isAuthenticated = function() {
            return req.session && req.session.user;
        };
        next();
    };

    o.tokenEnpoint = function(req, res, next) {
        console.log('OpenIDConnect: Token endpoint');
        const allowedMethod = req.method === 'POST';
        if (!allowedMethod) {
            console.log('OpenIDConnect: Unsupported request method!');
            return next();
        }

        console.log('OpenIDConnect: Client authentication');
        console.log('OpenIDConnect: Headers: ' + req.headers);
        const credentials = auth(req);
        const data = req.body;
        let clientId;
        let clientSecret;
        if (credentials) {
            console.log('OpenIDConnect: Client authentication with basic authentication.');
            console.log('OpenIDConnect: Client credentials: ' + JSON.stringify(credentials));
            if (!validator.isLength(credentials.name, 1) || !validator.isLength(credentials.pass, 1)) {
                console.log('OpenIDConnect: Client basic authentication: invalid credentials.');
                req.error = new TokenError('invalid_client');
                return next();

            }
            clientId = credentials.name;
            clientSecret = credentials.pass;
        } else {
            console.log('OpenIDConnect: Client authentication with form authentication.');
            req.check('client_id', 'Client id is missing!').notEmpty();
            req.check('client_secret', 'Client secret is missing!').notEmpty();
            if (req.validationErrors()) {

                req.error = new TokenError('invalid_client');
                return next();

            }
            clientId = data.client_id;
            clientSecret = data.client_secret;

        }
        req.check('grant_type', 'Invalid grant_type').notEmpty();
        req.check('code', 'Invalid code').notEmpty().isAlphanumeric();
        req.check('redirect_uri', 'Invalid redirect_uri').notEmpty();

        if (req.validationErrors()) {
            req.error = new TokenError('invalid_request');
            return next();
        }
        //data.client_secret = credentials.pass;
        data.clientId = clientId;
        data.clientSecret = clientSecret;
        req.tq = new TokenRequest(data, config);
        console.log('TokenEndpoint:' + JSON.stringify(req.tq));
        next();
    };

    o.verificationEndpoint = function(req, res, next) {

        req.verifyAccessToken = config.accessTokenService.verify;

        next();
    };

    o.bearerTokenFilter = function(req, res, next) {

        const bearer = req.get('Bearer');
        console.log('OpenIDConnect: check bearer token ' + bearer);
        if (!bearer) {
            res.set('WWW-Authenticate', 'error="invalid_request"');
            res.status(400).end();
        } else {
            config.accessTokenService.verify(bearer).then(function(accessToken) {
                console.log('OpenIDConnect: bearer token verified!');
                req.accessToken = accessToken;
                return next();
            }, function(error) {
                res.set('WWW-Authenticate', 'error="invalid_token"');
                return res.status(400).end();
            });
        }
    };

    o.userInfoEndpoint = function(req, res, next) {

        //req.verifyAccessToken = config.accessTokenService.verify;
        req.uiq = new UserInfoRequest(config);
        console.log('OpenIDConnect: forward to user info endpoint');
        next();
    };

    return o;

    /**
    return function(req, res, next) {
        console.log('OpenIDConnect: request method ' + req.method);
        console.log('OpenIDConnect: request path ' + req.path);

        const allowedMethod = req.method === 'GET' || req.method === 'POST';

        if (!allowedMethod) {
            console.log('OpenIDConnect: Unsupported request method!');
            return next();
        }

        // TODO: Passing Request Parameters as JWTs
        console.log('OpenIDConnect: request method data');
        let data;
        if (req.method === 'GET') {
            data = req.query;
        } else if (req.method === 'POST') {

            // our jwt original request:
            if (req.body.originalRequest) {
                // verify token
                data = req.body.originalRequest;
            }
            data = req.body;
        }

        if (req.session && req.session.user) {
            data.user = req.session.user;
        }

        if (req.session && req.session.authTime) {
            data.authTime = req.session.authTime;
        }

        console.log('OpenIDConnect: create authentication request with data: ' + JSON.stringify(data));
        req.aq = new AuthenticationRequest(data, config);

        req.isAuthenticated = function() {
            return req.session && req.session.user;
        };

        next();

    };
     */

};

module.exports = openIDConnect;<commit_msg>Header logging<commit_after>/**
 * Created by twagner on 10/10/15.
 */
"use strict";
const AuthenticationRequest = require('./model/authentication'),
    TokenRequest = require('../lib/model/tokenRequest'),
    UserInfoRequest = require('../lib/model/userInfoRequest'),
    TokenError = require('../lib/model/tokenError'),
    auth = require('basic-auth'),
    validator = require('validator');

/**
 *
 * @param config
 * @returns {Function}
 */
const openIDConnect = function(config) {

    if (!config.privkey || !config.pubkey || !config.iss) {
        throw new Error("Invalid configuration for OpenIdConnect middleware!");
    }

    const iss = config.iss;
    const privkey = config.privkey;
    const pubkey = config.pubkey;

    const o = {};

    o.authorizationEndpoint = function(req, res, next) {
        console.log('OpenIDConnect: request method ' + req.method);
        console.log('OpenIDConnect: request path ' + req.path);

        const allowedMethod = req.method === 'GET' || req.method === 'POST';

        if (!allowedMethod) {
            console.log('OpenIDConnect: Unsupported request method!');
            return next();
        }

        // TODO: Passing Request Parameters as JWTs
        console.log('OpenIDConnect: request method data');
        let data;
        if (req.method === 'GET') {
            data = req.query;
        } else if (req.method === 'POST') {

            // our jwt original request:
            if (req.body.originalRequest) {
                // verify token
                data = req.body.originalRequest;
            }
            data = req.body;
        }

        if (req.session && req.session.user) {
            data.user = req.session.user;
        }

        if (req.session && req.session.authTime) {
            data.authTime = req.session.authTime;
        }

        console.log('OpenIDConnect: create authentication request with data: ' + JSON.stringify(data));
        req.aq = new AuthenticationRequest(data, config);

        req.isAuthenticated = function() {
            return req.session && req.session.user;
        };
        next();
    };

    o.tokenEnpoint = function(req, res, next) {
        console.log('OpenIDConnect: Token endpoint');
        const allowedMethod = req.method === 'POST';
        if (!allowedMethod) {
            console.log('OpenIDConnect: Unsupported request method!');
            return next();
        }

        console.log('OpenIDConnect: Client authentication');
        console.log('OpenIDConnect: Headers: ' + JSON.stringify(req.headers));
        const credentials = auth(req);
        const data = req.body;
        let clientId;
        let clientSecret;
        if (credentials) {
            console.log('OpenIDConnect: Client authentication with basic authentication.');
            console.log('OpenIDConnect: Client credentials: ' + JSON.stringify(credentials));
            if (!validator.isLength(credentials.name, 1) || !validator.isLength(credentials.pass, 1)) {
                console.log('OpenIDConnect: Client basic authentication: invalid credentials.');
                req.error = new TokenError('invalid_client');
                return next();

            }
            clientId = credentials.name;
            clientSecret = credentials.pass;
        } else {
            console.log('OpenIDConnect: Client authentication with form authentication.');
            req.check('client_id', 'Client id is missing!').notEmpty();
            req.check('client_secret', 'Client secret is missing!').notEmpty();
            if (req.validationErrors()) {

                req.error = new TokenError('invalid_client');
                return next();

            }
            clientId = data.client_id;
            clientSecret = data.client_secret;

        }
        req.check('grant_type', 'Invalid grant_type').notEmpty();
        req.check('code', 'Invalid code').notEmpty().isAlphanumeric();
        req.check('redirect_uri', 'Invalid redirect_uri').notEmpty();

        if (req.validationErrors()) {
            req.error = new TokenError('invalid_request');
            return next();
        }
        //data.client_secret = credentials.pass;
        data.clientId = clientId;
        data.clientSecret = clientSecret;
        req.tq = new TokenRequest(data, config);
        console.log('TokenEndpoint:' + JSON.stringify(req.tq));
        next();
    };

    o.verificationEndpoint = function(req, res, next) {

        req.verifyAccessToken = config.accessTokenService.verify;

        next();
    };

    o.bearerTokenFilter = function(req, res, next) {

        const bearer = req.get('Bearer');
        console.log('OpenIDConnect: check bearer token ' + bearer);
        if (!bearer) {
            res.set('WWW-Authenticate', 'error="invalid_request"');
            res.status(400).end();
        } else {
            config.accessTokenService.verify(bearer).then(function(accessToken) {
                console.log('OpenIDConnect: bearer token verified!');
                req.accessToken = accessToken;
                return next();
            }, function(error) {
                res.set('WWW-Authenticate', 'error="invalid_token"');
                return res.status(400).end();
            });
        }
    };

    o.userInfoEndpoint = function(req, res, next) {

        //req.verifyAccessToken = config.accessTokenService.verify;
        req.uiq = new UserInfoRequest(config);
        console.log('OpenIDConnect: forward to user info endpoint');
        next();
    };

    return o;

    /**
    return function(req, res, next) {
        console.log('OpenIDConnect: request method ' + req.method);
        console.log('OpenIDConnect: request path ' + req.path);

        const allowedMethod = req.method === 'GET' || req.method === 'POST';

        if (!allowedMethod) {
            console.log('OpenIDConnect: Unsupported request method!');
            return next();
        }

        // TODO: Passing Request Parameters as JWTs
        console.log('OpenIDConnect: request method data');
        let data;
        if (req.method === 'GET') {
            data = req.query;
        } else if (req.method === 'POST') {

            // our jwt original request:
            if (req.body.originalRequest) {
                // verify token
                data = req.body.originalRequest;
            }
            data = req.body;
        }

        if (req.session && req.session.user) {
            data.user = req.session.user;
        }

        if (req.session && req.session.authTime) {
            data.authTime = req.session.authTime;
        }

        console.log('OpenIDConnect: create authentication request with data: ' + JSON.stringify(data));
        req.aq = new AuthenticationRequest(data, config);

        req.isAuthenticated = function() {
            return req.session && req.session.user;
        };

        next();

    };
     */

};

module.exports = openIDConnect;<|endoftext|>


<commit_before>require('shelljs/global');
var bump = require('gulp-bump');
var changelog = require('conventional-changelog');
var fs = require('fs');
var git = require('gulp-git');
var gulp = require('gulp');
var q = require('q');
var semver = require('semver');
var zip = require('gulp-zip');

var errorHandler = require('./errorHandler');
var githubService = require('./githubService');

var getNewVersion = function() {
  var deferred = q.defer();

  determineReleaseType(function(err, releaseType) {
    if (err) {
      deferred.reject(err);
    } else {
      var newVersion;
      var oldVersion = require('../package.json').version;
      newVersion = semver.inc(oldVersion, releaseType);
      deferred.resolve(newVersion);
    }
  });

  return deferred.promise;
}();

var getNewTagName = function() {
  var deferred = q.defer();

  getNewVersion
  .then(function(newVersion) {
    deferred.resolve('v' + newVersion);
  })
  .fail(function(err) {
    deferred.reject(err);
  });

  return deferred.promise;
}();

var getVersionChanges = function() {
  var deferred = q.defer();

  getNewVersion
  .then(function(newVersion) {
    changelog({
      version: newVersion,
      file: 'tmp/foo',
    }, function(err, versionChanges) {
      if (err) {
        deferred.reject(err);
      } else {
        deferred.resolve(versionChanges);
      }
    });
  })
  .fail(function(err) {
    deferred.reject(err);
  });

  return deferred.promise;
}();

gulp.task('release', [
  '_pushVersion',
  '_zip',
], function(done) {
  q.all([getNewTagName, getVersionChanges])
  .spread(function(newTagName, versionChanges) {
    return [
      githubService.createRelease(newTagName, versionChanges),
      q.nfcall(fs.readFile, 'src/pivotal-ui/components/variables.scss', {encoding: 'utf-8'})
    ];
  })
  .spread(function(res, sass) {
    return githubService.uploadFile(res.releaseId, sass);
  })
  .then(function() {
    done();
  })
  .catch(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

// private

gulp.task('_zip', [
  'assets',
], function(){
  return gulp.src('dist/**/*')
    .pipe(zip('dist.zip'))
    .pipe(gulp.dest('./'));
});

gulp.task('_changelog', function(done) {
  getVersionChanges
  .then(function(versionChanges) {
    fs.readFile('CHANGELOG.md', function(err, oldLog) {
      if (err) { errorHandler.handleError(err, {callback: done}); }

      fs.writeFile('CHANGELOG.md', versionChanges + oldLog, function(err) {
        if (err) { errorHandler.handleError(err, {callback: done}); }
        done();
      });
    });
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_bumpPackage', function(done) {
  getNewVersion
  .then(function(newVersion) {
    gulp.src(['./package.json'])
      .pipe(bump({version: newVersion}))
      .pipe(gulp.dest('./'))
      .on('end', done);
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_addVersionRelease', ['assets'], function(done) {
  getNewVersion
  .then(function(newVersion) {
    gulp.src('dist/**/*')
      .pipe(gulp.dest('release/' + newVersion + '/'))
      .pipe(git.add({args: '-N'}))
      .on('end', done);
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_bumpVersion', [
  '_changelog',
  '_bumpPackage',
  '_addVersionRelease'
], function(done) {
  getNewVersion
  .then(function(newVersion) {
    // Can't use gulp git because of https://github.com/stevelacy/gulp-git/issues/49
    var res = exec('git add package.json CHANGELOG.md release/');
    if (res.code !== 0) {
      errorHandler.handleError('Unable to add files for committing', {isFatal: true});
    }

    res = exec('git commit -m "v' + newVersion + '"');
    if (res.code !== 0) {
      errorHandler.handleError('Unable to commit version changes', {isFatal: true});
    }

    done();
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_tagVersion', ['_bumpVersion'], function(done) {
  getNewTagName
  .then(function(tagName) {
    git.tag(tagName, tagName, function(err) {
      if (err) {
        errorHandler.handleError(err, {isFatal: true});
      } else {
        done();
      }
    });
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_pushVersion', ['_tagVersion'], function(done) {
  // These calls are synchronous in case there is a prompt for credentials
  getNewTagName
  .then(function(tagName) {
    var res = exec('git push origin HEAD');
    if (res.code !== 0) {
      errorHandler.handleError('Unable to push version', {isFatal: true});
    }

    res = exec('git push origin ' + tagName);
    if (res.code !== 0) {
      errorHandler.handleError('Unable to push tag', {isFatal: true});
    }

    done();
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

function determineReleaseType(callback) {
  changelog({
    version: 'foo',
    file: 'tmp/foo'
  }, function(err, log) {
    if (err) {
      callback(err, null);
    } else if (/# breaking changes/i.test(log)) {
      callback(null, 'major');
    } else if (/# features/i.test(log)) {
      callback(null, 'minor');
    } else if (/# bug fixes/i.test(log)) {
      callback(null, 'patch');
    } else {
      callback('No changes found', null);
    }
  });
}
<commit_msg>chore(release): use exec instead of git-gulp<commit_after>require('shelljs/global');
var bump = require('gulp-bump');
var changelog = require('conventional-changelog');
var fs = require('fs');
var gulp = require('gulp');
var q = require('q');
var semver = require('semver');
var zip = require('gulp-zip');

var errorHandler = require('./errorHandler');
var githubService = require('./githubService');

var getNewVersion = function() {
  var deferred = q.defer();

  determineReleaseType(function(err, releaseType) {
    if (err) {
      deferred.reject(err);
    } else {
      var newVersion;
      var oldVersion = require('../package.json').version;
      newVersion = semver.inc(oldVersion, releaseType);
      deferred.resolve(newVersion);
    }
  });

  return deferred.promise;
}();

var getNewTagName = function() {
  var deferred = q.defer();

  getNewVersion
  .then(function(newVersion) {
    deferred.resolve('v' + newVersion);
  })
  .fail(function(err) {
    deferred.reject(err);
  });

  return deferred.promise;
}();

var getVersionChanges = function() {
  var deferred = q.defer();

  getNewVersion
  .then(function(newVersion) {
    changelog({
      version: newVersion,
      file: 'tmp/foo',
    }, function(err, versionChanges) {
      if (err) {
        deferred.reject(err);
      } else {
        deferred.resolve(versionChanges);
      }
    });
  })
  .fail(function(err) {
    deferred.reject(err);
  });

  return deferred.promise;
}();

gulp.task('release', [
  '_pushVersion',
  '_zip',
], function(done) {
  q.all([getNewTagName, getVersionChanges])
  .spread(function(newTagName, versionChanges) {
    return [
      githubService.createRelease(newTagName, versionChanges),
      q.nfcall(fs.readFile, 'src/pivotal-ui/components/variables.scss', {encoding: 'utf-8'})
    ];
  })
  .spread(function(res, sass) {
    return githubService.uploadFile(res.releaseId, sass);
  })
  .then(function() {
    done();
  })
  .catch(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

// private

gulp.task('_zip', [
  'assets',
], function(){
  return gulp.src('dist/**/*')
    .pipe(zip('dist.zip'))
    .pipe(gulp.dest('./'));
});

gulp.task('_changelog', function(done) {
  getVersionChanges
  .then(function(versionChanges) {
    fs.readFile('CHANGELOG.md', function(err, oldLog) {
      if (err) { errorHandler.handleError(err, {callback: done}); }

      fs.writeFile('CHANGELOG.md', versionChanges + oldLog, function(err) {
        if (err) { errorHandler.handleError(err, {callback: done}); }
        done();
      });
    });
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_bumpPackage', function(done) {
  getNewVersion
  .then(function(newVersion) {
    gulp.src(['./package.json'])
      .pipe(bump({version: newVersion}))
      .pipe(gulp.dest('./'))
      .on('end', done);
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_addVersionRelease', ['assets'], function(done) {
  getNewVersion
  .then(function(newVersion) {
    gulp.src('dist/**/*')
      .pipe(gulp.dest('release/' + newVersion + '/'))
      .on('end', done);
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_bumpVersion', [
  '_changelog',
  '_bumpPackage',
  '_addVersionRelease'
], function(done) {
  getNewVersion
  .then(function(newVersion) {
    // Can't use gulp git because of https://github.com/stevelacy/gulp-git/issues/49
    var res = exec('git add package.json CHANGELOG.md release/');
    if (res.code !== 0) {
      errorHandler.handleError('Unable to add files for committing', {isFatal: true});
    }

    res = exec('git commit -m "v' + newVersion + '"');
    if (res.code !== 0) {
      errorHandler.handleError('Unable to commit version changes', {isFatal: true});
    }

    done();
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_tagVersion', ['_bumpVersion'], function(done) {
  getNewTagName
  .then(function(tagName) {
    var res = exec('git tag ' + tagName);
    if (res.code !== 0) {
      errorHandler.handleError('Unable to create tag', {isFatal: true});
    }
    done();
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

gulp.task('_pushVersion', ['_tagVersion'], function(done) {
  // These calls are synchronous in case there is a prompt for credentials
  getNewTagName
  .then(function(tagName) {
    var res = exec('git push origin HEAD');
    if (res.code !== 0) {
      errorHandler.handleError('Unable to push version', {isFatal: true});
    }

    res = exec('git push origin ' + tagName);
    if (res.code !== 0) {
      errorHandler.handleError('Unable to push tag', {isFatal: true});
    }

    done();
  })
  .fail(function(err) {
    errorHandler.handleError(err, {callback: done});
  });
});

function determineReleaseType(callback) {
  changelog({
    version: 'foo',
    file: 'tmp/foo'
  }, function(err, log) {
    if (err) {
      callback(err, null);
    } else if (/# breaking changes/i.test(log)) {
      callback(null, 'major');
    } else if (/# features/i.test(log)) {
      callback(null, 'minor');
    } else if (/# bug fixes/i.test(log)) {
      callback(null, 'patch');
    } else {
      callback('No changes found', null);
    }
  });
}
<|endoftext|>


<commit_before>var builder = require('focus').component.builder;
var popinProperties = require('../mixin/popin-behaviour').mixin;
var type = require('focus').component.types;

/**
 * Popin mixin
 * @type {object}
 */
var popinMixin = {
  mixins: [popinProperties],
  /**
   * Display name.
   */
  displayName: 'popin',

  /** @inheritdoc */
  getDefaultProps: function () {
    return {
      type: 'full', // full, centered
      //  displaySelector: undefined, // Html selector of the element wich open/close the modal when click on it.
      contentLoadingFunction: undefined // Function wich returns the content of the modal.
    };
  },

  /** @inheritdoc */
  propTypes: {
    type: type('string'),
    contentLoadingFunction: type('string')
  },

  /** @inheritdoc */
  componentDidMount: function popinDidMount() {
    var source = document.querySelector(this.props.displaySelector);
    var currentView = this;
    if(source !== undefined && source !== null){
      source.onclick = function () {
        currentView.setState({open: !currentView.state.open});
      };
    }

  },

  /**
   * Open the modal.
   */
  openModal: function openModal() {
    this.setState({open: true});
  },
  /**
   * Close the modal
   */
  closeModal: function closeModal() {
    this.setState({open: false});
    document.querySelector('#modal-layer').classList.remove('popin-layer');
  },

  /**
   * Css class of modal.
   * @returns {string} css classes.
   * @private
   */
  _getModalCss: function () {
    var cssClass = 'popin animated ';
    switch (this.props.position) {
      case 'right':
        cssClass = `${cssClass} bounceInRight right btn-close-left`;
        break;
      case 'left':
        cssClass = `${cssClass} bounceInLeft left btn-close-right`;
        break;
      case 'down':
        cssClass = `${cssClass} bounceInDown down btn-close-left`;
        break;
      case 'up':
        cssClass = `${cssClass} bounceInUp up btn-close-left`;
        break;
    }
    if (this.props.style.className !== undefined && this.props.style.className !== null) {
      cssClass = `${cssClass}  ${this.props.style.className}`;
    }
    return cssClass;
  },

  /**
   * Css class of close btn.
   * @returns {string} - css classes.
   * @private
   */
  _getCloseBtnCss: function () {
    var cssClass = 'popin-close-btn';
    switch (this.props.position) {
      case 'right':
        cssClass += ' left';
        break;
      default :
        cssClass += ' right';
    }
    return cssClass;
  },
  /**
   * Content css class.
   * @returns {string} - css classes.
   * @private
   */
  _getModalContentCss: function () {
    var cssClass = 'modal-content';
    switch (this.props.type) {
      case 'full':
        cssClass += ' full';
        break;
      case 'centered':
        cssClass += ' centered';
        break;
    }
    return cssClass;
  },

  /**
   * Render the component.
   * @returns {JSX} - Html code.
   */
  render: function renderPopin() {
    if (!this.state.open) {
      return
;
    }
    return (

                    {this.renderPopinHeader(this)}

                        {this.renderContent(this)}

                    {this.renderPopinFooter(this)}

    );
  }
};

module.exports = builder(popinMixin);
<commit_msg>Change popin animation.<commit_after>var builder = require('focus').component.builder;
var popinProperties = require('../mixin/popin-behaviour').mixin;
var type = require('focus').component.types;

/**
 * Popin mixin
 * @type {object}
 */
var popinMixin = {
  mixins: [popinProperties],
  /**
   * Display name.
   */
  displayName: 'popin',

  /** @inheritdoc */
  getDefaultProps: function () {
    return {
      type: 'full', // full, centered
      //  displaySelector: undefined, // Html selector of the element wich open/close the modal when click on it.
      contentLoadingFunction: undefined // Function wich returns the content of the modal.
    };
  },

  /** @inheritdoc */
  propTypes: {
    type: type('string'),
    contentLoadingFunction: type('string')
  },

  /** @inheritdoc */
  componentDidMount: function popinDidMount() {
    var source = document.querySelector(this.props.displaySelector);
    var currentView = this;
    if(source !== undefined && source !== null){
      source.onclick = function () {
        currentView.setState({open: !currentView.state.open});
      };
    }

  },

  /**
   * Open the modal.
   */
  openModal: function openModal() {
    this.setState({open: true});
  },
  /**
   * Close the modal
   */
  closeModal: function closeModal() {
    this.setState({open: false});
    document.querySelector('#modal-layer').classList.remove('popin-layer');
  },

  /**
   * Css class of modal.
   * @returns {string} css classes.
   * @private
   */
  _getModalCss: function () {
    var cssClass = 'popin animated ';
    switch (this.props.position) {
      case 'right':
        cssClass = `${cssClass} fadeInRight right btn-close-left`;
        break;
      case 'left':
        cssClass = `${cssClass} fadeInLeft left btn-close-right`;
        break;
      case 'down':
        cssClass = `${cssClass} fadeInDown down btn-close-left`;
        break;
      case 'up':
        cssClass = `${cssClass} fadeInUp up btn-close-left`;
        break;
    }
    if (this.props.style.className !== undefined && this.props.style.className !== null) {
      cssClass = `${cssClass}  ${this.props.style.className}`;
    }
    return cssClass;
  },

  /**
   * Css class of close btn.
   * @returns {string} - css classes.
   * @private
   */
  _getCloseBtnCss: function () {
    var cssClass = 'popin-close-btn';
    switch (this.props.position) {
      case 'right':
        cssClass += ' left';
        break;
      default :
        cssClass += ' right';
    }
    return cssClass;
  },
  /**
   * Content css class.
   * @returns {string} - css classes.
   * @private
   */
  _getModalContentCss: function () {
    var cssClass = 'modal-content';
    switch (this.props.type) {
      case 'full':
        cssClass += ' full';
        break;
      case 'centered':
        cssClass += ' centered';
        break;
    }
    return cssClass;
  },

  /**
   * Render the component.
   * @returns {JSX} - Html code.
   */
  render: function renderPopin() {
    if (!this.state.open) {
      return
;
    }
    return (

                    {this.renderPopinHeader(this)}

                        {this.renderContent(this)}

                    {this.renderPopinFooter(this)}

    );
  }
};

module.exports = builder(popinMixin);
<|endoftext|>


<commit_before>var searchInputStyle = {
    width: '400px',
    display: 'block',
    marginBottom: '48px',
    marginLeft: 'auto',
    marginRight: 'auto'
  },
  fetchResultsStyle = {
    color: '#888',
    textAlign: 'center',
    fontSize: '1.2em'
  };

var SearchActive = React.createClass({
  render: function() {
    return (

        Looking up the Dewey Decimals

    );
  }
});

var searchNoResultsStyle = {
  textAlign: 'center'
}, searchNoResultsMessageStyle = {
  fontStyle: 'italic',
  marginRight: '5px'
};

var SearchNoResults = React.createClass({
  render: function() {
    return (

        No one has checked anything in for {this.props.query} yet.
        <Link label="Be the first" href="javascript:alert('aww I do nothing')" />

    );
  }
});

var SearchResults = React.createClass({
  render: function() {
    var rows = [];
    console.log('results');
    console.log('made it here');
    this.props.results.forEach(function(result) {
      rows.push(
        <SearchResultRow name={result.name} title={result.stream_name} imgUrl={result.thumbnail}
                         description={result.description} cost_est={result.cost_est} />
      );
    });
    console.log(this.props.results);
    console.log(rows);
    console.log('done');
    return (

{rows}

    );
  }
});

var searchRowImgStyle = {
    maxHeight: '100px',
    display: 'block',
    marginLeft: 'auto',
    marginRight: 'auto',
    float: 'left'
  },
  searchRowCostStyle = {
    float: 'right',
    marginLeft: '20px',
    marginTop: '5px',
    display: 'inline-block'
  },
  searchRowNameStyle = {
    fontSize: '0.9em',
    color: '#666',
    marginBottom: '24px',
    clear: 'both'
  },
  searchRowDescriptionStyle = {
    color : '#444',
    marginBottom: '24px',
    fontSize: '0.9em'
  };

var SearchResultRow = React.createClass({
  getInitialState: function() {
    return {
      downloading: false
    }
  },
  render: function() {
    return (

            <CreditAmount amount={this.props.cost_est} isEstimate={true}/>

{this.props.title}

lbry://{this.props.name}

{this.props.description}

            <WatchLink streamName={this.props.name} button="primary" />
            <DownloadLink streamName={this.props.name} button="alt" />

    );
  }
});

var featuredContentItemStyle = {
  fontSize: '0.95em',
  marginBottom: '10px',
}, featuredContentItemImgStyle = {
  maxHeight: '90px',
  display: 'block',
  marginLeft: 'auto',
  marginRight: 'auto',
  marginTop: '5px',
  float: 'left',
}, featuredContentItemDescriptionStyle = {
  color: '#444',
  marginBottom: '5px',
  fontSize: '0.9em',
}, featuredContentItemCostStyle = {
  display: 'block',
  float: 'right',
  fontSize: '0.95em',
};

var FeaturedContentItem = React.createClass({
  propTypes: {
    name: React.PropTypes.string,
  },
  getInitialState: function() {
    return {
      metadata: null,
      title: null,
    };
  },
  componentWillMount: function() {
    lbry.resolveName(this.props.name, (metadata) => {
      this.setState({
        metadata: metadata,
        title: metadata.name || metadata.stream_name || ('lbry://' + this.props.name),
      })
    });
  },
  render: function() {
    if (this.state.metadata == null) {
      // Still waiting for metadata
      return null;
    }

    var metadata = this.state.metadata;

    return (

{this.state.title}

{metadata.description}

            <WatchLink streamName={this.props.name} />
            { ' ' }
            <DownloadLink streamName={this.props.name} />

<CreditAmount amount={0.0} isEstimate={true}/>

);
  }
});

var featuredContentStyle = {
  width: '100%',
};

var FeaturedContent = React.createClass({
  render: function() {
    return (

Featured content

        <FeaturedContentItem name="wonderfullife" /> {/* When ready, change to one/two/three/four/five/six */}
        <FeaturedContentItem name="wonderfullife" />
        <FeaturedContentItem name="wonderfullife" />

        <FeaturedContentItem name="wonderfullife" />
        <FeaturedContentItem name="wonderfullife" />
        <FeaturedContentItem name="wonderfullife" />

);
  }
});

var discoverMainStyle = {
  color: '#333'
};

var Discover = React.createClass({
  userTypingTimer: null,

  getInitialState: function() {
    return {
      results: [],
      searching: false,
      query: ''
    };
  },

  search: function() {
    if (this.state.query)
    {
      console.log('search');
      lbry.search(this.state.query, this.searchCallback.bind(this, this.state.query));
    }
    else
    {
      this.setState({
        searching: false,
        results: []
      });
    }
  },

  searchCallback: function(originalQuery, results) {
    if (this.state.searching) //could have canceled while results were pending, in which case nothing to do
    {
      this.setState({
        results: results,
        searching: this.state.query != originalQuery, //multiple searches can be out, we're only done if we receive one we actually care about
      });
    }
  },

  onQueryChange: function(event) {
    if (this.userTypingTimer)
    {
      clearTimeout(this.userTypingTimer);
    }

    //@TODO: Switch to React.js timing
    this.userTypingTimer = setTimeout(this.search, 800); // 800ms delay, tweak for faster/slower

    this.setState({
      searching: event.target.value.length > 0,
      query: event.target.value
    });
  },

  render: function() {
    console.log(this.state);
    return (

<input type="search" style={searchInputStyle} onChange={this.onQueryChange}
                        placeholder="Find movies, music, games, and more"/>

        { this.state.searching ? <SearchActive /> : null }
        { !this.state.searching && this.state.query && this.state.results.length ? <SearchResults results={this.state.results} /> : null }
        { !this.state.searching && this.state.query && !this.state.results.length ? <SearchNoResults query={this.state.query} /> : null }
        { !this.state.query && !this.state.searching ? <FeaturedContent /> : null }

    );
  }
});

var logoStyle = {
    padding: '48px 12px',
    textAlign: 'center',
    maxHeight: '80px',
  },
  imgStyle = { //@TODO: remove this, img should be properly scaled once size is settled
    height: '80px'
  };

var Header = React.createClass({
  render: function() {
    return (

        <TopBar />

    );
  }
});

var topBarStyle = {
  'float': 'right'
},
menuStyle = {
  'fontSize': '1.1em',
},
balanceStyle = {
  'marginRight': '5px'
},
closeIconStyle = {
  'color': '#ff5155'
};

var TopBar = React.createClass({
  onClose: function() {
    window.location.href = "?start";
  },
  getInitialState: function() {
    return {
      balance: 0,
      showClose: /linux/i.test(navigator.userAgent) // @TODO: find a way to use getVersionInfo() here without messy state management
    };
  },
  componentDidMount: function() {
    lbry.getBalance(function(balance) {
      this.setState({
        balance: balance
      });
    }.bind(this));
  },

  render: function() {
    return (

          <CreditAmount amount={this.state.balance}/>

        <Link href='/?files' title="My Files" icon='icon-cloud-download' />
        <Link href='/?settings' title="Settings" icon='icon-gear' />
        <Link href='/?help' title="Help" icon='icon-question-circle' />
        <Link href="/?start" title="Start" onClick={this.onClose} icon="icon-close"
              style={closeIconStyle} hidden={!this.state.showClose} />

    );
  }
});

var HomePage = React.createClass({
  componentDidMount: function() {
    lbry.getStartNotice(function(notice) {
      if (notice) {
        alert(notice);
      }
    });
  },
  render: function() {
    return (

        <Discover />

    );
  }
});
<commit_msg>Hide featured content for now<commit_after>var searchInputStyle = {
    width: '400px',
    display: 'block',
    marginBottom: '48px',
    marginLeft: 'auto',
    marginRight: 'auto'
  },
  fetchResultsStyle = {
    color: '#888',
    textAlign: 'center',
    fontSize: '1.2em'
  };

var SearchActive = React.createClass({
  render: function() {
    return (

        Looking up the Dewey Decimals

    );
  }
});

var searchNoResultsStyle = {
  textAlign: 'center'
}, searchNoResultsMessageStyle = {
  fontStyle: 'italic',
  marginRight: '5px'
};

var SearchNoResults = React.createClass({
  render: function() {
    return (

        No one has checked anything in for {this.props.query} yet.
        <Link label="Be the first" href="javascript:alert('aww I do nothing')" />

    );
  }
});

var SearchResults = React.createClass({
  render: function() {
    var rows = [];
    console.log('results');
    console.log('made it here');
    this.props.results.forEach(function(result) {
      rows.push(
        <SearchResultRow name={result.name} title={result.stream_name} imgUrl={result.thumbnail}
                         description={result.description} cost_est={result.cost_est} />
      );
    });
    console.log(this.props.results);
    console.log(rows);
    console.log('done');
    return (

{rows}

    );
  }
});

var searchRowImgStyle = {
    maxHeight: '100px',
    display: 'block',
    marginLeft: 'auto',
    marginRight: 'auto',
    float: 'left'
  },
  searchRowCostStyle = {
    float: 'right',
    marginLeft: '20px',
    marginTop: '5px',
    display: 'inline-block'
  },
  searchRowNameStyle = {
    fontSize: '0.9em',
    color: '#666',
    marginBottom: '24px',
    clear: 'both'
  },
  searchRowDescriptionStyle = {
    color : '#444',
    marginBottom: '24px',
    fontSize: '0.9em'
  };

var SearchResultRow = React.createClass({
  getInitialState: function() {
    return {
      downloading: false
    }
  },
  render: function() {
    return (

            <CreditAmount amount={this.props.cost_est} isEstimate={true}/>

{this.props.title}

lbry://{this.props.name}

{this.props.description}

            <WatchLink streamName={this.props.name} button="primary" />
            <DownloadLink streamName={this.props.name} button="alt" />

    );
  }
});

var featuredContentItemStyle = {
  fontSize: '0.95em',
  marginBottom: '10px',
}, featuredContentItemImgStyle = {
  maxHeight: '90px',
  display: 'block',
  marginLeft: 'auto',
  marginRight: 'auto',
  marginTop: '5px',
  float: 'left',
}, featuredContentItemDescriptionStyle = {
  color: '#444',
  marginBottom: '5px',
  fontSize: '0.9em',
}, featuredContentItemCostStyle = {
  display: 'block',
  float: 'right',
  fontSize: '0.95em',
};

var FeaturedContentItem = React.createClass({
  propTypes: {
    name: React.PropTypes.string,
  },
  getInitialState: function() {
    return {
      metadata: null,
      title: null,
    };
  },
  componentWillMount: function() {
    lbry.resolveName(this.props.name, (metadata) => {
      this.setState({
        metadata: metadata,
        title: metadata.name || metadata.stream_name || ('lbry://' + this.props.name),
      })
    });
  },
  render: function() {
    if (this.state.metadata == null) {
      // Still waiting for metadata
      return null;
    }

    var metadata = this.state.metadata;

    return (

{this.state.title}

{metadata.description}

            <WatchLink streamName={this.props.name} />
            { ' ' }
            <DownloadLink streamName={this.props.name} />

<CreditAmount amount={0.0} isEstimate={true}/>

);
  }
});

var featuredContentStyle = {
  width: '100%',
};

var FeaturedContent = React.createClass({
  render: function() {
    return (

Featured content

        <FeaturedContentItem name="wonderfullife" /> {/* When ready, change to one/two/three/four/five/six */}
        <FeaturedContentItem name="wonderfullife" />
        <FeaturedContentItem name="wonderfullife" />

        <FeaturedContentItem name="wonderfullife" />
        <FeaturedContentItem name="wonderfullife" />
        <FeaturedContentItem name="wonderfullife" />

);
  }
});

var discoverMainStyle = {
  color: '#333'
};

var Discover = React.createClass({
  userTypingTimer: null,

  getInitialState: function() {
    return {
      results: [],
      searching: false,
      query: ''
    };
  },

  search: function() {
    if (this.state.query)
    {
      console.log('search');
      lbry.search(this.state.query, this.searchCallback.bind(this, this.state.query));
    }
    else
    {
      this.setState({
        searching: false,
        results: []
      });
    }
  },

  searchCallback: function(originalQuery, results) {
    if (this.state.searching) //could have canceled while results were pending, in which case nothing to do
    {
      this.setState({
        results: results,
        searching: this.state.query != originalQuery, //multiple searches can be out, we're only done if we receive one we actually care about
      });
    }
  },

  onQueryChange: function(event) {
    if (this.userTypingTimer)
    {
      clearTimeout(this.userTypingTimer);
    }

    //@TODO: Switch to React.js timing
    this.userTypingTimer = setTimeout(this.search, 800); // 800ms delay, tweak for faster/slower

    this.setState({
      searching: event.target.value.length > 0,
      query: event.target.value
    });
  },

  render: function() {
    console.log(this.state);
    return (

<input type="search" style={searchInputStyle} onChange={this.onQueryChange}
                        placeholder="Find movies, music, games, and more"/>

        { this.state.searching ? <SearchActive /> : null }
        { !this.state.searching && this.state.query && this.state.results.length ? <SearchResults results={this.state.results} /> : null }
        { !this.state.searching && this.state.query && !this.state.results.length ? <SearchNoResults query={this.state.query} /> : null }
        {/* !this.state.query && !this.state.searching ? <FeaturedContent /> : null */}

    );
  }
});

var logoStyle = {
    padding: '48px 12px',
    textAlign: 'center',
    maxHeight: '80px',
  },
  imgStyle = { //@TODO: remove this, img should be properly scaled once size is settled
    height: '80px'
  };

var Header = React.createClass({
  render: function() {
    return (

        <TopBar />

    );
  }
});

var topBarStyle = {
  'float': 'right'
},
menuStyle = {
  'fontSize': '1.1em',
},
balanceStyle = {
  'marginRight': '5px'
},
closeIconStyle = {
  'color': '#ff5155'
};

var TopBar = React.createClass({
  onClose: function() {
    window.location.href = "?start";
  },
  getInitialState: function() {
    return {
      balance: 0,
      showClose: /linux/i.test(navigator.userAgent) // @TODO: find a way to use getVersionInfo() here without messy state management
    };
  },
  componentDidMount: function() {
    lbry.getBalance(function(balance) {
      this.setState({
        balance: balance
      });
    }.bind(this));
  },

  render: function() {
    return (

          <CreditAmount amount={this.state.balance}/>

        <Link href='/?files' title="My Files" icon='icon-cloud-download' />
        <Link href='/?settings' title="Settings" icon='icon-gear' />
        <Link href='/?help' title="Help" icon='icon-question-circle' />
        <Link href="/?start" title="Start" onClick={this.onClose} icon="icon-close"
              style={closeIconStyle} hidden={!this.state.showClose} />

    );
  }
});

var HomePage = React.createClass({
  componentDidMount: function() {
    lbry.getStartNotice(function(notice) {
      if (notice) {
        alert(notice);
      }
    });
  },
  render: function() {
    return (

        <Discover />

    );
  }
});
<|endoftext|>


<commit_before>/*
 * This file is part of the Sylius package.
 *
 * (c) Paweł Jędrzejewski
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

import 'semantic-ui-css/components/accordion';
import $ from 'jquery';
import 'jquery.dirtyforms/jquery.dirtyforms';

import 'sylius/ui/app';
import 'sylius/ui/sylius-auto-complete';
import 'sylius/ui/sylius-product-attributes';
import 'sylius/ui/sylius-product-auto-complete';
import 'sylius/ui/sylius-prototype-handler';

import './sylius-compound-form-errors';
import './sylius-lazy-choice-tree';
import './sylius-move-product-variant';
import './sylius-move-taxon';
import './sylius-notification';
import './sylius-product-images-preview';
import './sylius-product-slug';
import './sylius-taxon-slug';
import './sylius-chart';

import SyliusTaxonomyTree from './sylius-taxon-tree';
import formsList from './sylius-forms-list';

$(document).ready(() => {
  $('#sylius_product_variant_pricingCalculator').handlePrototypes({
    prototypePrefix: 'sylius_product_variant_pricingCalculator',
    containerSelector: '#sylius_calculator_container',
  });

  $('#sylius_customer_createUser').change(() => {
    $('#user-form').toggle();
  });

  $('.sylius-autocomplete').autoComplete();

  $('.product-select.ui.fluid.multiple.search.selection.dropdown').productAutoComplete();
  $('div#attributeChoice > .ui.dropdown.search').productAttributes();

  $('table thead th.sortable').on('click', (event) => {
    window.location = $(event.currentTarget).find('a').attr('href');
  });

  $('.sylius-update-product-variants').moveProductVariant($('.sylius-product-variant-position'));
  $('.sylius-taxon-move-up').taxonMoveUp();
  $('.sylius-taxon-move-down').taxonMoveDown();

  $('#sylius_shipping_method_calculator').handlePrototypes({
    prototypePrefix: 'sylius_shipping_method_calculator_calculators',
    containerSelector: '.configuration',
  });

  $('#actions a[data-form-collection="add"]').on('click', () => {
    setTimeout(() => {
      $('select[name^="sylius_promotion[actions]"][name$="[type]"]').last().change();
    }, 50);
  });
  $('#rules a[data-form-collection="add"]').on('click', () => {
    setTimeout(() => {
      $('select[name^="sylius_promotion[rules]"][name$="[type]"]').last().change();
    }, 50);
  });

  $(document).on('collection-form-add', () => {
    $('.sylius-autocomplete').each((index, element) => {
      if ($._data($(element).get(0), 'events') == undefined) {
        $(element).autoComplete();
      }
    });
  });
  $(document).on('collection-form-update', () => {
    $('.sylius-autocomplete').each((index, element) => {
      if ($._data($(element).get(0), 'events') == undefined) {
        $(element).autoComplete();
      }
    });
  });

  $('.sylius-tabular-form').addTabErrors();
  $('.ui.accordion').addAccordionErrors();
  $('#sylius-product-taxonomy-tree').choiceTree('productTaxon', true, 1);

  $(document).notification();
  $(document).productSlugGenerator();
  $(document).taxonSlugGenerator();
  $(document).previewUploadedImage('#sylius_product_images');
  $(document).previewUploadedImage('#sylius_taxon_images');

  $(document).previewUploadedImage('#add-avatar');

  $('body').on('DOMNodeInserted', '[data-form-collection="item"]', (event) => {
    if ($(event.target).find('.accordion').length > 0) {
      $(event.target).find('.accordion').accordion();
    }
  });

  const taxonomyTree = new SyliusTaxonomyTree();

  $(`${formsList}, .check-unsaved`).dirtyForms();

  $('.variants-accordion__title').on('click', '.icon.button', function(e) {
    $(e.delegateTarget).next('.variants-accordion__content').toggle();
    $(this).find('.dropdown.icon').toggleClass('counterclockwise rotated');
  });
});

window.$ = $;
window.jQuery = $;
<commit_msg>change product translation tab behavior so tabs are not opened one per time<commit_after>/*
 * This file is part of the Sylius package.
 *
 * (c) Paweł Jędrzejewski
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

import 'semantic-ui-css/components/accordion';
import $ from 'jquery';
import 'jquery.dirtyforms/jquery.dirtyforms';

import 'sylius/ui/app';
import 'sylius/ui/sylius-auto-complete';
import 'sylius/ui/sylius-product-attributes';
import 'sylius/ui/sylius-product-auto-complete';
import 'sylius/ui/sylius-prototype-handler';

import './sylius-compound-form-errors';
import './sylius-lazy-choice-tree';
import './sylius-move-product-variant';
import './sylius-move-taxon';
import './sylius-notification';
import './sylius-product-images-preview';
import './sylius-product-slug';
import './sylius-taxon-slug';
import './sylius-chart';

import SyliusTaxonomyTree from './sylius-taxon-tree';
import formsList from './sylius-forms-list';

$(document).ready(() => {
  $('#sylius_product_variant_pricingCalculator').handlePrototypes({
    prototypePrefix: 'sylius_product_variant_pricingCalculator',
    containerSelector: '#sylius_calculator_container',
  });

  $('#sylius_customer_createUser').change(() => {
    $('#user-form').toggle();
  });

  $('.sylius-autocomplete').autoComplete();

  $('.product-select.ui.fluid.multiple.search.selection.dropdown').productAutoComplete();
  $('div#attributeChoice > .ui.dropdown.search').productAttributes();

  $('table thead th.sortable').on('click', (event) => {
    window.location = $(event.currentTarget).find('a').attr('href');
  });

  $('.sylius-update-product-variants').moveProductVariant($('.sylius-product-variant-position'));
  $('.sylius-taxon-move-up').taxonMoveUp();
  $('.sylius-taxon-move-down').taxonMoveDown();

  $('#sylius_shipping_method_calculator').handlePrototypes({
    prototypePrefix: 'sylius_shipping_method_calculator_calculators',
    containerSelector: '.configuration',
  });

  $('#actions a[data-form-collection="add"]').on('click', () => {
    setTimeout(() => {
      $('select[name^="sylius_promotion[actions]"][name$="[type]"]').last().change();
    }, 50);
  });
  $('#rules a[data-form-collection="add"]').on('click', () => {
    setTimeout(() => {
      $('select[name^="sylius_promotion[rules]"][name$="[type]"]').last().change();
    }, 50);
  });

  $(document).on('collection-form-add', () => {
    $('.sylius-autocomplete').each((index, element) => {
      if ($._data($(element).get(0), 'events') == undefined) {
        $(element).autoComplete();
      }
    });
  });
  $(document).on('collection-form-update', () => {
    $('.sylius-autocomplete').each((index, element) => {
      if ($._data($(element).get(0), 'events') == undefined) {
        $(element).autoComplete();
      }
    });
  });

  $('.sylius-tabular-form').addTabErrors();
  $('.ui.accordion').addAccordionErrors();
  $('#sylius-product-taxonomy-tree').choiceTree('productTaxon', true, 1);

  $(document).notification();
  $(document).productSlugGenerator();
  $(document).taxonSlugGenerator();
  $(document).previewUploadedImage('#sylius_product_images');
  $(document).previewUploadedImage('#sylius_taxon_images');

  $(document).previewUploadedImage('#add-avatar');

  $('body').on('DOMNodeInserted', '[data-form-collection="item"]', (event) => {
    if ($(event.target).find('.accordion').length > 0) {
      $(event.target).find('.accordion').accordion();
    }
  });

  const taxonomyTree = new SyliusTaxonomyTree();

  $(`${formsList}, .check-unsaved`).dirtyForms();

  $('#more-details').accordion({ exclusive: false });

  $('.variants-accordion__title').on('click', '.icon.button', function(e) {
    $(e.delegateTarget).next('.variants-accordion__content').toggle();
    $(this).find('.dropdown.icon').toggleClass('counterclockwise rotated');
  });
});

window.$ = $;
window.jQuery = $;
<|endoftext|>


<commit_before>module.exports = OpenTorrentAddressModal

var h = require('virtual-dom/h')
var hyperx = require('hyperx')
var hx = hyperx(h)

var {dispatch} = require('../lib/dispatcher')

function OpenTorrentAddressModal (state) {
  return hx`

Enter torrent address or magnet link

        <input id='add-torrent-url' type='text' autofocus onkeypress=${handleKeyPress} />
        <button class='primary' onclick=${handleOK}>OK</button>
        <button class='cancel' onclick=${handleCancel}>Cancel</button>

  `
}

function handleKeyPress (e) {
  if (e.which === 13) handleOK() /* hit Enter to submit */
}

function handleOK () {
  dispatch('exitModal')
  dispatch('addTorrent', document.querySelector('#add-torrent-url').value)
}

function handleCancel () {
  dispatch('exitModal')
}
<commit_msg>Fix text field focus after repeated open (#376)<commit_after>module.exports = OpenTorrentAddressModal

var h = require('virtual-dom/h')
var hyperx = require('hyperx')
var hx = hyperx(h)

var {dispatch} = require('../lib/dispatcher')

function OpenTorrentAddressModal (state) {
  return hx`

Enter torrent address or magnet link

        <input id='add-torrent-url' type='text' onkeypress=${handleKeyPress} />
        <button class='primary' onclick=${handleOK}>OK</button>
        <button class='cancel' onclick=${handleCancel}>Cancel</button>

  `
}

function handleKeyPress (e) {
  if (e.which === 13) handleOK() /* hit Enter to submit */
}

function handleOK () {
  dispatch('exitModal')
  dispatch('addTorrent', document.querySelector('#add-torrent-url').value)
}

function handleCancel () {
  dispatch('exitModal')
}
<|endoftext|>


<commit_before>import React from 'react'
import { Field, reduxForm } from 'redux-form'
import TextField from 'material-ui/TextField'
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'
import Checkbox from 'material-ui/Checkbox'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import asyncValidate from './asyncValidate'

const validate = values => {
  const errors = {}
  const requiredFields = [ 'firstName', 'lastName', 'email', 'favoriteColor', 'notes' ]
  requiredFields.forEach(field => {
    if (!values[ field ]) {
      errors[ field ] = 'Required'
    }
  })
  if (values.email && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = 'Invalid email address'
  }
  return errors
}

const renderTextField = ({ input, label, meta: { touched, error }, ...custom }) => (
  <TextField hintText={label}
    floatingLabelText={label}
    errorText={touched && error}
    {...input}
    {...custom}
  />
)

const renderCheckbox = ({ input, label }) => (
  <Checkbox label={label}
    checked={input.value ? true : false}
    onCheck={input.onChange}/>
)

const renderSelectField = ({ input, label, meta: { touched, error }, children }) => (
  <SelectField
    floatingLabelText={label}
    errorText={touched && error}
    {...input}
    onChange={(event, index, value) => input.onChange(value)}
    children={children}/>
)

const MaterialUiForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>

        <Field name="firstName" component={renderTextField} label="First Name"/>

        <Field name="lastName" component={renderTextField} label="Last Name"/>

        <Field name="email" component={renderTextField} label="Email"/>

        <Field name="sex" component={RadioButtonGroup}>
          <RadioButton value="male" label="male"/>
          <RadioButton value="female" label="female"/>
        </Field>

        <Field name="favoriteColor" component={renderSelectField} label="Favorite Color">
          <MenuItem value={'ff0000'} primaryText="Red"/>
          <MenuItem value={'00ff00'} primaryText="Green"/>
          <MenuItem value={'0000ff'} primaryText="Blue"/>
        </Field>

        <Field name="employed" component={renderCheckbox} label="Employed"/>

        <Field name="notes" component={renderTextField} label="Notes" multiLine={true} rows={2}/>

        <button type="submit" disabled={pristine || submitting}>Submit</button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values
        </button>

    </form>
  )
}

export default reduxForm({
  form: 'MaterialUiForm',  // a unique identifier for this form
  validate,
  asyncValidate
})(MaterialUiForm)
<commit_msg>Fixed broken RadioButtonGroup Material UI implementation. #1599<commit_after>import React from 'react'
import { Field, reduxForm } from 'redux-form'
import TextField from 'material-ui/TextField'
import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'
import Checkbox from 'material-ui/Checkbox'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import asyncValidate from './asyncValidate'

const validate = values => {
  const errors = {}
  const requiredFields = [ 'firstName', 'lastName', 'email', 'favoriteColor', 'notes' ]
  requiredFields.forEach(field => {
    if (!values[ field ]) {
      errors[ field ] = 'Required'
    }
  })
  if (values.email && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = 'Invalid email address'
  }
  return errors
}

const renderTextField = ({ input, label, meta: { touched, error }, ...custom }) => (
  <TextField hintText={label}
    floatingLabelText={label}
    errorText={touched && error}
    {...input}
    {...custom}
  />
)

const renderCheckbox = ({ input, label }) => (
  <Checkbox label={label}
    checked={input.value ? true : false}
    onCheck={input.onChange}/>
)

const renderRadioGroup = ({ input, ...rest }) => (
  <RadioButtonGroup {...input} {...rest}
    valueSelected={input.value}
    onChange={(event, value) => input.onChange(value)}/>
)

const renderSelectField = ({ input, label, meta: { touched, error }, children }) => (
  <SelectField
    floatingLabelText={label}
    errorText={touched && error}
    {...input}
    onChange={(event, index, value) => input.onChange(value)}
    children={children}/>
)

const MaterialUiForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>

        <Field name="firstName" component={renderTextField} label="First Name"/>

        <Field name="lastName" component={renderTextField} label="Last Name"/>

        <Field name="email" component={renderTextField} label="Email"/>

        <Field name="sex" component={renderRadioGroup}>
          <RadioButton value="male" label="male"/>
          <RadioButton value="female" label="female"/>
        </Field>

        <Field name="favoriteColor" component={renderSelectField} label="Favorite Color">
          <MenuItem value={'ff0000'} primaryText="Red"/>
          <MenuItem value={'00ff00'} primaryText="Green"/>
          <MenuItem value={'0000ff'} primaryText="Blue"/>
        </Field>

        <Field name="employed" component={renderCheckbox} label="Employed"/>

        <Field name="notes" component={renderTextField} label="Notes" multiLine={true} rows={2}/>

        <button type="submit" disabled={pristine || submitting}>Submit</button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values
        </button>

    </form>
  )
}

export default reduxForm({
  form: 'MaterialUiForm',  // a unique identifier for this form
  validate,
  asyncValidate
})(MaterialUiForm)
<|endoftext|>