1 | /** |
---|
2 | * @license AngularJS v1.2.7 |
---|
3 | * (c) 2010-2014 Google, Inc. http://angularjs.org |
---|
4 | * License: MIT |
---|
5 | */ |
---|
6 | (function(window, angular, undefined) {'use strict'; |
---|
7 | |
---|
8 | var $resourceMinErr = angular.$$minErr('$resource'); |
---|
9 | |
---|
10 | // Helper functions and regex to lookup a dotted path on an object |
---|
11 | // stopping at undefined/null. The path must be composed of ASCII |
---|
12 | // identifiers (just like $parse) |
---|
13 | var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/; |
---|
14 | |
---|
15 | function isValidDottedPath(path) { |
---|
16 | return (path != null && path !== '' && path !== 'hasOwnProperty' && |
---|
17 | MEMBER_NAME_REGEX.test('.' + path)); |
---|
18 | } |
---|
19 | |
---|
20 | function lookupDottedPath(obj, path) { |
---|
21 | if (!isValidDottedPath(path)) { |
---|
22 | throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path); |
---|
23 | } |
---|
24 | var keys = path.split('.'); |
---|
25 | for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) { |
---|
26 | var key = keys[i]; |
---|
27 | obj = (obj !== null) ? obj[key] : undefined; |
---|
28 | } |
---|
29 | return obj; |
---|
30 | } |
---|
31 | |
---|
32 | /** |
---|
33 | * Create a shallow copy of an object and clear other fields from the destination |
---|
34 | */ |
---|
35 | function shallowClearAndCopy(src, dst) { |
---|
36 | dst = dst || {}; |
---|
37 | |
---|
38 | angular.forEach(dst, function(value, key){ |
---|
39 | delete dst[key]; |
---|
40 | }); |
---|
41 | |
---|
42 | for (var key in src) { |
---|
43 | if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') { |
---|
44 | dst[key] = src[key]; |
---|
45 | } |
---|
46 | } |
---|
47 | |
---|
48 | return dst; |
---|
49 | } |
---|
50 | |
---|
51 | /** |
---|
52 | * @ngdoc overview |
---|
53 | * @name ngResource |
---|
54 | * @description |
---|
55 | * |
---|
56 | * # ngResource |
---|
57 | * |
---|
58 | * The `ngResource` module provides interaction support with RESTful services |
---|
59 | * via the $resource service. |
---|
60 | * |
---|
61 | * {@installModule resource} |
---|
62 | * |
---|
63 | * <div doc-module-components="ngResource"></div> |
---|
64 | * |
---|
65 | * See {@link ngResource.$resource `$resource`} for usage. |
---|
66 | */ |
---|
67 | |
---|
68 | /** |
---|
69 | * @ngdoc object |
---|
70 | * @name ngResource.$resource |
---|
71 | * @requires $http |
---|
72 | * |
---|
73 | * @description |
---|
74 | * A factory which creates a resource object that lets you interact with |
---|
75 | * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. |
---|
76 | * |
---|
77 | * The returned resource object has action methods which provide high-level behaviors without |
---|
78 | * the need to interact with the low level {@link ng.$http $http} service. |
---|
79 | * |
---|
80 | * Requires the {@link ngResource `ngResource`} module to be installed. |
---|
81 | * |
---|
82 | * @param {string} url A parametrized URL template with parameters prefixed by `:` as in |
---|
83 | * `/user/:username`. If you are using a URL with a port number (e.g. |
---|
84 | * `http://example.com:8080/api`), it will be respected. |
---|
85 | * |
---|
86 | * If you are using a url with a suffix, just add the suffix, like this: |
---|
87 | * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')` |
---|
88 | * or even `$resource('http://example.com/resource/:resource_id.:format')` |
---|
89 | * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be |
---|
90 | * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you |
---|
91 | * can escape it with `/\.`. |
---|
92 | * |
---|
93 | * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in |
---|
94 | * `actions` methods. If any of the parameter value is a function, it will be executed every time |
---|
95 | * when a param value needs to be obtained for a request (unless the param was overridden). |
---|
96 | * |
---|
97 | * Each key value in the parameter object is first bound to url template if present and then any |
---|
98 | * excess keys are appended to the url seapph query after the `?`. |
---|
99 | * |
---|
100 | * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in |
---|
101 | * URL `/path/greet?salutation=Hello`. |
---|
102 | * |
---|
103 | * If the parameter value is prefixed with `@` then the value of that parameter is extracted from |
---|
104 | * the data object (useful for non-GET operations). |
---|
105 | * |
---|
106 | * @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the |
---|
107 | * default set of resource actions. The declaration should be created in the format of {@link |
---|
108 | * ng.$http#usage_parameters $http.config}: |
---|
109 | * |
---|
110 | * {action1: {method:?, params:?, isArray:?, headers:?, ...}, |
---|
111 | * action2: {method:?, params:?, isArray:?, headers:?, ...}, |
---|
112 | * ...} |
---|
113 | * |
---|
114 | * Where: |
---|
115 | * |
---|
116 | * - **`action`** â {string} â The name of action. This name becomes the name of the method on |
---|
117 | * your resource object. |
---|
118 | * - **`method`** â {string} â HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, |
---|
119 | * `DELETE`, and `JSONP`. |
---|
120 | * - **`params`** â {Object=} â Optional set of pre-bound parameters for this action. If any of |
---|
121 | * the parameter value is a function, it will be executed every time when a param value needs to |
---|
122 | * be obtained for a request (unless the param was overridden). |
---|
123 | * - **`url`** â {string} â action specific `url` override. The url templating is supported just |
---|
124 | * like for the resource-level urls. |
---|
125 | * - **`isArray`** â {boolean=} â If true then the returned object for this action is an array, |
---|
126 | * see `returns` section. |
---|
127 | * - **`transformRequest`** â |
---|
128 | * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` â |
---|
129 | * transform function or an array of such functions. The transform function takes the http |
---|
130 | * request body and headers and returns its transformed (typically serialized) version. |
---|
131 | * - **`transformResponse`** â |
---|
132 | * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` â |
---|
133 | * transform function or an array of such functions. The transform function takes the http |
---|
134 | * response body and headers and returns its transformed (typically deserialized) version. |
---|
135 | * - **`cache`** â `{boolean|Cache}` â If true, a default $http cache will be used to cache the |
---|
136 | * GET request, otherwise if a cache instance built with |
---|
137 | * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for |
---|
138 | * caching. |
---|
139 | * - **`timeout`** â `{number|Promise}` â timeout in milliseconds, or {@link ng.$q promise} that |
---|
140 | * should abort the request when resolved. |
---|
141 | * - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the |
---|
142 | * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5 |
---|
143 | * requests with credentials} for more information. |
---|
144 | * - **`responseType`** - `{string}` - see {@link |
---|
145 | * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}. |
---|
146 | * - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods - |
---|
147 | * `response` and `responseError`. Both `response` and `responseError` interceptors get called |
---|
148 | * with `http response` object. See {@link ng.$http $http interceptors}. |
---|
149 | * |
---|
150 | * @returns {Object} A resource "class" object with methods for the default set of resource actions |
---|
151 | * optionally extended with custom `actions`. The default set contains these actions: |
---|
152 | * |
---|
153 | * { 'get': {method:'GET'}, |
---|
154 | * 'save': {method:'POST'}, |
---|
155 | * 'query': {method:'GET', isArray:true}, |
---|
156 | * 'remove': {method:'DELETE'}, |
---|
157 | * 'delete': {method:'DELETE'} }; |
---|
158 | * |
---|
159 | * Calling these methods invoke an {@link ng.$http} with the specified http method, |
---|
160 | * destination and parameters. When the data is returned from the server then the object is an |
---|
161 | * instance of the resource class. The actions `save`, `remove` and `delete` are available on it |
---|
162 | * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create, |
---|
163 | * read, update, delete) on server-side data like this: |
---|
164 | * <pre> |
---|
165 | var User = $resource('/user/:userId', {userId:'@id'}); |
---|
166 | var user = User.get({userId:123}, function() { |
---|
167 | user.abc = true; |
---|
168 | user.$save(); |
---|
169 | }); |
---|
170 | </pre> |
---|
171 | * |
---|
172 | * It is important to realize that invoking a $resource object method immediately returns an |
---|
173 | * empty reference (object or array depending on `isArray`). Once the data is returned from the |
---|
174 | * server the existing reference is populated with the actual data. This is a useful trick since |
---|
175 | * usually the resource is assigned to a model which is then rendered by the view. Having an empty |
---|
176 | * object results in no rendering, once the data arrives from the server then the object is |
---|
177 | * populated with the data and the view automatically re-renders itself showing the new data. This |
---|
178 | * means that in most cases one never has to write a callback function for the action methods. |
---|
179 | * |
---|
180 | * The action methods on the class object or instance object can be invoked with the following |
---|
181 | * parameters: |
---|
182 | * |
---|
183 | * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])` |
---|
184 | * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])` |
---|
185 | * - non-GET instance actions: `instance.$action([parameters], [success], [error])` |
---|
186 | * |
---|
187 | * Success callback is called with (value, responseHeaders) arguments. Error callback is called |
---|
188 | * with (httpResponse) argument. |
---|
189 | * |
---|
190 | * Class actions return empty instance (with additional properties below). |
---|
191 | * Instance actions return promise of the action. |
---|
192 | * |
---|
193 | * The Resource instances and collection have these additional properties: |
---|
194 | * |
---|
195 | * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this |
---|
196 | * instance or collection. |
---|
197 | * |
---|
198 | * On success, the promise is resolved with the same resource instance or collection object, |
---|
199 | * updated with data from server. This makes it easy to use in |
---|
200 | * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view |
---|
201 | * rendering until the resource(s) are loaded. |
---|
202 | * |
---|
203 | * On failure, the promise is resolved with the {@link ng.$http http response} object, without |
---|
204 | * the `resource` property. |
---|
205 | * |
---|
206 | * - `$resolved`: `true` after first server interaction is completed (either with success or |
---|
207 | * rejection), `false` before that. Knowing if the Resource has been resolved is useful in |
---|
208 | * data-binding. |
---|
209 | * |
---|
210 | * @example |
---|
211 | * |
---|
212 | * # Credit card resource |
---|
213 | * |
---|
214 | * <pre> |
---|
215 | // Define CreditCard class |
---|
216 | var CreditCard = $resource('/user/:userId/card/:cardId', |
---|
217 | {userId:123, cardId:'@id'}, { |
---|
218 | charge: {method:'POST', params:{charge:true}} |
---|
219 | }); |
---|
220 | |
---|
221 | // We can retrieve a collection from the server |
---|
222 | var cards = CreditCard.query(function() { |
---|
223 | // GET: /user/123/card |
---|
224 | // server returns: [ {id:456, number:'1234', name:'Smith'} ]; |
---|
225 | |
---|
226 | var card = cards[0]; |
---|
227 | // each item is an instance of CreditCard |
---|
228 | expect(card instanceof CreditCard).toEqual(true); |
---|
229 | card.name = "J. Smith"; |
---|
230 | // non GET methods are mapped onto the instances |
---|
231 | card.$save(); |
---|
232 | // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'} |
---|
233 | // server returns: {id:456, number:'1234', name: 'J. Smith'}; |
---|
234 | |
---|
235 | // our custom method is mapped as well. |
---|
236 | card.$charge({amount:9.99}); |
---|
237 | // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'} |
---|
238 | }); |
---|
239 | |
---|
240 | // we can create an instance as well |
---|
241 | var newCard = new CreditCard({number:'0123'}); |
---|
242 | newCard.name = "Mike Smith"; |
---|
243 | newCard.$save(); |
---|
244 | // POST: /user/123/card {number:'0123', name:'Mike Smith'} |
---|
245 | // server returns: {id:789, number:'0123', name: 'Mike Smith'}; |
---|
246 | expect(newCard.id).toEqual(789); |
---|
247 | * </pre> |
---|
248 | * |
---|
249 | * The object returned from this function execution is a resource "class" which has "static" method |
---|
250 | * for each action in the definition. |
---|
251 | * |
---|
252 | * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and |
---|
253 | * `headers`. |
---|
254 | * When the data is returned from the server then the object is an instance of the resource type and |
---|
255 | * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD |
---|
256 | * operations (create, read, update, delete) on server-side data. |
---|
257 | |
---|
258 | <pre> |
---|
259 | var User = $resource('/user/:userId', {userId:'@id'}); |
---|
260 | var user = User.get({userId:123}, function() { |
---|
261 | user.abc = true; |
---|
262 | user.$save(); |
---|
263 | }); |
---|
264 | </pre> |
---|
265 | * |
---|
266 | * It's worth noting that the success callback for `get`, `query` and other methods gets passed |
---|
267 | * in the response that came from the server as well as $http header getter function, so one |
---|
268 | * could rewrite the above example and get access to http headers as: |
---|
269 | * |
---|
270 | <pre> |
---|
271 | var User = $resource('/user/:userId', {userId:'@id'}); |
---|
272 | User.get({userId:123}, function(u, getResponseHeaders){ |
---|
273 | u.abc = true; |
---|
274 | u.$save(function(u, putResponseHeaders) { |
---|
275 | //u => saved user object |
---|
276 | //putResponseHeaders => $http header getter |
---|
277 | }); |
---|
278 | }); |
---|
279 | </pre> |
---|
280 | |
---|
281 | * # Creating a custom 'PUT' request |
---|
282 | * In this example we create a custom method on our resource to make a PUT request |
---|
283 | * <pre> |
---|
284 | * var app = angular.module('app', ['ngResource', 'ngRoute']); |
---|
285 | * |
---|
286 | * // Some APIs expect a PUT request in the format URL/object/ID |
---|
287 | * // Here we are creating an 'update' method |
---|
288 | * app.factory('Notes', ['$resource', function($resource) { |
---|
289 | * return $resource('/notes/:id', null, |
---|
290 | * { |
---|
291 | * 'update': { method:'PUT' } |
---|
292 | * }); |
---|
293 | * }]); |
---|
294 | * |
---|
295 | * // In our controller we get the ID from the URL using ngRoute and $routeParams |
---|
296 | * // We pass in $routeParams and our Notes factory along with $scope |
---|
297 | * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes', |
---|
298 | function($scope, $routeParams, Notes) { |
---|
299 | * // First get a note object from the factory |
---|
300 | * var note = Notes.get({ id:$routeParams.id }); |
---|
301 | * $id = note.id; |
---|
302 | * |
---|
303 | * // Now call update passing in the ID first then the object you are updating |
---|
304 | * Notes.update({ id:$id }, note); |
---|
305 | * |
---|
306 | * // This will PUT /notes/ID with the note object in the request payload |
---|
307 | * }]); |
---|
308 | * </pre> |
---|
309 | */ |
---|
310 | angular.module('ngResource', ['ng']). |
---|
311 | factory('$resource', ['$http', '$q', function($http, $q) { |
---|
312 | |
---|
313 | var DEFAULT_ACTIONS = { |
---|
314 | 'get': {method:'GET'}, |
---|
315 | 'save': {method:'POST'}, |
---|
316 | 'query': {method:'GET', isArray:true}, |
---|
317 | 'remove': {method:'DELETE'}, |
---|
318 | 'delete': {method:'DELETE'} |
---|
319 | }; |
---|
320 | var noop = angular.noop, |
---|
321 | forEach = angular.forEach, |
---|
322 | extend = angular.extend, |
---|
323 | copy = angular.copy, |
---|
324 | isFunction = angular.isFunction; |
---|
325 | |
---|
326 | /** |
---|
327 | * We need our custom method because encodeURIComponent is too aggressive and doesn't follow |
---|
328 | * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path |
---|
329 | * segments: |
---|
330 | * segment = *pchar |
---|
331 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
---|
332 | * pct-encoded = "%" HEXDIG HEXDIG |
---|
333 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" |
---|
334 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" |
---|
335 | * / "*" / "+" / "," / ";" / "=" |
---|
336 | */ |
---|
337 | function encodeUriSegment(val) { |
---|
338 | return encodeUriQuery(val, true). |
---|
339 | replace(/%26/gi, '&'). |
---|
340 | replace(/%3D/gi, '='). |
---|
341 | replace(/%2B/gi, '+'); |
---|
342 | } |
---|
343 | |
---|
344 | |
---|
345 | /** |
---|
346 | * This method is intended for encoding *key* or *value* parts of query component. We need a |
---|
347 | * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't |
---|
348 | * have to be encoded per http://tools.ietf.org/html/rfc3986: |
---|
349 | * query = *( pchar / "/" / "?" ) |
---|
350 | * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
---|
351 | * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" |
---|
352 | * pct-encoded = "%" HEXDIG HEXDIG |
---|
353 | * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" |
---|
354 | * / "*" / "+" / "," / ";" / "=" |
---|
355 | */ |
---|
356 | function encodeUriQuery(val, pctEncodeSpaces) { |
---|
357 | return encodeURIComponent(val). |
---|
358 | replace(/%40/gi, '@'). |
---|
359 | replace(/%3A/gi, ':'). |
---|
360 | replace(/%24/g, '$'). |
---|
361 | replace(/%2C/gi, ','). |
---|
362 | replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); |
---|
363 | } |
---|
364 | |
---|
365 | function Route(template, defaults) { |
---|
366 | this.template = template; |
---|
367 | this.defaults = defaults || {}; |
---|
368 | this.urlParams = {}; |
---|
369 | } |
---|
370 | |
---|
371 | Route.prototype = { |
---|
372 | setUrlParams: function(config, params, actionUrl) { |
---|
373 | var self = this, |
---|
374 | url = actionUrl || self.template, |
---|
375 | val, |
---|
376 | encodedVal; |
---|
377 | |
---|
378 | var urlParams = self.urlParams = {}; |
---|
379 | forEach(url.split(/\W/), function(param){ |
---|
380 | if (param === 'hasOwnProperty') { |
---|
381 | throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name."); |
---|
382 | } |
---|
383 | if (!(new RegExp("^\\d+$").test(param)) && param && |
---|
384 | (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) { |
---|
385 | urlParams[param] = true; |
---|
386 | } |
---|
387 | }); |
---|
388 | url = url.replace(/\\:/g, ':'); |
---|
389 | |
---|
390 | params = params || {}; |
---|
391 | forEach(self.urlParams, function(_, urlParam){ |
---|
392 | val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; |
---|
393 | if (angular.isDefined(val) && val !== null) { |
---|
394 | encodedVal = encodeUriSegment(val); |
---|
395 | url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), encodedVal + "$1"); |
---|
396 | } else { |
---|
397 | url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match, |
---|
398 | leadingSlashes, tail) { |
---|
399 | if (tail.charAt(0) == '/') { |
---|
400 | return tail; |
---|
401 | } else { |
---|
402 | return leadingSlashes + tail; |
---|
403 | } |
---|
404 | }); |
---|
405 | } |
---|
406 | }); |
---|
407 | |
---|
408 | // strip trailing slashes and set the url |
---|
409 | url = url.replace(/\/+$/, '') || '/'; |
---|
410 | // then replace collapse `/.` if found in the last URL path segment before the query |
---|
411 | // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x` |
---|
412 | url = url.replace(/\/\.(?=\w+($|\?))/, '.'); |
---|
413 | // replace escaped `/\.` with `/.` |
---|
414 | config.url = url.replace(/\/\\\./, '/.'); |
---|
415 | |
---|
416 | |
---|
417 | // set params - delegate param encoding to $http |
---|
418 | forEach(params, function(value, key){ |
---|
419 | if (!self.urlParams[key]) { |
---|
420 | config.params = config.params || {}; |
---|
421 | config.params[key] = value; |
---|
422 | } |
---|
423 | }); |
---|
424 | } |
---|
425 | }; |
---|
426 | |
---|
427 | |
---|
428 | function resourceFactory(url, paramDefaults, actions) { |
---|
429 | var route = new Route(url); |
---|
430 | |
---|
431 | actions = extend({}, DEFAULT_ACTIONS, actions); |
---|
432 | |
---|
433 | function extractParams(data, actionParams){ |
---|
434 | var ids = {}; |
---|
435 | actionParams = extend({}, paramDefaults, actionParams); |
---|
436 | forEach(actionParams, function(value, key){ |
---|
437 | if (isFunction(value)) { value = value(); } |
---|
438 | ids[key] = value && value.charAt && value.charAt(0) == '@' ? |
---|
439 | lookupDottedPath(data, value.substr(1)) : value; |
---|
440 | }); |
---|
441 | return ids; |
---|
442 | } |
---|
443 | |
---|
444 | function defaultResponseInterceptor(response) { |
---|
445 | return response.resource; |
---|
446 | } |
---|
447 | |
---|
448 | function Resource(value){ |
---|
449 | shallowClearAndCopy(value || {}, this); |
---|
450 | } |
---|
451 | |
---|
452 | forEach(actions, function(action, name) { |
---|
453 | var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method); |
---|
454 | |
---|
455 | Resource[name] = function(a1, a2, a3, a4) { |
---|
456 | var params = {}, data, success, error; |
---|
457 | |
---|
458 | /* jshint -W086 */ /* (purposefully fall through case statements) */ |
---|
459 | switch(arguments.length) { |
---|
460 | case 4: |
---|
461 | error = a4; |
---|
462 | success = a3; |
---|
463 | //fallthrough |
---|
464 | case 3: |
---|
465 | case 2: |
---|
466 | if (isFunction(a2)) { |
---|
467 | if (isFunction(a1)) { |
---|
468 | success = a1; |
---|
469 | error = a2; |
---|
470 | break; |
---|
471 | } |
---|
472 | |
---|
473 | success = a2; |
---|
474 | error = a3; |
---|
475 | //fallthrough |
---|
476 | } else { |
---|
477 | params = a1; |
---|
478 | data = a2; |
---|
479 | success = a3; |
---|
480 | break; |
---|
481 | } |
---|
482 | case 1: |
---|
483 | if (isFunction(a1)) success = a1; |
---|
484 | else if (hasBody) data = a1; |
---|
485 | else params = a1; |
---|
486 | break; |
---|
487 | case 0: break; |
---|
488 | default: |
---|
489 | throw $resourceMinErr('badargs', |
---|
490 | "Expected up to 4 arguments [params, data, success, error], got {0} arguments", |
---|
491 | arguments.length); |
---|
492 | } |
---|
493 | /* jshint +W086 */ /* (purposefully fall through case statements) */ |
---|
494 | |
---|
495 | var isInstanceCall = this instanceof Resource; |
---|
496 | var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data)); |
---|
497 | var httpConfig = {}; |
---|
498 | var responseInterceptor = action.interceptor && action.interceptor.response || |
---|
499 | defaultResponseInterceptor; |
---|
500 | var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || |
---|
501 | undefined; |
---|
502 | |
---|
503 | forEach(action, function(value, key) { |
---|
504 | if (key != 'params' && key != 'isArray' && key != 'interceptor') { |
---|
505 | httpConfig[key] = copy(value); |
---|
506 | } |
---|
507 | }); |
---|
508 | |
---|
509 | if (hasBody) httpConfig.data = data; |
---|
510 | route.setUrlParams(httpConfig, |
---|
511 | extend({}, extractParams(data, action.params || {}), params), |
---|
512 | action.url); |
---|
513 | |
---|
514 | var promise = $http(httpConfig).then(function(response) { |
---|
515 | var data = response.data, |
---|
516 | promise = value.$promise; |
---|
517 | |
---|
518 | if (data) { |
---|
519 | // Need to convert action.isArray to boolean in case it is undefined |
---|
520 | // jshint -W018 |
---|
521 | if (angular.isArray(data) !== (!!action.isArray)) { |
---|
522 | throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' + |
---|
523 | 'response to contain an {0} but got an {1}', |
---|
524 | action.isArray?'array':'object', angular.isArray(data)?'array':'object'); |
---|
525 | } |
---|
526 | // jshint +W018 |
---|
527 | if (action.isArray) { |
---|
528 | value.length = 0; |
---|
529 | forEach(data, function(item) { |
---|
530 | value.push(new Resource(item)); |
---|
531 | }); |
---|
532 | } else { |
---|
533 | shallowClearAndCopy(data, value); |
---|
534 | value.$promise = promise; |
---|
535 | } |
---|
536 | } |
---|
537 | |
---|
538 | value.$resolved = true; |
---|
539 | |
---|
540 | response.resource = value; |
---|
541 | |
---|
542 | return response; |
---|
543 | }, function(response) { |
---|
544 | value.$resolved = true; |
---|
545 | |
---|
546 | (error||noop)(response); |
---|
547 | |
---|
548 | return $q.reject(response); |
---|
549 | }); |
---|
550 | |
---|
551 | promise = promise.then( |
---|
552 | function(response) { |
---|
553 | var value = responseInterceptor(response); |
---|
554 | (success||noop)(value, response.headers); |
---|
555 | return value; |
---|
556 | }, |
---|
557 | responseErrorInterceptor); |
---|
558 | |
---|
559 | if (!isInstanceCall) { |
---|
560 | // we are creating instance / collection |
---|
561 | // - set the initial promise |
---|
562 | // - return the instance / collection |
---|
563 | value.$promise = promise; |
---|
564 | value.$resolved = false; |
---|
565 | |
---|
566 | return value; |
---|
567 | } |
---|
568 | |
---|
569 | // instance call |
---|
570 | return promise; |
---|
571 | }; |
---|
572 | |
---|
573 | |
---|
574 | Resource.prototype['$' + name] = function(params, success, error) { |
---|
575 | if (isFunction(params)) { |
---|
576 | error = success; success = params; params = {}; |
---|
577 | } |
---|
578 | var result = Resource[name].call(this, params, this, success, error); |
---|
579 | return result.$promise || result; |
---|
580 | }; |
---|
581 | }); |
---|
582 | |
---|
583 | Resource.bind = function(additionalParamDefaults){ |
---|
584 | return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); |
---|
585 | }; |
---|
586 | |
---|
587 | return Resource; |
---|
588 | } |
---|
589 | |
---|
590 | return resourceFactory; |
---|
591 | }]); |
---|
592 | |
---|
593 | |
---|
594 | })(window, window.angular); |
---|