<commit_before>const io = require('socket.io-client')
const kbd = require('@dasilvacontin/keyboard')
const deepEqual = require('deep-equal')
const capitalize = require('capitalize')
const { PLAYER_EDGE, COIN_RADIUS } = require('../common/constants.js')
const { calculatePlayerAcceleration } = require('../common/utils.js')

const serverEventsNames = [
  'connect', 'gameInit', 'serverPong',
  'playerMoved', 'playerDisconnected',
  'coinSpawned', 'coinCollected'
]

class GameClient {
  constructor (roomId) {
    this.roomId = roomId
    this.socket = io()

    this.myPlayerId = null
    this.myInputs = {
      LEFT_ARROW: false,
      RIGHT_ARROW: false,
      UP_ARROW: false,
      DOWN_ARROW: false
    }

    this.players = {}
    this.coins = {}

    this.ping = Infinity
    this.clockDiff = 0

    serverEventsNames.forEach((serverEventName) => {
      this.socket.on(
        serverEventName,
        this[`on${capitalize(serverEventName)}`].bind(this)
      )
    })
  }

  pingServer () {
    this.pingMessageTimestamp = Date.now()
    this.socket.emit('gamePing')
  }

  onConnect () {
    this.socket.emit('joinGame', this.roomId)
    this.pingServer()
  }

  onServerPong (serverNow) {
    const now = Date.now()
    this.ping = (now - this.pingMessageTimestamp) / 2
    this.clockDiff = (serverNow + this.ping) - now
    setTimeout(() => {
      this.pingServer()
    }, Math.max(200, this.ping))
  }

  onGameInit (myPlayerId, gameState) {
    this.myPlayerId = myPlayerId
    const { players, coins } = gameState
    this.players = players
    this.coins = coins
  }

  onPlayerMoved (player) {
    this.players[player.id] = player
  }

  onCoinSpawned (coin) {
    this.coins[coin.id] = coin
  }

  onCoinCollected (playerId, coinId) {
    delete this.coins[coinId]
    const player = this.players[playerId]
    player.score++
  }

  onPlayerDisconnected (playerId) {
    delete this.players[playerId]
  }

  updateInputs () {
    const { myInputs } = this
    const oldInputs = Object.assign({}, myInputs)

    for (let key in myInputs) {
      myInputs[key] = kbd.isKeyDown(kbd[key])
    }

    if (!deepEqual(myInputs, oldInputs)) {
      this.socket.emit('move', myInputs)

      // update our local player' inputs aproximately when
      // the server takes them into account
      const frozenInputs = Object.assign({}, myInputs)
      setTimeout(() => {
        const myPlayer = this.players[this.myPlayerId]
        myPlayer.inputs = frozenInputs
        calculatePlayerAcceleration(myPlayer)
      }, this.ping)
    }
  }

  logic () {
    const now = Date.now()
    this.updateInputs()

    for (let playerId in this.players) {
      const player = this.players[playerId]
      const { x, y, vx, vy, ax, ay } = player

      const delta = (now + this.clockDiff) - player.timestamp
      const delta2 = Math.pow(delta, 2)

      // dead reckoning
      player.x = x + (vx * delta) + (ax * delta2 / 2)
      player.y = y + (vy * delta) + (ay * delta2 / 2)
      player.vx = vx + (ax * delta)
      player.vy = vy + (ay * delta)
      player.timestamp = now + this.clockDiff
    }
  }

  render (canvas, ctx) {
    ctx.fillStyle = 'white'
    ctx.fillRect(0, 0, canvas.width, canvas.height)

    // render coins
    for (let coinId in this.coins) {
      const coin = this.coins[coinId]
      ctx.fillStyle = 'yellow'
      ctx.beginPath()
      ctx.arc(coin.x, coin.y, COIN_RADIUS, 0, 2 * Math.PI)
      ctx.fill()
    }

    // render players
    for (let playerId in this.players) {
      const { color, x, y, score } = this.players[playerId]
      ctx.save()
      ctx.translate(x, y)
      ctx.fillStyle = color
      const HALF_EDGE = PLAYER_EDGE / 2
      ctx.fillRect(-HALF_EDGE, -HALF_EDGE, PLAYER_EDGE, PLAYER_EDGE)
      if (playerId === this.myPlayerId) {
        ctx.strokeRect(-HALF_EDGE, -HALF_EDGE, PLAYER_EDGE, PLAYER_EDGE)
      }

      // render score inside players
      ctx.fillStyle = 'white'
      ctx.textAlign = 'center'
      ctx.font = '20px Arial'
      ctx.fillText(score, 0, 7)
      ctx.restore()
    }

    // render `ping` and `clockDiff`
    ctx.fillStyle = 'black'
    ctx.textAlign = 'left'
    ctx.font = '20px Arial'
    ctx.fillText(`ping: ${this.ping}`, 15, 30)
    ctx.fillText(`clockDiff: ${this.clockDiff}`, 15, 60)
  }
}

module.exports = GameClient
<commit_msg>:bug: Fix player move event name not updated in client<commit_after>const io = require('socket.io-client')
const kbd = require('@dasilvacontin/keyboard')
const deepEqual = require('deep-equal')
const capitalize = require('capitalize')
const { PLAYER_EDGE, COIN_RADIUS } = require('../common/constants.js')
const { calculatePlayerAcceleration } = require('../common/utils.js')

const serverEventsNames = [
  'connect', 'gameInit', 'serverPong',
  'playerMoved', 'playerDisconnected',
  'coinSpawned', 'coinCollected'
]

class GameClient {
  constructor (roomId) {
    this.roomId = roomId
    this.socket = io()

    this.myPlayerId = null
    this.myInputs = {
      LEFT_ARROW: false,
      RIGHT_ARROW: false,
      UP_ARROW: false,
      DOWN_ARROW: false
    }

    this.players = {}
    this.coins = {}

    this.ping = Infinity
    this.clockDiff = 0

    serverEventsNames.forEach((serverEventName) => {
      this.socket.on(
        serverEventName,
        this[`on${capitalize(serverEventName)}`].bind(this)
      )
    })
  }

  pingServer () {
    this.pingMessageTimestamp = Date.now()
    this.socket.emit('gamePing')
  }

  onConnect () {
    this.socket.emit('joinGame', this.roomId)
    this.pingServer()
  }

  onServerPong (serverNow) {
    const now = Date.now()
    this.ping = (now - this.pingMessageTimestamp) / 2
    this.clockDiff = (serverNow + this.ping) - now
    setTimeout(() => {
      this.pingServer()
    }, Math.max(200, this.ping))
  }

  onGameInit (myPlayerId, gameState) {
    this.myPlayerId = myPlayerId
    const { players, coins } = gameState
    this.players = players
    this.coins = coins
  }

  onPlayerMoved (player) {
    this.players[player.id] = player
  }

  onCoinSpawned (coin) {
    this.coins[coin.id] = coin
  }

  onCoinCollected (playerId, coinId) {
    delete this.coins[coinId]
    const player = this.players[playerId]
    player.score++
  }

  onPlayerDisconnected (playerId) {
    delete this.players[playerId]
  }

  updateInputs () {
    const { myInputs } = this
    const oldInputs = Object.assign({}, myInputs)

    for (let key in myInputs) {
      myInputs[key] = kbd.isKeyDown(kbd[key])
    }

    if (!deepEqual(myInputs, oldInputs)) {
      this.socket.emit('playerMove', myInputs)

      // update our local player' inputs aproximately when
      // the server takes them into account
      const frozenInputs = Object.assign({}, myInputs)
      setTimeout(() => {
        const myPlayer = this.players[this.myPlayerId]
        myPlayer.inputs = frozenInputs
        calculatePlayerAcceleration(myPlayer)
      }, this.ping)
    }
  }

  logic () {
    const now = Date.now()
    this.updateInputs()

    for (let playerId in this.players) {
      const player = this.players[playerId]
      const { x, y, vx, vy, ax, ay } = player

      const delta = (now + this.clockDiff) - player.timestamp
      const delta2 = Math.pow(delta, 2)

      // dead reckoning
      player.x = x + (vx * delta) + (ax * delta2 / 2)
      player.y = y + (vy * delta) + (ay * delta2 / 2)
      player.vx = vx + (ax * delta)
      player.vy = vy + (ay * delta)
      player.timestamp = now + this.clockDiff
    }
  }

  render (canvas, ctx) {
    ctx.fillStyle = 'white'
    ctx.fillRect(0, 0, canvas.width, canvas.height)

    // render coins
    for (let coinId in this.coins) {
      const coin = this.coins[coinId]
      ctx.fillStyle = 'yellow'
      ctx.beginPath()
      ctx.arc(coin.x, coin.y, COIN_RADIUS, 0, 2 * Math.PI)
      ctx.fill()
    }

    // render players
    for (let playerId in this.players) {
      const { color, x, y, score } = this.players[playerId]
      ctx.save()
      ctx.translate(x, y)
      ctx.fillStyle = color
      const HALF_EDGE = PLAYER_EDGE / 2
      ctx.fillRect(-HALF_EDGE, -HALF_EDGE, PLAYER_EDGE, PLAYER_EDGE)
      if (playerId === this.myPlayerId) {
        ctx.strokeRect(-HALF_EDGE, -HALF_EDGE, PLAYER_EDGE, PLAYER_EDGE)
      }

      // render score inside players
      ctx.fillStyle = 'white'
      ctx.textAlign = 'center'
      ctx.font = '20px Arial'
      ctx.fillText(score, 0, 7)
      ctx.restore()
    }

    // render `ping` and `clockDiff`
    ctx.fillStyle = 'black'
    ctx.textAlign = 'left'
    ctx.font = '20px Arial'
    ctx.fillText(`ping: ${this.ping}`, 15, 30)
    ctx.fillText(`clockDiff: ${this.clockDiff}`, 15, 60)
  }
}

module.exports = GameClient
<|endoftext|>


<commit_before>import repeat from 'lodash/repeat';
import partialRight from 'lodash/partialRight';
import t from 'tcomb';

