<commit_before>import React, { Component } from 'react';
import PropTypes, { connect, withViewport, browserHistory } from '../PropTypes.js';
import { getPlayingStatusClass, getPlayingStatusColor } from '../../models/PlayerModel.js';
import { util as storeUtil } from '../../store.js';
import _ from 'lodash';

import { editMatchPlayers } from '../../actions/matchActions.js';

import Table from 'react-bootstrap/lib/Table';
import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar';
import Button from 'react-bootstrap/lib/Button';

import Icon, { TrophyIcon } from '../controls/Icon.js';
import MatchVs from './Match/MatchVs.js';
import PlayerAutoComplete from '../players/PlayerAutoComplete.js';
import { TeamCaptainIcon } from '../players/PlayerCard.js';
import { PlayerCompetitionBadge, PlayerCompetitionButton } from '../players/PlayerBadges.js';

function isPickedForMatch(status) {
  return status === 'Play' || status === 'Captain' || status === 'Major';
}

@withViewport
@connect(state => ({}), {editMatchPlayers})
export default class MatchesTable extends Component {
  static contextTypes = PropTypes.contextTypes;
  static propTypes = {
    matches: PropTypes.MatchModelList.isRequired,
    allowOpponentOnly: PropTypes.bool, // Allow "MatchVs" to display just against whom on small devices
    viewport: PropTypes.viewport,
    user: PropTypes.UserModel.isRequired,
    editMode: PropTypes.bool,
    editMatchPlayers: PropTypes.func.isRequired,

    tableForm: PropTypes.bool,
    team: PropTypes.TeamModel,
    onTablePlayerSelect: PropTypes.func,
    tablePlayers: PropTypes.array,
  }
  static defaultProps = {
    allowOpponentOnly: false,
    editMode: false,
    tableForm: false,
  }
  constructor(props) {
    super(props);
    this.state = {editMatch: {}, players: []};
  }

  _renderMatchPlayers(match) {
    const players = match.getPlayerFormation();
    return (

        {players.map(plyInfo => <PlayerCompetitionBadge plyInfo={plyInfo} competition={match.competition} style={{marginRight: 5}} />)}

    );
  }

  _getTablePlayers() {
    // Matches the sorting with the Excel output (which happens on the backend)
    return _.sortBy(this.props.team.getPlayers(), ply => (ply.type === 'Reserve' ? '1' : '0') + ply.player.alias);
  }
  _getTablePlayerHeaders() {
    if (!this.props.team) {
      return [];
    }
    return this._getTablePlayers()
    .map(ply => (

        {ply.type === 'Captain' ? <TeamCaptainIcon t={this.context.t} /> : null}
        {ply.player.alias}

    ));
  }
  _renderTableEditMatchPlayers(match) {
    const majorFormation = match.getPlayerFormation();
    if (!this.props.editMode) {
      return this._getTablePlayers()
      .map((ply, index) => {
        const playerDecision = majorFormation.find(frm => frm.id === ply.player.id);
        return (

        );
      });
    }

    const formation = match.getPlayerFormation('Play');
    return this._getTablePlayers()
    .map((plyInfo, index) => {
      var playerDecision = formation.find(frm => frm.id === plyInfo.player.id);
      const majorDecision = majorFormation.find(frm => frm.id === plyInfo.player.id);
      if (!this.props.user.canEditMatchPlayers(match)) {
        return (

            {majorDecision ? <PlayerCompetitionBadge plyInfo={majorDecision} /> : null}

        );
      }

      var captainDecision = this.props.tablePlayers.find(frm => frm.matchId === match.id && frm.id === plyInfo.player.id);
      if (!captainDecision) {
        captainDecision = {
          id: plyInfo.player.id,
          matchPlayer: {status: ''},
          player: plyInfo.player
        };
      }
      const onButtonClick = this._toggleTablePlayer.bind(this, plyInfo.player.id, match);
      return (

          <PlayerCompetitionButton
            plyInfo={captainDecision}
            isPicked={!!captainDecision.matchPlayer.status}
            actionIconClass="fa fa-thumbs-o-up"
            onButtonClick={onButtonClick} />

      );
    });
  }

  _renderEditMatchPlayers() {
    const match = this.state.editMatch;
    return (

{this.context.t('match.plys.choiceCaptain')}

        {this.state.playersEdit.map(plyInfo => (
          <PlayerCompetitionButton
            plyInfo={plyInfo}
            isPicked={isPickedForMatch(plyInfo.matchPlayer.status)}
            actionIconClass="fa fa-trash-o"
            onButtonClick={this._togglePlayer.bind(this, plyInfo.player.id)}
            competition={match.competition}
            style={{marginRight: 5}} />
        ))}

{this.context.t('match.plys.choicePlayers')}

        {this.state.players.map(plyInfo => (
          <PlayerCompetitionButton
            plyInfo={plyInfo}
            isPicked={!!this.state.playersEdit.find(x => x.id === plyInfo.id)}
            actionIconClass="fa fa-thumbs-o-up"
            onButtonClick={this._togglePlayer.bind(this, plyInfo.player.id)}
            style={{marginRight: 5}} />
        ))}

        <PlayerAutoComplete
          clearOnSelect
          selectPlayer={playerId => this._togglePlayer(playerId)}
          hintText={this.context.t('match.chooseOtherPlayer')}
          competition={match.competition}
        />

    );
  }
  _onOpenEditMatchForm(match) {
    const team = match.getTeam();
    const userStatus = this._getUserStatus(match);

    const toDontKnowPlayer = teamPlayer => ({
      id: teamPlayer.player.id,
      player: teamPlayer.player,
      matchPlayer: {status: 'DontKnow'}
    });

    const playerChoices = match.getPlayerFormation('Play');
    const playerChoicesPlayerIds = playerChoices.map(x => x.player.id);
    const playersWithoutChoice = team.getPlayers().filter(x => playerChoicesPlayerIds.indexOf(x.player.id) === -1);

    var playersEdit = match.getPlayerFormation(userStatus);
    if (playersEdit.size === 0) {
      if (userStatus === 'Major') {
        playersEdit = match.getPlayerFormation('Captain');
      }
      if (playersEdit.size === 0) {
        playersEdit = playerChoices.filter(x => x.matchPlayer.status === 'Play');
      }
    }

    this.setState({
      editMatch: match,
      players: playerChoices.concat(playersWithoutChoice.map(toDontKnowPlayer)),
      playersEdit: playersEdit,
    });
  }
  _toggleTablePlayer(playerId, match) {
    const ply = this.props.tablePlayers.find(x => x.id === playerId && x.matchId === match.id);
    if (ply) {
      this.props.onTablePlayerSelect(this.props.tablePlayers.filter(x => x !== ply), match);

    } else {
      const team = match.getTeam();
      const plyInfo = {
        id: playerId,
        matchId: match.id,
        player: storeUtil.getPlayer(playerId),
        matchPlayer: {status: this._getUserStatus(match)}
      };
      this.props.onTablePlayerSelect(this.props.tablePlayers.concat([plyInfo]), match);
    }
  }
  _togglePlayer(playerId) {
    const ply = this.state.playersEdit.find(x => x.id === playerId);
    if (ply) {
      this.setState({playersEdit: this.state.playersEdit.filter(x => x !== ply)});

    } else {
      const team = this.state.editMatch.getTeam();
      const plyInfo = {
        id: playerId,
        player: storeUtil.getPlayer(playerId),
        matchPlayer: {status: this._getUserStatus(this.state.editMatch)}
      };
      this.setState({playersEdit: this.state.playersEdit.concat([plyInfo])});
    }
  }
  _saveFormation({blockAlso, closeForm}) {
    this.props.editMatchPlayers({
      matchId: this.state.editMatch.id,
      playerIds: this.state.playersEdit.map(x => x.id).toArray(),
      blockAlso: blockAlso,
      newStatus: this._getUserStatus(this.state.editMatch)
    });

    if (closeForm) {
      this.setState({editMatch: {}, players: [], playersEdit: []});
    }
  }

  _isCaptain(match) {
    const team = match.getTeam();
    return team.isCaptain(this.props.user.getPlayer());
  }
  _getUserStatus(match) {
    return this._isCaptain(match) ? 'Captain' : 'Major';
  }

  render() {
    const t = this.context.t;
    const matchRows = [];

    this.props.matches.forEach((match, i) => {
      const stripeColor = {backgroundColor: i % 2 === 0 && !this.props.tableForm ? '#f9f9f9' : undefined};
      const displayVictoryIcon = match.scoreType === 'Won';
      const score = match.renderScore();

      var thrillerIcon;
      const team = match.getTeam();
      if (team.getThriller(match)) {
        thrillerIcon = <ThrillerIcon color="red" />;
      }

      // Complexity galore
      matchRows.push(

            {displayVictoryIcon ? <TrophyIcon style={{marginRight: 5}} /> : null}
            {thrillerIcon}
            {t('match.date', match.getDisplayDate())}

          {match.frenoyMatchId}
          <MatchVs match={match} opponentOnly={this.props.allowOpponentOnly && this.props.viewport.width < 450} />
          {this.props.tableForm ? null : (
            {!this.props.editMode || match.isSyncedWithFrenoy ? (
              <button className="btn btn-default" onClick={() => browserHistory.push(t.route('match', {matchId: match.id}))}>
                {score || t('match.details')}
              </button>

            ) : this.state.editMatch.id !== match.id && this.props.user.canEditMatchPlayers(match) ? (
              <button
                onClick={this._onOpenEditMatchForm.bind(this, match)}
                className="btn btn-default pull-right"
                style={{marginRight: 5}}
                title={t('match.plys.tooltipOpenForm')}
              >

                  {!match.block ? (
                    <Icon fa="fa fa-pencil-square-o fa-stack-1x" />
                  ) : (

                      <Icon fa="fa fa-anchor fa-stack-1x" />
                      <Icon fa="fa fa-ban fa-stack-2x text-danger" />

                  )}

              </button>

            ) : this.state.editMatch.id !== match.id ? (

                <Icon fa="fa fa-pencil-square-o fa-stack-1x" />
                <Icon fa="fa fa-ban fa-stack-2x text-danger" />

            ) : (

                <button
                  className={'btn btn-default ' + (this.state.playersEdit.size !== team.getTeamPlayerCount() ? 'disabled' : '')}
                  onClick={this._saveFormation.bind(this, {blockAlso: true, closeForm: true})}
                  style={{marginRight: 5}}
                  title={t('match.plys.tooltipSaveAndBlock')}
                >

                  <Icon fa="fa fa-anchor" />
                </button>

                <button
                  className="btn btn-default"
                  onClick={this._saveFormation.bind(this, {blockAlso: false, closeForm: true})}
                  title={t('match.plys.tooltipSave')}
                >
                  <Icon fa="fa fa-floppy-o" />
                </button>

            )}
          	)}
          {this.props.tableForm ? this._renderTableEditMatchPlayers(match) : null}

      );

      if (!this.props.tableForm) {
        if (this.props.editMode && this.state.editMatch.id === match.id && this.props.user.canEditMatchPlayers(match)) {
          matchRows.push(

                {this._renderEditMatchPlayers()}

          );

        } else if (match.players.size) {
          matchRows.push(

                {this._renderMatchPlayers(match)}

          );
        }
      }
    });

    return (

            {t('common.date')}
            {t('common.frenoy')}
            {t('teamCalendar.match')}
            {!this.props.tableForm ? {this.props.editMode ? t('match.plys.blockMatchTitle') : t('teamCalendar.score')}	 : null}
            {this.props.tableForm ? this._getTablePlayerHeaders() : null}

          {matchRows}
          {this.props.tableForm && this.props.editMode ? (

              {this._getTablePlayers().map((ply, index) => {
                const played = this.props.matches.filter(match => match.players.find(mp => mp.playerId === ply.player.id && mp.status === match.block))
                return (
                  {played.size}
                );
              })}

          ) : null}

    );
  }
}

