text: /**
   * @ngdoc directive
   * @name ui.router.state.directive:ui-view
   *
   * @requires ui.router.state.$state
   * @requires $compile
   * @requires $controller
   * @requires $injector
   * @requires ui.router.state.$uiViewScroll
   * @requires $document
   *
   * @restrict ECA
   *
   * @description
   * The ui-view directive tells $state where to place your templates.
   *
   * @param {string=} ui-view A view name. The name should be unique amongst the other views in the
   * same state. You can have views of the same name that live in different states.
   *
   * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
   * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
   * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
   * scroll ui-view elements into view when they are populated during a state activation.
   *
   * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
   * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
   *
   * @param {string=} onload Expression to evaluate whenever the view updates.
   * 
   * @example
   * A view can be unnamed or named. 
   * <pre>
   * <!-- Unnamed -->
   * <div ui-view></div> 
   * 
   * <!-- Named -->
   * <div ui-view="viewName"></div>
   * </pre>
   *
   * You can only have one unnamed view within any template (or root html). If you are only using a 
   * single view and it is unnamed then you can populate it like so:
   * <pre>
   * <div ui-view></div> 
   * $stateProvider.state("home", {
   *   template: "<h1>HELLO!</h1>"
   * })
   * </pre>
   * 
   * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
   * config property, by name, in this case an empty name:
   * <pre>
   * $stateProvider.state("home", {
   *   views: {
   *     "": {
   *       template: "<h1>HELLO!</h1>"
   *     }
   *   }    
   * })
   * </pre>
   * 
   * But typically you'll only use the views property if you name your view or have more than one view 
   * in the same template. There's not really a compelling reason to name a view if its the only one, 
   * but you could if you wanted, like so:
   * <pre>
   * <div ui-view="main"></div>
   * </pre> 
   * <pre>
   * $stateProvider.state("home", {
   *   views: {
   *     "main": {
   *       template: "<h1>HELLO!</h1>"
   *     }
   *   }    
   * })
   * </pre>
   * 
   * Really though, you'll use views to set up multiple views:
   * <pre>
   * <div ui-view></div>
   * <div ui-view="chart"></div> 
   * <div ui-view="data"></div> 
   * </pre>
   * 
   * <pre>
   * $stateProvider.state("home", {
   *   views: {
   *     "": {
   *       template: "<h1>HELLO!</h1>"
   *     },
   *     "chart": {
   *       template: "<chart_thing/>"
   *     },
   *     "data": {
   *       template: "<data_thing/>"
   *     }
   *   }    
   * })
   * </pre>
   *
   * Examples for `autoscroll`:
   *
   * <pre>
   * <!-- If autoscroll present with no expression,
   *      then scroll ui-view into view -->
   * <ui-view autoscroll/>
   *
   * <!-- If autoscroll present with valid expression,
   *      then scroll ui-view into view if expression evaluates to true -->
   * <ui-view autoscroll='true'/>
   * <ui-view autoscroll='false'/>
   * <ui-view autoscroll='scopeVariable'/>
   * </pre>
   */
  $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll'];
  function $ViewDirective(   $state,   $injector,   $uiViewScroll) {
  
    function getService() {
      return ($injector.has) ? function(service) {
        return $injector.has(service) ? $injector.get(service) : null;
      } : function(service) {
        try {
          return $injector.get(service);
        } catch (e) {
          return null;
        }
      };
    }
  
    var service = getService(),
        $animator = service('$animator'),
        $animate = service('$animate');
  
    // Returns a set of DOM manipulation functions based on which Angular version
    // it should use
    function getRenderer(attrs, scope) {
      var statics = function() {
        return {
          enter: function (element, target, cb) { target.after(element); cb(); },
          leave: function (element, cb) { element.remove(); cb(); }
        };
      };
  
      if ($animate) {
        return {
          enter: function(element, target, cb) { $animate.enter(element, null, target, cb); },
          leave: function(element, cb) { $animate.leave(element, cb); }
        };
      }
  
      if ($animator) {
        var animate = $animator && $animator(scope, attrs);
  
        return {
          enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
          leave: function(element, cb) { animate.leave(element); cb(); }
        };
      }
  
      return statics();
    }
  
    var directive = {
      restrict: 'ECA',
      terminal: true,
      priority: 400,
      transclude: 'element',
      compile: function (tElement, tAttrs, $transclude) {
        return function (scope, $element, attrs) {
          var previousEl, currentEl, currentScope, latestLocals,
              onloadExp     = attrs.onload || '',
              autoScrollExp = attrs.autoscroll,
              renderer      = getRenderer(attrs, scope);
  
          scope.$on('$stateChangeSuccess', function() {
            updateView(false);
          });
          scope.$on('$viewContentLoading', function() {
            updateView(false);
          });
  
          updateView(true);
  
          function cleanupLastView() {
            if (previousEl) {
              previousEl.remove();
              previousEl = null;
            }
  
            if (currentScope) {
              currentScope.$destroy();
              currentScope = null;
            }
  
            if (currentEl) {
              renderer.leave(currentEl, function() {
                previousEl = null;
              });
  
              previousEl = currentEl;
              currentEl = null;
            }
          }
  
          function updateView(firstTime) {
            var newScope,
                name            = getUiViewName(attrs, $element.inheritedData('$uiView')),
                previousLocals  = name && $state.$current && $state.$current.locals[name];
  
            if (!firstTime && previousLocals === latestLocals) return; // nothing to do
            newScope = scope.$new();
            latestLocals = $state.$current.locals[name];
  
            var clone = $transclude(newScope, function(clone) {
              renderer.enter(clone, $element, function onUiViewEnter() {
                if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
                  $uiViewScroll(clone);
                }
              });
              cleanupLastView();
            });
  
            currentEl = clone;
            currentScope = newScope;
            /**
             * @ngdoc event
             * @name ui.router.state.directive:ui-view#$viewContentLoaded
             * @eventOf ui.router.state.directive:ui-view
             * @eventType emits on ui-view directive scope
             * @description           *
             * Fired once the view is **loaded**, *after* the DOM is rendered.
             *
             * @param {Object} event Event object.
             */
            currentScope.$emit('$viewContentLoaded');
            currentScope.$eval(onloadExp);
          }
        };
      }
    };
  
    return directive;
  }
  
  $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state'];
  function $ViewDirectiveFill ($compile, $controller, $state) {
    return {
      restrict: 'ECA',
      priority: -400,
      compile: function (tElement) {
        var initial = tElement.html();
        return function (scope, $element, attrs) {
          var current = $state.$current,
              name = getUiViewName(attrs, $element.inheritedData('$uiView')),
              locals  = current && current.locals[name];
  
          if (! locals) {
            return;
          }
  
          $element.data('$uiView', { name: name, state: locals.$$state });
          $element.html(locals.$template ? locals.$template : initial);
  
          var link = $compile($element.contents());
  
          if (locals.$$controller) {
            locals.$scope = scope;
            var controller = $controller(locals.$$controller, locals);
            if (locals.$$controllerAs) {
              scope[locals.$$controllerAs] = controller;
            }
            $element.data('$ngControllerController', controller);
            $element.children().data('$ngControllerController', controller);
          }
  
          link(scope);
        };
      }
    };
  }
  
  /**
   * Shared ui-view code for both directives:
   * Given attributes and inherited $uiView data, return the view's name
   */
  function getUiViewName(attrs, inherited) {
    var name = attrs.uiView || attrs.name || '';
    return name.indexOf('@') >= 0 ?  name :  (name + '@' + (inherited ? inherited.state.name : ''));
  }
  
  angular.module('ui.router.state').directive('uiView', $ViewDirective);
  angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
  