text: /**
   * NavigationView is basically a {@link Ext.Container} with a {@link Ext.layout.Card card} layout, so only one view
   * can be visible at a time. However, NavigationView also adds extra functionality on top of this to allow
   * you to `push` and `pop` views at any time. When you do this, your NavigationView will automatically animate
   * between your current active view, and the new view you want to `push`, or the previous view you want to `pop`.
   *
   * Using the NavigationView is very simple. Here is a basic example of it in action:
   *
   *     @example
   *     var view = Ext.create('Ext.NavigationView', {
   *         fullscreen: true,
   *
   *         items: [{
   *             title: 'First',
   *             items: [{
   *                 xtype: 'button',
   *                 text: 'Push a new view!',
   *                 handler: function() {
   *                     // use the push() method to push another view. It works much like
   *                     // add() or setActiveItem(). it accepts a view instance, or you can give it
   *                     // a view config.
   *                     view.push({
   *                         title: 'Second',
   *                         html: 'Second view!'
   *                     });
   *                 }
   *             }]
   *         }]
   *     });
   *
   * Now, here comes the fun part: you can push any view/item into the NavigationView, at any time, and it will
   * automatically handle the animations between the two views, including adding a back button (if necessary)
   * and showing the new title.
   *
   *     view.push({
   *         title: 'A new view',
   *         html: 'Some new content'
   *     });
   *
   * As you can see, it is as simple as calling the {@link #method-push} method, with a new view (instance or object). Done.
   *
   * You can also `pop` a view at any time. This will remove the top-most view from the NavigationView, and animate back
   * to the previous view. You can do this using the {@link #method-pop} method (which requires no arguments).
   *
   *     view.pop();
   *
   *  Applications that need compatibility with ##Older Android## devices will want to see the {@link #layout} config for details on
   *  disabling navigation view animations as these devices have poor animation support and performance.
   */
  Ext.define('Ext.navigation.View', {
      extend: 'Ext.Container',
      alternateClassName: 'Ext.NavigationView',
      xtype: 'navigationview',
      requires: ['Ext.navigation.Bar'],
  
      config: {
          /**
           * @cfg {Boolean/Object} navigationBar
           * The NavigationBar used in this navigation view. It defaults to be docked to the top.
           *
           * You can just pass in a normal object if you want to customize the NavigationBar. For example:
           *
           *     navigationBar: {
           *         ui: 'dark',
           *         docked: 'bottom'
           *     }
           *
           * You **cannot** specify a *title* property in this configuration. The title of the navigationBar is taken
           * from the configuration of this views children:
           *
           *     view.push({
           *         title: 'This views title which will be shown in the navigation bar',
           *         html: 'Some HTML'
           *     });
           *
           * @accessor
           */
          navigationBar: {
              docked: 'top'
          },
  
          /**
           * @cfg {String} defaultBackButtonText
           * The text to be displayed on the back button if:
           *
           * - The previous view does not have a title.
           * - The {@link #useTitleForBackButtonText} configuration is `true`.
           * @accessor
           */
          defaultBackButtonText: 'Back',
  
          /**
           * @cfg {Boolean} useTitleForBackButtonText
           * Set to `false` if you always want to display the {@link #defaultBackButtonText} as the text
           * on the back button. `true` if you want to use the previous views title.
           * @accessor
           */
          useTitleForBackButtonText: false,
  
          /**
           * @cfg {Array/Object} items The child items to add to this NavigationView. This is usually an array of Component
           * configurations or instances, for example:
           *
           *     Ext.create('Ext.Container', {
           *         items: [
           *             {
           *                 xtype: 'panel',
           *                 title: 'My title',
           *                 html: 'This is an item'
           *             }
           *         ]
           *     });
           *
           * If you want a title to be displayed in the {@link #navigationBar}, you must specify a `title` configuration in your
           * view, like above.
           *
           * __Note:__ Only one view will be visible at a time. If you want to change to another view, use the {@link #method-push} or
           * {@link #setActiveItem} methods.
           * @accessor
           */
  
          /**
           * @cfg {Object}
           * Layout used in this navigation view, type must be set to 'card'.
           * **Android NOTE:** Older Android devices have poor animation performance. It is recommended to set the animation to null, for example:
           *
           *      layout: {
           *          type: 'card',
           *          animation: null
           *      }
           *
           * @accessor
           */
          layout: {
              type: 'card',
              animation: {
                  duration: 300,
                  easing: 'ease-out',
                  type: 'slide',
                  direction: 'left'
              }
          }
      },
  
      baseCls: Ext.baseCSSPrefix + 'navigationview',
  
      /**
       * @event push
       * Fires when a view is pushed into this navigation view
       * @param {Ext.navigation.View} this The component instance
       * @param {Mixed} view The view that has been pushed
       */
  
      /**
       * @event pop
       * Fires when a view is popped from this navigation view
       * @param {Ext.navigation.View} this The component instance
       * @param {Mixed} view The view that has been popped
       */
  
      /**
       * @event back
       * Fires when the back button in the navigation view was tapped.
       * @param {Ext.navigation.View} this The component instance\
       */
  
      /**
       * @private
       */
      initialize: function() {
          var me     = this,
              navBar = me.getNavigationBar();
  
          //add a listener onto the back button in the navigationbar
          if (navBar) {
              navBar.on({
                  back: me.onBackButtonTap,
                  scope: me
              });
  
              me.relayEvents(navBar, 'rightbuttontap');
  
              me.relayEvents(me, {
                  add: 'push',
                  remove: 'pop'
              });
          }
  
          //<debug>
          var layout = me.getLayout();
          if (layout && !layout.isCard) {
              Ext.Logger.error('The base layout for a NavigationView must always be a Card Layout');
          }
          //</debug>
      },
  
      /**
       * @private
       * Called when the user taps on the back button
       */
      onBackButtonTap: function() {
          this.pop();
          this.fireEvent('back', this);
      },
  
      /**
       * Pushes a new view into this navigation view using the default animation that this view has.
       * @param {Object} view The view to push.
       * @return {Ext.Component} The new item you just pushed.
       */
      push: function(view) {
          return this.add(view);
      },
  
      /**
       * Removes the current active view from the stack and sets the previous view using the default animation
       * of this view. You can also pass a {@link Ext.ComponentQuery} selector to target what inner item to pop to.
       * @param {Number/String/Object} count If a Number, the number of views you want to pop. If a String, the pops to a matching
       * component query. If an Object, the pops to a matching view instance.
       * @return {Ext.Component} The new active item
       */
      pop: function(count) {
          if (this.beforePop(count)) {
              return this.doPop();
          }
      },
  
      /**
       * @private
       * Calculates whether it needs to remove any items from the stack when you are popping more than 1
       * item. If it does, it removes those views from the stack and returns `true`.
       * @return {Boolean} `true` if it has removed views.
       */
      beforePop: function(count) {
          var me = this,
              innerItems = me.getInnerItems(),
              last, i, ln, toRemove;
  
          if (Ext.isString(count) || Ext.isObject(count)) {
              last = innerItems.length - 1;
  
              for (i = last; i >= 0; i--) {
                  if ((Ext.isString(count) && Ext.ComponentQuery.is(innerItems[i], count)) || (Ext.isObject(count) && count == innerItems[i])) {
                      count = last - i;
                      break;
                  }
              }
  
              if (!Ext.isNumber(count)) {
                  return false;
              }
          }
  
          ln = innerItems.length;
  
          //default to 1 pop
          if (!Ext.isNumber(count) || count < 1) {
              count = 1;
          }
  
          //check if we are trying to remove more items than we have
          count = Math.min(count, ln - 1);
  
          if (count) {
              //we need to reset the backButtonStack in the navigation bar
              me.getNavigationBar().beforePop(count);
  
              //get the items we need to remove from the view and remove theme
              toRemove = innerItems.splice(-count, count - 1);
              for (i = 0; i < toRemove.length; i++) {
                  this.remove(toRemove[i]);
              }
  
              return true;
          }
  
          return false;
      },
  
      /**
       * @private
       */
      doPop: function() {
          var me = this,
              innerItems = this.getInnerItems();
  
          //set the new active item to be the new last item of the stack
          me.remove(innerItems[innerItems.length - 1]);
  
          // Hide the backButton
          if (innerItems.length < 3 && this.$backButton) {
              this.$backButton.hide();
          }
  
          // Update the title container
          if (this.$titleContainer) {
              //<debug>
              if (!this.$titleContainer.setTitle) {
                  Ext.Logger.error([
                      'You have selected to display a title in a component that does not ',
                      'support titles in NavigationView. Please remove the `title` configuration from your ',
                      'NavigationView item, or change it to a component that has a `setTitle` method.'
                  ].join(''));
              }
              //</debug>
  
              var item = innerItems[innerItems.length - 2];
              this.$titleContainer.setTitle((item.getTitle) ? item.getTitle() : item.config.title);
          }
  
          return this.getActiveItem();
      },
  
      /**
       * Returns the previous item, if one exists.
       * @return {Mixed} The previous view
       */
      getPreviousItem: function() {
          var innerItems = this.getInnerItems();
          return innerItems[innerItems.length - 2];
      },
  
      /**
       * Updates the backbutton text accordingly in the {@link #navigationBar}
       * @private
       */
      updateUseTitleForBackButtonText: function(useTitleForBackButtonText) {
          var navigationBar = this.getNavigationBar();
          if (navigationBar) {
              navigationBar.setUseTitleForBackButtonText(useTitleForBackButtonText);
          }
      },
  
      /**
       * Updates the backbutton text accordingly in the {@link #navigationBar}
       * @private
       */
      updateDefaultBackButtonText: function(defaultBackButtonText) {
          var navigationBar = this.getNavigationBar();
          if (navigationBar) {
              navigationBar.setDefaultBackButtonText(defaultBackButtonText);
          }
      },
  
      /**
       * This is called when an Item is added to the BackButtonContainer of a SplitNavigation View
       * @private
       *
       * @param toolbar
       * @param item
       */
      onBackButtonContainerAdd: function(toolbar, item) {
          item.on({
              scope: this,
              show: this.refreshBackButtonContainer,
              hide: this.refreshBackButtonContainer
          });
          this.refreshBackButtonContainer();
      },
  
      /**
       * This is called when an Item is removed from the BackButtonContainer of a SplitNavigation View
       * @private
       *
       * @param toolbar
       * @param item
       */
      onBackButtonContainerRemove: function(toolbar, item) {
          item.un({
              scope: this,
              show: this.refreshBackButtonContainer,
              hide: this.refreshBackButtonContainer
          });
          this.refreshBackButtonContainer();
      },
  
      /**
       * This is used for Blackberry SplitNavigation to monitor the state of child items in the bottom toolbar.
       * if no visible children exist the toolbar will be hidden
       * @private
       */
      refreshBackButtonContainer: function() {
          if (!this.$backButtonContainer) {
              return;
          }
          var i = 0,
              backButtonContainer = this.$backButtonContainer,
              items = backButtonContainer.items,
              item;
  
          for(;i < items.length; i++) {
              item = items.get(i);
              if(!item.isHidden()) {
                  this.$backButtonContainer.show();
                  return;
              }
          }
  
          this.$backButtonContainer.hide();
      },
  
      /**
       * @private
       */
      applyNavigationBar: function(config) {
          var me = this;
          if (!config) {
              config = {
                  hidden: true,
                  docked: 'top'
              };
          }
  
          // Call the getter for items on this view to insure that they will be
          // available (via innerItems) for the navigationBar created below.
          me.getItems();
  
          if (config.title) {
              delete config.title;
              //<debug>
              Ext.Logger.warn("Ext.navigation.View: The 'navigationBar' configuration does not accept a 'title' property. You " +
                  "set the title of the navigationBar by giving this navigation view's children a 'title' property.");
              //</debug>
          }
  
          config.view = me;
          config.useTitleForBackButtonText = me.getUseTitleForBackButtonText();
          // Blackberry specific nav setup where title is on the top title bar and the bottom toolbar is used for buttons and BACK
          if (config.splitNavigation) {
              me.$titleContainer = me.add({
                  docked: 'top',
                  xtype: 'titlebar',
                  ui: 'light',
                  title: me.$currentTitle || ''
              });
  
              var containerConfig = (config.splitNavigation === true) ? {} : config.splitNavigation;
  
              me.$backButtonContainer = me.add({
                  xtype: 'toolbar',
                  docked: 'bottom',
                  hidden: true
              });
  
              // Any item that is added to the BackButtonContainer should be monitored for visibility
              // this will allow the toolbar to be hidden when no items exist in it.
              me.$backButtonContainer.on ({
                  scope: me,
                  add: me.onBackButtonContainerAdd,
                  remove: me.onBackButtonContainerRemove
              });
  
              me.$backButton = me.$backButtonContainer.add({
                  xtype: 'button',
                  text: 'Back',
                  hidden: true,
                  ui: 'back'
              });
  
              // Default config items go into the bottom bar
              if(config.items) {
                  me.$backButtonContainer.add(config.items);
              }
  
              // If the user provided items and splitNav items, default items go into the bottom bar, split nav items go into the top
              if(containerConfig.items) {
                  me.$titleContainer.add(containerConfig.items);
              }
  
              me.$backButton.on({
                  scope: me,
                  tap: me.onBackButtonTap
              });
  
              config = {
                  hidden: true,
                  docked: 'top'
              };
          }
  
          return Ext.factory(config, Ext.navigation.Bar, this.getNavigationBar());
      },
  
      /**
       * @private
       */
      updateNavigationBar: function(newNavigationBar, oldNavigationBar) {
          if (oldNavigationBar) {
              this.remove(oldNavigationBar, true);
          }
  
          if (newNavigationBar) {
              this.add(newNavigationBar);
          }
      },
  
      /**
       * @private
       */
      applyActiveItem: function(activeItem, currentActiveItem) {
          var me = this,
              innerItems = me.getInnerItems();
  
          // Make sure the items are already initialized
          me.getItems();
  
          // If we are not initialzed yet, we should set the active item to the last item in the stack
          if (!me.initialized) {
              activeItem = innerItems.length - 1;
          }
  
          return this.callParent([activeItem, currentActiveItem]);
      },
  
      doResetActiveItem: function(innerIndex) {
          var me = this,
              innerItems = me.getInnerItems(),
              animation = me.getLayout().getAnimation();
  
          if (innerIndex > 0) {
              if (animation && animation.isAnimation) {
                  animation.setReverse(true);
              }
              me.setActiveItem(innerIndex - 1);
              me.getNavigationBar().onViewRemove(me, innerItems[innerIndex], innerIndex);
          }
      },
  
      /**
       * @private
       */
      doRemove: function() {
          var animation = this.getLayout().getAnimation();
  
          if (animation && animation.isAnimation) {
              animation.setReverse(false);
          }
  
          this.callParent(arguments);
      },
  
      /**
       * @private
       */
      onItemAdd: function(item, index) {
          var me = this,
              initialized = me.initialized,
              navigationBar;
  
          // Check for title configuration
          if (item && item.getDocked() && item.config.title === true) {
              me.$titleContainer = item;
          }
  
          me.doItemLayoutAdd(item, index);
  
          if (initialized && item.isInnerItem()) {
              me.setActiveItem(item);
  
              navigationBar = this.getNavigationBar();
              if (navigationBar) {
                  this.getNavigationBar().onViewAdd(me, item, index);
              }
  
              // Update the custom backButton
              if (me.$backButtonContainer) {
                  me.$backButton.show();
              }
          }
  
          if (item && item.isInnerItem()) {
              // Update the title container title
              me.updateTitleContainerTitle((item.getTitle) ? item.getTitle() : item.config.title);
          }
  
          if (initialized) {
              me.fireEvent('add', me, item, index);
          }
      },
  
      /**
       * @private
       * Updates the title of the titleContainer, if it exists
       */
      updateTitleContainerTitle: function(title) {
          if (this.$titleContainer) {
              //<debug>
              if (!this.$titleContainer.setTitle) {
                  Ext.Logger.error([
                      'You have selected to display a title in a component that does not ',
                      'support titles in NavigationView. Please remove the `title` configuration from your ',
                      'NavigationView item, or change it to a component that has a `setTitle` method.'
                  ].join(''));
              }
              //</debug>
  
              this.$titleContainer.setTitle(title);
          }
          else {
              this.$currentTitle = title;
          }
      },
  
      /**
       * Resets the view by removing all items between the first and last item.
       * @return {Ext.Component} The view that is now active
       */
      reset: function() {
          return this.pop(this.getInnerItems().length);
      }
  });
  