import { ThrillerIcon } from './Match/MatchCardHeader.js';<commit_msg>Bugfix: wrong import path<commit_after>import React, { Component } from 'react';
import PropTypes, { connect, withViewport, browserHistory } from '../PropTypes.js';
import { getPlayingStatusClass, getPlayingStatusColor } from '../../models/PlayerModel.js';
import { util as storeUtil } from '../../store.js';
import _ from 'lodash';

import { editMatchPlayers } from '../../actions/matchActions.js';

import Table from 'react-bootstrap/lib/Table';
import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar';
import Button from 'react-bootstrap/lib/Button';
import Icon, { TrophyIcon, ThrillerIcon } from '../controls/Icon.js';
import MatchVs from './Match/MatchVs.js';
import PlayerAutoComplete from '../players/PlayerAutoComplete.js';
import { TeamCaptainIcon } from '../players/PlayerCard.js';
import { PlayerCompetitionBadge, PlayerCompetitionButton } from '../players/PlayerBadges.js';

function isPickedForMatch(status) {
  return status === 'Play' || status === 'Captain' || status === 'Major';
}

@withViewport
@connect(state => ({}), {editMatchPlayers})
export default class MatchesTable extends Component {
  static contextTypes = PropTypes.contextTypes;
  static propTypes = {
    matches: PropTypes.MatchModelList.isRequired,
    allowOpponentOnly: PropTypes.bool, // Allow "MatchVs" to display just against whom on small devices
    viewport: PropTypes.viewport,
    user: PropTypes.UserModel.isRequired,
    editMode: PropTypes.bool,
    editMatchPlayers: PropTypes.func.isRequired,

    tableForm: PropTypes.bool,
    team: PropTypes.TeamModel,
    onTablePlayerSelect: PropTypes.func,
    tablePlayers: PropTypes.array,
  }
  static defaultProps = {
    allowOpponentOnly: false,
    editMode: false,
    tableForm: false,
  }
  constructor(props) {
    super(props);
    this.state = {editMatch: {}, players: []};
  }

  _renderMatchPlayers(match) {
    const players = match.getPlayerFormation();
    return (

        {players.map(plyInfo => <PlayerCompetitionBadge plyInfo={plyInfo} competition={match.competition} style={{marginRight: 5}} />)}

    );
  }

  _getTablePlayers() {
    // Matches the sorting with the Excel output (which happens on the backend)
    return _.sortBy(this.props.team.getPlayers(), ply => (ply.type === 'Reserve' ? '1' : '0') + ply.player.alias);
  }
  _getTablePlayerHeaders() {
    if (!this.props.team) {
      return [];
    }
    return this._getTablePlayers()
    .map(ply => (

        {ply.type === 'Captain' ? <TeamCaptainIcon t={this.context.t} /> : null}
        {ply.player.alias}

    ));
  }
  _renderTableEditMatchPlayers(match) {
    const majorFormation = match.getPlayerFormation();
    if (!this.props.editMode) {
      return this._getTablePlayers()
      .map((ply, index) => {
        const playerDecision = majorFormation.find(frm => frm.id === ply.player.id);
        return (

        );
      });
    }

    const formation = match.getPlayerFormation('Play');
    return this._getTablePlayers()
    .map((plyInfo, index) => {
      var playerDecision = formation.find(frm => frm.id === plyInfo.player.id);
      const majorDecision = majorFormation.find(frm => frm.id === plyInfo.player.id);
      if (!this.props.user.canEditMatchPlayers(match)) {
        return (

            {majorDecision ? <PlayerCompetitionBadge plyInfo={majorDecision} /> : null}

        );
      }

      var captainDecision = this.props.tablePlayers.find(frm => frm.matchId === match.id && frm.id === plyInfo.player.id);
      if (!captainDecision) {
        captainDecision = {
          id: plyInfo.player.id,
          matchPlayer: {status: ''},
          player: plyInfo.player
        };
      }
      const onButtonClick = this._toggleTablePlayer.bind(this, plyInfo.player.id, match);
      return (

          <PlayerCompetitionButton
            plyInfo={captainDecision}
            isPicked={!!captainDecision.matchPlayer.status}
            actionIconClass="fa fa-thumbs-o-up"
            onButtonClick={onButtonClick} />

      );
    });
  }

  _renderEditMatchPlayers() {
    const match = this.state.editMatch;
    return (

{this.context.t('match.plys.choiceCaptain')}

        {this.state.playersEdit.map(plyInfo => (
          <PlayerCompetitionButton
            plyInfo={plyInfo}
            isPicked={isPickedForMatch(plyInfo.matchPlayer.status)}
            actionIconClass="fa fa-trash-o"
            onButtonClick={this._togglePlayer.bind(this, plyInfo.player.id)}
            competition={match.competition}
            style={{marginRight: 5}} />
        ))}

{this.context.t('match.plys.choicePlayers')}

        {this.state.players.map(plyInfo => (
          <PlayerCompetitionButton
            plyInfo={plyInfo}
            isPicked={!!this.state.playersEdit.find(x => x.id === plyInfo.id)}
            actionIconClass="fa fa-thumbs-o-up"
            onButtonClick={this._togglePlayer.bind(this, plyInfo.player.id)}
            style={{marginRight: 5}} />
        ))}

        <PlayerAutoComplete
          clearOnSelect
          selectPlayer={playerId => this._togglePlayer(playerId)}
          hintText={this.context.t('match.chooseOtherPlayer')}
          competition={match.competition}
        />

    );
  }
  _onOpenEditMatchForm(match) {
    const team = match.getTeam();
    const userStatus = this._getUserStatus(match);

    const toDontKnowPlayer = teamPlayer => ({
      id: teamPlayer.player.id,
      player: teamPlayer.player,
      matchPlayer: {status: 'DontKnow'}
    });

    const playerChoices = match.getPlayerFormation('Play');
    const playerChoicesPlayerIds = playerChoices.map(x => x.player.id);
    const playersWithoutChoice = team.getPlayers().filter(x => playerChoicesPlayerIds.indexOf(x.player.id) === -1);

    var playersEdit = match.getPlayerFormation(userStatus);
    if (playersEdit.size === 0) {
      if (userStatus === 'Major') {
        playersEdit = match.getPlayerFormation('Captain');
      }
      if (playersEdit.size === 0) {
        playersEdit = playerChoices.filter(x => x.matchPlayer.status === 'Play');
      }
    }

    this.setState({
      editMatch: match,
      players: playerChoices.concat(playersWithoutChoice.map(toDontKnowPlayer)),
      playersEdit: playersEdit,
    });
  }
  _toggleTablePlayer(playerId, match) {
    const ply = this.props.tablePlayers.find(x => x.id === playerId && x.matchId === match.id);
    if (ply) {
      this.props.onTablePlayerSelect(this.props.tablePlayers.filter(x => x !== ply), match);

    } else {
      const team = match.getTeam();
      const plyInfo = {
        id: playerId,
        matchId: match.id,
        player: storeUtil.getPlayer(playerId),
        matchPlayer: {status: this._getUserStatus(match)}
      };
      this.props.onTablePlayerSelect(this.props.tablePlayers.concat([plyInfo]), match);
    }
  }
  _togglePlayer(playerId) {
    const ply = this.state.playersEdit.find(x => x.id === playerId);
    if (ply) {
      this.setState({playersEdit: this.state.playersEdit.filter(x => x !== ply)});

    } else {
      const team = this.state.editMatch.getTeam();
      const plyInfo = {
        id: playerId,
        player: storeUtil.getPlayer(playerId),
        matchPlayer: {status: this._getUserStatus(this.state.editMatch)}
      };
      this.setState({playersEdit: this.state.playersEdit.concat([plyInfo])});
    }
  }
  _saveFormation({blockAlso, closeForm}) {
    this.props.editMatchPlayers({
      matchId: this.state.editMatch.id,
      playerIds: this.state.playersEdit.map(x => x.id).toArray(),
      blockAlso: blockAlso,
      newStatus: this._getUserStatus(this.state.editMatch)
    });

    if (closeForm) {
      this.setState({editMatch: {}, players: [], playersEdit: []});
    }
  }

  _isCaptain(match) {
    const team = match.getTeam();
    return team.isCaptain(this.props.user.getPlayer());
  }
  _getUserStatus(match) {
    return this._isCaptain(match) ? 'Captain' : 'Major';
  }

  render() {
    const t = this.context.t;
    const matchRows = [];

    this.props.matches.forEach((match, i) => {
      const stripeColor = {backgroundColor: i % 2 === 0 && !this.props.tableForm ? '#f9f9f9' : undefined};
      const displayVictoryIcon = match.scoreType === 'Won';
      const score = match.renderScore();

      var thrillerIcon;
      const team = match.getTeam();
      if (team.getThriller(match)) {
        thrillerIcon = <ThrillerIcon color="red" />;
      }

      // Complexity galore
      matchRows.push(

            {displayVictoryIcon ? <TrophyIcon style={{marginRight: 5}} /> : null}
            {thrillerIcon}
            {t('match.date', match.getDisplayDate())}

          {match.frenoyMatchId}
          <MatchVs match={match} opponentOnly={this.props.allowOpponentOnly && this.props.viewport.width < 450} />
          {this.props.tableForm ? null : (
            {!this.props.editMode || match.isSyncedWithFrenoy ? (
              <button className="btn btn-default" onClick={() => browserHistory.push(t.route('match', {matchId: match.id}))}>
                {score || t('match.details')}
              </button>

            ) : this.state.editMatch.id !== match.id && this.props.user.canEditMatchPlayers(match) ? (
              <button
                onClick={this._onOpenEditMatchForm.bind(this, match)}
                className="btn btn-default pull-right"
                style={{marginRight: 5}}
                title={t('match.plys.tooltipOpenForm')}
              >

                  {!match.block ? (
                    <Icon fa="fa fa-pencil-square-o fa-stack-1x" />
                  ) : (

                      <Icon fa="fa fa-anchor fa-stack-1x" />
                      <Icon fa="fa fa-ban fa-stack-2x text-danger" />

                  )}

              </button>

            ) : this.state.editMatch.id !== match.id ? (

                <Icon fa="fa fa-pencil-square-o fa-stack-1x" />
                <Icon fa="fa fa-ban fa-stack-2x text-danger" />

            ) : (

                <button
                  className={'btn btn-default ' + (this.state.playersEdit.size !== team.getTeamPlayerCount() ? 'disabled' : '')}
                  onClick={this._saveFormation.bind(this, {blockAlso: true, closeForm: true})}
                  style={{marginRight: 5}}
                  title={t('match.plys.tooltipSaveAndBlock')}
                >

                  <Icon fa="fa fa-anchor" />
                </button>

                <button
                  className="btn btn-default"
                  onClick={this._saveFormation.bind(this, {blockAlso: false, closeForm: true})}
                  title={t('match.plys.tooltipSave')}
                >
                  <Icon fa="fa fa-floppy-o" />
                </button>

            )}
          	)}
          {this.props.tableForm ? this._renderTableEditMatchPlayers(match) : null}

      );

      if (!this.props.tableForm) {
        if (this.props.editMode && this.state.editMatch.id === match.id && this.props.user.canEditMatchPlayers(match)) {
          matchRows.push(

                {this._renderEditMatchPlayers()}

          );

        } else if (match.players.size) {
          matchRows.push(

                {this._renderMatchPlayers(match)}

          );
        }
      }
    });

    return (

            {t('common.date')}
            {t('common.frenoy')}
            {t('teamCalendar.match')}
            {!this.props.tableForm ? {this.props.editMode ? t('match.plys.blockMatchTitle') : t('teamCalendar.score')}	 : null}
            {this.props.tableForm ? this._getTablePlayerHeaders() : null}

          {matchRows}
          {this.props.tableForm && this.props.editMode ? (

              {this._getTablePlayers().map((ply, index) => {
                const played = this.props.matches.filter(match => match.players.find(mp => mp.playerId === ply.player.id && mp.status === match.block))
                return (
                  {played.size}
                );
              })}

          ) : null}

    );
  }
}<|endoftext|>