export default function(config, loadMessagesFor) {
  function longerStrings(data) {
    if (!t.Num.is(config.localStringLengthMultiplier) || config.localStringLengthMultiplier === 1) {
      return data;
    }

    const _repeat = partialRight(repeat, config.localStringLengthMultiplier);
    const _longerStrings = msgs => Object.keys(msgs).reduce((ac, k) => ({
      ...ac,
      [k]: t.Str.is(msgs[k]) ? _repeat(msgs[k]) : _longerStrings(msgs[k])
    }), {});

    return {
      ...data,
      messages: _longerStrings(data.messages)
    };
  }

  return function loadLocale(locale) {
    return new Promise(resolve => {
      return loadMessagesFor(locale).then(longerStrings).then(resolve.bind(this));
    });
  };
}
<commit_msg>Removing longerStrings in loadLocale<commit_after>export default function(config, loadMessagesFor) {
  return function loadLocale(locale) {
    return new Promise(resolve => {
      return loadMessagesFor(locale).then(resolve.bind(this));
    });
  };
}
<|endoftext|>


<commit_before>angular.module('ui.bootstrap.collapse', [])

  .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) {
    var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
    return {
      link: function(scope, element, attrs) {
        function expand() {
          element.removeClass('collapse')
            .addClass('collapsing')
            .attr('aria-expanded', true)
            .attr('aria-hidden', false);

          if ($animateCss) {
            $animateCss(element, {
              addClass: 'in',
              easing: 'ease',
              to: { height: element[0].scrollHeight + 'px' }
            }).start().finally(expandDone);
          } else {
            $animate.addClass(element, 'in', {
              to: { height: element[0].scrollHeight + 'px' }
            }).then(expandDone);
          }
        }

        function expandDone() {
          element.removeClass('collapsing')
            .addClass('collapse')
            .css({height: 'auto'});
        }

        function collapse() {
          if (!element.hasClass('collapse') && !element.hasClass('in')) {
            return collapseDone();
          }

          element
            // IMPORTANT: The height must be set before adding "collapsing" class.
            // Otherwise, the browser attempts to animate from height 0 (in
            // collapsing class) to the given height here.
            .css({height: element[0].scrollHeight + 'px'})
            // initially all panel collapse have the collapse class, this removal
            // prevents the animation from jumping to collapsed state
            .removeClass('collapse')
            .addClass('collapsing')
            .attr('aria-expanded', false)
            .attr('aria-hidden', true);

          if ($animateCss) {
            $animateCss(element, {
              removeClass: 'in',
              to: {height: '0'}
            }).start().finally(collapseDone);
          } else {
            $animate.removeClass(element, 'in', {
              to: {height: '0'}
            }).then(collapseDone);
          }
        }

        function collapseDone() {
          element.css({height: '0'}); // Required so that collapse works when animation is disabled
          element.removeClass('collapsing')
            .addClass('collapse');
        }

        scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
          if (shouldCollapse) {
            collapse();
          } else {
            expand();
          }
        });
      }
    };
  }]);

/* Deprecated collapse below */

angular.module('ui.bootstrap.collapse')

  .value('$collapseSuppressWarning', false)

  .directive('collapse', ['$animate', '$injector', '$log', '$collapseSuppressWarning', function($animate, $injector, $log, $collapseSuppressWarning) {
    var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
    return {
      link: function(scope, element, attrs) {
        if (!$collapseSuppressWarning) {
          $log.warn('collapse is now deprecated. Use uib-collapse instead.');
        }

        function expand() {
          element.removeClass('collapse')
            .addClass('collapsing')
            .attr('aria-expanded', true)
            .attr('aria-hidden', false);

          if ($animateCss) {
            $animateCss(element, {
              addClass: 'in',
              easing: 'ease',
              to: { height: element[0].scrollHeight + 'px' }
            }).start().done(expandDone);
          } else {
            $animate.addClass(element, 'in', {
              to: { height: element[0].scrollHeight + 'px' }
            }).then(expandDone);
          }
        }

        function expandDone() {
          element.removeClass('collapsing')
            .addClass('collapse')
            .css({height: 'auto'});
        }

        function collapse() {
          if (!element.hasClass('collapse') && !element.hasClass('in')) {
            return collapseDone();
          }

          element
            // IMPORTANT: The height must be set before adding "collapsing" class.
            // Otherwise, the browser attempts to animate from height 0 (in
            // collapsing class) to the given height here.
            .css({height: element[0].scrollHeight + 'px'})
            // initially all panel collapse have the collapse class, this removal
            // prevents the animation from jumping to collapsed state
            .removeClass('collapse')
            .addClass('collapsing')
            .attr('aria-expanded', false)
            .attr('aria-hidden', true);

          if ($animateCss) {
            $animateCss(element, {
              removeClass: 'in',
              to: {height: '0'}
            }).start().done(collapseDone);
          } else {
            $animate.removeClass(element, 'in', {
              to: {height: '0'}
            }).then(collapseDone);
          }
        }

        function collapseDone() {
          element.css({height: '0'}); // Required so that collapse works when animation is disabled
          element.removeClass('collapsing')
            .addClass('collapse');
        }

        scope.$watch(attrs.collapse, function(shouldCollapse) {
          if (shouldCollapse) {
            collapse();
          } else {
            expand();
          }
        });
      }
    };
  }]);
<commit_msg>Fix collapse animation<commit_after>angular.module('ui.bootstrap.collapse', [])

  .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) {
    var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
    return {
      link: function(scope, element, attrs) {
        function expand() {
          element.removeClass('collapse')
            .addClass('collapsing')
            .attr('aria-expanded', true)
            .attr('aria-hidden', false);

          if ($animateCss) {
            $animateCss(element, {
              addClass: 'in',
              easing: 'ease',
              to: { height: element[0].scrollHeight + 'px' }
            }).start().finally(expandDone);
          } else {
            $animate.addClass(element, 'in', {
              to: { height: element[0].scrollHeight + 'px' }
            }).then(expandDone);
          }
        }

        function expandDone() {
          element.removeClass('collapsing')
            .addClass('collapse')
            .css({height: 'auto'});
        }

        function collapse() {
          if (!element.hasClass('collapse') && !element.hasClass('in')) {
            return collapseDone();
          }

          element
            // IMPORTANT: The height must be set before adding "collapsing" class.
            // Otherwise, the browser attempts to animate from height 0 (in
            // collapsing class) to the given height here.
            .css({height: element[0].scrollHeight + 'px'})
            // initially all panel collapse have the collapse class, this removal
            // prevents the animation from jumping to collapsed state
            .removeClass('collapse')
            .addClass('collapsing')
            .attr('aria-expanded', false)
            .attr('aria-hidden', true);

          if ($animateCss) {
            $animateCss(element, {
              removeClass: 'in',
              to: {height: '0'}
            }).start().finally(collapseDone);
          } else {
            $animate.removeClass(element, 'in', {
              to: {height: '0'}
            }).then(collapseDone);
          }
        }

        function collapseDone() {
          element.css({height: '0'}); // Required so that collapse works when animation is disabled
          element.removeClass('collapsing')
            .addClass('collapse');
        }

        scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
          if (shouldCollapse) {
            collapse();
          } else {
            expand();
          }
        });
      }
    };
  }]);

/* Deprecated collapse below */

angular.module('ui.bootstrap.collapse')

  .value('$collapseSuppressWarning', false)

  .directive('collapse', ['$animate', '$injector', '$log', '$collapseSuppressWarning', function($animate, $injector, $log, $collapseSuppressWarning) {
    var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
    return {
      link: function(scope, element, attrs) {
        if (!$collapseSuppressWarning) {
          $log.warn('collapse is now deprecated. Use uib-collapse instead.');
        }

        function expand() {
          element.removeClass('collapse')
            .addClass('collapsing')
            .attr('aria-expanded', true)
            .attr('aria-hidden', false);

          if ($animateCss) {
            $animateCss(element, {
              easing: 'ease',
              to: { height: element[0].scrollHeight + 'px' }
            }).start().done(expandDone);
          } else {
            $animate.animate(element, {}, {
              height: element[0].scrollHeight + 'px'
            }).then(expandDone);
          }
        }

        function expandDone() {
          element.removeClass('collapsing')
            .addClass('collapse in')
            .css({height: 'auto'});
        }

        function collapse() {
          if (!element.hasClass('collapse') && !element.hasClass('in')) {
            return collapseDone();
          }

          element
            // IMPORTANT: The height must be set before adding "collapsing" class.
            // Otherwise, the browser attempts to animate from height 0 (in
            // collapsing class) to the given height here.
            .css({height: element[0].scrollHeight + 'px'})
            // initially all panel collapse have the collapse class, this removal
            // prevents the animation from jumping to collapsed state
            .removeClass('collapse in')
            .addClass('collapsing')
            .attr('aria-expanded', false)
            .attr('aria-hidden', true);

          if ($animateCss) {
            $animateCss(element, {
              to: {height: '0'}
            }).start().done(collapseDone);
          } else {
            $animate.animate(element, {}, {
              height: '0'
            }).then(collapseDone);
          }
        }

        function collapseDone() {
          element.css({height: '0'}); // Required so that collapse works when animation is disabled
          element.removeClass('collapsing')
            .addClass('collapse');
        }

        scope.$watch(attrs.collapse, function(shouldCollapse) {
          if (shouldCollapse) {
            collapse();
          } else {
            expand();
          }
        });
      }
    };
  }]);
<|endoftext|>


<commit_before>import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { Swipeable } from 'react-swipeable';
import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import * as PropTypes from 'prop-types';
import { serverType } from '../servers/prop-types';
import MutedMessage from '../utils/MutedMessage';
import NotFound from './NotFound';
import './MenuLayout.scss';

