<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import clamp from 'clamp'

import {
  TextInput,
  Keyboard,
  BackHandler,
  Animated,
  PanResponder,
  Platform
} from 'react-native'

import closest from './libs/closest'
import measureElement from './libs/measureElement'
import FlickAnimation from './libs/FlickAnimation'
import {statusBarHeight, visibleHeight} from './libs/layout'
import * as Constants from './libs/constants'
import styles from './libs/styles'

const keyboardShowEvent = Platform.select({
  android: 'keyboardDidShow',
  ios: 'keyboardWillShow'
})

const keyboardHideEvent = Platform.select({
  android: 'keyboardDidHide',
  ios: 'keyboardWillHide'
})

const usableHeight = visibleHeight() - statusBarHeight()

class SlidingUpPanel extends React.PureComponent {
  static propTypes = {
    height: PropTypes.number,
    animatedValue: PropTypes.instanceOf(Animated.Value),
    draggableRange: PropTypes.shape({
      top: PropTypes.number,
      bottom: PropTypes.number
    }),
    snappingPoints: PropTypes.arrayOf(PropTypes.number),
    minimumVelocityThreshold: PropTypes.number,
    minimumDistanceThreshold: PropTypes.number,
    avoidKeyboard: PropTypes.bool,
    onBackButtonPress: PropTypes.func,
    onDragStart: PropTypes.func,
    onDragEnd: PropTypes.func,
    onMomentumDragStart: PropTypes.func,
    onMomentumDragEnd: PropTypes.func,
    allowMomentum: PropTypes.bool,
    allowDragging: PropTypes.bool,
    showBackdrop: PropTypes.bool,
    backdropOpacity: PropTypes.number,
    friction: PropTypes.number,
    children: PropTypes.oneOfType([PropTypes.element, PropTypes.func])
  }

  static defaultProps = {
    height: usableHeight,
    animatedValue: new Animated.Value(0),
    draggableRange: {top: usableHeight, bottom: 0},
    snappingPoints: [],
    minimumVelocityThreshold: Constants.DEFAULT_MINIMUM_VELOCITY_THRESHOLD,
    minimumDistanceThreshold: Constants.DEFAULT_MINIMUM_DISTANCE_THRESHOLD,
    avoidKeyboard: true,
    onBackButtonPress: null,
    onDragStart: () => {},
    onDragEnd: () => {},
    onMomentumDragStart: () => {},
    onMomentumDragEnd: () => {},
    allowMomentum: true,
    allowDragging: true,
    showBackdrop: true,
    backdropOpacity: 0.75,
    friction: Constants.DEFAULT_FRICTION
  }

  // eslint-disable-next-line react/sort-comp
  _panResponder = PanResponder.create({
    onMoveShouldSetPanResponder: this._onMoveShouldSetPanResponder.bind(this),
    onPanResponderGrant: this._onPanResponderGrant.bind(this),
    onPanResponderStart: this._onPanResponderStart.bind(this),
    onPanResponderMove: this._onPanResponderMove.bind(this),
    onPanResponderRelease: this._onPanResponderRelease.bind(this),
    onPanResponderTerminate: this._onPanResponderTerminate.bind(this),
    onShouldBlockNativeResponder: () => false,
    onPanResponderTerminationRequest: () => false
  })

  _keyboardShowListener = Keyboard.addListener(
    keyboardShowEvent,
    this._onKeyboardShown.bind(this)
  )

  _keyboardHideListener = Keyboard.addListener(
    keyboardHideEvent,
    this._onKeyboardHiden.bind(this)
  )

  _backButtonListener = BackHandler.addEventListener(
    'hardwareBackPress',
    this._onBackButtonPress.bind(this)
  )

  constructor(props) {
    super(props)

    this._storeKeyboardPosition = this._storeKeyboardPosition.bind(this)
    this._isInsideDraggableRange = this._isInsideDraggableRange.bind(this)
    this._triggerAnimation = this._triggerAnimation.bind(this)
    this._renderContent = this._renderContent.bind(this)
    this._renderBackdrop = this._renderBackdrop.bind(this)

    this.show = this.show.bind(this)
    this.hide = this.hide.bind(this)
    this.scrollIntoView = this.scrollIntoView.bind(this)

    const {top, bottom} = this.props.draggableRange
    const animatedValue = this.props.animatedValue.__getValue()
    const initialValue = clamp(animatedValue, bottom, top)

    // Ensure the animation are within draggable range
    this.props.animatedValue.setValue(initialValue)

    this._initialDragPosition = initialValue
    this._backdropPointerEvents = this._isAtBottom(initialValue) ? 'none' : 'box-only' // prettier-ignore
    this._flick = new FlickAnimation({max: top, min: bottom})

    this._flickAnimationListener = this._flick.onUpdate(value => {
      this.props.animatedValue.setValue(value)
    })

    this._animatedValueListener = this.props.animatedValue.addListener(
      this._onAnimatedValueChange.bind(this)
    )
  }

  componentDidUpdate(prevProps) {
    if (
      prevProps.draggableRange.top !== this.props.draggableRange.top ||
      prevProps.draggableRange.bottom !== this.props.draggableRange.bottom
    ) {
      const {top, bottom} = this.props.draggableRange
      const animatedValue = this.props.animatedValue.__getValue()

      this._flick.setMax(top)
      this._flick.setMin(bottom)

      // If the panel is below the new 'bottom'
      if (animatedValue < bottom || animatedValue > top) {
        const newValue = clamp(animatedValue, bottom, top)
        this.props.animatedValue.setValue(newValue)
      }
    }
  }

  componentWillUnmount() {
    if (this._animatedValueListener != null) {
      this.props.animatedValue.removeListener(this._animatedValueListener)
    }

    if (this._keyboardShowListener != null) {
      this._keyboardShowListener.remove()
    }

    if (this._keyboardHideListener != null) {
      this._keyboardHideListener.remove()
    }

    if (this._flickAnimationListener != null) {
      this._flickAnimationListener.remove()
    }

    if (this._backButtonListener != null) {
      this._backButtonListener.remove()
    }
  }

  _onMoveShouldSetPanResponder(evt, gestureState) {
    if (!this.props.allowDragging) {
      return false
    }

    const animatedValue = this.props.animatedValue.__getValue()

    return (
      this._isInsideDraggableRange(animatedValue, gestureState) &&
      Math.abs(gestureState.dy) > this.props.minimumDistanceThreshold
    )
  }

  _onPanResponderGrant() {
    this._flick.stop()
  }

  _onPanResponderStart(evt, gestureState) {
    const value = this.props.animatedValue.__getValue()

    this._initialDragPosition = value
    this.props.onDragStart(value, gestureState)
  }

  _onPanResponderMove(evt, gestureState) {
    const {top, bottom} = this.props.draggableRange
    const delta = this._initialDragPosition - gestureState.dy
    const newValue = clamp(delta, top, bottom)

    this.props.animatedValue.setValue(newValue)
  }

  // Trigger when you release your finger
  _onPanResponderRelease(evt, gestureState) {
    const animatedValue = this.props.animatedValue.__getValue()

    if (!this._isInsideDraggableRange(animatedValue, gestureState)) {
      return
    }

    this._initialDragPosition = animatedValue
    this.props.onDragEnd(animatedValue, gestureState)

    if (!this.props.allowMomentum) {
      return
    }

    if (this.props.snappingPoints.length > 0) {
      this.props.onMomentumDragStart(animatedValue)

      const {top, bottom} = this.props.draggableRange
      const nextPoint = this._flick.predictNextPosition({
        fromValue: animatedValue,
        velocity: gestureState.vy,
        friction: this.props.friction
      })

      const closestPoint = closest(nextPoint, [
        bottom,
        ...this.props.snappingPoints,
        top
      ])

      const remainingDistance = animatedValue - closestPoint
      const velocity = remainingDistance / Constants.TIME_CONSTANT

      this._flick.start({
        velocity,
        toValue: closestPoint,
        fromValue: animatedValue,
        friction: this.props.friction,
        onMomentumEnd: this.props.onMomentumDragEnd
      })
      return
    }

    if (Math.abs(gestureState.vy) > this.props.minimumVelocityThreshold) {
      this.props.onMomentumDragStart(animatedValue)

      this._flick.start({
        velocity: gestureState.vy,
        fromValue: animatedValue,
        friction: this.props.friction,
        onMomentumEnd: this.props.onMomentumDragEnd
      })
    }
  }

  _onPanResponderTerminate(evt, gestureState) {
    const animatedValue = this.props.animatedValue.__getValue()

    if (!this._isInsideDraggableRange(animatedValue, gestureState)) {
      return
    }

    this._initialDragPosition = animatedValue
    this.props.onDragEnd(animatedValue, gestureState)
  }

  _onAnimatedValueChange({value}) {
    const isAtBottom = this._isAtBottom(value)

    if (isAtBottom) {
      Keyboard.dismiss()
    }

    if (this._backdrop == null) {
      return
    }

    // @TODO: Find a better way to update pointer events when animated value changed

    if (isAtBottom && this._backdropPointerEvents === 'box-only') {
      this._backdropPointerEvents = 'none'
      this._backdrop.setNativeProps({pointerEvents: 'none'})
    }

    if (!isAtBottom && this._backdropPointerEvents === 'none') {
      this._backdropPointerEvents = 'box-only'
      this._backdrop.setNativeProps({pointerEvents: 'box-only'})
    }
  }

  _onKeyboardShown(event) {
    if (!this.props.avoidKeyboard) {
      return
    }

    this._storeKeyboardPosition(event.endCoordinates.screenY)

    const node = TextInput.State.currentlyFocusedField()

    if (node != null) {
      this.scrollIntoView(node)
    }
  }

  _onKeyboardHiden() {
    this._storeKeyboardPosition(0)

    const animatedValue = this.props.animatedValue.__getValue()

    // Restore last position
    if (this._lastPosition != null && !this._isAtBottom(animatedValue)) {
      Animated.timing(this.props.animatedValue, {
        toValue: this._lastPosition,
        duration: Constants.KEYBOARD_TRANSITION_DURATION
      }).start()
    }

    this._lastPosition = null
  }

  _onBackButtonPress() {
    if (this.props.onBackButtonPress) {
      return this.props.onBackButtonPress()
    }

    const value = this.props.animatedValue.__getValue()

    if (this._isAtBottom(value)) {
      return false
    }

    this.hide()

    return true
  }

  _isInsideDraggableRange(value, gestureState) {
    const {top, bottom} = this.props.draggableRange

    if (gestureState.dy > 0) {
      return value > bottom
    }

    return value < top
  }

  _isAtBottom(value) {
    const {bottom} = this.props.draggableRange
    return value <= bottom
  }

  _storeKeyboardPosition(value) {
    this._keyboardYPosition = value
  }

  _triggerAnimation(options = {}) {
    const animatedValue = this.props.animatedValue.__getValue()
    const remainingDistance = animatedValue - options.toValue
    const velocity = options.velocity || remainingDistance / Constants.TIME_CONSTANT // prettier-ignore

    this._flick.start({
      velocity,
      toValue: options.toValue,
      fromValue: animatedValue,
      friction: this.props.friction
    })
  }

