angularjs-rails 1.5.8 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }