# WHY IS THIS FILE HERE? ReactCSSTransitionGroup is causing
# inconsitency exceptions when you hammer on the animations and don't let them
# finish. This is from http://khan.github.io/react-components/#timeout-transition-group
# and uses timeouts to clean up elements rather than listeners on CSS events, which
# don't always seem to fire.

# https://github.com/facebook/react/issues/1707

React = require('react/addons')
PriorityUICoordinator = require('../priority-ui-coordinator')
ReactTransitionGroup = React.addons.TransitionGroup
TICK = 17

endEvents = ['webkitTransitionEnd', 'webkitAnimationEnd']

animationSupported = => true

###*
# Functions for element class management to replace dependency on jQuery
# addClass, removeClass and hasClass
###

addClass = (element, className) =>
  if element.classList
    element.classList.add className
  else if !hasClass(element, className)
    element.className = element.className + ' ' + className
  element

removeClass = (element, className) =>
  if hasClass(className)
    if element.classList
      element.classList.remove className
    else
      element.className = (' ' + element.className + ' ').replace(' ' + className + ' ', ' ').trim()
  element

hasClass = (element, className) =>
  if element.classList
    element.classList.contains className
  else
    (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1


###
Public: TimeoutTransitionGroup applies a CSS transition to the components added
or removed from it's children. It is similar to React's CSSTransitionGroup, but
that uses the 'transitionend' event, which browsers will not send for any
number of reasons, including the transitioning node not being painted.

This TimeoutTransitionGroup instead uses a user-defined timeout to determine
when it is a good time to remove the component. Currently there is only one
timeout specified, but in the future it would be nice to be able to specify
separate timeouts for enter and leave, in case the timeouts for those
animations differ. Even nicer would be some sort of inspection of the CSS to
automatically determine the duration of the animation or transition.

This is adapted from Facebook's CSSTransitionGroup which is in the React
addons and under the Apache 2.0 License.

Example:
```
<TimeoutTransitionGroup 
  leaveTimeout={125}
  enterTimeout={125}
  transitionName="sheet-toolbar">
  {toolbarElements[1..-1]}
</TimeoutTransitionGroup>
```
###
class TimeoutTransitionGroupChild extends React.Component

  transition: (animationType, finishCallback) =>
    node = React.findDOMNode(@)
    className = @props.name + '-' + animationType
    activeClassName = className + '-active'

    # If you animate back and forth fast enough, you can call `transition`
    # before a previous transition has finished. Make sure we cancel the
    # old timeout.
    if @animationTimeout
      clearTimeout(@animationTimeout)
      @animationTimeout = null

    if @animationTaskId
      PriorityUICoordinator.endPriorityTask(@animationTaskId)
      @animationTaskId = null

    # Block database responses, JSON parsing while we are in flight
    @animationTaskId = PriorityUICoordinator.beginPriorityTask()

    endListener = =>
      removeClass(node, className)
      removeClass(node, activeClassName)
      # Usually this optional callback is used for informing an owner of
      # a leave animation and telling it to remove the child.
      finishCallback and finishCallback()

      if @animationTaskId
        PriorityUICoordinator.endPriorityTask(@animationTaskId)
        @animationTaskId = null
      @animationTimeout = null
      return

    if !animationSupported()
      endListener()
    else
      if animationType == 'enter'
        @animationTimeout = setTimeout(endListener, @props.enterTimeout)
      else if animationType == 'leave'
        @animationTimeout = setTimeout(endListener, @props.leaveTimeout)

    addClass(node, className)

    # Need to do this to actually trigger a transition.
    @queueClass activeClassName
    return

  queueClass: (className) =>
    @classNameQueue.push className
    if !@timeout
      @timeout = setTimeout(@flushClassNameQueue, TICK)
    return
  
  flushClassNameQueue: =>
    @classNameQueue.forEach ((name) =>
      addClass(React.findDOMNode(@), name)
      return
    ).bind(this)
    @classNameQueue.length = 0
    @timeout = null
    return

  componentWillMount: =>
    @classNameQueue = []
    @animationTimeout = null
    @animationTaskId = null
    return

  componentWillUnmount: =>
    if @timeout
      clearTimeout(@timeout)
    if @animationTimeout
      clearTimeout(@animationTimeout)
      @animationTimeout = null
    if @animationTaskId
      PriorityUICoordinator.endPriorityTask(@animationTaskId)
      @animationTaskId = null
    return

  componentWillEnter: (done) =>
    if @props.enter
      @transition 'enter', done
    else
      done()
    return

  componentWillLeave: (done) =>
    if @props.leave
      @transition 'leave', done
    else
      done()
    return

  render: =>
    React.Children.only @props.children

class TimeoutTransitionGroup extends React.Component
  @propTypes =
    enterTimeout: React.PropTypes.number.isRequired
    leaveTimeout: React.PropTypes.number.isRequired
    transitionName: React.PropTypes.string.isRequired
    transitionEnter: React.PropTypes.bool
    transitionLeave: React.PropTypes.bool

  @defaultProps =
    transitionEnter: true
    transitionLeave: true

  _wrapChild: (child) =>
    <TimeoutTransitionGroupChild
      enterTimeout={@props.enterTimeout}
      leaveTimeout={@props.leaveTimeout}
      name={@props.transitionName}
      enter={@props.transitionEnter}
      leave={@props.transitionLeave}>
        {child}
    </TimeoutTransitionGroupChild>

  render: =>
    <ReactTransitionGroup
        {...@props}
        childFactory={@_wrapChild} />


module.exports = TimeoutTransitionGroup