<commit_before>import React, {PropTypes, PureComponent} from 'react';
import {Animated, Easing, PanResponder, StyleSheet, View} from 'react-native';

function noop() {}

const animationFnPropType = PropTypes.oneOf([
  Animated.decay,
  Animated.spring,
  Animated.timing
]);

export default class Swipeable extends PureComponent {

  static propTypes = {
    // elements
    children: PropTypes.any,
    leftContent: PropTypes.any,
    rightContent: PropTypes.any,
    leftButtons: PropTypes.array,
    rightButtons: PropTypes.array,

    // left action lifecycle
    onLeftActionActivate: PropTypes.func,
    onLeftActionDeactivate: PropTypes.func,
    onLeftActionRelease: PropTypes.func,
    onLeftActionComplete: PropTypes.func,
    leftActionActivationDistance: PropTypes.number,
    leftActionReleaseAnimationConfig: PropTypes.object,
    leftActionReleaseAnimationFn: animationFnPropType,

    // right action lifecycle
    onRightActionActivate: PropTypes.func,
    onRightActionDeactivate: PropTypes.func,
    onRightActionRelease: PropTypes.func,
    onRightActionComplete: PropTypes.func,
    rightActionActivationDistance: PropTypes.number,
    rightActionReleaseAnimationFn: animationFnPropType,
    rightActionReleaseAnimationConfig: PropTypes.object,

    // left buttons lifecycle
    onLeftButtonsOpenActivate: PropTypes.func,
    onLeftButtonsOpenDeactivate: PropTypes.func,
    onLeftButtonsOpenRelease: PropTypes.func,
    onLeftButtonsOpenComplete: PropTypes.func,
    leftButtonWidth: PropTypes.number,
    leftButtonsOpenActivationDistance: PropTypes.number,
    leftButtonsOpenReleaseAnimationConfig: PropTypes.object,
    leftButtonsOpenReleaseAnimationFn: animationFnPropType,

    // right buttons lifecycle
    onRightButtonsOpenActivate: PropTypes.func,
    onRightButtonsOpenDeactivate: PropTypes.func,
    onRightButtonsOpenComplete: PropTypes.func,
    onRightButtonsOpenRelease: PropTypes.func,
    rightButtonWidth: PropTypes.number,
    rightButtonsOpenActivationDistance: PropTypes.number,
    rightButtonsOpenReleaseAnimationConfig: PropTypes.object,
    rightButtonsOpenReleaseAnimationFn: animationFnPropType,

    // base swipe lifecycle
    onSwipeStart: PropTypes.func,
    onSwipeMove: PropTypes.func,
    onSwipeRelease: PropTypes.func,
    onSwipeComplete: PropTypes.func,
    swipeReleaseAnimationFn: animationFnPropType,
    swipeReleaseAnimationConfig: PropTypes.object,

    // misc
    onRef: PropTypes.func,
    onPanAnimatedValueRef: PropTypes.func,
    swipeStartMinDistance: PropTypes.number,

    // styles
    style: View.propTypes.style,
    leftContainerStyle: View.propTypes.style,
    leftButtonContainerStyle: View.propTypes.style,
    rightContainerStyle: View.propTypes.style,
    rightButtonContainerStyle: View.propTypes.style,
    contentContainerStyle: View.propTypes.style
  };

  static defaultProps = {
    leftContent: null,
    rightContent: null,
    leftButtons: null,
    rightButtons: null,

    // left action lifecycle
    onLeftActionActivate: noop,
    onLeftActionDeactivate: noop,
    onLeftActionRelease: noop,
    onLeftActionComplete: noop,
    leftActionActivationDistance: 125,
    leftActionReleaseAnimationFn: null,
    leftActionReleaseAnimationConfig: null,

    // right action lifecycle
    onRightActionActivate: noop,
    onRightActionDeactivate: noop,
    onRightActionRelease: noop,
    onRightActionComplete: noop,
    rightActionActivationDistance: 125,
    rightActionReleaseAnimationFn: null,
    rightActionReleaseAnimationConfig: null,

    // left buttons lifecycle
    onLeftButtonsOpenActivate: noop,
    onLeftButtonsOpenDeactivate: noop,
    onLeftButtonsOpenRelease: noop,
    onLeftButtonsOpenComplete: noop,
    leftButtonWidth: 75,
    leftButtonsOpenActivationDistance: 75,
    leftButtonsOpenReleaseAnimationFn: null,
    leftButtonsOpenReleaseAnimationConfig: null,

    // right buttons lifecycle
    onRightButtonsOpenActivate: noop,
    onRightButtonsOpenDeactivate: noop,
    onRightButtonsOpenRelease: noop,
    onRightButtonsOpenComplete: noop,
    rightButtonWidth: 75,
    rightButtonsOpenActivationDistance: 75,
    rightButtonsOpenReleaseAnimationFn: null,
    rightButtonsOpenReleaseAnimationConfig: null,

    // base swipe lifecycle
    onSwipeStart: noop,
    onSwipeMove: noop,
    onSwipeRelease: noop,
    onSwipeComplete: noop,
    swipeReleaseAnimationFn: Animated.timing,
    swipeReleaseAnimationConfig: {
      toValue: {x: 0, y: 0},
      duration: 250,
      easing: Easing.elastic(0.5)
    },

    // misc
    onRef: noop,
    onPanAnimatedValueRef: noop,
    swipeStartMinDistance: 10
  };

  state = {
    pan: new Animated.ValueXY(),
    width: 0,
    lastOffset: {x: 0, y: 0},
    leftActionActivated: false,
    leftButtonsOpenActivated: false,
    rightActionActivated: false,
    rightButtonsOpenActivated: false
  };

  componentWillMount() {
    const {onPanAnimatedValueRef, onRef} = this.props;

    onRef(this);
    onPanAnimatedValueRef(this.state.pan);
  }

  componentWillUnmount() {
    this._unmounted = true;
  }

  recenter = (
    animationFn = this.props.swipeReleaseAnimationFn,
    animationConfig = this.props.swipeReleaseAnimationConfig,
    onDone
  ) => {
    const {pan} = this.state;

    this.setState({
      lastOffset: {x: 0, y: 0},
      leftActionActivated: false,
      leftButtonsOpenActivated: false,
      rightActionActivated: false,
      rightButtonsOpenActivated: false
    });

    pan.flattenOffset();

    animationFn(pan, animationConfig).start(onDone);
  };

  _unmounted = false;

  _handlePan = Animated.event([null, {
    dx: this.state.pan.x,
    dy: this.state.pan.y
  }]);

  _handleMoveShouldSetPanResponder = (event, gestureState) => (
    Math.abs(gestureState.dx) > this.props.swipeStartMinDistance
  );

  _handlePanResponderStart = (event, gestureState) => {
    const {lastOffset, pan} = this.state;

    pan.setOffset(lastOffset);
    this.props.onSwipeStart(event, gestureState);
  };

  _handlePanResponderMove = (event, gestureState) => {
    const {
      leftActionActivationDistance,
      leftButtonsOpenActivationDistance,
      onLeftActionActivate,
      onLeftActionDeactivate,
      onLeftButtonsOpenActivate,
      onLeftButtonsOpenDeactivate,
      rightActionActivationDistance,
      rightButtonsOpenActivationDistance,
      onRightActionActivate,
      onRightActionDeactivate,
      onRightButtonsOpenActivate,
      onRightButtonsOpenDeactivate
    } = this.props;
    const {
      lastOffset,
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated
    } = this.state;
    const {dx, vx} = gestureState;
    const x = dx + lastOffset.x;
    const canSwipeRight = this._canSwipeRight();
    const canSwipeLeft = this._canSwipeLeft();
    const hasLeftButtons = this._hasLeftButtons();
    const hasRightButtons = this._hasRightButtons();
    const isSwipingLeft = vx < 0;
    const isSwipingRight = vx > 0;
    let nextLeftActionActivated = leftActionActivated;
    let nextLeftButtonsOpenActivated = leftButtonsOpenActivated;
    let nextRightActionActivated = rightActionActivated;
    let nextRightButtonsOpenActivated = rightButtonsOpenActivated;

    this._handlePan(event, gestureState);
    this.props.onSwipeMove(event, gestureState);

    if (!leftActionActivated && canSwipeRight && x >= leftActionActivationDistance) {
      nextLeftActionActivated = true;
      onLeftActionActivate(event, gestureState);
    }

    if (leftActionActivated && canSwipeRight && x < leftActionActivationDistance) {
      nextLeftActionActivated = false;
      onLeftActionDeactivate(event, gestureState);
    }

    if (!rightActionActivated && canSwipeLeft && x <= -rightActionActivationDistance) {
      nextRightActionActivated = true;
      onRightActionActivate(event, gestureState);
    }

    if (rightActionActivated && canSwipeLeft && x > -rightActionActivationDistance) {
      nextRightActionActivated = false;
      onRightActionDeactivate(event, gestureState);
    }

    if (!leftButtonsOpenActivated && hasLeftButtons && !isSwipingLeft && x >= leftButtonsOpenActivationDistance) {
      nextLeftButtonsOpenActivated = true;
      onLeftButtonsOpenActivate(event, gestureState);
    }

    if (leftButtonsOpenActivated && hasLeftButtons && isSwipingLeft) {
      nextLeftButtonsOpenActivated = false;
      onLeftButtonsOpenDeactivate(event, gestureState);
    }

    if (!rightButtonsOpenActivated && hasRightButtons && !isSwipingRight && x <= -rightButtonsOpenActivationDistance) {
      nextRightButtonsOpenActivated = true;
      onRightButtonsOpenActivate(event, gestureState);
    }

    if (rightButtonsOpenActivated && hasRightButtons && isSwipingRight) {
      nextRightButtonsOpenActivated = false;
      onRightButtonsOpenDeactivate(event, gestureState);
    }

    const needsUpdate =
      nextLeftActionActivated !== leftActionActivated ||
      nextLeftButtonsOpenActivated !== leftButtonsOpenActivated ||
      nextRightActionActivated !== rightActionActivated ||
      nextRightButtonsOpenActivated !== rightButtonsOpenActivated;

    if (needsUpdate) {
      this.setState({
        leftActionActivated: nextLeftActionActivated,
        leftButtonsOpenActivated: nextLeftButtonsOpenActivated,
        rightActionActivated: nextRightActionActivated,
        rightButtonsOpenActivated: nextRightButtonsOpenActivated
      });
    }
  };

