angularjs-rails 1.5.8 → 1.8.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,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.5.8
3
- * (c) 2010-2016 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.8.0
3
+ * (c) 2010-2020 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular) {'use strict';
@@ -32,43 +32,96 @@ function shallowCopy(src, dst) {
32
32
  return dst || src;
33
33
  }
34
34
 
35
+ /* global routeToRegExp: true */
36
+
37
+ /**
38
+ * @param {string} path - The path to parse. (It is assumed to have query and hash stripped off.)
39
+ * @param {Object} opts - Options.
40
+ * @return {Object} - An object containing an array of path parameter names (`keys`) and a regular
41
+ * expression (`regexp`) that can be used to identify a matching URL and extract the path
42
+ * parameter values.
43
+ *
44
+ * @description
45
+ * Parses the given path, extracting path parameter names and a regular expression to match URLs.
46
+ *
47
+ * Originally inspired by `pathRexp` in `visionmedia/express/lib/utils.js`.
48
+ */
49
+ function routeToRegExp(path, opts) {
50
+ var keys = [];
51
+
52
+ var pattern = path
53
+ .replace(/([().])/g, '\\$1')
54
+ .replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) {
55
+ var optional = option === '?' || option === '*?';
56
+ var star = option === '*' || option === '*?';
57
+ keys.push({name: key, optional: optional});
58
+ slash = slash || '';
59
+ return (
60
+ (optional ? '(?:' + slash : slash + '(?:') +
61
+ (star ? '(.+?)' : '([^/]+)') +
62
+ (optional ? '?)?' : ')')
63
+ );
64
+ })
65
+ .replace(/([/$*])/g, '\\$1');
66
+
67
+ if (opts.ignoreTrailingSlashes) {
68
+ pattern = pattern.replace(/\/+$/, '') + '/*';
69
+ }
70
+
71
+ return {
72
+ keys: keys,
73
+ regexp: new RegExp(
74
+ '^' + pattern + '(?:[?#]|$)',
75
+ opts.caseInsensitiveMatch ? 'i' : ''
76
+ )
77
+ };
78
+ }
79
+
80
+ /* global routeToRegExp: false */
35
81
  /* global shallowCopy: false */
36
82
 
37
- // There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
83
+ // `isArray` and `isObject` are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
38
84
  // They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
39
85
  var isArray;
40
86
  var isObject;
87
+ var isDefined;
88
+ var noop;
41
89
 
42
90
  /**
43
91
  * @ngdoc module
44
92
  * @name ngRoute
45
93
  * @description
46
94
  *
47
- * # ngRoute
48
- *
49
- * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
95
+ * The `ngRoute` module provides routing and deeplinking services and directives for AngularJS apps.
50
96
  *
51
97
  * ## Example
52
- * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
98
+ * See {@link ngRoute.$route#examples $route} for an example of configuring and using `ngRoute`.
53
99
  *
54
- *
55
- * <div doc-module-components="ngRoute"></div>
56
100
  */
57
- /* global -ngRouteModule */
58
- var ngRouteModule = angular.module('ngRoute', ['ng']).
59
- provider('$route', $RouteProvider),
60
- $routeMinErr = angular.$$minErr('ngRoute');
101
+ /* global -ngRouteModule */
102
+ var ngRouteModule = angular.
103
+ module('ngRoute', []).
104
+ info({ angularVersion: '1.8.0' }).
105
+ provider('$route', $RouteProvider).
106
+ // Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess`
107
+ // event (unless explicitly disabled). This is necessary in case `ngView` is included in an
108
+ // asynchronously loaded template.
109
+ run(instantiateRoute);
110
+ var $routeMinErr = angular.$$minErr('ngRoute');
111
+ var isEagerInstantiationEnabled;
112
+
61
113
 
