import { warn } from 'core/util/debug' 
 | 
import { extend, once, noop } from 'shared/util' 
 | 
import { activeInstance } from 'core/instance/lifecycle' 
 | 
import { resolveTransition } from 'web/runtime/transition-util' 
 | 
  
 | 
export default { 
 | 
  create: enter, 
 | 
  activate: enter, 
 | 
  remove: leave 
 | 
} 
 | 
  
 | 
function enter (_, vnode) { 
 | 
  const el = vnode.elm 
 | 
  
 | 
  // call leave callback now 
 | 
  if (el._leaveCb) { 
 | 
    el._leaveCb.cancelled = true 
 | 
    el._leaveCb() 
 | 
  } 
 | 
  
 | 
  const data = resolveTransition(vnode.data.transition) 
 | 
  if (!data) { 
 | 
    return 
 | 
  } 
 | 
  
 | 
  /* istanbul ignore if */ 
 | 
  if (el._enterCb) { 
 | 
    return 
 | 
  } 
 | 
  
 | 
  const { 
 | 
    enterClass, 
 | 
    enterToClass, 
 | 
    enterActiveClass, 
 | 
    appearClass, 
 | 
    appearToClass, 
 | 
    appearActiveClass, 
 | 
    beforeEnter, 
 | 
    enter, 
 | 
    afterEnter, 
 | 
    enterCancelled, 
 | 
    beforeAppear, 
 | 
    appear, 
 | 
    afterAppear, 
 | 
    appearCancelled 
 | 
  } = data 
 | 
  
 | 
  let context = activeInstance 
 | 
  let transitionNode = activeInstance.$vnode 
 | 
  while (transitionNode && transitionNode.parent) { 
 | 
    transitionNode = transitionNode.parent 
 | 
    context = transitionNode.context 
 | 
  } 
 | 
  
 | 
  const isAppear = !context._isMounted || !vnode.isRootInsert 
 | 
  
 | 
  if (isAppear && !appear && appear !== '') { 
 | 
    return 
 | 
  } 
 | 
  
 | 
  const startClass = isAppear ? appearClass : enterClass 
 | 
  const toClass = isAppear ? appearToClass : enterToClass 
 | 
  const activeClass = isAppear ? appearActiveClass : enterActiveClass 
 | 
  const beforeEnterHook = isAppear ? (beforeAppear || beforeEnter) : beforeEnter 
 | 
  const enterHook = isAppear ? (typeof appear === 'function' ? appear : enter) : enter 
 | 
  const afterEnterHook = isAppear ? (afterAppear || afterEnter) : afterEnter 
 | 
  const enterCancelledHook = isAppear ? (appearCancelled || enterCancelled) : enterCancelled 
 | 
  
 | 
  const userWantsControl = 
 | 
    enterHook && 
 | 
    // enterHook may be a bound method which exposes 
 | 
    // the length of original fn as _length 
 | 
    (enterHook._length || enterHook.length) > 1 
 | 
  
 | 
  const stylesheet = vnode.context.$options.style || {} 
 | 
  const startState = stylesheet[startClass] 
 | 
  const transitionProperties = (stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][activeClass]) || {} 
 | 
  const endState = getEnterTargetState(el, stylesheet, startClass, toClass, activeClass) 
 | 
  const needAnimation = Object.keys(endState).length > 0 
 | 
  
 | 
  const cb = el._enterCb = once(() => { 
 | 
    if (cb.cancelled) { 
 | 
      enterCancelledHook && enterCancelledHook(el) 
 | 
    } else { 
 | 
      afterEnterHook && afterEnterHook(el) 
 | 
    } 
 | 
    el._enterCb = null 
 | 
  }) 
 | 
  
 | 
  // We need to wait until the native element has been inserted, but currently 
 | 
  // there's no API to do that. So we have to wait "one frame" - not entirely 
 | 
  // sure if this is guaranteed to be enough (e.g. on slow devices?) 
 | 
  setTimeout(() => { 
 | 
    const parent = el.parentNode 
 | 
    const pendingNode = parent && parent._pending && parent._pending[vnode.key] 
 | 
    if (pendingNode && 
 | 
      pendingNode.context === vnode.context && 
 | 
      pendingNode.tag === vnode.tag && 
 | 
      pendingNode.elm._leaveCb 
 | 
    ) { 
 | 
      pendingNode.elm._leaveCb() 
 | 
    } 
 | 
    enterHook && enterHook(el, cb) 
 | 
  
 | 
    if (needAnimation) { 
 | 
      const animation = vnode.context.$requireWeexModule('animation') 
 | 
      animation.transition(el.ref, { 
 | 
        styles: endState, 
 | 
        duration: transitionProperties.duration || 0, 
 | 
        delay: transitionProperties.delay || 0, 
 | 
        timingFunction: transitionProperties.timingFunction || 'linear' 
 | 
      }, userWantsControl ? noop : cb) 
 | 
    } else if (!userWantsControl) { 
 | 
      cb() 
 | 
    } 
 | 
  }, 16) 
 | 
  
 | 
  // start enter transition 
 | 
  beforeEnterHook && beforeEnterHook(el) 
 | 
  
 | 
  if (startState) { 
 | 
    if (typeof el.setStyles === 'function') { 
 | 
      el.setStyles(startState) 
 | 
    } else { 
 | 
      for (const key in startState) { 
 | 
        el.setStyle(key, startState[key]) 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
  
 | 
  if (!needAnimation && !userWantsControl) { 
 | 
    cb() 
 | 
  } 
 | 
} 
 | 
  
 | 
function leave (vnode, rm) { 
 | 
  const el = vnode.elm 
 | 
  
 | 
  // call enter callback now 
 | 
  if (el._enterCb) { 
 | 
    el._enterCb.cancelled = true 
 | 
    el._enterCb() 
 | 
  } 
 | 
  
 | 
  const data = resolveTransition(vnode.data.transition) 
 | 
  if (!data) { 
 | 
    return rm() 
 | 
  } 
 | 
  
 | 
  if (el._leaveCb) { 
 | 
    return 
 | 
  } 
 | 
  
 | 
  const { 
 | 
    leaveClass, 
 | 
    leaveToClass, 
 | 
    leaveActiveClass, 
 | 
    beforeLeave, 
 | 
    leave, 
 | 
    afterLeave, 
 | 
    leaveCancelled, 
 | 
    delayLeave 
 | 
  } = data 
 | 
  
 | 
  const userWantsControl = 
 | 
    leave && 
 | 
    // leave hook may be a bound method which exposes 
 | 
    // the length of original fn as _length 
 | 
    (leave._length || leave.length) > 1 
 | 
  
 | 
  const stylesheet = vnode.context.$options.style || {} 
 | 
  const startState = stylesheet[leaveClass] 
 | 
  const endState = stylesheet[leaveToClass] || stylesheet[leaveActiveClass] 
 | 
  const transitionProperties = (stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][leaveActiveClass]) || {} 
 | 
  
 | 
  const cb = el._leaveCb = once(() => { 
 | 
    if (el.parentNode && el.parentNode._pending) { 
 | 
      el.parentNode._pending[vnode.key] = null 
 | 
    } 
 | 
    if (cb.cancelled) { 
 | 
      leaveCancelled && leaveCancelled(el) 
 | 
    } else { 
 | 
      rm() 
 | 
      afterLeave && afterLeave(el) 
 | 
    } 
 | 
    el._leaveCb = null 
 | 
  }) 
 | 
  
 | 
  if (delayLeave) { 
 | 
    delayLeave(performLeave) 
 | 
  } else { 
 | 
    performLeave() 
 | 
  } 
 | 
  
 | 
  function performLeave () { 
 | 
    const animation = vnode.context.$requireWeexModule('animation') 
 | 
    // the delayed leave may have already been cancelled 
 | 
    if (cb.cancelled) { 
 | 
      return 
 | 
    } 
 | 
    // record leaving element 
 | 
    if (!vnode.data.show) { 
 | 
      (el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key] = vnode 
 | 
    } 
 | 
    beforeLeave && beforeLeave(el) 
 | 
  
 | 
    if (startState) { 
 | 
      animation.transition(el.ref, { 
 | 
        styles: startState 
 | 
      }, next) 
 | 
    } else { 
 | 
      next() 
 | 
    } 
 | 
  
 | 
    function next () { 
 | 
      animation.transition(el.ref, { 
 | 
        styles: endState, 
 | 
        duration: transitionProperties.duration || 0, 
 | 
        delay: transitionProperties.delay || 0, 
 | 
        timingFunction: transitionProperties.timingFunction || 'linear' 
 | 
      }, userWantsControl ? noop : cb) 
 | 
    } 
 | 
  
 | 
    leave && leave(el, cb) 
 | 
    if (!endState && !userWantsControl) { 
 | 
      cb() 
 | 
    } 
 | 
  } 
 | 
} 
 | 
  
 | 
// determine the target animation style for an entering transition. 
 | 
function getEnterTargetState (el, stylesheet, startClass, endClass, activeClass) { 
 | 
  const targetState = {} 
 | 
  const startState = stylesheet[startClass] 
 | 
  const endState = stylesheet[endClass] 
 | 
  const activeState = stylesheet[activeClass] 
 | 
  // 1. fallback to element's default styling 
 | 
  if (startState) { 
 | 
    for (const key in startState) { 
 | 
      targetState[key] = el.style[key] 
 | 
      if ( 
 | 
        process.env.NODE_ENV !== 'production' && 
 | 
        targetState[key] == null && 
 | 
        (!activeState || activeState[key] == null) && 
 | 
        (!endState || endState[key] == null) 
 | 
      ) { 
 | 
        warn( 
 | 
          `transition property "${key}" is declared in enter starting class (.${startClass}), ` + 
 | 
          `but not declared anywhere in enter ending class (.${endClass}), ` + 
 | 
          `enter active cass (.${activeClass}) or the element's default styling. ` + 
 | 
          `Note in Weex, CSS properties need explicit values to be transitionable.` 
 | 
        ) 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
  // 2. if state is mixed in active state, extract them while excluding 
 | 
  //    transition properties 
 | 
  if (activeState) { 
 | 
    for (const key in activeState) { 
 | 
      if (key.indexOf('transition') !== 0) { 
 | 
        targetState[key] = activeState[key] 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
  // 3. explicit endState has highest priority 
 | 
  if (endState) { 
 | 
    extend(targetState, endState) 
 | 
  } 
 | 
  return targetState 
 | 
} 
 |