bastion 3.2.2 → 3.3.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,12 +1,12 @@
1
1
  /**
2
- * @license AngularJS v1.2.9
3
- * (c) 2010-2014 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.5
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {'use strict';
6
+ (function(window, angular) {'use strict';
7
7
 
8
8
  /**
9
- * @ngdoc overview
9
+ * @ngdoc module
10
10
  * @name ngRoute
11
11
  * @description
12
12
  *
@@ -16,60 +16,58 @@
16
16
  *
17
17
  * ## Example
18
18
  * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
19
- *
20
- * {@installModule route}
19
+ *
21
20
  *
22
21
  * <div doc-module-components="ngRoute"></div>
23
22
  */
24
23
  /* global -ngRouteModule */
25
24
  var ngRouteModule = angular.module('ngRoute', ['ng']).
26
- provider('$route', $RouteProvider);
25
+ provider('$route', $RouteProvider),
26
+ $routeMinErr = angular.$$minErr('ngRoute');
27
27
 
28
28
  /**
29
- * @ngdoc object
30
- * @name ngRoute.$routeProvider
31
- * @function
29
+ * @ngdoc provider
30
+ * @name $routeProvider
32
31
  *
33
32
  * @description
34
33
  *
35
34
  * Used for configuring routes.
36
- *
35
+ *
37
36
  * ## Example
38
37
  * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
39
38
  *
40
39
  * ## Dependencies
41
40
  * Requires the {@link ngRoute `ngRoute`} module to be installed.
42
41
  */
