@@ -0,0 +1,345 @@ 
   
    
     
     
  
    /**  
 
    
     
     
  
     * Interceptor  
 
    
     
     
  
     * Implements functionality to catch various requests and fire events when they happen. This is generally to ensure  
 
    
     
     
  
     * that responses from the server are handled in a uniform fashion across the application. Also, by firing events  
 
    
     
     
  
     * it allows to have any number of handlers attach to the response.  
 
    
     
     
  
     *   
 
    
     
     
  
     * @author  Kirk Bushell  
 
    
     
     
  
     * @date  28th March 2013  
 
    
     
     
  
     */  
 
    
     
     
  
    var  module  =  angular . module ( 'core.interceptor' ,  [ ] ) ;  
 
    
     
     
  
     
 
    
     
     
  
    module . service (  'Messages' ,  [  '$filter' ,  function (  $filter  )  {  
 
    
     
     
  
    	var  service  =  {   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Store all the messages here. The default (base) or the custom messages.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @type  {Object }  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		messages : {   
 
    
     
     
  
    			base : {   
 
    
     
     
  
    				error : {   
 
    
     
     
  
    					create : 'Could not create :resource. Please try again.' ,   
 
    
     
     
  
    					read :   'Could not load :resource. Please try again.' ,   
 
    
     
     
  
    					update : 'Could not update :resource. Please try again.' ,   
 
    
     
     
  
    					'delete' : 'Could not delete :resource. Please try again.'   
 
    
     
     
  
    				} ,   
 
    
     
     
  
    				success : {   
 
    
     
     
  
    					create : ':resource created.' ,   
 
    
     
     
  
    					read :   'Loaded :resource successfully.' ,   
 
    
     
     
  
    					update : ':resource saved.' ,   
 
    
     
     
  
    					'delete' : ':resource deleted.'   
 
    
     
     
  
    				}   
 
    
     
     
  
    			} ,   
 
    
     
     
  
     
 
    
     
     
  
    			// Custom messages are stored here.   
 
    
     
     
  
    			custom : { }   
 
    
     
     
  
    		} ,   
 
    
     
     
  
     
 
    
     
     
  
    		get : function (  resource  ,  action  ,  type  )  {   
 
    
     
     
  
    			resource . split (  '-'  ) . join (  ' '  ) ;   
 
    
     
     
  
    			resource  =  $filter (  'ucfirst'  ) (  resource  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			var  customResource  =  get (  service . messages . custom [  resource . toLowerCase ( )  ]  ) ;   
 
    
     
     
  
    			if  (  customResource  !=  null  )  {   
 
    
     
     
  
    				var  custom  =  get (  customResource [  type  ] [  action  ]  ) ;   
 
    
     
     
  
    				if  (  custom  !=  null  )  return  custom ;   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			// Get the default message, if available, and perform the replacement.   
 
    
     
     
  
    			var  msg  =  get (  service . messages . base [  type  ] [  action  ]  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			if  (  msg  &&  msg . length  >  0  )  {   
 
    
     
     
  
    				msg  =  msg . split (  ':resource'  ) . join (  resource . singularize ( )  ) ;   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			return  msg ;   
 
    
     
     
  
    		} ,   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Registers a resource and all its custom messages.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param   {String } resource Resource name  
 
    
     
     
  
    		 * @param   {Object } messages An object with create, read, update, delete keys  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @return  {void }  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		register : function (  resource  ,  messages  )  {   
 
    
     
     
  
    			service . messages . custom [  resource  ]  =  messages ;   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
    	} ;   
 
    
     
     
  
     
 
    
     
     
  
    	return  service ;   
 
    
     
     
  
    } ] ) ;  
 
    
     
     
  
     
 
    
     
     
  
    module . config ( [ '$httpProvider' ,  function ( $httpProvider )  {  
 
    
     
     
  
    	var  interceptor  =  [ '$rootScope' ,  '$q' ,  'Notify' ,  'Messages' ,  'Analytics' ,  function ( $rootScope ,  $q ,  Notify ,  Messages ,  Analytics  )  {   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Parses the resource based on the url that was sent.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param   {object } response Response object  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @return  {string }  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		var  getResourceFromResponse  =  function (  response  )  {   
 
    
     
     
  
    			var  urlParts  =  response . config . url . split ( '?' ) ,   
 
    
     
     
  
    				url       =  urlParts [ 0 ] . replace ( / ^ \/ | \/ $ / g,  '' ) ,  // strip first and last slash.   
 
    
     
     
  
    				base      =  $rootScope . config . app . base . replace ( / ^ \/ | \/ $ / g,  '' ) ;  // strip first and last slash.   
 
    
     
     
  
     
 
    
     
     
  
    			// If there's a base, let's strip it out.   
 
    
     
     
  
    			if  (  base . length  )  {   
 
    
     
     
  
    				url  =  url . replace (  base  ,  ''  ) ;   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			urlParts  =  url . split ( '/' ) ;   
 
    
     
     
  
    			url  =  urlParts [ 0 ] ;   
 
    
     
     
  
     
 
    
     
     
  
    			return  url ;   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Extracts an ID from a URL, if it's available.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param   {string } resource The name of the resource.  
 
    
     
     
  
    		 * @param   {string } url      The URL to parse.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @return  {mixed }           Returns the extracted ID or null.  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		var  getIDFromURL  =  function (  resource  ,  url  )  {   
 
    
     
     
  
    			var  urlParts    =  url . split (  resource  ) ,   
 
    
     
     
  
    				possibleID  =  urlParts [  1  ] ,   
 
    
     
     
  
    				id ;   
 
    
     
     
  
     
 
    
     
     
  
    			// If there is nothing, we know we're creating and not updating.   
 
    
     
     
  
    			// Therefore, there will be no ID to return.   
 
    
     
     
  
    			if  (  ! possibleID  )  return  null ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Remove the first occurance of a slash.   
 
    
     
     
  
    			possibleID  =  possibleID . replace ( '/' ,  '' ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Check if there are any more slashes, so we know whether we should split   
 
    
     
     
  
    			// the possible ID variable or just return it.   
 
    
     
     
  
    			if  (  possibleID . indexOf ( '/' )  ===  - 1  )  {   
 
    
     
     
  
    				return  isNaN (  possibleID  )  ? null  : possibleID ;   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			// Since at this point we know that the url still has a slash in it, we will   
 
    
     
     
  
    			// split on that slash and get the first part of the array.   
 
    
     
     
  
    			id  =  possibleID . split ( '/' ) [ 0 ] ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Now we want to check if the value is a number or not.   
 
    
     
     
  
    			return  isNaN (  id  )  ? null  : id ;   
 
    
     
     
  
    		} ;   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Returns a custom resource action, if it has been supplied in the URL. This is defined  
 
    
     
     
  
    		 * by a string representation AFTER the resource id. Eg.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * /entries/1/submit  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param  string resource  
 
    
     
     
  
    		 * @param  string url  
 
    
     
     
  
    		 * @return  mixed string on success, null on failure  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		var  getActionFromUrl  =  function (  resource ,  url  )  {   
 
    
     
     
  
    			url  =  url . replace (  config . app . base ,  ''  ) . split (  '/'  ) ;   
 
    
     
     
  
    			url . shift ( ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Could be dealing with an integer or extra action   
 
    
     
     
  
    			if  (  url . length  >  1  )  {   
 
    
     
     
  
    				if  (  isNaN (  url [  1  ]  )  )  {   
 
    
     
     
  
    					// custom action   
 
    
     
     
  
    					return  url [  1  ] ;   
 
    
     
     
  
    				}   
 
    
     
     
  
     
 
    
     
     
  
    				if  (  url . length  >  2  &&  isNaN (  url [  2  ]  )  )  {   
 
    
     
     
  
    					return  url [  2  ] ;   
 
    
     
     
  
    				}   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			return  null ;   
 
    
     
     
  
    		} ;   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Based on the data returned from the server whenever there's a validation error  
 
    
     
     
  
    		 * we will construct a single validation message that is displayed in an alert.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param   {Object } data The data object returned from the server.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @return  {String }  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		var  getValidationMessages  =  function (  response  )  {   
 
    
     
     
  
    			var  errors  =  [ ] ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Check if the response is empty, meaning there are no validation errors.   
 
    
     
     
  
    			if  (  getResponseType ( response )  !=  'validation'  ||  $ . isEmptyObject (  response . data  )  ||  $ . isEmptyObject (  response . data . errors  )  )  return  errors ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Put all the errors from all the fields into 1 array. Basically flatenning the array.   
 
    
     
     
  
    			angular . forEach (  response . data . errors  ,  function (  issues  )  {   
 
    
     
     
  
    				angular . forEach (  issues  ,  function (  error  )  {   
 
    
     
     
  
    					errors . push (  error  ) ;   
 
    
     
     
  
    				} ) ;   
 
    
     
     
  
    			} ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			return  errors ;   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
    		var  getResponseType  =  function (  response  )  {   
 
    
     
     
  
    			return  get (  response . headers ( ) [  'x-response-type'  ]  ) ;   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
    		var  notificationate  =  function (  response  )  {   
 
    
     
     
  
    			var  method        =  response . config . method . toLowerCase ( ) ,   
 
    
     
     
  
    				action        =  '' ,   
 
    
     
     
  
    				status        =  response . status ,   
 
    
     
     
  
    				type          =  'Error' ,  // notification type. Error, Success, Info or Warning.   
 
    
     
     
  
    				responseType  =  getResponseType (  response  ) ,  // custom response type.   
 
    
     
     
  
    				message       =  null ,   
 
    
     
     
  
    				resource ,   
 
    
     
     
  
    				possibleAction ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Ignore GET requests.   
 
    
     
     
  
    			if  (  method  ==  'get'  &&  status  ==  200  )  return ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Any status 200 responses at this point are for successful operations.   
 
    
     
     
  
    			// So we will change the notification type to Success.   
 
    
     
     
  
    			if  (  status  ==  200  )  type  =  'Success' ;   
 
    
     
     
  
     
 
    
     
     
  
     
 
    
     
     
  
    			// Parse the resource name.   
 
    
     
     
  
    			resource  =  getResourceFromResponse (  response  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Do not display any message for exceptions.   
 
    
     
     
  
    			if  (  resource  ==  'exceptions'  )  return ;   
 
    
     
     
  
     
 
    
     
     
  
    			// Let's determine what action is taken based on the request method.   
 
    
     
     
  
    			switch  (  method  )  {   
 
    
     
     
  
    				case  'post' :   action  =  'create' ;  break ;   
 
    
     
     
  
    				case  'get' :    action  =  'read' ;    break ;   
 
    
     
     
  
    				case  'put' :    action  =  'update' ;  break ;   
 
    
     
     
  
    				case  'delete' : action  =  'delete' ;  break ;   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			// Check if there's an ID in the URL whenever we send a post request because the action   
 
    
     
     
  
    			// could be update instead of create.   
 
    
     
     
  
    			// This is done because ngResource sends a POST request for updates instead of PUT.   
 
    
     
     
  
    			if  (  method  ==  'post'  )  {   
 
    
     
     
  
    				if  (  getIDFromURL (  resource  ,  response . config . url  )  )  {   
 
    
     
     
  
    					action  =  'update' ;   
 
    
     
     
  
    				}   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			// Set up our action based on whether or not a custom one has been defined in the URL   
 
    
     
     
  
    			possibleAction  =  getActionFromUrl (  resource ,  response . config . url  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			if  (  possibleAction  )  {   
 
    
     
     
  
    				action  =  possibleAction ;   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			// Based on the response status.   
 
    
     
     
  
    			switch  (  status  )  {   
 
    
     
     
  
    				case  400 :  
 
    
     
     
  
    					if  (  responseType  ==  'validation'  )  {   
 
    
     
     
  
    						if  (  typeof  response . data . message  ==  'string'  )  {   
 
    
     
     
  
    							message  =  response . data . message ;   
 
    
     
     
  
    						}   
 
    
     
     
  
    					}   
 
    
     
     
  
    					else  {   
 
    
     
     
  
    						message  =  response . data ;   
 
    
     
     
  
    					}   
 
    
     
     
  
    					break ;   
 
    
     
     
  
     
 
    
     
     
  
    				case  401 :  
 
    
     
     
  
    					message  =  'Your current session has expired. Please log in.' ;   
 
    
     
     
  
    					break ;   
 
    
     
     
  
     
 
    
     
     
  
    				case  403 :  
 
    
     
     
  
    					message  =  'You do not have sufficient permission to access this resource.' ;   
 
    
     
     
  
    					break ;   
 
    
     
     
  
     
 
    
     
     
  
    				case  500 :  
 
    
     
     
  
    					if  (  angular . isString (  response . data  )  &&  response . data . length  )  {   
 
    
     
     
  
    						message  =  response . data ;   
 
    
     
     
  
    					}   
 
    
     
     
  
    					break ;   
 
    
     
     
  
     
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			if  (  ! message  )  {   
 
    
     
     
  
    				message  =  Messages . get (  resource  ,  action  ,  type . toLowerCase ( )  ) ;   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			// Send an update to the validation-errors directive to show/hide validation errors.   
 
    
     
     
  
    			$rootScope . $broadcast (  'validation.errors' ,  getValidationMessages (  response  )  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			if  (  message  )  {   
 
    
     
     
  
    				Notify [ type ] (  message  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    				// Google Analytics event tracking.   
 
    
     
     
  
    				Analytics . trackEvent (  resource  ,  type  ,  message  ) ;   
 
    
     
     
  
    			}   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Broadcasts an event that any part of the app can listen to  
 
    
     
     
  
    		 * and perform custom actions.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param   string name     The event name  
 
    
     
     
  
    		 * @param   mixed  response The response that comes back from the server  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @return  void  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		var  broadcast  =  function (  name  ,  response  )  {   
 
    
     
     
  
    			$rootScope . $broadcast (  name  ,  response  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			notificationate (  response  ) ;   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Successful response handler.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param   mixed response The response th at comes back from the server  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @return  mixed  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		var  success  =  function (  response  )  {   
 
    
     
     
  
    			broadcast (  'app.success'  ,  response  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			return  response ;   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
    		/**   
 
    
     
     
  
    		 * Invalid response handler.  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @param   mixed response The response th at comes back from the server  
 
    
     
     
  
    		 *  
 
    
     
     
  
    		 * @return  mixed  
 
    
     
     
  
    		 */  
 
    
     
     
  
    		var  error  =  function (  response  )  {   
 
    
     
     
  
    			// This is the default error event to broadcast.   
 
    
     
     
  
    			// It may be overwritten depending on the response status.   
 
    
     
     
  
    			var  event  =  'app.unknown-error' ;   
 
    
     
     
  
     
 
    
     
     
  
    			switch  ( response . status )  {   
 
    
     
     
  
    				case  400 : event  =  'app.error' ;  break ;  // Bad requests (validation errors.etc.)   
 
    
     
     
  
    				case  401 : event  =  'app.unauthorised' ;  break ;  // Unauthorised, should require login   
 
    
     
     
  
    				case  403 : event  =  'app.forbidden' ;  break ;  // Forbidden, user is simply not allowed access   
 
    
     
     
  
    				case  500 : event  =  'app.failure' ;  break ;  // Critical error on the server, catch and display   
 
    
     
     
  
    			}   
 
    
     
     
  
     
 
    
     
     
  
    			broadcast (  event  ,  response  ) ;   
 
    
     
     
  
     
 
    
     
     
  
    			return  $q . reject (  response  ) ;   
 
    
     
     
  
    		}   
 
    
     
     
  
     
 
    
     
     
  
     
 
    
     
     
  
    		return  function ( promise )  {   
 
    
     
     
  
    			return  promise . then (  success ,  error  ) ;   
 
    
     
     
  
    		} ;   
 
    
     
     
  
    	} ] ;   
 
    
     
     
  
     
 
    
     
     
  
    	$httpProvider . responseInterceptors . push (  interceptor  ) ;   
 
    
     
     
  
    } ] ) ;