text: /* oauth-ng - v0.4.2 - 2015-08-27 */
  
  'use strict';
  
  // App libraries
  angular.module('oauth', [
    'oauth.directive',      // login directive
    'oauth.accessToken',    // access token service
    'oauth.endpoint',       // oauth endpoint service
    'oauth.profile',        // profile model
    'oauth.storage',        // storage
    'oauth.interceptor',     // bearer token interceptor
    'oauth.configuration'   // token appender
  ])
    .config(['$locationProvider','$httpProvider',
    function($locationProvider, $httpProvider) {
      $httpProvider.interceptors.push('ExpiredInterceptor');
    }]);
  
  'use strict';
  
  var accessTokenService = angular.module('oauth.accessToken', []);
  
  accessTokenService.factory('AccessToken', ['Storage', '$rootScope', '$location', '$interval', function(Storage, $rootScope, $location, $interval){
  
    var service = {
      token: null
    },
    oAuth2HashTokens = [ //per http://tools.ietf.org/html/rfc6749#section-4.2.2
      'access_token', 'token_type', 'expires_in', 'scope', 'state',
      'error','error_description'
    ];
  
    /**
     * Returns the access token.
     */
    service.get = function(){
      return this.token;
    };
  
    /**
     * Sets and returns the access token. It tries (in order) the following strategies:
     * - takes the token from the fragment URI
     * - takes the token from the sessionStorage
     */
    service.set = function(){
      this.setTokenFromString($location.hash());
  
      //If hash is present in URL always use it, cuz its coming from oAuth2 provider redirect
      if(null === service.token){
        setTokenFromSession();
      }
  
      return this.token;
    };
  
    /**
     * Delete the access token and remove the session.
     * @returns {null}
     */
    service.destroy = function(){
      Storage.delete('token');
      this.token = null;
      return this.token;
    };
  
    /**
     * Tells if the access token is expired.
     */
    service.expired = function(){
      return (this.token && this.token.expires_at && new Date(this.token.expires_at) < new Date());
    };
  
    /**
     * Get the access token from a string and save it
     * @param hash
     */
    service.setTokenFromString = function(hash){
      var params = getTokenFromString(hash);
  
      if(params){
        removeFragment();
        setToken(params);
        setExpiresAt();
        // We have to save it again to make sure expires_at is set
        //  and the expiry event is set up properly
        setToken(this.token);
        $rootScope.$broadcast('oauth:login', service.token);
      }
    };
  
    /* * * * * * * * * *
     * PRIVATE METHODS *
     * * * * * * * * * */
  
    /**
     * Set the access token from the sessionStorage.
     */
    var setTokenFromSession = function(){
      var params = Storage.get('token');
      if (params) {
        setToken(params);
      }
    };
  
    /**
     * Set the access token.
     *
     * @param params
     * @returns {*|{}}
     */
    var setToken = function(params){
      service.token = service.token || {};      // init the token
      angular.extend(service.token, params);      // set the access token params
      setTokenInSession();                // save the token into the session
      setExpiresAtEvent();                // event to fire when the token expires
  
      return service.token;
    };
  
    /**
     * Parse the fragment URI and return an object
     * @param hash
     * @returns {{}}
     */
    var getTokenFromString = function(hash){
      var params = {},
          regex = /([^&=]+)=([^&]*)/g,
          m;
  
      while ((m = regex.exec(hash)) !== null) {
        params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
      }
  
      if(params.access_token || params.error){
        return params;
      }
    };
  
    /**
     * Save the access token into the session
     */
    var setTokenInSession = function(){
      Storage.set('token', service.token);
    };
  
    /**
     * Set the access token expiration date (useful for refresh logics)
     */
    var setExpiresAt = function(){
      if (!service.token) {
        return;
      }
      if(typeof(service.token.expires_in) !== 'undefined' && service.token.expires_in !== null) {
        var expires_at = new Date();
        expires_at.setSeconds(expires_at.getSeconds() + parseInt(service.token.expires_in)-60); // 60 seconds less to secure browser and response latency
        service.token.expires_at = expires_at;
      }
      else {
        service.token.expires_at = null;
      }
    };
  
  
    /**
     * Set the timeout at which the expired event is fired
     */
    var setExpiresAtEvent = function(){
      // Don't bother if there's no expires token
      if (typeof(service.token.expires_at) === 'undefined' || service.token.expires_at === null) {
        return;
      }
      var time = (new Date(service.token.expires_at))-(new Date());
      if(time && time > 0){
        $interval(function(){
          $rootScope.$broadcast('oauth:expired', service.token);
        }, time, 1);
      }
    };
  
    /**
     * Remove the oAuth2 pieces from the hash fragment
     */
    var removeFragment = function(){
      var curHash = $location.hash();
      angular.forEach(oAuth2HashTokens,function(hashKey){
        var re = new RegExp('&'+hashKey+'(=[^&]*)?|^'+hashKey+'(=[^&]*)?&?');
        curHash = curHash.replace(re,'');
      });
  
      $location.hash(curHash);
    };
  
    return service;
  
  }]);
  
  'use strict';
  
  var endpointClient = angular.module('oauth.endpoint', []);
  
  endpointClient.factory('Endpoint', function() {
  
    var service = {};
  
    /*
     * Defines the authorization URL
     */
  
    service.set = function(configuration) {
      this.config = configuration;
      return this.get();
    };
  
    /*
     * Returns the authorization URL
     */
  
    service.get = function( overrides ) {
      var params = angular.extend( {}, service.config, overrides);
      var oAuthScope = (params.scope) ? encodeURIComponent(params.scope) : '',
          state = (params.state) ? encodeURIComponent(params.state) : '',
          authPathHasQuery = (params.authorizePath.indexOf('?') === -1) ? false : true,
          appendChar = (authPathHasQuery) ? '&' : '?',    //if authorizePath has ? already append OAuth2 params
          responseType = (params.responseType) ? encodeURIComponent(params.responseType) : '';
  
      var url = params.site +
            params.authorizePath +
            appendChar + 'response_type=' + responseType + '&' +
            'client_id=' + encodeURIComponent(params.clientId) + '&' +
            'redirect_uri=' + encodeURIComponent(params.redirectUri) + '&' +
            'scope=' + oAuthScope + '&' +
            'state=' + state;
  
      if( params.nonce ) {
        url = url + '&nonce=' + params.nonce;
      }
      return url;
    };
  
    /*
     * Redirects the app to the authorization URL
     */
  
    service.redirect = function( overrides ) {
      var targetLocation = this.get( overrides );
      window.location.replace(targetLocation);
    };
  
    return service;
  });
  
  'use strict';
  
  var profileClient = angular.module('oauth.profile', []);
  
  profileClient.factory('Profile', ['$http', 'AccessToken', '$rootScope', function($http, AccessToken, $rootScope) {
    var service = {};
    var profile;
  
    service.find = function(uri) {
      var promise = $http.get(uri, { headers: headers() });
      promise.success(function(response) {
          profile = response;
          $rootScope.$broadcast('oauth:profile', profile);
        });
      return promise;
    };
  
    service.get = function() {
      return profile;
    };
  
    service.set = function(resource) {
      profile = resource;
      return profile;
    };
  
    var headers = function() {
      return { Authorization: 'Bearer ' + AccessToken.get().access_token };
    };
  
    return service;
  }]);
  
  'use strict';
  
  var storageService = angular.module('oauth.storage', ['ngStorage']);
  
  storageService.factory('Storage', ['$rootScope', '$sessionStorage', '$localStorage', function($rootScope, $sessionStorage, $localStorage){
  
    var service = {
      storage: $sessionStorage // By default
    };
  
    /**
     * Deletes the item from storage,
     * Returns the item's previous value
     */
    service.delete = function (name) {
      var stored = this.get(name);
      delete this.storage[name];
      return stored;
    };
  
    /**
     * Returns the item from storage
     */
    service.get = function (name) {
      return this.storage[name];
    };
  
    /**
     * Sets the item in storage to the value specified
     * Returns the item's value
     */
    service.set = function (name, value) {
      this.storage[name] = value;
      return this.get(name);
    };
  
    /**
     * Change the storage service being used
     */
    service.use = function (storage) {
      if (storage === 'sessionStorage') {
        this.storage = $sessionStorage;
      } else if (storage === 'localStorage') {
        this.storage = $localStorage;
      }
    };
  
    return service;
  }]);
  'use strict';
  
  var oauthConfigurationService = angular.module('oauth.configuration', []);
  
  oauthConfigurationService.provider('OAuthConfiguration', function() {
  	var _config = {};
  	
  	this.init = function(config, httpProvider) {
  		_config.protectedResources = config.protectedResources || [];
  		httpProvider.interceptors.push('AuthInterceptor');
  	};
  	
  	this.$get = function() {
  		return {
  			getConfig: function() {
  				return _config;
  			}
  		};
  	};
  })
  .factory('AuthInterceptor', function($q, $rootScope, OAuthConfiguration, AccessToken) {
  	return {
  		'request': function(config) {
  			OAuthConfiguration.getConfig().protectedResources.forEach(function(resource) {
  				// If the url is one of the protected resources, we want to see if there's a token and then
  				// add the token if it exists.
  				if (config.url.indexOf(resource) > -1) {
  					var token = AccessToken.get();
  					if (token) {
  						config.headers.Authorization = 'Bearer ' + token.access_token;
  					}
  				}
  			});
  			
  			return config;
  		}
  	};
  });
  'use strict';
  
  var interceptorService = angular.module('oauth.interceptor', []);
  
  interceptorService.factory('ExpiredInterceptor', ['Storage', '$rootScope', function (Storage, $rootScope) {
  
    var service = {};
  
    service.request = function(config) {
      var token = Storage.get('token');
  
      if (token && expired(token)) {
        $rootScope.$broadcast('oauth:expired', token);
      }
  
      return config;
    };
  
    var expired = function(token) {
      return (token && token.expires_at && new Date(token.expires_at) < new Date());
    };
  
    return service;
  }]);
  
  'use strict';
  
  var directives = angular.module('oauth.directive', []);
  
  directives.directive('oauth', [
    'AccessToken',
    'Endpoint',
    'Profile',
    'Storage',
    '$location',
    '$rootScope',
    '$compile',
    '$http',
    '$templateCache',
    function(AccessToken, Endpoint, Profile, Storage, $location, $rootScope, $compile, $http, $templateCache) {
  
      var definition = {
        restrict: 'AE',
        replace: true,
        scope: {
          site: '@',          // (required) set the oauth server host (e.g. http://oauth.example.com)
          clientId: '@',      // (required) client id
          redirectUri: '@',   // (required) client redirect uri
          responseType: '@',  // (optional) response type, defaults to token (use 'token' for implicit flow and 'code' for authorization code flow
          scope: '@',         // (optional) scope
          profileUri: '@',    // (optional) user profile uri (e.g http://example.com/me)
          template: '@',      // (optional) template to render (e.g bower_components/oauth-ng/dist/views/templates/default.html)
          text: '@',          // (optional) login text
          authorizePath: '@', // (optional) authorization url
          state: '@',         // (optional) An arbitrary unique string created by your app to guard against Cross-site Request Forgery
          storage: '@'        // (optional) Store token in 'sessionStorage' or 'localStorage', defaults to 'sessionStorage'
        }
      };
  
      definition.link = function postLink(scope, element) {
        scope.show = 'none';
  
        scope.$watch('clientId', function() {
          init();
        });
  
        var init = function() {
          initAttributes();          // sets defaults
          Storage.use(scope.storage);// set storage
          compile();                 // compiles the desired layout
          Endpoint.set(scope);       // sets the oauth authorization url
          AccessToken.set(scope);    // sets the access token object (if existing, from fragment or session)
          initProfile(scope);        // gets the profile resource (if existing the access token)
          initView();                // sets the view (logged in or out)
        };
  
        var initAttributes = function() {
          scope.authorizePath = scope.authorizePath || '/oauth/authorize';
          scope.tokenPath     = scope.tokenPath     || '/oauth/token';
          scope.template      = scope.template      || 'bower_components/oauth-ng/dist/views/templates/default.html';
          scope.responseType  = scope.responseType  || 'token';
          scope.text          = scope.text          || 'Sign In';
          scope.state         = scope.state         || undefined;
          scope.scope         = scope.scope         || undefined;
          scope.storage       = scope.storage       || 'sessionStorage';
        };
  
        var compile = function() {
          $http.get(scope.template, { cache: $templateCache }).success(function(html) {
            element.html(html);
            $compile(element.contents())(scope);
          });
        };
  
        var initProfile = function(scope) {
          var token = AccessToken.get();
  
          if (token && token.access_token && scope.profileUri) {
            Profile.find(scope.profileUri).success(function(response) {
              scope.profile = response;
            });
          }
        };
  
        var initView = function() {
          var token = AccessToken.get();
  
          if (!token) {
            return loggedOut(); // without access token it's logged out
          }
          if (token.access_token) {
            return authorized(); // if there is the access token we are done
          }
          if (token.error) {
            return denied(); // if the request has been denied we fire the denied event
          }
        };
  
        scope.login = function() {
          Endpoint.redirect();
        };
  
        scope.logout = function() {
          AccessToken.destroy(scope);
          $rootScope.$broadcast('oauth:logout');
          loggedOut();
        };
  
        scope.$on('oauth:expired', function() {
          AccessToken.destroy(scope);
          scope.show = 'logged-out';
        });
  
        // user is authorized
        var authorized = function() {
          $rootScope.$broadcast('oauth:authorized', AccessToken.get());
          scope.show = 'logged-in';
        };
  
        // set the oauth directive to the logged-out status
        var loggedOut = function() {
          $rootScope.$broadcast('oauth:loggedOut');
          scope.show = 'logged-out';
        };
  
        // set the oauth directive to the denied status
        var denied = function() {
          scope.show = 'denied';
          $rootScope.$broadcast('oauth:denied');
        };
  
        // Updates the template at runtime
        scope.$on('oauth:template:update', function(event, template) {
          scope.template = template;
          compile(scope);
        });
  
        // Hack to update the directive content on logout
        // TODO think to a cleaner solution
        scope.$on('$routeChangeSuccess', function () {
          init();
        });
      };
  
      return definition;
    }
  ]);
  