  _renderBackdrop() {
    if (!this.props.showBackdrop) {
      return null
    }

    const {top, bottom} = this.props.draggableRange

    const backdropOpacity = this.props.animatedValue.interpolate({
      inputRange: [bottom, top],
      outputRange: [0, this.props.backdropOpacity],
      extrapolate: 'clamp'
    })

    return (
      <Animated.View
        key="backdrop"
        pointerEvents={this._backdropPointerEvents}
        ref={c => (this._backdrop = c)}
        onTouchStart={() => this._flick.stop()}
        onTouchEnd={() => this.hide()}
        style={[styles.backdrop, {opacity: backdropOpacity}]}
      />
    )
  }

  _renderContent() {
    const {
      height,
      draggableRange: {top, bottom}
    } = this.props

    const translateY = this.props.animatedValue.interpolate({
      inputRange: [bottom, top],
      outputRange: [-bottom, -top],
      extrapolate: 'clamp'
    })

    const transform = {transform: [{translateY}]}

    const animatedContainerStyles = [
      styles.animatedContainer,
      transform,
      {height, bottom: -height}
    ]

    if (typeof this.props.children === 'function') {
      return (
        <Animated.View
          key="content"
          pointerEvents="box-none"
          style={animatedContainerStyles}>
          {this.props.children(this._panResponder.panHandlers)}
        </Animated.View>
      )
    }

    return (
      <Animated.View
        key="content"
        pointerEvents="box-none"
        style={animatedContainerStyles}
        {...this._panResponder.panHandlers}>
        {this.props.children}
      </Animated.View>
    )
  }

  render() {
    return [this._renderBackdrop(), this._renderContent()]
  }

  show(mayBeValueOrOptions) {
    if (!mayBeValueOrOptions) {
      const {top} = this.props.draggableRange
      return this._triggerAnimation({toValue: top})
    }

    if (typeof mayBeValueOrOptions === 'object') {
      return this._triggerAnimation(mayBeValueOrOptions)
    }

    return this._triggerAnimation({toValue: mayBeValueOrOptions})
  }

  hide() {
    const {bottom} = this.props.draggableRange
    this._triggerAnimation({toValue: bottom})
  }

  async scrollIntoView(node, options = {}) {
    if (!this._keyboardYPosition) {
      return
    }

    // Stop any animation when the keyboard starts showing
    this._flick.stop()

    const {y} = await measureElement(node)
    const extraMargin = options.keyboardExtraMargin || Constants.KEYBOARD_EXTRA_MARGIN // prettier-ignore
    const keyboardActualPos = this._keyboardYPosition - extraMargin

    if (y > keyboardActualPos) {
      this._lastPosition = this.props.animatedValue.__getValue()

      const fromKeyboardToElement = y - keyboardActualPos
      const transitionDistance = this._lastPosition + fromKeyboardToElement

      Animated.timing(this.props.animatedValue, {
        toValue: transitionDistance,
        duration: Constants.KEYBOARD_TRANSITION_DURATION
      }).start()
    }
  }
}

export default SlidingUpPanel
<commit_msg>Fix issue wrong position of panel if it stops in the middle of animation<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import clamp from 'clamp'

import {
  TextInput,
  Keyboard,
  BackHandler,
  Animated,
  PanResponder,
  Platform
} from 'react-native'

import closest from './libs/closest'
import measureElement from './libs/measureElement'
import FlickAnimation from './libs/FlickAnimation'
import {statusBarHeight, visibleHeight} from './libs/layout'
import * as Constants from './libs/constants'
import styles from './libs/styles'

const keyboardShowEvent = Platform.select({
  android: 'keyboardDidShow',
  ios: 'keyboardWillShow'
})

const keyboardHideEvent = Platform.select({
  android: 'keyboardDidHide',
  ios: 'keyboardWillHide'
})

const usableHeight = visibleHeight() - statusBarHeight()

class SlidingUpPanel extends React.PureComponent {
  static propTypes = {
    height: PropTypes.number,
    animatedValue: PropTypes.instanceOf(Animated.Value),
    draggableRange: PropTypes.shape({
      top: PropTypes.number,
      bottom: PropTypes.number
    }),
    snappingPoints: PropTypes.arrayOf(PropTypes.number),
    minimumVelocityThreshold: PropTypes.number,
    minimumDistanceThreshold: PropTypes.number,
    avoidKeyboard: PropTypes.bool,
    onBackButtonPress: PropTypes.func,
    onDragStart: PropTypes.func,
    onDragEnd: PropTypes.func,
    onMomentumDragStart: PropTypes.func,
    onMomentumDragEnd: PropTypes.func,
    allowMomentum: PropTypes.bool,
    allowDragging: PropTypes.bool,
    showBackdrop: PropTypes.bool,
    backdropOpacity: PropTypes.number,
    friction: PropTypes.number,
    children: PropTypes.oneOfType([PropTypes.element, PropTypes.func])
  }

  static defaultProps = {
    height: usableHeight,
    animatedValue: new Animated.Value(0),
    draggableRange: {top: usableHeight, bottom: 0},
    snappingPoints: [],
    minimumVelocityThreshold: Constants.DEFAULT_MINIMUM_VELOCITY_THRESHOLD,
    minimumDistanceThreshold: Constants.DEFAULT_MINIMUM_DISTANCE_THRESHOLD,
    avoidKeyboard: true,
    onBackButtonPress: null,
    onDragStart: () => {},
    onDragEnd: () => {},
    onMomentumDragStart: () => {},
    onMomentumDragEnd: () => {},
    allowMomentum: true,
    allowDragging: true,
    showBackdrop: true,
    backdropOpacity: 0.75,
    friction: Constants.DEFAULT_FRICTION
  }

  // eslint-disable-next-line react/sort-comp
  _panResponder = PanResponder.create({
    onMoveShouldSetPanResponder: this._onMoveShouldSetPanResponder.bind(this),
    onPanResponderGrant: this._onPanResponderGrant.bind(this),
    onPanResponderStart: this._onPanResponderStart.bind(this),
    onPanResponderMove: this._onPanResponderMove.bind(this),
    onPanResponderRelease: this._onPanResponderRelease.bind(this),
    onPanResponderTerminate: this._onPanResponderTerminate.bind(this),
    onShouldBlockNativeResponder: () => false,
    onPanResponderTerminationRequest: () => false
  })

  _keyboardShowListener = Keyboard.addListener(
    keyboardShowEvent,
    this._onKeyboardShown.bind(this)
  )

  _keyboardHideListener = Keyboard.addListener(
    keyboardHideEvent,
    this._onKeyboardHiden.bind(this)
  )

  _backButtonListener = BackHandler.addEventListener(
    'hardwareBackPress',
    this._onBackButtonPress.bind(this)
  )

  constructor(props) {
    super(props)

    this._storeKeyboardPosition = this._storeKeyboardPosition.bind(this)
    this._isInsideDraggableRange = this._isInsideDraggableRange.bind(this)
    this._triggerAnimation = this._triggerAnimation.bind(this)
    this._renderContent = this._renderContent.bind(this)
    this._renderBackdrop = this._renderBackdrop.bind(this)

    this.show = this.show.bind(this)
    this.hide = this.hide.bind(this)
    this.scrollIntoView = this.scrollIntoView.bind(this)

    const {top, bottom} = this.props.draggableRange
    const animatedValue = this.props.animatedValue.__getValue()
    const initialValue = clamp(animatedValue, bottom, top)

    // Ensure the animation are within draggable range
    this.props.animatedValue.setValue(initialValue)

    this._initialDragPosition = initialValue
    this._backdropPointerEvents = this._isAtBottom(initialValue) ? 'none' : 'box-only' // prettier-ignore
    this._flick = new FlickAnimation({max: top, min: bottom})

    this._flickAnimationListener = this._flick.onUpdate(value => {
      this.props.animatedValue.setValue(value)
    })

    this._animatedValueListener = this.props.animatedValue.addListener(
      this._onAnimatedValueChange.bind(this)
    )
  }

  componentDidUpdate(prevProps) {
    if (
      prevProps.draggableRange.top !== this.props.draggableRange.top ||
      prevProps.draggableRange.bottom !== this.props.draggableRange.bottom
    ) {
      const {top, bottom} = this.props.draggableRange
      const animatedValue = this.props.animatedValue.__getValue()

      this._flick.setMax(top)
      this._flick.setMin(bottom)

      // If the panel is below the new 'bottom'
      if (animatedValue < bottom || animatedValue > top) {
        const newValue = clamp(animatedValue, bottom, top)
        this.props.animatedValue.setValue(newValue)
      }
    }
  }

  componentWillUnmount() {
    if (this._animatedValueListener != null) {
      this.props.animatedValue.removeListener(this._animatedValueListener)
    }

    if (this._keyboardShowListener != null) {
      this._keyboardShowListener.remove()
    }

    if (this._keyboardHideListener != null) {
      this._keyboardHideListener.remove()
    }

    if (this._flickAnimationListener != null) {
      this._flickAnimationListener.remove()
    }

    if (this._backButtonListener != null) {
      this._backButtonListener.remove()
    }
  }

  _onMoveShouldSetPanResponder(evt, gestureState) {
    if (!this.props.allowDragging) {
      return false
    }

    const animatedValue = this.props.animatedValue.__getValue()

    return (
      this._isInsideDraggableRange(animatedValue, gestureState) &&
      Math.abs(gestureState.dy) > this.props.minimumDistanceThreshold
    )
  }

  _onPanResponderGrant() {
    this._flick.stop()

    const value = this.props.animatedValue.__getValue()
    this._initialDragPosition = value
  }

  _onPanResponderStart(evt, gestureState) {
    const value = this.props.animatedValue.__getValue()
    this.props.onDragStart(value, gestureState)
  }

  _onPanResponderMove(evt, gestureState) {
    const {top, bottom} = this.props.draggableRange
    const delta = this._initialDragPosition - gestureState.dy
    const newValue = clamp(delta, top, bottom)

    this.props.animatedValue.setValue(newValue)
  }

  // Trigger when you release your finger
  _onPanResponderRelease(evt, gestureState) {
    const animatedValue = this.props.animatedValue.__getValue()

    if (!this._isInsideDraggableRange(animatedValue, gestureState)) {
      return
    }

    this._initialDragPosition = animatedValue
    this.props.onDragEnd(animatedValue, gestureState)

    if (!this.props.allowMomentum) {
      return
    }

    if (this.props.snappingPoints.length > 0) {
      this.props.onMomentumDragStart(animatedValue)

      const {top, bottom} = this.props.draggableRange
      const nextPoint = this._flick.predictNextPosition({
        fromValue: animatedValue,
        velocity: gestureState.vy,
        friction: this.props.friction
      })

      const closestPoint = closest(nextPoint, [
        bottom,
        ...this.props.snappingPoints,
        top
      ])

      const remainingDistance = animatedValue - closestPoint
      const velocity = remainingDistance / Constants.TIME_CONSTANT

      this._flick.start({
        velocity,
        toValue: closestPoint,
        fromValue: animatedValue,
        friction: this.props.friction,
        onMomentumEnd: this.props.onMomentumDragEnd
      })
      return
    }

    if (Math.abs(gestureState.vy) > this.props.minimumVelocityThreshold) {
      this.props.onMomentumDragStart(animatedValue)

      this._flick.start({
        velocity: gestureState.vy,
        fromValue: animatedValue,
        friction: this.props.friction,
        onMomentumEnd: this.props.onMomentumDragEnd
      })
    }
  }