<commit_before>import {curry, isNil} from 'ramda'
import React from 'react'
import THREE from 'three'
import {randomMesh} from '../../utils/webGLHelpers'

const clamp = curry((min, max, value) => value < min ? min : value > max ? max : value)

const cameraZ = 16
const fallAwayVelocity = 1.2
const minZ = -160
const returnVelocity = 12

const sideLength = 1
const maxDepth = 3 * sideLength

let currentXYRatios = null
let controlPadElement = null
let renderLoopActive = null
let token = null
let renderer = null
let camera = null
let scene = null
let mouseInputEnabled = false
let controlPadActive = false

const setRendererSize = _ => {
  const rendererSize = window.innerWidth < window.innerHeight
    ? window.innerWidth
    : window.innerHeight * 0.8
  renderer.setSize(rendererSize, rendererSize)
}

const renderLoop = _ => {
  if (!renderLoopActive) return
  window.requestAnimationFrame(renderLoop)
  const controlPadHasNotBeenUsed = isNil(currentXYRatios)
  const {z} = token.position
  if (controlPadHasNotBeenUsed) return
  if (!controlPadActive) {
    if (z > minZ - maxDepth) token.position.z -= fallAwayVelocity
    renderer.render(scene, camera)
    return
  }
  const {xRatio, yRatio} = currentXYRatios
  const xMod = xRatio < 0.5
    ? -(xRatio - 0.5) ** 2
    : (xRatio - 0.5) ** 2
  const yMod = yRatio < 0.5
    ? -(yRatio - 0.5) ** 2
    : (yRatio - 0.5) ** 2
  const rotationBaseAmount = 0.01
  const rotationVelocityComponent = 0.8
  token.rotation.x += rotationBaseAmount + rotationVelocityComponent * xMod
  token.rotation.y += rotationBaseAmount + rotationVelocityComponent * yMod
  token.rotation.z += rotationBaseAmount + rotationVelocityComponent * xMod * yMod
  token.position.x = (xRatio - 0.5) * cameraZ
  token.position.y = (0.5 - yRatio) * cameraZ
  if (z < 0) token.position.z += z > -returnVelocity ? -z : returnVelocity
  renderer.render(scene, camera)
}

const validRatio = clamp(0, 1 - Number.EPSILON)

const calculateXAndYRatio = e => {
  const {top, right, bottom, left} = e.target.getBoundingClientRect()
  const [width, height] = [right - left, bottom - top]
  const {clientX, clientY} = e.changedTouches && e.changedTouches[0] || e
  const [x, y] = [clientX - left, clientY - top]
  return {xRatio: validRatio(x / width), yRatio: validRatio(y / height)}
}

export default class extends React.Component {
  componentDidMount () {
    const {inputStopHandler, inputStartHandler, inputModifyHandler} = this.props
    controlPadElement = document.querySelector('.control-pad')

    const inputCallback = e => {
      mouseInputEnabled = e.type === 'mousedown' ? true : mouseInputEnabled
      if (e instanceof window.MouseEvent && !mouseInputEnabled) return
      currentXYRatios = calculateXAndYRatio(e)
      if (controlPadActive) {
        inputModifyHandler(currentXYRatios)
        return
      }
      controlPadActive = true
      inputStartHandler(currentXYRatios)
    }

    controlPadElement.addEventListener('touchstart', inputCallback)
    controlPadElement.addEventListener('touchmove', inputCallback)
    controlPadElement.addEventListener('mousedown', inputCallback)
    controlPadElement.addEventListener('mousemove', inputCallback)

    const inputEndCallback = _ => {
      controlPadActive = mouseInputEnabled = false
      inputStopHandler()
    }

    controlPadElement.addEventListener('touchend', inputEndCallback)
    controlPadElement.addEventListener('mouseup', inputEndCallback)

    renderLoopActive = true
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(
      54,
      1,
      cameraZ - maxDepth,
      cameraZ - minZ
    )
    renderer = new THREE.WebGLRenderer({alpha: true, canvas: controlPadElement})
    const directionalLight = new THREE.DirectionalLight(0xffffff)
    token = randomMesh()
    token.position.z = minZ - maxDepth

    directionalLight.position.set(16, 16, 24).normalize()
    scene.add(new THREE.AmbientLight(0x333333))
      .add(directionalLight)
      .add(token)
    camera.position.z = cameraZ

    setRendererSize()
    window.onresize = setRendererSize

    controlPadElement.oncontextmenu = e => e.preventDefault()

    renderLoop()
  }

  componentWillUnmount () {
    renderLoopActive = false
    window.onresize = null
  }

  render () {
    return <canvas width='768' height='768' className='control-pad'></canvas>
  }
}
<commit_msg>feat(control-pad) faster rotation on click obj<commit_after>import {curry, isNil} from 'ramda'
import React from 'react'
import THREE from 'three'
import {randomMesh} from '../../utils/webGLHelpers'

const clamp = curry((min, max, value) => value < min ? min : value > max ? max : value)

const cameraZ = 16
const fallAwayVelocity = 1.2
const minZ = -160
const returnVelocity = 12

const sideLength = 1
const maxDepth = 3 * sideLength

let currentXYRatios = null
let controlPadElement = null
let renderLoopActive = null
let token = null
let renderer = null
let camera = null
let scene = null
let mouseInputEnabled = false
let controlPadActive = false

const setRendererSize = _ => {
  const rendererSize = window.innerWidth < window.innerHeight
    ? window.innerWidth
    : window.innerHeight * 0.8
  renderer.setSize(rendererSize, rendererSize)
}

const renderLoop = _ => {
  if (!renderLoopActive) return
  window.requestAnimationFrame(renderLoop)
  const controlPadHasNotBeenUsed = isNil(currentXYRatios)
  const {z} = token.position
  if (controlPadHasNotBeenUsed) return
  if (!controlPadActive) {
    if (z > minZ - maxDepth) token.position.z -= fallAwayVelocity
    renderer.render(scene, camera)
    return
  }
  const {xRatio, yRatio} = currentXYRatios
  const xMod = xRatio < 0.5
    ? -(xRatio - 0.5) ** 2
    : (xRatio - 0.5) ** 2
  const yMod = yRatio < 0.5
    ? -(yRatio - 0.5) ** 2
    : (yRatio - 0.5) ** 2
  const rotationBaseAmount = 0.01
  const rotationVelocityComponent = 1.5
  token.rotation.x += rotationBaseAmount + rotationVelocityComponent * xMod
  token.rotation.y += rotationBaseAmount + rotationVelocityComponent * yMod
  token.rotation.z += rotationBaseAmount + rotationVelocityComponent * xMod * yMod
  token.position.x = (xRatio - 0.5) * cameraZ
  token.position.y = (0.5 - yRatio) * cameraZ
  if (z < 0) token.position.z += z > -returnVelocity ? -z : returnVelocity
  renderer.render(scene, camera)
}

const validRatio = clamp(0, 1 - Number.EPSILON)

const calculateXAndYRatio = e => {
  const {top, right, bottom, left} = e.target.getBoundingClientRect()
  const [width, height] = [right - left, bottom - top]
  const {clientX, clientY} = e.changedTouches && e.changedTouches[0] || e
  const [x, y] = [clientX - left, clientY - top]
  return {xRatio: validRatio(x / width), yRatio: validRatio(y / height)}
}

export default class extends React.Component {
  componentDidMount () {
    const {inputStopHandler, inputStartHandler, inputModifyHandler} = this.props
    controlPadElement = document.querySelector('.control-pad')

    const inputCallback = e => {
      mouseInputEnabled = e.type === 'mousedown' ? true : mouseInputEnabled
      if (e instanceof window.MouseEvent && !mouseInputEnabled) return
      currentXYRatios = calculateXAndYRatio(e)
      if (controlPadActive) {
        inputModifyHandler(currentXYRatios)
        return
      }
      controlPadActive = true
      inputStartHandler(currentXYRatios)
    }

    controlPadElement.addEventListener('touchstart', inputCallback)
    controlPadElement.addEventListener('touchmove', inputCallback)
    controlPadElement.addEventListener('mousedown', inputCallback)
    controlPadElement.addEventListener('mousemove', inputCallback)

    const inputEndCallback = _ => {
      controlPadActive = mouseInputEnabled = false
      inputStopHandler()
    }

    controlPadElement.addEventListener('touchend', inputEndCallback)
    controlPadElement.addEventListener('mouseup', inputEndCallback)

    renderLoopActive = true
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(
      54,
      1,
      cameraZ - maxDepth,
      cameraZ - minZ
    )
    renderer = new THREE.WebGLRenderer({alpha: true, canvas: controlPadElement})
    const directionalLight = new THREE.DirectionalLight(0xffffff)
    token = randomMesh()
    token.position.z = minZ - maxDepth

    directionalLight.position.set(16, 16, 24).normalize()
    scene.add(new THREE.AmbientLight(0x333333))
      .add(directionalLight)
      .add(token)
    camera.position.z = cameraZ

    setRendererSize()
    window.onresize = setRendererSize

    controlPadElement.oncontextmenu = e => e.preventDefault()

    renderLoop()
  }

  componentWillUnmount () {
    renderLoopActive = false
    window.onresize = null
  }