  _handlePanResponderEnd = (event, gestureState) => {
    const {
      onLeftButtonsOpenRelease,
      onRightButtonsOpenRelease,
      onLeftActionRelease,
      onRightActionRelease,
      onSwipeRelease
    } = this.props;
    const {
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated,
      pan
    } = this.state;
    const animationFn = this._getReleaseAnimationFn();
    const animationConfig = this._getReleaseAnimationConfig();

    onSwipeRelease(event, gestureState);

    if (leftActionActivated) {
      onLeftActionRelease(event, gestureState);
    }

    if (leftButtonsOpenActivated) {
      onLeftButtonsOpenRelease(event, gestureState);
    }

    if (rightActionActivated) {
      onRightActionRelease(event, gestureState);
    }

    if (rightButtonsOpenActivated) {
      onRightButtonsOpenRelease(event, gestureState);
    }

    this.setState({
      lastOffset: {x: animationConfig.toValue.x, y: animationConfig.toValue.y},
      leftActionActivated: false,
      rightActionActivated: false
    });

    pan.flattenOffset();

    animationFn(pan, animationConfig).start(animationInfo => {
      if (this._unmounted) {
        return;
      }

      const {
        onLeftButtonsOpenComplete,
        onRightButtonsOpenComplete,
        onLeftActionComplete,
        onRightActionComplete,
        onSwipeComplete
      } = this.props;

      onSwipeComplete(animationInfo);

      if (leftActionActivated) {
        onLeftActionComplete(animationInfo);
      }

      if (leftButtonsOpenActivated) {
        onLeftButtonsOpenComplete(event, gestureState);
      }

      if (rightActionActivated) {
        onRightActionComplete(animationInfo);
      }

      if (rightButtonsOpenActivated) {
        onRightButtonsOpenComplete(event, gestureState);
      }
    });
  };

  _panResponder = PanResponder.create({
    onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
    onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponder,
    onPanResponderGrant: this._handlePanResponderStart,
    onPanResponderMove: this._handlePanResponderMove,
    onPanResponderRelease: this._handlePanResponderEnd,
    onPanResponderTerminate: this._handlePanResponderEnd,
    onPanResponderTerminationRequest: this._handlePanResponderEnd
  });

  _handleLayout = ({nativeEvent: {layout: {width}}}) => this.setState({width});

  _canSwipeRight() {
    return this.props.leftContent || this._hasLeftButtons();
  }

  _canSwipeLeft() {
    return this.props.rightContent || this._hasRightButtons();
  }

  _hasLeftButtons() {
    const {leftButtons, leftContent} = this.props;

    return !leftContent && leftButtons && leftButtons.length;
  }

  _hasRightButtons() {
    const {rightButtons, rightContent} = this.props;

    return !rightContent && rightButtons && rightButtons.length;
  }

  _getReleaseAnimationFn() {
    const {
      leftActionReleaseAnimationFn,
      leftButtonsOpenReleaseAnimationFn,
      rightActionReleaseAnimationFn,
      rightButtonsOpenReleaseAnimationFn,
      swipeReleaseAnimationFn
    } = this.props;
    const {
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated
    } = this.state;

    if (leftActionActivated && leftActionReleaseAnimationFn) {
      return leftActionReleaseAnimationFn;
    }

    if (rightActionActivated && rightActionReleaseAnimationFn) {
      return rightActionReleaseAnimationFn;
    }

    if (leftButtonsOpenActivated && leftButtonsOpenReleaseAnimationFn) {
      return leftButtonsOpenReleaseAnimationFn;
    }

    if (rightButtonsOpenActivated && rightButtonsOpenReleaseAnimationFn) {
      return rightButtonsOpenReleaseAnimationFn;
    }

    return swipeReleaseAnimationFn;
  }

  _getReleaseAnimationConfig() {
    const {
      leftActionReleaseAnimationConfig,
      leftButtons,
      leftButtonsOpenReleaseAnimationConfig,
      leftButtonWidth,
      rightActionReleaseAnimationConfig,
      rightButtons,
      rightButtonsOpenReleaseAnimationConfig,
      rightButtonWidth,
      swipeReleaseAnimationConfig
    } = this.props;
    const {
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated
    } = this.state;

    if (leftActionActivated && leftActionReleaseAnimationConfig) {
      return leftActionReleaseAnimationConfig;
    }

    if (rightActionActivated && rightActionReleaseAnimationConfig) {
      return rightActionReleaseAnimationConfig;
    }

    if (leftButtonsOpenActivated) {
      return {
        ...swipeReleaseAnimationConfig,
        toValue: {
          x: leftButtons.length * leftButtonWidth,
          y: 0
        },
        ...leftButtonsOpenReleaseAnimationConfig
      };
    }

    if (rightButtonsOpenActivated) {
      return {
        ...swipeReleaseAnimationConfig,
        toValue: {
          x: rightButtons.length * rightButtonWidth * -1,
          y: 0
        },
        ...rightButtonsOpenReleaseAnimationConfig
      };
    }

    return swipeReleaseAnimationConfig;
  }

  _renderButtons(buttons, isLeftButtons) {
    const {leftButtonContainerStyle, rightButtonContainerStyle} = this.props;
    const {pan, width} = this.state;
    const canSwipeLeft = this._canSwipeLeft();
    const canSwipeRight = this._canSwipeRight();
    const count = buttons.length;
    const inputRange = isLeftButtons ?
      [0, canSwipeRight ? width : 0] :
      [canSwipeLeft ? -width : 0, 0];

    return buttons.map((buttonContent, index) => {
      const outputMultiplier = -index / count;
      const outputRange = isLeftButtons ?
        [0, (canSwipeRight ? width : 0) * outputMultiplier] :
        [(canSwipeLeft ? -width : 0) * outputMultiplier, 0];

      const buttonStyle = [
        StyleSheet.absoluteFill,
        {
          width,
          transform: [{
            translateX: pan.x.interpolate({
              inputRange,
              outputRange,
              extrapolate: 'clamp'
            })
          }]
        },
        isLeftButtons ? leftButtonContainerStyle : rightButtonContainerStyle
      ];

      return (
        <Animated.View key={index} style={buttonStyle}>
          {buttonContent}
        </Animated.View>
      );
    });
  }

  render() {
    const {
      children,
      contentContainerStyle,
      leftButtons,
      leftContainerStyle,
      leftContent,
      rightButtons,
      rightContainerStyle,
      rightContent,
      style,
      ...props
    } = this.props;
    const {pan, width} = this.state;
    const canSwipeLeft = this._canSwipeLeft();
    const canSwipeRight = this._canSwipeRight();
    const containerStyle = [
      {
        transform: [{
          translateX: pan.x.interpolate({
            inputRange: [canSwipeLeft ? -width : 0, canSwipeRight ? width : 0],
            outputRange: [
              canSwipeLeft ? -width + StyleSheet.hairlineWidth : 0,
              canSwipeRight ? width - StyleSheet.hairlineWidth : 0
            ],
            extrapolate: 'clamp'
          })
        }]
      },
      styles.container,
      style
    ];

    return (
      <Animated.View onLayout={this._handleLayout} style={containerStyle} {...this._panResponder.panHandlers} {...props}>
        {canSwipeRight && (
          <View style={[{marginLeft: -width, width}, leftContainerStyle]}>
            {leftContent || this._renderButtons(leftButtons, true)}
          </View>
        )}
        <View style={[styles.content, contentContainerStyle]}>{children}</View>
        {canSwipeLeft && (
          <View style={[{marginRight: -width, width}, rightContainerStyle]}>
            {rightContent || this._renderButtons(rightButtons, false)}
          </View>
        )}
      </Animated.View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row'
  },
  content: {
    flex: 1
  }
});
<commit_msg>destructure onSwipeMove<commit_after>import React, {PropTypes, PureComponent} from 'react';
import {Animated, Easing, PanResponder, StyleSheet, View} from 'react-native';

function noop() {}

const animationFnPropType = PropTypes.oneOf([
  Animated.decay,
  Animated.spring,
  Animated.timing
]);

export default class Swipeable extends PureComponent {

  static propTypes = {
    // elements
    children: PropTypes.any,
    leftContent: PropTypes.any,
    rightContent: PropTypes.any,
    leftButtons: PropTypes.array,
    rightButtons: PropTypes.array,

    // left action lifecycle
    onLeftActionActivate: PropTypes.func,
    onLeftActionDeactivate: PropTypes.func,
    onLeftActionRelease: PropTypes.func,
    onLeftActionComplete: PropTypes.func,
    leftActionActivationDistance: PropTypes.number,
    leftActionReleaseAnimationConfig: PropTypes.object,
    leftActionReleaseAnimationFn: animationFnPropType,

    // right action lifecycle
    onRightActionActivate: PropTypes.func,
    onRightActionDeactivate: PropTypes.func,
    onRightActionRelease: PropTypes.func,
    onRightActionComplete: PropTypes.func,
    rightActionActivationDistance: PropTypes.number,
    rightActionReleaseAnimationFn: animationFnPropType,
    rightActionReleaseAnimationConfig: PropTypes.object,

    // left buttons lifecycle
    onLeftButtonsOpenActivate: PropTypes.func,
    onLeftButtonsOpenDeactivate: PropTypes.func,
    onLeftButtonsOpenRelease: PropTypes.func,
    onLeftButtonsOpenComplete: PropTypes.func,
    leftButtonWidth: PropTypes.number,
    leftButtonsOpenActivationDistance: PropTypes.number,
    leftButtonsOpenReleaseAnimationConfig: PropTypes.object,
    leftButtonsOpenReleaseAnimationFn: animationFnPropType,

    // right buttons lifecycle
    onRightButtonsOpenActivate: PropTypes.func,
    onRightButtonsOpenDeactivate: PropTypes.func,
    onRightButtonsOpenComplete: PropTypes.func,
    onRightButtonsOpenRelease: PropTypes.func,
    rightButtonWidth: PropTypes.number,
    rightButtonsOpenActivationDistance: PropTypes.number,
    rightButtonsOpenReleaseAnimationConfig: PropTypes.object,
    rightButtonsOpenReleaseAnimationFn: animationFnPropType,

    // base swipe lifecycle
    onSwipeStart: PropTypes.func,
    onSwipeMove: PropTypes.func,
    onSwipeRelease: PropTypes.func,
    onSwipeComplete: PropTypes.func,
    swipeReleaseAnimationFn: animationFnPropType,
    swipeReleaseAnimationConfig: PropTypes.object,

    // misc
    onRef: PropTypes.func,
    onPanAnimatedValueRef: PropTypes.func,
    swipeStartMinDistance: PropTypes.number,

    // styles
    style: View.propTypes.style,
    leftContainerStyle: View.propTypes.style,
    leftButtonContainerStyle: View.propTypes.style,
    rightContainerStyle: View.propTypes.style,
    rightButtonContainerStyle: View.propTypes.style,
    contentContainerStyle: View.propTypes.style
  };

