text: /**
   * Copyright (c) 2015, Facebook, Inc.  All rights reserved.
   *
   * Facebook, Inc. ("Facebook") owns all right, title and interest, including
   * all intellectual property and other proprietary rights, in and to the React
   * Native CustomComponents software (the "Software").  Subject to your
   * compliance with these terms, you are hereby granted a non-exclusive,
   * worldwide, royalty-free copyright license to (1) use and copy the Software;
   * and (2) reproduce and distribute the Software as part of your own software
   * ("Your Software").  Facebook reserves all rights not expressly granted to
   * you in this license agreement.
   *
   * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
   * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
   * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
   * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * @providesModule NavigatorNavigationBar
   */
  'use strict';
  
  var React = require('React');
  var NavigatorNavigationBarStylesAndroid = require('NavigatorNavigationBarStylesAndroid');
  var NavigatorNavigationBarStylesIOS = require('NavigatorNavigationBarStylesIOS');
  var Platform = require('Platform');
  var StyleSheet = require('StyleSheet');
  var View = require('View');
  
  var guid = require('guid');
  
  var { Map } = require('immutable');
  
  var COMPONENT_NAMES = ['Title', 'LeftButton', 'RightButton'];
  
  var NavigatorNavigationBarStyles = Platform.OS === 'android' ?
    NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS;
  
  var navStatePresentedIndex = function(navState) {
    if (navState.presentedIndex !== undefined) {
      return navState.presentedIndex;
    }
    // TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS`
    return navState.observedTopOfStack;
  };
  
  var NavigatorNavigationBar = React.createClass({
  
    propTypes: {
      navigator: React.PropTypes.object,
      routeMapper: React.PropTypes.shape({
        Title: React.PropTypes.func.isRequired,
        LeftButton: React.PropTypes.func.isRequired,
        RightButton: React.PropTypes.func.isRequired,
      }).isRequired,
      navState: React.PropTypes.shape({
        routeStack: React.PropTypes.arrayOf(React.PropTypes.object),
        presentedIndex: React.PropTypes.number,
      }),
      navigationStyles: React.PropTypes.object,
      style: View.propTypes.style,
    },
  
    statics: {
      Styles: NavigatorNavigationBarStyles,
      StylesAndroid: NavigatorNavigationBarStylesAndroid,
      StylesIOS: NavigatorNavigationBarStylesIOS,
    },
  
    getDefaultProps() {
      return {
        navigationStyles: NavigatorNavigationBarStyles,
      };
    },
  
    componentWillMount: function() {
      this._reset();
    },
  
    /**
     * Stop transtion, immediately resets the cached state and re-render the
     * whole view.
     */
    immediatelyRefresh() {
      this._reset();
      this.forceUpdate();
    },
  
    _reset() {
      this._key = guid();
      this._reusableProps = {};
      this._components = {};
      this._descriptors = {};
  
      COMPONENT_NAMES.forEach(componentName => {
        this._components[componentName] = new Map();
        this._descriptors[componentName] = new Map();
      });
    },
  
    _getReusableProps: function(
      /*string*/componentName,
      /*number*/index
    ) /*object*/ {
      var propStack = this._reusableProps[componentName];
      if (!propStack) {
        propStack = this._reusableProps[componentName] = [];
      }
      var props = propStack[index];
      if (!props) {
        props = propStack[index] = {style:{}};
      }
      return props;
    },
  
    _updateIndexProgress: function(
      /*number*/progress,
      /*number*/index,
      /*number*/fromIndex,
      /*number*/toIndex
    ) {
      var amount = toIndex > fromIndex ? progress : (1 - progress);
      var oldDistToCenter = index - fromIndex;
      var newDistToCenter = index - toIndex;
      var interpolate;
      if (oldDistToCenter > 0 && newDistToCenter === 0 ||
          newDistToCenter > 0 && oldDistToCenter === 0) {
        interpolate = this.props.navigationStyles.Interpolators.RightToCenter;
      } else if (oldDistToCenter < 0 && newDistToCenter === 0 ||
                 newDistToCenter < 0 && oldDistToCenter === 0) {
        interpolate = this.props.navigationStyles.Interpolators.CenterToLeft;
      } else if (oldDistToCenter === newDistToCenter) {
        interpolate = this.props.navigationStyles.Interpolators.RightToCenter;
      } else {
        interpolate = this.props.navigationStyles.Interpolators.RightToLeft;
      }
  
      COMPONENT_NAMES.forEach(function (componentName) {
        var component = this._components[componentName].get(this.props.navState.routeStack[index]);
        var props = this._getReusableProps(componentName, index);
        if (component && interpolate[componentName](props.style, amount)) {
          props.pointerEvents = props.style.opacity === 0 ? 'none' : 'box-none';
          component.setNativeProps(props);
        }
      }, this);
    },
  
    updateProgress: function(
      /*number*/progress,
      /*number*/fromIndex,
      /*number*/toIndex
    ) {
      var max = Math.max(fromIndex, toIndex);
      var min = Math.min(fromIndex, toIndex);
      for (var index = min; index <= max; index++) {
        this._updateIndexProgress(progress, index, fromIndex, toIndex);
      }
    },
  
    render: function() {
      var navBarStyle = {
        height: this.props.navigationStyles.General.TotalNavHeight,
      };
      var navState = this.props.navState;
      var components = navState.routeStack.map((route, index) =>
        COMPONENT_NAMES.map(componentName =>
          this._getComponent(componentName, route, index)
        )
      );
  
      return (
        <View
          key={this._key}
          style={[styles.navBarContainer, navBarStyle, this.props.style]}>
          {components}
        </View>
      );
    },
  
    _getComponent: function(
      /*string*/componentName,
      /*object*/route,
      /*number*/index
    ) /*?Object*/ {
      if (this._descriptors[componentName].includes(route)) {
        return this._descriptors[componentName].get(route);
      }
  
      var rendered = null;
  
      var content = this.props.routeMapper[componentName](
        this.props.navState.routeStack[index],
        this.props.navigator,
        index,
        this.props.navState
      );
      if (!content) {
        return null;
      }
  
      var componentIsActive = index === navStatePresentedIndex(this.props.navState);
      var initialStage = componentIsActive ?
        this.props.navigationStyles.Stages.Center :
        this.props.navigationStyles.Stages.Left;
      rendered = (
        <View
          ref={(ref) => {
            this._components[componentName] = this._components[componentName].set(route, ref);
          }}
          pointerEvents={componentIsActive ? 'box-none' : 'none'}
          style={initialStage[componentName]}>
          {content}
        </View>
      );
  
      this._descriptors[componentName] = this._descriptors[componentName].set(route, rendered);
      return rendered;
    },
  
  });
  
  
  var styles = StyleSheet.create({
    navBarContainer: {
      position: 'absolute',
      top: 0,
      left: 0,
      right: 0,
      backgroundColor: 'transparent',
    },
  });
  
  module.exports = NavigatorNavigationBar;
  