62
114
  /**
63
115
  * @ngdoc provider
64
116
  * @name $routeProvider
117
+ * @this
65
118
  *
66
119
  * @description
67
120
  *
68
121
  * Used for configuring routes.
69
122
  *
70
123
  * ## Example
71
- * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
124
+ * See {@link ngRoute.$route#examples $route} for an example of configuring and using `ngRoute`.
72
125
  *
73
126
  * ## Dependencies
74
127
  * Requires the {@link ngRoute `ngRoute`} module to be installed.
@@ -76,6 +129,8 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
76
129
  function $RouteProvider() {
77
130
  isArray = angular.isArray;
78
131
  isObject = angular.isObject;
132
+ isDefined = angular.isDefined;
133
+ noop = angular.noop;
79
134
 
80
135
  function inherit(parent, extra) {
81
136
  return angular.extend(Object.create(parent), extra);
@@ -112,12 +167,12 @@ function $RouteProvider() {
112
167
  *
113
168
  * Object properties:
114
169
  *
115
- * - `controller` – `{(string|function()=}` – Controller fn that should be associated with
170
+ * - `controller` – `{(string|Function)=}` – Controller fn that should be associated with
116
171
  * newly created scope or the name of a {@link angular.Module#controller registered
117
172
  * controller} if passed as a string.
118
173
  * - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
119
174
  * 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
175
+ * - `template` – `{(string|Function)=}` – html template as a string or a function that
121
176
  * returns an html template as a string which should be used by {@link
122
177
  * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
123
178
  * This property takes precedence over `templateUrl`.
@@ -127,7 +182,9 @@ function $RouteProvider() {
127
182
  * - `{Array.<Object>}` - route parameters extracted from the current
128
183
  * `$location.path()` by applying the current route
129
184
  *
130
- * - `templateUrl` `{string=|function()=}` path or function that returns a path to an html
185
+ * One of `template` or `templateUrl` is required.
186
+ *
187
+ * - `templateUrl` – `{(string|Function)=}` – path or function that returns a path to an html
131
188
  * template that should be used by {@link ngRoute.directive:ngView ngView}.
132
189
  *
133
190
  * If `templateUrl` is a function, it will be called with the following parameters:
@@ -135,7 +192,9 @@ function $RouteProvider() {
135
192
  * - `{Array.<Object>}` - route parameters extracted from the current
136
193
  * `$location.path()` by applying the current route
137
194
  *
138
- * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
195
+ * One of `templateUrl` or `template` is required.
196
+ *
197
+ * - `resolve` - `{Object.<string, Function>=}` - An optional map of dependencies which should
139
198
  * be injected into the controller. If any of these dependencies are promises, the router
140
199
  * will wait for them all to be resolved or one to be rejected before the controller is
141
200
  * instantiated.
@@ -155,7 +214,7 @@ function $RouteProvider() {
155
214
  * The map object is:
156
215
  *
157
216
  * - `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.
217
+ * - `factory` - `{string|Function}`: If `string` then it is an alias for a service.
159
218
  * Otherwise if function, then it is {@link auto.$injector#invoke injected}
160
219
  * and the return value is treated as the dependency. If the result is a promise, it is
161
220
  * resolved before its value is injected into the controller. Be aware that
@@ -165,7 +224,7 @@ function $RouteProvider() {
165
224
  * - `resolveAs` - `{string=}` - The name under which the `resolve` map will be available on
166
225
  * the scope of the route. If omitted, defaults to `$resolve`.
167
226
  *
168
- * - `redirectTo` – `{(string|function())=}` – value to update
227
+ * - `redirectTo` – `{(string|Function)=}` – value to update
169
228
  * {@link ng.$location $location} path with and trigger route redirection.
170
229
  *
171
230
  * If `redirectTo` is a function, it will be called with the following parameters:
@@ -176,13 +235,48 @@ function $RouteProvider() {
176
235
  * - `{Object}` - current `$location.search()`
177
236
  *
178
237
  * The custom `redirectTo` function is expected to return a string which will be used
179
- * to update `$location.path()` and `$location.search()`.
238
+ * to update `$location.url()`. If the function throws an error, no further processing will
239
+ * take place and the {@link ngRoute.$route#$routeChangeError $routeChangeError} event will
240
+ * be fired.
241
+ *
242
+ * Routes that specify `redirectTo` will not have their controllers, template functions
243
+ * or resolves called, the `$location` will be changed to the redirect url and route
244
+ * processing will stop. The exception to this is if the `redirectTo` is a function that
245
+ * returns `undefined`. In this case the route transition occurs as though there was no
246
+ * redirection.
247
+ *
248
+ * - `resolveRedirectTo` – `{Function=}` – a function that will (eventually) return the value
249
+ * to update {@link ng.$location $location} URL with and trigger route redirection. In
250
+ * contrast to `redirectTo`, dependencies can be injected into `resolveRedirectTo` and the
251
+ * return value can be either a string or a promise that will be resolved to a string.
252
+ *
253
+ * Similar to `redirectTo`, if the return value is `undefined` (or a promise that gets
254
+ * resolved to `undefined`), no redirection takes place and the route transition occurs as
255
+ * though there was no redirection.
256
+ *
257
+ * If the function throws an error or the returned promise gets rejected, no further
258
+ * processing will take place and the
259
+ * {@link ngRoute.$route#$routeChangeError $routeChangeError} event will be fired.
260
+ *
261
+ * `redirectTo` takes precedence over `resolveRedirectTo`, so specifying both on the same
262
+ * route definition, will cause the latter to be ignored.
263
+ *
264
+ * - `[reloadOnUrl=true]` - `{boolean=}` - reload route when any part of the URL changes
265
+ * (including the path) even if the new URL maps to the same route.
266
+ *
267
+ * If the option is set to `false` and the URL in the browser changes, but the new URL maps
268
+ * to the same route, then a `$routeUpdate` event is broadcasted on the root scope (without
269
+ * reloading the route).
180
270
  *
181
271
  * - `[reloadOnSearch=true]` - `{boolean=}` - reload route when only `$location.search()`
182
272
  * or `$location.hash()` changes.
183
273
  *
184
- * If the option is set to `false` and url in the browser changes, then
185
- * `$routeUpdate` event is broadcasted on the root scope.
274
+ * If the option is set to `false` and the URL in the browser changes, then a `$routeUpdate`
275
+ * event is broadcasted on the root scope (without reloading the route).
276
+ *
277
+ * <div class="alert alert-warning">
278
+ * **Note:** This option has no effect if `reloadOnUrl` is set to `false`.
279
+ * </div>
186
280
  *
187
281
  * - `[caseInsensitiveMatch=false]` - `{boolean=}` - match routes without being case sensitive
188
282
  *
@@ -197,6 +291,9 @@ function $RouteProvider() {
197
291
  this.when = function(path, route) {
198
292
  //copy original route object to preserve params inherited from proto chain
199
293
  var routeCopy = shallowCopy(route);
294
+ if (angular.isUndefined(routeCopy.reloadOnUrl)) {
295
+ routeCopy.reloadOnUrl = true;
296
+ }
200
297
  if (angular.isUndefined(routeCopy.reloadOnSearch)) {
201
298
  routeCopy.reloadOnSearch = true;
202
299
  }
@@ -205,18 +302,19 @@ function $RouteProvider() {
205
302
  }
206
303
  routes[path] = angular.extend(
207
304
  routeCopy,
208
- path && pathRegExp(path, routeCopy)
305
+ {originalPath: path},
306
+ path && routeToRegExp(path, routeCopy)
209
307
  );
210
308
 
211
309
  // create redirection for trailing slashes
212
310
  if (path) {
213
- var redirectPath = (path[path.length - 1] == '/')
311
+ var redirectPath = (path[path.length - 1] === '/')
214
312
  ? path.substr(0, path.length - 1)
215
313
  : path + '/';
216
314
 
217
315
  routes[redirectPath] = angular.extend(
218
- {redirectTo: path},
219
- pathRegExp(redirectPath, routeCopy)
316
+ {originalPath: path, redirectTo: path},
317
+ routeToRegExp(redirectPath, routeCopy)
220
318
  );
221
319
  }
222
320
 
@@ -234,47 +332,6 @@ function $RouteProvider() {
234
332
  */