  static defaultProps = {
    leftContent: null,
    rightContent: null,
    leftButtons: null,
    rightButtons: null,

    // left action lifecycle
    onLeftActionActivate: noop,
    onLeftActionDeactivate: noop,
    onLeftActionRelease: noop,
    onLeftActionComplete: noop,
    leftActionActivationDistance: 125,
    leftActionReleaseAnimationFn: null,
    leftActionReleaseAnimationConfig: null,

    // right action lifecycle
    onRightActionActivate: noop,
    onRightActionDeactivate: noop,
    onRightActionRelease: noop,
    onRightActionComplete: noop,
    rightActionActivationDistance: 125,
    rightActionReleaseAnimationFn: null,
    rightActionReleaseAnimationConfig: null,

    // left buttons lifecycle
    onLeftButtonsOpenActivate: noop,
    onLeftButtonsOpenDeactivate: noop,
    onLeftButtonsOpenRelease: noop,
    onLeftButtonsOpenComplete: noop,
    leftButtonWidth: 75,
    leftButtonsOpenActivationDistance: 75,
    leftButtonsOpenReleaseAnimationFn: null,
    leftButtonsOpenReleaseAnimationConfig: null,

    // right buttons lifecycle
    onRightButtonsOpenActivate: noop,
    onRightButtonsOpenDeactivate: noop,
    onRightButtonsOpenRelease: noop,
    onRightButtonsOpenComplete: noop,
    rightButtonWidth: 75,
    rightButtonsOpenActivationDistance: 75,
    rightButtonsOpenReleaseAnimationFn: null,
    rightButtonsOpenReleaseAnimationConfig: null,

    // base swipe lifecycle
    onSwipeStart: noop,
    onSwipeMove: noop,
    onSwipeRelease: noop,
    onSwipeComplete: noop,
    swipeReleaseAnimationFn: Animated.timing,
    swipeReleaseAnimationConfig: {
      toValue: {x: 0, y: 0},
      duration: 250,
      easing: Easing.elastic(0.5)
    },

    // misc
    onRef: noop,
    onPanAnimatedValueRef: noop,
    swipeStartMinDistance: 10
  };

  state = {
    pan: new Animated.ValueXY(),
    width: 0,
    lastOffset: {x: 0, y: 0},
    leftActionActivated: false,
    leftButtonsOpenActivated: false,
    rightActionActivated: false,
    rightButtonsOpenActivated: false
  };

  componentWillMount() {
    const {onPanAnimatedValueRef, onRef} = this.props;

    onRef(this);
    onPanAnimatedValueRef(this.state.pan);
  }

  componentWillUnmount() {
    this._unmounted = true;
  }

  recenter = (
    animationFn = this.props.swipeReleaseAnimationFn,
    animationConfig = this.props.swipeReleaseAnimationConfig,
    onDone
  ) => {
    const {pan} = this.state;

    this.setState({
      lastOffset: {x: 0, y: 0},
      leftActionActivated: false,
      leftButtonsOpenActivated: false,
      rightActionActivated: false,
      rightButtonsOpenActivated: false
    });

    pan.flattenOffset();

    animationFn(pan, animationConfig).start(onDone);
  };

  _unmounted = false;

  _handlePan = Animated.event([null, {
    dx: this.state.pan.x,
    dy: this.state.pan.y
  }]);

  _handleMoveShouldSetPanResponder = (event, gestureState) => (
    Math.abs(gestureState.dx) > this.props.swipeStartMinDistance
  );

  _handlePanResponderStart = (event, gestureState) => {
    const {lastOffset, pan} = this.state;

    pan.setOffset(lastOffset);
    this.props.onSwipeStart(event, gestureState);
  };

  _handlePanResponderMove = (event, gestureState) => {
    const {
      leftActionActivationDistance,
      leftButtonsOpenActivationDistance,
      onLeftActionActivate,
      onLeftActionDeactivate,
      onLeftButtonsOpenActivate,
      onLeftButtonsOpenDeactivate,
      rightActionActivationDistance,
      rightButtonsOpenActivationDistance,
      onRightActionActivate,
      onRightActionDeactivate,
      onRightButtonsOpenActivate,
      onRightButtonsOpenDeactivate,
      onSwipeMove
    } = this.props;
    const {
      lastOffset,
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated
    } = this.state;
    const {dx, vx} = gestureState;
    const x = dx + lastOffset.x;
    const canSwipeRight = this._canSwipeRight();
    const canSwipeLeft = this._canSwipeLeft();
    const hasLeftButtons = this._hasLeftButtons();
    const hasRightButtons = this._hasRightButtons();
    const isSwipingLeft = vx < 0;
    const isSwipingRight = vx > 0;
    let nextLeftActionActivated = leftActionActivated;
    let nextLeftButtonsOpenActivated = leftButtonsOpenActivated;
    let nextRightActionActivated = rightActionActivated;
    let nextRightButtonsOpenActivated = rightButtonsOpenActivated;

    this._handlePan(event, gestureState);
    onSwipeMove(event, gestureState);

    if (!leftActionActivated && canSwipeRight && x >= leftActionActivationDistance) {
      nextLeftActionActivated = true;
      onLeftActionActivate(event, gestureState);
    }

    if (leftActionActivated && canSwipeRight && x < leftActionActivationDistance) {
      nextLeftActionActivated = false;
      onLeftActionDeactivate(event, gestureState);
    }

    if (!rightActionActivated && canSwipeLeft && x <= -rightActionActivationDistance) {
      nextRightActionActivated = true;
      onRightActionActivate(event, gestureState);
    }

    if (rightActionActivated && canSwipeLeft && x > -rightActionActivationDistance) {
      nextRightActionActivated = false;
      onRightActionDeactivate(event, gestureState);
    }

    if (!leftButtonsOpenActivated && hasLeftButtons && !isSwipingLeft && x >= leftButtonsOpenActivationDistance) {
      nextLeftButtonsOpenActivated = true;
      onLeftButtonsOpenActivate(event, gestureState);
    }

    if (leftButtonsOpenActivated && hasLeftButtons && isSwipingLeft) {
      nextLeftButtonsOpenActivated = false;
      onLeftButtonsOpenDeactivate(event, gestureState);
    }

    if (!rightButtonsOpenActivated && hasRightButtons && !isSwipingRight && x <= -rightButtonsOpenActivationDistance) {
      nextRightButtonsOpenActivated = true;
      onRightButtonsOpenActivate(event, gestureState);
    }

    if (rightButtonsOpenActivated && hasRightButtons && isSwipingRight) {
      nextRightButtonsOpenActivated = false;
      onRightButtonsOpenDeactivate(event, gestureState);
    }

    const needsUpdate =
      nextLeftActionActivated !== leftActionActivated ||
      nextLeftButtonsOpenActivated !== leftButtonsOpenActivated ||
      nextRightActionActivated !== rightActionActivated ||
      nextRightButtonsOpenActivated !== rightButtonsOpenActivated;

    if (needsUpdate) {
      this.setState({
        leftActionActivated: nextLeftActionActivated,
        leftButtonsOpenActivated: nextLeftButtonsOpenActivated,
        rightActionActivated: nextRightActionActivated,
        rightButtonsOpenActivated: nextRightButtonsOpenActivated
      });
    }
  };

  _handlePanResponderEnd = (event, gestureState) => {
    const {
      onLeftButtonsOpenRelease,
      onRightButtonsOpenRelease,
      onLeftActionRelease,
      onRightActionRelease,
      onSwipeRelease
    } = this.props;
    const {
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated,
      pan
    } = this.state;
    const animationFn = this._getReleaseAnimationFn();
    const animationConfig = this._getReleaseAnimationConfig();

    onSwipeRelease(event, gestureState);

    if (leftActionActivated) {
      onLeftActionRelease(event, gestureState);
    }

    if (leftButtonsOpenActivated) {
      onLeftButtonsOpenRelease(event, gestureState);
    }

    if (rightActionActivated) {
      onRightActionRelease(event, gestureState);
    }

    if (rightButtonsOpenActivated) {
      onRightButtonsOpenRelease(event, gestureState);
    }

    this.setState({
      lastOffset: {x: animationConfig.toValue.x, y: animationConfig.toValue.y},
      leftActionActivated: false,
      rightActionActivated: false
    });

    pan.flattenOffset();

    animationFn(pan, animationConfig).start(animationInfo => {
      if (this._unmounted) {
        return;
      }

      const {
        onLeftButtonsOpenComplete,
        onRightButtonsOpenComplete,
        onLeftActionComplete,
        onRightActionComplete,
        onSwipeComplete
      } = this.props;

      onSwipeComplete(animationInfo);

      if (leftActionActivated) {
        onLeftActionComplete(animationInfo);
      }

      if (leftButtonsOpenActivated) {
        onLeftButtonsOpenComplete(event, gestureState);
      }

      if (rightActionActivated) {
        onRightActionComplete(animationInfo);
      }

      if (rightButtonsOpenActivated) {
        onRightButtonsOpenComplete(event, gestureState);
      }
    });
  };

  _panResponder = PanResponder.create({
    onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
    onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponder,
    onPanResponderGrant: this._handlePanResponderStart,
    onPanResponderMove: this._handlePanResponderMove,
    onPanResponderRelease: this._handlePanResponderEnd,
    onPanResponderTerminate: this._handlePanResponderEnd,
    onPanResponderTerminationRequest: this._handlePanResponderEnd
  });

  _handleLayout = ({nativeEvent: {layout: {width}}}) => this.setState({width});

  _canSwipeRight() {
    return this.props.leftContent || this._hasLeftButtons();
  }

  _canSwipeLeft() {
    return this.props.rightContent || this._hasRightButtons();
  }

  _hasLeftButtons() {
    const {leftButtons, leftContent} = this.props;

    return !leftContent && leftButtons && leftButtons.length;
  }

  _hasRightButtons() {
    const {rightButtons, rightContent} = this.props;

    return !rightContent && rightButtons && rightButtons.length;
  }

  _getReleaseAnimationFn() {
    const {
      leftActionReleaseAnimationFn,
      leftButtonsOpenReleaseAnimationFn,
      rightActionReleaseAnimationFn,
      rightButtonsOpenReleaseAnimationFn,
      swipeReleaseAnimationFn
    } = this.props;
    const {
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated
    } = this.state;

    if (leftActionActivated && leftActionReleaseAnimationFn) {
      return leftActionReleaseAnimationFn;
    }

    if (rightActionActivated && rightActionReleaseAnimationFn) {
      return rightActionReleaseAnimationFn;
    }

    if (leftButtonsOpenActivated && leftButtonsOpenReleaseAnimationFn) {
      return leftButtonsOpenReleaseAnimationFn;
    }

    if (rightButtonsOpenActivated && rightButtonsOpenReleaseAnimationFn) {
      return rightButtonsOpenReleaseAnimationFn;
    }

    return swipeReleaseAnimationFn;
  }

