bastion 3.2.2 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,16 +1,16 @@
1
1
  /**
2
- * @license AngularJS v1.2.9
3
- * (c) 2010-2014 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.5
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {'use strict';
6
+ (function(window, angular) {'use strict';
7
7
 
8
8
  var $resourceMinErr = angular.$$minErr('$resource');
9
9
 
10
10
  // Helper functions and regex to lookup a dotted path on an object
11
11
  // stopping at undefined/null. The path must be composed of ASCII
12
12
  // identifiers (just like $parse)
13
- var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
13
+ var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
14
14
 
15
15
  function isValidDottedPath(path) {
16
16
  return (path != null && path !== '' && path !== 'hasOwnProperty' &&
@@ -22,7 +22,7 @@ function lookupDottedPath(obj, path) {
22
22
  throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
23
23
  }
24
24
  var keys = path.split('.');
25
- for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
25
+ for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
26
26
  var key = keys[i];
27
27
  obj = (obj !== null) ? obj[key] : undefined;
28
28
  }
@@ -35,12 +35,12 @@ function lookupDottedPath(obj, path) {
35
35
  function shallowClearAndCopy(src, dst) {
36
36
  dst = dst || {};
37
37
 
38
- angular.forEach(dst, function(value, key){
38
+ angular.forEach(dst, function(value, key) {
39
39
  delete dst[key];
40
40
  });
41
41
 
42
42
  for (var key in src) {
43
- if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
43
+ if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
44
44
  dst[key] = src[key];
45
45
  }
46
46
  }
@@ -49,7 +49,7 @@ function shallowClearAndCopy(src, dst) {
49
49
  }
50
50
 
51
51
  /**
52
- * @ngdoc overview
52
+ * @ngdoc module
53
53
  * @name ngResource
54
54
  * @description
55
55
  *
@@ -58,7 +58,6 @@ function shallowClearAndCopy(src, dst) {
58
58
  * The `ngResource` module provides interaction support with RESTful services
59
59
  * via the $resource service.
60
60
  *
61
- * {@installModule resource}
62
61
  *
63
62
  * <div doc-module-components="ngResource"></div>
64
63
  *
@@ -66,9 +65,12 @@ function shallowClearAndCopy(src, dst) {
66
65
  */
67
66
 