const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisits) =>
  class MenuLayout extends React.Component {
    static propTypes = {
      match: PropTypes.object,
      selectServer: PropTypes.func,
      location: PropTypes.object,
      selectedServer: serverType,
    };

    state = { showSideBar: false };

    componentDidMount() {
      const { match, selectServer } = this.props;
      const { params: { serverId } } = match;

      selectServer(serverId);
    }

    componentDidUpdate(prevProps) {
      const { location } = this.props;

      // Hide sidebar when location changes
      if (location !== prevProps.location) {
        this.setState({ showSideBar: false });
      }
    }

    render() {
      const { selectedServer, match } = this.props;

      if (!selectedServer) {
        return <MutedMessage loading />;
      }

      const { params: { serverId } } = match;
      const burgerClasses = classNames('menu-layout__burger-icon', {
        'menu-layout__burger-icon--active': this.state.showSideBar,
      });
      const swipeMenuIfNoModalExists = (showSideBar) => () => {
        if (document.querySelector('.modal')) {
          return;
        }

        this.setState({ showSideBar });
      };

      return (
        <React.Fragment>
          <FontAwesomeIcon
            icon={burgerIcon}
            className={burgerClasses}
            onClick={() => this.setState(({ showSideBar }) => ({ showSideBar: !showSideBar }))}
          />

          <Swipeable
            delta={40}
            className="menu-layout__swipeable"
            onSwipedLeft={swipeMenuIfNoModalExists(false)}
            onSwipedRight={swipeMenuIfNoModalExists(true)}
          >

              <AsideMenu
                className="col-lg-2 col-md-3"
                selectedServer={selectedServer}
                showOnMobile={this.state.showSideBar}
              />

 this.setState({ showSideBar: false })}
              >
                <Switch>
                  <Route
                    exact
                    path="/server/:serverId/list-short-urls/:page"
                    component={ShortUrls}
                  />
                  <Route
                    exact
                    path="/server/:serverId/create-short-url"
                    component={CreateShortUrl}
                  />
                  <Route
                    exact
                    path="/server/:serverId/short-code/:shortCode/visits"
                    component={ShortUrlVisits}
                  />
                  <Route
                    exact
                    path="/server/:serverId/manage-tags"
                    component={TagsList}
                  />
                  <Route
                    render={() => <NotFound to={`/server/${serverId}/list-short-urls/1`} btnText="List short URLs" />}
                  />
                </Switch>

          </Swipeable>
        </React.Fragment>
      );
    }
  };

export default MenuLayout;
<commit_msg>Converted MenuLayout into a functional component with hooks<commit_after>import React, { useState, useEffect } from 'react';
import { Route, Switch } from 'react-router-dom';
import { Swipeable } from 'react-swipeable';
import { faBars as burgerIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classNames from 'classnames';
import * as PropTypes from 'prop-types';
import { serverType } from '../servers/prop-types';
import MutedMessage from '../utils/MutedMessage';
import NotFound from './NotFound';
import './MenuLayout.scss';

const propTypes = {
  match: PropTypes.object,
  selectServer: PropTypes.func,
  location: PropTypes.object,
  selectedServer: serverType,
};

const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisits) => {
  const MenuLayoutComp = ({ match, location, selectedServer, selectServer }) => {
    const [ showSideBar, setShowSidebar ] = useState(false);

    useEffect(() => {
      const { params: { serverId } } = match;

      selectServer(serverId);
    }, []);
    useEffect(() => setShowSidebar(false), [ location ]);

    if (!selectedServer) {
      return <MutedMessage loading />;
    }

    const { params: { serverId } } = match;
    const burgerClasses = classNames('menu-layout__burger-icon', {
      'menu-layout__burger-icon--active': showSideBar,
    });
    const swipeMenuIfNoModalExists = (showSideBar) => () => {
      if (document.querySelector('.modal')) {
        return;
      }

      setShowSidebar(showSideBar);
    };

    return (
      <React.Fragment>
        <FontAwesomeIcon
          icon={burgerIcon}
          className={burgerClasses}
          onClick={() => setShowSidebar(!showSideBar)}
        />

        <Swipeable
          delta={40}
          className="menu-layout__swipeable"
          onSwipedLeft={swipeMenuIfNoModalExists(false)}
          onSwipedRight={swipeMenuIfNoModalExists(true)}
        >

            <AsideMenu className="col-lg-2 col-md-3" selectedServer={selectedServer} showOnMobile={showSideBar} />

 setShowSidebar(false)}>
              <Switch>
                <Route exact path="/server/:serverId/list-short-urls/:page" component={ShortUrls} />
                <Route exact path="/server/:serverId/create-short-url" component={CreateShortUrl} />
                <Route exact path="/server/:serverId/short-code/:shortCode/visits" component={ShortUrlVisits} />
                <Route exact path="/server/:serverId/manage-tags" component={TagsList} />
                <Route
                  render={() => <NotFound to={`/server/${serverId}/list-short-urls/1`} btnText="List short URLs" />}
                />
              </Switch>

        </Swipeable>
      </React.Fragment>
    );
  };

  MenuLayoutComp.propTypes = propTypes;

  return MenuLayoutComp;
};

export default MenuLayout;
<|endoftext|>


<commit_before>const { against, when, otherwise, isString } = require('match-iz')
const { pipe, flow, aside, memo } = require('./fp')

const makeRegExpFromWildcardString = memo(str => {
  if (!isString(str) || !str.length) {
    throw new TypeError('Please pass a non-empty string')
  }
  return pipe(
    str
      .replace(rxConsecutiveWildcards(), '*')
      .split('*')
      .map(x => x.trim())
      .map(escapeStringForRegExp),

    aside(x => console.log(`1 aside = "${x}"`)),

    against(
      when(hasNoWildcards)(templateMatchExact),
      when(hasNoWildcardAtStart)(flow(insertWildcards, templateMatchStart)),
      when(hasNoWildcardAtEnd)(flow(insertWildcards, templateMatchEnd)),
      otherwise(insertWildcards)
    ),

    aside(x => console.log(`2 aside = "${x}"`)),

    $ => new RegExp($)
  )
})

//
// Helpers
//

const rxEscape = () => /[.*+?^${}()|[\]\\]/g
const rxConsecutiveWildcards = () => /\*{2,}/g

const hasNoWildcards = x => x.length === 1
const hasNoWildcardAtStart = x => x.at(0) !== ''
const hasNoWildcardAtEnd = x => x.at(-1) !== ''

const insertWildcards = x => x.join('(.*)')
const templateMatchExact = ([x]) => `^${x}$`
const templateMatchStart = x => `^${x}`
const templateMatchEnd = x => `${x}$`

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
function escapeStringForRegExp(str) {
  if (!isString(str)) {
    throw new TypeError('Please pass a string')
  }
  // $& means the whole matched string
  return str.replace(rxEscape(), '\\$&')
}

module.exports = {
  makeRegExpFromWildcardString,
  escapeStringForRegExp
}
<commit_msg>Remove debugging statements<commit_after>const { against, when, otherwise, isString } = require('match-iz')
const { pipe, flow, memo } = require('./fp')

const makeRegExpFromWildcardString = memo(str => {
  if (!isString(str) || !str.length) {
    throw new TypeError('Please pass a non-empty string')
  }
  return pipe(
    str
      .replace(rxConsecutiveWildcards(), '*')
      .split('*')
      .map(x => x.trim())
      .map(escapeStringForRegExp),

    against(
      when(hasNoWildcards)(templateMatchExact),
      when(hasNoWildcardAtStart)(flow(insertWildcards, templateMatchStart)),
      when(hasNoWildcardAtEnd)(flow(insertWildcards, templateMatchEnd)),
      otherwise(insertWildcards)
    ),

    $ => new RegExp($)
  )
})

//
// Helpers
//

const rxEscape = () => /[.*+?^${}()|[\]\\]/g
const rxConsecutiveWildcards = () => /\*{2,}/g

const hasNoWildcards = x => x.length === 1
const hasNoWildcardAtStart = x => x.at(0) !== ''
const hasNoWildcardAtEnd = x => x.at(-1) !== ''

const insertWildcards = x => x.join('(.*)')
const templateMatchExact = ([x]) => `^${x}$`
const templateMatchStart = x => `^${x}`
const templateMatchEnd = x => `${x}$`

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
function escapeStringForRegExp(str) {
  if (!isString(str)) {
    throw new TypeError('Please pass a string')
  }
  // $& means the whole matched string
  return str.replace(rxEscape(), '\\$&')
}

module.exports = {
  makeRegExpFromWildcardString,
  escapeStringForRegExp
}
<|endoftext|>


<commit_before>import '../_sass/_main.scss';

import pace from 'pace-progress';
import 'bootstrap';
import 'bootstrap-toggle';
import AOS from 'aos';

import editor from './editor';
import favourites from './favourites.js';
import styleNavbar from './style-navbar';
import styleJumbotrons from './style-jumbotrons';
import displayScrollBtns from './display-scroll-btns';
import apiStats from './api-stats';
import imageError from './image-error';
import imageCrop from './image-crop';
import anchorScroll from './anchor-scroll';
import progress from './progress';
import s3 from './s3';
import ps from './project-shuffle';
import featuredProjects from './featured-projects';
import search from './search';
import btnScroll from './btn-scroll';
import tabControl from './tab-control';
import hamburger from './hamburger';
import sidebar from './sidebar.js';
import csrfToken from './csrf-token';
import msg from './flash-messages';

// TODO export to own repo to be bundled within projects - add bootstrap as dependency
import viewer from './ui/components/viewer/viewer';

// Init pace-progress
pace.start()

// Init Bootstrap popovers and tooltips
$('[data-toggle="popover"]').popover();
$('[data-toggle="tooltip"]').tooltip();

// Init aos
AOS.init({ once: 'true' });<commit_msg>Fix import<commit_after>import '../_sass/_main.scss';

import pace from 'pace-progress';
import 'bootstrap';
import 'bootstrap-toggle';
import AOS from 'aos';

import editor from './editor';
import favourites from './favourites.js';
import styleNavbar from './style-navbar';
import styleJumbotrons from './style-jumbotrons';
import displayScrollBtns from './display-scroll-btns';
import apiStats from './api-stats';
import imageError from './image-error';
import imageCrop from './image-crop';
import anchorScroll from './anchor-scroll';
import progress from './progress';
import s3 from './s3';
import ps from './project-shuffle';
import featuredProjects from './featured-projects';
import search from './search';
import btnScroll from './btn-scroll';
import tabControl from './tab-control';
import hamburger from './hamburger';
import sidebar from './sidebar.js';
import csrfToken from './csrf-token';
import msg from './flash-messages';

// TODO export to own repo to be bundled within projects - add bootstrap as dependency
import Interface from './ui/components/viewer/interface';

// Init pace-progress
pace.start()

// Init Bootstrap popovers and tooltips
$('[data-toggle="popover"]').popover();
$('[data-toggle="tooltip"]').tooltip();

// Init aos
AOS.init({ once: 'true' });<|endoftext|>


<commit_before>'use strict';

var mixin          = require('es5-ext/object/mixin')
  , setPrototypeOf = require('es5-ext/object/set-prototype-of')
  , d              = require('d')
  , compile        = require('es6-template-strings/compile')
  , resolve        = require('es6-template-strings/resolve-to-array');

var Message = module.exports = function (template, inserts) {
	var message = resolve(compile(template), inserts);
	if (setPrototypeOf) return setPrototypeOf(message, Message.prototype);
	return mixin(message, Message.prototype);
};