235
333
  this.caseInsensitiveMatch = false;
236
334
 
237
- /**
238
- * @param path {string} path
239
- * @param opts {Object} options
240
- * @return {?Object}
241
- *
242
- * @description
243
- * Normalizes the given path, returning a regular expression
244
- * and the original path.
245
- *
246
- * Inspired by pathRexp in visionmedia/express/lib/utils.js.
247
- */
248
- function pathRegExp(path, opts) {
249
- var insensitive = opts.caseInsensitiveMatch,
250
- ret = {
251
- originalPath: path,
252
- regexp: path
253
- },
254
- keys = ret.keys = [];
255
-
256
- path = path
257
- .replace(/([().])/g, '\\$1')
258
- .replace(/(\/)?:(\w+)(\*\?|[\?\*])?/g, function(_, slash, key, option) {
259
- var optional = (option === '?' || option === '*?') ? '?' : null;
260
- var star = (option === '*' || option === '*?') ? '*' : null;
261
- keys.push({ name: key, optional: !!optional });
262
- slash = slash || '';
263
- return ''
264
- + (optional ? '' : slash)
265
- + '(?:'
266
- + (optional ? slash : '')
267
- + (star && '(.+?)' || '([^/]+)')
268
- + (optional || '')
269
- + ')'
270
- + (optional || '');
271
- })
272
- .replace(/([\/$\*])/g, '\\$1');
273
-
274
- ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
275
- return ret;
276
- }
277
-
278
335
  /**
279
336
  * @ngdoc method
280
337
  * @name $routeProvider#otherwise
@@ -295,6 +352,47 @@ function $RouteProvider() {
295
352
  return this;
296
353
  };
297
354
 
355
+ /**
356
+ * @ngdoc method
357
+ * @name $routeProvider#eagerInstantiationEnabled
358
+ * @kind function
359
+ *
360
+ * @description
361
+ * Call this method as a setter to enable/disable eager instantiation of the
362
+ * {@link ngRoute.$route $route} service upon application bootstrap. You can also call it as a
363
+ * getter (i.e. without any arguments) to get the current value of the
364
+ * `eagerInstantiationEnabled` flag.
365
+ *
366
+ * Instantiating `$route` early is necessary for capturing the initial
367
+ * {@link ng.$location#$locationChangeStart $locationChangeStart} event and navigating to the
368
+ * appropriate route. Usually, `$route` is instantiated in time by the
369
+ * {@link ngRoute.ngView ngView} directive. Yet, in cases where `ngView` is included in an
370
+ * asynchronously loaded template (e.g. in another directive's template), the directive factory
371
+ * might not be called soon enough for `$route` to be instantiated _before_ the initial
372
+ * `$locationChangeSuccess` event is fired. Eager instantiation ensures that `$route` is always
373
+ * instantiated in time, regardless of when `ngView` will be loaded.
374
+ *
375
+ * The default value is true.
376
+ *
377
+ * **Note**:<br />
378
+ * You may want to disable the default behavior when unit-testing modules that depend on
379
+ * `ngRoute`, in order to avoid an unexpected request for the default route's template.
380
+ *
381
+ * @param {boolean=} enabled - If provided, update the internal `eagerInstantiationEnabled` flag.
382
+ *
383
+ * @returns {*} The current value of the `eagerInstantiationEnabled` flag if used as a getter or
384
+ * itself (for chaining) if used as a setter.
385
+ */
386
+ isEagerInstantiationEnabled = true;
387
+ this.eagerInstantiationEnabled = function eagerInstantiationEnabled(enabled) {
388
+ if (isDefined(enabled)) {
389
+ isEagerInstantiationEnabled = enabled;
390
+ return this;
391
+ }
392
+
393
+ return isEagerInstantiationEnabled;
394
+ };
395
+
298
396
 