68
67
  /**
69
- * @ngdoc object
70
- * @name ngResource.$resource
68
+ * @ngdoc service
69
+ * @name $resource
71
70
  * @requires $http
71
+ * @requires ng.$log
72
+ * @requires $q
73
+ * @requires ng.$timeout
72
74
  *
73
75
  * @description
74
76
  * A factory which creates a resource object that lets you interact with
@@ -79,7 +81,19 @@ function shallowClearAndCopy(src, dst) {
79
81
  *
80
82
  * Requires the {@link ngResource `ngResource`} module to be installed.
81
83
  *
82
- * @param {string} url A parametrized URL template with parameters prefixed by `:` as in
84
+ * By default, trailing slashes will be stripped from the calculated URLs,
85
+ * which can pose problems with server backends that do not expect that
86
+ * behavior. This can be disabled by configuring the `$resourceProvider` like
87
+ * this:
88
+ *
89
+ * ```js
90
+ app.config(['$resourceProvider', function($resourceProvider) {
91
+ // Don't strip trailing slashes from calculated URLs
92
+ $resourceProvider.defaults.stripTrailingSlashes = false;
93
+ }]);
94
+ * ```
95
+ *
96
+ * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
83
97
  * `/user/:username`. If you are using a URL with a port number (e.g.
84
98
  * `http://example.com:8080/api`), it will be respected.
85
99
  *
@@ -91,7 +105,7 @@ function shallowClearAndCopy(src, dst) {
91
105
  * can escape it with `/\.`.
92
106
  *
93
107
  * @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
108
+ * `actions` methods. If a parameter value is a function, it will be executed every time
95
109
  * when a param value needs to be obtained for a request (unless the param was overridden).
96
110
  *
97
111
  * Each key value in the parameter object is first bound to url template if present and then any
@@ -100,12 +114,14 @@ function shallowClearAndCopy(src, dst) {
100
114
  * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
101
115
  * URL `/path/greet?salutation=Hello`.
102
116
  *
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).
117
+ * If the parameter value is prefixed with `@` then the value for that parameter will be extracted
118
+ * from the corresponding property on the `data` object (provided when calling an action method).
119
+ * For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
120
+ * `someParam` will be `data.someProp`.
105
121
  *
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}:
122
+ * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
123
+ * the default set of resource actions. The declaration should be created in the format of {@link
124
+ * ng.$http#usage $http.config}:
109
125
  *
110
126
  * {action1: {method:?, params:?, isArray:?, headers:?, ...},
111
127
  * action2: {method:?, params:?, isArray:?, headers:?, ...},
@@ -115,8 +131,8 @@ function shallowClearAndCopy(src, dst) {
115
131
  *
116
132
  * - **`action`** – {string} – The name of action. This name becomes the name of the method on
117
133
  * your resource object.
118
- * - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`,
119
- * `DELETE`, and `JSONP`.
134
+ * - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
135
+ * `DELETE`, `JSONP`, etc).
120
136
  * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
121
137
  * the parameter value is a function, it will be executed every time when a param value needs to
122
138
  * be obtained for a request (unless the param was overridden).
@@ -128,46 +144,69 @@ function shallowClearAndCopy(src, dst) {
128
144
  * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
129
145
  * transform function or an array of such functions. The transform function takes the http
130
146
  * request body and headers and returns its transformed (typically serialized) version.
147
+ * By default, transformRequest will contain one function that checks if the request data is
148
+ * an object and serializes to using `angular.toJson`. To prevent this behavior, set
149
+ * `transformRequest` to an empty array: `transformRequest: []`
131
150
  * - **`transformResponse`** –
132
151
  * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
133
152
  * transform function or an array of such functions. The transform function takes the http
134
153
  * response body and headers and returns its transformed (typically deserialized) version.
154
+ * By default, transformResponse will contain one function that checks if the response looks
155
+ * like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior,
156
+ * set `transformResponse` to an empty array: `transformResponse: []`
135
157
  * - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
136
158
  * GET request, otherwise if a cache instance built with
137
159
  * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
138
160
  * caching.
139
- * - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
140
- * should abort the request when resolved.
161
+ * - **`timeout`** – `{number}` – timeout in milliseconds.<br />
162
+ * **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are
163
+ * **not** supported in $resource, because the same value would be used for multiple requests.
164
+ * If you are looking for a way to cancel requests, you should use the `cancellable` option.
165
+ * - **`cancellable`** – `{boolean}` – if set to true, the request made by a "non-instance" call
166
+ * will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's
167
+ * return value. Calling `$cancelRequest()` for a non-cancellable or an already
168
+ * completed/cancelled request will have no effect.<br />
141
169
  * - **`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}.
170
+ * XHR object. See
171
+ * [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
172
+ * for more information.
173
+ * - **`responseType`** - `{string}` - see
174
+ * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
146
175
  * - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
147
176
  * `response` and `responseError`. Both `response` and `responseError` interceptors get called
148
177
  * with `http response` object. See {@link ng.$http $http interceptors}.
149
178
  *
179
+ * @param {Object} options Hash with custom settings that should extend the
180
+ * default `$resourceProvider` behavior. The supported options are:
181
+ *
182
+ * - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
183
+ * slashes from any calculated URL will be stripped. (Defaults to true.)
184
+ * - **`cancellable`** – {boolean} – If true, the request made by a "non-instance" call will be
185
+ * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return value.
186
+ * This can be overwritten per action. (Defaults to false.)
187
+ *
150
188
  * @returns {Object} A resource "class" object with methods for the default set of resource actions
151
189
  * 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'} };
190
+ * ```js
191
+ * { 'get': {method:'GET'},
192
+ * 'save': {method:'POST'},
193
+ * 'query': {method:'GET', isArray:true},
194
+ * 'remove': {method:'DELETE'},
195
+ * 'delete': {method:'DELETE'} };
196
+ * ```
158
197
  *
159
198
  * Calling these methods invoke an {@link ng.$http} with the specified http method,
160
199
  * destination and parameters. When the data is returned from the server then the object is an
161
200
  * instance of the resource class. The actions `save`, `remove` and `delete` are available on it
162
201
  * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
163
202
  * 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>
203
+ * ```js
204
+ * var User = $resource('/user/:userId', {userId:'@id'});
205
+ * var user = User.get({userId:123}, function() {
206
+ * user.abc = true;
207
+ * user.$save();
208
+ * });
209
+ * ```
171
210
  *
172
211
  * It is important to realize that invoking a $resource object method immediately returns an
173
212
  * empty reference (object or array depending on `isArray`). Once the data is returned from the
@@ -184,13 +223,15 @@ function shallowClearAndCopy(src, dst) {
184
223
  * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
185
224
  * - non-GET instance actions: `instance.$action([parameters], [success], [error])`
186
225
  *
187
- * Success callback is called with (value, responseHeaders) arguments. Error callback is called
226
+ *
227
+ * Success callback is called with (value, responseHeaders) arguments, where the value is
228
+ * the populated resource instance or collection object. The error callback is called
188
229
  * with (httpResponse) argument.
189
230
  *
190
231
  * Class actions return empty instance (with additional properties below).
191
232
  * Instance actions return promise of the action.
192
233
  *
193
- * The Resource instances and collection have these additional properties:
234
+ * The Resource instances and collections have these additional properties:
194
235
  *
195
236
  * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
196
237
  * instance or collection.
@@ -200,18 +241,26 @@ function shallowClearAndCopy(src, dst) {
200
241
  * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
201
242
  * rendering until the resource(s) are loaded.
202
243
  *
203
- * On failure, the promise is resolved with the {@link ng.$http http response} object, without
244
+ * On failure, the promise is rejected with the {@link ng.$http http response} object, without
204
245
  * the `resource` property.
205
246
  *
247
+ * If an interceptor object was provided, the promise will instead be resolved with the value
248
+ * returned by the interceptor.
249
+ *
206
250
  * - `$resolved`: `true` after first server interaction is completed (either with success or
207
251
  * rejection), `false` before that. Knowing if the Resource has been resolved is useful in
208
252
  * data-binding.
209
253
  *
254
+ * The Resource instances and collections have these additional methods:
255
+ *
256
+ * - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
257
+ * collection, calling this method will abort the request.
258
+ *
210
259
  * @example
211
260
  *
212
261
  * # Credit card resource
213
262
  *
214
- * <pre>
263
+ * ```js
215
264
  // Define CreditCard class
216
265
  var CreditCard = $resource('/user/:userId/card/:cardId',
217
266
  {userId:123, cardId:'@id'}, {
@@ -244,57 +293,75 @@ function shallowClearAndCopy(src, dst) {
244
293
  // POST: /user/123/card {number:'0123', name:'Mike Smith'}
245
294
  // server returns: {id:789, number:'0123', name: 'Mike Smith'};
246
295
  expect(newCard.id).toEqual(789);
247
- * </pre>
296
+ * ```
248
297
  *
249
298
  * The object returned from this function execution is a resource "class" which has "static" method
250
299
  * for each action in the definition.
251
300
  *
252
301
  * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
253
302
  * `headers`.
303
+ *
304
+ * @example
305
+ *
306
+ * # User resource
307
+ *
254
308
  * When the data is returned from the server then the object is an instance of the resource type and
255
309
  * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
256
310
  * operations (create, read, update, delete) on server-side data.
257
311
 
258
- <pre>
312
+ ```js
259
313
  var User = $resource('/user/:userId', {userId:'@id'});
260
- var user = User.get({userId:123}, function() {
314
+ User.get({userId:123}, function(user) {
261
315
  user.abc = true;
262
316
  user.$save();
263
317
  });
264
- </pre>
318
+ ```
265
319
  *
266
320
  * It's worth noting that the success callback for `get`, `query` and other methods gets passed
267
321
  * in the response that came from the server as well as $http header getter function, so one
268
322
  * could rewrite the above example and get access to http headers as:
269
323
  *
270
- <pre>
324
+ ```js
271
325
  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
326
+ User.get({userId:123}, function(user, getResponseHeaders){
327
+ user.abc = true;
328
+ user.$save(function(user, putResponseHeaders) {
329
+ //user => saved user object
276
330
  //putResponseHeaders => $http header getter
277
331
  });
278
332
  });
279
- </pre>
280
-
333
+ ```
334
+ *
335
+ * You can also access the raw `$http` promise via the `$promise` property on the object returned
336
+ *
337
+ ```
338
+ var User = $resource('/user/:userId', {userId:'@id'});
339
+ User.get({userId:123})
340
+ .$promise.then(function(user) {
341
+ $scope.user = user;
342
+ });
343
+ ```
344
+ *
345
+ * @example
346
+ *
281
347
  * # Creating a custom 'PUT' request
348
+ *
282
349
  * 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']);
350
+ * ```js
351
+ * var app = angular.module('app', ['ngResource', 'ngRoute']);
285
352
  *
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) {
353
+ * // Some APIs expect a PUT request in the format URL/object/ID
354
+ * // Here we are creating an 'update' method
355
+ * app.factory('Notes', ['$resource', function($resource) {
289
356
  * return $resource('/notes/:id', null,
290
357
  * {
291
358
  * 'update': { method:'PUT' }
292
359
  * });
293
- * }]);
360
+ * }]);
294
361
  *
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',
362
+ * // In our controller we get the ID from the URL using ngRoute and $routeParams
363
+ * // We pass in $routeParams and our Notes factory along with $scope
364
+ * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
298
365
  function($scope, $routeParams, Notes) {
299
366
  * // First get a note object from the factory
300
367
  * var note = Notes.get({ id:$routeParams.id });
@@ -304,291 +371,398 @@ function shallowClearAndCopy(src, dst) {
304
371
  * Notes.update({ id:$id }, note);
305
372
  *
306
373
  * // This will PUT /notes/ID with the note object in the request payload
307
- * }]);
308
- * </pre>
374
+ * }]);
375
+ * ```
376
+ *
377
+ * @example
378
+ *
379
+ * # Cancelling requests
380
+ *
381
+ * If an action's configuration specifies that it is cancellable, you can cancel the request related
382
+ * to an instance or collection (as long as it is a result of a "non-instance" call):
383
+ *
384
+ ```js
385
+ // ...defining the `Hotel` resource...
386
+ var Hotel = $resource('/api/hotel/:id', {id: '@id'}, {
387
+ // Let's make the `query()` method cancellable
388
+ query: {method: 'get', isArray: true, cancellable: true}
389
+ });
390
+
391
+ // ...somewhere in the PlanVacationController...
392
+ ...
393
+ this.onDestinationChanged = function onDestinationChanged(destination) {
394
+ // We don't care about any pending request for hotels
395
+ // in a different destination any more
396
+ this.availableHotels.$cancelRequest();
397
+
398
+ // Let's query for hotels in '<destination>'
399
+ // (calls: /api/hotel?location=<destination>)
400
+ this.availableHotels = Hotel.query({location: destination});
401
+ };
402
+ ```
403
+ *
309
404
  */