Message.prototype = [];
Object.defineProperties(Message.prototype, {
	constructor: d(Message),
	toString: d(function () { return this.join(''); })
});
<commit_msg>Do not crash if some inserts are not provided<commit_after>'use strict';

var mixin          = require('es5-ext/object/mixin')
  , setPrototypeOf = require('es5-ext/object/set-prototype-of')
  , d              = require('d')
  , compile        = require('es6-template-strings/compile')
  , resolve        = require('es6-template-strings/resolve-to-array')

  , resolveOpts = { partial: true };

var Message = module.exports = function (template, inserts) {
	var message = resolve(compile(template), inserts, resolveOpts);
	if (setPrototypeOf) return setPrototypeOf(message, Message.prototype);
	return mixin(message, Message.prototype);
};

Message.prototype = [];
Object.defineProperties(Message.prototype, {
	constructor: d(Message),
	toString: d(function () { return this.join(''); })
});
<|endoftext|>


<commit_before>import createElement from 'virtual-dom/create-element'
import diff from 'virtual-dom/diff'
import patch from 'virtual-dom/patch'

import refsStack from './refs-stack'
import {getScheduler} from './scheduler-assignment'

const componentsWithPendingUpdates = new WeakSet
let syncUpdatesInProgressCounter = 0
let syncDestructionsInProgressCounter = 0

// This function associates a component object with a DOM element by calling
// the components `render` method, assigning an `.element` property on the
// object and also returning the element.
//
// It also assigns a `virtualElement` property based on the return value of the
// `render` method. This will be used later by `performElementUpdate` to diff
// the new results of `render` with the previous results when updating the
// component's element.
//
// Finally, this function also associates the component with a `refs` object,
// which is populated with references to elements based on `ref` properties on
// nodes of the `virtual-dom` tree. Before calling into `virtual-dom` to create
// the DOM tree, it pushes this `refs` object to a shared stack so it can be
// accessed by hooks during the creation of individual elements.
export function initialize(component) {
  component.refs = {}
  component.virtualElement = component.render()
  refsStack.push(component.refs)
  component.element = createElement(component.virtualElement)
  refsStack.pop()
  return component.element
}

// This function receives a component that has already been associated with an
// element via a previous call to `initialize` and updates this element by
// calling `render` on the component.
//
// When called in normal circumstances, it uses the scheduler to defer this
// update until the next animation frame, and will only perform one update of a
// given component in a given frame. This means you can call `update`
// repeatedly in a given tick without causing redundant updates.
//
// If this function called during another synchronous update (for example, as a
// result of a call to `update` on a child component), the update is performed
// synchronously.
//
// Returns a promise that will resolve when the requested update has been
// completed.
export function update (component) {
  if (syncUpdatesInProgressCounter > 0) {
    updateSync(component)
    return Promise.resolve()
  }

  let scheduler = getScheduler()

  if (!componentsWithPendingUpdates.has(component)) {
    componentsWithPendingUpdates.add(component)
    scheduler.updateDocument(function () {
      componentsWithPendingUpdates.delete(component)
      updateSync(component)
    })
  }

  return scheduler.getNextUpdatePromise()
}

// Synchronsly updates the DOM element associated with a component object. .
// This method assumes the presence of `.element` and `.virtualElement`
// properties on the component, which are assigned in the `initialize`
// function.
//
// It calls `render` on the component to obtain the desired state of the DOM,
// then `diff`s it with the previous state and `patch`es the element based on
// the resulting diff. During the patch operation, it pushes the component's
// `refs` object to a shared stack so that references to DOM elements can be
// updated.
//
// If `update` is called during the invocation of `updateSync`,
// the requests are processed synchronously as well. We track whether this is
// the case by incrementing and decrementing `syncUpdatesInProgressCounter`
// around the call.
//
// For now, etch does not allow the root tag of the `render` method to change
// between invocations, because we want to preserve a one-to-one relationship
// between component objects and DOM elements for simplicity.
export function updateSync (component) {
  syncUpdatesInProgressCounter++

  let oldVirtualElement = component.virtualElement
  let oldDomNode = component.element
  let newVirtualElement = component.render()
  refsStack.push(component.refs)
  let newDomNode = patch(component.element, diff(oldVirtualElement, newVirtualElement))
  refsStack.pop()
  component.virtualElement = newVirtualElement
  if (newDomNode !== oldDomNode) {
    throw new Error("etch does not support changing the root DOM node type of a component")
  }

  syncUpdatesInProgressCounter--
}

export function destroy (component) {
  if (syncUpdatesInProgressCounter > 0 || syncDestructionsInProgressCounter > 0) {
    destroySync(component)
    return Promise.resolve()
  }

  let scheduler = getScheduler()
  scheduler.updateDocument(function () {
    destroySync(component)
  })
  return scheduler.getNextUpdatePromise()
}

export function destroySync (component) {
  syncDestructionsInProgressCounter++
  destroyChildComponents(component.virtualElement)
  if (syncDestructionsInProgressCounter === 1) component.element.remove()
  syncDestructionsInProgressCounter--
}

function destroyChildComponents(virtualNode) {
  if (virtualNode.type === 'Widget') {
    if (virtualNode.component && typeof virtualNode.component.destroy === 'function') {
      virtualNode.component.destroy()
    }
  } else if (virtualNode.children) {
    virtualNode.children.forEach(destroyChildComponents)
  }
}
<commit_msg>Document `destroy` and `destroySync`<commit_after>import createElement from 'virtual-dom/create-element'
import diff from 'virtual-dom/diff'
import patch from 'virtual-dom/patch'

import refsStack from './refs-stack'
import {getScheduler} from './scheduler-assignment'

const componentsWithPendingUpdates = new WeakSet
let syncUpdatesInProgressCounter = 0
let syncDestructionsInProgressCounter = 0

// This function associates a component object with a DOM element by calling
// the components `render` method, assigning an `.element` property on the
// object and also returning the element.
//
// It also assigns a `virtualElement` property based on the return value of the
// `render` method. This will be used later by `performElementUpdate` to diff
// the new results of `render` with the previous results when updating the
// component's element.
//
// Finally, this function also associates the component with a `refs` object,
// which is populated with references to elements based on `ref` properties on
// nodes of the `virtual-dom` tree. Before calling into `virtual-dom` to create
// the DOM tree, it pushes this `refs` object to a shared stack so it can be
// accessed by hooks during the creation of individual elements.
export function initialize(component) {
  component.refs = {}
  component.virtualElement = component.render()
  refsStack.push(component.refs)
  component.element = createElement(component.virtualElement)
  refsStack.pop()
  return component.element
}

// This function receives a component that has already been associated with an
// element via a previous call to `initialize` and updates this element by
// calling `render` on the component.
//
// When called in normal circumstances, it uses the scheduler to defer this
// update until the next animation frame, and will only perform one update of a
// given component in a given frame. This means you can call `update`
// repeatedly in a given tick without causing redundant updates.
//
// If this function called during another synchronous update (for example, as a
// result of a call to `update` on a child component), the update is performed
// synchronously.
//
// Returns a promise that will resolve when the requested update has been
// completed.
export function update (component) {
  if (syncUpdatesInProgressCounter > 0) {
    updateSync(component)
    return Promise.resolve()
  }

  let scheduler = getScheduler()

  if (!componentsWithPendingUpdates.has(component)) {
    componentsWithPendingUpdates.add(component)
    scheduler.updateDocument(function () {
      componentsWithPendingUpdates.delete(component)
      updateSync(component)
    })
  }

  return scheduler.getNextUpdatePromise()
}

// Synchronsly updates the DOM element associated with a component object. .
// This method assumes the presence of `.element` and `.virtualElement`
// properties on the component, which are assigned in the `initialize`
// function.
//
// It calls `render` on the component to obtain the desired state of the DOM,
// then `diff`s it with the previous state and `patch`es the element based on
// the resulting diff. During the patch operation, it pushes the component's
// `refs` object to a shared stack so that references to DOM elements can be
// updated.
//
// If `update` is called during the invocation of `updateSync`,
// the requests are processed synchronously as well. We track whether this is
// the case by incrementing and decrementing `syncUpdatesInProgressCounter`
// around the call.
//
// For now, etch does not allow the root tag of the `render` method to change
// between invocations, because we want to preserve a one-to-one relationship
// between component objects and DOM elements for simplicity.
export function updateSync (component) {
  syncUpdatesInProgressCounter++

  let oldVirtualElement = component.virtualElement
  let oldDomNode = component.element
  let newVirtualElement = component.render()
  refsStack.push(component.refs)
  let newDomNode = patch(component.element, diff(oldVirtualElement, newVirtualElement))
  refsStack.pop()
  component.virtualElement = newVirtualElement
  if (newDomNode !== oldDomNode) {
    throw new Error("etch does not support changing the root DOM node type of a component")
  }

  syncUpdatesInProgressCounter--
}

// Removes the component's associated element and calls `destroy` on any child
// components. Normally, this function is asynchronous and will perform the
// destruction on the next animation frame. If called as the result of another
// update or destruction, it calls `destroy` on child components synchronously.
// If called as the result of destroying a component higher in the DOM, the
// element is not removed to avoid redundant DOM manipulation. Returns a promise
// that resolves when the destruction is completed.
export function destroy (component) {
  if (syncUpdatesInProgressCounter > 0 || syncDestructionsInProgressCounter > 0) {
    destroySync(component)
    return Promise.resolve()
  }

  let scheduler = getScheduler()
  scheduler.updateDocument(function () {
    destroySync(component)
  })
  return scheduler.getNextUpdatePromise()
}

// A synchronous version of `destroy`.
//
// Note that we track whether `destroy` calls are in progress and only remove
// the element if we are not a nested call.
export function destroySync (component) {
  syncDestructionsInProgressCounter++
  destroyChildComponents(component.virtualElement)
  if (syncDestructionsInProgressCounter === 1) component.element.remove()
  syncDestructionsInProgressCounter--
}

