docs: function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $controller) {
    var rootRouter = $router;
  
    return {
      restrict: 'AE',
      transclude: 'element',
      terminal: true,
      priority: 400,
      require: ['?^^ngOutlet', 'ngOutlet'],
      link: outletLink,
      controller: function() {},
      controllerAs: '$$ngOutlet'
    };
  
    function invoke(method, context, instruction) {
      return $injector.invoke(method, context, instruction.locals);
    }
  
    function outletLink(scope, $element, attrs, ctrls, $transclude) {
      var outletName = attrs.ngOutlet || 'default',
          parentCtrl = ctrls[0],
          myCtrl = ctrls[1],
          router = (parentCtrl && parentCtrl.$$router) || rootRouter;
  
      var currentScope,
          newScope,
          currentController,
          currentElement,
          previousLeaveAnimation,
          previousInstruction;
  
      function cleanupLastView() {
        if (previousLeaveAnimation) {
          $animate.cancel(previousLeaveAnimation);
          previousLeaveAnimation = null;
        }
  
        if (currentScope) {
          currentScope.$destroy();
          currentScope = null;
        }
        if (currentElement) {
          previousLeaveAnimation = $animate.leave(currentElement);
          previousLeaveAnimation.then(function() {
            previousLeaveAnimation = null;
          });
          currentElement = null;
        }
      }
  
      router.registerOutlet({
        canDeactivate: function(instruction) {
          if (currentController && currentController.canDeactivate) {
            return invoke(currentController.canDeactivate, currentController, instruction);
          }
          return true;
        },
        activate: function(instruction) {
          var nextInstruction = serializeInstruction(instruction);
          if (nextInstruction === previousInstruction) {
            return;
          }
  
          var controllerConstructor = instruction.controllerConstructor;
  
          if (!instruction.locals.$scope) {
            instruction.locals.$scope = scope.$new();
          }
          newScope = instruction.locals.$scope;
  
          if (controllerConstructor === NOOP_CONTROLLER) {
            console.warn && console.warn('Could not find controller for', $componentMapper.controllerName(instruction.component));
          }
          var ctrl = $controller(controllerConstructor, instruction.locals);
          instruction.controllerAs = $componentMapper.controllerAs(instruction.component);
          instruction.controller = ctrl;
  
          myCtrl.$$router = instruction.router;
          myCtrl.$$template = instruction.template;
          var controllerAs = instruction.controllerAs || instruction.component;
          var clone = $transclude(newScope, function(clone) {
            $animate.enter(clone, null, currentElement || $element);
            cleanupLastView();
          });
  
          var newController = instruction.controller;
          newScope[controllerAs] = newController;
  
          var result;
          if (currentController && currentController.deactivate) {
            result = $q.when(invoke(currentController.deactivate, currentController, instruction));
          }
  
          currentController = newController;
  
          currentElement = clone;
          currentScope = newScope;
  
          previousInstruction = nextInstruction;
  
          // finally, run the hook
          if (newController.activate) {
            var activationResult = $q.when(invoke(newController.activate, newController, instruction));
            if (result) {
              return result.then(activationResult);
            } else {
              return activationResult;
            }
          }
          return result;
        }
      }, outletName);
    }
  
    // TODO: how best to serialize?
    function serializeInstruction(instruction) {
      return JSON.stringify({
        path: instruction.path,
        component: instruction.component,
        params: Object.keys(instruction.params).reduce(function (acc, key) {
          return (key !== 'childRoute' && (acc[key] = instruction.params[key])), acc;
        }, {})
      });
    }
  }