299
397
  this.$get = ['$rootScope',
300
398
  '$location',
@@ -303,7 +401,8 @@ function $RouteProvider() {
303
401
  '$injector',
304
402
  '$templateRequest',
305
403
  '$sce',
306
- function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
404
+ '$browser',
405
+ function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce, $browser) {
307
406
 
308
407
  /**
309
408
  * @ngdoc service
@@ -388,12 +487,12 @@ function $RouteProvider() {
388
487
  * })
389
488
  *
390
489
  * .controller('BookController', function($scope, $routeParams) {
391
- * $scope.name = "BookController";
490
+ * $scope.name = 'BookController';
392
491
  * $scope.params = $routeParams;
393
492
  * })
394
493
  *
395
494
  * .controller('ChapterController', function($scope, $routeParams) {
396
- * $scope.name = "ChapterController";
495
+ * $scope.name = 'ChapterController';
397
496
  * $scope.params = $routeParams;
398
497
  * })
399
498
  *
@@ -426,15 +525,15 @@ function $RouteProvider() {
426
525
  * it('should load and compile correct template', function() {
427
526
  * element(by.linkText('Moby: Ch1')).click();
428
527
  * 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/);
528
+ * expect(content).toMatch(/controller: ChapterController/);
529
+ * expect(content).toMatch(/Book Id: Moby/);
530
+ * expect(content).toMatch(/Chapter Id: 1/);
432
531
  *
433
532
  * element(by.partialLinkText('Scarlet')).click();
434
533
  *
435
534
  * content = element(by.css('[ng-view]')).getText();
436
- * expect(content).toMatch(/controller\: BookController/);
437
- * expect(content).toMatch(/Book Id\: Scarlet/);
535
+ * expect(content).toMatch(/controller: BookController/);
536
+ * expect(content).toMatch(/Book Id: Scarlet/);
438
537
  * });
439
538
  * </file>
440
539
  * </example>
@@ -482,12 +581,14 @@ function $RouteProvider() {
482
581
  * @name $route#$routeChangeError
483
582
  * @eventType broadcast on root scope
484
583
  * @description
485
- * Broadcasted if any of the resolve promises are rejected.
584
+ * Broadcasted if a redirection function fails or any redirection or resolve promises are
585
+ * rejected.
486
586
  *
487
587
  * @param {Object} angularEvent Synthetic event object
488
588
  * @param {Route} current Current route information.
489
589
  * @param {Route} previous Previous route information.
490
- * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
590
+ * @param {Route} rejection The thrown error or the rejection reason of the promise. Usually
591
+ * the rejection reason is the error that caused the promise to get rejected.
491
592
  */
492
593
 
493
594
  /**
@@ -495,8 +596,9 @@ function $RouteProvider() {
495
596
  * @name $route#$routeUpdate
496
597
  * @eventType broadcast on root scope
497
598
  * @description
498
- * The `reloadOnSearch` property has been set to false, and we are reusing the same
499
- * instance of the Controller.
599
+ * Broadcasted if the same instance of a route (including template, controller instance,
600
+ * resolved dependencies, etc.) is being reused. This can happen if either `reloadOnSearch` or
601
+ * `reloadOnUrl` has been set to `false`.
500
602
  *
501
603
  * @param {Object} angularEvent Synthetic event object
502
604
  * @param {Route} current Current/previous route information.
@@ -556,7 +658,7 @@ function $RouteProvider() {
556
658
  // interpolate modifies newParams, only query params are left
557
659
  $location.search(newParams);
558
660
  } else {
559
- throw $routeMinErr('norout', 'Tried updating route when with no current route');
661
+ throw $routeMinErr('norout', 'Tried updating route with no current route');
560
662
  }
561
663
  }
562
664
  };
@@ -604,9 +706,7 @@ function $RouteProvider() {
604
706
  var lastRoute = $route.current;
605
707
 
606
708
  preparedRoute = parseRoute();
607
- preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
608
- && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
609
- && !preparedRoute.reloadOnSearch && !forceReload;
709
+ preparedRouteIsUpdateOnly = isNavigationUpdateOnly(preparedRoute, lastRoute);
610
710
 
611
711
  if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
612
712
  if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
@@ -628,37 +728,112 @@ function $RouteProvider() {
628
728
  } else if (nextRoute || lastRoute) {
629
729
  forceReload = false;
630
730
  $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
731
 
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) {
732
+ var nextRoutePromise = $q.resolve(nextRoute);
733
+
734
+ $browser.$$incOutstandingRequestCount('$route');
735
+
736
+ nextRoutePromise.
737
+ then(getRedirectionData).
738
+ then(handlePossibleRedirection).
739
+ then(function(keepProcessingRoute) {
740
+ return keepProcessingRoute && nextRoutePromise.
741
+ then(resolveLocals).
742
+ then(function(locals) {
743
+ // after route change
744
+ if (nextRoute === $route.current) {
745
+ if (nextRoute) {
746
+ nextRoute.locals = locals;
747
+ angular.copy(nextRoute.params, $routeParams);
748
+ }
749
+ $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
750
+ }
751
+ });
752
+ }).catch(function(error) {
753
+ if (nextRoute === $route.current) {
656
754
  $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
657
755
  }
756
+ }).finally(function() {
757
+ // Because `commitRoute()` is called from a `$rootScope.$evalAsync` block (see
758
+ // `$locationWatch`), this `$$completeOutstandingRequest()` call will not cause
759
+ // `outstandingRequestCount` to hit zero. This is important in case we are redirecting
760
+ // to a new route which also requires some asynchronous work.
761
+
762
+ $browser.$$completeOutstandingRequest(noop, '$route');
658
763
  });
659
764
  }
660
765
  }
661
766
 
767
+ function getRedirectionData(route) {
768
+ var data = {
769
+ route: route,
770
+ hasRedirection: false
771
+ };
772
+
773
+ if (route) {
774
+ if (route.redirectTo) {
775
+ if (angular.isString(route.redirectTo)) {
776
+ data.path = interpolate(route.redirectTo, route.params);
777
+ data.search = route.params;
778
+ data.hasRedirection = true;
779
+ } else {
780
+ var oldPath = $location.path();
781
+ var oldSearch = $location.search();
782
+ var newUrl = route.redirectTo(route.pathParams, oldPath, oldSearch);
783
+
784
+ if (angular.isDefined(newUrl)) {
785
+ data.url = newUrl;
786
+ data.hasRedirection = true;
787
+ }
788
+ }
789
+ } else if (route.resolveRedirectTo) {
790
+ return $q.
791
+ resolve($injector.invoke(route.resolveRedirectTo)).
792
+ then(function(newUrl) {
793
+ if (angular.isDefined(newUrl)) {
794
+ data.url = newUrl;
795
+ data.hasRedirection = true;
796
+ }
797
+
798
+ return data;
799
+ });
800
+ }
801
+ }
802
+
803
+ return data;
804
+ }
805
+
806
+ function handlePossibleRedirection(data) {
807
+ var keepProcessingRoute = true;
808
+
809
+ if (data.route !== $route.current) {
810
+ keepProcessingRoute = false;
811
+ } else if (data.hasRedirection) {
812
+ var oldUrl = $location.url();
813
+ var newUrl = data.url;
814
+
815
+ if (newUrl) {
816
+ $location.
817
+ url(newUrl).
818
+ replace();
819
+ } else {
820
+ newUrl = $location.
821
+ path(data.path).
822
+ search(data.search).
823
+ replace().
824
+ url();
825
+ }
826
+
827
+ if (newUrl !== oldUrl) {
828
+ // Exit out and don't process current next value,
829
+ // wait for next location change from redirect
830
+ keepProcessingRoute = false;
831
+ }
832
+ }
833
+
834
+ return keepProcessingRoute;
835
+ }
836
+
662
837
  function resolveLocals(route) {
663
838
  if (route) {
664
839
  var locals = angular.extend({}, route.resolve);
@@ -675,7 +850,6 @@ function $RouteProvider() {
675
850
  }
676
851
  }
677
852
 
678
-
679
853
  function getTemplateFor(route) {
680
854
  var template, templateUrl;
681
855
  if (angular.isDefined(template = route.template)) {
@@ -694,7 +868,6 @@ function $RouteProvider() {
694
868
  return template;
695
869
  }
696
870
 
697
-
698
871
  /**
699
872
  * @returns {Object} the current active route, by matching it against the URL
700
873
  */
@@ -713,6 +886,29 @@ function $RouteProvider() {
713
886
  return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
714
887
  }
715
888
 
889
+ /**
890
+ * @param {Object} newRoute - The new route configuration (as returned by `parseRoute()`).
891
+ * @param {Object} oldRoute - The previous route configuration (as returned by `parseRoute()`).
892
+ * @returns {boolean} Whether this is an "update-only" navigation, i.e. the URL maps to the same
893
+ * route and it can be reused (based on the config and the type of change).
894
+ */
895
+ function isNavigationUpdateOnly(newRoute, oldRoute) {
896
+ // IF this is not a forced reload
897
+ return !forceReload
898
+ // AND both `newRoute`/`oldRoute` are defined
899
+ && newRoute && oldRoute
900
+ // AND they map to the same Route Definition Object
901
+ && (newRoute.$$route === oldRoute.$$route)
902
+ // AND `reloadOnUrl` is disabled
903
+ && (!newRoute.reloadOnUrl
904
+ // OR `reloadOnSearch` is disabled
905
+ || (!newRoute.reloadOnSearch
906
+ // AND both routes have the same path params
907
+ && angular.equals(newRoute.pathParams, oldRoute.pathParams)
908
+ )
909
+ );
910
+ }
911
+
716
912
  /**
717
913
  * @returns {string} interpolation of the redirect path with the parameters
718
914
  */
@@ -734,6 +930,14 @@ function $RouteProvider() {
734
930
  }];
735
931
  }