310
405
  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'}
406
+ provider('$resource', function() {
407
+ var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
408
+ var provider = this;
409
+
410
+ this.defaults = {
411
+ // Strip slashes by default
412
+ stripTrailingSlashes: true,
413
+
414
+ // Default actions configuration
415
+ actions: {
416
+ 'get': {method: 'GET'},
417
+ 'save': {method: 'POST'},
418
+ 'query': {method: 'GET', isArray: true},
419
+ 'remove': {method: 'DELETE'},
420
+ 'delete': {method: 'DELETE'}
421
+ }
319
422
  };
320
- var noop = angular.noop,
423
+
424
+ this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) {
425
+
426
+ var noop = angular.noop,
321
427
  forEach = angular.forEach,
322
428
  extend = angular.extend,
323
429
  copy = angular.copy,
324
430
  isFunction = angular.isFunction;
325
431
 
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
- }
432
+ /**
433
+ * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
434
+ * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
435
+ * (pchar) allowed in path segments:
436
+ * segment = *pchar
437
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
438
+ * pct-encoded = "%" HEXDIG HEXDIG
439
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
440
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
441
+ * / "*" / "+" / "," / ";" / "="
442
+ */
443
+ function encodeUriSegment(val) {
444
+ return encodeUriQuery(val, true).
445
+ replace(/%26/gi, '&').
446
+ replace(/%3D/gi, '=').
447
+ replace(/%2B/gi, '+');
448
+ }
343
449
 
344
450
 
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
- }
451
+ /**
452
+ * This method is intended for encoding *key* or *value* parts of query component. We need a
453
+ * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
454
+ * have to be encoded per http://tools.ietf.org/html/rfc3986:
455
+ * query = *( pchar / "/" / "?" )
456
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
457
+ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
458
+ * pct-encoded = "%" HEXDIG HEXDIG
459
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
460
+ * / "*" / "+" / "," / ";" / "="
461
+ */
462
+ function encodeUriQuery(val, pctEncodeSpaces) {
463
+ return encodeURIComponent(val).
464
+ replace(/%40/gi, '@').
465
+ replace(/%3A/gi, ':').
466
+ replace(/%24/g, '$').
467
+ replace(/%2C/gi, ',').
468
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
469
+ }
364
470
 