  _getReleaseAnimationConfig() {
    const {
      leftActionReleaseAnimationConfig,
      leftButtons,
      leftButtonsOpenReleaseAnimationConfig,
      leftButtonWidth,
      rightActionReleaseAnimationConfig,
      rightButtons,
      rightButtonsOpenReleaseAnimationConfig,
      rightButtonWidth,
      swipeReleaseAnimationConfig
    } = this.props;
    const {
      leftActionActivated,
      leftButtonsOpenActivated,
      rightActionActivated,
      rightButtonsOpenActivated
    } = this.state;

    if (leftActionActivated && leftActionReleaseAnimationConfig) {
      return leftActionReleaseAnimationConfig;
    }

    if (rightActionActivated && rightActionReleaseAnimationConfig) {
      return rightActionReleaseAnimationConfig;
    }

    if (leftButtonsOpenActivated) {
      return {
        ...swipeReleaseAnimationConfig,
        toValue: {
          x: leftButtons.length * leftButtonWidth,
          y: 0
        },
        ...leftButtonsOpenReleaseAnimationConfig
      };
    }

    if (rightButtonsOpenActivated) {
      return {
        ...swipeReleaseAnimationConfig,
        toValue: {
          x: rightButtons.length * rightButtonWidth * -1,
          y: 0
        },
        ...rightButtonsOpenReleaseAnimationConfig
      };
    }

    return swipeReleaseAnimationConfig;
  }

  _renderButtons(buttons, isLeftButtons) {
    const {leftButtonContainerStyle, rightButtonContainerStyle} = this.props;
    const {pan, width} = this.state;
    const canSwipeLeft = this._canSwipeLeft();
    const canSwipeRight = this._canSwipeRight();
    const count = buttons.length;
    const inputRange = isLeftButtons ?
      [0, canSwipeRight ? width : 0] :
      [canSwipeLeft ? -width : 0, 0];

    return buttons.map((buttonContent, index) => {
      const outputMultiplier = -index / count;
      const outputRange = isLeftButtons ?
        [0, (canSwipeRight ? width : 0) * outputMultiplier] :
        [(canSwipeLeft ? -width : 0) * outputMultiplier, 0];

      const buttonStyle = [
        StyleSheet.absoluteFill,
        {
          width,
          transform: [{
            translateX: pan.x.interpolate({
              inputRange,
              outputRange,
              extrapolate: 'clamp'
            })
          }]
        },
        isLeftButtons ? leftButtonContainerStyle : rightButtonContainerStyle
      ];

      return (
        <Animated.View key={index} style={buttonStyle}>
          {buttonContent}
        </Animated.View>
      );
    });
  }

  render() {
    const {
      children,
      contentContainerStyle,
      leftButtons,
      leftContainerStyle,
      leftContent,
      rightButtons,
      rightContainerStyle,
      rightContent,
      style,
      ...props
    } = this.props;
    const {pan, width} = this.state;
    const canSwipeLeft = this._canSwipeLeft();
    const canSwipeRight = this._canSwipeRight();
    const containerStyle = [
      {
        transform: [{
          translateX: pan.x.interpolate({
            inputRange: [canSwipeLeft ? -width : 0, canSwipeRight ? width : 0],
            outputRange: [
              canSwipeLeft ? -width + StyleSheet.hairlineWidth : 0,
              canSwipeRight ? width - StyleSheet.hairlineWidth : 0
            ],
            extrapolate: 'clamp'
          })
        }]
      },
      styles.container,
      style
    ];

    return (
      <Animated.View onLayout={this._handleLayout} style={containerStyle} {...this._panResponder.panHandlers} {...props}>
        {canSwipeRight && (
          <View style={[{marginLeft: -width, width}, leftContainerStyle]}>
            {leftContent || this._renderButtons(leftButtons, true)}
          </View>
        )}
        <View style={[styles.content, contentContainerStyle]}>{children}</View>
        {canSwipeLeft && (
          <View style={[{marginRight: -width, width}, rightContainerStyle]}>
            {rightContent || this._renderButtons(rightButtons, false)}
          </View>
        )}
      </Animated.View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row'
  },
  content: {
    flex: 1
  }
});
<|endoftext|>


<commit_before>'use strict'

const Swarm = require('libp2p-swarm')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const PeerBook = require('peer-book')
const multiaddr = require('multiaddr')
const mafmt = require('mafmt')
const EE = require('events').EventEmitter
const assert = require('assert')
const Ping = require('libp2p-ping')

exports = module.exports

const OFFLINE_ERROR_MESSAGE = 'The libp2p node is not started yet'
const IPFS_CODE = 421

class Node {
  constructor (_modules, _peerInfo, _peerBook, _options) {
    assert(_modules, 'requires modules to equip libp2p with features')
    assert(_peerInfo, 'requires a PeerInfo instance')

    this.modules = _modules
    this.peerInfo = _peerInfo
    this.peerBook = _peerBook || new PeerBook()
    this.isOnline = false

    this.discovery = new EE()

    this.swarm = new Swarm(this.peerInfo)

    // Attach stream multiplexers
    if (this.modules.connection.muxer) {
      let muxers = this.modules.connection.muxer
      muxers = Array.isArray(muxers) ? muxers : [muxers]
      muxers.forEach((muxer) => {
        this.swarm.connection.addStreamMuxer(muxer)
      })

      // If muxer exists, we can use Identify
      this.swarm.connection.reuse()

      // Received incommind dial and muxer upgrade happened, reuse this
      // muxed connection
      this.swarm.on('peer-mux-established', (peerInfo) => {
        this.peerBook.put(peerInfo)
      })

      this.swarm.on('peer-mux-closed', (peerInfo) => {
        this.peerBook.removeByB58String(peerInfo.id.toB58String())
      })
    }

    // Attach crypto channels
    if (this.modules.connection.crypto) {
      let cryptos = this.modules.connection.crypto
      cryptos = Array.isArray(cryptos) ? cryptos : [cryptos]
      cryptos.forEach((crypto) => {
        this.swarm.connection.crypto(crypto.tag, crypto.encrypt)
      })
    }

    // Attach discovery mechanisms
    if (this.modules.discovery) {
      let discoveries = this.modules.discovery
      discoveries = Array.isArray(discoveries) ? discoveries : [discoveries]
      discoveries.forEach((discovery) => {
        discovery.on('peer', (peerInfo) => {
          this.discovery.emit('peer', peerInfo)
        })
      })
    }

    // Mount default protocols
    Ping.mount(this.swarm)

    // Not fully implemented in js-libp2p yet
    this.routing = undefined
    this.records = undefined
  }

  /*
   * Start the libp2p node
   *   - create listeners on the multiaddrs the Peer wants to listen
   */
  start (callback) {
    if (!this.modules.transport) {
      return callback(new Error('no transports were present'))
    }

    let ws
    let transports = this.modules.transport

    transports = Array.isArray(transports) ? transports : [transports]
    const multiaddrs = this.peerInfo.multiaddrs

    transports.forEach((transport) => {
      if (transport.filter(multiaddrs).length > 0) {
        this.swarm.transport.add(
          transport.tag || transport.constructor.name, transport)
      } else if (transport.constructor &&
                 transport.constructor.name === 'WebSockets') {
        // TODO find a cleaner way to signal that a transport is always
        // used for dialing, even if no listener
        ws = transport
      }
    })

    this.swarm.listen((err) => {
      if (err) {
        return callback(err)
      }
      if (ws) {
        this.swarm.transport.add(ws.tag || ws.constructor.name, ws)
      }

      this.isOnline = true

      if (this.modules.discovery) {
        this.modules.discovery.forEach((discovery) => {
          setImmediate(() => discovery.start(() => {}))
        })
      }

      callback()
    })
  }

  /*
   * Stop the libp2p node by closing its listeners and open connections
   */
  stop (callback) {
    this.isOnline = false

    if (this.modules.discovery) {
      this.modules.discovery.forEach((discovery) => {
        console.log(discovery)
        setImmediate(() => discovery.stop(() => {}))
      })
    }

    this.swarm.close(callback)
  }

  //
  // Ping
  //

  // TODO
  pingById (id, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
    callback(new Error('not implemented yet'))
  }

  // TODO
  pingByMultiaddr (maddr, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
    callback(new Error('not implemented yet'))
  }

  pingByPeerInfo (peerInfo, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
    callback(null, new Ping(this.swarm, peerInfo))
  }

  //
  // Dialing methods
  //

  // TODO
  dialById (id, protocol, callback) {
    // NOTE: dialById only works if a previous dial was made. This will
    // change once we have PeerRouting

    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof protocol === 'function') {
      callback = protocol
      protocol = undefined
    }

    callback(new Error('not implemented yet'))
  }

  dialByMultiaddr (maddr, protocol, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof protocol === 'function') {
      callback = protocol
      protocol = undefined
    }

    if (typeof maddr === 'string') {
      maddr = multiaddr(maddr)
    }

    if (!mafmt.IPFS.matches(maddr.toString())) {
      return callback(new Error('multiaddr not valid'))
    }

    const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
      if (tuple[0] === IPFS_CODE) {
        return true
      }
    })[0][1]

    let peer
    try {
      peer = this.peerBook.getByB58String(ipfsIdB58String)
    } catch (err) {
      peer = new PeerInfo(PeerId.createFromB58String(ipfsIdB58String))
    }

    peer.multiaddr.add(maddr)
    this.dialByPeerInfo(peer, protocol, callback)
  }

  dialByPeerInfo (peer, protocol, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof protocol === 'function') {
      callback = protocol
      protocol = undefined
    }

    this.swarm.dial(peer, protocol, (err, conn) => {
      if (err) {
        return callback(err)
      }
      this.peerBook.put(peer)
      callback(null, conn)
    })
  }

  //
  // Disconnecting (hangUp) methods
  //

  hangUpById (id, callback) {
    // TODO
    callback(new Error('not implemented yet'))
  }

  hangUpByMultiaddr (maddr, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof maddr === 'string') {
      maddr = multiaddr(maddr)
    }

    if (!mafmt.IPFS.matches(maddr.toString())) {
      return callback(new Error('multiaddr not valid'))
    }

    const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
      if (tuple[0] === IPFS_CODE) {
        return true
      }
    })[0][1]

    try {
      const pi = this.peerBook.getByB58String(ipfsIdB58String)
      this.hangUpByPeerInfo(pi, callback)
    } catch (err) {
      // already disconnected
      callback()
    }
  }

  hangUpByPeerInfo (peer, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    this.peerBook.removeByB58String(peer.id.toB58String())
    this.swarm.hangUp(peer, callback)
  }

  //
  // Protocol multiplexing handling
  //

  handle (protocol, handlerFunc, matchFunc) {
    this.swarm.handle(protocol, handlerFunc, matchFunc)
  }

  unhandle (protocol) {
    this.swarm.unhandle(protocol)
  }
}

module.exports = Node
<commit_msg>fix: remove unnecessary log<commit_after>'use strict'

