text: /*! umbraco - v7.0.0-Beta - 2014-04-08
   * https://github.com/umbraco/umbraco-cms/tree/7.0.0
   * Copyright (c) 2014 Umbraco HQ;
   * Licensed MIT
   */
  
  (function() { 
  
  // Based loosely around work by Witold Szczerba - https://github.com/witoldsz/angular-http-auth
  angular.module('umbraco.security', [
    'umbraco.security.retryQueue',
    'umbraco.security.interceptor']);
  angular.module('umbraco.security.interceptor', ['umbraco.security.retryQueue'])
      // This http interceptor listens for authentication successes and failures
      .factory('securityInterceptor', ['$injector', 'securityRetryQueue', 'notificationsService', function ($injector, queue, notifications) {
          return function(promise) {
  
              return promise.then(
                  function(originalResponse) {
                      // Intercept successful requests
                      
                      //Here we'll check if our custom header is in the response which indicates how many seconds the user's session has before it
                      //expires. Then we'll update the user in the user service accordingly.
                      var headers = originalResponse.headers();
                      if (headers["x-umb-user-seconds"]) {
                          // We must use $injector to get the $http service to prevent circular dependency
                          var userService = $injector.get('userService');
                          userService.setUserTimeout(headers["x-umb-user-seconds"]);
                      }
                      
                      return promise;
                  }, function(originalResponse) {
                      // Intercept failed requests
  
                      //A 401 means that the user is not logged in
                      if (originalResponse.status === 401) {
  
                          // The request bounced because it was not authorized - add a new request to the retry queue
                          promise = queue.pushRetryFn('unauthorized-server', function retryRequest() {
                              // We must use $injector to get the $http service to prevent circular dependency
                              return $injector.get('$http')(originalResponse.config);
                          });
                      }
                      else if (originalResponse.status === 404) {
  
                          //a 404 indicates that the request was not found - this could be due to a non existing url, or it could
                          //be due to accessing a url with a parameter that doesn't exist, either way we should notifiy the user about it
                          
                          var errMsg = "The URL returned a 404 (not found): <br/><i>" + originalResponse.config.url.split('?')[0] + "</i>";
                          if (originalResponse.data && originalResponse.data.ExceptionMessage) {
                              errMsg += "<br/> with error: <br/><i>" + originalResponse.data.ExceptionMessage + "</i>";
                          }
                          if (originalResponse.config.data) {
                              errMsg += "<br/> with data: <br/><i>" + angular.toJson(originalResponse.config.data) + "</i><br/>Contact your administrator for information.";
                          }
  
                          notifications.error(
                              "Request error",
                              errMsg);
                          
                      }
                      else if (originalResponse.status === 403) {
                          //if the status was a 403 it means the user didn't have permission to do what the request was trying to do.
                          //How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was 
                          //requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper,
                          // or completely custom for services calling resources.
  
                          //http://issues.umbraco.org/issue/U4-2749
  
                          //It was decided to just put these messages into the normal status messages. 
  
                          var msg = "Unauthorized access to URL: <br/><i>" + originalResponse.config.url.split('?')[0] + "</i>";
                          if (originalResponse.config.data) {
                              msg += "<br/> with data: <br/><i>" + angular.toJson(originalResponse.config.data) + "</i><br/>Contact your administrator for information.";
                          }
  
                          notifications.error(
                              "Authorization error",
                              msg);
                      }
  
                      return promise;
                  });
          };
      }])
  
      // We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block.
      .config(['$httpProvider', function ($httpProvider) {
          $httpProvider.responseInterceptors.push('securityInterceptor');
      }]);
  angular.module('umbraco.security.retryQueue', [])
  
  // This is a generic retry queue for security failures.  Each item is expected to expose two functions: retry and cancel.
  .factory('securityRetryQueue', ['$q', '$log', function ($q, $log) {
  
    var retryQueue = [];
    var service = {
      // The security service puts its own handler in here!
      onItemAddedCallbacks: [],
      
      hasMore: function() {
        return retryQueue.length > 0;
      },
      push: function(retryItem) {
        retryQueue.push(retryItem);
        // Call all the onItemAdded callbacks
        angular.forEach(service.onItemAddedCallbacks, function(cb) {
          try {
            cb(retryItem);
          } catch(e) {
            $log.error('securityRetryQueue.push(retryItem): callback threw an error' + e);
          }
        });
      },
      pushRetryFn: function(reason, retryFn) {
        // The reason parameter is optional
        if ( arguments.length === 1) {
          retryFn = reason;
          reason = undefined;
        }
  
        // The deferred object that will be resolved or rejected by calling retry or cancel
        var deferred = $q.defer();
        var retryItem = {
          reason: reason,
          retry: function() {
            // Wrap the result of the retryFn into a promise if it is not already
            $q.when(retryFn()).then(function(value) {
              // If it was successful then resolve our deferred
              deferred.resolve(value);
            }, function(value) {
              // Othewise reject it
              deferred.reject(value);
            });
          },
          cancel: function() {
            // Give up on retrying and reject our deferred
            deferred.reject();
          }
        };
        service.push(retryItem);
        return deferred.promise;
      },
      retryReason: function() {
        return service.hasMore() && retryQueue[0].reason;
      },
      cancelAll: function() {
        while(service.hasMore()) {
          retryQueue.shift().cancel();
        }
      },
      retryAll: function() {
        while(service.hasMore()) {
          retryQueue.shift().retry();
        }
      }
    };
    return service;
  }]);
  
  })();