365
- function Route(template, defaults) {
366
- this.template = template;
367
- this.defaults = defaults || {};
368
- this.urlParams = {};
369
- }
471
+ function Route(template, defaults) {
472
+ this.template = template;
473
+ this.defaults = extend({}, provider.defaults, defaults);
474
+ this.urlParams = {};
475
+ }
370
476
 
371
- Route.prototype = {
372
- setUrlParams: function(config, params, actionUrl) {
373
- var self = this,
477
+ Route.prototype = {
478
+ setUrlParams: function(config, params, actionUrl) {
479
+ var self = this,
374
480
  url = actionUrl || self.template,
375
481
  val,
376
- encodedVal;
482
+ encodedVal,
483
+ protocolAndDomain = '';
377
484
 
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;
485
+ var urlParams = self.urlParams = {};
486
+ forEach(url.split(/\W/), function(param) {
487
+ if (param === 'hasOwnProperty') {
488
+ throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
489
+ }
490
+ if (!(new RegExp("^\\d+$").test(param)) && param &&
491
+ (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
492
+ urlParams[param] = {
493
+ isQueryParamValue: (new RegExp("\\?.*=:" + param + "(?:\\W|$)")).test(url)
494
+ };
495
+ }
496
+ });
497
+ url = url.replace(/\\:/g, ':');
498
+ url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
499
+ protocolAndDomain = match;
500
+ return '';
501
+ });
502
+
503
+ params = params || {};
504
+ forEach(self.urlParams, function(paramInfo, urlParam) {
505
+ val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
506
+ if (angular.isDefined(val) && val !== null) {
507
+ if (paramInfo.isQueryParamValue) {
508
+ encodedVal = encodeUriQuery(val, true);
401
509
  } else {
402
- return leadingSlashes + tail;
510
+ encodedVal = encodeUriSegment(val);
403
511
  }
404
- });
512
+ url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
513
+ return encodedVal + p1;
514
+ });
515
+ } else {
516
+ url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
517
+ leadingSlashes, tail) {
518
+ if (tail.charAt(0) == '/') {
519
+ return tail;
520
+ } else {
521
+ return leadingSlashes + tail;
522
+ }
523
+ });
524
+ }
525
+ });
526
+
527
+ // strip trailing slashes and set the url (unless this behavior is specifically disabled)
528
+ if (self.defaults.stripTrailingSlashes) {
529
+ url = url.replace(/\/+$/, '') || '/';
405
530
  }