  _onPanResponderTerminate(evt, gestureState) {
    const animatedValue = this.props.animatedValue.__getValue()

    if (!this._isInsideDraggableRange(animatedValue, gestureState)) {
      return
    }

    this._initialDragPosition = animatedValue
    this.props.onDragEnd(animatedValue, gestureState)
  }

  _onAnimatedValueChange({value}) {
    const isAtBottom = this._isAtBottom(value)

    if (isAtBottom) {
      Keyboard.dismiss()
    }

    if (this._backdrop == null) {
      return
    }

    // @TODO: Find a better way to update pointer events when animated value changed

    if (isAtBottom && this._backdropPointerEvents === 'box-only') {
      this._backdropPointerEvents = 'none'
      this._backdrop.setNativeProps({pointerEvents: 'none'})
    }

    if (!isAtBottom && this._backdropPointerEvents === 'none') {
      this._backdropPointerEvents = 'box-only'
      this._backdrop.setNativeProps({pointerEvents: 'box-only'})
    }
  }

  _onKeyboardShown(event) {
    if (!this.props.avoidKeyboard) {
      return
    }

    this._storeKeyboardPosition(event.endCoordinates.screenY)

    const node = TextInput.State.currentlyFocusedField()

    if (node != null) {
      this.scrollIntoView(node)
    }
  }

  _onKeyboardHiden() {
    this._storeKeyboardPosition(0)

    const animatedValue = this.props.animatedValue.__getValue()

    // Restore last position
    if (this._lastPosition != null && !this._isAtBottom(animatedValue)) {
      Animated.timing(this.props.animatedValue, {
        toValue: this._lastPosition,
        duration: Constants.KEYBOARD_TRANSITION_DURATION
      }).start()
    }

    this._lastPosition = null
  }

  _onBackButtonPress() {
    if (this.props.onBackButtonPress) {
      return this.props.onBackButtonPress()
    }

    const value = this.props.animatedValue.__getValue()

    if (this._isAtBottom(value)) {
      return false
    }

    this.hide()

    return true
  }

  _isInsideDraggableRange(value, gestureState) {
    const {top, bottom} = this.props.draggableRange

    if (gestureState.dy > 0) {
      return value > bottom
    }

    return value < top
  }

  _isAtBottom(value) {
    const {bottom} = this.props.draggableRange
    return value <= bottom
  }

  _storeKeyboardPosition(value) {
    this._keyboardYPosition = value
  }

  _triggerAnimation(options = {}) {
    const animatedValue = this.props.animatedValue.__getValue()
    const remainingDistance = animatedValue - options.toValue
    const velocity = options.velocity || remainingDistance / Constants.TIME_CONSTANT // prettier-ignore

    this._flick.start({
      velocity,
      toValue: options.toValue,
      fromValue: animatedValue,
      friction: this.props.friction
    })
  }

  _renderBackdrop() {
    if (!this.props.showBackdrop) {
      return null
    }

    const {top, bottom} = this.props.draggableRange

    const backdropOpacity = this.props.animatedValue.interpolate({
      inputRange: [bottom, top],
      outputRange: [0, this.props.backdropOpacity],
      extrapolate: 'clamp'
    })

    return (
      <Animated.View
        key="backdrop"
        pointerEvents={this._backdropPointerEvents}
        ref={c => (this._backdrop = c)}
        onTouchStart={() => this._flick.stop()}
        onTouchEnd={() => this.hide()}
        style={[styles.backdrop, {opacity: backdropOpacity}]}
      />
    )
  }

  _renderContent() {
    const {
      height,
      draggableRange: {top, bottom}
    } = this.props

    const translateY = this.props.animatedValue.interpolate({
      inputRange: [bottom, top],
      outputRange: [-bottom, -top],
      extrapolate: 'clamp'
    })

    const transform = {transform: [{translateY}]}

    const animatedContainerStyles = [
      styles.animatedContainer,
      transform,
      {height, bottom: -height}
    ]

    if (typeof this.props.children === 'function') {
      return (
        <Animated.View
          key="content"
          pointerEvents="box-none"
          style={animatedContainerStyles}>
          {this.props.children(this._panResponder.panHandlers)}
        </Animated.View>
      )
    }

    return (
      <Animated.View
        key="content"
        pointerEvents="box-none"
        style={animatedContainerStyles}
        {...this._panResponder.panHandlers}>
        {this.props.children}
      </Animated.View>
    )
  }

  render() {
    return [this._renderBackdrop(), this._renderContent()]
  }

  show(mayBeValueOrOptions) {
    if (!mayBeValueOrOptions) {
      const {top} = this.props.draggableRange
      return this._triggerAnimation({toValue: top})
    }

    if (typeof mayBeValueOrOptions === 'object') {
      return this._triggerAnimation(mayBeValueOrOptions)
    }

    return this._triggerAnimation({toValue: mayBeValueOrOptions})
  }

  hide() {
    const {bottom} = this.props.draggableRange
    this._triggerAnimation({toValue: bottom})
  }

  async scrollIntoView(node, options = {}) {
    if (!this._keyboardYPosition) {
      return
    }

    // Stop any animation when the keyboard starts showing
    this._flick.stop()

    const {y} = await measureElement(node)
    const extraMargin = options.keyboardExtraMargin || Constants.KEYBOARD_EXTRA_MARGIN // prettier-ignore
    const keyboardActualPos = this._keyboardYPosition - extraMargin

    if (y > keyboardActualPos) {
      this._lastPosition = this.props.animatedValue.__getValue()

      const fromKeyboardToElement = y - keyboardActualPos
      const transitionDistance = this._lastPosition + fromKeyboardToElement

      Animated.timing(this.props.animatedValue, {
        toValue: transitionDistance,
        duration: Constants.KEYBOARD_TRANSITION_DURATION
      }).start()
    }
  }
}

export default SlidingUpPanel
<|endoftext|>


<commit_before>var FileInfo = loadExtension("qbs.FileInfo");

function soname(product, outputFileName) {
    if (product.moduleProperty("qbs", "targetOS").contains("darwin")) {
        if (product.moduleProperty("bundle", "isBundle"))
            outputFileName = product.moduleProperty("bundle", "executablePath");
        var prefix = product.moduleProperty("cpp", "installNamePrefix");
        if (prefix)
            outputFileName = FileInfo.joinPaths(prefix, outputFileName);
        return outputFileName;
    }

    function majorVersion(version, defaultValue) {
        var n = parseInt(version, 10);
        return isNaN(n) ? defaultValue : n;
    }

    if (product.version) {
        var major = majorVersion(product.version);
        if (major) {
            return outputFileName.substr(0, outputFileName.length - product.version.length)
                    + major;
        }
    }
    return outputFileName;
}
<commit_msg>Fix soname generation on non-Darwin Unix platforms.<commit_after>var FileInfo = loadExtension("qbs.FileInfo");

function soname(product, outputFileName) {
    if (product.moduleProperty("qbs", "targetOS").contains("darwin")) {
        if (product.moduleProperty("bundle", "isBundle"))
            outputFileName = product.moduleProperty("bundle", "executablePath");
        var prefix = product.moduleProperty("cpp", "installNamePrefix");
        if (prefix)
            outputFileName = FileInfo.joinPaths(prefix, outputFileName);
        return outputFileName;
    }

    function majorVersion(version, defaultValue) {
        var n = parseInt(version, 10);
        return isNaN(n) ? defaultValue : n;
    }

    var version = product.moduleProperty("cpp", "internalVersion");
    if (version) {
        var major = majorVersion(version);
        if (major) {
            return outputFileName.substr(0, outputFileName.length - version.length)
                    + major;
        }
    }
    return outputFileName;
}
<|endoftext|>


<commit_before>var labName = $('#lab-name').val();
var dateRange = $('#date-range').val();
var pageLen = $('#page-len').val();
var searchFilter = $('#search-filter').val();

function populateLabTable(data) {
    'use strict';

    var localData = data.result,
        table = null;

    table = $('#labtable').dataTable({
        'dom': '<"row"<"col-xs-12 col-sm-12 col-md-6 col-lg-6"' +
            '<"length-menu"l>>' +
            '<"col-xs-12 col-sm-12 col-md-4 col-lg-4 col-lg-offset-2"f>r' +
            '<"col-xs-12 col-sm-12 col-md-12 col-lg-12"t>>' +
            '<"row"<"col-xs-12 col-sm-12 col-md-6 col-lg-6"i>' +
            '<"col-xs-12 col-sm-12 col-md-6 col-lg-6"p>>',
        'language': {
            'lengthMenu': '_MENU_ boot reports per page',
            'zeroRecords': '
No boot reports to display.
',
            'search': '
' +
                '' +
                '_INPUT_
'
        },
        'initComplete': function(settings, data) {
            $('#table-loading').remove();
            $('#table-div').fadeIn('slow', 'linear');

            var api = this.api();

            if (pageLen !== undefined && pageLen !== null) {
                if (pageLen.length > 0) {
                    pageLen = Number(pageLen);
                    if (isNaN(pageLen)) {
                        pageLen = 25;
                    }

                    api.page.len(pageLen).draw();
                }
            }

            if (searchFilter !== null && searchFilter !== undefined) {
                if (searchFilter.length > 0) {
                    api.search(searchFilter, true).draw();
                }
            }
        },
        'lengthMenu': [25, 50, 75, 100],
        'deferRender': true,
        'ordering': true,
        'processing': true,
        'stateDuration': -1,
        'stateSave': true,
        'order': [5, 'desc'],
        'search': {
            'regex': true,
            'smart': true
        },
        'data': localData,
        'columns': [
            {
                'data': '_id',
                'visible': false,
                'searchable': false,
                'orderable': false
            },
            {
                'data': 'job',
                'title': 'Tree',
                'type': 'string',
                'render': function(data) {
                    return '' + data + '';
                }
            },
            {
                'data': 'kernel',
                'title': 'Kernel',
                'type': 'string',
                'render': function(data, type, object) {
                    return '' + data +
                        '';
                }
            },
            {
                'data': 'board',
                'title': 'Board Model',
                'render': function(data, type, object) {
                    return '' + data + '';
                }
            },
            {
                'data': 'defconfig_full',
                'title': 'Defconfig',
                'render': function(data, type, object) {
                    var display = null,
                        href = null,
                        linkEl = null,
                        board = object.board,
                        job = object.job,
                        kernel = object.kernel;

                    href = '/boot/' + board + '/job/' + job + '/kernel/' +
                        kernel + '/defconfig/' + data + '/';
                    linkEl = '' +
                        data + '';

                    if (data.length > 33) {
                        display = '' +
                            '' +
                            data.slice(0, 33) + '…';
                    } else {
                        display = linkEl;
                    }
                    return display;
                }
            },
            {
                'data': 'created_on',
                'title': 'Date',
                'type': 'date',
                'className': 'pull-center',
                'render': function(data) {
                    var created = new Date(data.$date);
                    return created.getCustomISODate();
                }
            },
            {
                'data': 'status',
                'title': 'Status',
                'type': 'string',
                'className': 'pull-center',
                'render': function(data) {
                    var displ;
                    switch (data) {
                        case 'PASS':
                            displ = '' +
                                '' +
                                '';
                            break;
                        case 'FAIL':
                            displ = '' +
                                '' +
                                '' +
                                '';
                            break;
                        case 'OFFLINE':
                            displ = '<span rel="tooltip"' +
                                'data-toggle="tooltip"' +
                                'title="Board offline"' +
                                '' +
                                '' +
                                '';
                            break;
                        default:
                            displ = '' +
                                '' +
                                '' +
                                '';
                            break;
                    }
                    return displ;
                }
            },
            {
                'data': 'board',
                'title': '',
                'orderable': false,
                'searchable': false,
                'className': 'pull-center',
                'render': function(data, type, object) {
                    var defconfigFull = object.defconfig_full,
                        kernel = object.kernel,
                        job = object.job,
                        lab = object.lab_name;

                    return '' +
                        '';
                }
            }
        ]
    });

    $(document).on('click', '#labtable tbody tr', function() {
        var localTable = table.fnGetData(this),
            location = '#';
        if (localTable) {
            location = '/boot/' + localTable.board + '/job/' +
                localTable.job + '/kernel/' + localTable.kernel +
                '/defconfig/' + localTable.defconfig_full + '/lab/' +
                localTable.lab_name + '/';
            if (localTable._id !== null) {
                location += '?_id=' + localTable._id.$oid;
            }
            window.location = location;
        }
    });

    $('#search-area > .input-sm').attr('placeholder', 'Filter the results');
    $('.input-sm').keyup(function(key) {
        // Remove focus from input when Esc is pressed.
        if (key.keyCode === 27) {
            $(this).blur();
        }
    });
}