43
- function $RouteProvider(){
42
+ function $RouteProvider() {
44
43
  function inherit(parent, extra) {
45
- return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
44
+ return angular.extend(Object.create(parent), extra);
46
45
  }
47
46
 
48
47
  var routes = {};
49
48
 
50
49
  /**
51
50
  * @ngdoc method
52
- * @name ngRoute.$routeProvider#when
53
- * @methodOf ngRoute.$routeProvider
51
+ * @name $routeProvider#when
54
52
  *
55
53
  * @param {string} path Route path (matched against `$location.path`). If `$location.path`
56
54
  * contains redundant trailing slash or is missing one, the route will still match and the
57
55
  * `$location.path` will be updated to add or drop the trailing slash to exactly match the
58
56
  * route definition.
59
57
  *
60
- * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
58
+ * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
61
59
  * to the next slash are matched and stored in `$routeParams` under the given `name`
62
60
  * when the route matches.
63
- * * `path` can contain named groups starting with a colon and ending with a star:
61
+ * * `path` can contain named groups starting with a colon and ending with a star:
64
62
  * e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
65
63
  * when the route matches.
66
- * * `path` can contain optional named groups with a question mark: e.g.`:name?`.
64
+ * * `path` can contain optional named groups with a question mark: e.g.`:name?`.
67
65
  *
68
66
  * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
69
- * `/color/brown/largecode/code/with/slashs/edit` and extract:
67
+ * `/color/brown/largecode/code/with/slashes/edit` and extract:
70
68
  *
71
- * * `color: brown`
72
- * * `largecode: code/with/slashs`.
69
+ * * `color: brown`
70
+ * * `largecode: code/with/slashes`.
73
71
  *
74
72
  *
75
73
  * @param {Object} route Mapping information to be assigned to `$route.current` on route
@@ -80,8 +78,8 @@ function $RouteProvider(){
80
78
  * - `controller` – `{(string|function()=}` – Controller fn that should be associated with
81
79
  * newly created scope or the name of a {@link angular.Module#controller registered
82
80
  * controller} if passed as a string.
83
- * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
84
- * published to scope under the `controllerAs` name.
81
+ * - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
82
+ * If present, the controller will be published to scope under the `controllerAs` name.
85
83
  * - `template` – `{string=|function()=}` – html template as a string or a function that
86
84
  * returns an html template as a string which should be used by {@link
87
85
  * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
@@ -107,18 +105,30 @@ function $RouteProvider(){
107
105
  * If all the promises are resolved successfully, the values of the resolved promises are
108
106
  * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
109
107
  * fired. If any of the promises are rejected the
110
- * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
111
- * is:
108
+ * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired.
109
+ * For easier access to the resolved dependencies from the template, the `resolve` map will
110
+ * be available on the scope of the route, under `$resolve` (by default) or a custom name
111
+ * specified by the `resolveAs` property (see below). This can be particularly useful, when
112
+ * working with {@link angular.Module#component components} as route templates.<br />
113
+ * <div class="alert alert-warning">
114
+ * **Note:** If your scope already contains a property with this name, it will be hidden
115
+ * or overwritten. Make sure, you specify an appropriate name for this property, that
116
+ * does not collide with other properties on the scope.
117
+ * </div>
118
+ * The map object is:
112
119
  *
113
120
  * - `key` – `{string}`: a name of a dependency to be injected into the controller.
114
121
  * - `factory` - `{string|function}`: If `string` then it is an alias for a service.
115
- * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
122
+ * Otherwise if function, then it is {@link auto.$injector#invoke injected}
116
123
  * and the return value is treated as the dependency. If the result is a promise, it is
117
124
  * resolved before its value is injected into the controller. Be aware that
118
125
  * `ngRoute.$routeParams` will still refer to the previous route within these resolve
119
126
  * functions. Use `$route.current.params` to access the new route parameters, instead.
120
127
  *
121
- * - `redirectTo` {(string|function())=} value to update
128
+ * - `resolveAs` - `{string=}` - The name under which the `resolve` map will be available on
129
+ * the scope of the route. If omitted, defaults to `$resolve`.
130
+ *
131
+ * - `redirectTo` – `{(string|function())=}` – value to update
122
132
  * {@link ng.$location $location} path with and trigger route redirection.
123
133
  *
124
134
  * If `redirectTo` is a function, it will be called with the following parameters:
@@ -131,13 +141,13 @@ function $RouteProvider(){
131
141
  * The custom `redirectTo` function is expected to return a string which will be used
132
142
  * to update `$location.path()` and `$location.search()`.
133
143
  *
134
- * - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
144
+ * - `[reloadOnSearch=true]` - `{boolean=}` - reload route when only `$location.search()`
135
145
  * or `$location.hash()` changes.
136
146
  *
137
147
  * If the option is set to `false` and url in the browser changes, then
138
148
  * `$routeUpdate` event is broadcasted on the root scope.
139
149
  *
140
- * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
150
+ * - `[caseInsensitiveMatch=false]` - `{boolean=}` - match routes without being case sensitive
141
151
  *
142
152
  * If the option is set to `true`, then the particular route can be matched without being
143
153
  * case sensitive
@@ -148,27 +158,45 @@ function $RouteProvider(){
148
158
  * Adds a new route definition to the `$route` service.
149
159
  */
150
160
  this.when = function(path, route) {
161
+ //copy original route object to preserve params inherited from proto chain
162
+ var routeCopy = angular.copy(route);
163
+ if (angular.isUndefined(routeCopy.reloadOnSearch)) {
164
+ routeCopy.reloadOnSearch = true;
165
+ }
166
+ if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
167
+ routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
168
+ }
151
169
  routes[path] = angular.extend(
152
- {reloadOnSearch: true},
153
- route,
154
- path && pathRegExp(path, route)
170
+ routeCopy,
171
+ path && pathRegExp(path, routeCopy)
155
172
  );
156
173
 
157
174
  // create redirection for trailing slashes
158
175
  if (path) {
159
- var redirectPath = (path[path.length-1] == '/')
160
- ? path.substr(0, path.length-1)
161
- : path +'/';
176
+ var redirectPath = (path[path.length - 1] == '/')
177
+ ? path.substr(0, path.length - 1)
178
+ : path + '/';
162
179
 
163
180
  routes[redirectPath] = angular.extend(
164
181
  {redirectTo: path},
165
- pathRegExp(redirectPath, route)
182
+ pathRegExp(redirectPath, routeCopy)
166
183
  );
167
184
  }
168
185
 
169
186
  return this;
170
187
  };
171
188
 
189
+ /**
190
+ * @ngdoc property
191
+ * @name $routeProvider#caseInsensitiveMatch
192
+ * @description
193
+ *
194
+ * A boolean property indicating if routes defined
195
+ * using this provider should be matched using a case insensitive
196
+ * algorithm. Defaults to `false`.
197
+ */
198
+ this.caseInsensitiveMatch = false;
199
+
172
200
  /**
173
201
  * @param path {string} path
174
202
  * @param opts {Object} options
@@ -190,9 +218,9 @@ function $RouteProvider(){
190
218
 
191
219
  path = path
192
220
  .replace(/([().])/g, '\\$1')
193
- .replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
194
- var optional = option === '?' ? option : null;
195
- var star = option === '*' ? option : null;
221
+ .replace(/(\/)?:(\w+)(\*\?|[\?\*])?/g, function(_, slash, key, option) {
222
+ var optional = (option === '?' || option === '*?') ? '?' : null;
223
+ var star = (option === '*' || option === '*?') ? '*' : null;
196
224
  keys.push({ name: key, optional: !!optional });
197
225
  slash = slash || '';
198
226
  return ''
@@ -212,17 +240,20 @@ function $RouteProvider(){
212
240
 
213
241
  /**
214
242
  * @ngdoc method
215
- * @name ngRoute.$routeProvider#otherwise
216
- * @methodOf ngRoute.$routeProvider
243
+ * @name $routeProvider#otherwise
217
244
  *
218
245
  * @description
219
246
  * Sets route definition that will be used on route change when no other route definition
220
247
  * is matched.
221
248
  *
222
- * @param {Object} params Mapping information to be assigned to `$route.current`.
249
+ * @param {Object|string} params Mapping information to be assigned to `$route.current`.
250
+ * If called with a string, the value maps to `redirectTo`.
223
251
  * @returns {Object} self
224
252
  */
225
253
  this.otherwise = function(params) {
254
+ if (typeof params === 'string') {
255
+ params = {redirectTo: params};
256
+ }
226
257
  this.when(null, params);
227
258
  return this;
228
259
  };
@@ -233,21 +264,20 @@ function $RouteProvider(){
233
264
  '$routeParams',
234
265
  '$q',
235
266
  '$injector',
236
- '$http',
237
- '$templateCache',
267
+ '$templateRequest',
238
268
  '$sce',
239
- function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
269
+ function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
240
270
 
241
271
  /**
242
- * @ngdoc object
243
- * @name ngRoute.$route
272
+ * @ngdoc service
273
+ * @name $route
244
274
  * @requires $location
245
275
  * @requires $routeParams
246
276
  *
247
277
  * @property {Object} current Reference to the current route definition.
248
278
  * The route definition contains:
249
279
  *
250
- * - `controller`: The controller constructor as define in route definition.
280
+ * - `controller`: The controller constructor as defined in the route definition.
251
281
  * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
252
282
  * controller instantiation. The `locals` contain
253
283
  * the resolved values of the `resolve` map. Additionally the `locals` also contain:
@@ -255,7 +285,11 @@ function $RouteProvider(){
255
285
  * - `$scope` - The current route scope.
256
286
  * - `$template` - The current route template HTML.
257
287
  *
258
- * @property {Array.<Object>} routes Array of all configured routes.
288
+ * The `locals` will be assigned to the route scope's `$resolve` property. You can override
289
+ * the property name, using `resolveAs` in the route definition. See
290
+ * {@link ngRoute.$routeProvider $routeProvider} for more info.
291
+ *
292
+ * @property {Object} routes Object with all route configuration Objects as its properties.
259
293
  *
260
294
  * @description
261
295
  * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
@@ -270,116 +304,120 @@ function $RouteProvider(){
270
304
  * {@link ngRoute.$routeParams `$routeParams`} service.
271
305
  *
272
306
  * @example
273
- This example shows how changing the URL hash causes the `$route` to match a route against the
274
- URL, and the `ngView` pulls in the partial.
275
-
276
- Note that this example is using {@link ng.directive:script inlined templates}
277
- to get it working on jsfiddle as well.
278
-
279
- <example module="ngViewExample" deps="angular-route.js">
280
- <file name="index.html">
281
- <div ng-controller="MainCntl">
282
- Choose:
283
- <a href="Book/Moby">Moby</a> |
284
- <a href="Book/Moby/ch/1">Moby: Ch1</a> |
285
- <a href="Book/Gatsby">Gatsby</a> |
286
- <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
287
- <a href="Book/Scarlet">Scarlet Letter</a><br/>
288
-
289
- <div ng-view></div>
290
- <hr />
291
-
292
- <pre>$location.path() = {{$location.path()}}</pre>
293
- <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
294
- <pre>$route.current.params = {{$route.current.params}}</pre>
295
- <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
296
- <pre>$routeParams = {{$routeParams}}</pre>
297
- </div>
298
- </file>
299
-
300
- <file name="book.html">
301
- controller: {{name}}<br />
302
- Book Id: {{params.bookId}}<br />
303
- </file>
304
-
305
- <file name="chapter.html">
306
- controller: {{name}}<br />
307
- Book Id: {{params.bookId}}<br />
308
- Chapter Id: {{params.chapterId}}
309
- </file>
310
-
311
- <file name="script.js">
312
- angular.module('ngViewExample', ['ngRoute'])
313
-
314
- .config(function($routeProvider, $locationProvider) {
315
- $routeProvider.when('/Book/:bookId', {
316
- templateUrl: 'book.html',
317
- controller: BookCntl,
318
- resolve: {
319
- // I will cause a 1 second delay
320
- delay: function($q, $timeout) {
321
- var delay = $q.defer();
322
- $timeout(delay.resolve, 1000);
323
- return delay.promise;
324
- }
325
- }
326
- });
327
- $routeProvider.when('/Book/:bookId/ch/:chapterId', {
328
- templateUrl: 'chapter.html',
329
- controller: ChapterCntl
330
- });
331
-
332
- // configure html5 to get links working on jsfiddle
333
- $locationProvider.html5Mode(true);
334
- });
335
-
336
- function MainCntl($scope, $route, $routeParams, $location) {
337
- $scope.$route = $route;
338
- $scope.$location = $location;
339
- $scope.$routeParams = $routeParams;
340
- }
341
-
342
- function BookCntl($scope, $routeParams) {
343
- $scope.name = "BookCntl";
344
- $scope.params = $routeParams;
345
- }
346
-
347
- function ChapterCntl($scope, $routeParams) {
348
- $scope.name = "ChapterCntl";
349
- $scope.params = $routeParams;
350
- }
351
- </file>
352
-
353
- <file name="scenario.js">
354
- it('should load and compile correct template', function() {
355
- element('a:contains("Moby: Ch1")').click();
356
- var content = element('.doc-example-live [ng-view]').text();
357
- expect(content).toMatch(/controller\: ChapterCntl/);
358
- expect(content).toMatch(/Book Id\: Moby/);
359
- expect(content).toMatch(/Chapter Id\: 1/);
360
-
361
- element('a:contains("Scarlet")').click();
362
- sleep(2); // promises are not part of scenario waiting
363
- content = element('.doc-example-live [ng-view]').text();
364
- expect(content).toMatch(/controller\: BookCntl/);
365
- expect(content).toMatch(/Book Id\: Scarlet/);
366
- });
367
- </file>
368
- </example>
307
+ * This example shows how changing the URL hash causes the `$route` to match a route against the
308
+ * URL, and the `ngView` pulls in the partial.
309
+ *
310
+ * <example name="$route-service" module="ngRouteExample"
311
+ * deps="angular-route.js" fixBase="true">
312
+ * <file name="index.html">
313
+ * <div ng-controller="MainController">
314
+ * Choose:
315
+ * <a href="Book/Moby">Moby</a> |
316
+ * <a href="Book/Moby/ch/1">Moby: Ch1</a> |
317
+ * <a href="Book/Gatsby">Gatsby</a> |
318
+ * <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
319
+ * <a href="Book/Scarlet">Scarlet Letter</a><br/>
320
+ *
321
+ * <div ng-view></div>
322
+ *
323
+ * <hr />
324
+ *
325
+ * <pre>$location.path() = {{$location.path()}}</pre>
326
+ * <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
327
+ * <pre>$route.current.params = {{$route.current.params}}</pre>
328
+ * <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
329
+ * <pre>$routeParams = {{$routeParams}}</pre>
330
+ * </div>
331
+ * </file>
332
+ *
333
+ * <file name="book.html">
334
+ * controller: {{name}}<br />
335
+ * Book Id: {{params.bookId}}<br />
336
+ * </file>
337
+ *
338
+ * <file name="chapter.html">
339
+ * controller: {{name}}<br />
340
+ * Book Id: {{params.bookId}}<br />
341
+ * Chapter Id: {{params.chapterId}}
342
+ * </file>
343
+ *
344
+ * <file name="script.js">
345
+ * angular.module('ngRouteExample', ['ngRoute'])
346
+ *
347
+ * .controller('MainController', function($scope, $route, $routeParams, $location) {
348
+ * $scope.$route = $route;
349
+ * $scope.$location = $location;
350
+ * $scope.$routeParams = $routeParams;
351
+ * })
352
+ *
353
+ * .controller('BookController', function($scope, $routeParams) {
354
+ * $scope.name = "BookController";
355
+ * $scope.params = $routeParams;
356
+ * })
357
+ *
358
+ * .controller('ChapterController', function($scope, $routeParams) {
359
+ * $scope.name = "ChapterController";
360
+ * $scope.params = $routeParams;
361
+ * })
362
+ *
363
+ * .config(function($routeProvider, $locationProvider) {
364
+ * $routeProvider
365
+ * .when('/Book/:bookId', {
366
+ * templateUrl: 'book.html',
367
+ * controller: 'BookController',
368
+ * resolve: {
369
+ * // I will cause a 1 second delay
370
+ * delay: function($q, $timeout) {
371
+ * var delay = $q.defer();
372
+ * $timeout(delay.resolve, 1000);
373
+ * return delay.promise;
374
+ * }
375
+ * }
376
+ * })
377
+ * .when('/Book/:bookId/ch/:chapterId', {
378
+ * templateUrl: 'chapter.html',
379
+ * controller: 'ChapterController'
380
+ * });
381
+ *
382
+ * // configure html5 to get links working on jsfiddle
383
+ * $locationProvider.html5Mode(true);
384
+ * });
385
+ *
386
+ * </file>
387
+ *
388
+ * <file name="protractor.js" type="protractor">
389
+ * it('should load and compile correct template', function() {
390
+ * element(by.linkText('Moby: Ch1')).click();
391
+ * var content = element(by.css('[ng-view]')).getText();
392
+ * expect(content).toMatch(/controller\: ChapterController/);
393
+ * expect(content).toMatch(/Book Id\: Moby/);
394
+ * expect(content).toMatch(/Chapter Id\: 1/);
395
+ *
396
+ * element(by.partialLinkText('Scarlet')).click();
397
+ *
398
+ * content = element(by.css('[ng-view]')).getText();
399
+ * expect(content).toMatch(/controller\: BookController/);
400
+ * expect(content).toMatch(/Book Id\: Scarlet/);
401
+ * });
402
+ * </file>
403
+ * </example>
369
404
  */
370
405
 
371
406
  /**
372
407
  * @ngdoc event
373
- * @name ngRoute.$route#$routeChangeStart
374
- * @eventOf ngRoute.$route
408
+ * @name $route#$routeChangeStart
375
409
  * @eventType broadcast on root scope
376
410
  * @description
377
411
  * Broadcasted before a route change. At this point the route services starts
378
- * resolving all of the dependencies needed for the route change to occurs.
412
+ * resolving all of the dependencies needed for the route change to occur.
379
413
  * Typically this involves fetching the view template as well as any dependencies
380
414
  * defined in `resolve` route property. Once all of the dependencies are resolved
381
415
  * `$routeChangeSuccess` is fired.
382
416
  *
417
+ * The route change (and the `$location` change that triggered it) can be prevented
418
+ * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
419
+ * for more details about event object.
420
+ *
383
421
  * @param {Object} angularEvent Synthetic event object.
384
422
  * @param {Route} next Future route information.
385
423
  * @param {Route} current Current route information.
@@ -387,11 +425,12 @@ function $RouteProvider(){
387
425
 
388
426
  /**
389
427
  * @ngdoc event
390
- * @name ngRoute.$route#$routeChangeSuccess
391
- * @eventOf ngRoute.$route
428
+ * @name $route#$routeChangeSuccess
392
429
  * @eventType broadcast on root scope
393
430
  * @description
394
- * Broadcasted after a route dependencies are resolved.
431
+ * Broadcasted after a route change has happened successfully.
432
+ * The `resolve` dependencies are now available in the `current.locals` property.
433
+ *
395
434
  * {@link ngRoute.directive:ngView ngView} listens for the directive
396
435
  * to instantiate the controller and render the view.
397
436
  *
@@ -403,8 +442,7 @@ function $RouteProvider(){
403
442
 
404
443
  /**
405
444
  * @ngdoc event
406
- * @name ngRoute.$route#$routeChangeError
407
- * @eventOf ngRoute.$route
445
+ * @name $route#$routeChangeError
408
446
  * @eventType broadcast on root scope
409
447
  * @description
410
448
  * Broadcasted if any of the resolve promises are rejected.
@@ -417,38 +455,77 @@ function $RouteProvider(){
417
455
 
418
456
  /**
419
457
  * @ngdoc event
420
- * @name ngRoute.$route#$routeUpdate
421
- * @eventOf ngRoute.$route
458
+ * @name $route#$routeUpdate
422
459
  * @eventType broadcast on root scope
423
460
  * @description
424
- *
425
461
  * The `reloadOnSearch` property has been set to false, and we are reusing the same
426
462
  * instance of the Controller.
463
+ *
464
+ * @param {Object} angularEvent Synthetic event object
465
+ * @param {Route} current Current/previous route information.
427
466
  */
428
467
 
429
468
  var forceReload = false,
469
+ preparedRoute,
470
+ preparedRouteIsUpdateOnly,
430
471
  $route = {
431
472
  routes: routes,
432
473
 
433
474
  /**
434
475
  * @ngdoc method
435
- * @name ngRoute.$route#reload
436
- * @methodOf ngRoute.$route
476
+ * @name $route#reload
437
477
  *
438
478
  * @description
439
479
  * Causes `$route` service to reload the current route even if
440
480
  * {@link ng.$location $location} hasn't changed.
441
481
  *
442
482
  * As a result of that, {@link ngRoute.directive:ngView ngView}
443
- * creates new scope, reinstantiates the controller.
483
+ * creates new scope and reinstantiates the controller.
444
484
  */
445
485
  reload: function() {
446
486
  forceReload = true;
447
- $rootScope.$evalAsync(updateRoute);
487
+
488
+ var fakeLocationEvent = {
489
+ defaultPrevented: false,
490
+ preventDefault: function fakePreventDefault() {
491
+ this.defaultPrevented = true;
492
+ forceReload = false;
493
+ }
494
+ };
495
+
496
+ $rootScope.$evalAsync(function() {
497
+ prepareRoute(fakeLocationEvent);
498
+ if (!fakeLocationEvent.defaultPrevented) commitRoute();
499
+ });
500
+ },
501
+
502
+ /**
503
+ * @ngdoc method
504
+ * @name $route#updateParams
505
+ *
506
+ * @description
507
+ * Causes `$route` service to update the current URL, replacing
508
+ * current route parameters with those specified in `newParams`.
509
+ * Provided property names that match the route's path segment
510
+ * definitions will be interpolated into the location's path, while
511
+ * remaining properties will be treated as query params.
512
+ *
513
+ * @param {!Object<string, string>} newParams mapping of URL parameter names to values
514
+ */
515
+ updateParams: function(newParams) {
516
+ if (this.current && this.current.$$route) {
517
+ newParams = angular.extend({}, this.current.params, newParams);
518
+ $location.path(interpolate(this.current.$$route.originalPath, newParams));
519
+ // interpolate modifies newParams, only query params are left
520
+ $location.search(newParams);
521
+ } else {
522
+ throw $routeMinErr('norout', 'Tried updating route when with no current route');
523
+ }
448
524
  }
449
525
  };
450
526
 
451
- $rootScope.$on('$locationChangeSuccess', updateRoute);
527
+ $rootScope.$on('$locationChangeStart', prepareRoute);
528
+ $rootScope.$on('$locationChangeSuccess', commitRoute);
452
529
 
453
530
  return $route;
454
531
 
@@ -477,9 +554,7 @@ function $RouteProvider(){
477
554
  for (var i = 1, len = m.length; i < len; ++i) {
478
555
  var key = keys[i - 1];
479
556
 
480
- var val = 'string' == typeof m[i]
481
- ? decodeURIComponent(m[i])
482
- : m[i];
557
+ var val = m[i];
483
558
 
484
559
  if (key && val) {
485
560
  params[key.name] = val;
@@ -488,56 +563,68 @@ function $RouteProvider(){
488
563
  return params;
489
564
  }
490
565
 
491
- function updateRoute() {
492
- var next = parseRoute(),
493
- last = $route.current;
494
-
495
- if (next && last && next.$$route === last.$$route
496
- && angular.equals(next.pathParams, last.pathParams)
497
- && !next.reloadOnSearch && !forceReload) {
498
- last.params = next.params;
499
- angular.copy(last.params, $routeParams);
500
- $rootScope.$broadcast('$routeUpdate', last);
501
- } else if (next || last) {
566
+ function prepareRoute($locationEvent) {
567
+ var lastRoute = $route.current;
568
+
569
+ preparedRoute = parseRoute();
570
+ preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
571
+ && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
572
+ && !preparedRoute.reloadOnSearch && !forceReload;
573
+
574
+ if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
575
+ if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
576
+ if ($locationEvent) {
577
+ $locationEvent.preventDefault();
578
+ }
579
+ }
580
+ }
581
+ }
582
+
583
+ function commitRoute() {
584
+ var lastRoute = $route.current;
585
+ var nextRoute = preparedRoute;
586
+
587
+ if (preparedRouteIsUpdateOnly) {
588
+ lastRoute.params = nextRoute.params;
589
+ angular.copy(lastRoute.params, $routeParams);
590
+ $rootScope.$broadcast('$routeUpdate', lastRoute);
591
+ } else if (nextRoute || lastRoute) {
502
592
  forceReload = false;
503
- $rootScope.$broadcast('$routeChangeStart', next, last);
504
- $route.current = next;
505
- if (next) {
506
- if (next.redirectTo) {
507
- if (angular.isString(next.redirectTo)) {
508
- $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
593
+ $route.current = nextRoute;
594
+ if (nextRoute) {
595
+ if (nextRoute.redirectTo) {
596
+ if (angular.isString(nextRoute.redirectTo)) {
597
+ $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
509
598
  .replace();
510
599
  } else {
511
- $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
600
+ $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
512
601
  .replace();
513
602
  }
514
603
  }
515
604
  }
516
605
 
517
- $q.when(next).
606
+ $q.when(nextRoute).
518
607
  then(function() {
519
- if (next) {
520
- var locals = angular.extend({}, next.resolve),
608
+ if (nextRoute) {
609
+ var locals = angular.extend({}, nextRoute.resolve),
521
610
  template, templateUrl;
522
611
 
523
612
  angular.forEach(locals, function(value, key) {
524
613
  locals[key] = angular.isString(value) ?
525
- $injector.get(value) : $injector.invoke(value);
614
+ $injector.get(value) : $injector.invoke(value, null, null, key);
526
615
  });
527
616
 
528
- if (angular.isDefined(template = next.template)) {
617
+ if (angular.isDefined(template = nextRoute.template)) {
529
618
  if (angular.isFunction(template)) {
530
- template = template(next.params);
619
+ template = template(nextRoute.params);
531
620
  }
532
- } else if (angular.isDefined(templateUrl = next.templateUrl)) {
621
+ } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
533
622
  if (angular.isFunction(templateUrl)) {
534
- templateUrl = templateUrl(next.params);
623
+ templateUrl = templateUrl(nextRoute.params);
535
624
  }
536
- templateUrl = $sce.getTrustedResourceUrl(templateUrl);
537
625
  if (angular.isDefined(templateUrl)) {
538
- next.loadedTemplateUrl = templateUrl;
539
- template = $http.get(templateUrl, {cache: $templateCache}).
540
- then(function(response) { return response.data; });
626
+ nextRoute.loadedTemplateUrl = $sce.valueOf(templateUrl);
627
+ template = $templateRequest(templateUrl);
541
628
  }
542
629
  }
543
630
  if (angular.isDefined(template)) {
@@ -546,18 +633,18 @@ function $RouteProvider(){
546
633
  return $q.all(locals);
547
634
  }
548
635
  }).
549
- // after route change
550
636
  then(function(locals) {
551
- if (next == $route.current) {
552
- if (next) {
553
- next.locals = locals;
554
- angular.copy(next.params, $routeParams);
637
+ // after route change
638
+ if (nextRoute == $route.current) {
639
+ if (nextRoute) {
640
+ nextRoute.locals = locals;
641
+ angular.copy(nextRoute.params, $routeParams);
555
642
  }
556
- $rootScope.$broadcast('$routeChangeSuccess', next, last);
643
+ $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
557
644
  }
558
645
  }, function(error) {
559
- if (next == $route.current) {
560
- $rootScope.$broadcast('$routeChangeError', next, last, error);
646
+ if (nextRoute == $route.current) {
647
+ $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
561
648
  }
562
649
  });
563
650
  }
@@ -565,7 +652,7 @@ function $RouteProvider(){
565
652
 
566
653
 
567
654
  /**
568
- * @returns the current active route, by matching it against the URL
655
+ * @returns {Object} the current active route, by matching it against the URL
569
656
  */
570
657
  function parseRoute() {
571
658
  // Match a route
@@ -583,15 +670,15 @@ function $RouteProvider(){
583
670
  }
584
671
 
585
672
  /**
586
- * @returns interpolation of the redirect path with the parameters
673
+ * @returns {string} interpolation of the redirect path with the parameters
587
674
  */
588
675
  function interpolate(string, params) {
589
676
  var result = [];
590
- angular.forEach((string||'').split(':'), function(segment, i) {
677
+ angular.forEach((string || '').split(':'), function(segment, i) {
591
678
  if (i === 0) {
592
679
  result.push(segment);
593
680
  } else {
594
- var segmentMatch = segment.match(/(\w+)(.*)/);
681
+ var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
595
682
  var key = segmentMatch[1];
596
683
  result.push(params[key]);
597
684
  result.push(segmentMatch[2] || '');
@@ -607,8 +694,8 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
607
694
 
608
695
 
609
696
  /**
610
- * @ngdoc object
611
- * @name ngRoute.$routeParams
697
+ * @ngdoc service
698
+ * @name $routeParams
612
699
  * @requires $route
613
700
  *
614
701
  * @description
@@ -617,7 +704,7 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
617
704
  * Requires the {@link ngRoute `ngRoute`} module to be installed.
618
705
  *
619
706
  * The route parameters are a combination of {@link ng.$location `$location`}'s
620
- * {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
707
+ * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
621
708
  * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
622
709
  *
623
710
  * In case of parameter name collision, `path` params take precedence over `search` params.
@@ -630,14 +717,14 @@ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
630
717
  * Instead you can use `$route.current.params` to access the new route's parameters.
631
718
  *
632
719
  * @example
633
- * <pre>
720
+ * ```js
634
721
  * // Given:
635
722
  * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
636
723
  * // Route: /Chapter/:chapterId/Section/:sectionId
637
724
  * //
638
725
  * // Then
639
- * $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
640
- * </pre>
726
+ * $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
727
+ * ```
641
728
  */
642
729
  function $RouteParamsProvider() {
643
730
  this.$get = function() { return {}; };
@@ -649,7 +736,7 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
649
736
 
650
737
  /**
651
738
  * @ngdoc directive
652
- * @name ngRoute.directive:ngView
739
+ * @name ngView
653
740
  * @restrict ECA
654
741
  *
655
742
  * @description
@@ -662,11 +749,20 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
662
749
  * Requires the {@link ngRoute `ngRoute`} module to be installed.
663
750
  *
664
751
  * @animations
665
- * enter - animation is used to bring new content into the browser.
666
- * leave - animation is used to animate existing content away.
752
+ * | Animation | Occurs |
753
+ * |----------------------------------|-------------------------------------|
754
+ * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM |
755
+ * | {@link ng.$animate#leave leave} | when the old element is removed from to the DOM |
667
756
  *
668
757
  * The enter and leave animation occur concurrently.
669
758
  *
759
+ * @knownIssue If `ngView` is contained in an asynchronously loaded template (e.g. in another
760
+ * directive's templateUrl or in a template loaded using `ngInclude`), then you need to
761
+ * make sure that `$route` is instantiated in time to capture the initial
762
+ * `$locationChangeStart` event and load the appropriate view. One way to achieve this
763
+ * is to have it as a dependency in a `.run` block:
764
+ * `myModule.run(['$route', function() {}]);`
765
+ *
670
766
  * @scope
671
767
  * @priority 400
672
768
  * @param {string=} onload Expression to evaluate whenever the view updates.
@@ -679,9 +775,11 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
679
775
  * - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
680
776
  * as an expression yields a truthy value.
681
777
  * @example
682
- <example module="ngViewExample" deps="angular-route.js" animations="true">
778
+ <example name="ngView-directive" module="ngViewExample"
779
+ deps="angular-route.js;angular-animate.js"
780
+ animations="true" fixBase="true">
683
781
  <file name="index.html">
684
- <div ng-controller="MainCntl as main">
782
+ <div ng-controller="MainCtrl as main">
685
783
  Choose:
686
784
  <a href="Book/Moby">Moby</a> |
687
785
  <a href="Book/Moby/ch/1">Moby: Ch1</a> |
@@ -697,7 +795,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
697
795
  <pre>$location.path() = {{main.$location.path()}}</pre>
698
796
  <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
699
797
  <pre>$route.current.params = {{main.$route.current.params}}</pre>
700
- <pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
701
798
  <pre>$routeParams = {{main.$routeParams}}</pre>
702
799
  </div>
703
800
  </file>
@@ -721,7 +818,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
721
818
  .view-animate-container {
722
819
  position:relative;
723
820
  height:100px!important;
724
- position:relative;
725
821
  background:white;
726
822
  border:1px solid black;
727
823
  height:40px;
@@ -733,7 +829,6 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
733
829
  }
734
830
 
735
831
  .view-animate.ng-enter, .view-animate.ng-leave {
736
- -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
737
832
  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
738
833
 
739
834
  display:block;
@@ -760,51 +855,52 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
760
855
  </file>
761
856
 
762
857
  <file name="script.js">
763
- angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
764
- function($routeProvider, $locationProvider) {
765
- $routeProvider.when('/Book/:bookId', {
766
- templateUrl: 'book.html',
767
- controller: BookCntl,
768
- controllerAs: 'book'
769
- });
770
- $routeProvider.when('/Book/:bookId/ch/:chapterId', {
771
- templateUrl: 'chapter.html',
772
- controller: ChapterCntl,
773
- controllerAs: 'chapter'
774
- });
775
-
776
- // configure html5 to get links working on jsfiddle
777
- $locationProvider.html5Mode(true);
778
- });
858
+ angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
859
+ .config(['$routeProvider', '$locationProvider',
860
+ function($routeProvider, $locationProvider) {
861
+ $routeProvider
862
+ .when('/Book/:bookId', {
863
+ templateUrl: 'book.html',
864
+ controller: 'BookCtrl',
865
+ controllerAs: 'book'
866
+ })
867
+ .when('/Book/:bookId/ch/:chapterId', {
868
+ templateUrl: 'chapter.html',
869
+ controller: 'ChapterCtrl',
870
+ controllerAs: 'chapter'
871
+ });
872
+
873
+ $locationProvider.html5Mode(true);
874
+ }])
875
+ .controller('MainCtrl', ['$route', '$routeParams', '$location',
876
+ function($route, $routeParams, $location) {
877
+ this.$route = $route;
878
+ this.$location = $location;
879
+ this.$routeParams = $routeParams;
880
+ }])
881
+ .controller('BookCtrl', ['$routeParams', function($routeParams) {
882
+ this.name = "BookCtrl";
883
+ this.params = $routeParams;
884
+ }])
885
+ .controller('ChapterCtrl', ['$routeParams', function($routeParams) {
886
+ this.name = "ChapterCtrl";
887
+ this.params = $routeParams;
888
+ }]);
779
889
 
780
- function MainCntl($route, $routeParams, $location) {
781
- this.$route = $route;
782
- this.$location = $location;
783
- this.$routeParams = $routeParams;
784
- }
785
-
786
- function BookCntl($routeParams) {
787
- this.name = "BookCntl";
788
- this.params = $routeParams;
789
- }
790
-
791
- function ChapterCntl($routeParams) {
792
- this.name = "ChapterCntl";
793
- this.params = $routeParams;
794
- }
795
890
  </file>
796
891
 
797
- <file name="scenario.js">
892
+ <file name="protractor.js" type="protractor">
798
893
  it('should load and compile correct template', function() {
799
- element('a:contains("Moby: Ch1")').click();
800
- var content = element('.doc-example-live [ng-view]').text();
801
- expect(content).toMatch(/controller\: ChapterCntl/);
894
+ element(by.linkText('Moby: Ch1')).click();
895
+ var content = element(by.css('[ng-view]')).getText();
896
+ expect(content).toMatch(/controller\: ChapterCtrl/);
802
897
  expect(content).toMatch(/Book Id\: Moby/);
803
898
  expect(content).toMatch(/Chapter Id\: 1/);
804
899
 
805
- element('a:contains("Scarlet")').click();
806
- content = element('.doc-example-live [ng-view]').text();
807
- expect(content).toMatch(/controller\: BookCntl/);
900
+ element(by.partialLinkText('Scarlet')).click();
901
+
902
+ content = element(by.css('[ng-view]')).getText();
903
+ expect(content).toMatch(/controller\: BookCtrl/);
808
904
  expect(content).toMatch(/Book Id\: Scarlet/);
809
905
  });
810
906
  </file>
@@ -814,14 +910,13 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
814
910
 
815
911
  /**
816
912
  * @ngdoc event
817
- * @name ngRoute.directive:ngView#$viewContentLoaded
818
- * @eventOf ngRoute.directive:ngView
913
+ * @name ngView#$viewContentLoaded
819
914
  * @eventType emit on the current ngView scope
820
915
  * @description
821
916
  * Emitted every time the ngView content is reloaded.
822
917
  */
823
918
  ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
824
- function ngViewFactory( $route, $anchorScroll, $animate) {
919
+ function ngViewFactory($route, $anchorScroll, $animate) {
825
920
  return {
826
921
  restrict: 'ECA',
827
922
  terminal: true,
@@ -830,6 +925,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
830
925
  link: function(scope, $element, attr, ctrl, $transclude) {
831
926
  var currentScope,
832
927
  currentElement,
928
+ previousLeaveAnimation,
833
929
  autoScrollExp = attr.autoscroll,
834
930
  onloadExp = attr.onload || '';
835
931
 
@@ -837,12 +933,20 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
837
933
  update();
838
934
 
839
935
  function cleanupLastView() {
936
+ if (previousLeaveAnimation) {
937
+ $animate.cancel(previousLeaveAnimation);
938
+ previousLeaveAnimation = null;
939
+ }
940
+
840
941
  if (currentScope) {
841
942
  currentScope.$destroy();
842
943
  currentScope = null;
843
944
  }
844
- if(currentElement) {
845
- $animate.leave(currentElement);
945
+ if (currentElement) {
946
+ previousLeaveAnimation = $animate.leave(currentElement);
947
+ previousLeaveAnimation.then(function() {
948
+ previousLeaveAnimation = null;
949
+ });
846
950
  currentElement = null;
847
951
  }
848
952
  }
@@ -862,7 +966,7 @@ function ngViewFactory( $route, $anchorScroll, $animate) {
862
966
  // function is called before linking the content, which would apply child
863
967
  // directives to non existing elements.
864
968
  var clone = $transclude(newScope, function(clone) {
865
- $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
969
+ $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
866
970
  if (angular.isDefined(autoScrollExp)
867
971
  && (!autoScrollExp || scope.$eval(autoScrollExp))) {
868
972
  $anchorScroll();
@@ -910,6 +1014,7 @@ function ngViewFillContentFactory($compile, $controller, $route) {
910
1014
  $element.data('$ngControllerController', controller);
911
1015
  $element.children().data('$ngControllerController', controller);
912
1016
  }
1017
+ scope[current.resolveAs || '$resolve'] = locals;
913
1018
 
914
1019
  link(scope);
915
1020
  }