const Swarm = require('libp2p-swarm')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const PeerBook = require('peer-book')
const multiaddr = require('multiaddr')
const mafmt = require('mafmt')
const EE = require('events').EventEmitter
const assert = require('assert')
const Ping = require('libp2p-ping')

exports = module.exports

const OFFLINE_ERROR_MESSAGE = 'The libp2p node is not started yet'
const IPFS_CODE = 421

class Node {
  constructor (_modules, _peerInfo, _peerBook, _options) {
    assert(_modules, 'requires modules to equip libp2p with features')
    assert(_peerInfo, 'requires a PeerInfo instance')

    this.modules = _modules
    this.peerInfo = _peerInfo
    this.peerBook = _peerBook || new PeerBook()
    this.isOnline = false

    this.discovery = new EE()

    this.swarm = new Swarm(this.peerInfo)

    // Attach stream multiplexers
    if (this.modules.connection.muxer) {
      let muxers = this.modules.connection.muxer
      muxers = Array.isArray(muxers) ? muxers : [muxers]
      muxers.forEach((muxer) => {
        this.swarm.connection.addStreamMuxer(muxer)
      })

      // If muxer exists, we can use Identify
      this.swarm.connection.reuse()

      // Received incommind dial and muxer upgrade happened, reuse this
      // muxed connection
      this.swarm.on('peer-mux-established', (peerInfo) => {
        this.peerBook.put(peerInfo)
      })

      this.swarm.on('peer-mux-closed', (peerInfo) => {
        this.peerBook.removeByB58String(peerInfo.id.toB58String())
      })
    }

    // Attach crypto channels
    if (this.modules.connection.crypto) {
      let cryptos = this.modules.connection.crypto
      cryptos = Array.isArray(cryptos) ? cryptos : [cryptos]
      cryptos.forEach((crypto) => {
        this.swarm.connection.crypto(crypto.tag, crypto.encrypt)
      })
    }

    // Attach discovery mechanisms
    if (this.modules.discovery) {
      let discoveries = this.modules.discovery
      discoveries = Array.isArray(discoveries) ? discoveries : [discoveries]
      discoveries.forEach((discovery) => {
        discovery.on('peer', (peerInfo) => {
          this.discovery.emit('peer', peerInfo)
        })
      })
    }

    // Mount default protocols
    Ping.mount(this.swarm)

    // Not fully implemented in js-libp2p yet
    this.routing = undefined
    this.records = undefined
  }

  /*
   * Start the libp2p node
   *   - create listeners on the multiaddrs the Peer wants to listen
   */
  start (callback) {
    if (!this.modules.transport) {
      return callback(new Error('no transports were present'))
    }

    let ws
    let transports = this.modules.transport

    transports = Array.isArray(transports) ? transports : [transports]
    const multiaddrs = this.peerInfo.multiaddrs

    transports.forEach((transport) => {
      if (transport.filter(multiaddrs).length > 0) {
        this.swarm.transport.add(
          transport.tag || transport.constructor.name, transport)
      } else if (transport.constructor &&
                 transport.constructor.name === 'WebSockets') {
        // TODO find a cleaner way to signal that a transport is always
        // used for dialing, even if no listener
        ws = transport
      }
    })

    this.swarm.listen((err) => {
      if (err) {
        return callback(err)
      }
      if (ws) {
        this.swarm.transport.add(ws.tag || ws.constructor.name, ws)
      }

      this.isOnline = true

      if (this.modules.discovery) {
        this.modules.discovery.forEach((discovery) => {
          setImmediate(() => discovery.start(() => {}))
        })
      }

      callback()
    })
  }

  /*
   * Stop the libp2p node by closing its listeners and open connections
   */
  stop (callback) {
    this.isOnline = false

    if (this.modules.discovery) {
      this.modules.discovery.forEach((discovery) => {
        setImmediate(() => discovery.stop(() => {}))
      })
    }

    this.swarm.close(callback)
  }

  //
  // Ping
  //

  // TODO
  pingById (id, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
    callback(new Error('not implemented yet'))
  }

  // TODO
  pingByMultiaddr (maddr, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
    callback(new Error('not implemented yet'))
  }

  pingByPeerInfo (peerInfo, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)
    callback(null, new Ping(this.swarm, peerInfo))
  }

  //
  // Dialing methods
  //

  // TODO
  dialById (id, protocol, callback) {
    // NOTE: dialById only works if a previous dial was made. This will
    // change once we have PeerRouting

    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof protocol === 'function') {
      callback = protocol
      protocol = undefined
    }

    callback(new Error('not implemented yet'))
  }

  dialByMultiaddr (maddr, protocol, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof protocol === 'function') {
      callback = protocol
      protocol = undefined
    }

    if (typeof maddr === 'string') {
      maddr = multiaddr(maddr)
    }

    if (!mafmt.IPFS.matches(maddr.toString())) {
      return callback(new Error('multiaddr not valid'))
    }

    const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
      if (tuple[0] === IPFS_CODE) {
        return true
      }
    })[0][1]

    let peer
    try {
      peer = this.peerBook.getByB58String(ipfsIdB58String)
    } catch (err) {
      peer = new PeerInfo(PeerId.createFromB58String(ipfsIdB58String))
    }

    peer.multiaddr.add(maddr)
    this.dialByPeerInfo(peer, protocol, callback)
  }

  dialByPeerInfo (peer, protocol, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof protocol === 'function') {
      callback = protocol
      protocol = undefined
    }

    this.swarm.dial(peer, protocol, (err, conn) => {
      if (err) {
        return callback(err)
      }
      this.peerBook.put(peer)
      callback(null, conn)
    })
  }

  //
  // Disconnecting (hangUp) methods
  //

  hangUpById (id, callback) {
    // TODO
    callback(new Error('not implemented yet'))
  }

  hangUpByMultiaddr (maddr, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    if (typeof maddr === 'string') {
      maddr = multiaddr(maddr)
    }

    if (!mafmt.IPFS.matches(maddr.toString())) {
      return callback(new Error('multiaddr not valid'))
    }

    const ipfsIdB58String = maddr.stringTuples().filter((tuple) => {
      if (tuple[0] === IPFS_CODE) {
        return true
      }
    })[0][1]

    try {
      const pi = this.peerBook.getByB58String(ipfsIdB58String)
      this.hangUpByPeerInfo(pi, callback)
    } catch (err) {
      // already disconnected
      callback()
    }
  }

  hangUpByPeerInfo (peer, callback) {
    assert(this.isOnline, OFFLINE_ERROR_MESSAGE)

    this.peerBook.removeByB58String(peer.id.toB58String())
    this.swarm.hangUp(peer, callback)
  }

  //
  // Protocol multiplexing handling
  //

  handle (protocol, handlerFunc, matchFunc) {
    this.swarm.handle(protocol, handlerFunc, matchFunc)
  }

  unhandle (protocol) {
    this.swarm.unhandle(protocol)
  }
}

module.exports = Node
<|endoftext|>


<commit_before>var express = require('express');
var fs = require('fs');
var morgan = require('morgan');
var bodyParser = require('body-parser');
var helmet = require('helmet');

// Config variables.
var VROOM_PATH = '';
var MAX_JOB_NUMBER = 50;
var ROUTE_GEOMETRY = false;
var USE_LIBOSRM = false;
var OSRM_ADDRESS = "0.0.0.0";
var OSRM_PORT = "5000";
var ALLOW_OPTIONS_OVERRIDE = true; // -g only so far.
var LOG_DIRNAME = __dirname + '/..';

// App and loaded modules.
var app = express();