$(document).ready(function() {
    'use strict';
    $('#li-boot').addClass('active');
    $('#table-div').hide();

    var ajaxDeferredCall = null,
        ajaxData = null,
        errorReason = 'Error loading lab data';

    ajaxData = {
        'lab_name': labName,
        'date_range': dateRange
    };
    ajaxDeferredCall = JSBase.createDeferredCall(
        '/_ajax/boot',
        'GET',
        ajaxData,
        null,
        null,
        errorReason
    );

    $.when(ajaxDeferredCall).done(populateLabTable);
});
<commit_msg>Remove refactored library.<commit_after><|endoftext|>


<commit_before><commit_msg>Resolved conflict<commit_after><|endoftext|>


<commit_before>'use strict';

import postcss from 'postcss';
import convert from './lib/convert';
import valueParser, {unit} from 'postcss-value-parser';

function parseWord (node, opts, stripZeroUnit) {
    let pair = unit(node.value);
    if (pair) {
        let num = Number(pair.number);
        let u = pair.unit.toLowerCase();
        if (num === 0) {
            node.value = (
                stripZeroUnit ||
                u === 'ms' ||
                u === 's' ||
                u === 'deg'||
                u === 'turn') ? 0 + u : 0;
        } else {
            node.value = convert(num, u, opts);
        }
    }
}

function transform (opts) {
    return decl => {
        if (~decl.prop.indexOf('flex')) {
            return;
        }

        decl.value = valueParser(decl.value).walk(function (node) {
            if (node.type === 'word') {
                parseWord(node, opts);
            } else if (node.type === 'function') {
                if (node.value === 'calc') {
                    node.nodes.forEach(function walkNodes (node) {
                        if (node.type === 'word') {
                            parseWord(node, opts, true);
                        }
                        if (node.type === 'function') {
                            node.nodes.forEach(walkNodes);
                        }
                    });
                    return false;
                } else if (node.value === 'url') {
                    return false;
                }
            }
        }).toString();
    };
}

export default postcss.plugin('postcss-convert-values', (opts) => {
    opts = opts || {};
    if (opts.length === undefined && opts.convertLength !== undefined) {
        console.warn('postcss-convert-values: `convertLength` option is deprecated. Use `length`');
        opts.length = opts.convertLength;
    }
    if (opts.length === undefined && opts.convertTime !== undefined) {
        console.warn('postcss-convert-values: `convertTime` option is deprecated. Use `time`');
        opts.time = opts.convertTime;
    }
    return css => {
        css.walkDecls(transform(opts));
    };
});
<commit_msg>& rad/grad values.<commit_after>'use strict';

import postcss from 'postcss';
import convert from './lib/convert';
import valueParser, {unit} from 'postcss-value-parser';

function parseWord (node, opts, stripZeroUnit) {
    let pair = unit(node.value);
    if (pair) {
        let num = Number(pair.number);
        let u = pair.unit.toLowerCase();
        if (num === 0) {
            node.value = (
                stripZeroUnit ||
                u === 'ms' ||
                u === 's' ||
                u === 'deg'||
                u === 'rad' ||
                u === 'grad' ||
                u === 'turn') ? 0 + u : 0;
        } else {
            node.value = convert(num, u, opts);
        }
    }
}

function transform (opts) {
    return decl => {
        if (~decl.prop.indexOf('flex')) {
            return;
        }

        decl.value = valueParser(decl.value).walk(function (node) {
            if (node.type === 'word') {
                parseWord(node, opts);
            } else if (node.type === 'function') {
                if (node.value === 'calc') {
                    node.nodes.forEach(function walkNodes (node) {
                        if (node.type === 'word') {
                            parseWord(node, opts, true);
                        }
                        if (node.type === 'function') {
                            node.nodes.forEach(walkNodes);
                        }
                    });
                    return false;
                } else if (node.value === 'url') {
                    return false;
                }
            }
        }).toString();
    };
}

export default postcss.plugin('postcss-convert-values', (opts) => {
    opts = opts || {};
    if (opts.length === undefined && opts.convertLength !== undefined) {
        console.warn('postcss-convert-values: `convertLength` option is deprecated. Use `length`');
        opts.length = opts.convertLength;
    }
    if (opts.length === undefined && opts.convertTime !== undefined) {
        console.warn('postcss-convert-values: `convertTime` option is deprecated. Use `time`');
        opts.time = opts.convertTime;
    }
    return css => {
        css.walkDecls(transform(opts));
    };
});
<|endoftext|>


<commit_before>/*
---
script: testigo.js
description: main testigo class
license: MIT-style license
authors:
- Mark Obcena
provides: [Testigo]
...
*/

(function(){

var Suite = require('./lib/suite').Suite;

var Testigo = function(callbacks){
	this.$suites = [];
	this.$results = {};

	this.$suiteCount = 0;
	this.$suitesDone = 0;
	this.$testCount = 0;
	this.$doneCount = 0;
	this.$passes = 0;
	this.$failures = 0;

	callbacks = callbacks || {};
	this.$callbacks = {
		before: callbacks.before,
		beforeSuite: callbacks.beforeSuite,
		beforeTest: callbacks.beforeTest,
		after: callbacks.after,
		afterSuite: callbacks.afterSuite,
		afterTest: callbacks.afterTest
	};
};

Testigo.prototype.setCallback = function(name, fn){
	this.$callbacks[name] = fn;
	return this;
};

Testigo.prototype.setCallbacks = function(keys){
	for (var key in keys) this.setCallback(key, keys[key]);
	return this;
}

Testigo.prototype.done = function(){
	return !(this.$suiteCount - this.$suitesDone);
};

Testigo.prototype.results = function(){
	if (!this.done()) return {};
	return {
		allPassed: (this.$failures === 0),
		tests: {
			passes: this.$passes,
			failures: this.$failures,
			total: this.$testCount
		},
		results: this.$results
	};
};

Testigo.prototype.describe = function(name, fn){
	var self = this;
	this.$suiteCount++;
	this.$results[name] = {};
	var suite = new Suite(name, fn, {
		before: function(suite, count){
			self.$testCount += count;
			if (self.$callbacks.beforeSuite instanceof Function)
				self.$callbacks.beforeSuite.call(null, suite, count);
		},
		after: function(suite, success, results){
			self.$suitesDone++;
			self.$passes += results.tests.passes;
			self.$failures += results.tests.failures;
			self.results[suite] = results;
			if (self.$callbacks.afterSuite instanceof Function)
				self.$callbacks.afterSuite.call(null, suite, success, results);
			callNext.call(self);
		},
		beforeEach: function(suite, test, count){
			if (self.$callbacks.beforeTest instanceof Function)
				self.$callbacks.beforeTest.call(null, suite, test, count);
		},
		afterEach: function(suite, test, results){
			if (self.$callbacks.afterTest instanceof Function)
				self.$callbacks.afterTest.call(null, suite, test, results);
		}
	});
	this.$suites.push(suite);
};

var callNext = function(){
	var current = this.$suites.shift();
	if (current){
		current.run();
	} else {
		if (this.$callbacks.after instanceof Function)
			this.$callbacks.after.call(null, (this.$failures === 0), this.results());
	}
};

Testigo.prototype.run = function(){
	if (this.$callbacks.before instanceof Function)
		this.$callbacks.before.call(null, this.$suiteCount);
	callNext.call(this);
};

Testigo.version = [0,1,0];
Testigo.versionText = "0.1.0";

Testigo.Runners = {
	Simple: require('./runners/simple').SimpleRunner
};

exports.Testigo = Testigo;

})();<commit_msg>Incrementing version most-minor.<commit_after>/*
---
script: testigo.js
description: main testigo class
license: MIT-style license
authors:
- Mark Obcena
provides: [Testigo]
...
*/

(function(){

var Suite = require('./lib/suite').Suite;

var Testigo = function(callbacks){
	this.$suites = [];
	this.$results = {};

	this.$suiteCount = 0;
	this.$suitesDone = 0;
	this.$testCount = 0;
	this.$doneCount = 0;
	this.$passes = 0;
	this.$failures = 0;

	callbacks = callbacks || {};
	this.$callbacks = {
		before: callbacks.before,
		beforeSuite: callbacks.beforeSuite,
		beforeTest: callbacks.beforeTest,
		after: callbacks.after,
		afterSuite: callbacks.afterSuite,
		afterTest: callbacks.afterTest
	};
};

Testigo.prototype.setCallback = function(name, fn){
	this.$callbacks[name] = fn;
	return this;
};

Testigo.prototype.setCallbacks = function(keys){
	for (var key in keys) this.setCallback(key, keys[key]);
	return this;
}

Testigo.prototype.done = function(){
	return !(this.$suiteCount - this.$suitesDone);
};

Testigo.prototype.results = function(){
	if (!this.done()) return {};
	return {
		allPassed: (this.$failures === 0),
		tests: {
			passes: this.$passes,
			failures: this.$failures,
			total: this.$testCount
		},
		results: this.$results
	};
};

Testigo.prototype.describe = function(name, fn){
	var self = this;
	this.$suiteCount++;
	this.$results[name] = {};
	var suite = new Suite(name, fn, {
		before: function(suite, count){
			self.$testCount += count;
			if (self.$callbacks.beforeSuite instanceof Function)
				self.$callbacks.beforeSuite.call(null, suite, count);
		},
		after: function(suite, success, results){
			self.$suitesDone++;
			self.$passes += results.tests.passes;
			self.$failures += results.tests.failures;
			self.results[suite] = results;
			if (self.$callbacks.afterSuite instanceof Function)
				self.$callbacks.afterSuite.call(null, suite, success, results);
			callNext.call(self);
		},
		beforeEach: function(suite, test, count){
			if (self.$callbacks.beforeTest instanceof Function)
				self.$callbacks.beforeTest.call(null, suite, test, count);
		},
		afterEach: function(suite, test, results){
			if (self.$callbacks.afterTest instanceof Function)
				self.$callbacks.afterTest.call(null, suite, test, results);
		}
	});
	this.$suites.push(suite);
};

var callNext = function(){
	var current = this.$suites.shift();
	if (current){
		current.run();
	} else {
		if (this.$callbacks.after instanceof Function)
			this.$callbacks.after.call(null, (this.$failures === 0), this.results());
	}
};

Testigo.prototype.run = function(){
	if (this.$callbacks.before instanceof Function)
		this.$callbacks.before.call(null, this.$suiteCount);
	callNext.call(this);
};

Testigo.version = [0,1,1];
Testigo.versionText = "0.1.1";

Testigo.Runners = {
	Simple: require('./runners/simple').SimpleRunner
};

exports.Testigo = Testigo;

})();<|endoftext|>