736
932
 
933
+ instantiateRoute.$inject = ['$injector'];
934
+ function instantiateRoute($injector) {
935
+ if (isEagerInstantiationEnabled) {
936
+ // Instantiate `$route`
937
+ $injector.get('$route');
938
+ }
939
+ }
940
+
737
941
  ngRouteModule.provider('$routeParams', $RouteParamsProvider);
738
942
 
739
943
 
@@ -741,6 +945,7 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
741
945
  * @ngdoc service
742
946
  * @name $routeParams
743
947
  * @requires $route
948
+ * @this
744
949
  *
745
950
  * @description
746
951
  * The `$routeParams` service allows you to retrieve the current set of route parameters.
@@ -784,7 +989,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
784
989
  * @restrict ECA
785
990
  *
786
991
  * @description
787
- * # Overview
788
992
  * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
789
993
  * including the rendered template of the current route into the main layout (`index.html`) file.
790
994
  * Every time the current route changes, the included view changes with it according to the
@@ -800,13 +1004,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
800
1004
  *
801
1005
  * The enter and leave animation occur concurrently.
802
1006
  *
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
1007
  * @scope
811
1008
  * @priority 400
812
1009
  * @param {string=} onload Expression to evaluate whenever the view updates.
@@ -917,17 +1114,17 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
917
1114
  $locationProvider.html5Mode(true);