  render () {
    return <canvas width='768' height='768' className='control-pad'></canvas>
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Tooltip from 'react-toolbox/lib/tooltip'
import Button from 'react-toolbox/lib/button/Button'
import { fetchSingleProject, getProjectById } from 'reducers/projects.reducer'
import {
  actions as taskActions,
  selectors as taskSelectors
} from 'reducers/tasks.reducer'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import TaskListItem from './TaskListItem'
import ProgressBar from 'react-toolbox/lib/progress_bar/ProgressBar'
import styled from 'styled-components'

const TooltipButton = Tooltip(Button);
const List = styled.ul`
  list-style: none;
  padding: 0;
  margin: 0;
`

class TaskList extends Component {
  state = {
    editModes: {}
  }
  componentDidMount() {
    const id = this.props.routeParams.projectId
    this.props.fetchSingleProject(id)
    this.fetchTasks(0)
  }
  fetchTasks(page) {
    const id = this.props.routeParams.projectId
    this.props.taskActions.fetchByProject(id, {page})
  }
  handleNameEdit = (data) => {
    this.props.taskActions.save(data, true)
    .then(() => {
      this.setState(state => ({
        ...state,
        editModes: {
          ...state.editModes,
          [data._id]: false
        }
      }))
    })
  }
  handleEditMode = (task, editMode) => {
    this.setState(state => ({
      ...state,
      editModes: {
        ...state.editModes,
        [task._id]: editMode
      }
    }))
  }
  createTask = () => {
    const newTask = {
      name: 'Nueva tarea',
      project: this.props.project._id
    }
    this.props.taskActions.save(newTask, false)
    .then(res => res.value)
    .then(task => {
      this.setState(state => ({
        ...state,
        editModes: {
          ...state.editModes,
          [task._id]: true
        }
      }))
    })
  }
  render () {
    const {editModes} = this.state
    const {loading, tasks, pageParams, project, children} = this.props
    return (

            {project.name}

          {loading && (

              <ProgressBar type='circular' mode='indeterminate' />

                Cargando ...

          )}
          {!loading && tasks.length < 1 ? (

              No hay ninguna tarea para este proyecto todavía.

              Puedes crear una con el boton redondo naranja de arriba

          ) : null}

        <TooltipButton
          icon="add"
          floating accent
          tooltip="Nueva tarea"
          tooltipPosition="left"
          className="list-corner-fab"
          onClick={this.createTask}
        />
        <List>
          {tasks.map(task => (
            <TaskListItem
              onChange={this.handleNameEdit}
              task={task}
              editMode={editModes[task._id]}
              onEdit={this.handleEditMode}
              key={task._id} />
          ))}
        </List>
        {pageParams.last === false && (
          <Button
            icon="refresh"
            raised primary
            disabled={loading}
            style={{margin: '2em 0'}}
            onClick={() => this.fetchTasks(pageParams.page + 1)}>
            Cargar más
          </Button>
        )}
        {children}

    );
  }
}

export default connect(
  (state, props) => {
    const projectId = props.routeParams.projectId
    const project = getProjectById(state, projectId)
    const {tasks, pageParams} = taskSelectors.getByProjectId(state, projectId)
    const loading = taskSelectors.getLoading(state)
    return {
      project,
      tasks,
      pageParams,
      loading
    }
  },
  (dispatch) => ({
    ...bindActionCreators({fetchSingleProject}, dispatch),
    taskActions: bindActionCreators(taskActions, dispatch)
  })
)(TaskList)
<commit_msg>feat: add backbutton to task list<commit_after>import React, { Component } from 'react';
import Tooltip from 'react-toolbox/lib/tooltip'
import Button from 'react-toolbox/lib/button/Button'
import { fetchSingleProject, getProjectById } from 'reducers/projects.reducer'
import {
  actions as taskActions,
  selectors as taskSelectors
} from 'reducers/tasks.reducer'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import TaskListItem from './TaskListItem'
import ProgressBar from 'react-toolbox/lib/progress_bar/ProgressBar'
import BackButton from 'components/shared/BackButton'
import styled from 'styled-components'

const TooltipButton = Tooltip(Button);
const List = styled.ul`
  list-style: none;
  padding: 0;
  margin: 0;
`

class TaskList extends Component {
  state = {
    editModes: {}
  }
  componentDidMount() {
    const id = this.props.routeParams.projectId
    const {tasks, project, fetchSingleProject} = this.props
    if(project.missing) {
      fetchSingleProject(id)
    }
    if(tasks.length < 1) {
      this.fetchTasks(0)
    }
  }
  fetchTasks(page) {
    const id = this.props.routeParams.projectId
    this.props.taskActions.fetchByProject(id, {page})
  }
  handleNameEdit = (data) => {
    this.props.taskActions.save(data, true)
    .then(() => {
      this.setState(state => ({
        ...state,
        editModes: {
          ...state.editModes,
          [data._id]: false
        }
      }))
    })
  }
  handleEditMode = (task, editMode) => {
    this.setState(state => ({
      ...state,
      editModes: {
        ...state.editModes,
        [task._id]: editMode
      }
    }))
  }
  createTask = () => {
    const newTask = {
      name: 'Nueva tarea',
      project: this.props.project._id
    }
    this.props.taskActions.save(newTask, false)
    .then(res => res.value)
    .then(task => {
      this.setState(state => ({
        ...state,
        editModes: {
          ...state.editModes,
          [task._id]: true
        }
      }))
    })
  }
  render () {
    const {editModes} = this.state
    const {loading, tasks, pageParams, project, children} = this.props
    return (

        <BackButton router={this.props.router} />

            {project.name}

          {loading && (

              <ProgressBar type='circular' mode='indeterminate' />

                Cargando ...

          )}
          {!loading && tasks.length < 1 ? (

              No hay ninguna tarea para este proyecto todavía.

              Puedes crear una con el boton redondo naranja de arriba

          ) : null}

        <TooltipButton
          icon="add"
          floating accent
          tooltip="Nueva tarea"
          tooltipPosition="left"
          className="list-corner-fab"
          onClick={this.createTask}
        />
        <List>
          {tasks.map(task => (
            <TaskListItem
              onChange={this.handleNameEdit}
              task={task}
              editMode={editModes[task._id]}
              onEdit={this.handleEditMode}
              key={task._id} />
          ))}
        </List>
        {pageParams.last === false && (
          <Button
            icon="refresh"
            raised primary
            disabled={loading}
            style={{margin: '2em 0'}}
            onClick={() => this.fetchTasks(pageParams.page + 1)}>
            Cargar más
          </Button>
        )}
        {children}

    );
  }
}

export default connect(
  (state, props) => {
    const projectId = props.routeParams.projectId
    const project = getProjectById(state, projectId)
    const {tasks, pageParams} = taskSelectors.getByProjectId(state, projectId)
    const loading = taskSelectors.getLoading(state)
    return {
      project,
      tasks,
      pageParams,
      loading
    }
  },
  (dispatch) => ({
    ...bindActionCreators({fetchSingleProject}, dispatch),
    taskActions: bindActionCreators(taskActions, dispatch)
  })
)(TaskList)
<|endoftext|>


<commit_before>// Device
var Device = (function () {
  var ua = navigator.userAgent.toLowerCase()
  // 内核
  var kernel = ''
  if (ua.indexOf('trident') > -1) {
    kernel = 'trident'
  } else if (ua.indexOf('presto') > -1) {
    kernel = 'presto'
  } else if (ua.indexOf('applewebkit') > -1) {
    kernel = 'webkit'
  } else if (ua.indexOf('gecko') > -1 && ua.indexOf('khtml') === -1) {
    kernel = 'gecko'
  }
  // 设备
  var device = ''
  if (ua.match(/applewebkit.*mobile.*/)) {
    device = 'mobile'
  } else {
    device = 'pc'
  }
  // 系统
  var os = ''
  var osVersion = ''
  var andriodExp = ua.match(/android\s*(\d*\.*\d*)/)
  var iosExp = ua.match(/cpu iphone os (.*?) like mac os/)
  if (andriodExp) {
    os = 'andriod'
    osVersion = andriodExp[1]
  } else if (iosExp) {
    os = 'ios'
    osVersion = iosExp[1]
  }
  // 平台
  var platform = ''
  if (ua.indexOf('micromessenger') > -1) {
    platform = 'weixin'
  } else if (ua.indexOf('mqqbrowser') > -1) {
    platform = 'qq'
  } else if (ua.indexOf('ucbrowser') > -1) {
    platform = 'uc'
  }
  // 网络监听
  var onLineCallback
  function handleOnline (e) {
    onLineCallback(true)
  }
  function handleOffline (e) {
    onLineCallback(false)
  }
  function onLine (callback) {
    onLineCallback = callback
    window.removeEventListener('online', handleOnline, false)
    window.removeEventListener('offline', handleOffline, false)
    window.addEventListener('online', handleOnline, false)
    window.addEventListener('offline', handleOffline, false)
  }
  return {
    kernel: kernel,
    device: device,
    os: os,
    osVersion: osVersion,
    platform: platform,
    // 应用程序判断
    language: (window.navigator.browserLanguage || window.navigator.language).toLowerCase(),
    appVersion: window.navigator.appVersion,
    onLine: onLine,
    isOnLine: window.navigator.onLine,
    ua: ua
  }
})();

//export default Device
<commit_msg>增强device<commit_after>// Device
var Device = (function () {
  var ua = navigator.userAgent.toLowerCase()
  // 内核
  var kernel = ''
  if (ua.indexOf('trident') > -1) {
    kernel = 'trident'
  } else if (ua.indexOf('presto') > -1) {
    kernel = 'presto'
  } else if (ua.indexOf('applewebkit') > -1) {
    kernel = 'webkit'
  } else if (ua.indexOf('gecko') > -1 && ua.indexOf('khtml') === -1) {
    kernel = 'gecko'
  }
  // 设备
  var device = ''
  if (ua.match(/applewebkit.*mobile.*/)) {
    device = 'mobile'
  } else {
    device = 'pc'
  }
  // 系统
  var os = ''
  var osVersion = ''
  var andriodExp = ua.match(/android\s*(\d*\.*\d*)/)
  var iosExp = ua.match(/cpu iphone os (.*?) like mac os/)
  if (andriodExp) {
    os = 'andriod'
    osVersion = andriodExp[1]
  } else if (iosExp) {
    os = 'ios'
    osVersion = iosExp[1]
  }
  // 平台
  var platform = ''
  if (ua.indexOf('micromessenger') > -1) {
    platform = 'weixin'
  } else if (ua.indexOf('mqqbrowser') > -1) {
    platform = 'qq'
  } else if (ua.indexOf('ucbrowser') > -1) {
    platform = 'uc'
  }
  // 获得苹果机型
  function appleModel () { // 获取设备型号
    let ua = navigator.userAgent.toLocaleLowerCase()
    if (ua && /(iphone|ipad|ipod|ios)/i.test(ua)) {
      let m = ua.match(/mobile\/([\w.]*)/)
      if (m && m[1]) {
        return m[1]
      }
    }
    return ''
  }

  function getAppleDevice () { // 获取弱设备
    let model = appleModel()
    switch (model) {
      case '15b150':
        return 'iPhone6s'
      case '15b202':
        return 'iPhone6'
      case '13g36':
        return 'iPhone5SE'
      case '14e304':
        return 'iPhone6P'
      default:
        return ''
    }
  }

  // 网络监听
  var onLineCallback
  function handleOnline (e) {
    onLineCallback(true)
  }
  function handleOffline (e) {
    onLineCallback(false)
  }
  function onLine (callback) {
    onLineCallback = callback
    window.removeEventListener('online', handleOnline, false)
    window.removeEventListener('offline', handleOffline, false)
    window.addEventListener('online', handleOnline, false)
    window.addEventListener('offline', handleOffline, false)
  }
  return {
    kernel: kernel,
    device: device,
    os: os,
    osVersion: osVersion,
    platform: platform,
    appleDevice: getAppleDevice(),
    // 应用程序判断
    language: (window.navigator.browserLanguage || window.navigator.language).toLowerCase(),
    appVersion: window.navigator.appVersion,
    onLine: onLine,
    isOnLine: window.navigator.onLine,
    ua: ua
  }
})()

;//export default Device
<|endoftext|>


<commit_before>// The fs here actually refers to http://phantomjs.org/api/fs/
var fs = require('fs');
var utils = require('utils');

var bitmaps_reference = 'bitmaps_reference';
var bitmaps_test = 'bitmaps_test';
var compareConfigFileName = 'compare/config.json';
var genConfigPath = 'capture/config.json';

var configJSON = fs.read(genConfigPath);
var config = JSON.parse(configJSON);

var viewports = config.viewports;
var scenarios = config.scenarios||config.grabConfigs;

var compareConfig = {testPairs:[]};

var casper = require("casper").create({
	// clientScripts: ["jquery.js"] //lets try not to use this it's friggin 2014 already people...
});
var options = casper.cli.options;

casper.on('resource.received', function(resource) {
		//casper.echo(resource.url);
});

casper.on("page.error", function(msg, trace) {
	// this.echo("Remote Error >    " + msg, "error");
	// this.echo("file:     " + trace[0].file, "WARNING");
	// this.echo("line:     " + trace[0].line, "WARNING");
	// this.echo("function: " + trace[0]["function"], "WARNING");
});

casper.on('remote.message', function(message) {
	this.echo('remote console > ' + message);
});

casper.on('resource.received', function(resource) {
	var status = resource.status;
	if(status >= 400) {
		casper.log('remote error > ' + resource.url + ' failed to load (' + status + ')', 'error');
	}
});

function capturePageSelectors(url,scenarios,viewports,bitmaps_reference,bitmaps_test,isReference){

	var
		screenshotNow = new Date(),
		screenshotDateTime = screenshotNow.getFullYear() + pad(screenshotNow.getMonth() + 1) + pad(screenshotNow.getDate()) + '-' + pad(screenshotNow.getHours()) + pad(screenshotNow.getMinutes()) + pad(screenshotNow.getSeconds());

	var consoleBuffer = '';
	var scriptTimeout = 20000;

  // Keeps track of all the filenames.
  var scenarioRegistry = {};

	casper.on('remote.message', function(message) {
	    this.echo(message);
	    consoleBuffer = consoleBuffer + '\n' + message;
	});

	casper.start();
	// casper.viewport(1280,1024);

	casper.each(scenarios,function(casper, scenario, scenario_index){
    // If no label is present, default to normal labeling procedure.
    if ( !scenario.hasOwnProperty('label') ) {
      scenario.label = scenario_index;
    }
    // Replace certain special characters with underscores so
    // we don't end up creating subdirectories.
    scenario.label = scenario.label.replace(/[\/\.]/g, '_');

    // To guarantee file name uniqueness, we need to perform this check.
    // If the label is not unique, then we need to make it unique
    // and then register that label to the registry.
    if (scenarioRegistry[scenario.label]) {
      var i = 1;
      while (scenarioRegistry[scenario.label]) {
        scenario.label = scenario.label + i;
      }
    }
    scenarioRegistry[scenario.label] = true;

		casper.each(viewports, function(casper, vp, viewport_index) {
			this.then(function() {
				this.viewport(vp.width||vp.viewport.width, vp.height||vp.viewport.height);
			});
			this.thenOpen(scenario.url, function() {

				casper.waitFor(
					function(){ //test
						if(!scenario.readyEvent)return true;
						var regExReadyFlag = new RegExp(scenario.readyEvent,'i');
						return consoleBuffer.search(regExReadyFlag)>=0;
					}
					,function(){//on done
						consoleBuffer = '';
						casper.echo('Ready event received.');
					}
					,function(){casper.echo('ERROR: casper timeout.')} //on timeout
					,scriptTimeout
				);
				casper.wait(scenario.delay||1);

			});
			casper.then(function() {
				this.echo('Current location is ' + scenario.url, 'info');

				//var src = this.evaluate(function() {return document.body.outerHTML; });
				//this.echo(src);
			});

			this.then(function(){

				this.echo('Screenshots for ' + vp.name + ' (' + (vp.width||vp.viewport.width) + 'x' + (vp.height||vp.viewport.height) + ')', 'info');

				//HIDE SELECTORS WE WANT TO AVOID
                if ( scenario.hasOwnProperty('hideSelectors') ) {
          				scenario.hideSelectors.forEach(function(o,i,a){
          					casper.evaluate(function(o){
          						Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
          							s.style.visibility='hidden';
          						});
          					},o);
          				});
                }

				//REMOVE UNWANTED SELECTORS FROM RENDER TREE
                if ( scenario.hasOwnProperty('removeSelectors') ) {
                    // this.echo('removeSelectors');
          				scenario.removeSelectors.forEach(function(o,i,a){
                            this.echo('evaluate' + o);
          					casper.evaluate(function(o){
          						Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
          							s.style.display='none';
          						});
          					},o);
          				});
                }
                // this.echo('removeSelectors after');

				//CREATE SCREEN SHOTS AND TEST COMPARE CONFIGURATION (CONFIG FILE WILL BE SAVED WHEN THIS PROCESS RETURNS)
                // If no selectors are provided then set the default 'body'
                if ( !scenario.hasOwnProperty('selectors') ) {
                  scenario.selectors = [ 'body' ];
                }

                var that = this;

				scenario.selectors.forEach(function(o,i,a){
                    // that.echo('something is fishy');
                    //remove anything that's not a letter or a number
					var cleanedSelectorName = o.replace(/[\]\[=]/g, '--').replace(/[^a-zA-Z\d-_]/g,'');
                    //var cleanedUrl = scenario.url.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number

                    var fileName = scenario.label + '_' + i + '_' + cleanedSelectorName + '_' + viewport_index + '_' + vp.name + '.png';

					var reference_FP 	= bitmaps_reference + '/' + fileName;
                    var reference_tmp_FP = bitmaps_reference + '/.tmp/' + fileName;
					var test_FP 			= bitmaps_test + '/' + screenshotDateTime + '/' + fileName;

					var filePath 			= (isReference)?reference_FP:test_FP;

				    if(!isReference) {
						compareConfig.testPairs.push({
							reference:reference_FP,
							test:test_FP,
							selector:o,
							fileName:fileName,
							label:scenario.label
						});
                    }

                    // Check for this is options.sync is passed in, and copy over
                    // the file from .tmp if it exists there. Otherwise, create
                    // the file. We need to look up the image based on the selector name
                    // and label.
                    if (!options.baseline && options.sync && fs.exists(reference_tmp_FP) && !fs.exists(reference_FP)) {
                        // If we don't catch the exception here, it will cause the backstop process
                        // to just hang-- which sucks.
                        try {
                            fs.move(reference_tmp_FP, reference_FP);
                        } catch(e) {
                            that.echo(e);
                        }
                    } else {
                        // that.echo('capture');
                        casper.captureSelector(filePath, o);
                    }

					//casper.echo('remote capture to > '+filePath,'info');

				});//end topLevelModules.forEach

			});

		});//end casper.each viewports

	});//end casper.each scenario

}