406
- });
407
531
 
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(/\/\\\./, '/.');
532
+ // then replace collapse `/.` if found in the last URL path segment before the query
533
+ // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
534
+ url = url.replace(/\/\.(?=\w+($|\?))/, '.');
535
+ // replace escaped `/\.` with `/.`
536
+ config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
415
537
 
416
538
 
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
- };
539
+ // set params - delegate param encoding to $http
540
+ forEach(params, function(value, key) {
541
+ if (!self.urlParams[key]) {
542
+ config.params = config.params || {};
543
+ config.params[key] = value;
544
+ }
545
+ });
546
+ }
547
+ };
426
548
 
427
549
 
428
- function resourceFactory(url, paramDefaults, actions) {
429
- var route = new Route(url);
550
+ function resourceFactory(url, paramDefaults, actions, options) {
551
+ var route = new Route(url, options);
430
552
 
431
- actions = extend({}, DEFAULT_ACTIONS, actions);
553
+ actions = extend({}, provider.defaults.actions, actions);
432
554
 
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
- }
555
+ function extractParams(data, actionParams) {
556
+ var ids = {};
557
+ actionParams = extend({}, paramDefaults, actionParams);
558
+ forEach(actionParams, function(value, key) {
559
+ if (isFunction(value)) { value = value(); }
560
+ ids[key] = value && value.charAt && value.charAt(0) == '@' ?
561
+ lookupDottedPath(data, value.substr(1)) : value;
562
+ });
563
+ return ids;
564
+ }
565
+
566
+ function defaultResponseInterceptor(response) {
567
+ return response.resource;
568
+ }
569
+
570
+ function Resource(value) {
571
+ shallowClearAndCopy(value || {}, this);
572
+ }
573
+
574
+ Resource.prototype.toJSON = function() {
575
+ var data = extend({}, this);
576
+ delete data.$promise;
577
+ delete data.$resolved;
578
+ return data;
579
+ };
443
580
 
444
- function defaultResponseInterceptor(response) {
445
- return response.resource;
446
- }
581
+ forEach(actions, function(action, name) {
582
+ var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
583
+ var numericTimeout = action.timeout;
584
+ var cancellable = angular.isDefined(action.cancellable) ? action.cancellable :
585
+ (options && angular.isDefined(options.cancellable)) ? options.cancellable :
586
+ provider.defaults.cancellable;
587
+
588
+ if (numericTimeout && !angular.isNumber(numericTimeout)) {
589
+ $log.debug('ngResource:\n' +
590
+ ' Only numeric values are allowed as `timeout`.\n' +
591
+ ' Promises are not supported in $resource, because the same value would ' +
592
+ 'be used for multiple requests. If you are looking for a way to cancel ' +
593
+ 'requests, you should use the `cancellable` option.');
594
+ delete action.timeout;
595
+ numericTimeout = null;
596
+ }
447
597
 
448
- function Resource(value){
449
- shallowClearAndCopy(value || {}, this);
450
- }
598
+ Resource[name] = function(a1, a2, a3, a4) {
599
+ var params = {}, data, success, error;
451
600
 
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;
601
+ /* jshint -W086 */ /* (purposefully fall through case statements) */
602
+ switch (arguments.length) {
603
+ case 4:
604
+ error = a4;
605
+ success = a3;
606
+ //fallthrough
607
+ case 3:
608
+ case 2:
609
+ if (isFunction(a2)) {
610
+ if (isFunction(a1)) {
611
+ success = a1;
612
+ error = a2;
613
+ break;
614
+ }
615
+
616
+ success = a2;
617
+ error = a3;
618
+ //fallthrough
619
+ } else {
620
+ params = a1;
621
+ data = a2;
622
+ success = a3;
623
+ break;
624
+ }
625
+ case 1:
626
+ if (isFunction(a1)) success = a1;
627
+ else if (hasBody) data = a1;
628
+ else params = a1;
470
629
  break;
630
+ case 0: break;
631
+ default:
632
+ throw $resourceMinErr('badargs',
633
+ "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
634
+ arguments.length);
635
+ }
636
+ /* jshint +W086 */ /* (purposefully fall through case statements) */
637
+
638
+ var isInstanceCall = this instanceof Resource;
639
+ var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
640
+ var httpConfig = {};
641
+ var responseInterceptor = action.interceptor && action.interceptor.response ||
642
+ defaultResponseInterceptor;
643
+ var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
644
+ undefined;
645
+ var timeoutDeferred;
646
+ var numericTimeoutPromise;
647
+
648
+ forEach(action, function(value, key) {
649
+ switch (key) {
650
+ default:
651
+ httpConfig[key] = copy(value);
652
+ break;
653
+ case 'params':
654
+ case 'isArray':
655
+ case 'interceptor':
656
+ case 'cancellable':
657
+ break;
471
658
  }
659
+ });
472
660
 
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
- });
661
+ if (!isInstanceCall && cancellable) {
662
+ timeoutDeferred = $q.defer();
663
+ httpConfig.timeout = timeoutDeferred.promise;
508
664
 
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;
665
+ if (numericTimeout) {
666
+ numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout);
535
667
  }
536
668
  }
537
669
 
538
- value.$resolved = true;
539
-
540
- response.resource = value;
541
-
542
- return response;
543
- }, function(response) {
544
- value.$resolved = true;
670
+ if (hasBody) httpConfig.data = data;
671
+ route.setUrlParams(httpConfig,
672
+ extend({}, extractParams(data, action.params || {}), params),
673
+ action.url);
674
+
675
+ var promise = $http(httpConfig).then(function(response) {
676
+ var data = response.data;
677
+
678
+ if (data) {
679
+ // Need to convert action.isArray to boolean in case it is undefined
680
+ // jshint -W018
681
+ if (angular.isArray(data) !== (!!action.isArray)) {
682
+ throw $resourceMinErr('badcfg',
683
+ 'Error in resource configuration for action `{0}`. Expected response to ' +
684
+ 'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
685
+ angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
686
+ }
687
+ // jshint +W018
688
+ if (action.isArray) {
689
+ value.length = 0;
690
+ forEach(data, function(item) {
691
+ if (typeof item === "object") {
692
+ value.push(new Resource(item));
693
+ } else {
694
+ // Valid JSON values may be string literals, and these should not be converted
695
+ // into objects. These items will not have access to the Resource prototype
696
+ // methods, but unfortunately there
697
+ value.push(item);
698
+ }
699
+ });
700
+ } else {
701
+ var promise = value.$promise; // Save the promise
702
+ shallowClearAndCopy(data, value);
703
+ value.$promise = promise; // Restore the promise
704
+ }
705
+ }
706
+ response.resource = value;
545
707
 
546
- (error||noop)(response);
708
+ return response;
709
+ }, function(response) {
710
+ (error || noop)(response);
711
+ return $q.reject(response);
712
+ });
547
713
 
548
- return $q.reject(response);
549
- });
714
+ promise['finally'](function() {
715
+ value.$resolved = true;
716
+ if (!isInstanceCall && cancellable) {
717
+ value.$cancelRequest = angular.noop;
718
+ $timeout.cancel(numericTimeoutPromise);
719
+ timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null;
720
+ }
721
+ });
550
722
 