918
1115
  }])
919
1116
  .controller('MainCtrl', ['$route', '$routeParams', '$location',
920
- function($route, $routeParams, $location) {
1117
+ function MainCtrl($route, $routeParams, $location) {
921
1118
  this.$route = $route;
922
1119
  this.$location = $location;
923
1120
  this.$routeParams = $routeParams;
924
1121
  }])
925
- .controller('BookCtrl', ['$routeParams', function($routeParams) {
926
- this.name = "BookCtrl";
1122
+ .controller('BookCtrl', ['$routeParams', function BookCtrl($routeParams) {
1123
+ this.name = 'BookCtrl';
927
1124
  this.params = $routeParams;
928
1125
  }])
929
- .controller('ChapterCtrl', ['$routeParams', function($routeParams) {
930
- this.name = "ChapterCtrl";
1126
+ .controller('ChapterCtrl', ['$routeParams', function ChapterCtrl($routeParams) {
1127
+ this.name = 'ChapterCtrl';
931
1128
  this.params = $routeParams;
932
1129
  }]);
933
1130
 
@@ -937,15 +1134,15 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
937
1134
  it('should load and compile correct template', function() {
938
1135
  element(by.linkText('Moby: Ch1')).click();
939
1136
  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/);
1137
+ expect(content).toMatch(/controller: ChapterCtrl/);
1138
+ expect(content).toMatch(/Book Id: Moby/);
1139
+ expect(content).toMatch(/Chapter Id: 1/);
943
1140
 
944
1141
  element(by.partialLinkText('Scarlet')).click();
945
1142
 
946
1143
  content = element(by.css('[ng-view]')).getText();
947
- expect(content).toMatch(/controller\: BookCtrl/);
948
- expect(content).toMatch(/Book Id\: Scarlet/);
1144
+ expect(content).toMatch(/controller: BookCtrl/);
1145
+ expect(content).toMatch(/Book Id: Scarlet/);
949
1146
  });
950
1147
  </file>
951
1148
  </example>
@@ -988,8 +1185,8 @@ function ngViewFactory($route, $anchorScroll, $animate) {
988
1185
  }
989
1186
  if (currentElement) {
990
1187
  previousLeaveAnimation = $animate.leave(currentElement);
991
- previousLeaveAnimation.then(function() {
992
- previousLeaveAnimation = null;
1188
+ previousLeaveAnimation.done(function(response) {
1189
+ if (response !== false) previousLeaveAnimation = null;
993
1190
  });
994
1191
  currentElement = null;
995
1192
  }
@@ -1010,8 +1207,8 @@ function ngViewFactory($route, $anchorScroll, $animate) {
1010
1207
  // function is called before linking the content, which would apply child
1011
1208
  // directives to non existing elements.
1012
1209
  var clone = $transclude(newScope, function(clone) {
1013
- $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
1014
- if (angular.isDefined(autoScrollExp)
1210
+ $animate.enter(clone, null, currentElement || $element).done(function onNgViewEnter(response) {
1211
+ if (response !== false && angular.isDefined(autoScrollExp)
1015
1212
  && (!autoScrollExp || scope.$eval(autoScrollExp))) {
1016
1213
  $anchorScroll();
1017
1214
  }