angularjs-rails 1.5.8 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.8
2
+ * @license AngularJS v1.6.0
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1223,6 +1223,8 @@ function IDC_Y(cp) {
1223
1223
  return false;
1224
1224
  }
1225
1225
 
1226
+ /* eslint-disable new-cap */
1227
+
1226
1228
  /**
1227
1229
  * @ngdoc module
1228
1230
  * @name ngParseExt
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.8
2
+ * @license AngularJS v1.6.0
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -137,9 +137,12 @@ function shallowClearAndCopy(src, dst) {
137
137
  * Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
138
138
  * method that does not accept a request body)
139
139
  *
140
- * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
141
- * the default set of resource actions. The declaration should be created in the format of {@link
142
- * ng.$http#usage $http.config}:
140
+ * @param {Object.<Object>=} actions Hash with declaration of custom actions that will be available
141
+ * in addition to the default set of resource actions (see below). If a custom action has the same
142
+ * key as a default action (e.g. `save`), then the default action will be *overwritten*, and not
143
+ * extended.
144
+ *
145
+ * The declaration should be created in the format of {@link ng.$http#usage $http.config}:
143
146
  *
144
147
  * {action1: {method:?, params:?, isArray:?, headers:?, ...},
145
148
  * action2: {method:?, params:?, isArray:?, headers:?, ...},
@@ -164,12 +167,13 @@ function shallowClearAndCopy(src, dst) {
164
167
  * transform function or an array of such functions. The transform function takes the http
165
168
  * request body and headers and returns its transformed (typically serialized) version.
166
169
  * By default, transformRequest will contain one function that checks if the request data is
167
- * an object and serializes to using `angular.toJson`. To prevent this behavior, set
170
+ * an object and serializes it using `angular.toJson`. To prevent this behavior, set
168
171
  * `transformRequest` to an empty array: `transformRequest: []`
169
172
  * - **`transformResponse`** –
170
- * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
173
+ * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
171
174
  * transform function or an array of such functions. The transform function takes the http
172
- * response body and headers and returns its transformed (typically deserialized) version.
175
+ * response body, headers and status and returns its transformed (typically deserialized)
176
+ * version.
173
177
  * By default, transformResponse will contain one function that checks if the response looks
174
178
  * like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior,
175
179
  * set `transformResponse` to an empty array: `transformResponse: []`
@@ -243,9 +247,9 @@ function shallowClearAndCopy(src, dst) {
243
247
  * - non-GET instance actions: `instance.$action([parameters], [success], [error])`
244
248
  *
245
249
  *
246
- * Success callback is called with (value, responseHeaders) arguments, where the value is
247
- * the populated resource instance or collection object. The error callback is called
248
- * with (httpResponse) argument.
250
+ * Success callback is called with (value (Object|Array), responseHeaders (Function),
251
+ * status (number), statusText (string)) arguments, where the value is the populated resource
252
+ * instance or collection object. The error callback is called with (httpResponse) argument.
249
253
  *
250
254
  * Class actions return empty instance (with additional properties below).
251
255
  * Instance actions return promise of the action.
@@ -430,8 +434,9 @@ function shallowClearAndCopy(src, dst) {
430
434
  *
431
435
  */
432
436
  angular.module('ngResource', ['ng']).