//========================
//this query should be moved to the prior process
//`isReference` could be better passed as env parameter
var exists = fs.exists(bitmaps_reference);
var isReference = false;
if(!exists || options.genReferenceMode){isReference=true; console.log('CREATING NEW REFERENCE FILES')}
//========================

capturePageSelectors(
	'index.html'
	,scenarios
	,viewports
	,bitmaps_reference
	,bitmaps_test
	,isReference
);

casper.run(function(){
	complete();
	this.exit();
});

function complete(){
	var configData = JSON.stringify(compareConfig,null,2);
	fs.write(compareConfigFileName, configData, 'w');
	console.log(
		'Comparison config file updated.'
		//,configData
	);
}

function pad(number) {
	var r = String(number);
	if ( r.length === 1 ) {
		r = '0' + r;
	}
	return r;
}
<commit_msg>Add blacklist feature to block certain requests with casperjs.<commit_after>// The fs here actually refers to http://phantomjs.org/api/fs/
var fs = require('fs');
var utils = require('utils');

var bitmaps_reference = 'bitmaps_reference';
var bitmaps_test = 'bitmaps_test';
var compareConfigFileName = 'compare/config.json';
var genConfigPath = 'capture/config.json';

var configJSON = fs.read(genConfigPath);
var config = JSON.parse(configJSON);

var viewports = config.viewports;
var scenarios = config.scenarios||config.grabConfigs;

var compareConfig = {testPairs:[]};

var casper = require("casper").create({
	// clientScripts: ["jquery.js"] //lets try not to use this it's friggin 2014 already people...
});
var options = casper.cli.options;

casper.on('resource.received', function(resource) {
		//casper.echo(resource.url);
});

casper.on("page.error", function(msg, trace) {
	// this.echo("Remote Error >    " + msg, "error");
	// this.echo("file:     " + trace[0].file, "WARNING");
	// this.echo("line:     " + trace[0].line, "WARNING");
	// this.echo("function: " + trace[0]["function"], "WARNING");
});

casper.on('remote.message', function(message) {
	this.echo('remote console > ' + message);
});

casper.on('resource.received', function(resource) {
	var status = resource.status;
	if(status >= 400) {
		casper.log('remote error > ' + resource.url + ' failed to load (' + status + ')', 'error');
	}
});

// Keep a list of blacklist functions to run against certain types.
var _isBlacklisted = function(requestData, request, regex) {
    return (regex.test(requestData.url));
}

// Our list of types to blacklist functions.
var _requestHash = {
    'default': _isBlacklisted
}

// Block certain requests.
casper.on('resource.requested', function(requestData, request) {
    // Look for certain patterns to determine if they are of that type.
    var blacklistedRequests = [
        // {"type": "css", "regex": "https?:\/\/.+?\\.css", "regexFlags": "gi"},
        {"type": "ad",  "regex": "https?:\/\/.*?(ad\\.doubleclick\\.net)|(adserver)|(doubleclick)|(googleads).*", "regexFlags": "gi"}
    //     {"type": "social", "regex": "https?:\/\/.*?(tpc\\.googlesyndication\\.com)|((s-static|static)\\.ak\\.facebook\\.com\/connect)|(facebook\\.com).*", "regexFlags": "gi"},
    //     {"type": "youtube", "regex": "https?:\/\/.*?(youtube\\.com).*", "regexFlags": "gi"}
    ];

    var abort = false,
        i = 0;

    while(i < blacklistedRequests.length) {
        var obj = blacklistedRequests[i],
            type = obj.type;
            regex = new RegExp(obj.regex, obj.regexFlags);

        if (typeof _requestHash[type] === 'undefined') {
            type = 'default';
        }

        try {
            abort = _requestHash[type](requestData, request, regex);
        } catch(e) {
            casper.echo(e);
        }
        if (abort) {
            casper.echo('Aborting request: ' + requestData.url);
            request.abort();
            break;
        }

        i++;
    }
});