// Enable cross-origin ressource sharing.
app.use(function(req, res, next){
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers",
             "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.use(bodyParser.json());

var accessLogStream = fs.createWriteStream(LOG_DIRNAME + '/access.log',
                                           {flags: 'a'});
app.use(morgan('combined', {stream: accessLogStream}));

app.use(helmet());

// Simple date generator for console output.
var now = function(){
  var date = new Date();
  return date.toUTCString();
}

// Callback for size and some input validity checks.
var sizeCheckCallback = function(maxJobNumber){
  return function (req, res, next){
    res.setHeader('Content-Type', 'application/json');

    var correctInput = ('jobs' in req.body)
        && ('vehicles' in req.body)
        && (req.body['vehicles'].length >= 1);

    if(!correctInput){
      res.send({code: 1, error: 'Invalid query.'});
      return;
    }

    if(req.body['jobs'].length > maxJobNumber){
      console.log(now()
                  + '\n' + 'Too many jobs in query ('
                  + req.body['jobs'].length + ')');
      res.send({code: 1, error: 'Too many jobs.'});
      return;
    }
    next();
  }
}

// Cli wrapper and associated callback.
var spawn = require('child_process').spawn;

var vroomCommand = VROOM_PATH + 'vroom';
var options = [];
if(USE_LIBOSRM){
  options.push('-l');
}
else{
  options.push('-a', OSRM_ADDRESS);
  options.push('-p', OSRM_PORT);
}
if(ROUTE_GEOMETRY){
  options.push('-g');
}

// As of v5.1.0, profile name doesn't matter, so car will do until it
// should be made into a variable.
options.push('-m', 'car');

var execCallback = function (req, res){
  var reqOptions = options.slice();
  if(!ROUTE_GEOMETRY && ALLOW_OPTIONS_OVERRIDE
     && 'options' in req.body && 'g' in req.body['options']
     && req.body['options']['g']){
    reqOptions.push('-g');
  }

  reqOptions.push(JSON.stringify(req.body));
  var vroom = spawn(vroomCommand, reqOptions);

  vroom.stdout.pipe(res);

  vroom.stderr.on('data', function (data){
    console.log(now() + '\n' + data.toString());
  });
}

app.post('/', [sizeCheckCallback(MAX_JOB_NUMBER), execCallback]);

app.listen(3000, function (){
  console.log('vroom-express listening on port 3000!');
});
<commit_msg>Avoid server crash and return explicit error on unfound command. Fixes #4<commit_after>var express = require('express');
var fs = require('fs');
var morgan = require('morgan');
var bodyParser = require('body-parser');
var helmet = require('helmet');

// Config variables.
var VROOM_PATH = '';
var MAX_JOB_NUMBER = 50;
var ROUTE_GEOMETRY = false;
var USE_LIBOSRM = false;
var OSRM_ADDRESS = "0.0.0.0";
var OSRM_PORT = "5000";
var ALLOW_OPTIONS_OVERRIDE = true; // -g only so far.
var LOG_DIRNAME = __dirname + '/..';

// App and loaded modules.
var app = express();

// Enable cross-origin ressource sharing.
app.use(function(req, res, next){
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers",
             "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.use(bodyParser.json());

var accessLogStream = fs.createWriteStream(LOG_DIRNAME + '/access.log',
                                           {flags: 'a'});
app.use(morgan('combined', {stream: accessLogStream}));

app.use(helmet());

// Simple date generator for console output.
var now = function(){
  var date = new Date();
  return date.toUTCString();
}

// Callback for size and some input validity checks.
var sizeCheckCallback = function(maxJobNumber){
  return function (req, res, next){
    res.setHeader('Content-Type', 'application/json');

    var correctInput = ('jobs' in req.body)
        && ('vehicles' in req.body)
        && (req.body['vehicles'].length >= 1);

    if(!correctInput){
      res.send({code: 1, error: 'Invalid query.'});
      return;
    }

    if(req.body['jobs'].length > maxJobNumber){
      console.log(now()
                  + '\n' + 'Too many jobs in query ('
                  + req.body['jobs'].length + ')');
      res.send({code: 1, error: 'Too many jobs.'});
      return;
    }
    next();
  }
}

// Cli wrapper and associated callback.
var spawn = require('child_process').spawn;

var vroomCommand = VROOM_PATH + 'vroom';
var options = [];
if(USE_LIBOSRM){
  options.push('-l');
}
else{
  options.push('-a', OSRM_ADDRESS);
  options.push('-p', OSRM_PORT);
}
if(ROUTE_GEOMETRY){
  options.push('-g');
}

// As of v5.1.0, profile name doesn't matter, so car will do until it
// should be made into a variable.
options.push('-m', 'car');

var execCallback = function (req, res){
  var reqOptions = options.slice();
  if(!ROUTE_GEOMETRY && ALLOW_OPTIONS_OVERRIDE
     && 'options' in req.body && 'g' in req.body['options']
     && req.body['options']['g']){
    reqOptions.push('-g');
  }

  reqOptions.push(JSON.stringify(req.body));
  var vroom = spawn(vroomCommand, reqOptions);

  vroom.on('error', function(err){
    console.log(now() + '\n' + err);
    res.send({code: 1, error: 'Unfound command: ' + vroomCommand});
  });

  vroom.stdout.pipe(res);

  vroom.stderr.on('data', function (data){
    console.log(now() + '\n' + data.toString());
  });
}

app.post('/', [sizeCheckCallback(MAX_JOB_NUMBER), execCallback]);

app.listen(3000, function (){
  console.log('vroom-express listening on port 3000!');
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { ListView, RefreshControl } from 'react-native';

const { array, func } = PropTypes;

class ScrollableList extends Component {
  static propTypes = {
    data: array.isRequired,
    renderRow: func.isRequired,
  };

  constructor(props) {
    super(props);

    const { data, onRefresh, ...other } = props;
    this.otherProps = other;
    this.onRefresh = onRefresh;

    this.refreshEvent = this.refreshEvent.bind(this);

    this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
    this.state = {
      dataSource: this.ds.cloneWithRows(data),
      refreshing: false,
    };
  }

  componentWillReceiveProps(props) {
    if (this.props.data !== props.data) {
      this.setState({
        // If props.data == null, set an empty array instead
        dataSource: this.ds.cloneWithRows(props.data || []),
      });
    }
  }

  refreshEvent() {
    this.setState({ refreshing: true });

    if (typeof this.onRefresh() === 'object' &&
      typeof this.onRefresh().then === 'function') {
      this.onRefresh().then(this.setState({ refreshing: false }));
    } else {
      this.onRefresh();
      this.setState({ refreshing: false });
    }
  }

  renderRefreshControl() {
    return (
      <RefreshControl
        refreshing={this.state.refreshing}
        onRefresh={this.refreshEvent}
      />);
  }

  render() {
    return (
      <ListView
        {...this.otherProps}
        dataSource={this.state.dataSource}
        refreshControl={this.onRefresh ? this.renderRefreshControl() : null}
      />
    );
  }
}

export default ScrollableList;
<commit_msg>Fix misleading comment<commit_after>import React, { Component, PropTypes } from 'react';
import { ListView, RefreshControl } from 'react-native';

const { array, func } = PropTypes;

class ScrollableList extends Component {
  static propTypes = {
    data: array.isRequired,
    renderRow: func.isRequired,
  };

  constructor(props) {
    super(props);

    const { data, onRefresh, ...other } = props;
    this.otherProps = other;
    this.onRefresh = onRefresh;

    this.refreshEvent = this.refreshEvent.bind(this);

    this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
    this.state = {
      dataSource: this.ds.cloneWithRows(data),
      refreshing: false,
    };
  }

  componentWillReceiveProps(props) {
    if (this.props.data !== props.data) {
      this.setState({
        dataSource: this.ds.cloneWithRows(props.data || []),
      });
    }
  }

  refreshEvent() {
    this.setState({ refreshing: true });

    if (typeof this.onRefresh() === 'object' &&
      typeof this.onRefresh().then === 'function') {
      this.onRefresh().then(this.setState({ refreshing: false }));
    } else {
      this.onRefresh();
      this.setState({ refreshing: false });
    }
  }

  renderRefreshControl() {
    return (
      <RefreshControl
        refreshing={this.state.refreshing}
        onRefresh={this.refreshEvent}
      />);
  }

  render() {
    return (
      <ListView
        {...this.otherProps}
        dataSource={this.state.dataSource}
        refreshControl={this.onRefresh ? this.renderRefreshControl() : null}
      />
    );
  }
}

export default ScrollableList;
<|endoftext|>


<commit_before>const Boot = require('./lib/Boot');
const RestService = require('./lib/decorators/RestService');
const Remote = require('./lib/decorators/Remote');

exports.Boot = Boot;
exports.RestService = RestService;
exports.Remote = Remote;
<commit_msg>expose utils<commit_after>const Boot = require('./lib/Boot');
const RestService = require('./lib/decorators/RestService');
const Remote = require('./lib/decorators/Remote');
const fsUtil = require('./utils/fsUtil');
const jsUtil = require('./utils/jsUtil');
const objectUtil = require('./utils/objectUtil');

exports.Boot = Boot;
exports.RestService = RestService;
exports.Remote = Remote;

exports.Utils = {
  fs: fsUtil,
  js: jsUtil,
  object: objectUtil
};<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var request = require('request');

var tap = function (func) {
  return function (value) {
    func.apply(null, arguments);
    return value;
  };
};

var cronofy = function (config) {
  if (!this || this.constructor !== cronofy) {
    throw new Error('Cronofy must be called as a constructor');
  }

  this.config = config;

  this.urls = {
    api: 'https://api' + (config.dataCenter ? '-' + config.dataCenter : '') + '.cronofy.com'
  };
};

cronofy.prototype._httpCall = function (method, path, options, callback, optionsToOmit) {
  var settings = {
    method: method,
    url: path,
    gzip: true,
    json: true,
    headers: {
      Authorization: 'Bearer ' + (options.access_token || options.bearer_token),
      'Content-Type': 'application/json',
      'User-Agent': 'Cronofy Node'
    }
  };

  if (method === 'GET') {
    settings['qs'] = _.omit(options, optionsToOmit || ['access_token']);
  } else {
    settings['body'] = _.omit(options, optionsToOmit || ['access_token']);
  }

  return new Promise(function (resolve, reject) {
    request(settings, function (error, result, body) {
      if (error || result.statusCode >= 400) {
        var err = new Error(JSON.stringify(body));

        if (result && result.statusCode) {
          err.statusCode = result.statusCode;
        }

        err.error = {
          url: path,
          entity: body
        };

        if (callback) {
          callback(err);
        } else {
          reject(err);
        }
      } else {
        if (callback) {
          callback(null, body);
        } else {
          resolve(body);
        }
      }
    });
  });
};

cronofy.prototype._parseArguments = function (args, configDefaults) {
  var parsed = {options: {}, callback: null};

  if (args.length === 2) {
    parsed.options = args[0];
    parsed.callback = args[1];
  } else {
    switch (typeof args[0]) {
      case 'object':
        parsed.options = args[0];
        break;
      case 'function':
        parsed.callback = args[0];
        break;
    }
  }

  for (var i = 0; i < configDefaults.length; i++) {
    var key = configDefaults[i];

    parsed.options[key] = parsed.options[key] || this.config[key];
  }

  return parsed;
};

cronofy.prototype._httpGet = function (path, options, callback, optionsToOmit) {
  return this._httpCall('GET', this.urls.api + path, options, callback, optionsToOmit);
};

cronofy.prototype._httpPost = function (path, options, callback, optionsToOmit) {
  return this._httpCall('POST', this.urls.api + path, options, callback, optionsToOmit);
};

cronofy.prototype._httpDelete = function (path, options, callback, optionsToOmit) {
  return this._httpCall('DELETE', this.urls.api + path, options, callback, optionsToOmit);
};

cronofy.prototype.accountInformation = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/account', details.options, details.callback);
};

cronofy.prototype.addToCalendar = function () {
  var details = this._parseArguments(arguments, ['client_id', 'client_secret']);

  return this._httpPost('/v1/add_to_calendar', details.options, details.callback);
};

cronofy.prototype.realTimeScheduling = function () {
  var details = this._parseArguments(arguments, ['client_id', 'client_secret']);

  return this._httpPost('/v1/real_time_scheduling', details.options, details.callback);
};

cronofy.prototype.authorizeWithServiceAccount = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/service_account_authorizations', details.options, details.callback);
};

cronofy.prototype.availability = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/availability', details.options, details.callback);
};

cronofy.prototype.createEvent = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/calendars/' + details.options.calendar_id + '/events', details.options, details.callback, ['access_token', 'calendar_id']);
};

cronofy.prototype.createNotificationChannel = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/channels', details.options, details.callback);
};

cronofy.prototype.deleteEvent = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpDelete('/v1/calendars/' + details.options.calendar_id + '/events', details.options, details.callback, ['access_token', 'calendar_id']);
};

cronofy.prototype.deleteExternalEvent = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpDelete('/v1/calendars/' + details.options.calendar_id + '/events', details.options, details.callback, ['access_token', 'calendar_id']);
};

cronofy.prototype.deleteNotificationChannel = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpDelete('/v1/channels/' + details.options.channel_id, details.options, details.callback, ['access_token', 'channel_id']);
};

cronofy.prototype.elevatedPermissions = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/permissions', details.options, details.callback);
};

cronofy.prototype.freeBusy = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet(details.options.next_page || '/v1/free_busy', details.options, details.callback);
};

cronofy.prototype.listCalendars = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/calendars', details.options, details.callback);
};

cronofy.prototype.createCalendar = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/calendars', details.options, details.callback);
};

cronofy.prototype.listNotificationChannels = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/channels', details.options, details.callback);
};

cronofy.prototype.profileInformation = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/profiles', details.options, details.callback);
};

cronofy.prototype.readEvents = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  if (details.options.next_page) {
    return this._httpCall('GET', details.options.next_page, details.options, details.callback, ['access_token', 'next_page']);
  }

  return this._httpGet('/v1/events', details.options, details.callback);
};

cronofy.prototype.revokeProfileAuthorization = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/profiles/' + details.options.profile_id + '/revoke', details.options, details.callback);
};