433
- provider('$resource', function() {
434
- var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
437
+ provider('$resource', function ResourceProvider() {
438
+ var PROTOCOL_AND_IPV6_REGEX = /^https?:\/\/\[[^\]]*][^/]*/;
439
+
435
440
  var provider = this;
436
441
 
437
442
  /**
@@ -475,7 +480,7 @@ angular.module('ngResource', ['ng']).
475
480
  * ```js
476
481
  * angular.
477
482
  * module('myApp').
478
- * config(['resourceProvider', function ($resourceProvider) {
483
+ * config(['$resourceProvider', function ($resourceProvider) {
479
484
  * $resourceProvider.defaults.actions.update = {
480
485
  * method: 'PUT'
481
486
  * };
@@ -487,9 +492,9 @@ angular.module('ngResource', ['ng']).
487
492
  * ```js
488
493
  * angular.
489
494
  * module('myApp').
490
- * config(['resourceProvider', function ($resourceProvider) {
495
+ * config(['$resourceProvider', function ($resourceProvider) {
491
496
  * $resourceProvider.defaults.actions = {
492
- * create: {method: 'POST'}
497
+ * create: {method: 'POST'},
493
498
  * get: {method: 'GET'},
494
499
  * getAll: {method: 'GET', isArray:true},
495
500
  * update: {method: 'PUT'},
@@ -519,49 +524,15 @@ angular.module('ngResource', ['ng']).
519
524
  this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) {
520
525
 
521
526
  var noop = angular.noop,
522
- forEach = angular.forEach,
523
- extend = angular.extend,
524
- copy = angular.copy,
525
- isFunction = angular.isFunction;
526
-
527
- /**
528
- * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
529
- * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
530
- * (pchar) allowed in path segments:
531
- * segment = *pchar
532
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
533
- * pct-encoded = "%" HEXDIG HEXDIG
534
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
535
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
536
- * / "*" / "+" / "," / ";" / "="
537
- */
538
- function encodeUriSegment(val) {
539
- return encodeUriQuery(val, true).
540
- replace(/%26/gi, '&').
541
- replace(/%3D/gi, '=').
542
- replace(/%2B/gi, '+');
543
- }
544
-
545
-
546
- /**
547
- * This method is intended for encoding *key* or *value* parts of query component. We need a
548
- * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
549
- * have to be encoded per http://tools.ietf.org/html/rfc3986:
550
- * query = *( pchar / "/" / "?" )
551
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
552
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
553
- * pct-encoded = "%" HEXDIG HEXDIG
554
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
555
- * / "*" / "+" / "," / ";" / "="
556
- */
557
- function encodeUriQuery(val, pctEncodeSpaces) {
558
- return encodeURIComponent(val).
559
- replace(/%40/gi, '@').
560
- replace(/%3A/gi, ':').
561
- replace(/%24/g, '$').
562
- replace(/%2C/gi, ',').
563
- replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
564
- }
527
+ forEach = angular.forEach,
528
+ extend = angular.extend,
529
+ copy = angular.copy,
530
+ isArray = angular.isArray,
531
+ isDefined = angular.isDefined,
532
+ isFunction = angular.isFunction,
533
+ isNumber = angular.isNumber,
534
+ encodeUriQuery = angular.$$encodeUriQuery,
535
+ encodeUriSegment = angular.$$encodeUriSegment;
565
536
 
566
537
  function Route(template, defaults) {
567
538
  this.template = template;
@@ -575,42 +546,42 @@ angular.module('ngResource', ['ng']).
575
546
  url = actionUrl || self.template,
576
547
  val,
577
548
  encodedVal,
578
- protocolAndDomain = '';
549
+ protocolAndIpv6 = '';
579
550
 
580
- var urlParams = self.urlParams = {};
551
+ var urlParams = self.urlParams = Object.create(null);
581
552
  forEach(url.split(/\W/), function(param) {
582
553
  if (param === 'hasOwnProperty') {
583
- throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
554
+ throw $resourceMinErr('badname', 'hasOwnProperty is not a valid parameter name.');
584
555
  }
585
- if (!(new RegExp("^\\d+$").test(param)) && param &&
586
- (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
556
+ if (!(new RegExp('^\\d+$').test(param)) && param &&
557
+ (new RegExp('(^|[^\\\\]):' + param + '(\\W|$)').test(url))) {
587
558
  urlParams[param] = {
588
- isQueryParamValue: (new RegExp("\\?.*=:" + param + "(?:\\W|$)")).test(url)
559
+ isQueryParamValue: (new RegExp('\\?.*=:' + param + '(?:\\W|$)')).test(url)
589
560
  };
590
561
  }
591
562
  });
592
563
  url = url.replace(/\\:/g, ':');
593
- url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
594
- protocolAndDomain = match;
564
+ url = url.replace(PROTOCOL_AND_IPV6_REGEX, function(match) {
565
+ protocolAndIpv6 = match;
595
566
  return '';
596
567
  });
597
568
 
598
569
  params = params || {};
599
570
  forEach(self.urlParams, function(paramInfo, urlParam) {
600
571
  val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
601
- if (angular.isDefined(val) && val !== null) {
572
+ if (isDefined(val) && val !== null) {
602
573
  if (paramInfo.isQueryParamValue) {
603
574
  encodedVal = encodeUriQuery(val, true);
604
575
  } else {
605
576
  encodedVal = encodeUriSegment(val);
606
577
  }
607
- url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
578
+ url = url.replace(new RegExp(':' + urlParam + '(\\W|$)', 'g'), function(match, p1) {
608
579
  return encodedVal + p1;
609
580
  });
610
581
  } else {
611
- url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
582
+ url = url.replace(new RegExp('(/?):' + urlParam + '(\\W|$)', 'g'), function(match,
612
583
  leadingSlashes, tail) {
613
- if (tail.charAt(0) == '/') {
584
+ if (tail.charAt(0) === '/') {
614
585
  return tail;
615
586
  } else {
616
587
  return leadingSlashes + tail;
@@ -628,7 +599,7 @@ angular.module('ngResource', ['ng']).
628
599
  // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
629
600
  url = url.replace(/\/\.(?=\w+($|\?))/, '.');
630
601
  // replace escaped `/\.` with `/.`
631
- config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
602
+ config.url = protocolAndIpv6 + url.replace(/\/\\\./, '/.');
632
603
 
633
604
 
634
605
  // set params - delegate param encoding to $http
@@ -652,7 +623,7 @@ angular.module('ngResource', ['ng']).
652
623
  actionParams = extend({}, paramDefaults, actionParams);
653
624
  forEach(actionParams, function(value, key) {
654
625
  if (isFunction(value)) { value = value(data); }
655
- ids[key] = value && value.charAt && value.charAt(0) == '@' ?
626
+ ids[key] = value && value.charAt && value.charAt(0) === '@' ?
656
627
  lookupDottedPath(data, value.substr(1)) : value;
657
628
  });
658
629
  return ids;
@@ -676,11 +647,10 @@ angular.module('ngResource', ['ng']).
676
647
  forEach(actions, function(action, name) {
677
648
  var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
678
649
  var numericTimeout = action.timeout;
679
- var cancellable = angular.isDefined(action.cancellable) ? action.cancellable :
680
- (options && angular.isDefined(options.cancellable)) ? options.cancellable :
681
- provider.defaults.cancellable;
650
+ var cancellable = isDefined(action.cancellable) ?
651
+ action.cancellable : route.defaults.cancellable;
682
652
 
683
- if (numericTimeout && !angular.isNumber(numericTimeout)) {
653
+ if (numericTimeout && !isNumber(numericTimeout)) {
684
654
  $log.debug('ngResource:\n' +
685
655
  ' Only numeric values are allowed as `timeout`.\n' +
686
656
  ' Promises are not supported in $resource, because the same value would ' +
@@ -693,12 +663,11 @@ angular.module('ngResource', ['ng']).
693
663
  Resource[name] = function(a1, a2, a3, a4) {
694
664
  var params = {}, data, success, error;
695
665
 
696
- /* jshint -W086 */ /* (purposefully fall through case statements) */
697
666
  switch (arguments.length) {
698
667
  case 4:
699
668
  error = a4;
700
669
  success = a3;
701
- //fallthrough
670
+ // falls through
702
671
  case 3:
703
672
  case 2:
704
673
  if (isFunction(a2)) {
@@ -710,13 +679,14 @@ angular.module('ngResource', ['ng']).
710
679
 
711
680
  success = a2;
712
681
  error = a3;
713
- //fallthrough
682
+ // falls through
714
683
  } else {
715
684
  params = a1;
716
685
  data = a2;
717
686
  success = a3;
718
687
  break;
719
688
  }
689
+ // falls through
720
690
  case 1:
721
691
  if (isFunction(a1)) success = a1;
722
692
  else if (hasBody) data = a1;
@@ -725,10 +695,9 @@ angular.module('ngResource', ['ng']).
725
695
  case 0: break;
726
696
  default:
727
697
  throw $resourceMinErr('badargs',
728
- "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
698
+ 'Expected up to 4 arguments [params, data, success, error], got {0} arguments',
729
699
  arguments.length);
730
700
  }
731
- /* jshint +W086 */ /* (purposefully fall through case statements) */
732
701
 
733
702
  var isInstanceCall = this instanceof Resource;
734
703
  var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
@@ -737,6 +706,8 @@ angular.module('ngResource', ['ng']).
737
706
  defaultResponseInterceptor;
738
707
  var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
739
708
  undefined;
709
+ var hasError = !!error;
710
+ var hasResponseErrorInterceptor = !!responseErrorInterceptor;
740
711
  var timeoutDeferred;
741
712
  var numericTimeoutPromise;
742
713
 
@@ -772,18 +743,16 @@ angular.module('ngResource', ['ng']).
772
743
 
773
744
  if (data) {
774
745
  // Need to convert action.isArray to boolean in case it is undefined
775
- // jshint -W018
776
- if (angular.isArray(data) !== (!!action.isArray)) {
746
+ if (isArray(data) !== (!!action.isArray)) {
777
747
  throw $resourceMinErr('badcfg',
778
748
  'Error in resource configuration for action `{0}`. Expected response to ' +
779
749
  'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
780
- angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
750
+ isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
781
751
  }
782
- // jshint +W018
783
752
  if (action.isArray) {
784
753
  value.length = 0;
785
754
  forEach(data, function(item) {
786
- if (typeof item === "object") {
755
+ if (typeof item === 'object') {
787
756
  value.push(new Resource(item));
788
757
  } else {
789
758
  // Valid JSON values may be string literals, and these should not be converted
@@ -801,15 +770,12 @@ angular.module('ngResource', ['ng']).
801
770
  response.resource = value;
802
771
 
803
772
  return response;
804
- }, function(response) {
805
- (error || noop)(response);
806
- return $q.reject(response);
807
773
  });
808
774
 
809
- promise['finally'](function() {
775
+ promise = promise['finally'](function() {
810
776
  value.$resolved = true;
811
777
  if (!isInstanceCall && cancellable) {
812
- value.$cancelRequest = angular.noop;
778
+ value.$cancelRequest = noop;
813
779
  $timeout.cancel(numericTimeoutPromise);
814
780
  timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null;
815
781
  }
@@ -818,10 +784,22 @@ angular.module('ngResource', ['ng']).
818
784
  promise = promise.then(
819
785
  function(response) {
820
786
  var value = responseInterceptor(response);
821
- (success || noop)(value, response.headers);
787
+ (success || noop)(value, response.headers, response.status, response.statusText);
822
788
  return value;
823
789
  },
824
- responseErrorInterceptor);
790
+ (hasError || hasResponseErrorInterceptor) ?
791
+ function(response) {
792
+ if (hasError) error(response);
793
+ return hasResponseErrorInterceptor ?
794
+ responseErrorInterceptor(response) :
795
+ $q.reject(response);
796
+ } :
797
+ undefined);
798
+ if (hasError && !hasResponseErrorInterceptor) {
799
+ // Avoid `Possibly Unhandled Rejection` error,
800
+ // but still fulfill the returned promise with a rejection
801
+ promise.catch(noop);
802
+ }
825
803
 
826
804
  if (!isInstanceCall) {
827
805
  // we are creating instance / collection
@@ -829,13 +807,18 @@ angular.module('ngResource', ['ng']).
829
807
  // - return the instance / collection
830
808
  value.$promise = promise;
831
809
  value.$resolved = false;
832
- if (cancellable) value.$cancelRequest = timeoutDeferred.resolve;
810
+ if (cancellable) value.$cancelRequest = cancelRequest;
833
811
 
834
812
  return value;
835
813
  }
836
814
 
837
815
  // instance call
838
816
  return promise;
817
+
818
+ function cancelRequest(value) {
819
+ promise.catch(noop);
820
+ timeoutDeferred.resolve(value);
821
+ }
839
822
  };
840
823
 
841
824
 
@@ -849,7 +832,8 @@ angular.module('ngResource', ['ng']).
849
832
  });
850
833
 
851
834
  Resource.bind = function(additionalParamDefaults) {
852
- return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
835
+ var extendedParamDefaults = extend({}, paramDefaults, additionalParamDefaults);
836
+ return resourceFactory(url, extendedParamDefaults, actions, options);
853
837
  };
854
838
 
855
839
  return Resource;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.8
2
+ * @license AngularJS v1.6.0
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -34,10 +34,11 @@ function shallowCopy(src, dst) {
34
34
 
35
35
  /* global shallowCopy: false */
36
36
 
37
- // There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
37
+ // `isArray` and `isObject` are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
38
38
  // They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
39
39
  var isArray;
40
40
  var isObject;
41
+ var isDefined;
41
42
 
42
43
  /**
43
44
  * @ngdoc module
@@ -54,14 +55,22 @@ var isObject;
54
55
  *
55
56
  * <div doc-module-components="ngRoute"></div>
56
57
  */
57
- /* global -ngRouteModule */
58
- var ngRouteModule = angular.module('ngRoute', ['ng']).
59
- provider('$route', $RouteProvider),
60
- $routeMinErr = angular.$$minErr('ngRoute');
58
+ /* global -ngRouteModule */
59
+ var ngRouteModule = angular.
60
+ module('ngRoute', []).
61
+ provider('$route', $RouteProvider).
62
+ // Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess`
63
+ // event (unless explicitly disabled). This is necessary in case `ngView` is included in an
64
+ // asynchronously loaded template.
65
+ run(instantiateRoute);
66
+ var $routeMinErr = angular.$$minErr('ngRoute');
67
+ var isEagerInstantiationEnabled;
68
+
61
69
 
62
70
  /**
63
71
  * @ngdoc provider
64
72
  * @name $routeProvider
73
+ * @this
65
74
  *
66
75
  * @description
67
76
  *
@@ -76,6 +85,7 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
76
85
  function $RouteProvider() {
77
86
  isArray = angular.isArray;
78
87
  isObject = angular.isObject;
88
+ isDefined = angular.isDefined;
79
89
 
80
90
  function inherit(parent, extra) {
81
91
  return angular.extend(Object.create(parent), extra);
@@ -112,12 +122,12 @@ function $RouteProvider() {
112
122
  *
113
123
  * Object properties:
114
124
  *
115
- * - `controller` – `{(string|function()=}` – Controller fn that should be associated with
125
+ * - `controller` – `{(string|Function)=}` – Controller fn that should be associated with
116
126
  * newly created scope or the name of a {@link angular.Module#controller registered
117
127
  * controller} if passed as a string.
118
128
  * - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
119
129
  * If present, the controller will be published to scope under the `controllerAs` name.
120
- * - `template` – `{string=|function()=}` – html template as a string or a function that
130
+ * - `template` – `{(string|Function)=}` – html template as a string or a function that
121
131
  * returns an html template as a string which should be used by {@link
122
132
  * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
123
133
  * This property takes precedence over `templateUrl`.
@@ -127,7 +137,9 @@ function $RouteProvider() {
127
137
  * - `{Array.<Object>}` - route parameters extracted from the current
128
138
  * `$location.path()` by applying the current route
129
139
  *
130
- * - `templateUrl` `{string=|function()=}` path or function that returns a path to an html
140
+ * One of `template` or `templateUrl` is required.
141
+ *
142
+ * - `templateUrl` – `{(string|Function)=}` – path or function that returns a path to an html
131
143
  * template that should be used by {@link ngRoute.directive:ngView ngView}.
132
144
  *
133
145
  * If `templateUrl` is a function, it will be called with the following parameters:
@@ -135,7 +147,9 @@ function $RouteProvider() {
135
147
  * - `{Array.<Object>}` - route parameters extracted from the current
136
148
  * `$location.path()` by applying the current route
137
149
  *
138
- * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
150
+ * One of `templateUrl` or `template` is required.
151
+ *
152
+ * - `resolve` - `{Object.<string, Function>=}` - An optional map of dependencies which should
139
153
  * be injected into the controller. If any of these dependencies are promises, the router
140
154
  * will wait for them all to be resolved or one to be rejected before the controller is
141
155
  * instantiated.
@@ -155,7 +169,7 @@ function $RouteProvider() {
155
169
  * The map object is:
156
170
  *
157
171
  * - `key` – `{string}`: a name of a dependency to be injected into the controller.
158
- * - `factory` - `{string|function}`: If `string` then it is an alias for a service.
172
+ * - `factory` - `{string|Function}`: If `string` then it is an alias for a service.
159
173
  * Otherwise if function, then it is {@link auto.$injector#invoke injected}
160
174
  * and the return value is treated as the dependency. If the result is a promise, it is
161
175
  * resolved before its value is injected into the controller. Be aware that
@@ -165,7 +179,7 @@ function $RouteProvider() {
165
179
  * - `resolveAs` - `{string=}` - The name under which the `resolve` map will be available on
166
180
  * the scope of the route. If omitted, defaults to `$resolve`.
167
181
  *
168
- * - `redirectTo` – `{(string|function())=}` – value to update
182
+ * - `redirectTo` – `{(string|Function)=}` – value to update
169
183
  * {@link ng.$location $location} path with and trigger route redirection.
170
184
  *
171
185
  * If `redirectTo` is a function, it will be called with the following parameters:
@@ -176,7 +190,31 @@ function $RouteProvider() {
176
190
  * - `{Object}` - current `$location.search()`
177
191
  *
178
192
  * The custom `redirectTo` function is expected to return a string which will be used
179
- * to update `$location.path()` and `$location.search()`.
193
+ * to update `$location.url()`. If the function throws an error, no further processing will
194
+ * take place and the {@link ngRoute.$route#$routeChangeError $routeChangeError} event will
195
+ * be fired.
196
+ *
197
+ * Routes that specify `redirectTo` will not have their controllers, template functions
198
+ * or resolves called, the `$location` will be changed to the redirect url and route
199
+ * processing will stop. The exception to this is if the `redirectTo` is a function that
200
+ * returns `undefined`. In this case the route transition occurs as though there was no
201
+ * redirection.
202
+ *
203
+ * - `resolveRedirectTo` – `{Function=}` – a function that will (eventually) return the value
204
+ * to update {@link ng.$location $location} URL with and trigger route redirection. In
205
+ * contrast to `redirectTo`, dependencies can be injected into `resolveRedirectTo` and the
206
+ * return value can be either a string or a promise that will be resolved to a string.
207
+ *
208
+ * Similar to `redirectTo`, if the return value is `undefined` (or a promise that gets
209
+ * resolved to `undefined`), no redirection takes place and the route transition occurs as
210
+ * though there was no redirection.
211
+ *
212
+ * If the function throws an error or the returned promise gets rejected, no further
213
+ * processing will take place and the
214
+ * {@link ngRoute.$route#$routeChangeError $routeChangeError} event will be fired.
215
+ *
216
+ * `redirectTo` takes precedence over `resolveRedirectTo`, so specifying both on the same
217
+ * route definition, will cause the latter to be ignored.
180
218
  *
181
219
  * - `[reloadOnSearch=true]` - `{boolean=}` - reload route when only `$location.search()`
182
220
  * or `$location.hash()` changes.
@@ -210,7 +248,7 @@ function $RouteProvider() {
210
248
 
211
249
  // create redirection for trailing slashes
212
250
  if (path) {
213
- var redirectPath = (path[path.length - 1] == '/')
251
+ var redirectPath = (path[path.length - 1] === '/')
214
252
  ? path.substr(0, path.length - 1)
215
253
  : path + '/';
216
254
 
@@ -255,7 +293,7 @@ function $RouteProvider() {
255
293
 
256
294
  path = path
257
295
  .replace(/([().])/g, '\\$1')
258
- .replace(/(\/)?:(\w+)(\*\?|[\?\*])?/g, function(_, slash, key, option) {
296
+ .replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) {
259
297
  var optional = (option === '?' || option === '*?') ? '?' : null;
260
298
  var star = (option === '*' || option === '*?') ? '*' : null;
261
299
  keys.push({ name: key, optional: !!optional });
@@ -269,7 +307,7 @@ function $RouteProvider() {
269
307
  + ')'
270
308
  + (optional || '');
271
309
  })
272
- .replace(/([\/$\*])/g, '\\$1');
310
+ .replace(/([/$*])/g, '\\$1');
273
311
 
274
312
  ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
275
313
  return ret;
@@ -295,6 +333,47 @@ function $RouteProvider() {
295
333
  return this;
296
334
  };
297
335
 
336
+ /**
337
+ * @ngdoc method
338
+ * @name $routeProvider#eagerInstantiationEnabled
339
+ * @kind function
340
+ *
341
+ * @description
342
+ * Call this method as a setter to enable/disable eager instantiation of the
343
+ * {@link ngRoute.$route $route} service upon application bootstrap. You can also call it as a
344
+ * getter (i.e. without any arguments) to get the current value of the
345
+ * `eagerInstantiationEnabled` flag.
346
+ *
347
+ * Instantiating `$route` early is necessary for capturing the initial
348
+ * {@link ng.$location#$locationChangeStart $locationChangeStart} event and navigating to the
349
+ * appropriate route. Usually, `$route` is instantiated in time by the
350
+ * {@link ngRoute.ngView ngView} directive. Yet, in cases where `ngView` is included in an
351
+ * asynchronously loaded template (e.g. in another directive's template), the directive factory
352
+ * might not be called soon enough for `$route` to be instantiated _before_ the initial
353
+ * `$locationChangeSuccess` event is fired. Eager instantiation ensures that `$route` is always
354
+ * instantiated in time, regardless of when `ngView` will be loaded.
355
+ *
356
+ * The default value is true.
357
+ *
358
+ * **Note**:<br />
359
+ * You may want to disable the default behavior when unit-testing modules that depend on
360
+ * `ngRoute`, in order to avoid an unexpected request for the default route's template.
361
+ *
362
+ * @param {boolean=} enabled - If provided, update the internal `eagerInstantiationEnabled` flag.
363
+ *
364
+ * @returns {*} The current value of the `eagerInstantiationEnabled` flag if used as a getter or
365
+ * itself (for chaining) if used as a setter.
366
+ */
367
+ isEagerInstantiationEnabled = true;
368
+ this.eagerInstantiationEnabled = function eagerInstantiationEnabled(enabled) {
369
+ if (isDefined(enabled)) {
370
+ isEagerInstantiationEnabled = enabled;
371
+ return this;
372
+ }
373
+
374
+ return isEagerInstantiationEnabled;
375
+ };
376
+
298
377
 
299
378
  this.$get = ['$rootScope',
300
379
  '$location',
@@ -388,12 +467,12 @@ function $RouteProvider() {
388
467
  * })
389
468
  *
390
469
  * .controller('BookController', function($scope, $routeParams) {
391
- * $scope.name = "BookController";
470
+ * $scope.name = 'BookController';
392
471
  * $scope.params = $routeParams;
393
472
  * })
394
473
  *
395
474
  * .controller('ChapterController', function($scope, $routeParams) {
396
- * $scope.name = "ChapterController";
475
+ * $scope.name = 'ChapterController';
397
476
  * $scope.params = $routeParams;
398
477
  * })
399
478
  *
@@ -426,15 +505,15 @@ function $RouteProvider() {
426
505
  * it('should load and compile correct template', function() {
427
506
  * element(by.linkText('Moby: Ch1')).click();
428
507
  * var content = element(by.css('[ng-view]')).getText();
429
- * expect(content).toMatch(/controller\: ChapterController/);
430
- * expect(content).toMatch(/Book Id\: Moby/);
431
- * expect(content).toMatch(/Chapter Id\: 1/);
508
+ * expect(content).toMatch(/controller: ChapterController/);
509
+ * expect(content).toMatch(/Book Id: Moby/);
510
+ * expect(content).toMatch(/Chapter Id: 1/);
432
511
  *
433
512
  * element(by.partialLinkText('Scarlet')).click();
434
513
  *
435
514
  * content = element(by.css('[ng-view]')).getText();
436
- * expect(content).toMatch(/controller\: BookController/);
437
- * expect(content).toMatch(/Book Id\: Scarlet/);
515
+ * expect(content).toMatch(/controller: BookController/);
516
+ * expect(content).toMatch(/Book Id: Scarlet/);
438
517
  * });
439
518
  * </file>
440
519
  * </example>
@@ -482,12 +561,14 @@ function $RouteProvider() {
482
561
  * @name $route#$routeChangeError
483
562
  * @eventType broadcast on root scope
484
563
  * @description
485
- * Broadcasted if any of the resolve promises are rejected.
564
+ * Broadcasted if a redirection function fails or any redirection or resolve promises are
565
+ * rejected.
486
566
  *
487
567
  * @param {Object} angularEvent Synthetic event object
488
568
  * @param {Route} current Current route information.
489
569
  * @param {Route} previous Previous route information.
490
- * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
570
+ * @param {Route} rejection The thrown error or the rejection reason of the promise. Usually
571
+ * the rejection reason is the error that caused the promise to get rejected.
491
572
  */
492
573
 
493
574
  /**
@@ -628,37 +709,103 @@ function $RouteProvider() {
628
709
  } else if (nextRoute || lastRoute) {
629
710
  forceReload = false;
630
711
  $route.current = nextRoute;
631
- if (nextRoute) {
632
- if (nextRoute.redirectTo) {
633
- if (angular.isString(nextRoute.redirectTo)) {
634
- $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
635
- .replace();
636
- } else {
637
- $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
638
- .replace();
639
- }
640
- }
641
- }
642
712
 
643
- $q.when(nextRoute).
644
- then(resolveLocals).
645
- then(function(locals) {
646
- // after route change
647
- if (nextRoute == $route.current) {
648
- if (nextRoute) {
649
- nextRoute.locals = locals;
650
- angular.copy(nextRoute.params, $routeParams);
651
- }
652
- $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
653
- }
654
- }, function(error) {
655
- if (nextRoute == $route.current) {
713
+ var nextRoutePromise = $q.resolve(nextRoute);
714
+
715
+ nextRoutePromise.
716
+ then(getRedirectionData).
717
+ then(handlePossibleRedirection).
718
+ then(function(keepProcessingRoute) {
719
+ return keepProcessingRoute && nextRoutePromise.
720
+ then(resolveLocals).
721
+ then(function(locals) {
722
+ // after route change
723
+ if (nextRoute === $route.current) {
724
+ if (nextRoute) {
725
+ nextRoute.locals = locals;
726
+ angular.copy(nextRoute.params, $routeParams);
727
+ }
728
+ $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
729
+ }
730
+ });
731
+ }).catch(function(error) {
732
+ if (nextRoute === $route.current) {
656
733
  $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
657
734
  }
658
735
  });
659
736
  }
660
737
  }
661
738
 
739
+ function getRedirectionData(route) {
740
+ var data = {
741
+ route: route,
742
+ hasRedirection: false
743
+ };
744
+
745
+ if (route) {
746
+ if (route.redirectTo) {
747
+ if (angular.isString(route.redirectTo)) {
748
+ data.path = interpolate(route.redirectTo, route.params);
749
+ data.search = route.params;
750
+ data.hasRedirection = true;
751
+ } else {
752
+ var oldPath = $location.path();
753
+ var oldSearch = $location.search();
754
+ var newUrl = route.redirectTo(route.pathParams, oldPath, oldSearch);
755
+
756
+ if (angular.isDefined(newUrl)) {
757
+ data.url = newUrl;
758
+ data.hasRedirection = true;
759
+ }
760
+ }
761
+ } else if (route.resolveRedirectTo) {
762
+ return $q.
763
+ resolve($injector.invoke(route.resolveRedirectTo)).
764
+ then(function(newUrl) {
765
+ if (angular.isDefined(newUrl)) {
766
+ data.url = newUrl;
767
+ data.hasRedirection = true;
768
+ }
769
+
770
+ return data;
771
+ });
772
+ }
773
+ }
774
+
775
+ return data;
776
+ }
777
+
778
+ function handlePossibleRedirection(data) {
779
+ var keepProcessingRoute = true;
780
+
781
+ if (data.route !== $route.current) {
782
+ keepProcessingRoute = false;
783
+ } else if (data.hasRedirection) {
784
+ var oldUrl = $location.url();
785
+ var newUrl = data.url;
786
+
787
+ if (newUrl) {
788
+ $location.
789
+ url(newUrl).
790
+ replace();
791
+ } else {
792
+ newUrl = $location.
793
+ path(data.path).
794
+ search(data.search).
795
+ replace().
796
+ url();
797
+ }
798
+
799
+ if (newUrl !== oldUrl) {
800
+ // Exit out and don't process current next value,
801
+ // wait for next location change from redirect
802
+ keepProcessingRoute = false;
803
+ }
804
+ }
805
+
806
+ return keepProcessingRoute;
807
+ }
808
+
662
809
  function resolveLocals(route) {
663
810
  if (route) {
664
811
  var locals = angular.extend({}, route.resolve);
@@ -675,7 +822,6 @@ function $RouteProvider() {
675
822
  }
676
823
  }
677
824
 
678
-
679
825
  function getTemplateFor(route) {
680
826
  var template, templateUrl;
681
827
  if (angular.isDefined(template = route.template)) {
@@ -694,7 +840,6 @@ function $RouteProvider() {
694
840
  return template;
695
841
  }
696
842
 
697
-
698
843
  /**
699
844
  * @returns {Object} the current active route, by matching it against the URL
700
845
  */
@@ -734,6 +879,14 @@ function $RouteProvider() {
734
879
  }];
735
880
  }
736
881
 
882
+ instantiateRoute.$inject = ['$injector'];
883
+ function instantiateRoute($injector) {
884
+ if (isEagerInstantiationEnabled) {
885
+ // Instantiate `$route`
886
+ $injector.get('$route');
887
+ }
888
+ }
889
+
737
890
  ngRouteModule.provider('$routeParams', $RouteParamsProvider);
738
891
 
739
892
 
@@ -741,6 +894,7 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
741
894
  * @ngdoc service
742
895
  * @name $routeParams
743
896
  * @requires $route
897
+ * @this
744
898
  *
745
899
  * @description
746
900
  * The `$routeParams` service allows you to retrieve the current set of route parameters.
@@ -800,13 +954,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
800
954
  *
801
955
  * The enter and leave animation occur concurrently.
802
956
  *
803
- * @knownIssue If `ngView` is contained in an asynchronously loaded template (e.g. in another
804
- * directive's templateUrl or in a template loaded using `ngInclude`), then you need to
805
- * make sure that `$route` is instantiated in time to capture the initial
806
- * `$locationChangeStart` event and load the appropriate view. One way to achieve this
807
- * is to have it as a dependency in a `.run` block:
808
- * `myModule.run(['$route', function() {}]);`
809
- *
810
957
  * @scope
811
958
  * @priority 400
812
959
  * @param {string=} onload Expression to evaluate whenever the view updates.
@@ -917,17 +1064,17 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
917
1064
  $locationProvider.html5Mode(true);
918
1065
  }])
919
1066
  .controller('MainCtrl', ['$route', '$routeParams', '$location',
920
- function($route, $routeParams, $location) {
1067
+ function MainCtrl($route, $routeParams, $location) {
921
1068
  this.$route = $route;
922
1069
  this.$location = $location;
923
1070
  this.$routeParams = $routeParams;
924
1071
  }])
925
- .controller('BookCtrl', ['$routeParams', function($routeParams) {
926
- this.name = "BookCtrl";
1072
+ .controller('BookCtrl', ['$routeParams', function BookCtrl($routeParams) {
1073
+ this.name = 'BookCtrl';
927
1074
  this.params = $routeParams;
928
1075
  }])
929
- .controller('ChapterCtrl', ['$routeParams', function($routeParams) {
930
- this.name = "ChapterCtrl";
1076
+ .controller('ChapterCtrl', ['$routeParams', function ChapterCtrl($routeParams) {
1077
+ this.name = 'ChapterCtrl';
931
1078
  this.params = $routeParams;
932
1079
  }]);
933
1080
 
@@ -937,15 +1084,15 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
937
1084
  it('should load and compile correct template', function() {
938
1085
  element(by.linkText('Moby: Ch1')).click();
939
1086
  var content = element(by.css('[ng-view]')).getText();
940
- expect(content).toMatch(/controller\: ChapterCtrl/);
941
- expect(content).toMatch(/Book Id\: Moby/);
942
- expect(content).toMatch(/Chapter Id\: 1/);
1087
+ expect(content).toMatch(/controller: ChapterCtrl/);
1088
+ expect(content).toMatch(/Book Id: Moby/);
1089
+ expect(content).toMatch(/Chapter Id: 1/);
943
1090
 
944
1091
  element(by.partialLinkText('Scarlet')).click();
945
1092
 
946
1093
  content = element(by.css('[ng-view]')).getText();
947
- expect(content).toMatch(/controller\: BookCtrl/);
948
- expect(content).toMatch(/Book Id\: Scarlet/);
1094
+ expect(content).toMatch(/controller: BookCtrl/);
1095
+ expect(content).toMatch(/Book Id: Scarlet/);
949
1096
  });
950
1097
  </file>
951
1098
  </example>
@@ -988,8 +1135,8 @@ function ngViewFactory($route, $anchorScroll, $animate) {
988
1135
  }
989
1136
  if (currentElement) {
990
1137
  previousLeaveAnimation = $animate.leave(currentElement);
991
- previousLeaveAnimation.then(function() {
992
- previousLeaveAnimation = null;
1138
+ previousLeaveAnimation.done(function(response) {
1139
+ if (response !== false) previousLeaveAnimation = null;
993
1140
  });
994
1141
  currentElement = null;
995
1142
  }
@@ -1010,8 +1157,8 @@ function ngViewFactory($route, $anchorScroll, $animate) {
1010
1157
  // function is called before linking the content, which would apply child
1011
1158
  // directives to non existing elements.
1012
1159
  var clone = $transclude(newScope, function(clone) {
1013
- $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
1014
- if (angular.isDefined(autoScrollExp)
1160
+ $animate.enter(clone, null, currentElement || $element).done(function onNgViewEnter(response) {
1161
+ if (response !== false && angular.isDefined(autoScrollExp)
1015
1162
  && (!autoScrollExp || scope.$eval(autoScrollExp))) {
1016
1163
  $anchorScroll();
1017
1164
  }