function capturePageSelectors(url,scenarios,viewports,bitmaps_reference,bitmaps_test,isReference){

	var
		screenshotNow = new Date(),
		screenshotDateTime = screenshotNow.getFullYear() + pad(screenshotNow.getMonth() + 1) + pad(screenshotNow.getDate()) + '-' + pad(screenshotNow.getHours()) + pad(screenshotNow.getMinutes()) + pad(screenshotNow.getSeconds());

	var consoleBuffer = '';
	var scriptTimeout = 20000;

  // Keeps track of all the filenames.
  var scenarioRegistry = {};

	casper.on('remote.message', function(message) {
	    this.echo(message);
	    consoleBuffer = consoleBuffer + '\n' + message;
	});

	casper.start();
	// casper.viewport(1280,1024);

	casper.each(scenarios,function(casper, scenario, scenario_index){
    // If no label is present, default to normal labeling procedure.
    if ( !scenario.hasOwnProperty('label') ) {
      scenario.label = scenario_index;
    }
    // Replace certain special characters with underscores so
    // we don't end up creating subdirectories.
    scenario.label = scenario.label.replace(/[\/\.]/g, '_');

    // To guarantee file name uniqueness, we need to perform this check.
    // If the label is not unique, then we need to make it unique
    // and then register that label to the registry.
    if (scenarioRegistry[scenario.label]) {
      var i = 1;
      while (scenarioRegistry[scenario.label]) {
        scenario.label = scenario.label + i;
      }
    }
    scenarioRegistry[scenario.label] = true;

		casper.each(viewports, function(casper, vp, viewport_index) {
			this.then(function() {
				this.viewport(vp.width||vp.viewport.width, vp.height||vp.viewport.height);
			});

			this.thenOpen(scenario.url, function() {

				casper.waitFor(
					function(){ //test
						if(!scenario.readyEvent)return true;
						var regExReadyFlag = new RegExp(scenario.readyEvent,'i');
						return consoleBuffer.search(regExReadyFlag)>=0;
					}
					,function(){//on done
						consoleBuffer = '';
						casper.echo('Ready event received.');
					}
					,function(){casper.echo('ERROR: casper timeout.')} //on timeout
					,scriptTimeout
				);
				casper.wait(scenario.delay||1);

			});
			casper.then(function() {
				this.echo('Current location is ' + scenario.url, 'info');

				//var src = this.evaluate(function() {return document.body.outerHTML; });
				//this.echo(src);
			});

			this.then(function(){

				this.echo('Screenshots for ' + vp.name + ' (' + (vp.width||vp.viewport.width) + 'x' + (vp.height||vp.viewport.height) + ')', 'info');

				//HIDE SELECTORS WE WANT TO AVOID
                if ( scenario.hasOwnProperty('hideSelectors') ) {
          				scenario.hideSelectors.forEach(function(o,i,a){
          					casper.evaluate(function(o){
          						Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
          							s.style.visibility='hidden';
          						});
          					},o);
          				});
                }

				//REMOVE UNWANTED SELECTORS FROM RENDER TREE
                if ( scenario.hasOwnProperty('removeSelectors') ) {
          				scenario.removeSelectors.forEach(function(o,i,a){
          					casper.evaluate(function(o){
          						Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
          							s.style.display='none';
          						});
          					},o);
          				});
                }
                // this.echo('removeSelectors after');

				//CREATE SCREEN SHOTS AND TEST COMPARE CONFIGURATION (CONFIG FILE WILL BE SAVED WHEN THIS PROCESS RETURNS)
                // If no selectors are provided then set the default 'body'
                if ( !scenario.hasOwnProperty('selectors') ) {
                  scenario.selectors = [ 'body' ];
                }

                var that = this;

				scenario.selectors.forEach(function(o,i,a){
                    // that.echo('something is fishy');
                    //remove anything that's not a letter or a number
					var cleanedSelectorName = o.replace(/[\]\[=]/g, '--').replace(/[^a-zA-Z\d-_]/g,'');
                    //var cleanedUrl = scenario.url.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number

                    var fileName = scenario.label + '_' + i + '_' + cleanedSelectorName + '_' + viewport_index + '_' + vp.name + '.png';

					var reference_FP 	= bitmaps_reference + '/' + fileName;
                    var reference_tmp_FP = bitmaps_reference + '/.tmp/' + fileName;
					var test_FP 			= bitmaps_test + '/' + screenshotDateTime + '/' + fileName;

					var filePath 			= (isReference)?reference_FP:test_FP;

				    if(!isReference) {
						compareConfig.testPairs.push({
							reference:reference_FP,
							test:test_FP,
							selector:o,
							fileName:fileName,
							label:scenario.label
						});
                    }

                    // Check for this is options.sync is passed in, and copy over
                    // the file from .tmp if it exists there. Otherwise, create
                    // the file. We need to look up the image based on the selector name
                    // and label.
                    if (!options.baseline && options.sync && fs.exists(reference_tmp_FP) && !fs.exists(reference_FP)) {
                        // If we don't catch the exception here, it will cause the backstop process
                        // to just hang-- which sucks.
                        try {
                            fs.move(reference_tmp_FP, reference_FP);
                        } catch(e) {
                            that.echo(e);
                        }
                    } else {
                        // that.echo('capture');
                        casper.captureSelector(filePath, o);
                    }

					//casper.echo('remote capture to > '+filePath,'info');

				});//end topLevelModules.forEach

			});

		});//end casper.each viewports

	});//end casper.each scenario

}

//========================
//this query should be moved to the prior process
//`isReference` could be better passed as env parameter
var exists = fs.exists(bitmaps_reference);
var isReference = false;
if(!exists || options.genReferenceMode){isReference=true; console.log('CREATING NEW REFERENCE FILES')}
//========================

capturePageSelectors(
	'index.html'
	,scenarios
	,viewports
	,bitmaps_reference
	,bitmaps_test
	,isReference
);

casper.run(function(){
	complete();
	this.exit();
});

function complete(){
	var configData = JSON.stringify(compareConfig,null,2);
	fs.write(compareConfigFileName, configData, 'w');
	console.log(
		'Comparison config file updated.'
		//,configData
	);
}

function pad(number) {
	var r = String(number);
	if ( r.length === 1 ) {
		r = '0' + r;
	}
	return r;
}
<|endoftext|>


<commit_before>
var fs = require('fs');

var selectorNotFoundPath = 'capture/resources/selectorNotFound_noun_164558_cc.png'
var hiddenSelectorPath = 'capture/resources/hiddenSelector_noun_63405.png'
var genConfigPath = 'capture/config.json'

var configJSON = fs.read(genConfigPath);
var config = JSON.parse(configJSON);
if (!config.paths) {
  config.paths = {};
}

var bitmaps_reference = config.paths.bitmaps_reference || 'bitmaps_reference';
var bitmaps_test = config.paths.bitmaps_test || 'bitmaps_test';
var casper_scripts = config.paths.casper_scripts || null;
var compareConfigFileName = config.paths.compare_data || 'compare/config.json';
var viewports = config.viewports;
var scenarios = config.scenarios||config.grabConfigs;

var compareConfig = {testPairs:[]};

var casper = require("casper").create({
  // clientScripts: ["jquery.js"] // uncomment to add jQuery if you need that.
});

if (config.debug) {
  console.log('Debug is enabled!');

  casper.on("page.error", function(msg, trace) {
      this.echo("Remote Error >    " + msg, "error");
      this.echo("file:     " + trace[0].file, "WARNING");
      this.echo("line:     " + trace[0].line, "WARNING");
      this.echo("function: " + trace[0]["function"], "WARNING");
  });
}

casper.on('remote.message', function(message) {
  this.echo('remote console > ' + message);
});

casper.on('resource.received', function(resource) {
  var status = resource.status;
  if(status >= 400) {
    casper.log('remote error > ' + resource.url + ' failed to load (' + status + ')', 'error');
  }
});

function capturePageSelectors(url,scenarios,viewports,bitmaps_reference,bitmaps_test,isReference){

  var
    screenshotNow = new Date(),
    screenshotDateTime = screenshotNow.getFullYear() + pad(screenshotNow.getMonth() + 1) + pad(screenshotNow.getDate()) + '-' + pad(screenshotNow.getHours()) + pad(screenshotNow.getMinutes()) + pad(screenshotNow.getSeconds());

  var consoleBuffer = '';
  var scriptTimeout = 20000;

  casper.on('remote.message', function(message) {
      this.echo(message);
      consoleBuffer = consoleBuffer + '\n' + message;
  });

  casper.start();

  casper.each(scenarios,function(casper, scenario, scenario_index){

    if (scenario.cookiesJsonFile && fs.isFile(scenario.cookiesJsonFile)) {
      var cookiesJson = fs.read(scenario.cookiesJsonFile);
      var cookies = JSON.parse(cookiesJson);
      for (var i = 0; i < cookies.length; i++) {
        phantom.addCookie(cookies[i]);
      }
    }

    casper.each(viewports, function(casper, vp, viewport_index) {
      this.then(function() {
        this.viewport(vp.width||vp.viewport.width, vp.height||vp.viewport.height);
      });
      this.thenOpen(scenario.url, function() {

        casper.waitFor(
          function(){ //test
            if(!scenario.readyEvent)return true;
            var regExReadyFlag = new RegExp(scenario.readyEvent,'i');
            return consoleBuffer.search(regExReadyFlag)>=0;
          }
          ,function(){//on done
            consoleBuffer = '';
            casper.echo('Ready event received.');
          }
          ,function(){casper.echo('ERROR: casper timeout.')} //on timeout
          ,scriptTimeout
        );
        casper.wait(scenario.delay||1);

      });

      casper.then(function() {
        this.echo('Current location is ' + scenario.url, 'info');

        if (config.debug) {
          var src = this.evaluate(function() {return document.body.outerHTML; });
          this.echo(src);
        }
      });

      // Custom casperjs scripting after ready event and delay
      casper.then(function() {

        // onReadyScript files should export a module like so:
        //
        // module.exports = function(casper, scenario) {
        //   // run custom casperjs code
        // };
        //
        if ( scenario.onReadyScript ) {

          casper.echo('Running custom scripts.');

          // Ensure a `.js` file suffix
          var script_path = scenario.onReadyScript.replace(/\.js$/, '') + '.js';
          // if a casper_scripts path exists, append the onReadyScript soft-enforcing a single slash between them.
          if ( casper_scripts ) {
            script_path = casper_scripts.replace(/\/$/, '') + '/' + script_path.replace(/^\//, '');
          }

          // make sure it's there...
          if ( !fs.isFile( script_path ) ) {
            casper.echo('Warning: onReadyScript path is invalid. File will be ignored.');
            return;
          }

          // the require() call below is relative to this file `genBitmaps.js` (not CWD) -- therefore relative paths need shimmimg
          var require_script_path = script_path.replace(/^\.\.\//, '../../../').replace(/^\.\//, '../../');

          require(require_script_path)(casper, scenario);

        }
      });

      this.then(function(){

        this.echo('Screenshots for ' + vp.name + ' (' + (vp.width||vp.viewport.width) + 'x' + (vp.height||vp.viewport.height) + ')', 'info');

        //HIDE SELECTORS WE WANT TO AVOID
            if ( scenario.hasOwnProperty('hideSelectors') ) {
              scenario.hideSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.visibility='hidden';
                  });
                },o);
              });
            }

        //REMOVE UNWANTED SELECTORS FROM RENDER TREE
            if ( scenario.hasOwnProperty('removeSelectors') ) {
              scenario.removeSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.display='none';
                  });
                },o);
              });
            }

        //CREATE SCREEN SHOTS AND TEST COMPARE CONFIGURATION (CONFIG FILE WILL BE SAVED WHEN THIS PROCESS RETURNS)
            // If no selectors are provided then set the default 'body'
            if ( !scenario.hasOwnProperty('selectors') ) {
              scenario.selectors = [ 'body' ];
            }
        scenario.selectors.forEach(function(o,i,a){
          var cleanedSelectorName = o.replace(/[^a-z0-9_\-]/gi,'');//remove anything that's not a letter or a number
          //var cleanedUrl = scenario.url.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number
          var fileName = scenario_index + '_' + i + '_' + cleanedSelectorName + '_' + viewport_index + '_' + vp.name + '.png';;

          var reference_FP  = bitmaps_reference + '/' + fileName;
          var test_FP       = bitmaps_test + '/' + screenshotDateTime + '/' + fileName;

          var filePath      = (isReference)?reference_FP:test_FP;

          if (casper.exists(o)) {
            if (casper.visible(o)) {
              casper.captureSelector(filePath, o);
            } else {
              var assetData = fs.read(hiddenSelectorPath, 'b');
              fs.write(filePath, assetData, 'b');
            }
          } else {
            var assetData = fs.read(selectorNotFoundPath, 'b');
            fs.write(filePath, assetData, 'b');
          }

          if (!isReference) {
            compareConfig.testPairs.push({
              reference:reference_FP,
              test:test_FP,
              selector:o,
              fileName:fileName,
              label:scenario.label,
              misMatchThreshold: scenario.misMatchThreshold
            });
          }
          //casper.echo('remote capture to > '+filePath,'info');

        });//end topLevelModules.forEach

      });

    });//end casper.each viewports

  });//end casper.each scenario

}

//========================
//this query should be moved to the prior process
//`isReference` could be better passed as env parameter
var exists = fs.exists(bitmaps_reference);
var isReference = false;
if(!exists){isReference=true; console.log('CREATING NEW REFERENCE FILES')}
//========================

capturePageSelectors(
  'index.html'
  ,scenarios
  ,viewports
  ,bitmaps_reference
  ,bitmaps_test
  ,isReference
);

casper.run(function(){
  complete();
  this.exit();
});

function complete(){
  var configData = JSON.stringify(compareConfig,null,2);
  fs.touch(compareConfigFileName);
  fs.write(compareConfigFileName, configData, 'w');
  console.log(
    'Comparison config file updated.'
    //,configData
  );
}