cronofy.prototype.refreshAccessToken = function () {
  var that = this;
  var details = this._parseArguments(arguments, ['client_id', 'client_secret', 'refresh_token']);

  details.options.grant_type = 'refresh_token';

  return this._httpPost('/oauth/token', details.options).then(tap(function (token) {
    that.config.access_token = token.access_token;
    that.config.refresh_token = token.refresh_token;

    if (details.callback) {
      details.callback(null, token);
    }
  }), details.callback);
};

cronofy.prototype.requestAccessToken = function () {
  var that = this;
  var details = this._parseArguments(arguments, ['client_id', 'client_secret', 'refresh_token']);

  details.options.grant_type = 'authorization_code';

  return this._httpPost('/oauth/token', details.options).then(tap(function (token) {
    that.config.access_token = token.access_token;
    that.config.refresh_token = token.refresh_token;

    if (details.callback) {
      details.callback(null, token);
    }
  }), details.callback);
};

cronofy.prototype.revokeAuthorization = function () {
  var that = this;
  var details = this._parseArguments(arguments, ['client_id', 'client_secret', 'refresh_token']);

  return this._httpPost('/oauth/token/revoke', details.options).then(tap(function () {
    delete that.config.access_token;
    delete that.config.refresh_token;

    if (details.callback) {
      details.callback();
    }
  }), details.callback);
};

cronofy.prototype.getSmartInvite = function () {
  var details = this._parseArguments(arguments, ['smart_invite_id', 'recipient_email', 'client_secret']);

  details.options.bearer_token = details.options.client_secret;

  return this._httpGet('/v1/smart_invites', details.options, details.callback, ['client_secret', 'access_token', 'bearer_token']);
};

cronofy.prototype.createSmartInvite = function () {
  var details = this._parseArguments(arguments, ['client_secret']);

  details.options.bearer_token = details.options.client_secret;

  return this._httpPost('/v1/smart_invites', details.options, details.callback, ['access_token', 'client_secret', 'bearer_token']);
};

cronofy.prototype.cancelSmartInvite = function () {
  var details = this._parseArguments(arguments, ['client_secret']);

  details.options.bearer_token = details.options.client_secret;
  details.options.method = 'cancel';

  return this._httpPost('/v1/smart_invites', details.options, details.callback, ['access_token', 'client_secret', 'bearer_token']);
};

module.exports = cronofy;
<commit_msg>Add version to useragent<commit_after>'use strict';

var _ = require('lodash');
var request = require('request');
var version = require('../package.json').version;

var tap = function (func) {
  return function (value) {
    func.apply(null, arguments);
    return value;
  };
};

var cronofy = function (config) {
  if (!this || this.constructor !== cronofy) {
    throw new Error('Cronofy must be called as a constructor');
  }

  this.config = config;

  this.urls = {
    api: 'https://api' + (config.dataCenter ? '-' + config.dataCenter : '') + '.cronofy.com'
  };
};

cronofy.prototype._httpCall = function (method, path, options, callback, optionsToOmit) {
  var settings = {
    method: method,
    url: path,
    gzip: true,
    json: true,
    headers: {
      Authorization: 'Bearer ' + (options.access_token || options.bearer_token),
      'Content-Type': 'application/json',
      'User-Agent': 'Cronofy Node - ' + version
    }
  };

  if (method === 'GET') {
    settings['qs'] = _.omit(options, optionsToOmit || ['access_token']);
  } else {
    settings['body'] = _.omit(options, optionsToOmit || ['access_token']);
  }

  return new Promise(function (resolve, reject) {
    request(settings, function (error, result, body) {
      if (error || result.statusCode >= 400) {
        var err = new Error(JSON.stringify(body));

        if (result && result.statusCode) {
          err.statusCode = result.statusCode;
        }

        err.error = {
          url: path,
          entity: body
        };

        if (callback) {
          callback(err);
        } else {
          reject(err);
        }
      } else {
        if (callback) {
          callback(null, body);
        } else {
          resolve(body);
        }
      }
    });
  });
};

cronofy.prototype._parseArguments = function (args, configDefaults) {
  var parsed = {options: {}, callback: null};

  if (args.length === 2) {
    parsed.options = args[0];
    parsed.callback = args[1];
  } else {
    switch (typeof args[0]) {
      case 'object':
        parsed.options = args[0];
        break;
      case 'function':
        parsed.callback = args[0];
        break;
    }
  }

  for (var i = 0; i < configDefaults.length; i++) {
    var key = configDefaults[i];

    parsed.options[key] = parsed.options[key] || this.config[key];
  }

  return parsed;
};

cronofy.prototype._httpGet = function (path, options, callback, optionsToOmit) {
  return this._httpCall('GET', this.urls.api + path, options, callback, optionsToOmit);
};

cronofy.prototype._httpPost = function (path, options, callback, optionsToOmit) {
  return this._httpCall('POST', this.urls.api + path, options, callback, optionsToOmit);
};

cronofy.prototype._httpDelete = function (path, options, callback, optionsToOmit) {
  return this._httpCall('DELETE', this.urls.api + path, options, callback, optionsToOmit);
};

cronofy.prototype.accountInformation = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/account', details.options, details.callback);
};

cronofy.prototype.addToCalendar = function () {
  var details = this._parseArguments(arguments, ['client_id', 'client_secret']);

  return this._httpPost('/v1/add_to_calendar', details.options, details.callback);
};

cronofy.prototype.realTimeScheduling = function () {
  var details = this._parseArguments(arguments, ['client_id', 'client_secret']);

  return this._httpPost('/v1/real_time_scheduling', details.options, details.callback);
};

cronofy.prototype.authorizeWithServiceAccount = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/service_account_authorizations', details.options, details.callback);
};

cronofy.prototype.availability = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/availability', details.options, details.callback);
};

cronofy.prototype.createEvent = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/calendars/' + details.options.calendar_id + '/events', details.options, details.callback, ['access_token', 'calendar_id']);
};

cronofy.prototype.createNotificationChannel = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/channels', details.options, details.callback);
};

cronofy.prototype.deleteEvent = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpDelete('/v1/calendars/' + details.options.calendar_id + '/events', details.options, details.callback, ['access_token', 'calendar_id']);
};

cronofy.prototype.deleteExternalEvent = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpDelete('/v1/calendars/' + details.options.calendar_id + '/events', details.options, details.callback, ['access_token', 'calendar_id']);
};

cronofy.prototype.deleteNotificationChannel = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpDelete('/v1/channels/' + details.options.channel_id, details.options, details.callback, ['access_token', 'channel_id']);
};

cronofy.prototype.elevatedPermissions = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/permissions', details.options, details.callback);
};

cronofy.prototype.freeBusy = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet(details.options.next_page || '/v1/free_busy', details.options, details.callback);
};

cronofy.prototype.listCalendars = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/calendars', details.options, details.callback);
};

cronofy.prototype.createCalendar = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/calendars', details.options, details.callback);
};

cronofy.prototype.listNotificationChannels = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/channels', details.options, details.callback);
};

cronofy.prototype.profileInformation = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpGet('/v1/profiles', details.options, details.callback);
};

cronofy.prototype.readEvents = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  if (details.options.next_page) {
    return this._httpCall('GET', details.options.next_page, details.options, details.callback, ['access_token', 'next_page']);
  }

  return this._httpGet('/v1/events', details.options, details.callback);
};

cronofy.prototype.revokeProfileAuthorization = function () {
  var details = this._parseArguments(arguments, ['access_token']);

  return this._httpPost('/v1/profiles/' + details.options.profile_id + '/revoke', details.options, details.callback);
};

cronofy.prototype.refreshAccessToken = function () {
  var that = this;
  var details = this._parseArguments(arguments, ['client_id', 'client_secret', 'refresh_token']);

  details.options.grant_type = 'refresh_token';

  return this._httpPost('/oauth/token', details.options).then(tap(function (token) {
    that.config.access_token = token.access_token;
    that.config.refresh_token = token.refresh_token;

    if (details.callback) {
      details.callback(null, token);
    }
  }), details.callback);
};

cronofy.prototype.requestAccessToken = function () {
  var that = this;
  var details = this._parseArguments(arguments, ['client_id', 'client_secret', 'refresh_token']);

  details.options.grant_type = 'authorization_code';

  return this._httpPost('/oauth/token', details.options).then(tap(function (token) {
    that.config.access_token = token.access_token;
    that.config.refresh_token = token.refresh_token;

    if (details.callback) {
      details.callback(null, token);
    }
  }), details.callback);
};

cronofy.prototype.revokeAuthorization = function () {
  var that = this;
  var details = this._parseArguments(arguments, ['client_id', 'client_secret', 'refresh_token']);

  return this._httpPost('/oauth/token/revoke', details.options).then(tap(function () {
    delete that.config.access_token;
    delete that.config.refresh_token;

    if (details.callback) {
      details.callback();
    }
  }), details.callback);
};

cronofy.prototype.getSmartInvite = function () {
  var details = this._parseArguments(arguments, ['smart_invite_id', 'recipient_email', 'client_secret']);

  details.options.bearer_token = details.options.client_secret;

  return this._httpGet('/v1/smart_invites', details.options, details.callback, ['client_secret', 'access_token', 'bearer_token']);
};

cronofy.prototype.createSmartInvite = function () {
  var details = this._parseArguments(arguments, ['client_secret']);

  details.options.bearer_token = details.options.client_secret;

  return this._httpPost('/v1/smart_invites', details.options, details.callback, ['access_token', 'client_secret', 'bearer_token']);
};

cronofy.prototype.cancelSmartInvite = function () {
  var details = this._parseArguments(arguments, ['client_secret']);

  details.options.bearer_token = details.options.client_secret;
  details.options.method = 'cancel';

  return this._httpPost('/v1/smart_invites', details.options, details.callback, ['access_token', 'client_secret', 'bearer_token']);
};

module.exports = cronofy;
<|endoftext|>


<commit_before>var React = require('react')
var InputMask = require('inputmask-core')

var KEYCODE_Z = 90
var KEYCODE_Y = 89

function isUndo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}

function isRedo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}

function getSelection (el) {
  var start, end, rangeEl, clone

  if (el.selectionStart !== undefined) {
    start = el.selectionStart
    end = el.selectionEnd
  }
  else {
    try {
      el.focus()
      rangeEl = el.createTextRange()
      clone = rangeEl.duplicate()

      rangeEl.moveToBookmark(document.selection.createRange().getBookmark())
      clone.setEndPoint('EndToStart', rangeEl)

      start = clone.text.length
      end = start + rangeEl.text.length
    }
    catch (e) { /* not focused or not visible */ }
  }

  return { start, end }
}