<commit_before>/*!
    Copyright (C) 2017 Google Inc.
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

(function (can, $) {
  /*
    Below this line we're defining a few can.Components, which are in this file
    because they work similarly to the quick form controller (in fact, you should
    expect the quick form controller to be refactored into a component in the
    future) but they share no code with the quick form controller.

    the first component is quick add.  It is meant to have one or more form elements
    and a data-toggle="submit" link which will create a new join object between
    the parent instance and some selected option instance (likely picked through an
    autocomplete dropdown).

    Technically you can choose your instance however you want, as long as you find
    some way of getting its value into the component scope.  Extending this component
    with other methods to do that is fine.  You can also just pass it in when
    instantiating the component.
  */
  GGRC.Components('quickAdd', {
    tag: "ggrc-quick-add",
    // <content> in a component template will be replaced with whatever is contained
    //  within the component tag.  Since the views for the original uses of these components
    //  were already created with content, we just used <content> instead of making
    //  new view template files.
    template: "<content/>",
    scope: {
      parent_instance: null,
      source_mapping: null,
      join_model: "@",
      model: null,
      delay: "@",
      quick_create: "@",
      verify_event: "@",
      modal_description: "@",
      modal_title: "@",
      modal_button: "@",
      attributes: {},
      define: {
        deferred: {
          type: 'boolean',
          'default': false
        }
      },
      create_url: function () {
        var value = $.trim(this.element.find("input[type='text']").val());
        var dfd;

        // We are not validating the URL because application can locally we can
        // have URL's that are valid, but they wouldn't pass validation i.e.
        // - hi/there
        // - hi.something
        // - http://something.com etc
        // and thus we decided to validate just string existence
        if (!value || _.isEmpty(value)) {
          dfd = $.Deferred();
          dfd.reject({
            message: 'Please enter a URL'
          });
          return dfd.promise();
        }
        dfd = new CMS.Models.Document({
          link: value,
          title: value,
          context: this.scope.parent_instance.context || new CMS.Models.Context({
            id: null
          }),
          owners: [{type: "Person", id: GGRC.current_user.id}],
        });
        return dfd.save();
      }
    },
    events: {
      init: function () {
        this.scope.attr("controller", this);
      },
      // The inserted event fires when the component content is added to the DOM.
      //  At this time, live bound rendering should be resolved, which is not the
      //  case during init.
      inserted: function (el) {
        this.element.find("input:not([data-mapping], [data-lookup])").each(function(i, el) {
          this.scope.attributes.attr($(el).attr("name"), $(el).val());
        }.bind(this));
      },
      'a[data-toggle=submit]:not(.disabled):not([disabled]) click': function (el, ev) {
        var scope = this.scope;
        var join_model_class;
        var join_object;
        var quick_create;
        var created_dfd;
        var verify_dfd = $.Deferred();
        scope.attr('disabled', true);
        scope.attr('modal_description',
          this.element.context.attributes.modal_description.value);
        scope.attr('verify_event',
          !!this.element.context.attributes.verify_event);

        if (scope.attr("verify_event")) {
          GGRC.Controllers.Modals.confirm({
            modal_description: scope.attr("modal_description"),
            modal_confirm: scope.attr("modal_button"),
            modal_title: scope.attr("modal_title"),
            button_view: GGRC.mustache_path + "/quick_form/confirm_buttons.mustache"
          }, verify_dfd.resolve, verify_dfd.reject);
        } else {
          verify_dfd.resolve();
        }

        verify_dfd.done(function () {
          if (this.scope.quick_create && this.scope.quick_create !== "@") {
            quick_create = this.scope[this.scope.quick_create].bind(this);
            if (quick_create) {
              created_dfd = quick_create();
              if (!this.scope.deferred) {
                created_dfd
                  .fail(function (error) {
                    $(document.body).trigger('ajax:flash', {
                      error: error.message
                    });
                  })
                  .done(function (data) {
                    this.scope.attr('instance', data);
                  }.bind(this))
                  .always(function () {
                    scope.attr('disabled', false);
                  });
              }
            }
          }
          if (!created_dfd) {
            created_dfd = $.Deferred().resolve();
          }

          if (created_dfd.state() === 'rejected') {
            created_dfd.fail(function (error) {
              $(document.body).trigger('ajax:flash', {
                error: error.message
              });
            });
            return;
          }

          if (this.scope.deferred) {
            created_dfd.done(function (instance) {
              this.scope.parent_instance
                .mark_for_addition('related_objects_as_source', instance);
              el.trigger('modal:success', instance);
            }.bind(this));
            return;
          }

          created_dfd.then(function() {
            if (this.scope.join_model && this.scope.join_model !== "@") {
              join_model_class = CMS.Models[this.scope.join_model] || CMS.ModelHelpers[this.scope.join_model];
              join_object = {};
              if (this.scope.join_model === "Relationship") {
                join_object["source"] = this.scope.parent_instance;
                join_object["destination"] = this.scope.instance;
              } else {
                join_object[this.scope.instance.constructor.table_singular] = this.scope.instance;
              }
              join_object = new join_model_class($.extend(
                join_object,
                {
                  context: this.scope.parent_instance.context
                              || new CMS.Models.Context({id : null}),
                },
                this.scope.attributes.serialize()
              ));
            } else {
              join_object = GGRC.Mappings.make_join_object(
                this.scope.parent_instance,
                this.scope.instance || this.scope.attributes.instance,
                $.extend({
                  context : this.scope.parent_instance.context
                            || new CMS.Models.Context({id : null})
                          },
                          this.scope.attributes.serialize())
              );
            }
            this.bindXHRToButton(
              join_object.save().done(function() {
                el.trigger("modal:success", join_object);
              }),
              el
              );
          }.bind(this))
          .always(function () {
            scope.attr('disabled', false);
          });
        }.bind(this))
        .fail(function () {
          scope.attr('disabled', false);
        });
      },
      // this works like autocomplete_select on all modal forms and
      //  descendant class objects.
      autocomplete_select: function(el, event, ui) {
        var that = this;
        setTimeout(function() {
          that.scope.attr(el.attr("name"), ui.item);
        });
      },
      "input[null-if-empty] change" : function(el) {
        if (!el.val()) {
          this.scope.attributes.attr(el.attr("name"), null);
        }
      },
      "input:not([data-mapping], [data-lookup]) change" : function(el) {
        this.scope.attributes.attr(el.attr("name"), el.val());
      },
      ".ui-autocomplete-input modal:success" : function(el, ev, data, options) {
        var that = this,
          multi_map = data.multi_map,
          join_model_class,
          join_object;

        if(multi_map){
          var length = data.arr.length,
              my_data;

          if (length == 1){
            my_data = data.arr[0];

            GGRC.Mappings.make_join_object(
              this.scope.parent_instance,
              my_data,
              $.extend({
                context : this.scope.parent_instance.context
                        || new CMS.Models.Context({id : null})
                        },
                        this.scope.attributes.serialize())
            ).save().done(function() {
              that.element.find("a[data-toggle=submit]").trigger("modal:success");
            });
          }

          else{
            for(var i = 0; i < length-1; i++){
              my_data = data.arr[i];

              GGRC.Mappings.make_join_object(
                this.scope.parent_instance,
                my_data,
                $.extend({
                  context : this.scope.parent_instance.context
                          || new CMS.Models.Context({id : null})
                          },
                          this.scope.attributes.serialize())
              ).save().done(function(){});
            }
            my_data = data.arr[length-1];
            GGRC.Mappings.make_join_object(
              this.scope.parent_instance,
              my_data,
              $.extend({
                context : this.scope.parent_instance.context
                        || new CMS.Models.Context({id : null})
                        },
                        this.scope.attributes.serialize())
            ).save().done(function() {
              that.element.find("a[data-toggle=submit]").trigger("modal:success");
            });
          }
          //end multi-map
        } else {

          if (this.scope.join_model && this.scope.join_model !== "@") {
            join_model_class = CMS.Models[this.scope.join_model] || CMS.ModelHelpers[this.scope.join_model];
            join_object = new join_model_class(this.scope.attributes.serialize());
          } else {
            join_object = GGRC.Mappings.make_join_object(
              this.scope.parent_instance,
              data,
              $.extend({
                context : this.scope.parent_instance.context
                          || new CMS.Models.Context({id : null})
                        },
                        this.scope.attributes.serialize())
            );
          }
          join_object.save().done(function() {
             that.element.find("a[data-toggle=submit]").trigger("modal:success");
          });
        }
      }
    },
    helpers: {
      // Mapping-based autocomplete selectors use this helper to
      //  attach the mapping autocomplete ui widget.  These elements should
      //  be decorated with data-mapping attributes.
      mapping_autocomplete : function(options) {
        return function(el) {
          var $el = $(el);
          $el.ggrc_mapping_autocomplete({
            controller : options.contexts.attr("controller"),
            model : $el.data("model"),
            mapping : false
          });
        };
      }
    }
  }, true);
})(window.can, window.can.$);
<commit_msg>Update modal description only if verification needed<commit_after>/*!
    Copyright (C) 2017 Google Inc.
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

(function (can, $) {
  /*
    Below this line we're defining a few can.Components, which are in this file
    because they work similarly to the quick form controller (in fact, you should
    expect the quick form controller to be refactored into a component in the
    future) but they share no code with the quick form controller.

    the first component is quick add.  It is meant to have one or more form elements
    and a data-toggle="submit" link which will create a new join object between
    the parent instance and some selected option instance (likely picked through an
    autocomplete dropdown).

    Technically you can choose your instance however you want, as long as you find
    some way of getting its value into the component scope.  Extending this component
    with other methods to do that is fine.  You can also just pass it in when
    instantiating the component.
  */
  GGRC.Components('quickAdd', {
    tag: "ggrc-quick-add",
    // <content> in a component template will be replaced with whatever is contained
    //  within the component tag.  Since the views for the original uses of these components
    //  were already created with content, we just used <content> instead of making
    //  new view template files.
    template: "<content/>",
    scope: {
      parent_instance: null,
      source_mapping: null,
      join_model: "@",
      model: null,
      delay: "@",
      quick_create: "@",
      verify_event: "@",
      modal_description: "@",
      modal_title: "@",
      modal_button: "@",
      attributes: {},
      define: {
        deferred: {
          type: 'boolean',
          'default': false
        }
      },
      create_url: function () {
        var value = $.trim(this.element.find("input[type='text']").val());
        var dfd;

        // We are not validating the URL because application can locally we can
        // have URL's that are valid, but they wouldn't pass validation i.e.
        // - hi/there
        // - hi.something
        // - http://something.com etc
        // and thus we decided to validate just string existence
        if (!value || _.isEmpty(value)) {
          dfd = $.Deferred();
          dfd.reject({
            message: 'Please enter a URL'
          });
          return dfd.promise();
        }
        dfd = new CMS.Models.Document({
          link: value,
          title: value,
          context: this.scope.parent_instance.context || new CMS.Models.Context({
            id: null
          }),
          owners: [{type: "Person", id: GGRC.current_user.id}],
        });
        return dfd.save();
      }
    },
    events: {
      init: function () {
        this.scope.attr("controller", this);
      },
      // The inserted event fires when the component content is added to the DOM.
      //  At this time, live bound rendering should be resolved, which is not the
      //  case during init.
      inserted: function (el) {
        this.element.find("input:not([data-mapping], [data-lookup])").each(function(i, el) {
          this.scope.attributes.attr($(el).attr("name"), $(el).val());
        }.bind(this));
      },
      'a[data-toggle=submit]:not(.disabled):not([disabled]) click': function (el, ev) {
        var scope = this.scope;
        var join_model_class;
        var join_object;
        var quick_create;
        var created_dfd;
        var verify_dfd = $.Deferred();
        scope.attr('disabled', true);
        scope.attr('verify_event',
          !!this.element.context.attributes.verify_event);

        // Update modal description only if verification is needed
        if (this.element.context.attributes.modal_description) {
          scope.attr('modal_description',
            this.element.context.attributes.modal_description.value);
        }

        if (scope.attr("verify_event")) {
          GGRC.Controllers.Modals.confirm({
            modal_description: scope.attr("modal_description"),
            modal_confirm: scope.attr("modal_button"),
            modal_title: scope.attr("modal_title"),
            button_view: GGRC.mustache_path + "/quick_form/confirm_buttons.mustache"
          }, verify_dfd.resolve, verify_dfd.reject);
        } else {
          verify_dfd.resolve();
        }

        verify_dfd.done(function () {
          if (this.scope.quick_create && this.scope.quick_create !== "@") {
            quick_create = this.scope[this.scope.quick_create].bind(this);
            if (quick_create) {
              created_dfd = quick_create();
              if (!this.scope.deferred) {
                created_dfd
                  .fail(function (error) {
                    $(document.body).trigger('ajax:flash', {
                      error: error.message
                    });
                  })
                  .done(function (data) {
                    this.scope.attr('instance', data);
                  }.bind(this))
                  .always(function () {
                    scope.attr('disabled', false);
                  });
              }
            }
          }
          if (!created_dfd) {
            created_dfd = $.Deferred().resolve();
          }

          if (created_dfd.state() === 'rejected') {
            created_dfd.fail(function (error) {
              $(document.body).trigger('ajax:flash', {
                error: error.message
              });
            });
            return;
          }

          if (this.scope.deferred) {
            created_dfd.done(function (instance) {
              this.scope.parent_instance
                .mark_for_addition('related_objects_as_source', instance);
              el.trigger('modal:success', instance);
            }.bind(this));
            return;
          }

          created_dfd.then(function() {
            if (this.scope.join_model && this.scope.join_model !== "@") {
              join_model_class = CMS.Models[this.scope.join_model] || CMS.ModelHelpers[this.scope.join_model];
              join_object = {};
              if (this.scope.join_model === "Relationship") {
                join_object["source"] = this.scope.parent_instance;
                join_object["destination"] = this.scope.instance;
              } else {
                join_object[this.scope.instance.constructor.table_singular] = this.scope.instance;
              }
              join_object = new join_model_class($.extend(
                join_object,
                {
                  context: this.scope.parent_instance.context
                              || new CMS.Models.Context({id : null}),
                },
                this.scope.attributes.serialize()
              ));
            } else {
              join_object = GGRC.Mappings.make_join_object(
                this.scope.parent_instance,
                this.scope.instance || this.scope.attributes.instance,
                $.extend({
                  context : this.scope.parent_instance.context
                            || new CMS.Models.Context({id : null})
                          },
                          this.scope.attributes.serialize())
              );
            }
            this.bindXHRToButton(
              join_object.save().done(function() {
                el.trigger("modal:success", join_object);
              }),
              el
              );
          }.bind(this))
          .always(function () {
            scope.attr('disabled', false);
          });
        }.bind(this))
        .fail(function () {
          scope.attr('disabled', false);
        });
      },
      // this works like autocomplete_select on all modal forms and
      //  descendant class objects.
      autocomplete_select: function(el, event, ui) {
        var that = this;
        setTimeout(function() {
          that.scope.attr(el.attr("name"), ui.item);
        });
      },
      "input[null-if-empty] change" : function(el) {
        if (!el.val()) {
          this.scope.attributes.attr(el.attr("name"), null);
        }
      },
      "input:not([data-mapping], [data-lookup]) change" : function(el) {
        this.scope.attributes.attr(el.attr("name"), el.val());
      },
      ".ui-autocomplete-input modal:success" : function(el, ev, data, options) {
        var that = this,
          multi_map = data.multi_map,
          join_model_class,
          join_object;

        if(multi_map){
          var length = data.arr.length,
              my_data;

          if (length == 1){
            my_data = data.arr[0];

            GGRC.Mappings.make_join_object(
              this.scope.parent_instance,
              my_data,
              $.extend({
                context : this.scope.parent_instance.context
                        || new CMS.Models.Context({id : null})
                        },
                        this.scope.attributes.serialize())
            ).save().done(function() {
              that.element.find("a[data-toggle=submit]").trigger("modal:success");
            });
          }

          else{
            for(var i = 0; i < length-1; i++){
              my_data = data.arr[i];

              GGRC.Mappings.make_join_object(
                this.scope.parent_instance,
                my_data,
                $.extend({
                  context : this.scope.parent_instance.context
                          || new CMS.Models.Context({id : null})
                          },
                          this.scope.attributes.serialize())
              ).save().done(function(){});
            }
            my_data = data.arr[length-1];
            GGRC.Mappings.make_join_object(
              this.scope.parent_instance,
              my_data,
              $.extend({
                context : this.scope.parent_instance.context
                        || new CMS.Models.Context({id : null})
                        },
                        this.scope.attributes.serialize())
            ).save().done(function() {
              that.element.find("a[data-toggle=submit]").trigger("modal:success");
            });
          }
          //end multi-map
        } else {

          if (this.scope.join_model && this.scope.join_model !== "@") {
            join_model_class = CMS.Models[this.scope.join_model] || CMS.ModelHelpers[this.scope.join_model];
            join_object = new join_model_class(this.scope.attributes.serialize());
          } else {
            join_object = GGRC.Mappings.make_join_object(
              this.scope.parent_instance,
              data,
              $.extend({
                context : this.scope.parent_instance.context
                          || new CMS.Models.Context({id : null})
                        },
                        this.scope.attributes.serialize())
            );
          }
          join_object.save().done(function() {
             that.element.find("a[data-toggle=submit]").trigger("modal:success");
          });
        }
      }
    },
    helpers: {
      // Mapping-based autocomplete selectors use this helper to
      //  attach the mapping autocomplete ui widget.  These elements should
      //  be decorated with data-mapping attributes.
      mapping_autocomplete : function(options) {
        return function(el) {
          var $el = $(el);
          $el.ggrc_mapping_autocomplete({
            controller : options.contexts.attr("controller"),
            model : $el.data("model"),
            mapping : false
          });
        };
      }
    }
  }, true);
})(window.can, window.can.$);
<|endoftext|>