function pad(number) {
  var r = String(number);
  if ( r.length === 1 ) {
    r = '0' + r;
  }
  return r;
}
<commit_msg>updated onReadyScript not found<commit_after>
var fs = require('fs');

var selectorNotFoundPath = 'capture/resources/selectorNotFound_noun_164558_cc.png'
var hiddenSelectorPath = 'capture/resources/hiddenSelector_noun_63405.png'
var genConfigPath = 'capture/config.json'

var configJSON = fs.read(genConfigPath);
var config = JSON.parse(configJSON);
if (!config.paths) {
  config.paths = {};
}

var bitmaps_reference = config.paths.bitmaps_reference || 'bitmaps_reference';
var bitmaps_test = config.paths.bitmaps_test || 'bitmaps_test';
var casper_scripts = config.paths.casper_scripts || null;
var compareConfigFileName = config.paths.compare_data || 'compare/config.json';
var viewports = config.viewports;
var scenarios = config.scenarios||config.grabConfigs;

var compareConfig = {testPairs:[]};

var casper = require("casper").create({
  // clientScripts: ["jquery.js"] // uncomment to add jQuery if you need that.
});

if (config.debug) {
  console.log('Debug is enabled!');

  casper.on("page.error", function(msg, trace) {
      this.echo("Remote Error >    " + msg, "error");
      this.echo("file:     " + trace[0].file, "WARNING");
      this.echo("line:     " + trace[0].line, "WARNING");
      this.echo("function: " + trace[0]["function"], "WARNING");
  });
}

casper.on('remote.message', function(message) {
  this.echo('remote console > ' + message);
});

casper.on('resource.received', function(resource) {
  var status = resource.status;
  if(status >= 400) {
    casper.log('remote error > ' + resource.url + ' failed to load (' + status + ')', 'error');
  }
});

function capturePageSelectors(url,scenarios,viewports,bitmaps_reference,bitmaps_test,isReference){

  var
    screenshotNow = new Date(),
    screenshotDateTime = screenshotNow.getFullYear() + pad(screenshotNow.getMonth() + 1) + pad(screenshotNow.getDate()) + '-' + pad(screenshotNow.getHours()) + pad(screenshotNow.getMinutes()) + pad(screenshotNow.getSeconds());

  var consoleBuffer = '';
  var scriptTimeout = 20000;

  casper.on('remote.message', function(message) {
      this.echo(message);
      consoleBuffer = consoleBuffer + '\n' + message;
  });

  casper.start();

  casper.each(scenarios,function(casper, scenario, scenario_index){

    if (scenario.cookiesJsonFile && fs.isFile(scenario.cookiesJsonFile)) {
      var cookiesJson = fs.read(scenario.cookiesJsonFile);
      var cookies = JSON.parse(cookiesJson);
      for (var i = 0; i < cookies.length; i++) {
        phantom.addCookie(cookies[i]);
      }
    }

    casper.each(viewports, function(casper, vp, viewport_index) {
      this.then(function() {
        this.viewport(vp.width||vp.viewport.width, vp.height||vp.viewport.height);
      });
      this.thenOpen(scenario.url, function() {

        casper.waitFor(
          function(){ //test
            if(!scenario.readyEvent)return true;
            var regExReadyFlag = new RegExp(scenario.readyEvent,'i');
            return consoleBuffer.search(regExReadyFlag)>=0;
          }
          ,function(){//on done
            consoleBuffer = '';
            casper.echo('Ready event received.');
          }
          ,function(){casper.echo('ERROR: casper timeout.')} //on timeout
          ,scriptTimeout
        );
        casper.wait(scenario.delay||1);

      });

      casper.then(function() {
        this.echo('Current location is ' + scenario.url, 'info');

        if (config.debug) {
          var src = this.evaluate(function() {return document.body.outerHTML; });
          this.echo(src);
        }
      });

      // Custom casperjs scripting after ready event and delay
      casper.then(function() {

        // onReadyScript files should export a module like so:
        //
        // module.exports = function(casper, scenario) {
        //   // run custom casperjs code
        // };
        //
        if ( scenario.onReadyScript ) {

          casper.echo('Running custom scripts.');

          // Ensure a `.js` file suffix
          var script_path = scenario.onReadyScript.replace(/\.js$/, '') + '.js';
          // if a casper_scripts path exists, append the onReadyScript soft-enforcing a single slash between them.
          if ( casper_scripts ) {
            script_path = casper_scripts.replace(/\/$/, '') + '/' + script_path.replace(/^\//, '');
          }

          // make sure it's there...
          if ( !fs.isFile( script_path ) ) {
            casper.echo("FYI: onReadyScript was not found.");
            return;
          }

          // the require() call below is relative to this file `genBitmaps.js` (not CWD) -- therefore relative paths need shimmimg
          var require_script_path = script_path.replace(/^\.\.\//, '../../../').replace(/^\.\//, '../../');

          require(require_script_path)(casper, scenario);

        }
      });

      this.then(function(){

        this.echo('Screenshots for ' + vp.name + ' (' + (vp.width||vp.viewport.width) + 'x' + (vp.height||vp.viewport.height) + ')', 'info');

        //HIDE SELECTORS WE WANT TO AVOID
            if ( scenario.hasOwnProperty('hideSelectors') ) {
              scenario.hideSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.visibility='hidden';
                  });
                },o);
              });
            }

        //REMOVE UNWANTED SELECTORS FROM RENDER TREE
            if ( scenario.hasOwnProperty('removeSelectors') ) {
              scenario.removeSelectors.forEach(function(o,i,a){
                casper.evaluate(function(o){
                  Array.prototype.forEach.call(document.querySelectorAll(o), function(s, j){
                    s.style.display='none';
                  });
                },o);
              });
            }

        //CREATE SCREEN SHOTS AND TEST COMPARE CONFIGURATION (CONFIG FILE WILL BE SAVED WHEN THIS PROCESS RETURNS)
            // If no selectors are provided then set the default 'body'
            if ( !scenario.hasOwnProperty('selectors') ) {
              scenario.selectors = [ 'body' ];
            }
        scenario.selectors.forEach(function(o,i,a){
          var cleanedSelectorName = o.replace(/[^a-z0-9_\-]/gi,'');//remove anything that's not a letter or a number
          //var cleanedUrl = scenario.url.replace(/[^a-zA-Z\d]/,'');//remove anything that's not a letter or a number
          var fileName = scenario_index + '_' + i + '_' + cleanedSelectorName + '_' + viewport_index + '_' + vp.name + '.png';;

          var reference_FP  = bitmaps_reference + '/' + fileName;
          var test_FP       = bitmaps_test + '/' + screenshotDateTime + '/' + fileName;

          var filePath      = (isReference)?reference_FP:test_FP;

          if (casper.exists(o)) {
            if (casper.visible(o)) {
              casper.captureSelector(filePath, o);
            } else {
              var assetData = fs.read(hiddenSelectorPath, 'b');
              fs.write(filePath, assetData, 'b');
            }
          } else {
            var assetData = fs.read(selectorNotFoundPath, 'b');
            fs.write(filePath, assetData, 'b');
          }

          if (!isReference) {
            compareConfig.testPairs.push({
              reference:reference_FP,
              test:test_FP,
              selector:o,
              fileName:fileName,
              label:scenario.label,
              misMatchThreshold: scenario.misMatchThreshold
            });
          }
          //casper.echo('remote capture to > '+filePath,'info');

        });//end topLevelModules.forEach

      });

    });//end casper.each viewports

  });//end casper.each scenario

}

//========================
//this query should be moved to the prior process
//`isReference` could be better passed as env parameter
var exists = fs.exists(bitmaps_reference);
var isReference = false;
if(!exists){isReference=true; console.log('CREATING NEW REFERENCE FILES')}
//========================

capturePageSelectors(
  'index.html'
  ,scenarios
  ,viewports
  ,bitmaps_reference
  ,bitmaps_test
  ,isReference
);

casper.run(function(){
  complete();
  this.exit();
});

function complete(){
  var configData = JSON.stringify(compareConfig,null,2);
  fs.touch(compareConfigFileName);
  fs.write(compareConfigFileName, configData, 'w');
  console.log(
    'Comparison config file updated.'
    //,configData
  );
}

function pad(number) {
  var r = String(number);
  if ( r.length === 1 ) {
    r = '0' + r;
  }
  return r;
}
<|endoftext|>


<commit_before>import BaseModule from '../base'
import { isEmpty } from 'lodash'
import { NETWORKS } from '@config'
import NetworkModel from '@/models/network'
import Client from '@/services/client'
import Vue from 'vue'

