src/apiWrapperService.js
/**
		* Makes API call and handles data
		* @param <Object> requestData	Data necessary to make a request. May contain: 
		*								- method (defaults to GET)
		*								- url (defaults to /)
		*								- data (Object or other, see generateFormDataFromData)
		*								- headers (Object)
		* @return <Promise> 			Promise that will be resolved when answer is fine, else rejected with a custom HTTPError
		*/
Makes API call and handles data
@param  requestData   Data necessary to make a request. May contain:
- method (defaults to GET)
- url (defaults to /)
- data (Object or other, see generateFormDataFromData)
- headers (Object)
@return                      Promise that will be resolved when answer is fine, else rejected with a custom HTTPError
Makes API call and handles data @param requestData Data necessary to make a request . May contain : - method ( defaults to GET ) - url ( defaults to / ) - data ( Object or other see generateFormDataFromData ) - headers ( Object ) @return Promise that will be resolved when answer is fine else rejected with a custom HTTPError
function callAPI( requestData ) {
			var validMethods = [ 'options', 'get', 'post', 'patch', 'put', 'delete' ];
			requestData.method = ( requestData.method && angular.isString( requestData.method ) ) ? requestData.method.toLowerCase() : 'get';
			if( validMethods.indexOf( requestData.method ) === -1 ) {
				return new Error( 'APIWrapperService: Invalid API request method: ' + requestData.method );
			}
			console.log( 'APIWrapperService: Request ' + requestData.url + ' (' + requestData.method + ') with data %o and headers %o', requestData.data, requestData.headers ); 
			requestData.headers = requestData.headers || {};
			if( _defaults && _defaults.headers ) {
				angular.extend( requestData.headers, _defaults.headers );
			}
			 that stay the same across all implementations (therefore do not inject them in the config phase)
			requestData.headers[ 'Accept-Language' ] 	= requestData.headers[ 'accept-language' ] || requestData.headers[ 'Accept-Language' ] || getUserLanguages().join( ', ' );
			requestData.headers.Pragma 					= requestData.headers.pragma || requestData.headers.Pragma || 'no-cache';
			requestData.headers[ 'Cache-Control' ] 		= requestData.headers[ 'Cache-Control' ] || requestData.headers[ 'Cache-Control' ] || 'no-cache';
			requestData.data = requestData.data || {};
			if( requestData.method === 'post' || requestData.method == 'put' || requestData.method == 'patch' ) {
				requestData.headers[ 'Content-Language' ] 	= requestData.headers[ 'content-language' ] || requestData.headers[ 'Content-Language' ];
				if( getUserLanguages().length > 0 && !requestData.headers[ 'Content-Language' ] ) {
					requestData.headers[ 'Content-Language' ] = getUserLanguages()[ 0 ];
				}
				var contentType = requestData.headers[ 'content-type' ] || requestData.headers[ 'Content-Type' ];
				if( contentType ) {
					if( contentType.toLowerCase() === 'application/json' && !angular.isString( requestData.data ) ) {
						requestData.data = JSON.stringify( requestData.data );
					}
				}
				 was not yet set: Use FormData, transform data to multipart.
				else {
					var formData = new FormData();
					generateFormDataFromData( requestData.data, formData );
					requestData.data = formData;
					requestData.transformRequest = angular.identity;
					 needs only to be set if browser doesn't support FormData
					requestData.headers[ 'Content-Type' ] = undefined;
				}
			}
			requestData.cache = false;
			if( requestData.method === 'get' && navigator && navigator.appVersion && navigator.appVersion.toLowerCase().indexOf("msie 9") > -1 ) {
				requestData.params = requestData.params || {};
				requestData.params._nocache = new Date().getTime() + Math.round( Math.random() * 9999 );
			}
			console.log( 'APIWrapperService: Make $http request with %o', requestData );
			return $http( requestData )
				.then( function( response ) {
					return handleSuccess( response );					
				}, function( response ) {
					var validStatusCodes = [ 301, 302 ];
					if( response && response.status && validStatusCodes.indexOf( response.status ) > -1 ) {
						return handleSuccess( response );
					}
					var description;
					if( !angular.isString( response.data ) ) {
						if( angular.isObject( response.data ) ) {
							var fieldNames = [ 'status', 'msg', 'message', 'description' ];
							fieldNames.forEach( function( fieldName ) {
								if( response.data[ fieldName ] ) {
									description = response.data[ fieldName ];
								}
							} );
							if( !description ) {
								description  = JSON.stringify( response.data );
							}
						}
					}
					else {
						description = response.data;
					}
					if( !description ) {
						description = 'HTTP ' + requestData.method.toUpperCase() + ' request to ' + requestData.url + ' failed: ' + response.data + ' (Status ' + response.status + ')';
					}
					function HTTPError( description ) {
						if( Error.captureStackTrace && angular.isFunction( Error.captureStackTrace ) ) {
							Error.captureStackTrace( this );
						}
						this.statusCode = response.status;
						this.message = description;
						this.name = 'HTTPError';
					}
					HTTPError.prototype = Object.create( Error.prototype );
					return $q.reject( new HTTPError( description ) );
				} );
		}
