angular.module('chttp', []) .factory('chttp', ['$http', function($http) { /* Allow a url to be passed instead of an object, since its by far the most common use-case */ function normalize(config) { if('string' === typeof config) return {url: config}; return config; } /* Merge two configs */ function merge(oldConfig, newConfig) { oldConfig = normalize(oldConfig); newConfig = normalize(newConfig); var oldUrl = oldConfig.url; var newUrl = newConfig.url; var config = _.merge({}, oldConfig, newConfig, function(a, b) { if(_.isArray(a) && _.isArray(b)) return a.concat(b); return undefined; }); // Concatenate urls, so that we can do things like // var api = chttp({url: apiServerUrl}) // var user = api({url: '/user'}) // user.get('/me'); if(oldUrl && newUrl) config.url = oldUrl + newUrl; return config; } /* Final preparation of a config before handing it off to $http Currently only does parameter transformation. */ function prepare(config) { if(config.transformParams) { [].concat(config.transformParams).forEach(function(transform) { config.params = transform(config.params); }); } return config; } /* Decorate a function with the $http service's methods */ function decorate(fn, config) { // No data methods ['get', 'head', 'delete', 'jsonp'].forEach(function(method) { fn[method] = function(url, newConfig) { newConfig = newConfig || {}; newConfig.url = url || ''; newConfig.method = method; return $http(prepare(merge(config, newConfig))); }; }); // Data methods ['post', 'put'].forEach(function(method) { fn[method] = function(url, data, newConfig) { newConfig = newConfig || {}; newConfig.url = url || ''; newConfig.method = method; newConfig.data = data; return $http(prepare(merge(config, newConfig))); }; }); return fn; } function factory(config) { return decorate(function(newConfig) { newConfig = merge(config, newConfig); return factory(newConfig); }, config); } return factory({}); }]);