function destroyChildComponents(virtualNode) {
  if (virtualNode.type === 'Widget') {
    if (virtualNode.component && typeof virtualNode.component.destroy === 'function') {
      virtualNode.component.destroy()
    }
  } else if (virtualNode.children) {
    virtualNode.children.forEach(destroyChildComponents)
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import styled from 'styled-components';

import { selectors } from 'modules/options';

const StyledButton = styled.button`
  display: inline-flex;
  text-align: center;
  justify-content: center;
  align-items: center;
  text-transform: uppercase;
  border-radius: 3px;
  font-weight: 500;
  padding: 10px 15px;
  cursor: pointer;
  opacity: 0.9;
  border: none;
  outline: none;
  box-shadow: none;
  background: #ccc;
  color: #fff;
  transition: background 500ms, opacity 500ms;

  ${props => !props.noAccent && props.accent && `background: ${props.accent.value}`};
  ${props => props.noAccent && `color: #333`};

  &:hover {
    opacity: 1;
  }
`;

const Button = ({ accentColor, children, ...rest }) => (
  <StyledButton accent={accentColor} {...rest}>
    {children}
  </StyledButton>
);

const mapStateToProps = state => ({
  accentColor: selectors.accentColor(state),
});

export default connect(mapStateToProps)(Button);
<commit_msg>Updated Button to use contrast for setting text color<commit_after>import React from 'react';
import { connect } from 'react-redux';
import styled from 'styled-components';
import color from 'tinycolor2';

import { selectors } from 'modules/options';

const StyledButton = styled.button`
  display: inline-flex;
  text-align: center;
  justify-content: center;
  align-items: center;
  text-transform: uppercase;
  border-radius: 3px;
  font-weight: 500;
  padding: 10px 15px;
  cursor: pointer;
  opacity: 0.9;
  border: none;
  outline: none;
  box-shadow: none;
  background: #ccc;
  color: #333;
  transition: background 500ms, opacity 500ms;

  ${props => props.accent && `background: ${props.accent.value}`};
  ${props => props.accent && `color: ${color(props.accent.value).getBrightness() > 165 ? '#141517' : '#fff'}`};

  &:hover {
    opacity: 1;
  }
`;

const Button = ({ accentColor, noAccent, children, ...rest }) => (
  <StyledButton accent={!noAccent && accentColor} {...rest}>
    {children}
  </StyledButton>
);

const mapStateToProps = state => ({
  accentColor: selectors.accentColor(state),
});

export default connect(mapStateToProps)(Button);
<|endoftext|>


<commit_before>const React = require('react');
const Radium = require('radium');

const Spin = require('./Spin');

const StyleConstants = require('../constants/Style');

const Icon = require('../components/Icon');

const { buttonTypes } = require('../constants/App');

const Button = React.createClass({
  propTypes: {
    actionText: React.PropTypes.string,
    ariaLabel: React.PropTypes.string,
    icon: React.PropTypes.string,
    isActive: React.PropTypes.bool,
    onClick: React.PropTypes.func,
    primaryColor: React.PropTypes.string,
    style: React.PropTypes.object,
    type: React.PropTypes.oneOf(buttonTypes)
  },

  getDefaultProps () {
    return {
      onClick () {},
      isActive: false,
      primaryColor: StyleConstants.Colors.PRIMARY,
      type: 'primary'
    };
  },

  _isLargeOrMediumWindowSize () {
    const windowSize = StyleConstants.getWindowSize();

    return windowSize === 'medium' || windowSize === 'large';
  },

  render () {
    const styles = this.styles();

    return (
      <button
        aria-label={this.props.ariaLabel}
        onClick={this.props.type === 'disabled' ? null : this.props.onClick}
        style={Object.assign({}, styles.component, styles[this.props.type], this.props.style)}
      >

          {(this.props.icon && !this.props.isActive) ? <Icon size={20} style={styles.icon} type={this.props.icon} /> : null}
          {this.props.isActive ? (
            <Spin direction='counterclockwise'>
              <Icon elementProps={{ 'aria-hidden': true }} size={20} type='spinner' />
            </Spin>
          )}

            {this.props.isActive ? this.props.actionText : this.props.children}

      </button>
    );
  },

  styles () {
    return {
      component: Object.assign({
        borderRadius: 2,
        borderStyle: 'solid',
        borderWidth: 1,
        borderColor: 'transparent',
        boxSizing: 'border-box',
        display: 'inline-block',
        padding: '4px 14px',
        fontSize: StyleConstants.FontSizes.MEDIUM,
        fontFamily: StyleConstants.Fonts.SEMIBOLD,
        cursor: this.props.type === 'disabled' ? 'default' : 'pointer',
        transition: 'all .2s ease-in',
        minWidth: 16,
        position: 'relative'
      }, this.props.style),
      children: {
        justifyContent: 'center',
        display: 'flex',
        alignItems: 'center',
        lineHeight: '20px'
      },
      primary: {
        backgroundColor: this.props.primaryColor,
        borderColor: this.props.primaryColor,
        color: StyleConstants.Colors.WHITE,
        fill: StyleConstants.Colors.WHITE,
        transition: 'all .2s ease-in',

        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.adjustColor(this.props.primaryColor, -15),
          borderColor: StyleConstants.adjustColor(this.props.primaryColor, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          borderColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          transition: 'all .2s ease-in'
        }
      },
      primaryOutline: {
        backgroundColor: 'transparent',
        borderColor: this.props.primaryColor,
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        transition: 'all .2s ease-in',

        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: this.props.primaryColor,
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          borderColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      primaryInverse: {
        backgroundColor: StyleConstants.Colors.WHITE,
        borderColor: StyleConstants.Colors.WHITE,
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        transition: 'all .2s ease-in',

        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -15),
          borderColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -30),
          borderColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -30),
          transition: 'all .2s ease-in'
        }
      },
      secondary: {
        backgroundColor: 'transparent',
        borderColor: StyleConstants.Colors.ASH,
        color: StyleConstants.Colors.ASH,
        fill: StyleConstants.Colors.ASH,
        transition: 'all .2s ease-in',
        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.Colors.ASH,
          borderColor: StyleConstants.Colors.ASH,
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.ASH, -30),
          borderColor: StyleConstants.adjustColor(StyleConstants.Colors.ASH, -30),
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      base: {
        backgroundColor: 'transparent',
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        transition: 'all .2s ease-in',
        borderColor: 'transparent',
        borderRadius: 2,
        borderWidth: 1,
        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          color: StyleConstants.adjustColor(this.props.primaryColor, -8),
          fill: StyleConstants.adjustColor(this.props.primaryColor, -8),
          transition: 'all .2s ease-in',
          borderColor: StyleConstants.Colors.FOG
        },
        ':active': {
          color: StyleConstants.adjustColor(this.props.primaryColor, -16),
          fill: StyleConstants.adjustColor(this.props.primaryColor, -16),
          transition: 'all .2s ease-in',
          backgroundColor: StyleConstants.Colors.PORCELAIN
        }
      },
      neutral: {
        backgroundColor: 'transparent',
        borderColor: StyleConstants.Colors.FOG,
        borderRadius: 2,
        borderWidth: 1,
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.Colors.PORCELAIN
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.PORCELAIN, -15)
        }
      },
      disabled: {
        backgroundColor: 'transparent',
        borderColor: StyleConstants.Colors.FOG,
        color: StyleConstants.Colors.FOG,
        fill: StyleConstants.Colors.FOG
      },
      icon: {
        marginLeft: this.props.children ? -4 : 0,
        marginRight: this.props.children ? 5 : 0
      },
      buttonText: {
        marginLeft: (this.props.isActive && this.props.actionText) ? 10 : 0
      }
    };
  }
});

module.exports = Radium(Button);<commit_msg>Sets aria-hidden attribute true on button icon element<commit_after>const React = require('react');
const Radium = require('radium');

const Spin = require('./Spin');

const StyleConstants = require('../constants/Style');

const Icon = require('../components/Icon');

const { buttonTypes } = require('../constants/App');

const Button = React.createClass({
  propTypes: {
    actionText: React.PropTypes.string,
    ariaLabel: React.PropTypes.string,
    icon: React.PropTypes.string,
    isActive: React.PropTypes.bool,
    onClick: React.PropTypes.func,
    primaryColor: React.PropTypes.string,
    style: React.PropTypes.object,
    type: React.PropTypes.oneOf(buttonTypes)
  },

  getDefaultProps () {
    return {
      onClick () {},
      isActive: false,
      primaryColor: StyleConstants.Colors.PRIMARY,
      type: 'primary'
    };
  },

  _isLargeOrMediumWindowSize () {
    const windowSize = StyleConstants.getWindowSize();

    return windowSize === 'medium' || windowSize === 'large';
  },

  render () {
    const styles = this.styles();

    return (
      <button
        aria-label={this.props.ariaLabel}
        onClick={this.props.type === 'disabled' ? null : this.props.onClick}
        style={Object.assign({}, styles.component, styles[this.props.type], this.props.style)}
      >

          {(this.props.icon && !this.props.isActive) && (
            <Icon
              elementProps={{ 'aria-hidden': true }}
              size={20}
              style={styles.icon}
              type={this.props.icon}
            />
          )}
          {this.props.isActive && (
            <Spin direction='counterclockwise'>
              <Icon elementProps={{ 'aria-hidden': true }} size={20} type='spinner' />
            </Spin>
          )}

            {this.props.isActive ? this.props.actionText : this.props.children}

      </button>
    );
  },

  styles () {
    return {
      component: Object.assign({
        borderRadius: 2,
        borderStyle: 'solid',
        borderWidth: 1,
        borderColor: 'transparent',
        boxSizing: 'border-box',
        display: 'inline-block',
        padding: '4px 14px',
        fontSize: StyleConstants.FontSizes.MEDIUM,
        fontFamily: StyleConstants.Fonts.SEMIBOLD,
        cursor: this.props.type === 'disabled' ? 'default' : 'pointer',
        transition: 'all .2s ease-in',
        minWidth: 16,
        position: 'relative'
      }, this.props.style),
      children: {
        justifyContent: 'center',
        display: 'flex',
        alignItems: 'center',
        lineHeight: '20px'
      },
      primary: {
        backgroundColor: this.props.primaryColor,
        borderColor: this.props.primaryColor,
        color: StyleConstants.Colors.WHITE,
        fill: StyleConstants.Colors.WHITE,
        transition: 'all .2s ease-in',

        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.adjustColor(this.props.primaryColor, -15),
          borderColor: StyleConstants.adjustColor(this.props.primaryColor, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          borderColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          transition: 'all .2s ease-in'
        }
      },
      primaryOutline: {
        backgroundColor: 'transparent',
        borderColor: this.props.primaryColor,
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        transition: 'all .2s ease-in',

        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: this.props.primaryColor,
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          borderColor: StyleConstants.adjustColor(this.props.primaryColor, -30),
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      primaryInverse: {
        backgroundColor: StyleConstants.Colors.WHITE,
        borderColor: StyleConstants.Colors.WHITE,
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        transition: 'all .2s ease-in',

        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -15),
          borderColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -15),
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -30),
          borderColor: StyleConstants.adjustColor(StyleConstants.Colors.WHITE, -30),
          transition: 'all .2s ease-in'
        }
      },
      secondary: {
        backgroundColor: 'transparent',
        borderColor: StyleConstants.Colors.ASH,
        color: StyleConstants.Colors.ASH,
        fill: StyleConstants.Colors.ASH,
        transition: 'all .2s ease-in',
        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.Colors.ASH,
          borderColor: StyleConstants.Colors.ASH,
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.ASH, -30),
          borderColor: StyleConstants.adjustColor(StyleConstants.Colors.ASH, -30),
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE,
          transition: 'all .2s ease-in'
        }
      },
      base: {
        backgroundColor: 'transparent',
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        transition: 'all .2s ease-in',
        borderColor: 'transparent',
        borderRadius: 2,
        borderWidth: 1,
        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          color: StyleConstants.adjustColor(this.props.primaryColor, -8),
          fill: StyleConstants.adjustColor(this.props.primaryColor, -8),
          transition: 'all .2s ease-in',
          borderColor: StyleConstants.Colors.FOG
        },
        ':active': {
          color: StyleConstants.adjustColor(this.props.primaryColor, -16),
          fill: StyleConstants.adjustColor(this.props.primaryColor, -16),
          transition: 'all .2s ease-in',
          backgroundColor: StyleConstants.Colors.PORCELAIN
        }
      },
      neutral: {
        backgroundColor: 'transparent',
        borderColor: StyleConstants.Colors.FOG,
        borderRadius: 2,
        borderWidth: 1,
        color: this.props.primaryColor,
        fill: this.props.primaryColor,
        ':hover': !this._isLargeOrMediumWindowSize() ? null : {
          backgroundColor: StyleConstants.Colors.PORCELAIN
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(StyleConstants.Colors.PORCELAIN, -15)
        }
      },
      disabled: {
        backgroundColor: 'transparent',
        borderColor: StyleConstants.Colors.FOG,
        color: StyleConstants.Colors.FOG,
        fill: StyleConstants.Colors.FOG
      },
      icon: {
        marginLeft: this.props.children ? -4 : 0,
        marginRight: this.props.children ? 5 : 0
      },
      buttonText: {
        marginLeft: (this.props.isActive && this.props.actionText) ? 10 : 0
      }
    };
  }
});

module.exports = Radium(Button);<|endoftext|>


<commit_before>import React from 'react';
import Radium from 'radium';

const style = {
  wrapper: {
    padding: '5px',
    borderRadius: '3px',
    backgroundColor: '#FFF6F6',
    position: 'absolute',
    width: '35%',
    height: '8%',
    margin: 'auto',
    top: '20%',
    right: '35%',
    textAlign: 'center',
    '@media (max-width: 48em)': {
      left: '0',
      width: '100%'
    }
  },
  icon: {
    fontSize: '1em'
  },
  title: {
    color: '#912D2B',
    fontWeight: 'bold',
    marginTop: '5px'
  },
  msg: {
    color: 'rgb(159, 58, 56)'
  }
};

const ErrMsg = () => (

Error!!

We are sorry but something went wrong, please try again later.

);

export default Radium(ErrMsg);
<commit_msg>use flex in error msg component<commit_after>import React from 'react';
import Radium from 'radium';

const style = {
  wrapper: {
    minHeight: '50vh',
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center'
  },
  box: {
    borderRadius: '3px',
    backgroundColor: '#FFF6F6',
    padding: '15px'
  },
  title: {
    fontSize: '20px',
    textAlign: 'center',
    color: '#912D2B',
    fontWeight: 'bold'
  },
  msg: {
    color: 'rgb(159, 58, 56)'
  }
};

const ErrMsg = () => (

Error

We are sorry but something went wrong, please try again later.

);

export default Radium(ErrMsg);
<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'react-router-dom'

function Splash () {
  return (

      Öpücük 2018 💋 Contact 💋
      <Link  className="Link" to="/logout">Log out</Link>

  )
}

export default Splash
<commit_msg>Footer | Add credits 💋<commit_after>import React from 'react'
import { Link } from 'react-router-dom'

function Splash () {
  return (

      Made by Gabriel Cousin & Alexandre Broudin 💋 Öpücük 2018 💋 Contact 💋
      <Link  className="Link" to="/logout">Log out</Link>

  )
}

export default Splash
<|endoftext|>


<commit_before>import React from 'react'
import { NavLink } from 'react-router-dom'
import { HashLink as Link } from 'react-router-hash-link'
import PropTypes from 'prop-types'

const Header = ({view, userID, userName, userRole}) => (

    <NavLink exact to='/' className='item'>
      LLScanner {view === 'admin' ? 'Dashboard': ''}
    </NavLink>

      {userID ? userName : 'login'}

)

Header.proptypes = {
}

export default Header
<commit_msg>polish header<commit_after>import React from 'react'
import { NavLink } from 'react-router-dom'
import { HashLink as Link } from 'react-router-hash-link'
import PropTypes from 'prop-types'

const Header = ({view, userID, userName, userRole}) => (

    <NavLink exact to='/' className='item'>
      勞基法掃描器

      alpha

      {view === 'admin' ? 'Dashboard': ''}
    </NavLink>

      {userID ? userName : 登入}

)

Header.proptypes = {
}

export default Header
<|endoftext|>


<commit_before>import React from 'react'
import { ThemeProvider } from 'emotion-theming'
import Helmet from 'react-helmet'
import '../styles/reset'
import theme, * as palette from '../styles/themeDark'
import config from '../utils/siteConfig'
import Wrapper from '../components/Wrapper'
import Menu from '../components/Menu'
import { injectGlobal } from 'emotion'

injectGlobal`
body {
  background: ${palette.BASE} !important;
  color: ${palette.SECONDARY} !important;
}
h1,h2,h3,h4,h5,h6,a,strong {
  color: ${palette.TERTIARY};
}
p {
  color: ${palette.SECONDARY};
}
a{
  transition: all 0.5s;
  color: ${palette.TERTIARY};
  &:hover {
  color: ${palette.HIGHLIGHT};
  }
}
svg {
  transition: all 0.5s;
  fill: ${palette.TERTIARY};
  &:hover {
    fill: ${palette.HIGHLIGHT};
  }
}
.bm-overlay, .bm-menu-wrap {
  background: ${palette.BASE} !important;
}
.bm-cross {
  background: ${palette.TERTIARY};
}
.bm-burger-bars {
  background: ${palette.TERTIARY};
}
`

const Layout = ({ children, location }) => {
  return (

      <Helmet>

        <title>{config.siteTitle}</title>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/logos/logo-512.png" />
        <link rel="apple-touch-icon" href="/logos/logo-512.png" />
        <meta name="description" content={config.siteDescription} />
        <meta property="og:title" content={config.siteTitle} />
        <meta property="og:url" content={config.siteUrl} />
        <meta property="og:locale" content="en_US" />
        <meta property="og:type" content="website" />
        <meta property="og:site_name" content={config.siteTitle} />
      </Helmet>

      <ThemeProvider theme={theme}>
        <Wrapper>

            <Menu />

              {children}

        </Wrapper>
      </ThemeProvider>

  )
}

export default Layout
<commit_msg>Revert overflow scroll<commit_after>import React from 'react'
import { ThemeProvider } from 'emotion-theming'
import Helmet from 'react-helmet'
import '../styles/reset'
import theme, * as palette from '../styles/themeDark'
import config from '../utils/siteConfig'
import Wrapper from '../components/Wrapper'
import Menu from '../components/Menu'
import { injectGlobal } from 'emotion'

injectGlobal`
body {
  background: ${palette.BASE} !important;
  color: ${palette.SECONDARY} !important;
}
h1,h2,h3,h4,h5,h6,a,strong {
  color: ${palette.TERTIARY};
}
p {
  color: ${palette.SECONDARY};
}
a{
  transition: all 0.5s;
  color: ${palette.TERTIARY};
  &:hover {
  color: ${palette.HIGHLIGHT};
  }
}
svg {
  transition: all 0.5s;
  fill: ${palette.TERTIARY};
  &:hover {
    fill: ${palette.HIGHLIGHT};
  }
}
.bm-overlay, .bm-menu-wrap {
  background: ${palette.BASE} !important;
}
.bm-cross {
  background: ${palette.TERTIARY};
}
.bm-burger-bars {
  background: ${palette.TERTIARY};
}
`

const Layout = ({ children, location }) => {
  return (

      <Helmet>

        <title>{config.siteTitle}</title>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/logos/logo-512.png" />
        <link rel="apple-touch-icon" href="/logos/logo-512.png" />
        <meta name="description" content={config.siteDescription} />
        <meta property="og:title" content={config.siteTitle} />
        <meta property="og:url" content={config.siteUrl} />
        <meta property="og:locale" content="en_US" />
        <meta property="og:type" content="website" />
        <meta property="og:site_name" content={config.siteTitle} />
      </Helmet>

      <ThemeProvider theme={theme}>
        <Wrapper>

            <Menu />

              {children}

        </Wrapper>
      </ThemeProvider>

  )
}

export default Layout
<|endoftext|>


<commit_before>import React from 'react'
import { ThemeProvider } from 'emotion-theming'
import Helmet from 'react-helmet'
import '../styles/global'
import theme, * as palette from '../styles/themeDark'
import config from '../utils/siteConfig'
import Wrapper from '../components/Wrapper'
import Menu from '../components/Menu'
import Transition from '../components/Transition'

import { injectGlobal } from 'emotion'

injectGlobal`
body {
  background: ${palette.BASE} !important;
  color: ${palette.SECONDARY} !important;
}
h1,h2,h3,h4,h5,h6,a {
  color: ${palette.TERTIARY};
}
a{
  &:hover {
    color: ${palette.HIGHLIGHT} !important;
  }
}
`

const Layout = ({ children, location }) => {
  return (

      <Helmet>

        <title>{config.siteTitle}</title>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/logos/logo-512.png" />
        <link rel="apple-touch-icon" href="/logos/logo-512.png" />
        <meta name="description" content={config.siteDescription} />
        <meta property="og:title" content={config.siteTitle} />
        <meta property="og:url" content={config.siteUrl} />
        <meta property="og:locale" content="en_US" />
        <meta property="og:type" content="website" />
        <meta property="og:site_name" content={config.siteTitle} />
      </Helmet>

      <ThemeProvider theme={theme}>
        <Wrapper>
          <Menu />
          <Transition location={location}>

{children}

          </Transition>
        </Wrapper>
      </ThemeProvider>

  )
}

export default Layout
<commit_msg>Back to light theme<commit_after>import React from 'react'
import { ThemeProvider } from 'emotion-theming'
import Helmet from 'react-helmet'
import '../styles/global'
import theme, * as palette from '../styles/themeLight'
import config from '../utils/siteConfig'
import Wrapper from '../components/Wrapper'
import Menu from '../components/Menu'
import Transition from '../components/Transition'

import { injectGlobal } from 'emotion'

injectGlobal`
body {
  background: ${palette.BASE} !important;
  color: ${palette.SECONDARY} !important;
}
h1,h2,h3,h4,h5,h6,a {
  color: ${palette.TERTIARY};
}
a{
  &:hover {
    color: ${palette.HIGHLIGHT} !important;
  }
}
`

const Layout = ({ children, location }) => {
  return (

      <Helmet>

        <title>{config.siteTitle}</title>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/logos/logo-512.png" />
        <link rel="apple-touch-icon" href="/logos/logo-512.png" />
        <meta name="description" content={config.siteDescription} />
        <meta property="og:title" content={config.siteTitle} />
        <meta property="og:url" content={config.siteUrl} />
        <meta property="og:locale" content="en_US" />
        <meta property="og:type" content="website" />
        <meta property="og:site_name" content={config.siteTitle} />
      </Helmet>

      <ThemeProvider theme={theme}>
        <Wrapper>
          <Menu />
          <Transition location={location}>

{children}

          </Transition>
        </Wrapper>
      </ThemeProvider>

  )
}

export default Layout
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from '@blueprintjs/core';

import queryString from 'query-string';
import debounce from 'lodash/debounce';

import './Search.css';

class Search extends Component {
  constructor(props)  {
    super(props);

    const params = queryString.parse(props.location.search);

    this.state = {
      searchTerm: params.q || ''
    }

    this.onChange = this.onChange.bind(this);
    this.updateLocation = this.updateLocation.bind(this);

    this.debouncedUpdate = debounce(this.updateLocation, 250);
  }

  updateLocation(searchTerm) {
    const { history, location } = this.props;

    const params = queryString.parse(location.search);
    const newParams = { ...params, q: searchTerm };

    if (!searchTerm) {
      delete newParams.q;
    }

    history.push({
      pathname: location.pathname,
      search: queryString.stringify(newParams)
    });
  }

  componentWillUpdate(nextProps, { searchTerm }) {
    if (searchTerm !== this.state.searchTerm) {
      this.debouncedUpdate(searchTerm);
    }
  }

  onChange(e) {
    this.setState({
      searchTerm: e.target.value
    });
  }

  render() {
    const { searchTerm } = this.state;

    return (
      <form className="top">

          <input className="pt-input" type="search" onChange={this.onChange} value={searchTerm}/>

          <Button rightIconName="caret-down">
            <FormattedMessage id="search.collections" defaultMessage="Collections"/>
            {' '}(55)
          </Button>

      </form>
    );
  }
}

export default Search;
<commit_msg>Not a form<commit_after>import React, { Component } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from '@blueprintjs/core';

import queryString from 'query-string';
import debounce from 'lodash/debounce';

import './Search.css';

class Search extends Component {
  constructor(props)  {
    super(props);

    const params = queryString.parse(props.location.search);

    this.state = {
      searchTerm: params.q || ''
    }

    this.onChange = this.onChange.bind(this);
    this.updateLocation = this.updateLocation.bind(this);

    this.debouncedUpdate = debounce(this.updateLocation, 250);
  }

  updateLocation(searchTerm) {
    const { history, location } = this.props;

    const params = queryString.parse(location.search);
    const newParams = { ...params, q: searchTerm };

    if (!searchTerm) {
      delete newParams.q;
    }

    history.push({
      pathname: location.pathname,
      search: queryString.stringify(newParams)
    });
  }

  componentWillUpdate(nextProps, { searchTerm }) {
    if (searchTerm !== this.state.searchTerm) {
      this.debouncedUpdate(searchTerm);
    }
  }

  onChange(e) {
    this.setState({
      searchTerm: e.target.value
    });
  }

  render() {
    const { searchTerm } = this.state;

    return (

          <input className="pt-input" type="search" onChange={this.onChange} value={searchTerm}/>

          <Button rightIconName="caret-down">
            <FormattedMessage id="search.collections" defaultMessage="Collections"/>
            {' '}(55)
          </Button>

    );
  }
}

export default Search;
<|endoftext|>


<commit_before>var registerComponent = require('../core/component').registerComponent;
var utils = require('../utils/');

var bind = utils.bind;

var EVENTS = {
  CLICK: 'click',
  FUSING: 'fusing',
  MOUSEENTER: 'mouseenter',
  MOUSEDOWN: 'mousedown',
  MOUSELEAVE: 'mouseleave',
  MOUSEUP: 'mouseup'
};

var STATES = {
  FUSING: 'fusing',
  HOVERING: 'hovering',
  HOVERED: 'hovered'
};

/**
 * Cursor component. Applies the raycaster component specifically for starting the raycaster
 * from the camera and pointing from camera's facing direction, and then only returning the
 * closest intersection. Cursor can be fine-tuned by setting raycaster properties.
 *
 * @member {object} fuseTimeout - Timeout to trigger fuse-click.
 * @member {Element} cursorDownEl - Entity that was last mousedowned during current click.
 * @member {object} intersection - Attributes of the current intersection event, including
 *         3D- and 2D-space coordinates. See: http://threejs.org/docs/api/core/Raycaster.html
 * @member {Element} intersectedEl - Currently-intersected entity. Used to keep track to
 *         emit events when unintersecting.
 */
module.exports.Component = registerComponent('cursor', {
  dependencies: ['raycaster'],

  schema: {
    downEvents: {default: []},
    fuse: {default: utils.device.isMobile()},
    fuseTimeout: {default: 1500, min: 0},
    upEvents: {default: []}
  },

  init: function () {
    this.fuseTimeout = undefined;
    this.cursorDownEl = null;
    this.intersection = null;
    this.intersectedEl = null;

    // Bind methods.
    this.onCursorDown = bind(this.onCursorDown, this);
    this.onCursorUp = bind(this.onCursorUp, this);
    this.onIntersection = bind(this.onIntersection, this);
    this.onIntersectionCleared = bind(this.onIntersectionCleared, this);
  },

  play: function () {
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
  },

  remove: function () {
    var el = this.el;
    el.removeState(STATES.HOVERING);
    el.removeState(STATES.FUSING);
    clearTimeout(this.fuseTimeout);
    if (this.intersectedEl) { this.intersectedEl.removeState(STATES.HOVERED); }
    this.removeEventListeners();
  },

  addEventListeners: function () {
    var canvas;
    var data = this.data;
    var el = this.el;
    var self = this;

    canvas = el.sceneEl.canvas;
    if (canvas) {
      canvas.addEventListener('mousedown', this.onCursorDown);
      canvas.addEventListener('mouseup', this.onCursorUp);
    } else {
      el.sceneEl.addEventListener('render-target-loaded', function () {
        canvas = el.sceneEl.canvas;
        canvas.addEventListener('mousedown', self.onCursorDown);
        canvas.addEventListener('mouseup', self.onCursorUp);
      });
    }

    data.downEvents.forEach(function (downEvent) {
      el.addEventListener(downEvent, self.onCursorDown);
    });
    data.upEvents.forEach(function (upEvent) {
      el.addEventListener(upEvent, self.onCursorUp);
    });
    el.addEventListener('raycaster-intersection', this.onIntersection);
    el.addEventListener('raycaster-intersection-cleared', this.onIntersectionCleared);
  },

  removeEventListeners: function () {
    var canvas;
    var data = this.data;
    var el = this.el;
    var self = this;

    canvas = el.sceneEl.canvas;
    if (canvas) {
      canvas.removeEventListener('mousedown', this.onCursorDown);
      canvas.removeEventListener('mouseup', this.onCursorUp);
    }

    data.downEvents.forEach(function (downEvent) {
      el.removeEventListener(downEvent, self.onCursorDown);
    });
    data.upEvents.forEach(function (upEvent) {
      el.removeEventListener(upEvent, self.onCursorUp);
    });
    el.removeEventListener('raycaster-intersection', this.onIntersection);
    el.removeEventListener('raycaster-intersection-cleared', this.onIntersectionCleared);
  },

  /**
   * Trigger mousedown and keep track of the mousedowned entity.
   */
  onCursorDown: function (evt) {
    this.twoWayEmit(EVENTS.MOUSEDOWN);
    this.cursorDownEl = this.intersectedEl;
  },

  /**
   * Trigger mouseup if:
   * - Not fusing (mobile has no mouse).
   * - Currently intersecting an entity.
   * - Currently-intersected entity is the same as the one when mousedown was triggered,
   *   in case user mousedowned one entity, dragged to another, and mouseupped.
   */
  onCursorUp: function (evt) {
    this.twoWayEmit(EVENTS.MOUSEUP);

    // If intersected entity has changed since the cursorDown, still emit mouseUp on the
    // previously cursorUp entity.
    if (this.cursorDownEl && this.cursorDownEl !== this.intersectedEl) {
      this.cursorDownEl.emit(EVENTS.MOUSEUP, {cursorEl: this.el, intersection: null});
    }

    if (!this.data.fuse && this.intersectedEl && this.cursorDownEl === this.intersectedEl) {
      this.twoWayEmit(EVENTS.CLICK);
    }

    this.cursorDownEl = null;
  },

  /**
   * Handle intersection.
   */
  onIntersection: function (evt) {
    var self = this;
    var cursorEl = this.el;
    var data = this.data;
    var index;
    var intersectedEl;
    var intersection;

    // Select closest object, excluding the cursor.
    index = evt.detail.els[0] === cursorEl ? 1 : 0;
    intersection = evt.detail.intersections[index];
    intersectedEl = evt.detail.els[index];

    // If cursor is the only intersected object, ignore the event.
    if (!intersectedEl) { return; }

    // Already intersecting this entity.
    if (this.intersectedEl === intersectedEl) {
      this.intersection = intersection;
      return;
    }

    // Unset current intersection.
    if (this.intersectedEl) { this.clearCurrentIntersection(); }

    // Set new intersection.
    this.intersection = intersection;
    this.intersectedEl = intersectedEl;

    // Hovering.
    cursorEl.addState(STATES.HOVERING);
    intersectedEl.addState(STATES.HOVERED);
    self.twoWayEmit(EVENTS.MOUSEENTER);

    // Begin fuse if necessary.
    if (data.fuseTimeout === 0 || !data.fuse) { return; }
    cursorEl.addState(STATES.FUSING);
    this.twoWayEmit(EVENTS.FUSING);
    this.fuseTimeout = setTimeout(function fuse () {
      cursorEl.removeState(STATES.FUSING);
      self.twoWayEmit(EVENTS.CLICK);
    }, data.fuseTimeout);
  },

  /**
   * Handle intersection cleared.
   */
  onIntersectionCleared: function (evt) {
    var cursorEl = this.el;
    var intersectedEl = evt.detail.el;

    // Ignore the cursor.
    if (cursorEl === intersectedEl) { return; }

    // Ignore if the event didn't occur on the current intersection.
    if (intersectedEl !== this.intersectedEl) { return; }

    this.clearCurrentIntersection();
  },

  clearCurrentIntersection: function () {
    var cursorEl = this.el;

    // No longer hovering (or fusing).
    this.intersectedEl.removeState(STATES.HOVERED);
    cursorEl.removeState(STATES.HOVERING);
    cursorEl.removeState(STATES.FUSING);
    this.twoWayEmit(EVENTS.MOUSELEAVE);

    // Unset intersected entity (after emitting the event).
    this.intersection = null;
    this.intersectedEl = null;

    // Clear fuseTimeout.
    clearTimeout(this.fuseTimeout);
  },

  /**
   * Helper to emit on both the cursor and the intersected entity (if exists).
   */
  twoWayEmit: function (evtName) {
    var el = this.el;
    var intersectedEl = this.intersectedEl;
    var intersection = this.intersection;
    el.emit(evtName, {intersectedEl: intersectedEl, intersection: intersection});
    if (!intersectedEl) { return; }
    intersectedEl.emit(evtName, {cursorEl: el, intersection: intersection});
  }
});
<commit_msg>revert cursor state name changes from laser-controls PR for backward compatibility<commit_after>var registerComponent = require('../core/component').registerComponent;
var utils = require('../utils/');

var bind = utils.bind;

var EVENTS = {
  CLICK: 'click',
  FUSING: 'fusing',
  MOUSEENTER: 'mouseenter',
  MOUSEDOWN: 'mousedown',
  MOUSELEAVE: 'mouseleave',
  MOUSEUP: 'mouseup'
};

var STATES = {
  FUSING: 'cursor-fusing',
  HOVERING: 'cursor-hovering',
  HOVERED: 'cursor-hovered'
};

/**
 * Cursor component. Applies the raycaster component specifically for starting the raycaster
 * from the camera and pointing from camera's facing direction, and then only returning the
 * closest intersection. Cursor can be fine-tuned by setting raycaster properties.
 *
 * @member {object} fuseTimeout - Timeout to trigger fuse-click.
 * @member {Element} cursorDownEl - Entity that was last mousedowned during current click.
 * @member {object} intersection - Attributes of the current intersection event, including
 *         3D- and 2D-space coordinates. See: http://threejs.org/docs/api/core/Raycaster.html
 * @member {Element} intersectedEl - Currently-intersected entity. Used to keep track to
 *         emit events when unintersecting.
 */
module.exports.Component = registerComponent('cursor', {
  dependencies: ['raycaster'],

  schema: {
    downEvents: {default: []},
    fuse: {default: utils.device.isMobile()},
    fuseTimeout: {default: 1500, min: 0},
    upEvents: {default: []}
  },

  init: function () {
    this.fuseTimeout = undefined;
    this.cursorDownEl = null;
    this.intersection = null;
    this.intersectedEl = null;

    // Bind methods.
    this.onCursorDown = bind(this.onCursorDown, this);
    this.onCursorUp = bind(this.onCursorUp, this);
    this.onIntersection = bind(this.onIntersection, this);
    this.onIntersectionCleared = bind(this.onIntersectionCleared, this);
  },

  play: function () {
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
  },

  remove: function () {
    var el = this.el;
    el.removeState(STATES.HOVERING);
    el.removeState(STATES.FUSING);
    clearTimeout(this.fuseTimeout);
    if (this.intersectedEl) { this.intersectedEl.removeState(STATES.HOVERED); }
    this.removeEventListeners();
  },

  addEventListeners: function () {
    var canvas;
    var data = this.data;
    var el = this.el;
    var self = this;

    canvas = el.sceneEl.canvas;
    if (canvas) {
      canvas.addEventListener('mousedown', this.onCursorDown);
      canvas.addEventListener('mouseup', this.onCursorUp);
    } else {
      el.sceneEl.addEventListener('render-target-loaded', function () {
        canvas = el.sceneEl.canvas;
        canvas.addEventListener('mousedown', self.onCursorDown);
        canvas.addEventListener('mouseup', self.onCursorUp);
      });
    }

    data.downEvents.forEach(function (downEvent) {
      el.addEventListener(downEvent, self.onCursorDown);
    });
    data.upEvents.forEach(function (upEvent) {
      el.addEventListener(upEvent, self.onCursorUp);
    });
    el.addEventListener('raycaster-intersection', this.onIntersection);
    el.addEventListener('raycaster-intersection-cleared', this.onIntersectionCleared);
  },

  removeEventListeners: function () {
    var canvas;
    var data = this.data;
    var el = this.el;
    var self = this;

    canvas = el.sceneEl.canvas;
    if (canvas) {
      canvas.removeEventListener('mousedown', this.onCursorDown);
      canvas.removeEventListener('mouseup', this.onCursorUp);
    }

    data.downEvents.forEach(function (downEvent) {
      el.removeEventListener(downEvent, self.onCursorDown);
    });
    data.upEvents.forEach(function (upEvent) {
      el.removeEventListener(upEvent, self.onCursorUp);
    });
    el.removeEventListener('raycaster-intersection', this.onIntersection);
    el.removeEventListener('raycaster-intersection-cleared', this.onIntersectionCleared);
  },

  /**
   * Trigger mousedown and keep track of the mousedowned entity.
   */
  onCursorDown: function (evt) {
    this.twoWayEmit(EVENTS.MOUSEDOWN);
    this.cursorDownEl = this.intersectedEl;
  },

  /**
   * Trigger mouseup if:
   * - Not fusing (mobile has no mouse).
   * - Currently intersecting an entity.
   * - Currently-intersected entity is the same as the one when mousedown was triggered,
   *   in case user mousedowned one entity, dragged to another, and mouseupped.
   */
  onCursorUp: function (evt) {
    this.twoWayEmit(EVENTS.MOUSEUP);

    // If intersected entity has changed since the cursorDown, still emit mouseUp on the
    // previously cursorUp entity.
    if (this.cursorDownEl && this.cursorDownEl !== this.intersectedEl) {
      this.cursorDownEl.emit(EVENTS.MOUSEUP, {cursorEl: this.el, intersection: null});
    }

    if (!this.data.fuse && this.intersectedEl && this.cursorDownEl === this.intersectedEl) {
      this.twoWayEmit(EVENTS.CLICK);
    }

    this.cursorDownEl = null;
  },

  /**
   * Handle intersection.
   */
  onIntersection: function (evt) {
    var self = this;
    var cursorEl = this.el;
    var data = this.data;
    var index;
    var intersectedEl;
    var intersection;

    // Select closest object, excluding the cursor.
    index = evt.detail.els[0] === cursorEl ? 1 : 0;
    intersection = evt.detail.intersections[index];
    intersectedEl = evt.detail.els[index];

    // If cursor is the only intersected object, ignore the event.
    if (!intersectedEl) { return; }

    // Already intersecting this entity.
    if (this.intersectedEl === intersectedEl) {
      this.intersection = intersection;
      return;
    }

    // Unset current intersection.
    if (this.intersectedEl) { this.clearCurrentIntersection(); }

    // Set new intersection.
    this.intersection = intersection;
    this.intersectedEl = intersectedEl;

    // Hovering.
    cursorEl.addState(STATES.HOVERING);
    intersectedEl.addState(STATES.HOVERED);
    self.twoWayEmit(EVENTS.MOUSEENTER);

    // Begin fuse if necessary.
    if (data.fuseTimeout === 0 || !data.fuse) { return; }
    cursorEl.addState(STATES.FUSING);
    this.twoWayEmit(EVENTS.FUSING);
    this.fuseTimeout = setTimeout(function fuse () {
      cursorEl.removeState(STATES.FUSING);
      self.twoWayEmit(EVENTS.CLICK);
    }, data.fuseTimeout);
  },

  /**
   * Handle intersection cleared.
   */
  onIntersectionCleared: function (evt) {
    var cursorEl = this.el;
    var intersectedEl = evt.detail.el;

    // Ignore the cursor.
    if (cursorEl === intersectedEl) { return; }

    // Ignore if the event didn't occur on the current intersection.
    if (intersectedEl !== this.intersectedEl) { return; }

    this.clearCurrentIntersection();
  },

  clearCurrentIntersection: function () {
    var cursorEl = this.el;

    // No longer hovering (or fusing).
    this.intersectedEl.removeState(STATES.HOVERED);
    cursorEl.removeState(STATES.HOVERING);
    cursorEl.removeState(STATES.FUSING);
    this.twoWayEmit(EVENTS.MOUSELEAVE);

    // Unset intersected entity (after emitting the event).
    this.intersection = null;
    this.intersectedEl = null;

    // Clear fuseTimeout.
    clearTimeout(this.fuseTimeout);
  },

  /**
   * Helper to emit on both the cursor and the intersected entity (if exists).
   */
  twoWayEmit: function (evtName) {
    var el = this.el;
    var intersectedEl = this.intersectedEl;
    var intersection = this.intersection;
    el.emit(evtName, {intersectedEl: intersectedEl, intersection: intersection});
    if (!intersectedEl) { return; }
    intersectedEl.emit(evtName, {cursorEl: el, intersection: intersection});
  }
});
<|endoftext|>