function callAPI ( requestData ) { var validMethods = [ 'options' , 'get' , 'post' , 'patch' , 'put' , 'delete' ] ; requestData . method = ( requestData . method && angular . isString ( requestData . method ) ) ? requestData . method . toLowerCase ( ) : 'get' ; if ( validMethods . indexOf ( requestData . method ) === - 1 ) { return new Error ( 'APIWrapperService: Invalid API request method: ' + requestData . method ) ; } console . log ( 'APIWrapperService: Request ' + requestData . url + ' (' + requestData . method + ') with data %o and headers %o' , requestData . data , requestData . headers ) ; requestData . headers = requestData . headers || { } ; if ( _defaults && _defaults . headers ) { angular . extend ( requestData . headers , _defaults . headers ) ; } requestData . headers [ 'Accept-Language' ] = requestData . headers [ 'accept-language' ] || requestData . headers [ 'Accept-Language' ] || getUserLanguages ( ) . join ( ', ' ) ; requestData . headers . Pragma = requestData . headers . pragma || requestData . headers . Pragma || 'no-cache' ; requestData . headers [ 'Cache-Control' ] = requestData . headers [ 'Cache-Control' ] || requestData . headers [ 'Cache-Control' ] || 'no-cache' ; requestData . data = requestData . data || { } ; if ( requestData . method === 'post' || requestData . method == 'put' || requestData . method == 'patch' ) { requestData . headers [ 'Content-Language' ] = requestData . headers [ 'content-language' ] || requestData . headers [ 'Content-Language' ] ; if ( getUserLanguages ( ) . length > 0 && ! requestData . headers [ 'Content-Language' ] ) { requestData . headers [ 'Content-Language' ] = getUserLanguages ( ) [ 0 ] ; } var contentType = requestData . headers [ 'content-type' ] || requestData . headers [ 'Content-Type' ] ; if ( contentType ) { if ( contentType . toLowerCase ( ) === 'application/json' && ! angular . isString ( requestData . data ) ) { requestData . data = JSON . stringify ( requestData . data ) ; } } else { var formData = new FormData ( ) ; generateFormDataFromData ( requestData . data , formData ) ; requestData . data = formData ; requestData . transformRequest = angular . identity ; requestData . headers [ 'Content-Type' ] = undefined ; } } requestData . cache = false ; if ( requestData . method === 'get' && navigator && navigator . appVersion && navigator . appVersion . toLowerCase ( ) . indexOf ( "msie 9" ) > - 1 ) { requestData . params = requestData . params || { } ; requestData . params . _nocache = new Date ( ) . getTime ( ) + Math . round ( Math . random ( ) * 9999 ) ; } console . log ( 'APIWrapperService: Make $http request with %o' , requestData ) ; return $http ( requestData ) . then ( function ( response ) { return handleSuccess ( response ) ; } , function ( response ) { var validStatusCodes = [ 301 , 302 ] ; if ( response && response . status && validStatusCodes . indexOf ( response . status ) > - 1 ) { return handleSuccess ( response ) ; } var description ; if ( ! angular . isString ( response . data ) ) { if ( angular . isObject ( response . data ) ) { var fieldNames = [ 'status' , 'msg' , 'message' , 'description' ] ; fieldNames . forEach ( function ( fieldName ) { if ( response . data [ fieldName ] ) { description = response . data [ fieldName ] ; } } ) ; if ( ! description ) { description = JSON . stringify ( response . data ) ; } } } else { description = response . data ; } if ( ! description ) { description = 'HTTP ' + requestData . method . toUpperCase ( ) + ' request to ' + requestData . url + ' failed: ' + response . data + ' (Status ' + response . status + ')' ; } function HTTPError ( description ) { if ( Error . captureStackTrace && angular . isFunction ( Error . captureStackTrace ) ) { Error . captureStackTrace ( this ) ; } this . statusCode = response . status ; this . message = description ; this . name = 'HTTPError' ; } HTTPError . prototype = Object . create ( Error . prototype ) ; return $q . reject ( new HTTPError ( description ) ) ; } ) ; }
Makes API call and handles data
 @param <Object> requestData   Data necessary to make a request.
Makes API call and handles data @param <Object > requestData Data necessary to make a request .
// Method // Headers // Headers that stay the same across all implementations (therefore do not inject them in the config phase) // Disable caching // Data // Convert data to Multipart/Form-Data and set header correspondingly // if we're PUTting, PATCHing or POSTing // Content-Language is only needed on requests that write to the server // Content-Type // If Content-Type is set and is application/json, stringify content, if needed // application/json: Stringify, if not already done. // Content-Type was not yet set: Use FormData, transform data to multipart. // Prevent angular from serializing our request data – especially for files // http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs // Let user set content type: https://groups.google.com/forum/#!topic/angular/MBf8qvBpuVE // In case of files, boundary ID is needed; can't be set manually. // Content-Type needs only to be set if browser doesn't support FormData // Disable caching // IE f***ing 9 f****ng cashes all f*****ng get requests // 301 and 302 should not fail, hell! // Try to get the best description of the error // Names of the fields that might hold the error, least important first