function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $interpolate) {

  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 = {
      enter: function (element, target, cb) { target.after(element); cb(); },
      leave: function (element, cb) { element.remove(); cb(); }
    };

    if (!!attrs.noanimation) return statics;

    function animEnabled(element) {
      if (ngMajorVer === 1 && ngMinorVer >= 4) return !!$animate.enabled(element);
      if (ngMajorVer === 1 && ngMinorVer >= 2) return !!$animate.enabled();
      return (!!$animator);
    }

    // ng 1.2+
    if ($animate) {
      return {
        enter: function(element, target, cb) {
          if (!animEnabled(element)) {
            statics.enter(element, target, cb);
          } else if (angular.version.minor > 2) {
            $animate.enter(element, null, target).then(cb);
          } else {
            $animate.enter(element, null, target, cb);
          }
        },
        leave: function(element, cb) {
          if (!animEnabled(element)) {
            statics.leave(element, cb);
          } else if (angular.version.minor > 2) {
            $animate.leave(element).then(cb);
          } else {
            $animate.leave(element, cb);
          }
        }
      };
    }

    // ng 1.1.5
    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);
        });

        updateView(true);

        function cleanupLastView() {
          var _previousEl = previousEl;
          var _currentScope = currentScope;

          if (_currentScope) {
            _currentScope._willBeDestroyed = true;
          }

          function cleanOld() {
            if (_previousEl) {
              _previousEl.remove();
            }

            if (_currentScope) {
              _currentScope.$destroy();
            }
          }

          if (currentEl) {
            renderer.leave(currentEl, function() {
              cleanOld();
              previousEl = null;
            });

            previousEl = currentEl;
          } else {
            cleanOld();
            previousEl = null;
          }

          currentEl = null;
          currentScope = null;
        }

        function updateView(firstTime) {
          var newScope,
              name            = getUiViewName(scope, attrs, $element, $interpolate),
              previousLocals  = name && $state.$current && $state.$current.locals[name];

          if (!firstTime && previousLocals === latestLocals || scope._willBeDestroyed) return; // nothing to do
          newScope = scope.$new();
          latestLocals = $state.$current.locals[name];

          /**
           * @ngdoc event
           * @name ui.router.state.directive:ui-view#$viewContentLoading
           * @eventOf ui.router.state.directive:ui-view
           * @eventType emits on ui-view directive scope
           * @description
           *
           * Fired once the view **begins loading**, *before* the DOM is rendered.
           *
           * @param {Object} event Event object.
           * @param {string} viewName Name of the view.
           */
          newScope.$emit('$viewContentLoading', name);

          var clone = $transclude(newScope, function(clone) {
            renderer.enter(clone, $element, function onUiViewEnter() {
              if(currentScope) {
                currentScope.$emit('$viewContentAnimationEnded');
              }

              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.
           * @param {string} viewName Name of the view.
           */
          currentScope.$emit('$viewContentLoaded', name);
          currentScope.$eval(onloadExp);
        }
      };
    }
  };

  return directive;
}
@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=} name 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.

@param {string=} noanimation If truthy, the non-animated renderer will be selected (no animations
will be applied to the ui-view)

*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>