551
- promise = promise.then(
723
+ promise = promise.then(
552
724
  function(response) {
553
725
  var value = responseInterceptor(response);
554
- (success||noop)(value, response.headers);
726
+ (success || noop)(value, response.headers);
555
727
  return value;
556
728
  },
557
729
  responseErrorInterceptor);
558
730
 
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;
731
+ if (!isInstanceCall) {
732
+ // we are creating instance / collection
733
+ // - set the initial promise
734
+ // - return the instance / collection
735
+ value.$promise = promise;
736
+ value.$resolved = false;
737
+ if (cancellable) value.$cancelRequest = timeoutDeferred.resolve;
565
738
 
566
- return value;
567
- }
739
+ return value;
740
+ }
568
741
 
569
- // instance call
570
- return promise;
571
- };
742
+ // instance call
743
+ return promise;
744
+ };
572
745
 
573
746
 
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
- });
747
+ Resource.prototype['$' + name] = function(params, success, error) {
748
+ if (isFunction(params)) {
749
+ error = success; success = params; params = {};
750
+ }
751
+ var result = Resource[name].call(this, params, this, success, error);
752
+ return result.$promise || result;
753
+ };
754
+ });
582
755
 
583
- Resource.bind = function(additionalParamDefaults){
584
- return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
585
- };
756
+ Resource.bind = function(additionalParamDefaults) {
757
+ return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
758
+ };
586
759
 
587
- return Resource;
588
- }
760
+ return Resource;
761
+ }
589
762
 
590
- return resourceFactory;
591
- }]);
763
+ return resourceFactory;
764
+ }];
765
+ });
592
766
 
593
767
 
594
768
  })(window, window.angular);