<commit_before>var Physics = function() {
    PIXELS_PER_METER = 16;
    return function(context, gameWidth, gameHeight, endGameCallback, pulseCallback) {
        var that = this;
        function newDebugDraw() {
            var debugDraw = new b2DebugDraw();
            debugDraw.SetSprite(document.getElementById("sup").getContext("2d"));
            debugDraw.SetDrawScale(PIXELS_PER_METER*that.debugScale);
            debugDraw.SetFillAlpha(0.5);
            debugDraw.SetLineThickness(1.0);
            debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
            return debugDraw;
        }

        var worldClock = new Clock(Constants.k('game_length'));
        worldClock.onOver(endGameCallback);
        worldClock.onPulse(pulseCallback);

        this.startClock = worldClock.start;

        var b2Vec2 = Box2D.Common.Math.b2Vec2;
        var b2BodyDef = Box2D.Dynamics.b2BodyDef;
        var b2Body = Box2D.Dynamics.b2Body;
        var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
        var b2Fixture = Box2D.Dynamics.b2Fixture;
        var b2World = Box2D.Dynamics.b2World;
        var b2MassData = Box2D.Collision.Shapes.b2MassData;
        var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
        var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
        var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
        var b2Settings = Box2D.Common.b2Settings;
        this.toRemove = [];
        this.boxesRemoved = 0;

        b2Settings.b2_maxTranslation = 10000.0;
        b2Settings.b2_maxTranslationSquared = 10000.0*10000.0;
        b2Settings.b2MixRestitution = function(r1, r2) {
            if (r1 === undefined)
                r1 = 0;
            if (r2 === undefined)
                r2 = 0;
            return Math.min(r1, r2);
        };

        var turnRate = Constants.k('world_rotation_rate');
        var targetRotation = 0;
        var rotation = 0;
        var gravity = 0.00001;

        this.world = new b2World(newGravity(), false);
        this.world.UserData = this;

        gravity = Constants.k('world_gravity');
        that.world.SetGravity(newGravity());

        var setPhysicalProperties = function(cls, fix, massless) {
            if (massless === undefined) {
                massless = false;
            }
            fix.SetFriction(Constants.k(cls + '_friction'));
            fix.SetRestitution(Constants.k(cls + '_restitution'));
            if (!massless) {
                fix.SetDensity(Constants.k(cls + '_density'));
            }
        };

        newWorld(endGameCallback);

        this.newBlock = function(cls, stage) {
            var fd                 = new b2FixtureDef;
            fd.shape               = new b2PolygonShape();
            fd.density = 1.0;
            fd.friction = 0.3;
            fd.restitution = 0.1;

            badBlockChance = Constants.k('block_bad_prob');
            var killsYou = Math.random() < badBlockChance;
            if (cls == "block" && killsYou) {
                cls = "block_bad";
            }

            value = Constants.k(cls + '_image');
            scale = Constants.k(cls + '_scale');
            flags = Constants.k(cls + '_flags');
            size = Constants.k('game_size');
            dist = Constants.k('mousehole_spawn_distance');
            randomness = Constants.k('spawn_randomness');
            var bodyDef                  = new b2BodyDef();
            bodyDef.type                 = b2Body.b2_dynamicBody;
            if (/c/.exec(flags)) {
                fd.shape.SetAsBox(1, 2.5);
            } else {
                fd.shape.SetAsBox(1, 1);
            }
            var radius = dist;
            var gravVec = newGravity()
            gravVec.NegativeSelf();
            gravVec.Normalize();
            gravVec.Multiply(radius);

            var x = size/2+gravVec.x+Math.random()*randomness-randomness/2;
            var y = size/2+gravVec.y+Math.random()*randomness-randomness/2;

            bodyDef.position.x           = x/PIXELS_PER_METER;
            bodyDef.position.y           = y/PIXELS_PER_METER;
            bodyDef.allowSleep           = false;
            if (/f/.exec(flags)) {
                bodyDef.fixedRotation = true;
            }
            var body = that.world.CreateBody(bodyDef);
            var fix = body.CreateFixture(fd);
            setPhysicalProperties(cls, fix);

            var e = new BaseEntity();

            // pick a source
            value = value.split(' ');
            value = value[Math.floor(Math.random() * value.length)];
            var beeTexture = PIXI.Texture.fromImage(value, true);
            var beeSprite = new PIXI.Sprite(beeTexture);

            if (/c/.exec(flags)) {
                beeSprite.anchor.x = 0.42;
            } else {
                beeSprite.anchor.x = 0.5;
            }
            beeSprite.anchor.y = 0.5;

            beeSprite.scale.x = scale;
            beeSprite.scale.y = scale;

            e = new PhysicsEntityAdapter(body, e)
            e = new SpriteAdapter(stage, beeSprite, e);
            if (/f/.exec(flags)) {
                e = new RotateWithWorldAdapter(e);
            }
            if (/d/.exec(flags)) {
                e = new PoisonAdapter(endGameCallback, 'player', e);
            }
            if (/g/.exec(flags)) {
                e = new TagAdapter(['worthPoints', 'grabbable'], e);
                e = new ScoreAdapter(function(x) {
                    x.seppuku();
                    clock.add(Constants.k('clock_cheese_bonus'));
                }, e);
            }
            if (/t/.exec(flags)) {
                e = new TagAdapter(['block'], e);
                e = new LimitedLifespanAdapter(10, function() {
                    var total = 0;
                    World.all(function(x) {
                        if (e.hasTag('block')) {
                            total += 1;
                        }
                    });
                    return total > 20;
                }, e);
            }
            if (/c/.exec(flags)) {
                e = new TagAdapter(['player'], e);
                e = new LateralMovementAdapter(e);
                e = new GrabAdapter(e);
                e = new JumpAdapter(e);
                e = new DebugLateralMovementAdapter(e);
                e = new JumpSpacingAdapter(e);
                e = new ControlAdapter(e);
            }
            body.SetUserData({tag: "ENTITY", entity:e});
            World.add(e);
        };

        this.draw = function(scaleFactor) {
            if (Constants.k('debug_physics')) {
                that.debugScale = scaleFactor;
                that.world.SetDebugDraw(newDebugDraw());
                that.world.DrawDebugData();
            }
        };

        var doneRotating = true
        this.update = function(callback) {
            if (Math.abs(targetRotation - rotation) > turnRate) {
                doneRotating = false;
                function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
                direction = sign(targetRotation - rotation);
                rotation += direction * turnRate;
                that.world.SetGravity(newGravity());
            } else {
                if (!doneRotating) {
                    rotation = targetRotation;
                    callback();
                    doneRotating = true;
                }
            }

            that.toRemove = [];
            that.world.Step(1/60,
                            Constants.k('physics_velocityIterations'),
                            Constants.k('physics_positionIterations'));
            for (var i = 0; i < that.toRemove.length; i++) {
                var body = that.toRemove[i];
                that.world.DestroyBody(body);
                World.del(body.GetUserData().entity);
                that.boxesRemoved += 1;
            }
            that.world.ClearForces();
        };

        this.getRotation = function() {
            return rotation;
        };

        this.setTargetRotation = function(rotation) {
            targetRotation = rotation;
        };

        function newGravity() {
            var vector = rotate([0, gravity*PIXELS_PER_METER]);
            return new b2Vec2(vector[0], vector[1]);
        }

        function rotate(vec) {
            var sine   = Math.sin(rotation * (Math.PI/180));
            var cosine = Math.cos(rotation * (Math.PI/180));
            return [vec[0] * cosine + vec[1] * sine,
                   vec[0] * -sine + vec[1] * cosine];
        }

        this.newPlatform = function(x, y, width, height, orientation, stage) {
            var orientationRadians  = orientation * (Math.PI/180);
            var platformDef         = new b2FixtureDef;
            platformDef.shape       = new b2PolygonShape();
            platformDef.friction    = 0.2;
            platformDef.restitution = 0.7;
            platformDef.shape.SetAsBox(width/2, height/2);

            var platformBodyDef    = new b2BodyDef();
            platformBodyDef.type   = b2Body.b2_staticBody;
            platformBodyDef.position.x = x/PIXELS_PER_METER;
            platformBodyDef.position.y = y/PIXELS_PER_METER;
            var body = that.world.CreateBody(platformBodyDef);
            body.SetAngle(orientationRadians*2);

            body.SetUserData({'tag': 'WALL'});

            var fix = body.CreateFixture(platformDef);
            setPhysicalProperties('platform', fix, true);
            var sprite = Constants.k("platform_sprite");
            var e = new BaseEntity();

            var beeTexture = PIXI.Texture.fromImage(sprite, true);
            var beeSprite = new PIXI.Sprite(beeTexture);

            beeSprite.anchor.x = 0.5;
            beeSprite.anchor.y = 0.5;

            beeSprite.width    = width*PIXELS_PER_METER;
            beeSprite.height   = height*PIXELS_PER_METER;

            e = new PhysicsEntityAdapter(body, e);
            e = new SpriteAdapter(stage, beeSprite, e);
            World.add(e);
        }

        function addWall(x, y, width, height, rotation, offset1, offset2) {
            //floor
            var floorDef                 = new b2FixtureDef;
            floorDef.shape               = new b2PolygonShape();
            floorDef.friction            = 0.2;
            floorDef.restitution         = 0.7;
            floorDef.shape.SetAsOrientedBox(width/2, height/2, new b2Vec2(offset1, offset2), rotation * Math.PI/180);

            var floorBodyDef                  = new b2BodyDef();
            floorBodyDef.type                 = b2Body.b2_staticBody;
            floorBodyDef.position.x           = x/PIXELS_PER_METER;
            floorBodyDef.position.y           = y/PIXELS_PER_METER;
            var body = that.world.CreateBody(floorBodyDef)
            body.SetUserData({'tag': 'WALL'});
            var fix = body.CreateFixture(floorDef);
            setPhysicalProperties('wall', fix, true);
        }

        function newWorld(endGameCallback) {
            addWall(gameWidth/2, 0, 10000, 3, 0, 0, 0);
            addWall(gameWidth/2, gameHeight, 10000, 3, 0, 0, 0);
            addWall(0, gameHeight/2, 3, 100000, 0, 0, 0);
            addWall(gameWidth, gameHeight/2, 3, 100000, 0, 0, 0);
            //addWall(gameWidth, gameHeight/2, 100000, 3, 45, 3, 3);
            addWall(gameWidth+gameWidth*0.25, gameHeight/2, 100000, 3, 45, 0, 0);
            addWall(-gameWidth*0.25, gameHeight/2, 100000, 3, 45, 0, 0);
            addWall(gameWidth/2, -gameHeight*0.25, 10000, 3, -45, 0, 0);
            addWall(gameWidth/2, gameHeight+gameHeight*0.25, 10000, 3, -45, 0, 0);
            var myContactListener = {
                "BeginContact" : function(contact) {
                    var data1 = contact.GetFixtureA().GetBody().GetUserData();
                    var data2 = contact.GetFixtureB().GetBody().GetUserData();
                    if (data1 && data2) {
                        if (data1.entity !== undefined && data2.entity !== undefined) {
                            data1.entity.collideInto(data2.entity);
                            data2.entity.collideInto(data1.entity);
                        }
                        if (data1.tag == "WALL" && data2.entity != null) {
                            data2.entity.doHitWall();
                        }
                        if (data2.tag == "WALL" && data1.entity != null) {
                            data1.entity.doHitWall();
                        }
                    }
                },
                "EndContact" : function() {
                },
                "PreSolve" : function() {
                },
                "PostSolve" : function() {
                }
            };
            that.world.SetContactListener(myContactListener);
        }

        this.newGoal = function(gameSize, stage) {
            width = Constants.k('goal_width');
            height = Constants.k('goal_height');
            image = Constants.k('goal_image');
            scale = Constants.k('goal_scale');
            //floor
            var floorDef                 = new b2FixtureDef;
            floorDef.shape               = new b2PolygonShape();
            floorDef.isSensor            = true;
            //floorDef.friction            = 0.2;
            //floorDef.restitution         = 0.7;
            floorDef.shape.SetAsBox(width/2, height/2);

            var floorBodyDef                  = new b2BodyDef();
            floorBodyDef.type                 = b2Body.b2_staticBody;
            floorBodyDef.position.x           = gameSize*0.5/PIXELS_PER_METER;
            floorBodyDef.position.y           = gameSize*0.5/PIXELS_PER_METER;
            var body = that.world.CreateBody(floorBodyDef);

            var fix = body.CreateFixture(floorDef);

            var e = new BaseEntity();

            var beeTexture = PIXI.Texture.fromImage(image, true);
            var beeSprite = new PIXI.Sprite(beeTexture);

            beeSprite.anchor.x = 0.5;
            beeSprite.anchor.y = 0.5;

            beeSprite.scale.x = scale;
            beeSprite.scale.y = scale;
            e = new PhysicsEntityAdapter(body, e);
            e = new SpriteAdapter(stage, beeSprite, e);
            e = new PoisonAdapter(function(x) {
                x.doHitGoal();
            }, 'worthPoints', e);
            body.SetUserData({tag: "ENTITY", entity: e});
            World.add(e);
        }

        this.rotate = rotate;
    };
}();
<commit_msg>Goal uses width and height parameters.<commit_after>var Physics = function() {
    PIXELS_PER_METER = 16;
    return function(context, gameWidth, gameHeight, endGameCallback, pulseCallback) {
        var that = this;
        function newDebugDraw() {
            var debugDraw = new b2DebugDraw();
            debugDraw.SetSprite(document.getElementById("sup").getContext("2d"));
            debugDraw.SetDrawScale(PIXELS_PER_METER*that.debugScale);
            debugDraw.SetFillAlpha(0.5);
            debugDraw.SetLineThickness(1.0);
            debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
            return debugDraw;
        }

        var worldClock = new Clock(Constants.k('game_length'));
        worldClock.onOver(endGameCallback);
        worldClock.onPulse(pulseCallback);

        this.startClock = worldClock.start;

        var b2Vec2 = Box2D.Common.Math.b2Vec2;
        var b2BodyDef = Box2D.Dynamics.b2BodyDef;
        var b2Body = Box2D.Dynamics.b2Body;
        var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
        var b2Fixture = Box2D.Dynamics.b2Fixture;
        var b2World = Box2D.Dynamics.b2World;
        var b2MassData = Box2D.Collision.Shapes.b2MassData;
        var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
        var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
        var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;
        var b2Settings = Box2D.Common.b2Settings;
        this.toRemove = [];
        this.boxesRemoved = 0;

        b2Settings.b2_maxTranslation = 10000.0;
        b2Settings.b2_maxTranslationSquared = 10000.0*10000.0;
        b2Settings.b2MixRestitution = function(r1, r2) {
            if (r1 === undefined)
                r1 = 0;
            if (r2 === undefined)
                r2 = 0;
            return Math.min(r1, r2);
        };

        var turnRate = Constants.k('world_rotation_rate');
        var targetRotation = 0;
        var rotation = 0;
        var gravity = 0.00001;

        this.world = new b2World(newGravity(), false);
        this.world.UserData = this;

        gravity = Constants.k('world_gravity');
        that.world.SetGravity(newGravity());

        var setPhysicalProperties = function(cls, fix, massless) {
            if (massless === undefined) {
                massless = false;
            }
            fix.SetFriction(Constants.k(cls + '_friction'));
            fix.SetRestitution(Constants.k(cls + '_restitution'));
            if (!massless) {
                fix.SetDensity(Constants.k(cls + '_density'));
            }
        };

        newWorld(endGameCallback);

        this.newBlock = function(cls, stage) {
            var fd                 = new b2FixtureDef;
            fd.shape               = new b2PolygonShape();
            fd.density = 1.0;
            fd.friction = 0.3;
            fd.restitution = 0.1;

            badBlockChance = Constants.k('block_bad_prob');
            var killsYou = Math.random() < badBlockChance;
            if (cls == "block" && killsYou) {
                cls = "block_bad";
            }

            value = Constants.k(cls + '_image');
            scale = Constants.k(cls + '_scale');
            flags = Constants.k(cls + '_flags');
            size = Constants.k('game_size');
            dist = Constants.k('mousehole_spawn_distance');
            randomness = Constants.k('spawn_randomness');
            var bodyDef                  = new b2BodyDef();
            bodyDef.type                 = b2Body.b2_dynamicBody;
            if (/c/.exec(flags)) {
                fd.shape.SetAsBox(1, 2.5);
            } else {
                fd.shape.SetAsBox(1, 1);
            }
            var radius = dist;
            var gravVec = newGravity()
            gravVec.NegativeSelf();
            gravVec.Normalize();
            gravVec.Multiply(radius);

            var x = size/2+gravVec.x+Math.random()*randomness-randomness/2;
            var y = size/2+gravVec.y+Math.random()*randomness-randomness/2;

            bodyDef.position.x           = x/PIXELS_PER_METER;
            bodyDef.position.y           = y/PIXELS_PER_METER;
            bodyDef.allowSleep           = false;
            if (/f/.exec(flags)) {
                bodyDef.fixedRotation = true;
            }
            var body = that.world.CreateBody(bodyDef);
            var fix = body.CreateFixture(fd);
            setPhysicalProperties(cls, fix);

            var e = new BaseEntity();

            // pick a source
            value = value.split(' ');
            value = value[Math.floor(Math.random() * value.length)];
            var beeTexture = PIXI.Texture.fromImage(value, true);
            var beeSprite = new PIXI.Sprite(beeTexture);

            if (/c/.exec(flags)) {
                beeSprite.anchor.x = 0.42;
            } else {
                beeSprite.anchor.x = 0.5;
            }
            beeSprite.anchor.y = 0.5;

            beeSprite.scale.x = scale;
            beeSprite.scale.y = scale;

            e = new PhysicsEntityAdapter(body, e)
            e = new SpriteAdapter(stage, beeSprite, e);
            if (/f/.exec(flags)) {
                e = new RotateWithWorldAdapter(e);
            }
            if (/d/.exec(flags)) {
                e = new PoisonAdapter(endGameCallback, 'player', e);
            }
            if (/g/.exec(flags)) {
                e = new TagAdapter(['worthPoints', 'grabbable'], e);
                e = new ScoreAdapter(function(x) {
                    x.seppuku();
                    clock.add(Constants.k('clock_cheese_bonus'));
                }, e);
            }
            if (/t/.exec(flags)) {
                e = new TagAdapter(['block'], e);
                e = new LimitedLifespanAdapter(10, function() {
                    var total = 0;
                    World.all(function(x) {
                        if (e.hasTag('block')) {
                            total += 1;
                        }
                    });
                    return total > 20;
                }, e);
            }
            if (/c/.exec(flags)) {
                e = new TagAdapter(['player'], e);
                e = new LateralMovementAdapter(e);
                e = new GrabAdapter(e);
                e = new JumpAdapter(e);
                e = new DebugLateralMovementAdapter(e);
                e = new JumpSpacingAdapter(e);
                e = new ControlAdapter(e);
            }
            body.SetUserData({tag: "ENTITY", entity:e});
            World.add(e);
        };

        this.draw = function(scaleFactor) {
            if (Constants.k('debug_physics')) {
                that.debugScale = scaleFactor;
                that.world.SetDebugDraw(newDebugDraw());
                that.world.DrawDebugData();
            }
        };

        var doneRotating = true
        this.update = function(callback) {
            if (Math.abs(targetRotation - rotation) > turnRate) {
                doneRotating = false;
                function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
                direction = sign(targetRotation - rotation);
                rotation += direction * turnRate;
                that.world.SetGravity(newGravity());
            } else {
                if (!doneRotating) {
                    rotation = targetRotation;
                    callback();
                    doneRotating = true;
                }
            }

            that.toRemove = [];
            that.world.Step(1/60,
                            Constants.k('physics_velocityIterations'),
                            Constants.k('physics_positionIterations'));
            for (var i = 0; i < that.toRemove.length; i++) {
                var body = that.toRemove[i];
                that.world.DestroyBody(body);
                World.del(body.GetUserData().entity);
                that.boxesRemoved += 1;
            }
            that.world.ClearForces();
        };

        this.getRotation = function() {
            return rotation;
        };

        this.setTargetRotation = function(rotation) {
            targetRotation = rotation;
        };

        function newGravity() {
            var vector = rotate([0, gravity*PIXELS_PER_METER]);
            return new b2Vec2(vector[0], vector[1]);
        }

        function rotate(vec) {
            var sine   = Math.sin(rotation * (Math.PI/180));
            var cosine = Math.cos(rotation * (Math.PI/180));
            return [vec[0] * cosine + vec[1] * sine,
                   vec[0] * -sine + vec[1] * cosine];
        }

        this.newPlatform = function(x, y, width, height, orientation, stage) {
            var orientationRadians  = orientation * (Math.PI/180);
            var platformDef         = new b2FixtureDef;
            platformDef.shape       = new b2PolygonShape();
            platformDef.friction    = 0.2;
            platformDef.restitution = 0.7;
            platformDef.shape.SetAsBox(width/2, height/2);

            var platformBodyDef    = new b2BodyDef();
            platformBodyDef.type   = b2Body.b2_staticBody;
            platformBodyDef.position.x = x/PIXELS_PER_METER;
            platformBodyDef.position.y = y/PIXELS_PER_METER;
            var body = that.world.CreateBody(platformBodyDef);
            body.SetAngle(orientationRadians*2);

            body.SetUserData({'tag': 'WALL'});

            var fix = body.CreateFixture(platformDef);
            setPhysicalProperties('platform', fix, true);
            var sprite = Constants.k("platform_sprite");
            var e = new BaseEntity();

            var beeTexture = PIXI.Texture.fromImage(sprite, true);
            var beeSprite = new PIXI.Sprite(beeTexture);

            beeSprite.anchor.x = 0.5;
            beeSprite.anchor.y = 0.5;

            beeSprite.width    = width*PIXELS_PER_METER;
            beeSprite.height   = height*PIXELS_PER_METER;

            e = new PhysicsEntityAdapter(body, e);
            e = new SpriteAdapter(stage, beeSprite, e);
            World.add(e);
        }

        function addWall(x, y, width, height, rotation, offset1, offset2) {
            //floor
            var floorDef                 = new b2FixtureDef;
            floorDef.shape               = new b2PolygonShape();
            floorDef.friction            = 0.2;
            floorDef.restitution         = 0.7;
            floorDef.shape.SetAsOrientedBox(width/2, height/2, new b2Vec2(offset1, offset2), rotation * Math.PI/180);

            var floorBodyDef                  = new b2BodyDef();
            floorBodyDef.type                 = b2Body.b2_staticBody;
            floorBodyDef.position.x           = x/PIXELS_PER_METER;
            floorBodyDef.position.y           = y/PIXELS_PER_METER;
            var body = that.world.CreateBody(floorBodyDef)
            body.SetUserData({'tag': 'WALL'});
            var fix = body.CreateFixture(floorDef);
            setPhysicalProperties('wall', fix, true);
        }

        function newWorld(endGameCallback) {
            addWall(gameWidth/2, 0, 10000, 3, 0, 0, 0);
            addWall(gameWidth/2, gameHeight, 10000, 3, 0, 0, 0);
            addWall(0, gameHeight/2, 3, 100000, 0, 0, 0);
            addWall(gameWidth, gameHeight/2, 3, 100000, 0, 0, 0);
            //addWall(gameWidth, gameHeight/2, 100000, 3, 45, 3, 3);
            addWall(gameWidth+gameWidth*0.25, gameHeight/2, 100000, 3, 45, 0, 0);
            addWall(-gameWidth*0.25, gameHeight/2, 100000, 3, 45, 0, 0);
            addWall(gameWidth/2, -gameHeight*0.25, 10000, 3, -45, 0, 0);
            addWall(gameWidth/2, gameHeight+gameHeight*0.25, 10000, 3, -45, 0, 0);
            var myContactListener = {
                "BeginContact" : function(contact) {
                    var data1 = contact.GetFixtureA().GetBody().GetUserData();
                    var data2 = contact.GetFixtureB().GetBody().GetUserData();
                    if (data1 && data2) {
                        if (data1.entity !== undefined && data2.entity !== undefined) {
                            data1.entity.collideInto(data2.entity);
                            data2.entity.collideInto(data1.entity);
                        }
                        if (data1.tag == "WALL" && data2.entity != null) {
                            data2.entity.doHitWall();
                        }
                        if (data2.tag == "WALL" && data1.entity != null) {
                            data1.entity.doHitWall();
                        }
                    }
                },
                "EndContact" : function() {
                },
                "PreSolve" : function() {
                },
                "PostSolve" : function() {
                }
            };
            that.world.SetContactListener(myContactListener);
        }

        this.newGoal = function(gameSize, stage) {
            width = Constants.k('goal_width');
            height = Constants.k('goal_height');
            image = Constants.k('goal_image');
            scale = Constants.k('goal_scale');
            //floor
            var floorDef                 = new b2FixtureDef;
            floorDef.shape               = new b2PolygonShape();
            floorDef.isSensor            = true;
            //floorDef.friction            = 0.2;
            //floorDef.restitution         = 0.7;
            floorDef.shape.SetAsBox(width/2, height/2);

            var floorBodyDef                  = new b2BodyDef();
            floorBodyDef.type                 = b2Body.b2_staticBody;
            floorBodyDef.position.x           = gameSize*0.5/PIXELS_PER_METER;
            floorBodyDef.position.y           = gameSize*0.5/PIXELS_PER_METER;
            var body = that.world.CreateBody(floorBodyDef);

            var fix = body.CreateFixture(floorDef);

            var e = new BaseEntity();

            var beeTexture = PIXI.Texture.fromImage(image, true);
            var beeSprite = new PIXI.Sprite(beeTexture);

            beeSprite.anchor.x = 0.5;
            beeSprite.anchor.y = 0.5;

            beeSprite.width = width*PIXELS_PER_METER;
            beeSprite.height = height*PIXELS_PER_METER;
            e = new PhysicsEntityAdapter(body, e);
            e = new SpriteAdapter(stage, beeSprite, e);
            e = new PoisonAdapter(function(x) {
                x.doHitGoal();
            }, 'worthPoints', e);
            body.SetUserData({tag: "ENTITY", entity: e});
            World.add(e);
        }

        this.rotate = rotate;
    };
}();
<|endoftext|>