export default new BaseModule(NetworkModel, {

  state: () => ({
    all: [],
    customNetworks: {}
  }),

  getters: {
    all: (state, getters) => {
      return [...state.all, ...getters.customNetworksArray]
    },
    bySymbol: state => symbol => {
      return state.all.find(network => network.symbol === symbol)
    },
    byToken: state => token => {
      return state.all.find(network => network.token === token)
    },

    feeStatisticsByType: (_, __, ___, rootGetters) => type => {
      const network = rootGetters['session/network']

      if (!network) {
        throw new Error('[network/feeStatisticsByType] No active network.')
      }

      if (network.apiVersion === 1) {
        throw new Error('[network/feeStatisticsByType] Supported only by v2 networks.')
      }

      const { feeStatistics } = network
      const data = feeStatistics.find(transactionType => transactionType.type === type)
      return data ? data.fees : []
    },

    customNetworkById: state => id => {
      return state.customNetworks[id]
    },

    customNetworks: state => state.customNetworks,

    customNetworksArray: state => Object.values(state.customNetworks)
  },

  mutations: {
    SET_ALL (state, value) {
      state.all = value
    },
    ADD_CUSTOM_NETWORK (state, value) {
      Vue.set(state.customNetworks, value.id, value)
    },
    UPDATE_CUSTOM_NETWORK (state, value) {
      if (state.customNetworks[value.id]) {
        Vue.set(state.customNetworks, value.id, value)
      }
    },
    REMOVE_CUSTOM_NETWORK (state, value) {
      Vue.delete(state.customNetworks, value)
    }
  },

  actions: {
    load ({ commit, getters }) {
      if (!isEmpty(getters['network/all'])) return

      commit('SET_ALL', NETWORKS)
    },

    // TODO: look into where this is used, as it might need to be changed to getters[network/update] instead
    async updateNetworkConfig ({ dispatch, getters, _, rootGetters }, networkId) {
      var network = getters['byId'](networkId)
      if (!network) {
        network = rootGetters['network/customNetworkById'](networkId)
      }
      const response = await Client.fetchNetworkConfig(network.server, network.apiVersion)

      if (response) {
        try {
          const result = dispatch('update', {
            ...network,
            ...response
          })
          return result
        } catch (error) {
          // Network did not exist yet, so create it
          const result = dispatch('create', {
            ...network,
            ...response
          })
          return result
        }
      }
    },

    addCustomNetwork ({ dispatch, commit }, network) {
      commit('ADD_CUSTOM_NETWORK', network)
      dispatch('create', network)
    },

    updateCustomNetwork ({ dispatch, commit }, network) {
      commit('UPDATE_CUSTOM_NETWORK', network)
      dispatch('update', network)
    },

    removeCustomNetwork ({ dispatch, commit }, id) {
      commit('REMOVE_CUSTOM_NETWORK', id)
      dispatch('delete', { id })
    }
  }
})
<commit_msg>revert: fix catering for old data/code (#762)<commit_after>import BaseModule from '../base'
import { isEmpty } from 'lodash'
import { NETWORKS } from '@config'
import NetworkModel from '@/models/network'
import Client from '@/services/client'
import Vue from 'vue'

export default new BaseModule(NetworkModel, {

  state: () => ({
    all: [],
    customNetworks: {}
  }),

  getters: {
    bySymbol: state => symbol => {
      return state.all.find(network => network.symbol === symbol)
    },
    byToken: state => token => {
      return state.all.find(network => network.token === token)
    },

    feeStatisticsByType: (_, __, ___, rootGetters) => type => {
      const network = rootGetters['session/network']

      if (!network) {
        throw new Error('[network/feeStatisticsByType] No active network.')
      }

      if (network.apiVersion === 1) {
        throw new Error('[network/feeStatisticsByType] Supported only by v2 networks.')
      }

      const { feeStatistics } = network
      const data = feeStatistics.find(transactionType => transactionType.type === type)
      return data ? data.fees : []
    },

    customNetworkById: state => id => {
      return state.customNetworks[id]
    },

    customNetworks: state => state.customNetworks
  },

  mutations: {
    SET_ALL (state, value) {
      state.all = value
    },
    ADD_CUSTOM_NETWORK (state, value) {
      Vue.set(state.customNetworks, value.id, value)
    },
    UPDATE_CUSTOM_NETWORK (state, value) {
      if (state.customNetworks[value.id]) {
        Vue.set(state.customNetworks, value.id, value)
      }
    },
    REMOVE_CUSTOM_NETWORK (state, value) {
      Vue.delete(state.customNetworks, value)
    }
  },

  actions: {
    load ({ commit, getters }) {
      if (!isEmpty(getters['network/all'])) return

      commit('SET_ALL', NETWORKS)
    },

    // TODO: look into where this is used, as it might need to be changed to getters[network/update] instead
    async updateNetworkConfig ({ dispatch, getters, _, rootGetters }, networkId) {
      var network = getters['byId'](networkId)
      if (!network) {
        network = rootGetters['network/customNetworkById'](networkId)
      }
      const response = await Client.fetchNetworkConfig(network.server, network.apiVersion)

      if (response) {
        try {
          const result = dispatch('update', {
            ...network,
            ...response
          })
          return result
        } catch (error) {
          // Network did not exist yet, so create it
          const result = dispatch('create', {
            ...network,
            ...response
          })
          return result
        }
      }
    },

    addCustomNetwork ({ dispatch, commit }, network) {
      commit('ADD_CUSTOM_NETWORK', network)
      dispatch('create', network)
    },

    updateCustomNetwork ({ dispatch, commit }, network) {
      commit('UPDATE_CUSTOM_NETWORK', network)
      dispatch('update', network)
    },

    removeCustomNetwork ({ dispatch, commit }, id) {
      commit('REMOVE_CUSTOM_NETWORK', id)
      dispatch('delete', { id })
    }
  }
})
<|endoftext|>


<commit_before>/**
Definition of the HandlebarsGenerator class.
@license MIT. See LICENSE.md for details.
@module handlebars-generator.js
*/

(function() {

  var _ = require('underscore')
    , HANDLEBARS = require('handlebars')
    , FS = require('fs')
    , registerHelpers = require('../helpers/handlebars-helpers')
    , PATH = require('path')
    , parsePath = require('parse-filepath')
    , READFILES = require('recursive-readdir-sync')
    , SLASH = require('slash');

  /**
  Perform template-based resume generation using Handlebars.js.
  @class HandlebarsGenerator
  */
  var HandlebarsGenerator = module.exports = {

    generate: function( json, jst, format, cssInfo, opts, theme ) {

      registerPartials( format, theme );
      registerHelpers( theme );

      // Preprocess text
      var encData = json;
      ( format === 'html' || format === 'pdf' ) && (encData = json.markdownify());
      ( format === 'doc' ) && (encData = json.xmlify());

      // Compile and run the Handlebars template.
      var template = HANDLEBARS.compile(jst, { strict: false, assumeObjects: false });
      return template({
        r: encData,
        RAW: json,
        filt: opts.filters,
        cssInfo: cssInfo,
        format: format,
        opts: opts,
        headFragment: opts.headFragment || ''
      });

    }

  };

  function registerPartials(format, theme) {
    if( format === 'html' || format === 'doc' ) {

      // Locate the global partials folder
      var partialsFolder = PATH.join(
        parsePath( require.resolve('fresh-themes') ).dirname,
        '/partials/',
        format
      );

      // Register global partials in the /partials folder
      // TODO: Only do this once per HMR invocation.
      _.each( READFILES( partialsFolder, function(error){ }), function( el ) {
        var pathInfo = parsePath( el );
        var name = SLASH( PATH.relative( partialsFolder, el )
          .replace(/\.html$|\.xml$/, '') );
        if( pathInfo.dirname.endsWith('section') ) {
          name = SLASH(name.replace(/\.html$|\.xml$/, ''));
        }
        var tplData = FS.readFileSync( el, 'utf8' );
        var compiledTemplate = HANDLEBARS.compile( tplData );
        HANDLEBARS.registerPartial( name, compiledTemplate );
        theme.partialsInitialized = true;
      });

    }

    // Register theme-specific partials
    _.each( theme.partials, function( el ) {
      var tplData = FS.readFileSync( el.path, 'utf8' );
      var compiledTemplate = HANDLEBARS.compile( tplData );
      HANDLEBARS.registerPartial( el.name, compiledTemplate );
    });
  }

}());
<commit_msg>Remove redundant conditional.<commit_after>/**
Definition of the HandlebarsGenerator class.
@license MIT. See LICENSE.md for details.
@module handlebars-generator.js
*/

(function() {

  var _ = require('underscore')
    , HANDLEBARS = require('handlebars')
    , FS = require('fs')
    , registerHelpers = require('../helpers/handlebars-helpers')
    , PATH = require('path')
    , parsePath = require('parse-filepath')
    , READFILES = require('recursive-readdir-sync')
    , SLASH = require('slash');

  /**
  Perform template-based resume generation using Handlebars.js.
  @class HandlebarsGenerator
  */
  var HandlebarsGenerator = module.exports = {

    generate: function( json, jst, format, cssInfo, opts, theme ) {

      registerPartials( format, theme );
      registerHelpers( theme );

      // Preprocess text
      var encData = json;
      ( format === 'html' || format === 'pdf' ) && (encData = json.markdownify());
      ( format === 'doc' ) && (encData = json.xmlify());

      // Compile and run the Handlebars template.
      var template = HANDLEBARS.compile(jst, { strict: false, assumeObjects: false });
      return template({
        r: encData,
        RAW: json,
        filt: opts.filters,
        cssInfo: cssInfo,
        format: format,
        opts: opts,
        headFragment: opts.headFragment || ''
      });

    }

  };

  function registerPartials(format, theme) {
    if( format === 'html' || format === 'doc' ) {

      // Locate the global partials folder
      var partialsFolder = PATH.join(
        parsePath( require.resolve('fresh-themes') ).dirname,
        '/partials/',
        format
      );

      // Register global partials in the /partials/[format] folder
      // TODO: Only do this once per HMR invocation.
      _.each( READFILES( partialsFolder, function(error){ }), function( el ) {
        var pathInfo = parsePath( el );
        var name = SLASH( PATH.relative( partialsFolder, el )
          .replace(/\.html$|\.xml$/, '') );
        var tplData = FS.readFileSync( el, 'utf8' );
        var compiledTemplate = HANDLEBARS.compile( tplData );
        HANDLEBARS.registerPartial( name, compiledTemplate );
        theme.partialsInitialized = true;
      });

    }

    // Register theme-specific partials
    _.each( theme.partials, function( el ) {
      var tplData = FS.readFileSync( el.path, 'utf8' );
      var compiledTemplate = HANDLEBARS.compile( tplData );
      HANDLEBARS.registerPartial( el.name, compiledTemplate );
    });
  }

}());
<|endoftext|>


<commit_before>/**
 * Created by AlexanderC on 5/25/15.
 */

'use strict';

import BaseJoi from 'joi';
import Vogels from './vogelsPolyfill';
import uriRegexp from './uriRegexp';

/**
 * Abstraction on Joi validation expressions
 */
export class Joi {
  /**
   * @returns {Object}
   */
  static get uuid() {
    return Vogels.types.uuid();
  }

  /**
   * @returns {Object}
   */
  static get timeUUID() {
    return Vogels.types.timeUUID();
  }

  /**
   * @returns {Object}
   */
  static get stringSet() {
    return Vogels.types.stringSet();
  }

  /**
   * @returns {Object}
   */
  static get numberSet() {
    return Vogels.types.numberSet();
  }

  /**
   * @returns {Object}
   */
  static get binarySet() {
    return Vogels.types.binarySet();
  }

  /**
   * @returns {Object}
   */
  static get binary() {
    return BaseJoi.binary();
  }

  /**
   * @returns {Object}
   */
  static get number() {
    return BaseJoi.number();
  }

  /**
   * @returns {Object}
   */
  static get string() {
    return BaseJoi.string();
  }

  /**
   * @returns {Object}
   */
  static get boolean() {
    return BaseJoi.boolean();
  }

  /**
   * @returns {Object}
   */
  static get email() {
    return BaseJoi.string().email();
  }

  /**
   * @returns {Object}
   */
  static get website() {
    let strJoiObj = BaseJoi.string();

    return strJoiObj.hasOwnProperty('uri')
      ? strJoiObj.uri()
      : strJoiObj.regex(uriRegexp.createUriRegex());
  }

  /**
   * @returns {Object}
   */
  static get map() {
    return BaseJoi.object();
  }

  /**
   * @returns {*}
   */
  static get mapSet() {
    let array = BaseJoi.array();
    if (array.includes) {
      return array.includes(BaseJoi.object());
    }

    return array.items(BaseJoi.object());
  }
}
<commit_msg>Add comment<commit_after>/**
 * Created by AlexanderC on 5/25/15.
 */

'use strict';

import BaseJoi from 'joi';
import Vogels from './vogelsPolyfill';
import uriRegexp from './uriRegexp';

/**
 * Abstraction on Joi validation expressions
 */
export class Joi {
  /**
   * @returns {Object}
   */
  static get uuid() {
    return Vogels.types.uuid();
  }

  /**
   * @returns {Object}
   */
  static get timeUUID() {
    return Vogels.types.timeUUID();
  }

  /**
   * @returns {Object}
   */
  static get stringSet() {
    return Vogels.types.stringSet();
  }

  /**
   * @returns {Object}
   */
  static get numberSet() {
    return Vogels.types.numberSet();
  }

  /**
   * @returns {Object}
   */
  static get binarySet() {
    return Vogels.types.binarySet();
  }

  /**
   * @returns {Object}
   */
  static get binary() {
    return BaseJoi.binary();
  }

  /**
   * @returns {Object}
   */
  static get number() {
    return BaseJoi.number();
  }

  /**
   * @returns {Object}
   */
  static get string() {
    return BaseJoi.string();
  }

  /**
   * @returns {Object}
   */
  static get boolean() {
    return BaseJoi.boolean();
  }

  /**
   * @returns {Object}
   */
  static get email() {
    return BaseJoi.string().email();
  }

  /**
   * @returns {Object}
   */
  static get website() {
    let strJoiObj = BaseJoi.string();

    // Fix for Joi v5.x.x
    return strJoiObj.hasOwnProperty('uri')
      ? strJoiObj.uri()
      : strJoiObj.regex(uriRegexp.createUriRegex());
  }

  /**
   * @returns {Object}
   */
  static get map() {
    return BaseJoi.object();
  }

  /**
   * @returns {*}
   */
  static get mapSet() {
    let array = BaseJoi.array();
    if (array.includes) {
      return array.includes(BaseJoi.object());
    }

    return array.items(BaseJoi.object());
  }
}
<|endoftext|>