function setSelection(el, selection) {
  var rangeEl

  try {
    if (el.selectionStart !== undefined) {
      el.focus()
      el.setSelectionRange(selection.start, selection.end)
    }
    else {
      el.focus()
      rangeEl = el.createTextRange()
      rangeEl.collapse(true)
      rangeEl.moveStart('character', selection.start)
      rangeEl.moveEnd('character', selection.end - selection.start)
      rangeEl.select()
    }
  }
  catch (e) { /* not focused or not visible */ }
}

var MaskedInput = React.createClass({
  propTypes: {
    mask: React.PropTypes.string.isRequired,

    formatCharacters: React.PropTypes.object,
    placeholderChar: React.PropTypes.string
  },

  getDefaultProps() {
    return {
      value: ''
    }
  },

  componentWillMount() {
    var options = {
      pattern: this.props.mask,
      value: this.props.value,
      formatCharacters: this.props.formatCharacters
    }
    if (this.props.placeholderChar) {
      options.placeholderChar = this.props.placeholderChar
    }
    this.mask = new InputMask(options)
  },

  componentWillReceiveProps(nextProps) {
    if (this.props.mask !== nextProps.mask && this.props.value !== nextProps.mask) {
      // if we get a new value and a new mask at the same time
      // check if the mask.value is still the initial value
      // - if so use the nextProps value
      // - otherwise the `this.mask` has a value for us (most likely from paste action)
      if (this.mask.getValue() === this.mask.emptyValue) {
        this.mask.setPattern(nextProps.mask, {value: nextProps.value})
      }
      else {
        this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
      }
    }
    else if (this.props.mask !== nextProps.mask) {
      this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
    }
    else if (this.props.value !== nextProps.value) {
      this.mask.setValue(nextProps.value)
    }
  },

  componentWillUpdate(nextProps, nextState) {
    if (nextProps.mask !== this.props.mask) {
      this._updatePattern(nextProps)
    }
  },

  componentDidUpdate(prevProps) {
    if (prevProps.mask !== this.props.mask && this.mask.selection.start) {
      this._updateInputSelection()
    }
  },

  _updatePattern: function(props) {
    this.mask.setPattern(props.mask, {
      value: this.mask.getRawValue(),
      selection: getSelection(this.input)
    })
  },

  _updateMaskSelection() {
    this.mask.selection = getSelection(this.input)
  },

  _updateInputSelection() {
    setSelection(this.input, this.mask.selection)
  },

  _onChange(e) {
    // console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)

    var maskValue = this.mask.getValue()
    if (e.target.value !== maskValue) {
      // Cut or delete operations will have shortened the value
      if (e.target.value.length < maskValue.length) {
        var sizeDiff = maskValue.length - e.target.value.length
        this._updateMaskSelection()
        this.mask.selection.end = this.mask.selection.start + sizeDiff
        this.mask.backspace()
      }
      var value = this._getDisplayValue()
      e.target.value = value
      if (value) {
        this._updateInputSelection()
      }
    }
    if (this.props.onChange) {
      this.props.onChange(e)
    }
  },

  _onKeyDown(e) {
    // console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    if (isUndo(e)) {
      e.preventDefault()
      if (this.mask.undo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }
    else if (isRedo(e)) {
      e.preventDefault()
      if (this.mask.redo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }

    if (e.key === 'Backspace') {
      e.preventDefault()
      this._updateMaskSelection()
      if (this.mask.backspace()) {
        var value = this._getDisplayValue()
        e.target.value = value
        if (value) {
          this._updateInputSelection()
        }
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
    }
  },

  _onKeyPress(e) {
    // console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    // Ignore modified key presses
    // Ignore enter key to allow form submission
    if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return }

    e.preventDefault()
    this._updateMaskSelection()
    if (this.mask.input((e.key || e.data))) {
      e.target.value = this.mask.getValue()
      this._updateInputSelection()
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  },

  _onPaste(e) {
    // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)

    e.preventDefault()
    this._updateMaskSelection()
    // getData value needed for IE also works in FF & Chrome
    if (this.mask.paste(e.clipboardData.getData('Text'))) {
      e.target.value = this.mask.getValue()
      // Timeout needed for IE
      setTimeout(this._updateInputSelection, 0)
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  },

  _getDisplayValue() {
    var value = this.mask.getValue()
    return value === this.mask.emptyValue ? '' : value
  },

  focus() {
    this.input.focus()
  },

  blur() {
    this.input.blur()
  },

  render() {
    var {mask, formatCharacters, size, placeholder, placeholderChar, ...props} = this.props
    var patternLength = this.mask.pattern.length
    return <input {...props}
      ref={r => this.input = r }
      maxLength={patternLength}
      onChange={this._onChange}
      onKeyDown={this._onKeyDown}
      onBeforeInput={this._onKeyPress}
      onPaste={this._onPaste}
      placeholder={placeholder || this.mask.emptyValue}
      size={size || patternLength}
      value={this._getDisplayValue()}
    />
  }
})

module.exports = MaskedInput
<commit_msg>Can't enter more than one character in Edge browser (#79)<commit_after>var React = require('react')
var InputMask = require('inputmask-core')

var KEYCODE_Z = 90
var KEYCODE_Y = 89

function isUndo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}

function isRedo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}

function getSelection (el) {
  var start, end, rangeEl, clone

  if (el.selectionStart !== undefined) {
    start = el.selectionStart
    end = el.selectionEnd
  }
  else {
    try {
      el.focus()
      rangeEl = el.createTextRange()
      clone = rangeEl.duplicate()

      rangeEl.moveToBookmark(document.selection.createRange().getBookmark())
      clone.setEndPoint('EndToStart', rangeEl)

      start = clone.text.length
      end = start + rangeEl.text.length
    }
    catch (e) { /* not focused or not visible */ }
  }

  return { start, end }
}

function setSelection(el, selection) {
  var rangeEl

  try {
    if (el.selectionStart !== undefined) {
      el.focus()
      el.setSelectionRange(selection.start, selection.end)
    }
    else {
      el.focus()
      rangeEl = el.createTextRange()
      rangeEl.collapse(true)
      rangeEl.moveStart('character', selection.start)
      rangeEl.moveEnd('character', selection.end - selection.start)
      rangeEl.select()
    }
  }
  catch (e) { /* not focused or not visible */ }
}

var MaskedInput = React.createClass({
  propTypes: {
    mask: React.PropTypes.string.isRequired,

    formatCharacters: React.PropTypes.object,
    placeholderChar: React.PropTypes.string
  },

  getDefaultProps() {
    return {
      value: ''
    }
  },

  componentWillMount() {
    var options = {
      pattern: this.props.mask,
      value: this.props.value,
      formatCharacters: this.props.formatCharacters
    }
    if (this.props.placeholderChar) {
      options.placeholderChar = this.props.placeholderChar
    }
    this.mask = new InputMask(options)
  },

  componentWillReceiveProps(nextProps) {
    if (this.props.mask !== nextProps.mask && this.props.value !== nextProps.mask) {
      // if we get a new value and a new mask at the same time
      // check if the mask.value is still the initial value
      // - if so use the nextProps value
      // - otherwise the `this.mask` has a value for us (most likely from paste action)
      if (this.mask.getValue() === this.mask.emptyValue) {
        this.mask.setPattern(nextProps.mask, {value: nextProps.value})
      }
      else {
        this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
      }
    }
    else if (this.props.mask !== nextProps.mask) {
      this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
    }
    else if (this.props.value !== nextProps.value) {
      this.mask.setValue(nextProps.value)
    }
  },

  componentWillUpdate(nextProps, nextState) {
    if (nextProps.mask !== this.props.mask) {
      this._updatePattern(nextProps)
    }
  },

  componentDidUpdate(prevProps) {
    if (prevProps.mask !== this.props.mask && this.mask.selection.start) {
      this._updateInputSelection()
    }
  },

  _updatePattern: function(props) {
    this.mask.setPattern(props.mask, {
      value: this.mask.getRawValue(),
      selection: getSelection(this.input)
    })
  },

  _updateMaskSelection() {
    this.mask.selection = getSelection(this.input)
  },

  _updateInputSelection() {
    setSelection(this.input, this.mask.selection)
  },

  _onChange(e) {
    // console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)

    var maskValue = this.mask.getValue()
    if (e.target.value !== maskValue) {
      // Cut or delete operations will have shortened the value
      if (e.target.value.length < maskValue.length) {
        var sizeDiff = maskValue.length - e.target.value.length
        this._updateMaskSelection()
        this.mask.selection.end = this.mask.selection.start + sizeDiff
        this.mask.backspace()
      }
      var value = this._getDisplayValue()
      e.target.value = value
      if (value) {
        this._updateInputSelection()
      }
    }
    if (this.props.onChange) {
      this.props.onChange(e)
    }
  },

  _onKeyDown(e) {
    // console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    if (isUndo(e)) {
      e.preventDefault()
      if (this.mask.undo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }
    else if (isRedo(e)) {
      e.preventDefault()
      if (this.mask.redo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }

    if (e.key === 'Backspace') {
      e.preventDefault()
      this._updateMaskSelection()
      if (this.mask.backspace()) {
        var value = this._getDisplayValue()
        e.target.value = value
        if (value) {
          this._updateInputSelection()
        }
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
    }
  },

  _onKeyPress(e) {
    // console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    // Ignore modified key presses
    // Ignore enter key to allow form submission
    if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return }

    e.preventDefault()
    this._updateMaskSelection()
    if (this.mask.input((e.key || e.data))) {
      e.target.value = this.mask.getValue()
      this._updateInputSelection()
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  },

  _onPaste(e) {
    // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)

    e.preventDefault()
    this._updateMaskSelection()
    // getData value needed for IE also works in FF & Chrome
    if (this.mask.paste(e.clipboardData.getData('Text'))) {
      e.target.value = this.mask.getValue()
      // Timeout needed for IE
      setTimeout(this._updateInputSelection, 0)
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  },

  _getDisplayValue() {
    var value = this.mask.getValue()
    return value === this.mask.emptyValue ? '' : value
  },

  _keyPressPropName() {
    return navigator.userAgent.match(/Android/i)
      ? 'onBeforeInput'
      : 'onKeyPress'
  },

  _getEventHandlers() {
    return {
      onChange: this._onChange,
      onKeyDown: this._onKeyDown,
      onPaste: this._onPaste,
      [this._keyPressPropName()]: this._onKeyPress
    }
  },

  focus() {
    this.input.focus()
  },

  blur() {
    this.input.blur()
  },

  render() {
    var ref = r => this.input = r
    var maxLength = this.mask.pattern.length
    var value = this._getDisplayValue()
    var eventHandlers = this._getEventHandlers()
    var { size = maxLength, placeholder = this.mask.emptyValue } = this.props
    var props = { ...this.props, ...eventHandlers, ref, maxLength, value, size, placeholder }

    return <input {...props} />
  }
})

module.exports = MaskedInput
<|endoftext|>